support price callback for partial exits in bt
This will align results to how live works. closes #7292
This commit is contained in:
parent
9204f01312
commit
2b70c3d0c0
@ -70,7 +70,7 @@ This loop will be repeated again and again until the bot is stopped.
|
|||||||
* Determine stake size by calling the `custom_stake_amount()` callback.
|
* Determine stake size by calling the `custom_stake_amount()` callback.
|
||||||
* Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested.
|
* Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested.
|
||||||
* Call `custom_stoploss()` and `custom_exit()` to find custom exit points.
|
* Call `custom_stoploss()` and `custom_exit()` to find custom exit points.
|
||||||
* For exits based on exit-signal and custom-exit: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle).
|
* For exits based on exit-signal, custom-exit and partial exits: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle).
|
||||||
* Generate backtest report output
|
* Generate backtest report output
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
|
@ -423,7 +423,7 @@ class AwesomeStrategy(IStrategy):
|
|||||||
!!! Warning "Backtesting"
|
!!! Warning "Backtesting"
|
||||||
Custom prices are supported in backtesting (starting with 2021.12), and orders will fill if the price falls within the candle's low/high range.
|
Custom prices are supported in backtesting (starting with 2021.12), and orders will fill if the price falls within the candle's low/high range.
|
||||||
Orders that don't fill immediately are subject to regular timeout handling, which happens once per (detail) candle.
|
Orders that don't fill immediately are subject to regular timeout handling, which happens once per (detail) candle.
|
||||||
`custom_exit_price()` is only called for sells of type exit_signal and Custom exit. All other exit-types will use regular backtesting prices.
|
`custom_exit_price()` is only called for sells of type exit_signal, Custom exit and partial exits. All other exit-types will use regular backtesting prices.
|
||||||
|
|
||||||
## Custom order timeout rules
|
## Custom order timeout rules
|
||||||
|
|
||||||
|
@ -554,7 +554,8 @@ class Backtesting:
|
|||||||
if remaining < min_stake:
|
if remaining < min_stake:
|
||||||
# Remaining stake is too low to be sold.
|
# Remaining stake is too low to be sold.
|
||||||
return trade
|
return trade
|
||||||
pos_trade = self._exit_trade(trade, row, current_rate, amount)
|
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT)
|
||||||
|
pos_trade = self._get_exit_for_signal(trade, row, exit_, amount)
|
||||||
if pos_trade is not None:
|
if pos_trade is not None:
|
||||||
order = pos_trade.orders[-1]
|
order = pos_trade.orders[-1]
|
||||||
if self._get_order_filled(order.price, row):
|
if self._get_order_filled(order.price, row):
|
||||||
@ -589,14 +590,15 @@ class Backtesting:
|
|||||||
return t
|
return t
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_exit_for_signal(self, trade: LocalTrade, row: Tuple,
|
def _get_exit_for_signal(
|
||||||
exit_: ExitCheckTuple) -> Optional[LocalTrade]:
|
self, trade: LocalTrade, row: Tuple, exit_: ExitCheckTuple,
|
||||||
|
amount: Optional[float] = None) -> Optional[LocalTrade]:
|
||||||
|
|
||||||
exit_candle_time: datetime = row[DATE_IDX].to_pydatetime()
|
exit_candle_time: datetime = row[DATE_IDX].to_pydatetime()
|
||||||
if exit_.exit_flag:
|
if exit_.exit_flag:
|
||||||
trade.close_date = exit_candle_time
|
trade.close_date = exit_candle_time
|
||||||
exit_reason = exit_.exit_reason
|
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)
|
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
|
||||||
try:
|
try:
|
||||||
close_rate = self._get_close_rate(row, trade, exit_, trade_dur)
|
close_rate = self._get_close_rate(row, trade, exit_, trade_dur)
|
||||||
@ -605,7 +607,8 @@ class Backtesting:
|
|||||||
# call the custom exit price,with default value as previous close_rate
|
# call the custom exit price,with default value as previous close_rate
|
||||||
current_profit = trade.calc_profit_ratio(close_rate)
|
current_profit = trade.calc_profit_ratio(close_rate)
|
||||||
order_type = self.strategy.order_types['exit']
|
order_type = self.strategy.order_types['exit']
|
||||||
if exit_.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT):
|
if exit_.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT,
|
||||||
|
ExitType.PARTIAL_EXIT):
|
||||||
# Checks and adds an exit tag, after checking that the length of the
|
# Checks and adds an exit tag, after checking that the length of the
|
||||||
# row has the length for an exit tag column
|
# row has the length for an exit tag column
|
||||||
if (
|
if (
|
||||||
@ -633,22 +636,23 @@ class Backtesting:
|
|||||||
# Confirm trade exit:
|
# Confirm trade exit:
|
||||||
time_in_force = self.strategy.order_time_in_force['exit']
|
time_in_force = self.strategy.order_time_in_force['exit']
|
||||||
|
|
||||||
if (exit_.exit_type != ExitType.LIQUIDATION and not strategy_safe_wrapper(
|
if (exit_.exit_type not in (ExitType.LIQUIDATION, ExitType.PARTIAL_EXIT)
|
||||||
self.strategy.confirm_trade_exit, default_retval=True)(
|
and not strategy_safe_wrapper(
|
||||||
pair=trade.pair,
|
self.strategy.confirm_trade_exit, default_retval=True)(
|
||||||
trade=trade, # type: ignore[arg-type]
|
pair=trade.pair,
|
||||||
order_type=order_type,
|
trade=trade, # type: ignore[arg-type]
|
||||||
amount=trade.amount,
|
order_type=order_type,
|
||||||
rate=close_rate,
|
amount=amount_,
|
||||||
time_in_force=time_in_force,
|
rate=close_rate,
|
||||||
sell_reason=exit_reason, # deprecated
|
time_in_force=time_in_force,
|
||||||
exit_reason=exit_reason,
|
sell_reason=exit_reason, # deprecated
|
||||||
current_time=exit_candle_time)):
|
exit_reason=exit_reason,
|
||||||
|
current_time=exit_candle_time)):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
trade.exit_reason = exit_reason
|
trade.exit_reason = exit_reason
|
||||||
|
|
||||||
return self._exit_trade(trade, row, close_rate, trade.amount)
|
return self._exit_trade(trade, row, close_rate, amount_)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
|
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
|
||||||
|
Loading…
Reference in New Issue
Block a user