Merge branch 'develop' into configvalidation

This commit is contained in:
Matthias 2019-11-27 19:48:21 +01:00
commit 5b996920f2
15 changed files with 165 additions and 125 deletions

View File

@ -74,7 +74,7 @@ jobs:
# Fake travis environment to get coveralls working correctly # Fake travis environment to get coveralls working correctly
export TRAVIS_PULL_REQUEST="https://github.com/${GITHUB_REPOSITORY}/pull/$(cat $GITHUB_EVENT_PATH | jq -r .number)" export TRAVIS_PULL_REQUEST="https://github.com/${GITHUB_REPOSITORY}/pull/$(cat $GITHUB_EVENT_PATH | jq -r .number)"
export TRAVIS_BRANCH=${GITHUB_REF#"ref/heads"} export TRAVIS_BRANCH=${GITHUB_REF#"ref/heads"}
export TRAVIS_BRANCH=${HEAD_REF} export CI_BRANCH=${GITHUB_REF#"ref/heads"}
echo "${TRAVIS_BRANCH}" echo "${TRAVIS_BRANCH}"
coveralls || true coveralls || true

View File

@ -266,7 +266,11 @@ class FreqtradeBot:
amount_reserve_percent += self.strategy.stoploss amount_reserve_percent += self.strategy.stoploss
# it should not be more than 50% # it should not be more than 50%
amount_reserve_percent = max(amount_reserve_percent, 0.5) amount_reserve_percent = max(amount_reserve_percent, 0.5)
return min(min_stake_amounts) / amount_reserve_percent
# The value returned should satisfy both limits: for amount (base currency) and
# for cost (quote, stake currency), so max() is used here.
# See also #2575 at github.
return max(min_stake_amounts) / amount_reserve_percent
def create_trades(self) -> bool: def create_trades(self) -> bool:
""" """

View File

@ -354,7 +354,8 @@ class ApiServer(RPC):
Returns the current status of the trades in json format Returns the current status of the trades in json format
""" """
results = self._rpc_balance(self._config.get('fiat_display_currency', '')) results = self._rpc_balance(self._config['stake_currency'],
self._config.get('fiat_display_currency', ''))
return self.rest_dump(results) return self.rest_dump(results)
@require_login @require_login

View File

@ -297,34 +297,42 @@ class RPC:
'best_rate': round(bp_rate * 100, 2), 'best_rate': round(bp_rate * 100, 2),
} }
def _rpc_balance(self, fiat_display_currency: str) -> Dict: def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict:
""" Returns current account balance per crypto """ """ Returns current account balance per crypto """
output = [] output = []
total = 0.0 total = 0.0
for coin, balance in self._freqtrade.exchange.get_balances().items(): try:
if not balance['total']: tickers = self._freqtrade.exchange.get_tickers()
except (TemporaryError, DependencyException):
raise RPCException('Error getting current tickers.')
for coin, balance in self._freqtrade.wallets.get_all_balances().items():
if not balance.total:
continue continue
if coin == 'BTC': est_stake: float = 0
if coin == stake_currency:
rate = 1.0 rate = 1.0
est_stake = balance.total
else: else:
try: try:
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, "BTC") pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
if pair.startswith("BTC"): rate = tickers.get(pair, {}).get('bid', None)
rate = 1.0 / self._freqtrade.get_sell_rate(pair, False) if rate:
else: if pair.startswith(stake_currency):
rate = self._freqtrade.get_sell_rate(pair, False) rate = 1.0 / rate
est_stake = rate * balance.total
except (TemporaryError, DependencyException): except (TemporaryError, DependencyException):
logger.warning(f" Could not get rate for pair {coin}.") logger.warning(f" Could not get rate for pair {coin}.")
continue continue
est_btc: float = rate * balance['total'] total = total + (est_stake or 0)
total = total + est_btc
output.append({ output.append({
'currency': coin, 'currency': coin,
'free': balance['free'] if balance['free'] is not None else 0, 'free': balance.free if balance.free is not None else 0,
'balance': balance['total'] if balance['total'] is not None else 0, 'balance': balance.total if balance.total is not None else 0,
'used': balance['used'] if balance['used'] is not None else 0, 'used': balance.used if balance.used is not None else 0,
'est_btc': est_btc, 'est_stake': est_stake or 0,
'stake': stake_currency,
}) })
if total == 0.0: if total == 0.0:
if self._freqtrade.config.get('dry_run', False): if self._freqtrade.config.get('dry_run', False):

