Merge branch 'freqtrade:develop' into RangeStabilityFilterMax

This commit is contained in:
sauces1313
2021-07-25 02:37:56 -05:00
committed by GitHub
83 changed files with 1766 additions and 423 deletions

View File

@@ -13,7 +13,7 @@ from freqtrade.commands import (start_convert_data, start_create_userdir, start_
start_list_data, start_list_exchanges, start_list_hyperopts,
start_list_markets, start_list_strategies, start_list_timeframes,
start_new_hyperopt, start_new_strategy, start_show_trades,
start_test_pairlist, start_trading)
start_test_pairlist, start_trading, start_webserver)
from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui,
get_ui_download_url, read_ui_version)
from freqtrade.configuration import setup_utils_configuration
@@ -26,7 +26,7 @@ from tests.conftest_trades import MOCK_TRADE_COUNT
def test_setup_utils_configuration():
args = [
'list-exchanges', '--config', 'config_bittrex.json.example',
'list-exchanges', '--config', 'config_examples/config_bittrex.example.json',
]
config = setup_utils_configuration(get_args(args), RunMode.OTHER)
@@ -45,7 +45,7 @@ def test_start_trading_fail(mocker, caplog):
exitmock = mocker.patch("freqtrade.worker.Worker.exit", MagicMock())
args = [
'trade',
'-c', 'config_bittrex.json.example'
'-c', 'config_examples/config_bittrex.example.json'
]
start_trading(get_args(args))
assert exitmock.call_count == 1
@@ -58,6 +58,18 @@ def test_start_trading_fail(mocker, caplog):
assert log_has('Fatal exception!', caplog)
def test_start_webserver(mocker, caplog):
api_server_mock = mocker.patch("freqtrade.rpc.api_server.ApiServer", )
args = [
'webserver',
'-c', 'config_examples/config_bittrex.example.json'
]
start_webserver(get_args(args))
assert api_server_mock.call_count == 1
def test_list_exchanges(capsys):
args = [
@@ -127,10 +139,10 @@ def test_list_timeframes(mocker, capsys):
match=r"This command requires a configured exchange.*"):
start_list_timeframes(pargs)
# Test with --config config_bittrex.json.example
# Test with --config config_examples/config_bittrex.example.json
args = [
"list-timeframes",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
]
start_list_timeframes(get_args(args))
captured = capsys.readouterr()
@@ -174,7 +186,7 @@ def test_list_timeframes(mocker, capsys):
# Test with --one-column
args = [
"list-timeframes",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--one-column",
]
start_list_timeframes(get_args(args))
@@ -214,10 +226,10 @@ def test_list_markets(mocker, markets, capsys):
match=r"This command requires a configured exchange.*"):
start_list_markets(pargs, False)
# Test with --config config_bittrex.json.example
# Test with --config config_examples/config_bittrex.example.json
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--print-list",
]
start_list_markets(get_args(args), False)
@@ -244,7 +256,7 @@ def test_list_markets(mocker, markets, capsys):
# Test with --all: all markets
args = [
"list-markets", "--all",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--print-list",
]
start_list_markets(get_args(args), False)
@@ -257,7 +269,7 @@ def test_list_markets(mocker, markets, capsys):
# Test list-pairs subcommand: active pairs
args = [
"list-pairs",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--print-list",
]
start_list_markets(get_args(args), True)
@@ -269,7 +281,7 @@ def test_list_markets(mocker, markets, capsys):
# Test list-pairs subcommand with --all: all pairs
args = [
"list-pairs", "--all",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--print-list",
]
start_list_markets(get_args(args), True)
@@ -282,7 +294,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=ETH, LTC
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "ETH", "LTC",
"--print-list",
]
@@ -295,7 +307,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "LTC",
"--print-list",
]
@@ -308,7 +320,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, quote=USDT, USD
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--quote", "USDT", "USD",
"--print-list",
]
@@ -321,7 +333,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, quote=USDT
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--quote", "USDT",
"--print-list",
]
@@ -334,7 +346,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC, quote=USDT
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "LTC", "--quote", "USDT",
"--print-list",
]
@@ -347,7 +359,7 @@ def test_list_markets(mocker, markets, capsys):
# active pairs, base=LTC, quote=USDT
args = [
"list-pairs",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "LTC", "--quote", "USD",
"--print-list",
]
@@ -360,7 +372,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC, quote=USDT, NONEXISTENT
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "LTC", "--quote", "USDT", "NONEXISTENT",
"--print-list",
]
@@ -373,7 +385,7 @@ def test_list_markets(mocker, markets, capsys):
# active markets, base=LTC, quote=NONEXISTENT
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "LTC", "--quote", "NONEXISTENT",
"--print-list",
]
@@ -386,7 +398,7 @@ def test_list_markets(mocker, markets, capsys):
# Test tabular output
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
]
start_list_markets(get_args(args), False)
captured = capsys.readouterr()
@@ -396,7 +408,7 @@ def test_list_markets(mocker, markets, capsys):
# Test tabular output, no markets found
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--base", "LTC", "--quote", "NONEXISTENT",
]
start_list_markets(get_args(args), False)
@@ -408,7 +420,7 @@ def test_list_markets(mocker, markets, capsys):
# Test --print-json
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--print-json"
]
start_list_markets(get_args(args), False)
@@ -420,7 +432,7 @@ def test_list_markets(mocker, markets, capsys):
# Test --print-csv
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--print-csv"
]
start_list_markets(get_args(args), False)
@@ -432,7 +444,7 @@ def test_list_markets(mocker, markets, capsys):
# Test --one-column
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--one-column"
]
start_list_markets(get_args(args), False)
@@ -444,7 +456,7 @@ def test_list_markets(mocker, markets, capsys):
# Test --one-column
args = [
"list-markets",
'--config', 'config_bittrex.json.example',
'--config', 'config_examples/config_bittrex.example.json',
"--one-column"
]
with pytest.raises(OperationalException, match=r"Cannot get markets.*"):
@@ -887,7 +899,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
patched_configuration_load_config_file(mocker, default_conf)
args = [
'test-pairlist',
'-c', 'config_bittrex.json.example'
'-c', 'config_examples/config_bittrex.example.json'
]
start_test_pairlist(get_args(args))
@@ -901,7 +913,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
args = [
'test-pairlist',
'-c', 'config_bittrex.json.example',
'-c', 'config_examples/config_bittrex.example.json',
'--one-column',
]
start_test_pairlist(get_args(args))
@@ -910,7 +922,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
args = [
'test-pairlist',
'-c', 'config_bittrex.json.example',
'-c', 'config_examples/config_bittrex.example.json',
'--print-json',
]
start_test_pairlist(get_args(args))

