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
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=${HEAD_REF}
export CI_BRANCH=${GITHUB_REF#"ref/heads"}
echo "${TRAVIS_BRANCH}"
coveralls || true

View File

@ -266,7 +266,11 @@ class FreqtradeBot:
amount_reserve_percent += self.strategy.stoploss
# it should not be more than 50%
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:
"""

View File

@ -354,7 +354,8 @@ class ApiServer(RPC):
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)
@require_login

View File

@ -297,34 +297,42 @@ class RPC:
'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 """
output = []
total = 0.0
for coin, balance in self._freqtrade.exchange.get_balances().items():
if not balance['total']:
try:
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
if coin == 'BTC':
est_stake: float = 0
if coin == stake_currency:
rate = 1.0
est_stake = balance.total
else:
try:
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, "BTC")
if pair.startswith("BTC"):
rate = 1.0 / self._freqtrade.get_sell_rate(pair, False)
else:
rate = self._freqtrade.get_sell_rate(pair, False)
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
rate = tickers.get(pair, {}).get('bid', None)
if rate:
if pair.startswith(stake_currency):
rate = 1.0 / rate
est_stake = rate * balance.total
except (TemporaryError, DependencyException):
logger.warning(f" Could not get rate for pair {coin}.")
continue
est_btc: float = rate * balance['total']
total = total + est_btc
total = total + (est_stake or 0)
output.append({
'currency': coin,
'free': balance['free'] if balance['free'] 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,
'est_btc': est_btc,
'free': balance.free if balance.free 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,
'est_stake': est_stake or 0,
'stake': stake_currency,
})
if total == 0.0:
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:
""" Handler for /balance """
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 = ''
for currency in result['currencies']:
if currency['est_btc'] > 0.0001:
if currency['est_stake'] > 0.0001:
curr_output = "*{currency}:*\n" \
"\t`Available: {free: .8f}`\n" \
"\t`Balance: {balance: .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:
curr_output = "*{currency}:* not showing <1$ amount \n".format(**currency)

View File

@ -2,7 +2,7 @@
""" Wallet """
import logging
from typing import Dict, NamedTuple
from typing import Dict, NamedTuple, Any
from freqtrade.exchange import Exchange
from freqtrade import constants
@ -72,3 +72,6 @@ class Wallets:
)
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
# mainly used for Raspberry pi installs
ccxt==1.19.54
ccxt==1.19.86
SQLAlchemy==1.3.11
python-telegram-bot==12.2.0
arrow==0.15.4
@ -8,7 +8,7 @@ cachetools==3.1.1
requests==2.22.0
urllib3==1.25.7
wrapt==1.11.2
jsonschema==3.1.1
jsonschema==3.2.0
TA-Lib==0.4.17
tabulate==0.8.6
coinmarketcap==5.0.3

View File

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

View File

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

View File

