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 […]