View File

@@ -1783,14 +1783,14 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': ask, 'last': last, 'bid': bid})
assert exchange.get_buy_rate('ETH/BTC', True) == expected
assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
assert exchange.get_buy_rate('ETH/BTC', False) == expected
assert exchange.get_rate('ETH/BTC', refresh=False, side="buy") == expected
assert log_has("Using cached buy rate for ETH/BTC.", caplog)
# Running a 2nd time with Refresh on!
caplog.clear()
assert exchange.get_buy_rate('ETH/BTC', True) == expected
assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
@@ -1825,12 +1825,12 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
# Test regular mode
exchange = get_patched_exchange(mocker, default_conf)
rate = exchange.get_sell_rate(pair, True)
rate = exchange.get_rate(pair, refresh=True, side="sell")
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float)
assert rate == expected
# Use caching
rate = exchange.get_sell_rate(pair, False)
rate = exchange.get_rate(pair, refresh=False, side="sell")
assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
@@ -1848,11 +1848,11 @@ def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, o
pair = "ETH/BTC"
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
exchange = get_patched_exchange(mocker, default_conf)
rate = exchange.get_sell_rate(pair, True)
rate = exchange.get_rate(pair, refresh=True, side="sell")
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float)
assert rate == expected
rate = exchange.get_sell_rate(pair, False)
rate = exchange.get_rate(pair, refresh=False, side="sell")
assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
@@ -1868,7 +1868,7 @@ def test_get_sell_rate_orderbook_exception(default_conf, mocker, caplog):
return_value={'bids': [[]], 'asks': [[]]})
exchange = get_patched_exchange(mocker, default_conf)
with pytest.raises(PricingError):
exchange.get_sell_rate(pair, True)
exchange.get_rate(pair, refresh=True, side="sell")
assert log_has_re(r"Sell Price at location 1 from orderbook could not be determined\..*",
caplog)
@@ -1881,18 +1881,18 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
return_value={'ask': None, 'bid': 0.12, 'last': None})
exchange = get_patched_exchange(mocker, default_conf)
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
exchange.get_sell_rate(pair, True)
exchange.get_rate(pair, refresh=True, side="sell")
exchange._config['ask_strategy']['price_side'] = 'bid'
assert exchange.get_sell_rate(pair, True) == 0.12
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.12
# Reverse sides
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': 0.13, 'bid': None, 'last': None})
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
exchange.get_sell_rate(pair, True)
exchange.get_rate(pair, refresh=True, side="sell")
exchange._config['ask_strategy']['price_side'] = 'ask'
assert exchange.get_sell_rate(pair, True) == 0.13
assert exchange.get_rate(pair, refresh=True, side="sell") == 0.13
def make_fetch_ohlcv_mock(data):
@@ -2203,7 +2203,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
({'status': 'canceled', 'filled': 10.0}, False),
({'status': 'unknown', 'filled': 10.0}, False),
({'result': 'testest123'}, False),
])
])
def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
assert exchange.check_order_canceled_empty(order) == result

View File

@@ -346,6 +346,20 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None:
assert processed['UNITTEST/BTC'].equals(processed2['UNITTEST/BTC'])
def test_backtest_abort(default_conf, mocker, testdatadir) -> None:
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
backtesting.check_abort()
backtesting.abort = True
with pytest.raises(DependencyException, match="Stop requested"):
backtesting.check_abort()
# abort flag resets
assert backtesting.abort is False
assert backtesting.progress.progress == 0
def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None:
def get_timerange(input1):
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
@@ -496,6 +510,17 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
trade = backtesting._enter_trade(pair, row=row)
assert trade is not None
backtesting.strategy.custom_stake_amount = lambda **kwargs: 123.5
trade = backtesting._enter_trade(pair, row=row)
assert trade
assert trade.stake_amount == 123.5
# In case of error - use proposed stake
backtesting.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
trade = backtesting._enter_trade(pair, row=row)
assert trade
assert trade.stake_amount == 495
# Stake-amount too high!
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0)

