mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-02-19 19:01:06 +00:00
Merge branch 'develop' into feature_keyval_storage
This commit is contained in:
@@ -455,6 +455,13 @@ def test_calculate_max_drawdown2():
|
||||
with pytest.raises(ValueError, match='No losing trade, therefore no drawdown.'):
|
||||
calculate_max_drawdown(df, date_col='open_date', value_col='profit')
|
||||
|
||||
df1 = DataFrame(zip(values[:5], dates[:5]), columns=['profit', 'open_date'])
|
||||
df1.loc[:, 'profit'] = df1['profit'] * -1
|
||||
# No winning trade ...
|
||||
drawdown, hdate, ldate, hval, lval, drawdown_rel = calculate_max_drawdown(
|
||||
df1, date_col='open_date', value_col='profit')
|
||||
assert drawdown == 0.043965
|
||||
|
||||
|
||||
@pytest.mark.parametrize('profits,relative,highd,lowd,result,result_rel', [
|
||||
([0.0, -500.0, 500.0, 10000.0, -1000.0], False, 3, 4, 1000.0, 0.090909),
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.enums.marginmode import MarginMode
|
||||
from freqtrade.enums.tradingmode import TradingMode
|
||||
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has
|
||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||
|
||||
|
||||
def test_additional_exchange_init_bybit(default_conf, mocker):
|
||||
def test_additional_exchange_init_bybit(default_conf, mocker, caplog):
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['trading_mode'] = TradingMode.FUTURES
|
||||
default_conf['margin_mode'] = MarginMode.ISOLATED
|
||||
api_mock = MagicMock()
|
||||
api_mock.set_position_mode = MagicMock(return_value={"dualSidePosition": False})
|
||||
get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
|
||||
api_mock.is_unified_enabled = MagicMock(return_value=[False, False])
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
|
||||
assert api_mock.set_position_mode.call_count == 1
|
||||
assert api_mock.is_unified_enabled.call_count == 1
|
||||
assert exchange.unified_account is False
|
||||
|
||||
assert log_has("Bybit: Standard account.", caplog)
|
||||
|
||||
api_mock.set_position_mode.reset_mock()
|
||||
api_mock.is_unified_enabled = MagicMock(return_value=[False, True])
|
||||
with pytest.raises(OperationalException, match=r"Bybit: Unified account is not supported.*"):
|
||||
get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
|
||||
assert log_has("Bybit: Unified account.", caplog)
|
||||
# exchange = get_patched_exchange(mocker, default_conf, id="bybit", api_mock=api_mock)
|
||||
# assert api_mock.set_position_mode.call_count == 1
|
||||
# assert api_mock.is_unified_enabled.call_count == 1
|
||||
# assert exchange.unified_account is True
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'bybit',
|
||||
"additional_exchange_init", "set_position_mode")
|
||||
|
||||
@@ -111,6 +131,7 @@ def test_bybit_fetch_order_canceled_empty(default_conf_usdt, mocker):
|
||||
'amount': 20.0,
|
||||
})
|
||||
|
||||
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, id='bybit')
|
||||
|
||||
res = exchange.fetch_order('123', 'BTC/USDT')
|
||||
|
||||
@@ -2303,6 +2303,66 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach
|
||||
assert res[pair2].at[0, 'open']
|
||||
|
||||
|
||||
def test_refresh_ohlcv_with_cache(mocker, default_conf, time_machine) -> None:
|
||||
start = datetime(2021, 8, 1, 0, 0, 0, 0, tzinfo=timezone.utc)
|
||||
ohlcv = generate_test_data_raw('1h', 100, start.strftime('%Y-%m-%d'))
|
||||
time_machine.move_to(start, tick=False)
|
||||
pairs = [
|
||||
('ETH/BTC', '1d', CandleType.SPOT),
|
||||
('TKN/BTC', '1d', CandleType.SPOT),
|
||||
('LTC/BTC', '1d', CandleType.SPOT),
|
||||
('LTC/BTC', '5m', CandleType.SPOT),
|
||||
('LTC/BTC', '1h', CandleType.SPOT),
|
||||
]
|
||||
|
||||
ohlcv_data = {
|
||||
p: ohlcv for p in pairs
|
||||
}
|
||||
ohlcv_mock = mocker.patch(f"{EXMS}.refresh_latest_ohlcv", return_value=ohlcv_data)
|
||||
mocker.patch(f"{EXMS}.ohlcv_candle_limit", return_value=100)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
assert len(exchange._expiring_candle_cache) == 0
|
||||
|
||||
res = exchange.refresh_ohlcv_with_cache(pairs, start.timestamp())
|
||||
assert ohlcv_mock.call_count == 1
|
||||
assert ohlcv_mock.call_args_list[0][0][0] == pairs
|
||||
assert len(ohlcv_mock.call_args_list[0][0][0]) == 5
|
||||
|
||||
assert len(res) == 5
|
||||
# length of 3 - as we have 3 different timeframes
|
||||
assert len(exchange._expiring_candle_cache) == 3
|
||||
|
||||
ohlcv_mock.reset_mock()
|
||||
res = exchange.refresh_ohlcv_with_cache(pairs, start.timestamp())
|
||||
assert ohlcv_mock.call_count == 0
|
||||
|
||||
# Expire 5m cache
|
||||
time_machine.move_to(start + timedelta(minutes=6), tick=False)
|
||||
|
||||
ohlcv_mock.reset_mock()
|
||||
res = exchange.refresh_ohlcv_with_cache(pairs, start.timestamp())
|
||||
assert ohlcv_mock.call_count == 1
|
||||
assert len(ohlcv_mock.call_args_list[0][0][0]) == 1
|
||||
|
||||
# Expire 5m and 1h cache
|
||||
time_machine.move_to(start + timedelta(hours=2), tick=False)
|
||||
|
||||
ohlcv_mock.reset_mock()
|
||||
res = exchange.refresh_ohlcv_with_cache(pairs, start.timestamp())
|
||||
assert ohlcv_mock.call_count == 1
|
||||
assert len(ohlcv_mock.call_args_list[0][0][0]) == 2
|
||||
|
||||
# Expire all caches
|
||||
time_machine.move_to(start + timedelta(days=1, hours=2), tick=False)
|
||||
|
||||
ohlcv_mock.reset_mock()
|
||||
res = exchange.refresh_ohlcv_with_cache(pairs, start.timestamp())
|
||||
assert ohlcv_mock.call_count == 1
|
||||
assert len(ohlcv_mock.call_args_list[0][0][0]) == 5
|
||||
assert ohlcv_mock.call_args_list[0][0][0] == pairs
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_name):
|
||||
ohlcv = [
|
||||
@@ -3177,6 +3237,7 @@ def test_is_cancel_order_result_suitable(mocker, default_conf, exchange_name, or
|
||||
def test_cancel_order_with_result(default_conf, mocker, exchange_name, corder,
|
||||
call_corder, call_forder):
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
|
||||
api_mock = MagicMock()
|
||||
api_mock.cancel_order = MagicMock(return_value=corder)
|
||||
api_mock.fetch_order = MagicMock(return_value={})
|
||||
@@ -3190,6 +3251,7 @@ def test_cancel_order_with_result(default_conf, mocker, exchange_name, corder,
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_cancel_order_with_result_error(default_conf, mocker, exchange_name, caplog):
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
|
||||
api_mock = MagicMock()
|
||||
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
|
||||
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order"))
|
||||
@@ -3287,6 +3349,7 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
|
||||
order.myid = 123
|
||||
order.symbol = 'TKN/BTC'
|
||||
|
||||
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
exchange._dry_run_open_orders['X'] = order
|
||||
assert exchange.fetch_order('X', 'TKN/BTC').myid == 123
|
||||
@@ -3331,10 +3394,80 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_fetch_order_emulated(default_conf, mocker, exchange_name, caplog):
|
||||
default_conf['dry_run'] = True
|
||||
default_conf['exchange']['log_responses'] = True
|
||||
order = MagicMock()
|
||||
order.myid = 123
|
||||
order.symbol = 'TKN/BTC'
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
mocker.patch(f'{EXMS}.exchange_has', return_value=False)
|
||||
exchange._dry_run_open_orders['X'] = order
|
||||
# Dry run - regular fetch_order behavior
|
||||
assert exchange.fetch_order('X', 'TKN/BTC').myid == 123
|
||||
|
||||
with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'):
|
||||
exchange.fetch_order('Y', 'TKN/BTC')
|
||||
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch(f'{EXMS}.exchange_has', return_value=False)
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_open_order = MagicMock(
|
||||
return_value={'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'})
|
||||
api_mock.fetch_closed_order = MagicMock(
|
||||
return_value={'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert exchange.fetch_order(
|
||||
'X', 'TKN/BTC') == {'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'}
|
||||
assert log_has(
|
||||
("API fetch_open_order: {\'id\': \'123\', \'amount\': 2, \'symbol\': \'TKN/BTC\'}"
|
||||
),
|
||||
caplog
|
||||
)
|
||||
assert api_mock.fetch_open_order.call_count == 1
|
||||
assert api_mock.fetch_closed_order.call_count == 0
|
||||
caplog.clear()
|
||||
|
||||
# open_order doesn't find order
|
||||
api_mock.fetch_open_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found"))
|
||||
api_mock.fetch_closed_order = MagicMock(
|
||||
return_value={'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert exchange.fetch_order(
|
||||
'X', 'TKN/BTC') == {'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'}
|
||||
assert log_has(
|
||||
("API fetch_closed_order: {\'id\': \'123\', \'amount\': 2, \'symbol\': \'TKN/BTC\'}"
|
||||
),
|
||||
caplog
|
||||
)
|
||||
assert api_mock.fetch_open_order.call_count == 1
|
||||
assert api_mock.fetch_closed_order.call_count == 1
|
||||
caplog.clear()
|
||||
|
||||
with pytest.raises(InvalidOrderException):
|
||||
api_mock.fetch_open_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
|
||||
api_mock.fetch_closed_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.fetch_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.fetch_open_order.call_count == 1
|
||||
|
||||
api_mock.fetch_open_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found"))
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'fetch_order_emulated', 'fetch_open_order',
|
||||
retries=1,
|
||||
order_id='_', pair='TKN/BTC', params={})
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
|
||||
default_conf['dry_run'] = True
|
||||
mocker.patch(f"{EXMS}.exchange_has", return_value=True)
|
||||
order = MagicMock()
|
||||
order.myid = 123
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
|
||||
@@ -324,7 +324,8 @@ def get_futures_exchange(exchange_name, exchange_conf, class_mocker):
|
||||
|
||||
|
||||
@pytest.fixture(params=EXCHANGES, scope="class")
|
||||
def exchange(request, exchange_conf):
|
||||
def exchange(request, exchange_conf, class_mocker):
|
||||
class_mocker.patch('freqtrade.exchange.bybit.Bybit.additional_exchange_init')
|
||||
yield from get_exchange(request.param, exchange_conf)
|
||||
|
||||
|
||||
|
||||
@@ -1400,6 +1400,8 @@ def test_to_json(fee):
|
||||
'is_open': None,
|
||||
'open_date': trade.open_date.strftime(DATETIME_PRINT_FORMAT),
|
||||
'open_timestamp': int(trade.open_date.timestamp() * 1000),
|
||||
'open_fill_date': None,
|
||||
'open_fill_timestamp': None,
|
||||
'close_date': None,
|
||||
'close_timestamp': None,
|
||||
'open_rate': 0.123,
|
||||
@@ -1486,6 +1488,8 @@ def test_to_json(fee):
|
||||
'quote_currency': 'BTC',
|
||||
'open_date': trade.open_date.strftime(DATETIME_PRINT_FORMAT),
|
||||
'open_timestamp': int(trade.open_date.timestamp() * 1000),
|
||||
'open_fill_date': None,
|
||||
'open_fill_timestamp': None,
|
||||
'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT),
|
||||
'close_timestamp': int(trade.close_date.timestamp() * 1000),
|
||||
'open_rate': 0.123,
|
||||
|
||||
@@ -223,7 +223,7 @@ def test_trade_serialize_load_back(fee):
|
||||
'realized_profit_ratio', 'close_profit_pct',
|
||||
'trade_duration_s', 'trade_duration',
|
||||
'profit_ratio', 'profit_pct', 'profit_abs', 'stop_loss_abs',
|
||||
'initial_stop_loss_abs',
|
||||
'initial_stop_loss_abs', 'open_fill_date', 'open_fill_timestamp',
|
||||
'orders',
|
||||
]
|
||||
failed = []
|
||||
|
||||
@@ -621,13 +621,20 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1d", "lookback_period": 6, "refresh_period": 86400}],
|
||||
"BTC", "binance", ['LTC/BTC', 'XRP/BTC', 'ETH/BTC', 'HOT/BTC', 'NEO/BTC']),
|
||||
# VolumePairlist in range mode as filter.
|
||||
# TKN/BTC is removed because it doesn't have enough candles
|
||||
([{"method": "VolumePairList", "number_assets": 5},
|
||||
{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1d", "lookback_period": 2, "refresh_period": 86400}],
|
||||
"BTC", "binance", ['LTC/BTC', 'XRP/BTC', 'ETH/BTC', 'TKN/BTC', 'HOT/BTC']),
|
||||
# ftx data is already in Quote currency, therefore won't require conversion
|
||||
# ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
# "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
|
||||
# "BTC", "ftx", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']),
|
||||
])
|
||||
def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history,
|
||||
pairlists, base_currency, exchange, volumefilter_result) -> None:
|
||||
def test_VolumePairList_range(
|
||||
mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history,
|
||||
pairlists, base_currency, exchange, volumefilter_result, time_machine) -> None:
|
||||
whitelist_conf['pairlists'] = pairlists
|
||||
whitelist_conf['stake_currency'] = base_currency
|
||||
whitelist_conf['exchange']['name'] = exchange
|
||||
@@ -686,23 +693,36 @@ def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers,
|
||||
get_tickers=tickers,
|
||||
markets=PropertyMock(return_value=shitcoinmarkets)
|
||||
)
|
||||
|
||||
start_dt = dt_now()
|
||||
time_machine.move_to(start_dt)
|
||||
# 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 = []
|
||||
ohlcv_data = {}
|
||||
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data),
|
||||
)
|
||||
ohclv_mock = mocker.patch(f"{EXMS}.refresh_latest_ohlcv", return_value=ohlcv_data)
|
||||
|
||||
freqtrade.pairlists.refresh_pairlist()
|
||||
whitelist = freqtrade.pairlists.whitelist
|
||||
assert ohclv_mock.call_count == 1
|
||||
|
||||
assert isinstance(whitelist, list)
|
||||
assert whitelist == volumefilter_result
|
||||
# Test caching
|
||||
ohclv_mock.reset_mock()
|
||||
freqtrade.pairlists.refresh_pairlist()
|
||||
# in "filter" mode, caching is disabled.
|
||||
assert ohclv_mock.call_count == 0
|
||||
whitelist = freqtrade.pairlists.whitelist
|
||||
assert whitelist == volumefilter_result
|
||||
|
||||
time_machine.move_to(start_dt + timedelta(days=2))
|
||||
ohclv_mock.reset_mock()
|
||||
freqtrade.pairlists.refresh_pairlist()
|
||||
assert ohclv_mock.call_count == 1
|
||||
whitelist = freqtrade.pairlists.whitelist
|
||||
assert whitelist == volumefilter_result
|
||||
|
||||
|
||||
def test_PrecisionFilter_error(mocker, whitelist_conf) -> None:
|
||||
|
||||
@@ -25,6 +25,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'quote_currency': 'BTC',
|
||||
'open_date': ANY,
|
||||
'open_timestamp': ANY,
|
||||
'open_fill_date': ANY,
|
||||
'open_fill_timestamp': ANY,
|
||||
'is_open': ANY,
|
||||
'fee_open': ANY,
|
||||
'fee_open_cost': ANY,
|
||||
|
||||
@@ -180,7 +180,9 @@ def test_api_auth():
|
||||
|
||||
def test_api_ws_auth(botclient):
|
||||
ftbot, client = botclient
|
||||
def url(token): return f"/api/v1/message/ws?token={token}"
|
||||
|
||||
def url(token):
|
||||
return f"/api/v1/message/ws?token={token}"
|
||||
|
||||
bad_token = "bad-ws_token"
|
||||
with pytest.raises(WebSocketDisconnect):
|
||||
@@ -1165,6 +1167,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
|
||||
'current_rate': current_rate,
|
||||
'open_date': ANY,
|
||||
'open_timestamp': ANY,
|
||||
'open_fill_date': ANY,
|
||||
'open_fill_timestamp': ANY,
|
||||
'open_rate': 0.123,
|
||||
'pair': 'ETH/BTC',
|
||||
'base_currency': 'ETH',
|
||||
@@ -1368,6 +1372,8 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
|
||||
'close_rate': 0.265441,
|
||||
'open_date': ANY,
|
||||
'open_timestamp': ANY,
|
||||
'open_fill_date': ANY,
|
||||
'open_fill_timestamp': ANY,
|
||||
'open_rate': 0.245441,
|
||||
'pair': 'ETH/BTC',
|
||||
'base_currency': 'ETH',
|
||||
|
||||
@@ -1507,7 +1507,7 @@ async def test_telegram_entry_tag_performance_handle(
|
||||
await telegram._enter_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Entry Tag Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>TEST1\t3.987 USDT (5.00%) (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
assert '`TEST1\t3.987 USDT (5.00%) (1)`' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
context.args = ['XRP/USDT']
|
||||
await telegram._enter_tag_performance(update=update, context=context)
|
||||
@@ -1538,7 +1538,7 @@ async def test_telegram_exit_reason_performance_handle(
|
||||
await telegram._exit_reason_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Exit Reason Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>roi\t2.842 USDT (10.00%) (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
assert '`roi\t2.842 USDT (10.00%) (1)`' in msg_mock.call_args_list[0][0][0]
|
||||
context.args = ['XRP/USDT']
|
||||
|
||||
await telegram._exit_reason_performance(update=update, context=context)
|
||||
@@ -1570,7 +1570,7 @@ async def test_telegram_mix_tag_performance_handle(default_conf_usdt, update, ti
|
||||
await 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>TEST3 roi\t2.842 USDT (10.00%) (1)</code>'
|
||||
assert ('`TEST3 roi\t2.842 USDT (10.00%) (1)`'
|
||||
in msg_mock.call_args_list[0][0][0])
|
||||
|
||||
context.args = ['XRP/USDT']
|
||||
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timedelta, timezone
|
||||
import pytest
|
||||
import time_machine
|
||||
|
||||
from freqtrade.util import (dt_floor_day, dt_from_ts, dt_humanize, dt_now, dt_ts, dt_ts_def, dt_utc,
|
||||
format_date, format_ms_time, shorten_date)
|
||||
from freqtrade.util import (dt_floor_day, dt_from_ts, dt_humanize, dt_now, dt_ts, dt_ts_def,
|
||||
dt_ts_none, dt_utc, format_date, format_ms_time, shorten_date)
|
||||
|
||||
|
||||
def test_dt_now():
|
||||
@@ -29,6 +29,13 @@ def test_dt_ts_def():
|
||||
assert dt_ts_def(datetime(2023, 5, 5, tzinfo=timezone.utc), 123) == 1683244800000
|
||||
|
||||
|
||||
def test_dt_ts_none():
|
||||
assert dt_ts_none(None) is None
|
||||
assert dt_ts_none(None) is None
|
||||
assert dt_ts_none(datetime(2023, 5, 5, tzinfo=timezone.utc)) == 1683244800000
|
||||
assert dt_ts_none(datetime(2023, 5, 5, tzinfo=timezone.utc)) == 1683244800000
|
||||
|
||||
|
||||
def test_dt_utc():
|
||||
assert dt_utc(2023, 5, 5) == datetime(2023, 5, 5, tzinfo=timezone.utc)
|
||||
assert dt_utc(2023, 5, 5, 0, 0, 0, 555500) == datetime(2023, 5, 5, 0, 0, 0, 555500,
|
||||
|
||||
@@ -5,7 +5,7 @@ from freqtrade.util import PeriodicCache
|
||||
|
||||
def test_ttl_cache():
|
||||
|
||||
with time_machine.travel("2021-09-01 05:00:00 +00:00") as t:
|
||||
with time_machine.travel("2021-09-01 05:00:00 +00:00", tick=False) as t:
|
||||
|
||||
cache = PeriodicCache(5, ttl=60)
|
||||
cache1h = PeriodicCache(5, ttl=3600)
|
||||
|
||||
Reference in New Issue
Block a user