Merge branch 'develop' into feat/pairlistconfig
This commit is contained in:
+9
-2
@@ -181,7 +181,7 @@ def get_patched_exchange(mocker, config, api_mock=None, id='binance',
|
||||
patch_exchange(mocker, api_mock, id, mock_markets, mock_supported_modes)
|
||||
config['exchange']['name'] = id
|
||||
try:
|
||||
exchange = ExchangeResolver.load_exchange(id, config, load_leverage_tiers=True)
|
||||
exchange = ExchangeResolver.load_exchange(config, load_leverage_tiers=True)
|
||||
except ImportError:
|
||||
exchange = Exchange(config)
|
||||
return exchange
|
||||
@@ -411,6 +411,14 @@ def patch_gc(mocker) -> None:
|
||||
mocker.patch("freqtrade.main.gc_set_threshold")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def user_dir(mocker, tmpdir) -> Path:
|
||||
user_dir = Path(tmpdir) / "user_data"
|
||||
mocker.patch('freqtrade.configuration.configuration.create_userdata_dir',
|
||||
return_value=user_dir)
|
||||
return user_dir
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def patch_coingekko(mocker) -> None:
|
||||
"""
|
||||
@@ -485,7 +493,6 @@ def get_default_conf(testdatadir):
|
||||
},
|
||||
"exchange": {
|
||||
"name": "binance",
|
||||
"enabled": True,
|
||||
"key": "key",
|
||||
"secret": "secret",
|
||||
"pair_whitelist": [
|
||||
|
||||
@@ -18,8 +18,9 @@ def entryexitanalysis_cleanup() -> None:
|
||||
Backtesting.cleanup()
|
||||
|
||||
|
||||
def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, tmpdir, capsys):
|
||||
def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, user_dir, capsys):
|
||||
caplog.set_level(logging.INFO)
|
||||
(user_dir / 'backtest_results').mkdir(parents=True, exist_ok=True)
|
||||
|
||||
default_conf.update({
|
||||
"use_exit_signal": True,
|
||||
@@ -80,7 +81,7 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, tmp
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--datadir', str(testdatadir),
|
||||
'--user-data-dir', str(tmpdir),
|
||||
'--user-data-dir', str(user_dir),
|
||||
'--timeframe', '5m',
|
||||
'--timerange', '1515560100-1517287800',
|
||||
'--export', 'signals',
|
||||
@@ -98,7 +99,7 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, tmp
|
||||
'backtesting-analysis',
|
||||
'--config', 'config.json',
|
||||
'--datadir', str(testdatadir),
|
||||
'--user-data-dir', str(tmpdir),
|
||||
'--user-data-dir', str(user_dir),
|
||||
]
|
||||
|
||||
# test group 0 and indicator list
|
||||
@@ -200,8 +201,17 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, tmp
|
||||
assert 'trailing_stop_loss' in captured.out
|
||||
|
||||
# test date filtering
|
||||
args = get_args(base_args + ['--timerange', "20180129-20180130"])
|
||||
args = get_args(base_args +
|
||||
['--analysis-groups', "0", "1", "2",
|
||||
'--timerange', "20180129-20180130"]
|
||||
)
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert 'enter_tag_long_a' in captured.out
|
||||
assert 'enter_tag_long_b' not in captured.out
|
||||
|
||||
# Due to the backtest mock, there's no rejected signals generated.
|
||||
args = get_args(base_args + ['--rejected-signals'])
|
||||
start_analysis_entries_exits(args)
|
||||
captured = capsys.readouterr()
|
||||
assert 'no rejected signals' in captured.out
|
||||
|
||||
@@ -302,7 +302,7 @@ def exchange(request, exchange_conf):
|
||||
exchange_conf, EXCHANGES[request.param].get('use_ci_proxy', False))
|
||||
exchange_conf['exchange']['name'] = request.param
|
||||
exchange_conf['stake_currency'] = EXCHANGES[request.param]['stake_currency']
|
||||
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
|
||||
exchange = ExchangeResolver.load_exchange(exchange_conf, validate=True)
|
||||
|
||||
yield exchange, request.param
|
||||
|
||||
@@ -330,7 +330,7 @@ def exchange_futures(request, exchange_conf, class_mocker):
|
||||
class_mocker.patch(f'{EXMS}.cache_leverage_tiers')
|
||||
|
||||
exchange = ExchangeResolver.load_exchange(
|
||||
request.param, exchange_conf, validate=True, load_leverage_tiers=True)
|
||||
exchange_conf, validate=True, load_leverage_tiers=True)
|
||||
|
||||
yield exchange, request.param
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from freqtrade.exchange import (Binance, Bittrex, Exchange, Kraken, amount_to_pr
|
||||
timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date,
|
||||
timeframe_to_prev_date, timeframe_to_seconds)
|
||||
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, API_RETRY_COUNT,
|
||||
calculate_backoff, remove_credentials)
|
||||
calculate_backoff, remove_exchange_credentials)
|
||||
from freqtrade.exchange.exchange import amount_to_contract_precision
|
||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||
from tests.conftest import (EXMS, generate_test_data_raw, get_mock_coro, get_patched_exchange,
|
||||
@@ -137,16 +137,14 @@ def test_init(default_conf, mocker, caplog):
|
||||
assert log_has('Instance is running with dry_run enabled', caplog)
|
||||
|
||||
|
||||
def test_remove_credentials(default_conf, caplog) -> None:
|
||||
def test_remove_exchange_credentials(default_conf) -> None:
|
||||
conf = deepcopy(default_conf)
|
||||
conf['dry_run'] = False
|
||||
remove_credentials(conf)
|
||||
remove_exchange_credentials(conf['exchange'], False)
|
||||
|
||||
assert conf['exchange']['key'] != ''
|
||||
assert conf['exchange']['secret'] != ''
|
||||
|
||||
conf['dry_run'] = True
|
||||
remove_credentials(conf)
|
||||
remove_exchange_credentials(conf['exchange'], True)
|
||||
assert conf['exchange']['key'] == ''
|
||||
assert conf['exchange']['secret'] == ''
|
||||
assert conf['exchange']['password'] == ''
|
||||
@@ -228,27 +226,30 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
||||
mocker.patch(f'{EXMS}.validate_timeframes')
|
||||
mocker.patch(f'{EXMS}.validate_stakecurrency')
|
||||
mocker.patch(f'{EXMS}.validate_pricing')
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('zaif', default_conf)
|
||||
default_conf['exchange']['name'] = 'zaif'
|
||||
exchange = ExchangeResolver.load_exchange(default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog)
|
||||
caplog.clear()
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('Bittrex', default_conf)
|
||||
default_conf['exchange']['name'] = 'Bittrex'
|
||||
exchange = ExchangeResolver.load_exchange(default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Bittrex)
|
||||
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||
caplog)
|
||||
caplog.clear()
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('kraken', default_conf)
|
||||
default_conf['exchange']['name'] = 'kraken'
|
||||
exchange = ExchangeResolver.load_exchange(default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Kraken)
|
||||
assert not isinstance(exchange, Binance)
|
||||
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||
caplog)
|
||||
|
||||
exchange = ExchangeResolver.load_exchange('binance', default_conf)
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
exchange = ExchangeResolver.load_exchange(default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Binance)
|
||||
assert not isinstance(exchange, Kraken)
|
||||
@@ -257,7 +258,8 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
||||
caplog)
|
||||
|
||||
# Test mapping
|
||||
exchange = ExchangeResolver.load_exchange('binanceus', default_conf)
|
||||
default_conf['exchange']['name'] = 'binanceus'
|
||||
exchange = ExchangeResolver.load_exchange(default_conf)
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Binance)
|
||||
assert not isinstance(exchange, Kraken)
|
||||
@@ -990,19 +992,20 @@ def test_validate_pricing(default_conf, mocker):
|
||||
mocker.patch(f'{EXMS}.validate_timeframes')
|
||||
mocker.patch(f'{EXMS}.validate_stakecurrency')
|
||||
mocker.patch(f'{EXMS}.name', 'Binance')
|
||||
ExchangeResolver.load_exchange('binance', default_conf)
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
has.update({'fetchTicker': False})
|
||||
with pytest.raises(OperationalException, match="Ticker pricing not available for .*"):
|
||||
ExchangeResolver.load_exchange('binance', default_conf)
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
|
||||
has.update({'fetchTicker': True})
|
||||
|
||||
default_conf['exit_pricing']['use_order_book'] = True
|
||||
ExchangeResolver.load_exchange('binance', default_conf)
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
has.update({'fetchL2OrderBook': False})
|
||||
|
||||
with pytest.raises(OperationalException, match="Orderbook not available for .*"):
|
||||
ExchangeResolver.load_exchange('binance', default_conf)
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
|
||||
has.update({'fetchL2OrderBook': True})
|
||||
|
||||
@@ -1011,7 +1014,7 @@ def test_validate_pricing(default_conf, mocker):
|
||||
default_conf['margin_mode'] = MarginMode.ISOLATED
|
||||
|
||||
with pytest.raises(OperationalException, match="Ticker pricing not available for .*"):
|
||||
ExchangeResolver.load_exchange('binance', default_conf)
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_ordertypes(default_conf, mocker):
|
||||
@@ -1091,12 +1094,13 @@ def test_validate_ordertypes_stop_advanced(default_conf, mocker, exchange_name,
|
||||
'stoploss_on_exchange': True,
|
||||
'stoploss_price_type': stopadv,
|
||||
}
|
||||
default_conf['exchange']['name'] = exchange_name
|
||||
if expected:
|
||||
ExchangeResolver.load_exchange(exchange_name, default_conf)
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
else:
|
||||
with pytest.raises(OperationalException,
|
||||
match=r'On exchange stoploss price type is not supported for .*'):
|
||||
ExchangeResolver.load_exchange(exchange_name, default_conf)
|
||||
ExchangeResolver.load_exchange(default_conf)
|
||||
|
||||
|
||||
def test_validate_order_types_not_in_config(default_conf, mocker):
|
||||
@@ -1773,6 +1777,71 @@ def test_fetch_positions(default_conf, mocker, exchange_name):
|
||||
"fetch_positions", "fetch_positions")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_fetch_orders(default_conf, mocker, exchange_name, limit_order):
|
||||
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_orders = MagicMock(return_value=[
|
||||
limit_order['buy'],
|
||||
limit_order['sell'],
|
||||
])
|
||||
api_mock.fetch_open_orders = MagicMock(return_value=[limit_order['buy']])
|
||||
api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order['buy']])
|
||||
|
||||
mocker.patch(f'{EXMS}.exchange_has', return_value=True)
|
||||
start_time = datetime.now(timezone.utc) - timedelta(days=5)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
# Not available in dry-run
|
||||
assert exchange.fetch_orders('mocked', start_time) == []
|
||||
assert api_mock.fetch_orders.call_count == 0
|
||||
default_conf['dry_run'] = False
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
res = exchange.fetch_orders('mocked', start_time)
|
||||
assert api_mock.fetch_orders.call_count == 1
|
||||
assert api_mock.fetch_open_orders.call_count == 0
|
||||
assert api_mock.fetch_closed_orders.call_count == 0
|
||||
assert len(res) == 2
|
||||
|
||||
res = exchange.fetch_orders('mocked', start_time)
|
||||
|
||||
api_mock.fetch_orders.reset_mock()
|
||||
|
||||
def has_resp(_, endpoint):
|
||||
if endpoint == 'fetchOrders':
|
||||
return False
|
||||
if endpoint == 'fetchClosedOrders':
|
||||
return True
|
||||
if endpoint == 'fetchOpenOrders':
|
||||
return True
|
||||
|
||||
mocker.patch(f'{EXMS}.exchange_has', has_resp)
|
||||
|
||||
# happy path without fetchOrders
|
||||
res = exchange.fetch_orders('mocked', start_time)
|
||||
assert api_mock.fetch_orders.call_count == 0
|
||||
assert api_mock.fetch_open_orders.call_count == 1
|
||||
assert api_mock.fetch_closed_orders.call_count == 1
|
||||
|
||||
mocker.patch(f'{EXMS}.exchange_has', return_value=True)
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
"fetch_orders", "fetch_orders", retries=1,
|
||||
pair='mocked', since=start_time)
|
||||
|
||||
# Unhappy path - first fetch-orders call fails.
|
||||
api_mock.fetch_orders = MagicMock(side_effect=ccxt.NotSupported())
|
||||
api_mock.fetch_open_orders.reset_mock()
|
||||
api_mock.fetch_closed_orders.reset_mock()
|
||||
|
||||
res = exchange.fetch_orders('mocked', start_time)
|
||||
|
||||
assert api_mock.fetch_orders.call_count == 1
|
||||
assert api_mock.fetch_open_orders.call_count == 1
|
||||
assert api_mock.fetch_closed_orders.call_count == 1
|
||||
|
||||
|
||||
def test_fetch_trading_fees(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
tick = {
|
||||
@@ -4932,7 +5001,7 @@ def test_get_maintenance_ratio_and_amt_exceptions(mocker, default_conf, leverage
|
||||
|
||||
exchange._leverage_tiers = leverage_tiers
|
||||
with pytest.raises(
|
||||
OperationalException,
|
||||
DependencyException,
|
||||
match='nominal value can not be lower than 0',
|
||||
):
|
||||
exchange.get_maintenance_ratio_and_amt('1000SHIB/USDT:USDT', -1)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import platform
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
@@ -14,6 +15,11 @@ from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver
|
||||
from tests.conftest import get_patched_exchange
|
||||
|
||||
|
||||
def is_mac() -> bool:
|
||||
machine = platform.system()
|
||||
return "Darwin" in machine
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def freqai_conf(default_conf, tmpdir):
|
||||
freqaiconf = deepcopy(default_conf)
|
||||
@@ -36,6 +42,7 @@ def freqai_conf(default_conf, tmpdir):
|
||||
"identifier": "uniqe-id100",
|
||||
"live_trained_timestamp": 0,
|
||||
"data_kitchen_thread_count": 2,
|
||||
"activate_tensorboard": False,
|
||||
"feature_parameters": {
|
||||
"include_timeframes": ["5m"],
|
||||
"include_corr_pairlist": ["ADA/BTC"],
|
||||
|
||||
@@ -12,6 +12,7 @@ from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from tests.conftest import get_patched_exchange, log_has_re
|
||||
from tests.freqai.conftest import (get_patched_data_kitchen, get_patched_freqai_strategy,
|
||||
make_data_dictionary, make_unfiltered_dataframe)
|
||||
from tests.freqai.test_freqai_interface import is_mac
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -173,6 +174,9 @@ def test_get_full_model_path(mocker, freqai_conf, model):
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
freqai_conf.update({"strategy": "freqai_test_strat"})
|
||||
|
||||
if is_mac():
|
||||
pytest.skip("Mac is confused during this test for unknown reasons")
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
@@ -188,7 +192,7 @@ def test_get_full_model_path(mocker, freqai_conf, model):
|
||||
|
||||
data_load_timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
new_timerange = TimeRange.parse_timerange("20180120-20180130")
|
||||
|
||||
freqai.dk.set_paths('ADA/BTC', None)
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from freqtrade.optimize.backtesting import Backtesting
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.plugins.pairlistmanager import PairListManager
|
||||
from tests.conftest import EXMS, create_mock_trades, get_patched_exchange, log_has_re
|
||||
from tests.freqai.conftest import (get_patched_freqai_strategy, make_rl_config,
|
||||
from tests.freqai.conftest import (get_patched_freqai_strategy, is_mac, make_rl_config,
|
||||
mock_pytorch_mlp_model_training_parameters)
|
||||
|
||||
|
||||
@@ -28,29 +28,22 @@ def is_arm() -> bool:
|
||||
return "arm" in machine or "aarch64" in machine
|
||||
|
||||
|
||||
def is_mac() -> bool:
|
||||
machine = platform.system()
|
||||
return "Darwin" in machine
|
||||
|
||||
|
||||
def can_run_model(model: str) -> None:
|
||||
if (is_arm() or is_py11()) and "Catboost" in model:
|
||||
if is_arm() and "Catboost" in model:
|
||||
pytest.skip("CatBoost is not supported on ARM.")
|
||||
|
||||
is_pytorch_model = 'Reinforcement' in model or 'PyTorch' in model
|
||||
if is_pytorch_model and is_mac() and not is_arm():
|
||||
pytest.skip("Reinforcement learning / PyTorch module not available on intel based Mac OS.")
|
||||
|
||||
if is_pytorch_model and is_py11():
|
||||
pytest.skip("Reinforcement learning / PyTorch currently not available on python 3.11.")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model, pca, dbscan, float32, can_short, shuffle, buffer', [
|
||||
('LightGBMRegressor', True, False, True, True, False, 0),
|
||||
('XGBoostRegressor', False, True, False, True, False, 10),
|
||||
('XGBoostRFRegressor', False, False, False, True, False, 0),
|
||||
('CatboostRegressor', False, False, False, True, True, 0),
|
||||
('PyTorchMLPRegressor', False, False, False, True, False, 0),
|
||||
('PyTorchMLPRegressor', False, False, False, False, False, 0),
|
||||
('PyTorchTransformerRegressor', False, False, False, False, False, 0),
|
||||
('ReinforcementLearner', False, True, False, True, False, 0),
|
||||
('ReinforcementLearner_multiproc', False, False, False, True, False, 0),
|
||||
('ReinforcementLearner_test_3ac', False, False, False, False, False, 0),
|
||||
@@ -61,6 +54,11 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
dbscan, float32, can_short, shuffle, buffer):
|
||||
|
||||
can_run_model(model)
|
||||
|
||||
test_tb = True
|
||||
if is_mac():
|
||||
test_tb = False
|
||||
|
||||
model_save_ext = 'joblib'
|
||||
freqai_conf.update({"freqaimodel": model})
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
@@ -82,10 +80,13 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models")
|
||||
freqai_conf["freqai"]["rl_config"]["drop_ohlc_from_features"] = True
|
||||
|
||||
if 'PyTorchMLPRegressor' in model:
|
||||
if 'PyTorch' in model:
|
||||
model_save_ext = 'zip'
|
||||
pytorch_mlp_mtp = mock_pytorch_mlp_model_training_parameters()
|
||||
freqai_conf['freqai']['model_training_parameters'].update(pytorch_mlp_mtp)
|
||||
if 'Transformer' in model:
|
||||
# transformer model takes a window, unlike the MLP regressor
|
||||
freqai_conf.update({"conv_width": 10})
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
@@ -93,6 +94,7 @@ def test_extract_data_and_train_model_Standard(mocker, freqai_conf, model, pca,
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.live = True
|
||||
freqai.activate_tensorboard = test_tb
|
||||
freqai.can_short = can_short
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
freqai.dk.live = True
|
||||
@@ -228,6 +230,7 @@ def test_extract_data_and_train_model_Classifiers(mocker, freqai_conf, model):
|
||||
("XGBoostRegressor", 2, "freqai_test_strat"),
|
||||
("CatboostRegressor", 2, "freqai_test_strat"),
|
||||
("PyTorchMLPRegressor", 2, "freqai_test_strat"),
|
||||
("PyTorchTransformerRegressor", 2, "freqai_test_strat"),
|
||||
("ReinforcementLearner", 3, "freqai_rl_test_strat"),
|
||||
("XGBoostClassifier", 2, "freqai_test_classifier"),
|
||||
("LightGBMClassifier", 2, "freqai_test_classifier"),
|
||||
@@ -237,6 +240,9 @@ def test_extract_data_and_train_model_Classifiers(mocker, freqai_conf, model):
|
||||
)
|
||||
def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog):
|
||||
can_run_model(model)
|
||||
test_tb = True
|
||||
if is_mac():
|
||||
test_tb = False
|
||||
|
||||
freqai_conf.get("freqai", {}).update({"save_backtest_models": True})
|
||||
freqai_conf['runmode'] = RunMode.BACKTEST
|
||||
@@ -253,9 +259,12 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
|
||||
if 'test_4ac' in model:
|
||||
freqai_conf["freqaimodel_path"] = str(Path(__file__).parents[1] / "freqai" / "test_models")
|
||||
|
||||
if 'PyTorchMLP' in model:
|
||||
if 'PyTorch' in model:
|
||||
pytorch_mlp_mtp = mock_pytorch_mlp_model_training_parameters()
|
||||
freqai_conf['freqai']['model_training_parameters'].update(pytorch_mlp_mtp)
|
||||
if 'Transformer' in model:
|
||||
# transformer model takes a window, unlike the MLP regressor
|
||||
freqai_conf.update({"conv_width": 10})
|
||||
|
||||
freqai_conf.get("freqai", {}).get("feature_parameters", {}).update(
|
||||
{"indicator_periods_candles": [2]})
|
||||
@@ -266,6 +275,7 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.live = False
|
||||
freqai.activate_tensorboard = test_tb
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
@@ -277,6 +287,7 @@ def test_start_backtesting(mocker, freqai_conf, model, num_files, strat, caplog)
|
||||
df[f'%-constant_{i}'] = i
|
||||
|
||||
metadata = {"pair": "LTC/BTC"}
|
||||
freqai.dk.set_paths('LTC/BTC', None)
|
||||
freqai.start_backtesting(df, metadata, freqai.dk, strategy)
|
||||
model_folders = [x for x in freqai.dd.full_path.iterdir() if x.is_dir()]
|
||||
|
||||
@@ -434,6 +445,7 @@ def test_principal_component_analysis(mocker, freqai_conf):
|
||||
|
||||
data_load_timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
new_timerange = TimeRange.parse_timerange("20180120-20180130")
|
||||
freqai.dk.set_paths('ADA/BTC', None)
|
||||
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
@@ -467,6 +479,7 @@ def test_plot_feature_importance(mocker, freqai_conf):
|
||||
|
||||
data_load_timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
new_timerange = TimeRange.parse_timerange("20180120-20180130")
|
||||
freqai.dk.set_paths('ADA/BTC', None)
|
||||
|
||||
freqai.extract_data_and_train_model(
|
||||
new_timerange, "ADA/BTC", strategy, freqai.dk, data_load_timerange)
|
||||
|
||||
@@ -18,6 +18,11 @@ class ReinforcementLearner_test_3ac(ReinforcementLearner):
|
||||
"""
|
||||
User can override any function in BaseRLEnv and gym.Env. Here the user
|
||||
sets a custom reward based on profit and trade duration.
|
||||
|
||||
Warning!
|
||||
This is function is a showcase of functionality designed to show as many possible
|
||||
environment control features as possible. It is also designed to run quickly
|
||||
on small computers. This is a benchmark, it is *not* for live production.
|
||||
"""
|
||||
|
||||
def calculate_reward(self, action: int) -> float:
|
||||
|
||||
@@ -18,6 +18,11 @@ class ReinforcementLearner_test_4ac(ReinforcementLearner):
|
||||
"""
|
||||
User can override any function in BaseRLEnv and gym.Env. Here the user
|
||||
sets a custom reward based on profit and trade duration.
|
||||
|
||||
Warning!
|
||||
This is function is a showcase of functionality designed to show as many possible
|
||||
environment control features as possible. It is also designed to run quickly
|
||||
on small computers. This is a benchmark, it is *not* for live production.
|
||||
"""
|
||||
|
||||
def calculate_reward(self, action: int) -> float:
|
||||
|
||||
@@ -354,7 +354,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
||||
mocker.patch('freqtrade.optimize.backtesting.generate_backtest_stats')
|
||||
mocker.patch('freqtrade.optimize.backtesting.show_backtest_results')
|
||||
sbs = mocker.patch('freqtrade.optimize.backtesting.store_backtest_stats')
|
||||
sbc = mocker.patch('freqtrade.optimize.backtesting.store_backtest_signal_candles')
|
||||
sbc = mocker.patch('freqtrade.optimize.backtesting.store_backtest_analysis_results')
|
||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['UNITTEST/BTC']))
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ from freqtrade.optimize.optimize_reports import (_get_resample_from_period, gene
|
||||
generate_periodic_breakdown_stats,
|
||||
generate_strategy_comparison,
|
||||
generate_trading_stats, show_sorted_pairlist,
|
||||
store_backtest_signal_candles,
|
||||
store_backtest_analysis_results,
|
||||
store_backtest_stats, text_table_bt_results,
|
||||
text_table_exit_reason, text_table_strategy)
|
||||
from freqtrade.resolvers.strategy_resolver import StrategyResolver
|
||||
@@ -232,17 +232,17 @@ def test_store_backtest_candles(testdatadir, mocker):
|
||||
candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}}
|
||||
|
||||
# mock directory exporting
|
||||
store_backtest_signal_candles(testdatadir, candle_dict, '2022_01_01_15_05_13')
|
||||
store_backtest_analysis_results(testdatadir, candle_dict, {}, '2022_01_01_15_05_13')
|
||||
|
||||
assert dump_mock.call_count == 1
|
||||
assert dump_mock.call_count == 2
|
||||
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
|
||||
assert str(dump_mock.call_args_list[0][0][0]).endswith('_signals.pkl')
|
||||
|
||||
dump_mock.reset_mock()
|
||||
# mock file exporting
|
||||
filename = Path(testdatadir / 'testresult')
|
||||
store_backtest_signal_candles(filename, candle_dict, '2022_01_01_15_05_13')
|
||||
assert dump_mock.call_count == 1
|
||||
store_backtest_analysis_results(filename, candle_dict, {}, '2022_01_01_15_05_13')
|
||||
assert dump_mock.call_count == 2
|
||||
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
|
||||
# result will be testdatadir / testresult-<timestamp>_signals.pkl
|
||||
assert str(dump_mock.call_args_list[0][0][0]).endswith('_signals.pkl')
|
||||
@@ -254,10 +254,11 @@ def test_write_read_backtest_candles(tmpdir):
|
||||
candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}}
|
||||
|
||||
# test directory exporting
|
||||
stored_file = store_backtest_signal_candles(Path(tmpdir), candle_dict, '2022_01_01_15_05_13')
|
||||
scp = stored_file.open("rb")
|
||||
pickled_signal_candles = joblib.load(scp)
|
||||
scp.close()
|
||||
sample_date = '2022_01_01_15_05_13'
|
||||
store_backtest_analysis_results(Path(tmpdir), candle_dict, {}, sample_date)
|
||||
stored_file = Path(tmpdir / f'backtest-result-{sample_date}_signals.pkl')
|
||||
with stored_file.open("rb") as scp:
|
||||
pickled_signal_candles = joblib.load(scp)
|
||||
|
||||
assert pickled_signal_candles.keys() == candle_dict.keys()
|
||||
assert pickled_signal_candles['DefStrat'].keys() == pickled_signal_candles['DefStrat'].keys()
|
||||
@@ -268,10 +269,10 @@ def test_write_read_backtest_candles(tmpdir):
|
||||
|
||||
# test file exporting
|
||||
filename = Path(tmpdir / 'testresult')
|
||||
stored_file = store_backtest_signal_candles(filename, candle_dict, '2022_01_01_15_05_13')
|
||||
scp = stored_file.open("rb")
|
||||
pickled_signal_candles = joblib.load(scp)
|
||||
scp.close()
|
||||
store_backtest_analysis_results(filename, candle_dict, {}, sample_date)
|
||||
stored_file = Path(tmpdir / f'testresult-{sample_date}_signals.pkl')
|
||||
with stored_file.open("rb") as scp:
|
||||
pickled_signal_candles = joblib.load(scp)
|
||||
|
||||
assert pickled_signal_candles.keys() == candle_dict.keys()
|
||||
assert pickled_signal_candles['DefStrat'].keys() == pickled_signal_candles['DefStrat'].keys()
|
||||
|
||||
@@ -239,7 +239,7 @@ def test_interest(fee, exchange, is_short, lev, minutes, rate, interest,
|
||||
stake_amount=20.0,
|
||||
amount=30.0,
|
||||
open_rate=2.0,
|
||||
open_date=datetime.utcnow() - timedelta(minutes=minutes),
|
||||
open_date=datetime.now(timezone.utc) - timedelta(minutes=minutes),
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
exchange=exchange,
|
||||
@@ -2063,7 +2063,7 @@ def test_trade_truncates_string_fields():
|
||||
stake_amount=20.0,
|
||||
amount=30.0,
|
||||
open_rate=2.0,
|
||||
open_date=datetime.utcnow() - timedelta(minutes=20),
|
||||
open_date=datetime.now(timezone.utc) - timedelta(minutes=20),
|
||||
fee_open=0.001,
|
||||
fee_close=0.001,
|
||||
exchange='binance',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -24,8 +24,8 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
||||
stake_amount=0.01,
|
||||
fee_open=fee,
|
||||
fee_close=fee,
|
||||
open_date=datetime.utcnow() - timedelta(minutes=min_ago_open or 200),
|
||||
close_date=datetime.utcnow() - timedelta(minutes=min_ago_close or 30),
|
||||
open_date=datetime.now(timezone.utc) - timedelta(minutes=min_ago_open or 200),
|
||||
close_date=datetime.now(timezone.utc) - timedelta(minutes=min_ago_close or 30),
|
||||
open_rate=open_rate,
|
||||
is_open=is_open,
|
||||
amount=0.01 / open_rate,
|
||||
@@ -87,9 +87,9 @@ def test_protectionmanager(mocker, default_conf):
|
||||
for handler in freqtrade.protections._protection_handlers:
|
||||
assert handler.name in constants.AVAILABLE_PROTECTIONS
|
||||
if not handler.has_global_stop:
|
||||
assert handler.global_stop(datetime.utcnow(), '*') is None
|
||||
assert handler.global_stop(datetime.now(timezone.utc), '*') is None
|
||||
if not handler.has_local_stop:
|
||||
assert handler.stop_per_pair('XRP/BTC', datetime.utcnow(), '*') is None
|
||||
assert handler.stop_per_pair('XRP/BTC', datetime.now(timezone.utc), '*') is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize('timeframe,expected,protconf', [
|
||||
|
||||
@@ -261,8 +261,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
||||
assert isnan(fiat_profit_sum)
|
||||
|
||||
|
||||
def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee,
|
||||
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
||||
def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee, markets, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
@@ -295,7 +294,7 @@ def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee,
|
||||
assert day['starting_balance'] in (pytest.approx(1062.37), pytest.approx(1066.46))
|
||||
assert day['fiat_value'] in (0.0, )
|
||||
# ensure first day is current date
|
||||
assert str(days['data'][0]['date']) == str(datetime.utcnow().date())
|
||||
assert str(days['data'][0]['date']) == str(datetime.now(timezone.utc).date())
|
||||
|
||||
# Try invalid data
|
||||
with pytest.raises(RPCException, match=r'.*must be an integer greater than 0*'):
|
||||
@@ -415,8 +414,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
|
||||
assert pytest.approx(stats['profit_all_percent_mean']) == -57.86
|
||||
assert pytest.approx(stats['profit_all_fiat']) == -85.205614098
|
||||
assert stats['trade_count'] == 7
|
||||
assert stats['first_trade_date'] == '2 days ago'
|
||||
assert stats['latest_trade_date'] == '17 minutes ago'
|
||||
assert stats['first_trade_humanized'] == '2 days ago'
|
||||
assert stats['latest_trade_humanized'] == '17 minutes ago'
|
||||
assert stats['avg_duration'] in ('0:17:40')
|
||||
assert stats['best_pair'] == 'XRP/USDT'
|
||||
assert stats['best_rate'] == 10.0
|
||||
@@ -426,8 +425,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
|
||||
MagicMock(side_effect=ExchangeError("Pair 'XRP/USDT' not available")))
|
||||
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
|
||||
assert stats['trade_count'] == 7
|
||||
assert stats['first_trade_date'] == '2 days ago'
|
||||
assert stats['latest_trade_date'] == '17 minutes ago'
|
||||
assert stats['first_trade_humanized'] == '2 days ago'
|
||||
assert stats['latest_trade_humanized'] == '17 minutes ago'
|
||||
assert stats['avg_duration'] in ('0:17:40')
|
||||
assert stats['best_pair'] == 'XRP/USDT'
|
||||
assert stats['best_rate'] == 10.0
|
||||
|
||||
+147
-113
@@ -21,11 +21,13 @@ from freqtrade.__init__ import __version__
|
||||
from freqtrade.enums import CandleType, RunMode, State, TradingMode
|
||||
from freqtrade.exceptions import DependencyException, ExchangeError, OperationalException
|
||||
from freqtrade.loggers import setup_logging, setup_logging_pre
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.api_server import ApiServer
|
||||
from freqtrade.rpc.api_server.api_auth import create_token, get_user_from_token
|
||||
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
||||
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, EXMS, create_mock_trades, get_mock_coro,
|
||||
get_patched_freqtradebot, log_has, log_has_re, patch_get_signal)
|
||||
|
||||
@@ -601,7 +603,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||
assert len(rc.json()['data']) == 7
|
||||
assert rc.json()['stake_currency'] == 'BTC'
|
||||
assert rc.json()['fiat_display_currency'] == 'USD'
|
||||
assert rc.json()['data'][0]['date'] == str(datetime.utcnow().date())
|
||||
assert rc.json()['data'][0]['date'] == str(datetime.now(timezone.utc).date())
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
@@ -740,6 +742,33 @@ def test_api_delete_open_order(botclient, mocker, fee, markets, ticker, is_short
|
||||
assert cancel_mock.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_trade_reload_trade(botclient, mocker, fee, markets, ticker, is_short):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||
stoploss_mock = MagicMock()
|
||||
cancel_mock = MagicMock()
|
||||
ftbot.handle_onexchange_order = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
markets=PropertyMock(return_value=markets),
|
||||
fetch_ticker=ticker,
|
||||
cancel_order=cancel_mock,
|
||||
cancel_stoploss_order=stoploss_mock,
|
||||
)
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/trades/10/reload")
|
||||
assert_response(rc, 502)
|
||||
assert 'Could not find trade with id 10.' in rc.json()['error']
|
||||
assert ftbot.handle_onexchange_order.call_count == 0
|
||||
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
Trade.commit()
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/trades/5/reload")
|
||||
assert ftbot.handle_onexchange_order.call_count == 1
|
||||
|
||||
|
||||
def test_api_logs(botclient):
|
||||
ftbot, client = botclient
|
||||
rc = client_get(client, f"{BASE_URI}/logs")
|
||||
@@ -861,8 +890,10 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected)
|
||||
'best_pair_profit_ratio': expected['best_pair_profit_ratio'],
|
||||
'best_rate': expected['best_rate'],
|
||||
'first_trade_date': ANY,
|
||||
'first_trade_humanized': ANY,
|
||||
'first_trade_timestamp': ANY,
|
||||
'latest_trade_date': '5 minutes ago',
|
||||
'latest_trade_date': ANY,
|
||||
'latest_trade_humanized': '5 minutes ago',
|
||||
'latest_trade_timestamp': ANY,
|
||||
'profit_all_coin': pytest.approx(expected['profit_all_coin']),
|
||||
'profit_all_fiat': pytest.approx(expected['profit_all_fiat']),
|
||||
@@ -1197,7 +1228,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
|
||||
stake_amount=1,
|
||||
open_rate=0.245441,
|
||||
open_order_id="123456",
|
||||
open_date=datetime.utcnow(),
|
||||
open_date=datetime.now(timezone.utc),
|
||||
is_open=False,
|
||||
is_short=False,
|
||||
fee_close=fee.return_value,
|
||||
@@ -1659,137 +1690,140 @@ def test_sysinfo(botclient):
|
||||
|
||||
|
||||
def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
|
||||
ftbot, client = botclient
|
||||
mocker.patch(f'{EXMS}.get_fee', fee)
|
||||
try:
|
||||
ftbot, client = botclient
|
||||
mocker.patch(f'{EXMS}.get_fee', fee)
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/backtest")
|
||||
# Backtest prevented in default mode
|
||||
assert_response(rc, 502)
|
||||
rc = client_get(client, f"{BASE_URI}/backtest")
|
||||
# Backtest prevented in default mode
|
||||
assert_response(rc, 502)
|
||||
|
||||
ftbot.config['runmode'] = RunMode.WEBSERVER
|
||||
# Backtesting not started yet
|
||||
rc = client_get(client, f"{BASE_URI}/backtest")
|
||||
assert_response(rc)
|
||||
ftbot.config['runmode'] = RunMode.WEBSERVER
|
||||
# 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
|
||||
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'
|
||||
ftbot.config['export'] = 'trades'
|
||||
ftbot.config['backtest_cache'] = 'day'
|
||||
ftbot.config['user_data_dir'] = Path(tmpdir)
|
||||
ftbot.config['exportfilename'] = Path(tmpdir) / "backtest_results"
|
||||
ftbot.config['exportfilename'].mkdir()
|
||||
# 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'
|
||||
ftbot.config['export'] = 'trades'
|
||||
ftbot.config['backtest_cache'] = 'day'
|
||||
ftbot.config['user_data_dir'] = Path(tmpdir)
|
||||
ftbot.config['exportfilename'] = Path(tmpdir) / "backtest_results"
|
||||
ftbot.config['exportfilename'].mkdir()
|
||||
|
||||
# start backtesting
|
||||
data = {
|
||||
"strategy": CURRENT_TEST_STRATEGY,
|
||||
"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=data)
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
# start backtesting
|
||||
data = {
|
||||
"strategy": CURRENT_TEST_STRATEGY,
|
||||
"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=data)
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
|
||||
assert result['status'] == 'running'
|
||||
assert result['progress'] == 0
|
||||
assert result['running']
|
||||
assert result['status_msg'] == 'Backtest started'
|
||||
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)
|
||||
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']
|
||||
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'
|
||||
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'
|
||||
# Simulate running backtest
|
||||
ApiBG.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"
|
||||
# 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'
|
||||
# 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=data)
|
||||
assert_response(rc, 502)
|
||||
result = rc.json()
|
||||
assert 'Bot Background task already running' in result['error']
|
||||
# Post to backtest that's still running
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert_response(rc, 502)
|
||||
result = rc.json()
|
||||
assert 'Bot Background task already running' in result['error']
|
||||
|
||||
ApiServer._bgtask_running = False
|
||||
ApiBG.bgtask_running = False
|
||||
|
||||
# Rerun backtest (should get previous result)
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
assert log_has_re('Reusing result of previous backtest.*', caplog)
|
||||
# Rerun backtest (should get previous result)
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
assert log_has_re('Reusing result of previous backtest.*', caplog)
|
||||
|
||||
data['stake_amount'] = 101
|
||||
data['stake_amount'] = 101
|
||||
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
|
||||
side_effect=DependencyException('DeadBeef'))
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert log_has("Backtesting caused an error: DeadBeef", caplog)
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy',
|
||||
side_effect=DependencyException('DeadBeef'))
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert log_has("Backtesting caused an error: DeadBeef", caplog)
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/backtest")
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
assert result['status'] == 'error'
|
||||
assert 'Backtest failed' in result['status_msg']
|
||||
rc = client_get(client, f"{BASE_URI}/backtest")
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
assert result['status'] == 'error'
|
||||
assert 'Backtest failed' in result['status_msg']
|
||||
|
||||
# Delete backtesting to avoid leakage since the backtest-object may stick around.
|
||||
rc = client_delete(client, f"{BASE_URI}/backtest")
|
||||
assert_response(rc)
|
||||
# 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'
|
||||
result = rc.json()
|
||||
assert result['status'] == 'reset'
|
||||
assert not result['running']
|
||||
assert result['status_msg'] == 'Backtest reset'
|
||||
|
||||
# Disallow base64 strategies
|
||||
data['strategy'] = "xx:cHJpbnQoImhlbGxvIHdvcmxkIik="
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert_response(rc, 500)
|
||||
# Disallow base64 strategies
|
||||
data['strategy'] = "xx:cHJpbnQoImhlbGxvIHdvcmxkIik="
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
assert_response(rc, 500)
|
||||
finally:
|
||||
Backtesting.cleanup()
|
||||
|
||||
|
||||
def test_api_backtest_history(botclient, mocker, testdatadir):
|
||||
|
||||
@@ -52,7 +52,7 @@ def default_conf(default_conf) -> dict:
|
||||
|
||||
@pytest.fixture
|
||||
def update():
|
||||
message = Message(0, datetime.utcnow(), Chat(0, 0))
|
||||
message = Message(0, datetime.now(timezone.utc), Chat(0, 0))
|
||||
_update = Update(0, message=message)
|
||||
|
||||
return _update
|
||||
@@ -143,8 +143,8 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
|
||||
message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
|
||||
"['balance'], ['start'], ['stop'], "
|
||||
"['forceexit', 'forcesell', 'fx'], ['forcebuy', 'forcelong'], ['forceshort'], "
|
||||
"['trades'], ['delete'], ['cancel_open_order', 'coo'], ['performance'], "
|
||||
"['buys', 'entries'], ['exits', 'sells'], ['mix_tags'], "
|
||||
"['reload_trade'], ['trades'], ['delete'], ['cancel_open_order', 'coo'], "
|
||||
"['performance'], ['buys', 'entries'], ['exits', 'sells'], ['mix_tags'], "
|
||||
"['stats'], ['daily'], ['weekly'], ['monthly'], "
|
||||
"['count'], ['locks'], ['delete_locks', 'unlock'], "
|
||||
"['reload_conf', 'reload_config'], ['show_conf', 'show_config'], "
|
||||
@@ -213,7 +213,7 @@ async def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> Non
|
||||
patch_exchange(mocker)
|
||||
caplog.set_level(logging.DEBUG)
|
||||
chat = Chat(0xdeadbeef, 0)
|
||||
message = Message(randint(1, 100), datetime.utcnow(), chat)
|
||||
message = Message(randint(1, 100), datetime.now(timezone.utc), chat)
|
||||
update = Update(randint(1, 100), message=message)
|
||||
|
||||
default_conf['telegram']['enabled'] = False
|
||||
@@ -520,7 +520,7 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Daily Profit over the last 2 days</b>:" in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Day ' in msg_mock.call_args_list[0][0][0]
|
||||
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert str(datetime.now(timezone.utc).date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert ' 6.83 USDT' in msg_mock.call_args_list[0][0][0]
|
||||
assert ' 7.51 USD' in msg_mock.call_args_list[0][0][0]
|
||||
assert '(2)' in msg_mock.call_args_list[0][0][0]
|
||||
@@ -533,8 +533,9 @@ async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time
|
||||
await telegram._daily(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Daily Profit over the last 7 days</b>:" in msg_mock.call_args_list[0][0][0]
|
||||
assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert str((datetime.utcnow() - timedelta(days=5)).date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert str(datetime.now(timezone.utc).date()) in msg_mock.call_args_list[0][0][0]
|
||||
assert str((datetime.now(timezone.utc) - timedelta(days=5)).date()
|
||||
) in msg_mock.call_args_list[0][0][0]
|
||||
assert ' 6.83 USDT' in msg_mock.call_args_list[0][0][0]
|
||||
assert ' 7.51 USD' in msg_mock.call_args_list[0][0][0]
|
||||
assert '(2)' in msg_mock.call_args_list[0][0][0]
|
||||
@@ -608,7 +609,7 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim
|
||||
assert "Weekly Profit over the last 2 weeks (starting from Monday)</b>:" \
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Monday ' in msg_mock.call_args_list[0][0][0]
|
||||
today = datetime.utcnow().date()
|
||||
today = datetime.now(timezone.utc).date()
|
||||
first_iso_day_of_current_week = today - timedelta(days=today.weekday())
|
||||
assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0]
|
||||
assert ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
|
||||
@@ -677,7 +678,7 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Monthly Profit over the last 2 months</b>:' in msg_mock.call_args_list[0][0][0]
|
||||
assert 'Month ' in msg_mock.call_args_list[0][0][0]
|
||||
today = datetime.utcnow().date()
|
||||
today = datetime.now(timezone.utc).date()
|
||||
current_month = f"{today.year}-{today.month:02} "
|
||||
assert current_month in msg_mock.call_args_list[0][0][0]
|
||||
assert ' 2.74 USDT' in msg_mock.call_args_list[0][0][0]
|
||||
@@ -825,6 +826,9 @@ async def test_telegram_stats(default_conf, update, ticker, fee, mocker, is_shor
|
||||
assert 'Exit Reason' in msg_mock.call_args_list[-1][0][0]
|
||||
assert 'ROI' in msg_mock.call_args_list[-1][0][0]
|
||||
assert 'Avg. Duration' in msg_mock.call_args_list[-1][0][0]
|
||||
# Duration is not only N/A
|
||||
assert '0:19:00' in msg_mock.call_args_list[-1][0][0]
|
||||
assert 'N/A' in msg_mock.call_args_list[-1][0][0]
|
||||
msg_mock.reset_mock()
|
||||
|
||||
|
||||
@@ -1760,6 +1764,25 @@ async def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short
|
||||
assert "Please make sure to take care of this asset" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
async def test_telegram_reload_trade_from_exchange(mocker, update, default_conf, fee, is_short):
|
||||
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
context = MagicMock()
|
||||
context.args = []
|
||||
|
||||
await telegram._reload_trade_from_exchange(update=update, context=context)
|
||||
assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
context.args = [5]
|
||||
|
||||
await telegram._reload_trade_from_exchange(update=update, context=context)
|
||||
assert "Status: `Reloaded from orders from exchange`" in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
async def test_telegram_delete_open_order(mocker, update, default_conf, fee, is_short, ticker):
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import pytest
|
||||
from pandas import DataFrame
|
||||
@@ -43,12 +43,12 @@ def test_strategy_test_v3(dataframe_1m, fee, is_short, side):
|
||||
|
||||
assert strategy.confirm_trade_entry(pair='ETH/BTC', order_type='limit', amount=0.1,
|
||||
rate=20000, time_in_force='gtc',
|
||||
current_time=datetime.utcnow(),
|
||||
current_time=datetime.now(timezone.utc),
|
||||
side=side, entry_tag=None) is True
|
||||
assert strategy.confirm_trade_exit(pair='ETH/BTC', trade=trade, order_type='limit', amount=0.1,
|
||||
rate=20000, time_in_force='gtc', exit_reason='roi',
|
||||
sell_reason='roi',
|
||||
current_time=datetime.utcnow(),
|
||||
current_time=datetime.now(timezone.utc),
|
||||
side=side) is True
|
||||
|
||||
assert strategy.custom_stoploss(pair='ETH/BTC', trade=trade, current_time=datetime.now(),
|
||||
|
||||
@@ -1271,7 +1271,7 @@ def test_pairlist_resolving_with_config_pl_not_exists(mocker, default_conf):
|
||||
configuration.get_config()
|
||||
|
||||
|
||||
def test_pairlist_resolving_fallback(mocker):
|
||||
def test_pairlist_resolving_fallback(mocker, tmpdir):
|
||||
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
|
||||
mocker.patch.object(Path, "open", MagicMock(return_value=MagicMock()))
|
||||
mocker.patch("freqtrade.configuration.configuration.load_file",
|
||||
@@ -1290,7 +1290,7 @@ def test_pairlist_resolving_fallback(mocker):
|
||||
|
||||
assert config['pairs'] == ['ETH/BTC', 'XRP/BTC']
|
||||
assert config['exchange']['name'] == 'binance'
|
||||
assert config['datadir'] == Path.cwd() / "user_data/data/binance"
|
||||
assert config['datadir'] == Path(tmpdir) / "user_data/data/binance"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("setting", [
|
||||
|
||||
@@ -121,7 +121,7 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None:
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
if runmode == RunMode.LIVE:
|
||||
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
|
||||
assert not log_has_re(r".*stoploss_on_exchange .* dry-run", caplog)
|
||||
assert freqtrade.strategy.order_types['stoploss_on_exchange']
|
||||
|
||||
caplog.clear()
|
||||
@@ -136,7 +136,7 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None:
|
||||
}
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
|
||||
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
|
||||
assert not log_has_re(r".*stoploss_on_exchange .* dry-run", caplog)
|
||||
|
||||
|
||||
def test_get_trade_stake_amount(default_conf_usdt, mocker) -> None:
|
||||
@@ -149,6 +149,34 @@ def test_get_trade_stake_amount(default_conf_usdt, mocker) -> None:
|
||||
assert result == default_conf_usdt['stake_amount']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('runmode', [
|
||||
RunMode.DRY_RUN,
|
||||
RunMode.LIVE
|
||||
])
|
||||
def test_load_strategy_no_keys(default_conf_usdt, mocker, runmode, caplog) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
conf = deepcopy(default_conf_usdt)
|
||||
conf['runmode'] = runmode
|
||||
erm = mocker.patch('freqtrade.freqtradebot.ExchangeResolver.load_exchange')
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
strategy_config = freqtrade.strategy.config
|
||||
assert id(strategy_config['exchange']) == id(conf['exchange'])
|
||||
# Keys have been removed and are not passed to the exchange
|
||||
assert strategy_config['exchange']['key'] == ''
|
||||
assert strategy_config['exchange']['secret'] == ''
|
||||
|
||||
assert erm.call_count == 1
|
||||
ex_conf = erm.call_args_list[0][1]['exchange_config']
|
||||
assert id(ex_conf) != id(conf['exchange'])
|
||||
# Keys are still present
|
||||
assert ex_conf['key'] != ''
|
||||
assert ex_conf['key'] == default_conf_usdt['exchange']['key']
|
||||
assert ex_conf['secret'] != ''
|
||||
assert ex_conf['secret'] == default_conf_usdt['exchange']['secret']
|
||||
|
||||
|
||||
@pytest.mark.parametrize("amend_last,wallet,max_open,lsamr,expected", [
|
||||
(False, 120, 2, 0.5, [60, None]),
|
||||
(True, 120, 2, 0.5, [60, 58.8]),
|
||||
@@ -5552,6 +5580,51 @@ def test_handle_insufficient_funds(mocker, default_conf_usdt, fee, is_short, cap
|
||||
assert log_has(f"Error updating {order['id']}.", caplog)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_handle_onexchange_order(mocker, default_conf_usdt, limit_order, is_short, caplog):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
mock_uts = mocker.spy(freqtrade, 'update_trade_state')
|
||||
|
||||
entry_order = limit_order[entry_side(is_short)]
|
||||
exit_order = limit_order[exit_side(is_short)]
|
||||
mock_fo = mocker.patch(f'{EXMS}.fetch_orders', return_value=[
|
||||
entry_order,
|
||||
exit_order,
|
||||
])
|
||||
|
||||
order_id = entry_order['id']
|
||||
|
||||
trade = Trade(
|
||||
open_order_id=order_id,
|
||||
pair='ETH/USDT',
|
||||
fee_open=0.001,
|
||||
fee_close=0.001,
|
||||
open_rate=entry_order['price'],
|
||||
open_date=arrow.utcnow().datetime,
|
||||
stake_amount=entry_order['cost'],
|
||||
amount=entry_order['amount'],
|
||||
exchange="binance",
|
||||
is_short=is_short,
|
||||
leverage=1,
|
||||
)
|
||||
|
||||
trade.orders.append(Order.parse_from_ccxt_object(
|
||||
entry_order, 'ADA/USDT', entry_side(is_short))
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
freqtrade.handle_onexchange_order(trade)
|
||||
assert log_has_re(r"Found previously unknown order .*", caplog)
|
||||
assert mock_uts.call_count == 1
|
||||
assert mock_fo.call_count == 1
|
||||
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
|
||||
assert len(trade.orders) == 2
|
||||
assert trade.is_open is False
|
||||
assert trade.exit_reason == ExitType.SOLD_ON_EXCHANGE.value
|
||||
|
||||
|
||||
def test_get_valid_price(mocker, default_conf_usdt) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
|
||||
@@ -75,8 +75,9 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
||||
_notify_exit=MagicMock(),
|
||||
)
|
||||
mocker.patch("freqtrade.strategy.interface.IStrategy.should_exit", should_sell_mock)
|
||||
wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1000))
|
||||
wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update")
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=1000)
|
||||
mocker.patch("freqtrade.wallets.Wallets.check_exit_amount", return_value=True)
|
||||
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pandas as pd
|
||||
@@ -282,13 +281,13 @@ def test_generate_Plot_filename():
|
||||
assert fn == "freqtrade-plot-UNITTEST_BTC-5m.html"
|
||||
|
||||
|
||||
def test_generate_plot_file(mocker, caplog):
|
||||
def test_generate_plot_file(mocker, caplog, user_dir):
|
||||
fig = generate_empty_figure()
|
||||
plot_mock = mocker.patch("freqtrade.plot.plotting.plot", MagicMock())
|
||||
store_plot_file(fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html",
|
||||
directory=Path("user_data/plot"))
|
||||
directory=user_dir / "plot")
|
||||
|
||||
expected_fn = str(Path("user_data/plot/freqtrade-plot-UNITTEST_BTC-5m.html"))
|
||||
expected_fn = str(user_dir / "plot/freqtrade-plot-UNITTEST_BTC-5m.html")
|
||||
assert plot_mock.call_count == 1
|
||||
assert plot_mock.call_args[0][0] == fig
|
||||
assert (plot_mock.call_args_list[0][1]['filename']
|
||||
|
||||
@@ -16,18 +16,18 @@ if sys.version_info < (3, 9):
|
||||
pytest.skip("StrategyUpdater is not compatible with Python 3.8", allow_module_level=True)
|
||||
|
||||
|
||||
def test_strategy_updater_start(tmpdir, capsys) -> None:
|
||||
def test_strategy_updater_start(user_dir, capsys) -> None:
|
||||
# Effective test without mocks.
|
||||
teststrats = Path(__file__).parent / 'strategy/strats'
|
||||
tmpdirp = Path(tmpdir) / "strategies"
|
||||
tmpdirp.mkdir()
|
||||
tmpdirp = Path(user_dir) / "strategies"
|
||||
tmpdirp.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy(teststrats / 'strategy_test_v2.py', tmpdirp)
|
||||
old_code = (teststrats / 'strategy_test_v2.py').read_text()
|
||||
|
||||
args = [
|
||||
"strategy-updater",
|
||||
"--userdir",
|
||||
str(tmpdir),
|
||||
str(user_dir),
|
||||
"--strategy-list",
|
||||
"StrategyTestV2"
|
||||
]
|
||||
@@ -36,9 +36,9 @@ def test_strategy_updater_start(tmpdir, capsys) -> None:
|
||||
|
||||
start_strategy_update(pargs)
|
||||
|
||||
assert Path(tmpdir / "strategies_orig_updater").exists()
|
||||
assert Path(user_dir / "strategies_orig_updater").exists()
|
||||
# Backup file exists
|
||||
assert Path(tmpdir / "strategies_orig_updater" / 'strategy_test_v2.py').exists()
|
||||
assert Path(user_dir / "strategies_orig_updater" / 'strategy_test_v2.py').exists()
|
||||
# updated file exists
|
||||
new_file = Path(tmpdirp / 'strategy_test_v2.py')
|
||||
assert new_file.exists()
|
||||
|
||||
@@ -3,9 +3,11 @@ from copy import deepcopy
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT
|
||||
from freqtrade.exceptions import DependencyException
|
||||
from freqtrade.persistence import Trade
|
||||
from tests.conftest import EXMS, create_mock_trades, get_patched_freqtradebot, patch_wallet
|
||||
|
||||
|
||||
@@ -364,3 +366,48 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee):
|
||||
free = freqtrade.wallets.get_free('BTC')
|
||||
used = freqtrade.wallets.get_used('BTC')
|
||||
assert free + used == total
|
||||
|
||||
|
||||
def test_check_exit_amount(mocker, default_conf, fee):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
update_mock = mocker.patch("freqtrade.wallets.Wallets.update")
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123)
|
||||
|
||||
create_mock_trades(fee, is_short=None)
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
assert trade.amount == 123
|
||||
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is True
|
||||
assert update_mock.call_count == 0
|
||||
assert total_mock.call_count == 1
|
||||
|
||||
update_mock.reset_mock()
|
||||
# Reduce returned amount to below the trade amount - which should
|
||||
# trigger a wallet update and return False, triggering "order refinding"
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=100)
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is False
|
||||
assert update_mock.call_count == 1
|
||||
assert total_mock.call_count == 2
|
||||
|
||||
|
||||
def test_check_exit_amount_futures(mocker, default_conf, fee):
|
||||
default_conf['trading_mode'] = 'futures'
|
||||
default_conf['margin_mode'] = 'isolated'
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123)
|
||||
|
||||
create_mock_trades(fee, is_short=None)
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
trade.trading_mode = 'futures'
|
||||
assert trade.amount == 123
|
||||
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is True
|
||||
assert total_mock.call_count == 0
|
||||
|
||||
update_mock = mocker.patch("freqtrade.wallets.Wallets.update")
|
||||
trade.amount = 150
|
||||
# Reduce returned amount to below the trade amount - which should
|
||||
# trigger a wallet update and return False, triggering "order refinding"
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is False
|
||||
assert total_mock.call_count == 0
|
||||
assert update_mock.call_count == 1
|
||||
|
||||
Reference in New Issue
Block a user