View File

@@ -79,7 +79,8 @@ def whitelist_conf_agefilter(default_conf):
},
{
"method": "AgeFilter",
"min_days_listed": 2
"min_days_listed": 2,
"max_days_listed": 100
}
]
return default_conf
@@ -302,7 +303,7 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
# No pair for ETH, all handlers
([{"method": "StaticPairList"},
{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "AgeFilter", "min_days_listed": 2},
{"method": "AgeFilter", "min_days_listed": 2, "max_days_listed": None},
{"method": "PrecisionFilter"},
{"method": "PriceFilter", "low_price_ratio": 0.03},
{"method": "SpreadFilter", "max_spread_ratio": 0.005},
@@ -310,12 +311,24 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
"ETH", []),
# AgeFilter and VolumePairList (require 2 days only, all should pass age test)
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "AgeFilter", "min_days_listed": 2}],
{"method": "AgeFilter", "min_days_listed": 2, "max_days_listed": 100}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC', 'HOT/BTC']),
# AgeFilter and VolumePairList (require 10 days, all should fail age test)
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "AgeFilter", "min_days_listed": 10}],
{"method": "AgeFilter", "min_days_listed": 10, "max_days_listed": None}],
"BTC", []),
# AgeFilter and VolumePairList (all pair listed > 2, all should fail age test)
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "AgeFilter", "min_days_listed": 1, "max_days_listed": 2}],
"BTC", []),
# AgeFilter and VolumePairList LTC/BTC has 6 candles - removes all
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "AgeFilter", "min_days_listed": 4, "max_days_listed": 5}],
"BTC", []),
# AgeFilter and VolumePairList LTC/BTC has 6 candles - passes
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "AgeFilter", "min_days_listed": 4, "max_days_listed": 10}],
"BTC", ["LTC/BTC"]),
# Precisionfilter and quote volume
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
{"method": "PrecisionFilter"}],
@@ -417,7 +430,19 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
([{"method": "StaticPairList"},
{"method": "VolatilityFilter", "lookback_days": 3,
"min_volatility": 0.002, "max_volatility": 0.004, "refresh_period": 1440}],
"BTC", ['ETH/BTC', 'TKN/BTC'])
"BTC", ['ETH/BTC', 'TKN/BTC']),
# VolumePairList with no offset = unchanged pairlist
([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"},
{"method": "OffsetFilter", "offset": 0}],
"USDT", ['ETH/USDT', 'NANO/USDT', 'ADAHALF/USDT', 'ADADOUBLE/USDT']),
# VolumePairList with offset = 2
([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"},
{"method": "OffsetFilter", "offset": 2}],
"USDT", ['ADAHALF/USDT', 'ADADOUBLE/USDT']),
# VolumePairList with higher offset, than total pairlist
([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"},
{"method": "OffsetFilter", "offset": 100}],
"USDT", [])
])
def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, tickers,
ohlcv_history, pairlists, base_currency,
@@ -431,7 +456,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
ohlcv_data = {
('ETH/BTC', '1d'): ohlcv_history,
('TKN/BTC', '1d'): ohlcv_history,
('LTC/BTC', '1d'): ohlcv_history,
('LTC/BTC', '1d'): ohlcv_history.append(ohlcv_history),
('XRP/BTC', '1d'): ohlcv_history,
('HOT/BTC', '1d'): ohlcv_history_high_vola,
}
@@ -480,9 +505,13 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
for pairlist in pairlists:
if pairlist['method'] == 'AgeFilter' and pairlist['min_days_listed'] and \
len(ohlcv_history) <= pairlist['min_days_listed']:
len(ohlcv_history) < pairlist['min_days_listed']:
assert log_has_re(r'^Removed .* from whitelist, because age .* is less than '
r'.* day.*', caplog)
if pairlist['method'] == 'AgeFilter' and pairlist['max_days_listed'] and \
len(ohlcv_history) > pairlist['max_days_listed']:
assert log_has_re(r'^Removed .* from whitelist, because age .* is less than '
r'.* day.* or more than .* day', caplog)
if pairlist['method'] == 'PrecisionFilter' and whitelist_result:
assert log_has_re(r'^Removed .* from whitelist, because stop price .* '
r'would be <= stop limit.*', caplog)
@@ -507,6 +536,105 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
assert log_has_re(r'^Removed .* from whitelist, because volatility.*$', caplog)
@pytest.mark.parametrize("pairlists,base_currency,volumefilter_result", [
# default refresh of 1800 to small for daily candle lookback
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
"lookback_days": 1}],
"BTC", "default_refresh_too_short"), # OperationalException expected
# ambigous configuration with lookback days and period
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
"lookback_days": 1, "lookback_period": 1}],
"BTC", "lookback_days_and_period"), # OperationalException expected
# negative lookback period
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
"lookback_timeframe": "1d", "lookback_period": -1}],
"BTC", "lookback_period_negative"), # OperationalException expected
# lookback range exceedes exchange limit
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
"lookback_timeframe": "1m", "lookback_period": 2000, "refresh_period": 3600}],
"BTC", 'lookback_exceeds_exchange_request_size'), # OperationalException expected
# expecing pairs as given
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
"lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
"BTC", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']),
# expecting pairs from default tickers, because 1h candles are not available
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
"lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}],
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']),
])
def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history,
pairlists, base_currency, volumefilter_result, caplog) -> None:
whitelist_conf['pairlists'] = pairlists
whitelist_conf['stake_currency'] = base_currency
ohlcv_history_high_vola = ohlcv_history.copy()
ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090
# create candles for medium overall volume with last candle high volume
ohlcv_history_medium_volume = ohlcv_history.copy()
ohlcv_history_medium_volume.loc[ohlcv_history_medium_volume.index == 2, 'volume'] = 5
# create candles for high volume with all candles high volume
ohlcv_history_high_volume = ohlcv_history.copy()
ohlcv_history_high_volume.loc[:, 'volume'] = 10
ohlcv_data = {
('ETH/BTC', '1d'): ohlcv_history,
('TKN/BTC', '1d'): ohlcv_history,
('LTC/BTC', '1d'): ohlcv_history_medium_volume,
('XRP/BTC', '1d'): ohlcv_history_high_vola,
('HOT/BTC', '1d'): ohlcv_history_high_volume,
}
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
if volumefilter_result == 'default_refresh_too_short':
with pytest.raises(OperationalException,
match=r'Refresh period of [0-9]+ seconds is smaller than one timeframe '
r'of [0-9]+.*\. Please adjust refresh_period to at least [0-9]+ '
r'and restart the bot\.'):
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
return
elif volumefilter_result == 'lookback_days_and_period':
with pytest.raises(OperationalException,
match=r'Ambigous configuration: lookback_days and lookback_period both '
r'set in pairlist config\..*'):
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
elif volumefilter_result == 'lookback_period_negative':
with pytest.raises(OperationalException,
match=r'VolumeFilter requires lookback_period to be >= 0'):
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
elif volumefilter_result == 'lookback_exceeds_exchange_request_size':
with pytest.raises(OperationalException,
match=r'VolumeFilter requires lookback_period to not exceed '
r'exchange max request size \([0-9]+\)'):
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
else:
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_tickers=tickers,
markets=PropertyMock(return_value=shitcoinmarkets)
)
# remove ohlcv when looback_timeframe != 1d
# to enforce fallback to ticker data
if 'lookback_timeframe' in pairlists[0]:
if pairlists[0]['lookback_timeframe'] != '1d':
ohlcv_data = []
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data),
)
freqtrade.pairlists.refresh_pairlist()
whitelist = freqtrade.pairlists.whitelist
assert isinstance(whitelist, list)
assert whitelist == volumefilter_result
def test_PrecisionFilter_error(mocker, whitelist_conf) -> None:
whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PrecisionFilter"}]
del whitelist_conf['stoploss']
@@ -650,6 +778,22 @@ def test_agefilter_min_days_listed_too_small(mocker, default_conf, markets, tick
get_patched_freqtradebot(mocker, default_conf)
def test_agefilter_max_days_lower_than_min_days(mocker, default_conf, markets, tickers):
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'AgeFilter', 'min_days_listed': 3,
"max_days_listed": 2}]
mocker.patch.multiple('freqtrade.exchange.Exchange',
markets=PropertyMock(return_value=markets),
exchange_has=MagicMock(return_value=True),
get_tickers=tickers
)
with pytest.raises(OperationalException,
match=r'AgeFilter max_days_listed <= min_days_listed not permitted'):
get_patched_freqtradebot(mocker, default_conf)
def test_agefilter_min_days_listed_too_large(mocker, default_conf, markets, tickers):
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'AgeFilter', 'min_days_listed': 99999}]
@@ -695,6 +839,18 @@ def test_agefilter_caching(mocker, markets, whitelist_conf_agefilter, tickers, o
assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count + 1
def test_OffsetFilter_error(mocker, whitelist_conf) -> None:
whitelist_conf['pairlists'] = (
[{"method": "StaticPairList"}, {"method": "OffsetFilter", "offset": -1}]
)
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
with pytest.raises(OperationalException,
match=r'OffsetFilter requires offset to be >= 0'):
PairListManager(MagicMock, whitelist_conf)
def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers):
default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10},
{'method': 'RangeStabilityFilter', 'lookback_days': 99999}]

