Merge branch 'develop' into backtest_live_models
This commit is contained in:
@@ -15,7 +15,7 @@ from freqtrade.data.history.idatahandler import IDataHandler, get_datahandler, g
|
||||
from freqtrade.data.history.jsondatahandler import JsonDataHandler, JsonGzDataHandler
|
||||
from freqtrade.data.history.parquetdatahandler import ParquetDataHandler
|
||||
from freqtrade.enums import CandleType, TradingMode
|
||||
from tests.conftest import log_has
|
||||
from tests.conftest import log_has, log_has_re
|
||||
|
||||
|
||||
def test_datahandler_ohlcv_get_pairs(testdatadir):
|
||||
@@ -154,6 +154,85 @@ def test_jsondatahandler_ohlcv_load(testdatadir, caplog):
|
||||
assert df.columns.equals(df1.columns)
|
||||
|
||||
|
||||
def test_datahandler__check_empty_df(testdatadir, caplog):
|
||||
dh = JsonDataHandler(testdatadir)
|
||||
expected_text = r"Price jump in UNITTEST/USDT, 1h, spot between"
|
||||
df = DataFrame([
|
||||
[
|
||||
1511686200000, # 8:50:00
|
||||
8.794, # open
|
||||
8.948, # high
|
||||
8.794, # low
|
||||
8.88, # close
|
||||
2255, # volume (in quote currency)
|
||||
],
|
||||
[
|
||||
1511686500000, # 8:55:00
|
||||
8.88,
|
||||
8.942,
|
||||
8.88,
|
||||
8.893,
|
||||
9911,
|
||||
],
|
||||
[
|
||||
1511687100000, # 9:05:00
|
||||
8.891,
|
||||
8.893,
|
||||
8.875,
|
||||
8.877,
|
||||
2251
|
||||
],
|
||||
[
|
||||
1511687400000, # 9:10:00
|
||||
8.877,
|
||||
8.883,
|
||||
8.895,
|
||||
8.817,
|
||||
123551
|
||||
]
|
||||
], columns=['date', 'open', 'high', 'low', 'close', 'volume'])
|
||||
|
||||
dh._check_empty_df(df, 'UNITTEST/USDT', '1h', CandleType.SPOT, True, True)
|
||||
assert not log_has_re(expected_text, caplog)
|
||||
df = DataFrame([
|
||||
[
|
||||
1511686200000, # 8:50:00
|
||||
8.794, # open
|
||||
8.948, # high
|
||||
8.794, # low
|
||||
8.88, # close
|
||||
2255, # volume (in quote currency)
|
||||
],
|
||||
[
|
||||
1511686500000, # 8:55:00
|
||||
8.88,
|
||||
8.942,
|
||||
8.88,
|
||||
8.893,
|
||||
9911,
|
||||
],
|
||||
[
|
||||
1511687100000, # 9:05:00
|
||||
889.1, # Price jump by several decimals
|
||||
889.3,
|
||||
887.5,
|
||||
887.7,
|
||||
2251
|
||||
],
|
||||
[
|
||||
1511687400000, # 9:10:00
|
||||
8.877,
|
||||
8.883,
|
||||
8.895,
|
||||
8.817,
|
||||
123551
|
||||
]
|
||||
], columns=['date', 'open', 'high', 'low', 'close', 'volume'])
|
||||
|
||||
dh._check_empty_df(df, 'UNITTEST/USDT', '1h', CandleType.SPOT, True, True)
|
||||
assert log_has_re(expected_text, caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('datahandler', ['feather', 'parquet'])
|
||||
def test_datahandler_trades_not_supported(datahandler, testdatadir, ):
|
||||
dh = get_datahandler(testdatadir, datahandler)
|
||||
|
@@ -162,9 +162,6 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
|
||||
}
|
||||
assert exchange.stoploss_adjust(sl1, order, side=side)
|
||||
assert not exchange.stoploss_adjust(sl2, order, side=side)
|
||||
# Test with invalid order case
|
||||
order['type'] = 'stop_loss'
|
||||
assert not exchange.stoploss_adjust(sl3, order, side=side)
|
||||
|
||||
|
||||
def test_fill_leverage_tiers_binance(default_conf, mocker):
|
||||
|
@@ -113,5 +113,4 @@ def test_stoploss_adjust_huobi(mocker, default_conf):
|
||||
assert exchange.stoploss_adjust(1501, order, 'sell')
|
||||
assert not exchange.stoploss_adjust(1499, order, 'sell')
|
||||
# Test with invalid order case
|
||||
order['type'] = 'stop_loss'
|
||||
assert not exchange.stoploss_adjust(1501, order, 'sell')
|
||||
assert exchange.stoploss_adjust(1501, order, 'sell')
|
||||
|
@@ -130,7 +130,8 @@ def test_normalize_data(mocker, freqai_conf):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
data_dict = freqai.dk.data_dictionary
|
||||
freqai.dk.normalize_data(data_dict)
|
||||
assert len(freqai.dk.data) == 32
|
||||
assert any('_max' in entry for entry in freqai.dk.data.keys())
|
||||
assert any('_min' in entry for entry in freqai.dk.data.keys())
|
||||
|
||||
|
||||
def test_filter_features(mocker, freqai_conf):
|
||||
|
@@ -27,13 +27,13 @@ def is_mac() -> bool:
|
||||
return "Darwin" in machine
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model', [
|
||||
'LightGBMRegressor',
|
||||
'XGBoostRegressor',
|
||||
'XGBoostRFRegressor',
|
||||
'CatboostRegressor',
|
||||
@pytest.mark.parametrize('model, pca, dbscan', [
|
||||
('LightGBMRegressor', True, False),
|
||||
('XGBoostRegressor', False, True),
|
||||
('XGBoostRFRegressor', False, False),
|
||||
('CatboostRegressor', False, False),
|
||||
])
|
||||
def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model):
|
||||
def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca, dbscan):
|
||||
if is_arm() and model == 'CatboostRegressor':
|
||||
pytest.skip("CatBoost is not supported on ARM")
|
||||
|
||||
@@ -41,6 +41,8 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model):
|
||||
freqai_conf.update({"freqaimodel": model})
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.update({"strategy": "freqai_test_strat"})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"principal_component_analysis": pca})
|
||||
freqai_conf['freqai']['feature_parameters'].update({"use_DBSCAN_to_remove_outliers": dbscan})
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
@@ -234,6 +236,7 @@ def test_start_backtesting_subdaily_backtest_period(mocker, freqai_conf):
|
||||
metadata = {"pair": "LTC/BTC"}
|
||||
freqai.start_backtesting(df, metadata, freqai.dk)
|
||||
model_folders = [x for x in freqai.dd.full_path.iterdir() if x.is_dir()]
|
||||
|
||||
assert len(model_folders) == 9
|
||||
|
||||
shutil.rmtree(Path(freqai.dk.full_path))
|
||||
|
0
tests/persistence/__init__.py
Normal file
0
tests/persistence/__init__.py
Normal file
@@ -2404,7 +2404,7 @@ def test_Trade_object_idem():
|
||||
'get_enter_tag_performance',
|
||||
'get_mix_tag_performance',
|
||||
'get_trading_volume',
|
||||
|
||||
'from_json',
|
||||
)
|
||||
EXCLUDES2 = ('trades', 'trades_open', 'bt_trades_open_pp', 'bt_open_open_trade_count',
|
||||
'total_profit')
|
181
tests/persistence/test_trade_fromjson.py
Normal file
181
tests/persistence/test_trade_fromjson.py
Normal file
@@ -0,0 +1,181 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from freqtrade.persistence.trade_model import Trade
|
||||
|
||||
|
||||
def test_trade_fromjson():
|
||||
"""Test the Trade.from_json() method."""
|
||||
trade_string = """{
|
||||
"trade_id": 25,
|
||||
"pair": "ETH/USDT",
|
||||
"base_currency": "ETH",
|
||||
"quote_currency": "USDT",
|
||||
"is_open": false,
|
||||
"exchange": "binance",
|
||||
"amount": 407.0,
|
||||
"amount_requested": 102.92547026,
|
||||
"stake_amount": 102.7494348,
|
||||
"strategy": "SampleStrategy55",
|
||||
"buy_tag": "Strategy2",
|
||||
"enter_tag": "Strategy2",
|
||||
"timeframe": 5,
|
||||
"fee_open": 0.001,
|
||||
"fee_open_cost": 0.1027494,
|
||||
"fee_open_currency": "ETH",
|
||||
"fee_close": 0.001,
|
||||
"fee_close_cost": 0.1054944,
|
||||
"fee_close_currency": "USDT",
|
||||
"open_date": "2022-10-18 09:12:42",
|
||||
"open_timestamp": 1666084362912,
|
||||
"open_rate": 0.2518998249562391,
|
||||
"open_rate_requested": 0.2516,
|
||||
"open_trade_value": 102.62575199,
|
||||
"close_date": "2022-10-18 09:45:22",
|
||||
"close_timestamp": 1666086322208,
|
||||
"realized_profit": 2.76315361,
|
||||
"close_rate": 0.2592,
|
||||
"close_rate_requested": 0.2592,
|
||||
"close_profit": 0.026865,
|
||||
"close_profit_pct": 2.69,
|
||||
"close_profit_abs": 2.76315361,
|
||||
"trade_duration_s": 1959,
|
||||
"trade_duration": 32,
|
||||
"profit_ratio": 0.02686,
|
||||
"profit_pct": 2.69,
|
||||
"profit_abs": 2.76315361,
|
||||
"sell_reason": "no longer good",
|
||||
"exit_reason": "no longer good",
|
||||
"exit_order_status": "closed",
|
||||
"stop_loss_abs": 0.1981,
|
||||
"stop_loss_ratio": -0.216,
|
||||
"stop_loss_pct": -21.6,
|
||||
"stoploss_order_id": null,
|
||||
"stoploss_last_update": null,
|
||||
"stoploss_last_update_timestamp": null,
|
||||
"initial_stop_loss_abs": 0.1981,
|
||||
"initial_stop_loss_ratio": -0.216,
|
||||
"initial_stop_loss_pct": -21.6,
|
||||
"min_rate": 0.2495,
|
||||
"max_rate": 0.2592,
|
||||
"leverage": 1.0,
|
||||
"interest_rate": 0.0,
|
||||
"liquidation_price": null,
|
||||
"is_short": false,
|
||||
"trading_mode": "spot",
|
||||
"funding_fees": 0.0,
|
||||
"open_order_id": null,
|
||||
"orders": [
|
||||
{
|
||||
"amount": 102.0,
|
||||
"safe_price": 0.2526,
|
||||
"ft_order_side": "buy",
|
||||
"order_filled_timestamp": 1666084370887,
|
||||
"ft_is_entry": true,
|
||||
"pair": "ETH/USDT",
|
||||
"order_id": "78404228",
|
||||
"status": "closed",
|
||||
"average": 0.2526,
|
||||
"cost": 25.7652,
|
||||
"filled": 102.0,
|
||||
"is_open": false,
|
||||
"order_date": "2022-10-18 09:12:42",
|
||||
"order_timestamp": 1666084362684,
|
||||
"order_filled_date": "2022-10-18 09:12:50",
|
||||
"order_type": "limit",
|
||||
"price": 0.2526,
|
||||
"remaining": 0.0
|
||||
},
|
||||
{
|
||||
"amount": 102.0,
|
||||
"safe_price": 0.2517,
|
||||
"ft_order_side": "buy",
|
||||
"order_filled_timestamp": 1666084379056,
|
||||
"ft_is_entry": true,
|
||||
"pair": "ETH/USDT",
|
||||
"order_id": "78405139",
|
||||
"status": "closed",
|
||||
"average": 0.2517,
|
||||
"cost": 25.6734,
|
||||
"filled": 102.0,
|
||||
"is_open": false,
|
||||
"order_date": "2022-10-18 09:12:57",
|
||||
"order_timestamp": 1666084377681,
|
||||
"order_filled_date": "2022-10-18 09:12:59",
|
||||
"order_type": "limit",
|
||||
"price": 0.2517,
|
||||
"remaining": 0.0
|
||||
},
|
||||
{
|
||||
"amount": 102.0,
|
||||
"safe_price": 0.2517,
|
||||
"ft_order_side": "buy",
|
||||
"order_filled_timestamp": 1666084389644,
|
||||
"ft_is_entry": true,
|
||||
"pair": "ETH/USDT",
|
||||
"order_id": "78405265",
|
||||
"status": "closed",
|
||||
"average": 0.2517,
|
||||
"cost": 25.6734,
|
||||
"filled": 102.0,
|
||||
"is_open": false,
|
||||
"order_date": "2022-10-18 09:13:03",
|
||||
"order_timestamp": 1666084383295,
|
||||
"order_filled_date": "2022-10-18 09:13:09",
|
||||
"order_type": "limit",
|
||||
"price": 0.2517,
|
||||
"remaining": 0.0
|
||||
},
|
||||
{
|
||||
"amount": 102.0,
|
||||
"safe_price": 0.2516,
|
||||
"ft_order_side": "buy",
|
||||
"order_filled_timestamp": 1666084723521,
|
||||
"ft_is_entry": true,
|
||||
"pair": "ETH/USDT",
|
||||
"order_id": "78405395",
|
||||
"status": "closed",
|
||||
"average": 0.2516,
|
||||
"cost": 25.6632,
|
||||
"filled": 102.0,
|
||||
"is_open": false,
|
||||
"order_date": "2022-10-18 09:13:13",
|
||||
"order_timestamp": 1666084393920,
|
||||
"order_filled_date": "2022-10-18 09:18:43",
|
||||
"order_type": "limit",
|
||||
"price": 0.2516,
|
||||
"remaining": 0.0
|
||||
},
|
||||
{
|
||||
"amount": 407.0,
|
||||
"safe_price": 0.2592,
|
||||
"ft_order_side": "sell",
|
||||
"order_filled_timestamp": 1666086322198,
|
||||
"ft_is_entry": false,
|
||||
"pair": "ETH/USDT",
|
||||
"order_id": "78432649",
|
||||
"status": "closed",
|
||||
"average": 0.2592,
|
||||
"cost": 105.4944,
|
||||
"filled": 407.0,
|
||||
"is_open": false,
|
||||
"order_date": "2022-10-18 09:45:21",
|
||||
"order_timestamp": 1666086321435,
|
||||
"order_filled_date": "2022-10-18 09:45:22",
|
||||
"order_type": "market",
|
||||
"price": 0.2592,
|
||||
"remaining": 0.0
|
||||
}
|
||||
]
|
||||
}"""
|
||||
trade = Trade.from_json(trade_string)
|
||||
|
||||
assert trade.id == 25
|
||||
assert trade.pair == 'ETH/USDT'
|
||||
assert trade.open_date == datetime(2022, 10, 18, 9, 12, 42, tzinfo=timezone.utc)
|
||||
assert isinstance(trade.open_date, datetime)
|
||||
assert trade.exit_reason == 'no longer good'
|
||||
|
||||
assert len(trade.orders) == 5
|
||||
last_o = trade.orders[-1]
|
||||
assert last_o.order_filled_date == datetime(2022, 10, 18, 9, 45, 22, tzinfo=timezone.utc)
|
||||
assert isinstance(last_o.order_date, datetime)
|
@@ -2,6 +2,8 @@
|
||||
|
||||
import logging
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pandas as pd
|
||||
@@ -719,15 +721,26 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
|
||||
def test_ShuffleFilter_init(mocker, whitelist_conf, caplog) -> None:
|
||||
whitelist_conf['pairlists'] = [
|
||||
{"method": "StaticPairList"},
|
||||
{"method": "ShuffleFilter", "seed": 42}
|
||||
{"method": "ShuffleFilter", "seed": 43}
|
||||
]
|
||||
|
||||
exchange = get_patched_exchange(mocker, whitelist_conf)
|
||||
PairListManager(exchange, whitelist_conf)
|
||||
assert log_has("Backtesting mode detected, applying seed value: 42", caplog)
|
||||
plm = PairListManager(exchange, whitelist_conf)
|
||||
assert log_has("Backtesting mode detected, applying seed value: 43", caplog)
|
||||
|
||||
with time_machine.travel("2021-09-01 05:01:00 +00:00") as t:
|
||||
plm.refresh_pairlist()
|
||||
pl1 = deepcopy(plm.whitelist)
|
||||
plm.refresh_pairlist()
|
||||
assert plm.whitelist == pl1
|
||||
|
||||
t.shift(timedelta(minutes=10))
|
||||
plm.refresh_pairlist()
|
||||
assert plm.whitelist != pl1
|
||||
|
||||
caplog.clear()
|
||||
whitelist_conf['runmode'] = RunMode.DRY_RUN
|
||||
PairListManager(exchange, whitelist_conf)
|
||||
plm = 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)
|
||||
|
||||
|
@@ -3969,15 +3969,17 @@ def test__safe_exit_amount(default_conf_usdt, fee, caplog, mocker, amount_wallet
|
||||
patch_get_signal(freqtrade)
|
||||
if has_err:
|
||||
with pytest.raises(DependencyException, match=r"Not enough amount to exit trade."):
|
||||
assert freqtrade._safe_exit_amount(trade.pair, trade.amount)
|
||||
assert freqtrade._safe_exit_amount(trade, trade.pair, trade.amount)
|
||||
else:
|
||||
wallet_update.reset_mock()
|
||||
assert freqtrade._safe_exit_amount(trade.pair, trade.amount) == amount_wallet
|
||||
assert trade.amount != amount_wallet
|
||||
assert freqtrade._safe_exit_amount(trade, trade.pair, trade.amount) == amount_wallet
|
||||
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||
assert trade.amount == amount_wallet
|
||||
assert wallet_update.call_count == 1
|
||||
caplog.clear()
|
||||
wallet_update.reset_mock()
|
||||
assert freqtrade._safe_exit_amount(trade.pair, amount_wallet) == amount_wallet
|
||||
assert freqtrade._safe_exit_amount(trade, trade.pair, amount_wallet) == amount_wallet
|
||||
assert not log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||
assert wallet_update.call_count == 1
|
||||
|
||||
|
@@ -420,7 +420,7 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
assert trade.open_order_id is None
|
||||
# Open rate is not adjusted yet
|
||||
assert trade.open_rate == 1.99
|
||||
assert trade.stake_amount == 60
|
||||
assert pytest.approx(trade.stake_amount) == 60
|
||||
assert trade.stop_loss_pct == -0.1
|
||||
assert pytest.approx(trade.stop_loss) == 1.99 * (1 - 0.1 / leverage)
|
||||
assert pytest.approx(trade.initial_stop_loss) == 1.99 * (1 - 0.1 / leverage)
|
||||
@@ -446,7 +446,7 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
assert len(trade.orders) == 4
|
||||
assert trade.open_order_id is not None
|
||||
assert trade.open_rate == 1.99
|
||||
assert trade.stake_amount == 60
|
||||
assert pytest.approx(trade.stake_amount) == 60
|
||||
assert trade.orders[-1].price == 1.95
|
||||
assert pytest.approx(trade.orders[-1].cost) == 120 * leverage
|
||||
|
||||
|
@@ -1,7 +1,10 @@
|
||||
import logging
|
||||
import time
|
||||
from datetime import timedelta
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import time_machine
|
||||
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.enums import State
|
||||
from freqtrade.worker import Worker
|
||||
@@ -59,13 +62,58 @@ def test_throttle(mocker, default_conf, caplog) -> None:
|
||||
end = time.time()
|
||||
|
||||
assert result == 42
|
||||
assert end - start > 0.1
|
||||
assert 0.3 > end - start > 0.1
|
||||
assert log_has_re(r"Throttling with 'throttled_func\(\)': sleep for \d\.\d{2} s.*", caplog)
|
||||
|
||||
result = worker._throttle(throttled_func, throttle_secs=-1)
|
||||
assert result == 42
|
||||
|
||||
|
||||
def test_throttle_sleep_time(mocker, default_conf, caplog) -> None:
|
||||
|
||||
caplog.set_level(logging.DEBUG)
|
||||
worker = get_patched_worker(mocker, default_conf)
|
||||
sleep_mock = mocker.patch("freqtrade.worker.Worker._sleep")
|
||||
with time_machine.travel("2022-09-01 05:00:00 +00:00") as t:
|
||||
def throttled_func(x=1):
|
||||
t.shift(timedelta(seconds=x))
|
||||
return 42
|
||||
|
||||
assert worker._throttle(throttled_func, throttle_secs=5) == 42
|
||||
# This moves the clock by 1 second
|
||||
assert sleep_mock.call_count == 1
|
||||
assert 3.8 < sleep_mock.call_args[0][0] < 4.1
|
||||
|
||||
sleep_mock.reset_mock()
|
||||
# This moves the clock by 1 second
|
||||
assert worker._throttle(throttled_func, throttle_secs=10) == 42
|
||||
assert sleep_mock.call_count == 1
|
||||
assert 8.8 < sleep_mock.call_args[0][0] < 9.1
|
||||
|
||||
sleep_mock.reset_mock()
|
||||
# This moves the clock by 5 second, so we only throttle by 5s
|
||||
assert worker._throttle(throttled_func, throttle_secs=10, x=5) == 42
|
||||
assert sleep_mock.call_count == 1
|
||||
assert 4.8 < sleep_mock.call_args[0][0] < 5.1
|
||||
|
||||
t.move_to("2022-09-01 05:01:00 +00:00")
|
||||
sleep_mock.reset_mock()
|
||||
# Throttle for more than 5m (1 timeframe)
|
||||
assert worker._throttle(throttled_func, throttle_secs=400, x=5) == 42
|
||||
assert sleep_mock.call_count == 1
|
||||
assert 394.8 < sleep_mock.call_args[0][0] < 395.1
|
||||
|
||||
t.move_to("2022-09-01 05:01:00 +00:00")
|
||||
|
||||
sleep_mock.reset_mock()
|
||||
# Throttle for more than 5m (1 timeframe)
|
||||
assert worker._throttle(throttled_func, throttle_secs=400, timeframe='5m',
|
||||
timeframe_offset=0.4, x=5) == 42
|
||||
assert sleep_mock.call_count == 1
|
||||
# 300 (5m) - 60 (1m - see set time above) - 5 (duration of throttled_func) = 235
|
||||
assert 235.2 < sleep_mock.call_args[0][0] < 235.6
|
||||
|
||||
|
||||
def test_throttle_with_assets(mocker, default_conf) -> None:
|
||||
def throttled_func(nb_assets=-1):
|
||||
return nb_assets
|
||||
|
Reference in New Issue
Block a user