View File

@ -325,15 +325,16 @@ class Telegram(RPC):
def _balance(self, update: Update, context: CallbackContext) -> None: def _balance(self, update: Update, context: CallbackContext) -> None:
""" Handler for /balance """ """ Handler for /balance """
try: try:
result = self._rpc_balance(self._config.get('fiat_display_currency', '')) result = self._rpc_balance(self._config['stake_currency'],
self._config.get('fiat_display_currency', ''))
output = '' output = ''
for currency in result['currencies']: for currency in result['currencies']:
if currency['est_btc'] > 0.0001: if currency['est_stake'] > 0.0001:
curr_output = "*{currency}:*\n" \ curr_output = "*{currency}:*\n" \
"\t`Available: {free: .8f}`\n" \ "\t`Available: {free: .8f}`\n" \
"\t`Balance: {balance: .8f}`\n" \ "\t`Balance: {balance: .8f}`\n" \
"\t`Pending: {used: .8f}`\n" \ "\t`Pending: {used: .8f}`\n" \
"\t`Est. BTC: {est_btc: .8f}`\n".format(**currency) "\t`Est. {stake}: {est_stake: .8f}`\n".format(**currency)
else: else:
curr_output = "*{currency}:* not showing <1$ amount \n".format(**currency) curr_output = "*{currency}:* not showing <1$ amount \n".format(**currency)

View File

@ -2,7 +2,7 @@
""" Wallet """ """ Wallet """
import logging import logging
from typing import Dict, NamedTuple from typing import Dict, NamedTuple, Any
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade import constants from freqtrade import constants
@ -72,3 +72,6 @@ class Wallets:
) )
logger.info('Wallets synced.') logger.info('Wallets synced.')
def get_all_balances(self) -> Dict[str, Any]:
return self._wallets

View File

@ -1,6 +1,6 @@
# requirements without requirements installable via conda # requirements without requirements installable via conda
# mainly used for Raspberry pi installs # mainly used for Raspberry pi installs
ccxt==1.19.54 ccxt==1.19.86
SQLAlchemy==1.3.11 SQLAlchemy==1.3.11
python-telegram-bot==12.2.0 python-telegram-bot==12.2.0
arrow==0.15.4 arrow==0.15.4
@ -8,7 +8,7 @@ cachetools==3.1.1
requests==2.22.0 requests==2.22.0
urllib3==1.25.7 urllib3==1.25.7
wrapt==1.11.2 wrapt==1.11.2
jsonschema==3.1.1 jsonschema==3.2.0
TA-Lib==0.4.17 TA-Lib==0.4.17
tabulate==0.8.6 tabulate==0.8.6
coinmarketcap==5.0.3 coinmarketcap==5.0.3

View File

@ -8,10 +8,10 @@ flake8==3.7.9
flake8-type-annotations==0.1.0 flake8-type-annotations==0.1.0
flake8-tidy-imports==3.1.0 flake8-tidy-imports==3.1.0
mypy==0.740 mypy==0.740
pytest==5.2.4 pytest==5.3.0
pytest-asyncio==0.10.0 pytest-asyncio==0.10.0
pytest-cov==2.8.1 pytest-cov==2.8.1
pytest-mock==1.11.2 pytest-mock==1.12.1
pytest-random-order==1.0.4 pytest-random-order==1.0.4
# Convert jupyter notebooks to markdown documents # Convert jupyter notebooks to markdown documents

View File