View File

@@ -109,7 +109,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'exchange': 'binance',
}
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
results = rpc._rpc_trade_status()
assert isnan(results[0]['current_profit'])
@@ -217,7 +217,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert '-0.41% (-0.06)' == result[0][3]
assert '-0.06' == f'{fiat_profit_sum:.2f}'
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert 'instantly' == result[0][2]
@@ -427,7 +427,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert prec_satoshi(stats['best_rate'], 6.2)
# Test non-available pair
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert stats['trade_count'] == 2
@@ -886,7 +886,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
# Test not buying
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
freqtradebot.config['stake_amount'] = 0.0000001
freqtradebot.config['stake_amount'] = 0
patch_get_signal(freqtradebot, (True, False))
rpc = RPC(freqtradebot)
pair = 'TKN/BTC'

View File

@@ -2,6 +2,7 @@
Unit test file for rpc/api_server.py
"""
import json
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import ANY, MagicMock, PropertyMock
@@ -16,7 +17,7 @@ from requests.auth import _basic_auth_str
from freqtrade.__init__ import __version__
from freqtrade.enums import RunMode, State
from freqtrade.exceptions import ExchangeError
from freqtrade.exceptions import DependencyException, ExchangeError, OperationalException
from freqtrade.loggers import setup_logging, setup_logging_pre
from freqtrade.persistence import PairLocks, Trade
from freqtrade.rpc import RPC
@@ -48,9 +49,13 @@ def botclient(default_conf, mocker):
ftbot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(ftbot)
mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock())
apiserver = ApiServer(rpc, default_conf)
yield ftbot, TestClient(apiserver.app)
# Cleanup ... ?
try:
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(rpc)
yield ftbot, TestClient(apiserver.app)
# Cleanup ... ?
finally:
ApiServer.shutdown()
def client_post(client, url, data={}):
@@ -235,8 +240,13 @@ def test_api__init__(default_conf, mocker):
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.api_server.webserver.ApiServer.start_api', MagicMock())
apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
assert apiserver._config == default_conf
with pytest.raises(OperationalException, match="RPC Handler already attached."):
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
ApiServer.shutdown()
def test_api_UvicornServer(mocker):
@@ -298,15 +308,21 @@ def test_api_run(default_conf, mocker, caplog):
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
server_mock = MagicMock()
server_inst_mock = MagicMock()
server_inst_mock.run_in_thread = MagicMock()
server_inst_mock.run = MagicMock()
server_mock = MagicMock(return_value=server_inst_mock)
mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock)
apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
assert server_mock.call_count == 1
assert apiserver._config == default_conf
apiserver.start_api()
assert server_mock.call_count == 2
assert server_inst_mock.run_in_thread.call_count == 2
assert server_inst_mock.run.call_count == 0
assert server_mock.call_args_list[0][0][0].host == "127.0.0.1"
assert server_mock.call_args_list[0][0][0].port == 8080
assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI)
@@ -325,6 +341,8 @@ def test_api_run(default_conf, mocker, caplog):
apiserver.start_api()
assert server_mock.call_count == 1
assert server_inst_mock.run_in_thread.call_count == 1
assert server_inst_mock.run.call_count == 0
assert server_mock.call_args_list[0][0][0].host == "0.0.0.0"
assert server_mock.call_args_list[0][0][0].port == 8089
assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI)
@@ -338,12 +356,24 @@ def test_api_run(default_conf, mocker, caplog):
"Please make sure that this is intentional!", caplog)
assert log_has_re("SECURITY WARNING - `jwt_secret_key` seems to be default.*", caplog)
server_mock.reset_mock()
apiserver._standalone = True
apiserver.start_api()
assert server_inst_mock.run_in_thread.call_count == 0
assert server_inst_mock.run.call_count == 1
apiserver1 = ApiServer(default_conf)
assert id(apiserver1) == id(apiserver)
apiserver._standalone = False
# Test crashing API server
caplog.clear()
mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer',
MagicMock(side_effect=Exception))
apiserver.start_api()
assert log_has("Api server failed to start.", caplog)
ApiServer.shutdown()
def test_api_cleanup(default_conf, mocker, caplog):
@@ -359,11 +389,13 @@ def test_api_cleanup(default_conf, mocker, caplog):
server_mock.cleanup = MagicMock()
mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock)
apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
apiserver = ApiServer(default_conf)
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
apiserver.cleanup()
assert apiserver._server.cleanup.call_count == 1
assert log_has("Stopping API Server", caplog)
ApiServer.shutdown()
def test_api_reloadconf(botclient):
@@ -677,12 +709,16 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
'profit_all_ratio_mean': -0.6641100666666667,
'profit_all_percent_sum': -398.47,
'profit_all_ratio_sum': -3.9846604,
'profit_all_percent': -4.41,
'profit_all_ratio': -0.044063014216106644,
'profit_closed_coin': 0.00073913,
'profit_closed_fiat': 9.124559849999999,
'profit_closed_ratio_mean': 0.0075,
'profit_closed_percent_mean': 0.75,
'profit_closed_ratio_sum': 0.015,
'profit_closed_percent_sum': 1.5,
'profit_closed_ratio': 7.391275897987988e-07,
'profit_closed_percent': 0.0,
'trade_count': 6,
'closed_trade_count': 2,
'winning_trades': 2,
@@ -843,7 +879,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
'exchange': 'binance',
}
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
rc = client_get(client, f"{BASE_URI}/status")
@@ -1217,3 +1253,108 @@ def test_list_available_pairs(botclient):
assert rc.json()['length'] == 1
assert rc.json()['pairs'] == ['XRP/ETH']
assert len(rc.json()['pair_interval']) == 1
def test_api_backtesting(botclient, mocker, fee, caplog):
ftbot, client = botclient
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
# Backtesting not started yet
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'not_started'
assert not result['running']
assert result['status_msg'] == 'Backtest not yet executed'
assert result['progress'] == 0
# Reset backtesting
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'reset'
assert not result['running']
assert result['status_msg'] == 'Backtest reset'
# start backtesting
data = {
"strategy": "DefaultStrategy",
"timeframe": "5m",
"timerange": "20180110-20180111",
"max_open_trades": 3,
"stake_amount": 100,
"dry_run_wallet": 1000,
"enable_protections": False
}
rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data))
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
assert result['progress'] == 0
assert result['running']
assert result['status_msg'] == 'Backtest started'
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'ended'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
assert result['progress'] == 1
assert result['backtest_result']
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
assert result['status'] == 'not_running'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
# Simulate running backtest
ApiServer._bgtask_running = True
rc = client_get(client, f"{BASE_URI}/backtest/abort")
assert_response(rc)
result = rc.json()
assert result['status'] == 'stopping'
assert not result['running']
assert result['status_msg'] == 'Backtest ended'
# Get running backtest...
rc = client_get(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
assert result['running']
assert result['step'] == "backtest"
assert result['status_msg'] == "Backtest running"
# Try delete with task still running
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'running'
# Post to backtest that's still running
rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data))
assert_response(rc, 502)
result = rc.json()
assert 'Bot Background task already running' in result['error']
ApiServer._bgtask_running = False
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
side_effect=DependencyException())
rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data))
assert log_has("Backtesting caused an error: ", caplog)
# Delete backtesting to avoid leakage since the backtest-object may stick around.
rc = client_delete(client, f"{BASE_URI}/backtest")
assert_response(rc)
result = rc.json()
assert result['status'] == 'reset'
assert not result['running']
assert result['status_msg'] == 'Backtest reset'

View File

@@ -5,6 +5,7 @@ from unittest.mock import MagicMock
from freqtrade.enums import RPCMessageType
from freqtrade.rpc import RPCManager
from freqtrade.rpc.api_server.webserver import ApiServer
from tests.conftest import get_patched_freqtradebot, log_has
@@ -190,3 +191,4 @@ def test_init_apiserver_enabled(mocker, default_conf, caplog) -> None:
assert len(rpc_manager.registered_modules) == 1
assert 'apiserver' in [mod.name for mod in rpc_manager.registered_modules]
assert run_mock.call_count == 1
ApiServer.shutdown()

View File

@@ -452,7 +452,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
assert msg_mock.call_count == 1
assert 'No closed trade' in msg_mock.call_args_list[-1][0][0]
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
assert ('∙ `-0.00000500 BTC (-0.50%) (-0.5 \N{GREEK CAPITAL LETTER SIGMA}%)`'
mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=0.01)
assert ('∙ `-0.00000500 BTC (-0.50%) (-0.0 \N{GREEK CAPITAL LETTER SIGMA}%)`'
in msg_mock.call_args_list[-1][0][0])
msg_mock.reset_mock()
@@ -466,11 +467,11 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
telegram._profit(update=update, context=MagicMock())
assert msg_mock.call_count == 1
assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0]
assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`'
assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`'
in msg_mock.call_args_list[-1][0][0])
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`'
assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`'
in msg_mock.call_args_list[-1][0][0])
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]

View File

@@ -172,7 +172,7 @@ def test_download_data_options() -> None:
def test_plot_dataframe_options() -> None:
args = [
'plot-dataframe',
'-c', 'config_bittrex.json.example',
'-c', 'config_examples/config_bittrex.example.json',
'--indicators1', 'sma10', 'sma100',
'--indicators2', 'macd', 'fastd', 'fastk',
'--plot-limit', '30',

View File

@@ -28,7 +28,7 @@ from tests.conftest import log_has, log_has_re, patched_configuration_load_confi
@pytest.fixture(scope="function")
def all_conf():
config_file = Path(__file__).parents[1] / "config_full.json.example"
config_file = Path(__file__).parents[1] / "config_examples/config_full.example.json"
conf = load_config_file(str(config_file))
return conf

View File

@@ -161,7 +161,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
(True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]),
(True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]),
(True, 0.0022, 3, 1, [0.001, 0.001, 0.0]),
])
])
def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_open,
amend_last, wallet, max_open, lsamr, expected) -> None:
patch_RPCManager(mocker)
@@ -397,7 +397,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open,
def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order_open,
fee, mocker) -> None:
fee, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
buy_mock = MagicMock(return_value=limit_buy_order_open)
@@ -413,6 +413,27 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord
patch_get_signal(freqtrade)
assert freqtrade.create_trade('ETH/BTC')
assert log_has_re(r"Stake amount for pair .* is too small.*", caplog)
def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open,
fee, mocker) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
buy_mock = MagicMock(return_value=limit_buy_order_open)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
buy=buy_mock,
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf)
freqtrade.config['stake_amount'] = 0
patch_get_signal(freqtrade)
assert not freqtrade.create_trade('ETH/BTC')
@@ -763,7 +784,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
buy_mm = MagicMock(return_value=limit_buy_order_open)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_buy_rate=buy_rate_mock,
get_rate=buy_rate_mock,
fetch_ticker=MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
@@ -803,7 +824,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order_open['id'] = '33'
fix_price = 0.06
assert freqtrade.execute_buy(pair, stake_amount, fix_price)
# Make sure get_buy_rate wasn't called again
# Make sure get_rate wasn't called again
assert buy_rate_mock.call_count == 0
assert buy_mm.call_count == 2
@@ -842,6 +863,24 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
assert trade.open_rate == 0.5
assert trade.stake_amount == 40.495905365
# Test with custom stake
limit_buy_order['status'] = 'open'
limit_buy_order['id'] = '556'
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
assert freqtrade.execute_buy(pair, stake_amount)
trade = Trade.query.all()[4]
assert trade
assert trade.stake_amount == 150
# Exception case
limit_buy_order['id'] = '557'
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
assert freqtrade.execute_buy(pair, stake_amount)
trade = Trade.query.all()[5]
assert trade
assert trade.stake_amount == 2.0
# In case of the order is rejected and not filled at all
limit_buy_order['status'] = 'rejected'
limit_buy_order['amount'] = 90.99181073
@@ -854,7 +893,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
assert not freqtrade.execute_buy(pair, stake_amount)
# Fail to get price...
mocker.patch('freqtrade.exchange.Exchange.get_buy_rate', MagicMock(return_value=0.0))
mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0))
with pytest.raises(PricingError, match="Could not determine buy price."):
freqtrade.execute_buy(pair, stake_amount)
@@ -870,7 +909,7 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -
'last': 0.00001172
}),
buy=MagicMock(return_value=limit_buy_order),
get_buy_rate=MagicMock(return_value=0.11),
get_rate=MagicMock(return_value=0.11),
get_min_pair_stake_amount=MagicMock(return_value=1),
get_fee=fee,
)
@@ -2474,7 +2513,7 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None:
'freqtrade.exchange.Exchange',
cancel_order=cancel_order_mock,
)
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', return_value=0.245441)
mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.245441)
freqtrade = FreqtradeBot(default_conf)
@@ -3917,7 +3956,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o
def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
"""
test if function get_buy_rate will return the order book price
test if function get_rate will return the order book price
instead of the ask rate
"""
patch_exchange(mocker)
@@ -3935,7 +3974,7 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
default_conf['telegram']['enabled'] = False
freqtrade = FreqtradeBot(default_conf)
assert freqtrade.exchange.get_buy_rate('ETH/BTC', True) == 0.043935
assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935
assert ticker_mock.call_count == 0
@@ -3957,8 +3996,8 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None
freqtrade = FreqtradeBot(default_conf)
# orderbook shall be used even if tickers would be lower.
with pytest.raises(PricingError):
freqtrade.exchange.get_buy_rate('ETH/BTC', refresh=True)
assert log_has_re(r'Buy Price from orderbook could not be determined.', caplog)
freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy")
assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog)
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None:

View File

@@ -67,12 +67,12 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
args = ['trade', '-c', 'config_bittrex.json.example']
args = ['trade', '-c', 'config_examples/config_bittrex.example.json']
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Using config: config_bittrex.json.example ...', caplog)
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
assert log_has('Fatal exception!', caplog)
@@ -85,12 +85,12 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
args = ['trade', '-c', 'config_bittrex.json.example']
args = ['trade', '-c', 'config_examples/config_bittrex.example.json']
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Using config: config_bittrex.json.example ...', caplog)
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
assert log_has('SIGINT received, aborting ...', caplog)
@@ -106,12 +106,12 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
args = ['trade', '-c', 'config_bittrex.json.example']
args = ['trade', '-c', 'config_examples/config_bittrex.example.json']
# Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit):
main(args)
assert log_has('Using config: config_bittrex.json.example ...', caplog)
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
assert log_has('Oh snap!', caplog)
@@ -157,12 +157,16 @@ def test_main_reload_config(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
args = Arguments(['trade', '-c', 'config_bittrex.json.example']).get_parsed_arg()
args = Arguments([
'trade',
'-c',
'config_examples/config_bittrex.example.json'
]).get_parsed_arg()
worker = Worker(args=args, config=default_conf)
with pytest.raises(SystemExit):
main(['trade', '-c', 'config_bittrex.json.example'])
main(['trade', '-c', 'config_examples/config_bittrex.example.json'])
assert log_has('Using config: config_bittrex.json.example ...', caplog)
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
assert worker_mock.call_count == 4
assert reconfigure_mock.call_count == 1
assert isinstance(worker.freqtrade, FreqtradeBot)
@@ -180,7 +184,11 @@ def test_reconfigure(mocker, default_conf) -> None:
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
args = Arguments(['trade', '-c', 'config_bittrex.json.example']).get_parsed_arg()
args = Arguments([
'trade',
'-c',
'config_examples/config_bittrex.example.json'
]).get_parsed_arg()
worker = Worker(args=args, config=default_conf)
freqtrade = worker.freqtrade

View File

@@ -7,7 +7,7 @@ from unittest.mock import MagicMock
import pytest
from freqtrade.misc import (decimals_per_coin, file_dump_json, file_load_json, format_ms_time,
pair_to_filename, plural, render_template,
pair_to_filename, parse_db_uri_for_logging, plural, render_template,
render_template_with_fallback, round_coin_value, safe_value_fallback,
safe_value_fallback2, shorten_date)
@@ -179,3 +179,18 @@ def test_render_template_fallback(mocker):
)
assert isinstance(val, str)
assert 'if self.dp' in val
def test_parse_db_uri_for_logging() -> None:
postgresql_conn_uri = "postgresql+psycopg2://scott123:scott123@host/dbname"
mariadb_conn_uri = "mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company"
mysql_conn_uri = "mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4"
sqlite_conn_uri = "sqlite:////freqtrade/user_data/tradesv3.sqlite"
censored_pwd = "*****"
def get_pwd(x): return x.split(':')[2].split('@')[0]
assert get_pwd(parse_db_uri_for_logging(postgresql_conn_uri)) == censored_pwd
assert get_pwd(parse_db_uri_for_logging(mariadb_conn_uri)) == censored_pwd
assert get_pwd(parse_db_uri_for_logging(mysql_conn_uri)) == censored_pwd
assert sqlite_conn_uri == parse_db_uri_for_logging(sqlite_conn_uri)

View File

@@ -1124,6 +1124,21 @@ def test_total_open_trades_stakes(fee, use_db):
Trade.use_db = True
@pytest.mark.usefixtures("init_persistence")
@pytest.mark.parametrize('use_db', [True, False])
def test_get_total_closed_profit(fee, use_db):
Trade.use_db = use_db
Trade.reset_trades()
res = Trade.get_total_closed_profit()
assert res == 0
create_mock_trades(fee, use_db)
res = Trade.get_total_closed_profit()
assert res == 0.000739127
Trade.use_db = True
@pytest.mark.usefixtures("init_persistence")
@pytest.mark.parametrize('use_db', [True, False])
def test_get_trades_proxy(fee, use_db):
@@ -1298,6 +1313,7 @@ def test_Trade_object_idem():
'open_date',
'get_best_pair',
'get_overall_performance',
'get_total_closed_profit',
'total_open_trades_stakes',
'get_sold_trades_without_assigned_fees',
'get_open_trades_without_assigned_fees',

View File

@@ -364,7 +364,7 @@ def test_start_plot_dataframe(mocker):
aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock())
args = [
"plot-dataframe",
"--config", "config_bittrex.json.example",
"--config", "config_examples/config_bittrex.example.json",
"--pairs", "ETH/BTC"
]
start_plot_dataframe(get_args(args))
@@ -408,7 +408,7 @@ def test_start_plot_profit(mocker):
aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock())
args = [
"plot-profit",
"--config", "config_bittrex.json.example",
"--config", "config_examples/config_bittrex.example.json",
"--pairs", "ETH/BTC"
]
start_plot_profit(get_args(args))

View File

@@ -121,13 +121,19 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
freqtrade.wallets.get_trade_stake_amount('ETH/BTC')
@pytest.mark.parametrize("balance_ratio,result1,result2", [
(1, 50, 66.66666),
(0.99, 49.5, 66.0),
(0.50, 25, 33.3333),
@pytest.mark.parametrize("balance_ratio,capital,result1,result2", [
(1, None, 50, 66.66666),
(0.99, None, 49.5, 66.0),
(0.50, None, 25, 33.3333),
# Tests with capital ignore balance_ratio
(1, 100, 50, 0.0),
(0.99, 200, 50, 66.66666),
(0.99, 150, 50, 50),
(0.50, 50, 25, 0.0),
(0.50, 10, 5, 0.0),
])
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, result1,
result2, limit_buy_order_open,
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, capital,
result1, result2, limit_buy_order_open,
fee, mocker) -> None:
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
@@ -141,6 +147,8 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
conf['dry_run_wallet'] = 100
conf['max_open_trades'] = 2
conf['tradable_balance_ratio'] = balance_ratio
if capital is not None:
conf['available_capital'] = capital
freqtrade = get_patched_freqtradebot(mocker, conf)
@@ -170,3 +178,49 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
freqtrade.config['max_open_trades'] = 0
result = freqtrade.wallets.get_trade_stake_amount('NEO/USDT')
assert result == 0
@pytest.mark.parametrize('stake_amount,min_stake_amount,max_stake_amount,expected', [
(22, 11, 50, 22),
(100, 11, 500, 100),
(1000, 11, 500, 500), # Above max-stake
(20, 15, 10, 0), # Minimum stake > max-stake
(1, 11, 100, 11), # Below min stake
(1, 15, 10, 0), # Below min stake and min_stake > max_stake
])
def test__validate_stake_amount(mocker, default_conf,
stake_amount, min_stake_amount, max_stake_amount, expected):
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
return_value=max_stake_amount)
res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
assert res == expected
@pytest.mark.parametrize('available_capital,closed_profit,open_stakes,free,expected', [
(None, 10, 100, 910, 1000),
(None, 0, 0, 2500, 2500),
(None, 500, 0, 2500, 2000),
(None, 500, 0, 2500, 2000),
(None, -70, 0, 1930, 2000),
# Only available balance matters when it's set.
(100, 0, 0, 0, 100),
(1000, 0, 2, 5, 1000),
(1235, 2250, 2, 5, 1235),
(1235, -2250, 2, 5, 1235),
])
def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit,
open_stakes, free, expected):
if available_capital:
default_conf['available_capital'] = available_capital
mocker.patch("freqtrade.persistence.models.Trade.get_total_closed_profit",
return_value=closed_profit)
mocker.patch("freqtrade.persistence.models.Trade.total_open_trades_stakes",
return_value=open_stakes)
mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=free)
freqtrade = get_patched_freqtradebot(mocker, default_conf)
assert freqtrade.wallets.get_starting_balance() == expected