A slight improvement from the simple code, what if you want to include check based of dataframe value, for example, additional entries only when an enter signal is active?
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, min_stake: float,
max_stake: float, **kwargs) -> Optional[float]:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
current_profit = trade.calc_profit_ratio(current_candle['close'])
if current_profit > self.initial_safety_order_trigger:
return None
filled_entries = trade.select_filled_orders(trade.entry_side)
count_of_entries = len(filled_entries)
if 1 <= count_of_entries <= self.max_entry_position_adjustment:
# Make sure last entry order isn't on current candle, otherwise wait until next candle
if (current_time - timedelta(minutes=timeframe_to_minutes(self.timeframe))) >= filled_entries[(count_of_entries - 1)].order_filled_date.replace(tzinfo=timezone.utc):
# Check for entry and exit signal
enter_signal = False
exit_signal = False
if trade.is_short:
enter_signal = (current_candle['enter_short'] == 1)
exit_signal = (current_candle['exit_short'] == 1)
else:
enter_signal = (current_candle['enter_long'] == 1)
exit_signal = (current_candle['exit_long'] == 1)
if enter_signal and (exit_signal == False):
safety_order_trigger = (abs(self.initial_safety_order_trigger) * count_of_entries)
if (self.safety_order_step_scale > 1):
safety_order_trigger = abs(self.initial_safety_order_trigger) + (abs(self.initial_safety_order_trigger) * self.safety_order_step_scale * (math.pow(self.safety_order_step_scale,(count_of_entries - 1)) - 1) / (self.safety_order_step_scale - 1))
elif (self.safety_order_step_scale < 1):
safety_order_trigger = abs(self.initial_safety_order_trigger) + (abs(self.initial_safety_order_trigger) * self.safety_order_step_scale * (1 - math.pow(self.safety_order_step_scale,(count_of_entries - 1))) / (1 - self.safety_order_step_scale))
if current_profit <= (-1 * abs(safety_order_trigger)):
try:
# This returns first order stake size
stake_amount = filled_entries[0].stake_amount
# This then calculates current safety order size
stake_amount = stake_amount * math.pow(self.safety_order_volume_scale,(count_of_entries - 1))
amount = stake_amount / current_rate
logger.info(f"Initiating additional entry #{count_of_entries} for {trade.pair} with stake amount of {stake_amount} {trade.stake_currency} which equals {amount} {trade.base_currency}")
return stake_amount
except Exception as exception:
logger.info(f'Error occured while trying to get stake amount for {trade.pair}: {str(exception)}')
return None
return None
Same restriction from the simple code are still exist in this code, to make this backtestable. current_profit is tied to last closed candle’s close value, and only 1 entry order per candle (including original entry order).
You can modify the code above to add indicator check as well. Be creative with it.
[…] Additional Entries Based of Dataframe Value […]