Skip to content

Commit

Permalink
Merge pull request #10 from guanana/coingecko
Browse files Browse the repository at this point in the history
Coingecko add-on
  • Loading branch information
guanana authored Oct 28, 2021
2 parents 8d08254 + 36277af commit 2e67887
Show file tree
Hide file tree
Showing 15 changed files with 291 additions and 136 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-390/)
[![Django 3.2.8](https://img.shields.io/badge/Django-3.2.8-blue.svg)](https://docs.djangoproject.com/en/3.2/)
[![Docker-Compose](https://img.shields.io/badge/Docker-Docker%20Compose-orange.svg)](https://docs.docker.com/compose/gettingstarted/)
[![codecov](https://codecov.io/gh/guanana/vanir/branch/main/graph/badge.svg?token=CFWLAUD233)](https://codecov.io/gh/guanana/vanir)

Crypto asset management and bot control, all one place

Expand All @@ -23,12 +24,15 @@ Crypto asset management and bot control, all one place
* [Usage](#usage)
* [Default data](#default-data)
* [Default username/password](#how-to-change-default-username-and-password)
* [Knowing issues](#knowing-issues)
* [Contribute](#contribute)
* [Test Coverage](#test-coverage)
* [License](#license)
* [Disclaimer](#disclaimer)


## Data Sources and supported Exchanges
[![CoinGecko](vanir/static/images/data_logos/coingecko.png)](https://www.coingecko.com)
[![Binance](vanir/static/images/data_logos/binance.png)](https://www.binance.com/en)
## About Vanir
Vanir is a Django app aiming to solve the current problems with proprietary
software in the crypto world.
Expand Down Expand Up @@ -148,6 +152,11 @@ Fill the desired fields and click on Password:
password, but you can change the password using CLICK ME--> __this form__ <-- CLICK ME.*
![Change default password - Step 1](vanir/static/images/change_password/change_password_2.png)

## Knowing issues
- When using more than one account select same `Token Pair` to avoid constant update
and temporary wrong quantities being displayed. Recommended, Tether
- For now you can add tokens in accounts manually, but you cannot remove them

## Contribute
To start contributing just fork the project and run the local environment by running:
`docker-compose -f local.yml up -d`
Expand Down
54 changes: 2 additions & 52 deletions vanir/core/account/filtersets.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,22 @@
import django_filters
from django.contrib import auth
from django.db.models import Q
from django_filters import FilterSet

from vanir.core.account.models import Account

UserModel = auth.get_user_model()


class AccountFilterSet(FilterSet):
# exchange_id = django_filters.ModelMultipleChoiceFilter(
# field_name='exchange',
# queryset=Exchange.objects.all(),
# label='Exchange (ID)',
# )
# exchange = django_filters.ModelMultipleChoiceFilter(
# field_name='exchange__name',
# queryset=Exchange.objects.all(),
# to_field_name='name',
# label='Exchange (Name)',
# )
# user_id = django_filters.ModelMultipleChoiceFilter(
# field_name='user',
# queryset=UserModel.objects.all(),
# label='User (ID)',
# )
# user = django_filters.ModelMultipleChoiceFilter(
# field_name='user__username',
# queryset=UserModel.objects.all(),
# label='User (Username)',
# )
# token_id = django_filters.ModelMultipleChoiceFilter(
# field_name='token',
# queryset=Token.objects.all(),
# label='Token (id)',
# )
# token_symbol = django_filters.ModelMultipleChoiceFilter(
# field_name='token__symbol',
# queryset=Token.objects.all(),
# label='Token (Symbol)',
# )
# token = django_filters.ModelMultipleChoiceFilter(
# field_name='token__name',
# queryset=Token.objects.all(),
# label='Token (Name)',
# )

q = django_filters.CharFilter(
method="search",
label="Search",
)

def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(exchange__name__icontains=value)
| Q(token__name__icontains=value)
| Q(token__symbol__icontains=value)
)

class Meta:
model = Account
fields = {
"name": ["icontains"],
"api_key": ["iexact"],
"secret": ["iexact"],
"tld": ["iexact"],
"default": ["eq"],
"testnet": ["eq"],
"default": ["exact"],
"testnet": ["exact"],
}
29 changes: 15 additions & 14 deletions vanir/core/account/helpers/balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@
def update_balance(account, update_price=True):
df = account.exchange_obj.get_balance()
response = []
try:
for index, row in df.iterrows():
try:
token = Token.objects.get(symbol=row["asset"])
except Token.DoesNotExist:
token = token_import(account=account, token_symbol=row["asset"])
import_token_account(
account=account,
token_obj=token,
quantity=float(row["free"]) + float(row["locked"]),
)
response.append(row["asset"])
except AttributeError:
pass
if df is not None:
try:
for index, row in df.iterrows():
try:
token = Token.objects.get(symbol=row["asset"])
except Token.DoesNotExist:
token = token_import(account=account, token_symbol=row["asset"])
import_token_account(
account=account,
token_obj=token,
quantity=float(row["free"]) + float(row["locked"]),
)
response.append(row["asset"])
except AttributeError:
pass
if update_price:
bulk_update(account)
return response
117 changes: 72 additions & 45 deletions vanir/core/account/helpers/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from unittest.mock import patch

from binance import Client
from django.test import TestCase

from vanir.core.account.helpers.balance import update_balance
Expand Down Expand Up @@ -85,9 +86,28 @@
},
]

get_all_tickers_mock = [
{"symbol": "ETHUSD", "price": "1.06720800"},
{"symbol": "ETHUSDT", "price": "1.06720800"},
{"symbol": "LTCBTC", "price": "0.00306600"},
{"symbol": "BNBBTC", "price": "0.00768100"},
{"symbol": "EOSETH", "price": "0.00105500"},
{"symbol": "GASBTC", "price": "0.00014040"},
{"symbol": "BNBETH", "price": "0.11440000"},
{"symbol": "BTCUSDT", "price": "58410.13000000"},
{"symbol": "ICNETH", "price": "0.00166300"},
{"symbol": "MCOBTC", "price": "0.00021140"},
{"symbol": "WTCBTC", "price": "0.00001578"},
]


def side_effect_get_token_full_name(account, token_symbol):
return token_symbol


class TestAccountHelpers(TestCase):
def setUp(self):
@patch("vanir.core.account.helpers.balance.update_balance")
def setUp(self, mock_update_balance):
self.token = Token.objects.create(name="Test Helper", symbol="TSTHELP")
self.manual_exchange = Exchange.objects.create(
name="exchange_test", native_token=self.token
Expand All @@ -114,66 +134,73 @@ def test_account_update_balance_price_manual(self, mock_coingecko_price):
"binance.client.Client.get_account",
return_value=account_mock_dict_empty_balance,
) # noqa
@patch("binance.client.Client.ping")
@patch.object(Client, "get_all_tickers", autospec=True)
def test_account_update_balance_empty_binance_no_price(
self, mock_get_account, mock_binance_account # noqa
self, mock_get_all_tickers, mock_get_account
):
binance_account = Account.objects.create(
name="Binance Account",
exchange=self.binance,
api_key="account1_1234",
secret="1234secret",
token_pair=self.bnb,
default=True,
)
with patch("vanir.core.account.helpers.balance.update_balance"):
binance_account = Account.objects.create(
name="Binance Account",
exchange=self.binance,
api_key="account1_1234",
secret="1234secret",
token_pair=self.bnb,
default=True,
)
response = update_balance(binance_account, update_price=False)
self.assertEqual(response, [])
self.assertEqual(mock_binance_account.call_count, 2)
self.assertEqual(mock_get_account.call_count, 1)
self.assertEqual(mock_get_all_tickers.call_count, 0)

@patch.object(Client, "get_all_tickers", autospec=True)
@patch(
"binance.client.Client.get_account",
return_value=account_mock_dict_empty_balance,
) # noqa
@patch("binance.client.Client.ping")
def test_account_update_balance_empty_binance_price(
self, mock_get_account, mock_binance_account # noqa
self, mock_get_account, mock_get_all_tickers
):
binance_account = Account.objects.create(
name="Binance Account",
exchange=self.binance,
api_key="account1_1234",
secret="1234secret",
token_pair=self.bnb,
default=True,
)
with patch("vanir.core.account.helpers.balance.update_balance"):
binance_account = Account.objects.create(
name="Binance Account",
exchange=self.binance,
api_key="account1_1234",
secret="1234secret",
token_pair=self.bnb,
default=True,
)
response = update_balance(binance_account, update_price=True)
self.assertEqual(response, [])
self.assertEqual(mock_binance_account.call_count, 2)
self.assertEqual(mock_get_account.call_count, 1)
self.assertEqual(mock_get_all_tickers.call_count, 1)

@patch("binance.client.Client.get_account", return_value=account_mock_dict) # noqa
@patch("vanir.core.token.helpers.import_utils.token_import")
@patch.object(
Client, "get_all_tickers", autospec=True, return_value=get_all_tickers_mock
)
@patch(
"binance.client.Client.get_margin_all_assets",
return_value=get_margin_all_assets_mock_dict,
) # noqa
@patch("binance.client.Client.ping")
def ttest_account_update_balance_price_binance(
self,
mock_get_account,
mock_import,
mock_get_margin_all_assets, # noqa
mock_binance_account,
"binance.client.Client.get_account",
autospec=True,
return_value=account_mock_dict,
)
@patch(
"vanir.core.token.helpers.import_utils.get_token_full_name",
autospec=True,
side_effect=side_effect_get_token_full_name,
)
def test_account_update_balance_price_binance(
self, mock_get_token_full_name, mock_get_account, mock_get_all_tickers
):
binance_account = Account.objects.create(
name="Binance Account",
exchange=self.binance,
api_key="account1_1234",
secret="1234secret",
token_pair=self.bnb,
default=True,
)
with patch("vanir.core.account.helpers.balance.update_balance"):
binance_account = Account.objects.create(
name="Binance Account",
exchange=self.binance,
api_key="account1_1234",
secret="1234secret",
token_pair=self.bnb,
default=True,
)
response = update_balance(binance_account, update_price=True)
self.assertEqual(response, ["EOS", "BNB", "LTC", "ETH", "BTC"])
self.assertEqual(mock_get_account.call_count, 1)
self.assertEqual(mock_import.call_count, 1)
self.assertEqual(mock_binance_account.call_count, 2)
self.assertEqual(mock_get_all_tickers.call_count, 1)
self.assertEqual(mock_get_token_full_name.call_count, 4)
10 changes: 7 additions & 3 deletions vanir/core/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,17 @@ def clean(self):
)

def save(self, *args, **kwargs):
from vanir.core.account.helpers.balance import update_balance

if self._state.adding:
self.is_extended_exchange()
self.check_default()
update_balance(self)
elif self.pk is not None:
orig = Account.objects.get(pk=self.pk)
if orig.token_pair != self.token_pair:
update_balance(self)
super().save(*args, **kwargs)
from vanir.core.account.helpers.balance import update_balance

update_balance(self)

def check_default(self):
# Check if there are more accounts with default setting
Expand Down
23 changes: 15 additions & 8 deletions vanir/core/account/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ def setUp(self):
self.bnb = Token.objects.create(symbol="BNB", name="Binance Coin")
self.binance = Exchange.objects.create(name="Binance", native_token=self.bnb)

def test_create_account(self):
@patch("vanir.core.account.helpers.balance.update_balance")
def test_create_account(self, mock_update_balance):
account_create = aux_create_basic_account(
name="account1", exchange=self.exchange, token_pair=self.token
)
account_get = Account.objects.get(exchange=self.exchange.id)
self.assertEqual(account_create, account_get)
self.assertEqual(mock_update_balance.called, True)

def test_default_account_auto_change(self):
@patch("vanir.core.account.helpers.balance.update_balance")
def test_default_account_auto_change(self, mock_update_balance):
account1 = aux_create_basic_account(
name="account1", exchange=self.exchange, token_pair=self.token
)
Expand All @@ -39,8 +42,10 @@ def test_default_account_auto_change(self):
account2.refresh_from_db()
self.assertEqual(account1.default, False)
self.assertEqual(account2.default, True)
self.assertEqual(mock_update_balance.call_count, 2)

def test_add_tokens(self):
@patch("vanir.core.account.helpers.balance.update_balance")
def test_add_tokens(self, mock_update_balance):
account1 = aux_create_basic_account(
name="account1", exchange=self.exchange, token_pair=self.token
)
Expand All @@ -49,6 +54,7 @@ def test_add_tokens(self):
)
AccountTokens.objects.create(account=account1, token=test_token, quantity=2)
self.assertEqual(account1.total_value_account, 10.6848)
self.assertEqual(mock_update_balance.call_count, 1)

def test_supported_exchange_binance_no_valid_key(self):
with pytest.raises(BinanceAPIException):
Expand All @@ -57,24 +63,25 @@ def test_supported_exchange_binance_no_valid_key(self):
)

@patch("vanir.core.account.helpers.balance.update_balance")
def test_supported_exchange_binance(self, mock_balance):
def test_supported_exchange_binance(self, mock_update_balance):
account1 = aux_create_basic_account(
name="account1", exchange=self.binance, token_pair=self.token
)
self.assertEqual(account1.extended_exchange, True)
self.assertEqual(mock_balance.called, True)
self.assertEqual(mock_update_balance.call_count, 1)

@patch("vanir.core.account.helpers.balance.update_balance")
def test_manual_exchange(self, mock_balance):
def test_manual_exchange(self, mock_update_balance):
account1 = aux_create_basic_account(
name="account1", exchange=self.exchange, token_pair=self.token
)
self.assertEqual(account1.extended_exchange, False)
self.assertEqual(mock_balance.called, True)
self.assertEqual(mock_update_balance.call_count, 1)


class TestAccountUrls(TestCase):
def setUp(self):
@patch("vanir.core.account.helpers.balance.update_balance")
def setUp(self, mock_update_balance):
self.token = Token.objects.create(name="Test", symbol="TST")
self.exchange = Exchange.objects.create(
name="exchange_test", native_token=self.token
Expand Down
Loading

0 comments on commit 2e67887

Please sign in to comment.