Merge remote-tracking branch 'origin/develop' into pr/Axel-CH/9267
This commit is contained in:
@@ -11,6 +11,7 @@ from unittest.mock import MagicMock, Mock, PropertyMock
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from xdist.scheduler.loadscope import LoadScopeScheduling
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.commands import Arguments
|
||||
@@ -56,6 +57,27 @@ def pytest_configure(config):
|
||||
setattr(config.option, 'markexpr', 'not longrun')
|
||||
|
||||
|
||||
class FixtureScheduler(LoadScopeScheduling):
|
||||
# Based on the suggestion in
|
||||
# https://github.com/pytest-dev/pytest-xdist/issues/18
|
||||
|
||||
def _split_scope(self, nodeid):
|
||||
if 'exchange_online' in nodeid:
|
||||
try:
|
||||
# Extract exchange ID from nodeid
|
||||
exchange_id = nodeid.split('[')[1].split('-')[0].rstrip(']')
|
||||
return exchange_id
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
return nodeid
|
||||
|
||||
|
||||
def pytest_xdist_make_scheduler(config, log):
|
||||
return FixtureScheduler(config, log)
|
||||
|
||||
|
||||
def log_has(line, logs):
|
||||
"""Check if line is found on some caplog's message."""
|
||||
return any(line == message for message in logs.messages)
|
||||
|
||||
@@ -508,16 +508,13 @@ def test_dp_get_required_startup(default_conf_usdt):
|
||||
dp = DataProvider(default_conf_usdt, None)
|
||||
|
||||
# No FreqAI config
|
||||
assert dp.get_required_startup('5m', False) == 0
|
||||
assert dp.get_required_startup('1h', False) == 0
|
||||
assert dp.get_required_startup('1d', False) == 0
|
||||
assert dp.get_required_startup('1d', True) == 0
|
||||
assert dp.get_required_startup('5m') == 0
|
||||
assert dp.get_required_startup('1h') == 0
|
||||
assert dp.get_required_startup('1d') == 0
|
||||
|
||||
dp._config['startup_candle_count'] = 20
|
||||
assert dp.get_required_startup('5m', False) == 20
|
||||
assert dp.get_required_startup('5m', True) == 20
|
||||
assert dp.get_required_startup('1h', False) == 20
|
||||
assert dp.get_required_startup('5m') == 20
|
||||
assert dp.get_required_startup('1h') == 20
|
||||
assert dp.get_required_startup('1h') == 20
|
||||
|
||||
# With freqAI config
|
||||
@@ -532,37 +529,19 @@ def test_dp_get_required_startup(default_conf_usdt):
|
||||
]
|
||||
}
|
||||
}
|
||||
assert dp.get_required_startup('5m', False) == 20
|
||||
assert dp.get_required_startup('5m', True) == 5780
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 20
|
||||
assert dp.get_required_startup('1h', True) == 500
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 20
|
||||
assert dp.get_required_startup('1d', True) == 40
|
||||
assert dp.get_required_startup('5m') == 5780
|
||||
assert dp.get_required_startup('1h') == 500
|
||||
assert dp.get_required_startup('1d') == 40
|
||||
|
||||
# FreqAI kindof ignores startup_candle_count if it's below indicator_periods_candles
|
||||
dp._config['startup_candle_count'] = 0
|
||||
assert dp.get_required_startup('5m', False) == 20
|
||||
assert dp.get_required_startup('5m', True) == 5780
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 20
|
||||
assert dp.get_required_startup('1h', True) == 500
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 20
|
||||
assert dp.get_required_startup('1d', True) == 40
|
||||
assert dp.get_required_startup('5m') == 5780
|
||||
assert dp.get_required_startup('1h') == 500
|
||||
assert dp.get_required_startup('1d') == 40
|
||||
|
||||
dp._config['freqai']['feature_parameters']['indicator_periods_candles'][1] = 50
|
||||
assert dp.get_required_startup('5m', False) == 50
|
||||
assert dp.get_required_startup('5m', True) == 5810
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 50
|
||||
assert dp.get_required_startup('1h', True) == 530
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 50
|
||||
assert dp.get_required_startup('1d', True) == 70
|
||||
assert dp.get_required_startup('5m') == 5810
|
||||
assert dp.get_required_startup('1h') == 530
|
||||
assert dp.get_required_startup('1d') == 70
|
||||
|
||||
# scenario from issue https://github.com/freqtrade/freqtrade/issues/9432
|
||||
@@ -577,12 +556,6 @@ def test_dp_get_required_startup(default_conf_usdt):
|
||||
}
|
||||
}
|
||||
dp._config['startup_candle_count'] = 40
|
||||
assert dp.get_required_startup('5m', False) == 40
|
||||
assert dp.get_required_startup('5m', True) == 51880
|
||||
|
||||
assert dp.get_required_startup('1h', False) == 40
|
||||
assert dp.get_required_startup('1h', True) == 4360
|
||||
|
||||
assert dp.get_required_startup('1d', False) == 40
|
||||
assert dp.get_required_startup('1d', True) == 220
|
||||
assert dp.get_required_startup('5m') == 51880
|
||||
assert dp.get_required_startup('1h') == 4360
|
||||
assert dp.get_required_startup('1d') == 220
|
||||
|
||||
@@ -3194,7 +3194,7 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name):
|
||||
mocker.patch(f'{mock_prefix}.fetch_stoploss_order', side_effect=exc)
|
||||
co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555)
|
||||
assert co['amount'] == 555
|
||||
assert co == {'fee': {}, 'status': 'canceled', 'amount': 555, 'info': {}}
|
||||
assert co == {'id': '_', 'fee': {}, 'status': 'canceled', 'amount': 555, 'info': {}}
|
||||
|
||||
with pytest.raises(InvalidOrderException):
|
||||
exc = InvalidOrderException("Did not find order")
|
||||
|
||||
@@ -13,11 +13,14 @@ STOPLOSS_ORDERTYPE = 'stop-loss'
|
||||
STOPLOSS_LIMIT_ORDERTYPE = 'stop-loss-limit'
|
||||
|
||||
|
||||
def test_buy_kraken_trading_agreement(default_conf, mocker):
|
||||
@pytest.mark.parametrize("order_type,time_in_force,expected_params", [
|
||||
('limit', 'ioc', {'timeInForce': 'IOC', 'trading_agreement': 'agree'}),
|
||||
('limit', 'PO', {'postOnly': True, 'trading_agreement': 'agree'}),
|
||||
('market', None, {'trading_agreement': 'agree'})
|
||||
])
|
||||
def test_kraken_trading_agreement(default_conf, mocker, order_type, time_in_force, expected_params):
|
||||
api_mock = MagicMock()
|
||||
order_id = f'test_prod_buy_{randint(0, 10 ** 6)}'
|
||||
order_type = 'limit'
|
||||
time_in_force = 'ioc'
|
||||
order_id = f'test_prod_{order_type}_{randint(0, 10 ** 6)}'
|
||||
api_mock.options = {}
|
||||
api_mock.create_order = MagicMock(return_value={
|
||||
'id': order_id,
|
||||
@@ -49,41 +52,9 @@ def test_buy_kraken_trading_agreement(default_conf, mocker):
|
||||
assert api_mock.create_order.call_args[0][1] == order_type
|
||||
assert api_mock.create_order.call_args[0][2] == 'buy'
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
assert api_mock.create_order.call_args[0][4] == 200
|
||||
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'IOC',
|
||||
'trading_agreement': 'agree'}
|
||||
assert api_mock.create_order.call_args[0][4] == (200 if order_type == 'limit' else None)
|
||||
|
||||
|
||||
def test_sell_kraken_trading_agreement(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
order_id = f'test_prod_sell_{randint(0, 10 ** 6)}'
|
||||
order_type = 'market'
|
||||
api_mock.options = {}
|
||||
api_mock.create_order = MagicMock(return_value={
|
||||
'id': order_id,
|
||||
'symbol': 'ETH/BTC',
|
||||
'info': {
|
||||
'foo': 'bar'
|
||||
}
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
|
||||
mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y)
|
||||
mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
|
||||
|
||||
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type,
|
||||
side="sell", amount=1, rate=200, leverage=1.0)
|
||||
|
||||
assert 'id' in order
|
||||
assert 'info' in order
|
||||
assert order['id'] == order_id
|
||||
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
|
||||
assert api_mock.create_order.call_args[0][1] == order_type
|
||||
assert api_mock.create_order.call_args[0][2] == 'sell'
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
assert api_mock.create_order.call_args[0][4] is None
|
||||
assert api_mock.create_order.call_args[0][5] == {'trading_agreement': 'agree'}
|
||||
assert api_mock.create_order.call_args[0][5] == expected_params
|
||||
|
||||
|
||||
def test_get_balances_prod(default_conf, mocker):
|
||||
|
||||
@@ -54,7 +54,7 @@ def freqai_conf(default_conf, tmp_path):
|
||||
"backtest_period_days": 10,
|
||||
"live_retrain_hours": 0,
|
||||
"expiration_hours": 1,
|
||||
"identifier": "uniqe-id100",
|
||||
"identifier": "unique-id100",
|
||||
"live_trained_timestamp": 0,
|
||||
"data_kitchen_thread_count": 2,
|
||||
"activate_tensorboard": False,
|
||||
|
||||
@@ -6,11 +6,17 @@ from unittest.mock import PropertyMock
|
||||
import pytest
|
||||
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration
|
||||
from freqtrade.configuration.timerange import TimeRange
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.enums.candletype import CandleType
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has_re, patch_exchange,
|
||||
patched_configuration_load_config_file)
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, get_patched_exchange, log_has_re,
|
||||
patch_exchange, patched_configuration_load_config_file)
|
||||
from tests.freqai.conftest import get_patched_freqai_strategy
|
||||
|
||||
|
||||
def test_freqai_backtest_start_backtest_list(freqai_conf, mocker, testdatadir, caplog):
|
||||
@@ -40,7 +46,16 @@ def test_freqai_backtest_start_backtest_list(freqai_conf, mocker, testdatadir, c
|
||||
Backtesting.cleanup()
|
||||
|
||||
|
||||
def test_freqai_backtest_load_data(freqai_conf, mocker, caplog):
|
||||
@pytest.mark.parametrize(
|
||||
"timeframe, expected_startup_candle_count",
|
||||
[
|
||||
("5m", 876),
|
||||
("15m", 492),
|
||||
("1d", 302),
|
||||
],
|
||||
)
|
||||
def test_freqai_backtest_load_data(freqai_conf, mocker, caplog,
|
||||
timeframe, expected_startup_candle_count):
|
||||
patch_exchange(mocker)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -48,10 +63,14 @@ def test_freqai_backtest_load_data(freqai_conf, mocker, caplog):
|
||||
PropertyMock(return_value=['HULUMULU/USDT', 'XRP/USDT']))
|
||||
mocker.patch('freqtrade.optimize.backtesting.history.load_data')
|
||||
mocker.patch('freqtrade.optimize.backtesting.history.get_timerange', return_value=(now, now))
|
||||
freqai_conf['timeframe'] = timeframe
|
||||
freqai_conf.get('freqai', {}).get('feature_parameters', {}).update({'include_timeframes': []})
|
||||
backtesting = Backtesting(deepcopy(freqai_conf))
|
||||
backtesting.load_bt_data()
|
||||
|
||||
assert log_has_re('Increasing startup_candle_count for freqai to.*', caplog)
|
||||
assert log_has_re(f'Increasing startup_candle_count for freqai on {timeframe} '
|
||||
f'to {expected_startup_candle_count}', caplog)
|
||||
assert history.load_data.call_args[1]['startup_candles'] == expected_startup_candle_count
|
||||
|
||||
Backtesting.cleanup()
|
||||
|
||||
@@ -85,3 +104,34 @@ def test_freqai_backtest_live_models_model_not_found(freqai_conf, mocker, testda
|
||||
Backtesting(bt_config)
|
||||
|
||||
Backtesting.cleanup()
|
||||
|
||||
|
||||
def test_freqai_backtest_consistent_timerange(mocker, freqai_conf):
|
||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['XRP/USDT:USDT']))
|
||||
|
||||
gbs = mocker.patch('freqtrade.optimize.backtesting.generate_backtest_stats')
|
||||
|
||||
freqai_conf['candle_type_def'] = CandleType.FUTURES
|
||||
freqai_conf.get('exchange', {}).update({'pair_whitelist': ['XRP/USDT:USDT']})
|
||||
freqai_conf.get('freqai', {}).get('feature_parameters', {}).update(
|
||||
{'include_timeframes': ['5m', '1h'], 'include_corr_pairlist': []})
|
||||
freqai_conf['timerange'] = '20211120-20211121'
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
|
||||
timerange = TimeRange.parse_timerange("20211115-20211122")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
backtesting = Backtesting(deepcopy(freqai_conf))
|
||||
backtesting.start()
|
||||
|
||||
gbs.call_args[1]['min_date'] == datetime(2021, 11, 20, 0, 0, tzinfo=timezone.utc)
|
||||
gbs.call_args[1]['max_date'] == datetime(2021, 11, 21, 0, 0, tzinfo=timezone.utc)
|
||||
Backtesting.cleanup()
|
||||
|
||||
@@ -3,6 +3,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
@@ -135,3 +136,63 @@ def test_get_full_model_path(mocker, freqai_conf, model):
|
||||
|
||||
model_path = freqai.dk.get_full_models_path(freqai_conf)
|
||||
assert model_path.is_dir() is True
|
||||
|
||||
|
||||
def test_get_pair_data_for_features_with_prealoaded_data(mocker, freqai_conf):
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
_, base_df = freqai.dd.get_base_and_corr_dataframes(timerange, "LTC/BTC", freqai.dk)
|
||||
df = freqai.dk.get_pair_data_for_features("LTC/BTC", "5m", strategy, base_dataframes=base_df)
|
||||
|
||||
assert df is base_df["5m"]
|
||||
assert not df.empty
|
||||
|
||||
|
||||
def test_get_pair_data_for_features_without_preloaded_data(mocker, freqai_conf):
|
||||
freqai_conf.update({"timerange": "20180115-20180130"})
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
base_df = {'5m': pd.DataFrame()}
|
||||
df = freqai.dk.get_pair_data_for_features("LTC/BTC", "5m", strategy, base_dataframes=base_df)
|
||||
|
||||
assert df is not base_df["5m"]
|
||||
assert not df.empty
|
||||
assert df.iloc[0]['date'].strftime("%Y-%m-%d %H:%M:%S") == "2018-01-11 23:00:00"
|
||||
assert df.iloc[-1]['date'].strftime("%Y-%m-%d %H:%M:%S") == "2018-01-30 00:00:00"
|
||||
|
||||
|
||||
def test_populate_features(mocker, freqai_conf):
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
timerange = TimeRange.parse_timerange("20180115-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
corr_df, base_df = freqai.dd.get_base_and_corr_dataframes(timerange, "LTC/BTC", freqai.dk)
|
||||
mocker.patch.object(strategy, 'feature_engineering_expand_all', return_value=base_df["5m"])
|
||||
df = freqai.dk.populate_features(base_df["5m"], "LTC/BTC", strategy,
|
||||
base_dataframes=base_df, corr_dataframes=corr_df)
|
||||
|
||||
strategy.feature_engineering_expand_all.assert_called_once()
|
||||
pd.testing.assert_frame_equal(base_df["5m"],
|
||||
strategy.feature_engineering_expand_all.call_args[0][0])
|
||||
|
||||
assert df.iloc[0]['date'].strftime("%Y-%m-%d %H:%M:%S") == "2018-01-15 00:00:00"
|
||||
|
||||
@@ -20,8 +20,8 @@ from tests.freqai.conftest import (get_patched_freqai_strategy, is_mac, make_rl_
|
||||
mock_pytorch_mlp_model_training_parameters)
|
||||
|
||||
|
||||
def is_py11() -> bool:
|
||||
return sys.version_info >= (3, 11)
|
||||
def is_py12() -> bool:
|
||||
return sys.version_info >= (3, 12)
|
||||
|
||||
|
||||
def is_arm() -> bool:
|
||||
@@ -523,8 +523,8 @@ def test_get_state_info(mocker, freqai_conf, dp_exists, caplog, tickers):
|
||||
|
||||
if is_mac():
|
||||
pytest.skip("Reinforcement learning module not available on intel based Mac OS")
|
||||
if is_py11():
|
||||
pytest.skip("Reinforcement learning currently not available on python 3.11.")
|
||||
if is_py12():
|
||||
pytest.skip("Reinforcement learning currently not available on python 3.12.")
|
||||
|
||||
freqai_conf.update({"freqaimodel": "ReinforcementLearner"})
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
|
||||
@@ -1604,12 +1604,15 @@ def test_create_stoploss_order_insufficient_funds(
|
||||
])
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_handle_stoploss_on_exchange_trailing(
|
||||
mocker, default_conf_usdt, fee, is_short, bid, ask, limit_order, stop_price, hang_price
|
||||
mocker, default_conf_usdt, fee, is_short, bid, ask, limit_order, stop_price, hang_price,
|
||||
time_machine,
|
||||
) -> None:
|
||||
# When trailing stoploss is set
|
||||
enter_order = limit_order[entry_side(is_short)]
|
||||
exit_order = limit_order[exit_side(is_short)]
|
||||
stoploss = MagicMock(return_value={'id': 13434334, 'status': 'open'})
|
||||
stoploss = MagicMock(return_value={'id': '13434334', 'status': 'open'})
|
||||
start_dt = dt_now()
|
||||
time_machine.move_to(start_dt, tick=False)
|
||||
patch_RPCManager(mocker)
|
||||
mocker.patch.multiple(
|
||||
EXMS,
|
||||
@@ -1683,6 +1686,8 @@ def test_handle_stoploss_on_exchange_trailing(
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
|
||||
assert trade.stoploss_order_id == '13434334'
|
||||
|
||||
# price jumped 2x
|
||||
mocker.patch(
|
||||
f'{EXMS}.fetch_ticker',
|
||||
@@ -1704,16 +1709,15 @@ def test_handle_stoploss_on_exchange_trailing(
|
||||
cancel_order_mock.assert_not_called()
|
||||
stoploss_order_mock.assert_not_called()
|
||||
|
||||
# Move time by 10s ... so stoploss order should be replaced.
|
||||
time_machine.move_to(start_dt + timedelta(minutes=10), tick=False)
|
||||
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
assert trade.stop_loss == stop_price[1]
|
||||
trade.stoploss_order_id = '100'
|
||||
|
||||
# setting stoploss_on_exchange_interval to 0 seconds
|
||||
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
|
||||
|
||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||
|
||||
cancel_order_mock.assert_called_once_with('100', 'ETH/USDT')
|
||||
cancel_order_mock.assert_called_once_with('13434334', 'ETH/USDT')
|
||||
stoploss_order_mock.assert_called_once_with(
|
||||
amount=30,
|
||||
pair='ETH/USDT',
|
||||
|
||||
@@ -650,28 +650,42 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera
|
||||
caplog.clear()
|
||||
|
||||
# Sell more than what we got (we got ~20 coins left)
|
||||
# First adjusts the amount to 20 - then rejects.
|
||||
# Doesn't exit, as the amount is too high.
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-50)
|
||||
freqtrade.process()
|
||||
assert log_has_re("Adjusting amount to trade.amount as it is higher.*", caplog)
|
||||
assert log_has_re("Remaining amount of 0.0 would be smaller than the minimum of 10.", caplog)
|
||||
trade = Trade.get_trades().first()
|
||||
assert len(trade.orders) == 2
|
||||
|
||||
# Amount too low...
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-(trade.stake_amount * 0.99))
|
||||
freqtrade.process()
|
||||
|
||||
trade = Trade.get_trades().first()
|
||||
assert len(trade.orders) == 2
|
||||
|
||||
# Amount exactly comes out as exactly 0
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(
|
||||
return_value=-(trade.amount / trade.leverage * 2.02))
|
||||
freqtrade.process()
|
||||
|
||||
trade = Trade.get_trades().first()
|
||||
assert len(trade.orders) == 3
|
||||
|
||||
assert trade.orders[-1].ft_order_side == 'sell'
|
||||
assert pytest.approx(trade.stake_amount) == 40.198
|
||||
assert trade.is_open
|
||||
assert trade.is_open is False
|
||||
|
||||
# use amount that would trunc to 0.0 once selling
|
||||
mocker.patch(f"{EXMS}.amount_to_contract_precision", lambda s, p, v: round(v, 1))
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-0.01)
|
||||
freqtrade.process()
|
||||
trade = Trade.get_trades().first()
|
||||
assert len(trade.orders) == 2
|
||||
assert len(trade.orders) == 3
|
||||
assert trade.orders[-1].ft_order_side == 'sell'
|
||||
assert pytest.approx(trade.stake_amount) == 40.198
|
||||
assert trade.is_open
|
||||
assert trade.is_open is False
|
||||
assert log_has_re('Amount to exit is 0.0 due to exchange limits - not exiting.', caplog)
|
||||
expected_profit = starting_amount - 40.1980 + trade.realized_profit
|
||||
expected_profit = starting_amount - 60 + trade.realized_profit
|
||||
assert pytest.approx(freqtrade.wallets.get_free('USDT')) == expected_profit
|
||||
if spot:
|
||||
assert pytest.approx(freqtrade.wallets.get_total('USDT')) == expected_profit
|
||||
|
||||
Reference in New Issue
Block a user