Merge branch 'develop' into pr/dvdmchl/5929
This commit is contained in:
@@ -1026,6 +1026,12 @@ def test_create_dry_run_order_limit_fill(default_conf, mocker, side, startprice,
|
||||
assert order_closed['status'] == 'closed'
|
||||
assert order['fee']
|
||||
|
||||
# Empty orderbook test
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book',
|
||||
return_value={'asks': [], 'bids': []})
|
||||
exchange._dry_run_open_orders[order['id']]['status'] = 'open'
|
||||
order_closed = exchange.fetch_dry_run_order(order['id'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side,rate,amount,endprice", [
|
||||
# spread is 25.263-25.266
|
||||
@@ -1667,12 +1673,21 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
||||
assert len(res) == len(pairs)
|
||||
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 0
|
||||
exchange.required_candle_call_count = 1
|
||||
assert log_has(f"Using cached candle (OHLCV) data for pair {pairs[0][0]}, "
|
||||
f"timeframe {pairs[0][1]} ...",
|
||||
caplog)
|
||||
res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m'), ('XRP/ETH', '1d')],
|
||||
cache=False)
|
||||
assert len(res) == 3
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 3
|
||||
|
||||
# Test the same again, should NOT return from cache!
|
||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||
res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m'), ('XRP/ETH', '1d')],
|
||||
cache=False)
|
||||
assert len(res) == 3
|
||||
assert exchange._api_async.fetch_ohlcv.call_count == 3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -1768,7 +1783,7 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog):
|
||||
assert len(res) == 1
|
||||
# Test that each is in list at least once as order is not guaranteed
|
||||
assert log_has("Error loading ETH/BTC. Result was [[]].", caplog)
|
||||
assert log_has("Async code raised an exception: TypeError", caplog)
|
||||
assert log_has("Async code raised an exception: TypeError()", caplog)
|
||||
|
||||
|
||||
def test_get_next_limit_in_list():
|
||||
|
@@ -438,7 +438,8 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) ->
|
||||
Backtesting(default_conf)
|
||||
|
||||
default_conf['pairlists'] = [{"method": "VolumePairList", "number_assets": 5}]
|
||||
with pytest.raises(OperationalException, match='VolumePairList not allowed for backtesting.'):
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'VolumePairList not allowed for backtesting\..*StaticPairlist.*'):
|
||||
Backtesting(default_conf)
|
||||
|
||||
default_conf.update({
|
||||
@@ -470,7 +471,8 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
|
||||
default_conf['timerange'] = '20180101-20180102'
|
||||
|
||||
default_conf['pairlists'] = [{"method": "VolumePairList", "number_assets": 5}]
|
||||
with pytest.raises(OperationalException, match='VolumePairList not allowed for backtesting.'):
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'VolumePairList not allowed for backtesting\..*StaticPairlist.*'):
|
||||
Backtesting(default_conf)
|
||||
|
||||
default_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}]
|
||||
|
@@ -7,6 +7,7 @@ import pytest
|
||||
import time_machine
|
||||
|
||||
from freqtrade.constants import AVAILABLE_PAIRLISTS
|
||||
from freqtrade.enums.runmode import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
|
||||
@@ -657,6 +658,22 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
|
||||
assert log_has("PerformanceFilter is not available in this mode.", caplog)
|
||||
|
||||
|
||||
def test_ShuffleFilter_init(mocker, whitelist_conf, caplog) -> None:
|
||||
whitelist_conf['pairlists'] = [
|
||||
{"method": "StaticPairList"},
|
||||
{"method": "ShuffleFilter", "seed": 42}
|
||||
]
|
||||
|
||||
exchange = get_patched_exchange(mocker, whitelist_conf)
|
||||
PairListManager(exchange, whitelist_conf)
|
||||
assert log_has("Backtesting mode detected, applying seed value: 42", caplog)
|
||||
caplog.clear()
|
||||
whitelist_conf['runmode'] = RunMode.DRY_RUN
|
||||
PairListManager(exchange, whitelist_conf)
|
||||
assert not log_has("Backtesting mode detected, applying seed value: 42", caplog)
|
||||
assert log_has("Live mode detected, not applying seed.", caplog)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee, caplog) -> None:
|
||||
whitelist_conf['exchange']['pair_whitelist'].append('XRP/BTC')
|
||||
|
@@ -1093,7 +1093,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
||||
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
|
||||
rpc._rpc_forcebuy(pair, 0.0001)
|
||||
pair = 'XRP/BTC'
|
||||
trade = rpc._rpc_forcebuy(pair, 0.0001)
|
||||
trade = rpc._rpc_forcebuy(pair, 0.0001, order_type='limit')
|
||||
assert isinstance(trade, Trade)
|
||||
assert trade.pair == pair
|
||||
assert trade.open_rate == 0.0001
|
||||
|
@@ -538,6 +538,8 @@ def test_api_show_config(botclient):
|
||||
assert 'ask_strategy' in rc.json()
|
||||
assert 'unfilledtimeout' in rc.json()
|
||||
assert 'version' in rc.json()
|
||||
assert 'api_version' in rc.json()
|
||||
assert 1.1 <= rc.json()['api_version'] <= 1.2
|
||||
|
||||
|
||||
def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||
|
@@ -24,6 +24,7 @@ from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.loggers import setup_logging
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.rpc import RPCException
|
||||
from freqtrade.rpc.telegram import Telegram, authorized_only
|
||||
from tests.conftest import (create_mock_trades, get_patched_freqtradebot, log_has, log_has_re,
|
||||
patch_exchange, patch_get_signal, patch_whitelist)
|
||||
@@ -936,7 +937,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
|
||||
telegram._forcesell(update=update, context=context)
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
last_msg = msg_mock.call_args_list[-1][0][0]
|
||||
last_msg = msg_mock.call_args_list[-2][0][0]
|
||||
assert {
|
||||
'type': RPCMessageType.SELL,
|
||||
'trade_id': 1,
|
||||
@@ -1000,7 +1001,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
|
||||
last_msg = msg_mock.call_args_list[-1][0][0]
|
||||
last_msg = msg_mock.call_args_list[-2][0][0]
|
||||
assert {
|
||||
'type': RPCMessageType.SELL,
|
||||
'trade_id': 1,
|
||||
@@ -1054,7 +1055,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
|
||||
# Called for each trade 2 times
|
||||
assert msg_mock.call_count == 8
|
||||
msg = msg_mock.call_args_list[1][0][0]
|
||||
msg = msg_mock.call_args_list[0][0][0]
|
||||
assert {
|
||||
'type': RPCMessageType.SELL,
|
||||
'trade_id': 1,
|
||||
@@ -1186,8 +1187,8 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None:
|
||||
assert fbuy_mock.call_count == 1
|
||||
|
||||
|
||||
def test_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
def test_telegram_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -1216,8 +1217,8 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
||||
assert '<code>ETH/BTC\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_buy_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
@@ -1240,15 +1241,27 @@ def test_buy_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
telegram._buy_tag_performance(update=update, context=MagicMock())
|
||||
context = MagicMock()
|
||||
telegram._buy_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Buy Tag Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>TESTBUY\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
context.args = [trade.pair]
|
||||
telegram._buy_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 2
|
||||
|
||||
def test_sell_reason_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
msg_mock.reset_mock()
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_buy_tag_performance',
|
||||
side_effect=RPCException('Error'))
|
||||
telegram._buy_tag_performance(update=update, context=MagicMock())
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Error" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_telegram_sell_reason_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
@@ -1271,15 +1284,27 @@ def test_sell_reason_performance_handle(default_conf, update, ticker, fee,
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
telegram._sell_reason_performance(update=update, context=MagicMock())
|
||||
context = MagicMock()
|
||||
telegram._sell_reason_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Sell Reason Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>TESTSELL\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
context.args = [trade.pair]
|
||||
|
||||
telegram._sell_reason_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 2
|
||||
|
||||
msg_mock.reset_mock()
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_sell_reason_performance',
|
||||
side_effect=RPCException('Error'))
|
||||
telegram._sell_reason_performance(update=update, context=MagicMock())
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Error" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_mix_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
def test_telegram_mix_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
@@ -1305,12 +1330,25 @@ def test_mix_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
|
||||
telegram._mix_tag_performance(update=update, context=MagicMock())
|
||||
context = MagicMock()
|
||||
telegram._mix_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Mix Tag Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert ('<code>TESTBUY TESTSELL\t0.00006217 BTC (6.20%) (1)</code>'
|
||||
in msg_mock.call_args_list[0][0][0])
|
||||
|
||||
context.args = [trade.pair]
|
||||
telegram._mix_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 2
|
||||
|
||||
msg_mock.reset_mock()
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_mix_tag_performance',
|
||||
side_effect=RPCException('Error'))
|
||||
telegram._mix_tag_performance(update=update, context=MagicMock())
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Error" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
@@ -1851,6 +1889,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
|
@@ -292,3 +292,15 @@ def test__send_msg_with_json_format(default_conf, mocker, caplog):
|
||||
webhook._send_msg(msg)
|
||||
|
||||
assert post.call_args[1] == {'json': msg}
|
||||
|
||||
|
||||
def test__send_msg_with_raw_format(default_conf, mocker, caplog):
|
||||
default_conf["webhook"] = get_webhook_dict()
|
||||
default_conf["webhook"]["format"] = "raw"
|
||||
webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
|
||||
msg = {'data': 'Hello'}
|
||||
post = MagicMock()
|
||||
mocker.patch("freqtrade.rpc.webhook.post", post)
|
||||
webhook._send_msg(msg)
|
||||
|
||||
assert post.call_args[1] == {'data': msg['data'], 'headers': {'Content-Type': 'text/plain'}}
|
||||
|
@@ -20,7 +20,7 @@ class InformativeDecoratorTest(IStrategy):
|
||||
startup_candle_count: int = 20
|
||||
|
||||
def informative_pairs(self):
|
||||
return [('BTC/USDT', '5m')]
|
||||
return [('NEO/USDT', '5m')]
|
||||
|
||||
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
dataframe['buy'] = 0
|
||||
@@ -38,8 +38,8 @@ class InformativeDecoratorTest(IStrategy):
|
||||
return dataframe
|
||||
|
||||
# Simple informative test.
|
||||
@informative('1h', 'BTC/{stake}')
|
||||
def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
@informative('1h', 'NEO/{stake}')
|
||||
def populate_indicators_neo_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
dataframe['rsi'] = 14
|
||||
return dataframe
|
||||
|
||||
@@ -50,7 +50,7 @@ class InformativeDecoratorTest(IStrategy):
|
||||
return dataframe
|
||||
|
||||
# Formatting test.
|
||||
@informative('30m', 'BTC/{stake}', '{column}_{BASE}_{QUOTE}_{base}_{quote}_{asset}_{timeframe}')
|
||||
@informative('30m', 'NEO/{stake}', '{column}_{BASE}_{QUOTE}_{base}_{quote}_{asset}_{timeframe}')
|
||||
def populate_indicators_btc_1h_2(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
dataframe['rsi'] = 14
|
||||
return dataframe
|
||||
@@ -68,7 +68,7 @@ class InformativeDecoratorTest(IStrategy):
|
||||
dataframe['rsi_less'] = dataframe['rsi'] < dataframe['rsi_1h']
|
||||
|
||||
# Mixing manual informative pairs with decorators.
|
||||
informative = self.dp.get_pair_dataframe('BTC/USDT', '5m')
|
||||
informative = self.dp.get_pair_dataframe('NEO/USDT', '5m')
|
||||
informative['rsi'] = 14
|
||||
dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '5m', ffill=True)
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import pytest
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.strategy import (merge_informative_pair, stoploss_from_absolute, stoploss_from_open,
|
||||
timeframe_to_minutes)
|
||||
from tests.conftest import get_patched_exchange
|
||||
|
||||
|
||||
def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'):
|
||||
@@ -155,9 +156,9 @@ def test_informative_decorator(mocker, default_conf):
|
||||
('LTC/USDT', '5m'): test_data_5m,
|
||||
('LTC/USDT', '30m'): test_data_30m,
|
||||
('LTC/USDT', '1h'): test_data_1h,
|
||||
('BTC/USDT', '30m'): test_data_30m,
|
||||
('BTC/USDT', '5m'): test_data_5m,
|
||||
('BTC/USDT', '1h'): test_data_1h,
|
||||
('NEO/USDT', '30m'): test_data_30m,
|
||||
('NEO/USDT', '5m'): test_data_5m,
|
||||
('NEO/USDT', '1h'): test_data_1h,
|
||||
('ETH/USDT', '1h'): test_data_1h,
|
||||
('ETH/USDT', '30m'): test_data_30m,
|
||||
('ETH/BTC', '1h'): test_data_1h,
|
||||
@@ -165,15 +166,16 @@ def test_informative_decorator(mocker, default_conf):
|
||||
from .strats.informative_decorator_strategy import InformativeDecoratorTest
|
||||
default_conf['stake_currency'] = 'USDT'
|
||||
strategy = InformativeDecoratorTest(config=default_conf)
|
||||
strategy.dp = DataProvider({}, None, None)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
strategy.dp = DataProvider({}, exchange, None)
|
||||
mocker.patch.object(strategy.dp, 'current_whitelist', return_value=[
|
||||
'XRP/USDT', 'LTC/USDT', 'BTC/USDT'
|
||||
'XRP/USDT', 'LTC/USDT', 'NEO/USDT'
|
||||
])
|
||||
|
||||
assert len(strategy._ft_informative) == 6 # Equal to number of decorators used
|
||||
informative_pairs = [('XRP/USDT', '1h'), ('LTC/USDT', '1h'), ('XRP/USDT', '30m'),
|
||||
('LTC/USDT', '30m'), ('BTC/USDT', '1h'), ('BTC/USDT', '30m'),
|
||||
('BTC/USDT', '5m'), ('ETH/BTC', '1h'), ('ETH/USDT', '30m')]
|
||||
('LTC/USDT', '30m'), ('NEO/USDT', '1h'), ('NEO/USDT', '30m'),
|
||||
('NEO/USDT', '5m'), ('ETH/BTC', '1h'), ('ETH/USDT', '30m')]
|
||||
for inf_pair in informative_pairs:
|
||||
assert inf_pair in strategy.gather_informative_pairs()
|
||||
|
||||
@@ -186,8 +188,8 @@ def test_informative_decorator(mocker, default_conf):
|
||||
{p: data[(p, strategy.timeframe)] for p in ('XRP/USDT', 'LTC/USDT')})
|
||||
expected_columns = [
|
||||
'rsi_1h', 'rsi_30m', # Stacked informative decorators
|
||||
'btc_usdt_rsi_1h', # BTC 1h informative
|
||||
'rsi_BTC_USDT_btc_usdt_BTC/USDT_30m', # Column formatting
|
||||
'neo_usdt_rsi_1h', # NEO 1h informative
|
||||
'rsi_NEO_USDT_neo_usdt_NEO/USDT_30m', # Column formatting
|
||||
'rsi_from_callable', # Custom column formatter
|
||||
'eth_btc_rsi_1h', # Quote currency not matching stake currency
|
||||
'rsi', 'rsi_less', # Non-informative columns
|
||||
|
@@ -2979,7 +2979,7 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee,
|
||||
assert trade.close_profit == 0.09451372
|
||||
|
||||
assert rpc_mock.call_count == 3
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
last_msg = rpc_mock.call_args_list[-2][0][0]
|
||||
assert {
|
||||
'type': RPCMessageType.SELL,
|
||||
'trade_id': 1,
|
||||
|
@@ -67,6 +67,9 @@ def test_file_load_json(mocker, testdatadir) -> None:
|
||||
|
||||
@pytest.mark.parametrize("pair,expected_result", [
|
||||
("ETH/BTC", 'ETH_BTC'),
|
||||
("ETH/USDT", 'ETH_USDT'),
|
||||
("ETH/USDT:USDT", 'ETH_USDT_USDT'), # swap with USDT as settlement currency
|
||||
("ETH/USDT:USDT-210625", 'ETH_USDT_USDT_210625'), # expiring futures
|
||||
("Fabric Token/ETH", 'Fabric_Token_ETH'),
|
||||
("ETHH20", 'ETHH20'),
|
||||
(".XBTBON2H", '_XBTBON2H'),
|
||||
|
Reference in New Issue
Block a user