@ -2,7 +2,7 @@
-r requirements.txt -r requirements.txt
# Required for hyperopt # Required for hyperopt
scipy==1.3.2 scipy==1.3.3
scikit-learn==0.21.3 scikit-learn==0.21.3
scikit-optimize==0.5.2 scikit-optimize==0.5.2
filelock==3.0.12 filelock==3.0.12

View File

@ -325,7 +325,7 @@ def get_markets():
}, },
'price': 500000, 'price': 500000,
'cost': { 'cost': {
'min': 1, 'min': 0.0001,
'max': 500000, 'max': 500000,
}, },
}, },
@ -351,7 +351,7 @@ def get_markets():
}, },
'price': 500000, 'price': 500000,
'cost': { 'cost': {
'min': 1, 'min': 0.0001,
'max': 500000, 'max': 500000,
}, },
}, },
@ -376,7 +376,7 @@ def get_markets():
}, },
'price': 500000, 'price': 500000,
'cost': { 'cost': {
'min': 1, 'min': 0.0001,
'max': 500000, 'max': 500000,
}, },
}, },
@ -401,7 +401,7 @@ def get_markets():
}, },
'price': 500000, 'price': 500000,
'cost': { 'cost': {
'min': 1, 'min': 0.0001,
'max': 500000, 'max': 500000,
}, },
}, },
@ -426,7 +426,7 @@ def get_markets():
}, },
'price': 500000, 'price': 500000,
'cost': { 'cost': {
'min': 1, 'min': 0.0001,
'max': 500000, 'max': 500000,
}, },
}, },
@ -451,7 +451,7 @@ def get_markets():
}, },
'price': 500000, 'price': 500000,
'cost': { 'cost': {
'min': 1, 'min': 0.0001,
'max': 500000, 'max': 500000,
}, },
}, },
@ -479,7 +479,7 @@ def get_markets():
'max': None 'max': None
}, },
'cost': { 'cost': {
'min': 0.001, 'min': 0.0001,
'max': None 'max': None
} }
}, },
@ -980,6 +980,28 @@ def tickers():
'quoteVolume': 62.68220262, 'quoteVolume': 62.68220262,
'info': {} 'info': {}
}, },
'BTC/USDT': {
'symbol': 'BTC/USDT',
'timestamp': 1573758371399,
'datetime': '2019-11-14T19:06:11.399Z',
'high': 8800.0,
'low': 8582.6,
'bid': 8648.16,
'bidVolume': 0.238771,
'ask': 8648.72,
'askVolume': 0.016253,
'vwap': 8683.13647806,
'open': 8759.7,
'close': 8648.72,
'last': 8648.72,
'previousClose': 8759.67,
'change': -110.98,
'percentage': -1.267,
'average': None,
'baseVolume': 35025.943355,
'quoteVolume': 304135046.4242901,
'info': {}
},
'ETH/USDT': { 'ETH/USDT': {
'symbol': 'ETH/USDT', 'symbol': 'ETH/USDT',
'timestamp': 1522014804118, 'timestamp': 1522014804118,
@ -1067,7 +1089,29 @@ def tickers():
'baseVolume': 59698.79897, 'baseVolume': 59698.79897,
'quoteVolume': 29132399.743954, 'quoteVolume': 29132399.743954,
'info': {} 'info': {}
} },
'XRP/BTC': {
'symbol': 'XRP/BTC',
'timestamp': 1573758257534,
'datetime': '2019-11-14T19:04:17.534Z',
'high': 3.126e-05,
'low': 3.061e-05,
'bid': 3.093e-05,
'bidVolume': 27901.0,
'ask': 3.095e-05,
'askVolume': 10551.0,
'vwap': 3.091e-05,
'open': 3.119e-05,
'close': 3.094e-05,
'last': 3.094e-05,
'previousClose': 3.117e-05,
'change': -2.5e-07,
'percentage': -0.802,
'average': None,
'baseVolume': 37334921.0,
'quoteVolume': 1154.19266394,
'info': {}
},
}) })
@ -1317,8 +1361,8 @@ def rpc_balance():
'used': 0.0 'used': 0.0
}, },
'XRP': { 'XRP': {
'total': 1.0, 'total': 0.1,
'free': 1.0, 'free': 0.01,
'used': 0.0 'used': 0.0
}, },
'EUR': { 'EUR': {

View File

@ -100,7 +100,7 @@ def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_co
markets=PropertyMock(return_value=shitcoinmarkets), markets=PropertyMock(return_value=shitcoinmarkets),
) )
# argument: use the whitelist dynamically by exchange-volume # argument: use the whitelist dynamically by exchange-volume
whitelist = ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC'] whitelist = ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC', 'HOT/BTC']
bot.pairlists.refresh_pairlist() bot.pairlists.refresh_pairlist()
assert whitelist == bot.pairlists.whitelist assert whitelist == bot.pairlists.whitelist
@ -135,10 +135,10 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
@pytest.mark.parametrize("pairlists,base_currency,whitelist_result", [ @pytest.mark.parametrize("pairlists,base_currency,whitelist_result", [
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}], ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']), "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC', 'HOT/BTC']),
# Different sorting depending on quote or bid volume # Different sorting depending on quote or bid volume
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "bidVolume"}], ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "bidVolume"}],
"BTC", ['HOT/BTC', 'FUEL/BTC', 'LTC/BTC', 'TKN/BTC', 'ETH/BTC']), "BTC", ['HOT/BTC', 'FUEL/BTC', 'XRP/BTC', 'LTC/BTC', 'TKN/BTC']),
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}], ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}],
"USDT", ['ETH/USDT']), "USDT", ['ETH/USDT']),
# No pair for ETH ... # No pair for ETH ...
@ -146,19 +146,19 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
"ETH", []), "ETH", []),
# Precisionfilter and quote volume # Precisionfilter and quote volume
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "PrecisionFilter"}], "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'FUEL/BTC']), {"method": "PrecisionFilter"}], "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
# Precisionfilter bid # Precisionfilter bid
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "bidVolume"}, ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "bidVolume"},
{"method": "PrecisionFilter"}], "BTC", ['FUEL/BTC', 'LTC/BTC', 'TKN/BTC', 'ETH/BTC']), {"method": "PrecisionFilter"}], "BTC", ['FUEL/BTC', 'XRP/BTC', 'LTC/BTC', 'TKN/BTC']),
# PriceFilter and VolumePairList # PriceFilter and VolumePairList
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "PriceFilter", "low_price_ratio": 0.03}], {"method": "PriceFilter", "low_price_ratio": 0.03}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'FUEL/BTC']), "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
# Hot is removed by precision_filter, Fuel by low_price_filter. # Hot is removed by precision_filter, Fuel by low_price_filter.
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, ([{"method": "VolumePairList", "number_assets": 6, "sort_key": "quoteVolume"},
{"method": "PrecisionFilter"}, {"method": "PrecisionFilter"},
{"method": "PriceFilter", "low_price_ratio": 0.02} {"method": "PriceFilter", "low_price_ratio": 0.02}
], "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC']), ], "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
# StaticPairlist Only # StaticPairlist Only
([{"method": "StaticPairList"}, ([{"method": "StaticPairList"},
], "BTC", ['ETH/BTC', 'TKN/BTC']), ], "BTC", ['ETH/BTC', 'TKN/BTC']),

View File

@ -355,29 +355,18 @@ def test_rpc_balance_handle_error(default_conf, mocker):
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value=mock_balance), get_balances=MagicMock(return_value=mock_balance),
get_ticker=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx')) get_tickers=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx'))
) )
freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot, (True, False)) patch_get_signal(freqtradebot, (True, False))
rpc = RPC(freqtradebot) rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter() rpc._fiat_converter = CryptoToFiatConverter()
with pytest.raises(RPCException, match="Error getting current tickers."):
result = rpc._rpc_balance(default_conf['fiat_display_currency']) rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
assert prec_satoshi(result['total'], 12)
assert prec_satoshi(result['value'], 180000)
assert 'USD' == result['symbol']
assert result['currencies'] == [{
'currency': 'BTC',
'free': 10.0,
'balance': 12.0,
'used': 2.0,
'est_btc': 12.0,
}]
assert result['total'] == 12.0
def test_rpc_balance_handle(default_conf, mocker): def test_rpc_balance_handle(default_conf, mocker, tickers):
mock_balance = { mock_balance = {
'BTC': { 'BTC': {
'free': 10.0, 'free': 10.0,
@ -389,7 +378,7 @@ def test_rpc_balance_handle(default_conf, mocker):
'total': 5.0, 'total': 5.0,
'used': 4.0, 'used': 4.0,
}, },
'PAX': { 'USDT': {
'free': 5.0, 'free': 5.0,
'total': 10.0, 'total': 10.0,
'used': 5.0, 'used': 5.0,
@ -405,10 +394,9 @@ def test_rpc_balance_handle(default_conf, mocker):
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value=mock_balance), get_balances=MagicMock(return_value=mock_balance),
get_ticker=MagicMock( get_tickers=tickers,
side_effect=lambda p, r: {'bid': 100} if p == "BTC/PAX" else {'bid': 0.01}),
get_valid_pair_combination=MagicMock( get_valid_pair_combination=MagicMock(
side_effect=lambda a, b: f"{b}/{a}" if a == "PAX" else f"{a}/{b}") side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}")
) )
freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot = get_patched_freqtradebot(mocker, default_conf)
@ -416,30 +404,35 @@ def test_rpc_balance_handle(default_conf, mocker):
rpc = RPC(freqtradebot) rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter() rpc._fiat_converter = CryptoToFiatConverter()
result = rpc._rpc_balance(default_conf['fiat_display_currency']) result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
assert prec_satoshi(result['total'], 12.15) assert prec_satoshi(result['total'], 12.309096315)
assert prec_satoshi(result['value'], 182250) assert prec_satoshi(result['value'], 184636.44472997)
assert 'USD' == result['symbol'] assert 'USD' == result['symbol']
assert result['currencies'] == [ assert result['currencies'] == [
{'currency': 'BTC', {'currency': 'BTC',
'free': 10.0, 'free': 10.0,
'balance': 12.0, 'balance': 12.0,
'used': 2.0, 'used': 2.0,
'est_btc': 12.0, 'est_stake': 12.0,
'stake': 'BTC',
}, },
{'free': 1.0, {'free': 1.0,
'balance': 5.0, 'balance': 5.0,
'currency': 'ETH', 'currency': 'ETH',
'est_btc': 0.05, 'est_stake': 0.30794,
'used': 4.0 'used': 4.0,
'stake': 'BTC',
}, },
{'free': 5.0, {'free': 5.0,
'balance': 10.0, 'balance': 10.0,
'currency': 'PAX', 'currency': 'USDT',
'est_btc': 0.1, 'est_stake': 0.0011563153318162476,
'used': 5.0} 'used': 5.0,
'stake': 'BTC',
}
] ]
assert result['total'] == 12.15 assert result['total'] == 12.309096315331816
def test_rpc_start(mocker, default_conf) -> None: def test_rpc_start(mocker, default_conf) -> None:

View File

@ -230,28 +230,10 @@ def test_api_stopbuy(botclient):
def test_api_balance(botclient, mocker, rpc_balance): def test_api_balance(botclient, mocker, rpc_balance):
ftbot, client = botclient ftbot, client = botclient
def mock_ticker(symbol, refresh):
if symbol == 'BTC/USDT':
return {
'bid': 10000.00,
'ask': 10000.00,
'last': 10000.00,
}
elif symbol == 'XRP/BTC':
return {
'bid': 0.00001,
'ask': 0.00001,
'last': 0.00001,
}
return {
'bid': 0.1,
'ask': 0.1,
'last': 0.1,
}
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance)
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination',
side_effect=lambda a, b: f"{a}/{b}") side_effect=lambda a, b: f"{a}/{b}")
ftbot.wallets.update()
rc = client_get(client, f"{BASE_URI}/balance") rc = client_get(client, f"{BASE_URI}/balance")
assert_response(rc) assert_response(rc)
@ -262,7 +244,8 @@ def test_api_balance(botclient, mocker, rpc_balance):
'free': 12.0, 'free': 12.0,
'balance': 12.0, 'balance': 12.0,
'used': 0.0, 'used': 0.0,
'est_btc': 12.0, 'est_stake': 12.0,
'stake': 'BTC',
} }