@ -325,7 +325,7 @@ def get_markets():
},
'price': 500000,
'cost': {
'min': 1,
'min': 0.0001,
'max': 500000,
},
},
@ -351,7 +351,7 @@ def get_markets():
},
'price': 500000,
'cost': {
'min': 1,
'min': 0.0001,
'max': 500000,
},
},
@ -376,7 +376,7 @@ def get_markets():
},
'price': 500000,
'cost': {
'min': 1,
'min': 0.0001,
'max': 500000,
},
},
@ -401,7 +401,7 @@ def get_markets():
},
'price': 500000,
'cost': {
'min': 1,
'min': 0.0001,
'max': 500000,
},
},
@ -426,7 +426,7 @@ def get_markets():
},
'price': 500000,
'cost': {
'min': 1,
'min': 0.0001,
'max': 500000,
},
},
@ -451,7 +451,7 @@ def get_markets():
},
'price': 500000,
'cost': {
'min': 1,
'min': 0.0001,
'max': 500000,
},
},
@ -479,7 +479,7 @@ def get_markets():
'max': None
},
'cost': {
'min': 0.001,
'min': 0.0001,
'max': None
}
},
@ -980,6 +980,28 @@ def tickers():
'quoteVolume': 62.68220262,
'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': {
'symbol': 'ETH/USDT',
'timestamp': 1522014804118,
@ -1067,7 +1089,29 @@ def tickers():
'baseVolume': 59698.79897,
'quoteVolume': 29132399.743954,
'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
},
'XRP': {
'total': 1.0,
'free': 1.0,
'total': 0.1,
'free': 0.01,
'used': 0.0
},
'EUR': {

View File

@ -100,7 +100,7 @@ def test_refresh_pairlist_dynamic(mocker, shitcoinmarkets, tickers, whitelist_co
markets=PropertyMock(return_value=shitcoinmarkets),
)
# 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()
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", [
([{"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
([{"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"}],
"USDT", ['ETH/USDT']),
# No pair for ETH ...
@ -146,19 +146,19 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
"ETH", []),
# Precisionfilter and quote volume
([{"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
([{"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
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"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.
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
([{"method": "VolumePairList", "number_assets": 6, "sort_key": "quoteVolume"},
{"method": "PrecisionFilter"},
{"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
([{"method": "StaticPairList"},
], "BTC", ['ETH/BTC', 'TKN/BTC']),

View File

@ -355,29 +355,18 @@ def test_rpc_balance_handle_error(default_conf, mocker):
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
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)
patch_get_signal(freqtradebot, (True, False))
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
result = rpc._rpc_balance(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
with pytest.raises(RPCException, match="Error getting current tickers."):
rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
def test_rpc_balance_handle(default_conf, mocker):
def test_rpc_balance_handle(default_conf, mocker, tickers):
mock_balance = {
'BTC': {
'free': 10.0,
@ -389,7 +378,7 @@ def test_rpc_balance_handle(default_conf, mocker):
'total': 5.0,
'used': 4.0,
},
'PAX': {
'USDT': {
'free': 5.0,
'total': 10.0,
'used': 5.0,
@ -405,10 +394,9 @@ def test_rpc_balance_handle(default_conf, mocker):
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value=mock_balance),
get_ticker=MagicMock(
side_effect=lambda p, r: {'bid': 100} if p == "BTC/PAX" else {'bid': 0.01}),
get_tickers=tickers,
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)
@ -416,30 +404,35 @@ def test_rpc_balance_handle(default_conf, mocker):
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
result = rpc._rpc_balance(default_conf['fiat_display_currency'])
assert prec_satoshi(result['total'], 12.15)
assert prec_satoshi(result['value'], 182250)
result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
assert prec_satoshi(result['total'], 12.309096315)
assert prec_satoshi(result['value'], 184636.44472997)
assert 'USD' == result['symbol']
assert result['currencies'] == [
{'currency': 'BTC',
'free': 10.0,
'balance': 12.0,
'used': 2.0,
'est_btc': 12.0,
'free': 10.0,
'balance': 12.0,
'used': 2.0,
'est_stake': 12.0,
'stake': 'BTC',
},
{'free': 1.0,
'balance': 5.0,
'currency': 'ETH',
'est_btc': 0.05,
'used': 4.0
'est_stake': 0.30794,
'used': 4.0,
'stake': 'BTC',
},
{'free': 5.0,
'balance': 10.0,
'currency': 'PAX',
'est_btc': 0.1,
'used': 5.0}
'currency': 'USDT',
'est_stake': 0.0011563153318162476,
'used': 5.0,
'stake': 'BTC',
}
]
assert result['total'] == 12.15
assert result['total'] == 12.309096315331816
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):
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_ticker', side_effect=mock_ticker)
mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination',
side_effect=lambda a, b: f"{a}/{b}")
ftbot.wallets.update()
rc = client_get(client, f"{BASE_URI}/balance")
assert_response(rc)
@ -262,7 +244,8 @@ def test_api_balance(botclient, mocker, rpc_balance):
'free': 12.0,
'balance': 12.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]
def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance) -> 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,
}
def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tickers) -> None:
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',
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,
'used': 0.5,
'balance': i,
'est_btc': 1
'est_stake': 1,
'stake': 'BTC',
})
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={
'currencies': balances,

View File

@ -299,7 +299,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
limit_buy_order, fee) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
default_conf['stake_amount'] = 0.0098751
default_conf['stake_amount'] = 0.00098751
default_conf['max_open_trades'] = 2
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
@ -313,7 +313,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker,
trade = Trade.query.first()
assert trade is not None
assert trade.stake_amount == 0.0098751
assert trade.stake_amount == 0.00098751
assert trade.is_open
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()
assert trade is not None
assert trade.stake_amount == 0.0098751
assert trade.stake_amount == 0.00098751
assert trade.is_open
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:
@ -334,6 +334,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.stoploss = -0.05
markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}}
# no pair found
mocker.patch(
'freqtrade.exchange.Exchange.markets',
@ -425,7 +426,7 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
PropertyMock(return_value=markets)
)
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)
markets["ETH/BTC"]["limits"] = {
@ -437,7 +438,27 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
PropertyMock(return_value=markets)
)
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: