mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-01-30 02:41:12 +00:00
Simplify backtesting by using current_time more consequently
This commit is contained in:
@@ -525,10 +525,10 @@ class Backtesting:
|
||||
# This should not be reached...
|
||||
return row[OPEN_IDX]
|
||||
|
||||
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
|
||||
) -> LocalTrade:
|
||||
def _get_adjust_trade_entry_for_candle(
|
||||
self, trade: LocalTrade, row: Tuple, current_time: datetime
|
||||
) -> LocalTrade:
|
||||
current_rate = row[OPEN_IDX]
|
||||
current_date = row[DATE_IDX].to_pydatetime()
|
||||
current_profit = trade.calc_profit_ratio(current_rate)
|
||||
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, current_rate, -0.1)
|
||||
max_stake = self.exchange.get_max_pair_stake_amount(trade.pair, current_rate)
|
||||
@@ -536,7 +536,7 @@ class Backtesting:
|
||||
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
|
||||
default_retval=None, supress_error=True)(
|
||||
trade=trade, # type: ignore[arg-type]
|
||||
current_time=current_date, current_rate=current_rate,
|
||||
current_time=current_time, current_rate=current_rate,
|
||||
current_profit=current_profit, min_stake=min_stake,
|
||||
max_stake=min(max_stake, stake_available),
|
||||
current_entry_rate=current_rate, current_exit_rate=current_rate,
|
||||
@@ -569,10 +569,10 @@ class Backtesting:
|
||||
# Remaining stake is too low to be sold.
|
||||
return trade
|
||||
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT)
|
||||
pos_trade = self._get_exit_for_signal(trade, row, exit_, amount)
|
||||
pos_trade = self._get_exit_for_signal(trade, row, exit_, current_time, amount)
|
||||
if pos_trade is not None:
|
||||
order = pos_trade.orders[-1]
|
||||
if self._try_close_open_order(order, trade, current_date, row):
|
||||
if self._try_close_open_order(order, trade, current_time, row):
|
||||
trade.recalc_trade_from_orders()
|
||||
self.wallets.update()
|
||||
return pos_trade
|
||||
@@ -615,11 +615,11 @@ class Backtesting:
|
||||
|
||||
def _get_exit_for_signal(
|
||||
self, trade: LocalTrade, row: Tuple, exit_: ExitCheckTuple,
|
||||
current_time: datetime,
|
||||
amount: Optional[float] = None) -> Optional[LocalTrade]:
|
||||
|
||||
exit_candle_time: datetime = row[DATE_IDX].to_pydatetime()
|
||||
if exit_.exit_flag:
|
||||
trade.close_date = exit_candle_time
|
||||
trade.close_date = current_time
|
||||
exit_reason = exit_.exit_reason
|
||||
amount_ = amount if amount is not None else trade.amount
|
||||
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
|
||||
@@ -647,7 +647,7 @@ class Backtesting:
|
||||
default_retval=close_rate)(
|
||||
pair=trade.pair,
|
||||
trade=trade, # type: ignore[arg-type]
|
||||
current_time=exit_candle_time,
|
||||
current_time=current_time,
|
||||
proposed_rate=close_rate, current_profit=current_profit,
|
||||
exit_tag=exit_reason)
|
||||
if rate != close_rate:
|
||||
@@ -673,7 +673,7 @@ class Backtesting:
|
||||
time_in_force=time_in_force,
|
||||
sell_reason=exit_reason, # deprecated
|
||||
exit_reason=exit_reason,
|
||||
current_time=exit_candle_time)):
|
||||
current_time=current_time)):
|
||||
return None
|
||||
|
||||
trade.exit_reason = exit_reason
|
||||
@@ -714,8 +714,9 @@ class Backtesting:
|
||||
trade.orders.append(order)
|
||||
return trade
|
||||
|
||||
def _check_trade_exit(self, trade: LocalTrade, row: Tuple) -> Optional[LocalTrade]:
|
||||
exit_candle_time: datetime = row[DATE_IDX].to_pydatetime()
|
||||
def _check_trade_exit(
|
||||
self, trade: LocalTrade, row: Tuple, current_time: datetime
|
||||
) -> Optional[LocalTrade]:
|
||||
|
||||
if self.trading_mode == TradingMode.FUTURES:
|
||||
trade.set_funding_fees(
|
||||
@@ -724,13 +725,13 @@ class Backtesting:
|
||||
amount=trade.amount,
|
||||
is_short=trade.is_short,
|
||||
open_date=trade.date_last_filled_utc,
|
||||
close_date=exit_candle_time
|
||||
close_date=current_time
|
||||
)
|
||||
)
|
||||
|
||||
# Check if we need to adjust our current positions
|
||||
if self.strategy.position_adjustment_enable:
|
||||
trade = self._get_adjust_trade_entry_for_candle(trade, row)
|
||||
trade = self._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
enter = row[SHORT_IDX] if trade.is_short else row[LONG_IDX]
|
||||
exit_sig = row[ESHORT_IDX] if trade.is_short else row[ELONG_IDX]
|
||||
@@ -740,7 +741,7 @@ class Backtesting:
|
||||
low=row[LOW_IDX], high=row[HIGH_IDX]
|
||||
)
|
||||
for exit_ in exits:
|
||||
t = self._get_exit_for_signal(trade, row, exit_)
|
||||
t = self._get_exit_for_signal(trade, row, exit_, current_time)
|
||||
if t:
|
||||
return t
|
||||
return None
|
||||
@@ -1147,7 +1148,7 @@ class Backtesting:
|
||||
|
||||
# 4. Create exit orders (if any)
|
||||
if not trade.has_open_orders:
|
||||
self._check_trade_exit(trade, row) # Place exit order if necessary
|
||||
self._check_trade_exit(trade, row, current_time) # Place exit order if necessary
|
||||
|
||||
# 5. Process exit orders.
|
||||
order = trade.select_order(trade.exit_side, is_open=True)
|
||||
|
||||
@@ -665,7 +665,7 @@ def test_backtest__check_trade_exit(default_conf, fee, mocker) -> None:
|
||||
]
|
||||
|
||||
# No data available.
|
||||
res = backtesting._check_trade_exit(trade, row_sell)
|
||||
res = backtesting._check_trade_exit(trade, row_sell, row_sell[0].to_pydatetime())
|
||||
assert res is not None
|
||||
assert res.exit_reason == ExitType.ROI.value
|
||||
assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc)
|
||||
@@ -678,7 +678,7 @@ def test_backtest__check_trade_exit(default_conf, fee, mocker) -> None:
|
||||
[], columns=['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long',
|
||||
'enter_short', 'exit_short', 'long_tag', 'short_tag', 'exit_tag'])
|
||||
|
||||
res = backtesting._check_trade_exit(trade, row)
|
||||
res = backtesting._check_trade_exit(trade, row, row[0].to_pydatetime())
|
||||
assert res is None
|
||||
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
||||
]
|
||||
backtesting.strategy.leverage = MagicMock(return_value=leverage)
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
current_time = row[0].to_pydatetime()
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 100.0
|
||||
assert pytest.approx(trade.amount) == 47.61904762 * leverage
|
||||
@@ -140,7 +141,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=None)
|
||||
assert pytest.approx(trade.liquidation_price) == (0.10278333 if leverage == 1 else 1.2122249)
|
||||
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 100.0
|
||||
assert pytest.approx(trade.amount) == 47.61904762 * leverage
|
||||
@@ -148,7 +149,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
||||
# Increase position by 100
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=100)
|
||||
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 200.0
|
||||
@@ -159,7 +160,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
||||
# Reduce by more than amount - no change to trade.
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=-500)
|
||||
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 200.0
|
||||
@@ -170,7 +171,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
||||
|
||||
# Reduce position by 50
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=-100)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 100.0
|
||||
@@ -182,7 +183,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera
|
||||
|
||||
# Adjust below minimum
|
||||
backtesting.strategy.adjust_trade_position = MagicMock(return_value=-99)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row)
|
||||
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row, current_time)
|
||||
|
||||
assert trade
|
||||
assert pytest.approx(trade.stake_amount) == 100.0
|
||||
|
||||
Reference in New Issue
Block a user