View File

@ -461,29 +461,10 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0] assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0]
def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance) -> None: def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tickers) -> None:
def mock_ticker(symbol, refresh):
if symbol == 'BTC/USDT':
return {
'bid': 10000.00,
'ask': 10000.00,
'last': 10000.00,
}
elif symbol == 'XRP/BTC':
return {
'bid': 0.00001,
'ask': 0.00001,
'last': 0.00001,
}
return {
'bid': 0.1,
'ask': 0.1,
'last': 0.1,
}
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance)
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker) mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers)
mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination',
side_effect=lambda a, b: f"{a}/{b}") side_effect=lambda a, b: f"{a}/{b}")
@ -564,7 +545,8 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None
'free': 1.0, 'free': 1.0,
'used': 0.5, 'used': 0.5,
'balance': i, 'balance': i,
'est_btc': 1 'est_stake': 1,
'stake': 'BTC',
}) })
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={ mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={
'currencies': balances, 'currencies': balances,

View File

@ -299,7 +299,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
limit_buy_order, fee) -> None: limit_buy_order, fee) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
default_conf['stake_amount'] = 0.0098751 default_conf['stake_amount'] = 0.00098751
default_conf['max_open_trades'] = 2 default_conf['max_open_trades'] = 2
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
@ -313,7 +313,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
trade = Trade.query.first() trade = Trade.query.first()
assert trade is not None assert trade is not None
assert trade.stake_amount == 0.0098751 assert trade.stake_amount == 0.00098751
assert trade.is_open assert trade.is_open
assert trade.open_date is not None assert trade.open_date is not None
@ -321,11 +321,11 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
trade = Trade.query.order_by(Trade.id.desc()).first() trade = Trade.query.order_by(Trade.id.desc()).first()
assert trade is not None assert trade is not None
assert trade.stake_amount == 0.0098751 assert trade.stake_amount == 0.00098751
assert trade.is_open assert trade.is_open
assert trade.open_date is not None assert trade.open_date is not None
assert Trade.total_open_trades_stakes() == 1.97502e-02 assert Trade.total_open_trades_stakes() == 1.97502e-03
def test_get_min_pair_stake_amount(mocker, default_conf) -> None: def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
@ -334,6 +334,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.stoploss = -0.05 freqtrade.strategy.stoploss = -0.05
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}} markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
# no pair found # no pair found
mocker.patch( mocker.patch(
'freqtrade.exchange.Exchange.markets', 'freqtrade.exchange.Exchange.markets',
@ -425,7 +426,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
PropertyMock(return_value=markets) PropertyMock(return_value=markets)
) )
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2)
assert result == min(2, 2 * 2) / 0.9 assert result == max(2, 2 * 2) / 0.9
# min amount and cost are set (amount is minial) # min amount and cost are set (amount is minial)
markets["ETH/BTC"]["limits"] = { markets["ETH/BTC"]["limits"] = {
@ -437,7 +438,27 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
PropertyMock(return_value=markets) PropertyMock(return_value=markets)
) )
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2) result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 2)
assert result == min(8, 2 * 2) / 0.9 assert result == max(8, 2 * 2) / 0.9
def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.stoploss = -0.05
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
# Real Binance data
markets["ETH/BTC"]["limits"] = {
'cost': {'min': 0.0001},
'amount': {'min': 0.001}
}
mocker.patch(
'freqtrade.exchange.Exchange.markets',
PropertyMock(return_value=markets)
)
result = freqtrade._get_min_pair_stake_amount('ETH/BTC', 0.020405)
assert round(result, 8) == round(max(0.0001, 0.001 * 0.020405) / 0.9, 8)
def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> None: def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> None: