Backtrader Multiple TP levels sometimes don’t work

I have a major bug which makes strange orders which logically don’t make sense.
Hoply someone have a idea or solution how to get the logic working.
The 2. problem is: SL of 5% sometime is 6-8% or more. TP level are limit but sometime few % different.
But Let’s focus first on the basic logic.

Note: The problem just show up sometimes coins like AAVE and more (see the results)
I tried different timeframes. Like, 4h, 1D etc. Problem is the same.
Datasets are clean. No duplicated, Null or Nan values.
I use multiple assets!

Strategy Code:


class Strategy(bt.Strategy):
    params = (
        ('rsi_period', 14),
        ('crsi_period', 44),
        ("buffer", 0.05),
        ("threshold", 0.025),
    )

    def __init__(self):
      self.broker.set_coc(False) #Cheat on Close
      self.rsi = {}
      self.crsi = {}
      self.returns = []
      self.trades = dict()
      for i, d in enumerate(self.datas):
        self.rsi[d] = bt.ind.RSI(d.close, period=self.params.rsi_period)
        self.crsi[d] = CycledRSI(d.close)
        self.trades[d] = dict()
        self.trades[d] = {"stage": 0, "units" : 0.0, "pos_price" : 0.0}

    def log(self, txt, dt=None):
        """ Logging function fot this strategy"""
        dt = dt or self.data.datetime[0]
        if isinstance(dt, float):
            dt = bt.num2date(dt)
        print("%s, %s" % (dt.date(), txt))

    def notify_order(self, order):
        """ Triggered upon changes to orders. """
        # Suppress notification if it is just a submitted order.
        if order.status == order.Submitted:
            return

        if order.status == order.Margin:
            return

        # Check if an order has been completed
        if order.status in [order.Completed]:
            self.log(
                f"{order.data._name:<6} {('BUY' if order.isbuy() else 'SELL'):<5} "
                #f"EXECUTED for: {dn} "
                f"Price: {order.executed.price:6.4f} "
                f"Cost: {order.executed.value:6.2f} "
                f"Comm: {order.executed.comm:4.2f} "
                f"Size: {order.created.size:9.4f} "
            )

    def next(self):
      for i, d in enumerate(self.datas):
          if len(d) > self.params.crsi_period:  # Ensure enough data points for RSI calculation
            if self.getposition(d).size == 0:
              crsi_value = self.crsi[d][0]
              upper_band = self.crsi[d].lines.upper_band[0]
              crsi_prev_value = self.crsi[d][-1]
              upper_band_prev = self.crsi[d].lines.upper_band[-1]

              #if crsi_value > upper_band and crsi_prev_value <= upper_band_prev:
              if self.crsi[d][0] > self.crsi[d].lines.upper_band[0]:
                self.trades[d]["stage"] = 1
                self.trades[d]["pos_price"] = d.close[0] # don't needed anymore
                self.trades[d]["units"] = d.target / d.close[0] #d.target defined in USD from __name__ == "__main__"
                self.buy(data=d, price=d.close[0], size=self.trades[d]["units"], exectype=bt.Order.Limit)  #buy position

            if self.getposition(d).size > 0:
              #Multiple Take Profit levels
              pos_price = self.getposition(d).price
              pos_units = self.trades[d]["units"]
              tp_level4 = pos_price * (1 + 1.00)
              tp_level3 = pos_price * (1 + 0.75)
              tp_level2 = pos_price * (1 + 0.50)
              tp_level1 = pos_price * (1 + 0.25)
              sl_level  = pos_price * (1 - 0.05)

              if self.trades[d]["stage"] >= 2:
                sl_level  = pos_price * (1 + 0.05)

              #Stop Loss
              # Idea: Step up with SL on every level
              if d.low[0] <= sl_level:
                self.sell(data=d, price=sl_level, size=self.getposition(d).size, exectype=bt.Order.Market)
                self.trades[d]["stage"] = 0
                self.returns.append((self.datas[0].datetime.datetime(0), self.broker.getvalue())) #store timestamp and return for results

              #Take Profit
              # Idea: TTP 
              # Note: I tried also d.close[0] >= tp_level
              if self.trades[d]["stage"] == 1 and d.high[0] >= tp_level1:
                self.sell(data=d, price=tp_level1, size=pos_units*0.25, exectype=bt.Order.Limit)
                self.trades[d]["stage"] = 2
              if self.trades[d]["stage"] == 2 and d.high[0] >= tp_level2:
                self.sell(data=d, price=tp_level2, size=pos_units*0.25, exectype=bt.Order.Limit)
                self.trades[d]["stage"] = 3
              if self.trades[d]["stage"] == 3 and d.high[0] >= tp_level3:
                self.sell(data=d, price=tp_level3, size=pos_units*0.25, exectype=bt.Order.Limit)
                self.trades[d]["stage"] = 4
              if self.trades[d]["stage"] == 4 and d.high[0] >= tp_level4:
                self.sell(data=d, price=tp_level4, size=self.getposition(d).size, exectype=bt.Order.Limit) #use current position size to make sure no micro values are left
                #self.returns.append((self.datas[0].datetime.datetime(0), self.broker.getvalue())) #store timestamp and return for results
                self.trades[d]["stage"] = 0


if __name__ == "__main__":
    files = os.listdir('data/')
    cerebro = bt.Cerebro()

    #capital is ordersize * nr of assets *10
    #captial     = 10_000
    ordersize   = 100
    timeframe   = '1D'
    com_perc    = 0.08
    starttime   = "2023-01-01"
    assets      = files[1:2] #filtered

    for file in assets:
        full_path = os.path.join('data/', file)
        symbol    = file.split('-')[0]
        ticker    = symbol+'-USDT'
        df = pd.read_csv(full_path)
        # drop columns
        df.drop(columns=['close_time', 'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore'], inplace=True)

        # Format timestamp
        df['open_time'] = pd.to_datetime(df['open_time'], unit='ms')
        df["openinterest"] = 0
        # time filter
        df = df[df.open_time >= starttime]
        # Resample
        df.set_index('open_time', inplace=True)
        df = df.resample(timeframe).mean().ffill()
        data = bt.feeds.PandasData(
            dataname=df,
            name=ticker,
            datetime=None,
            open=0,
            high=1,
            low=2,
            close=3,
            volume=4,
            openinterest=5,
            )
        data.target = ordersize
        cerebro.adddata(data, name=ticker)

    cerebro.addstrategy(Strategy)
    cerebro.broker.setcash(ordersize*len(assets*10))  # Set initial cash
    cerebro.broker.setcommission(commission=(com_perc/100))  # Set commission

    # Analyzer
    #cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')

    # Execute
    results = cerebro.run()

    # Results
    strategy = results[0]
    #cerebro.addwriter(bt.WriterFile, csv=True, out='log.csv')

    plt.rcParams['figure.figsize'] = [15, 10]
    plt.rcParams.update({'font.size': 12})
    cerebro.plot(iplot = False, )

Results of Orders:

2023-06-26, AAVE-USDT BUY   Price: 65.0436 Cost:  94.06 Comm: 0.08 Size:    1.4461 
2023-06-29, AAVE-USDT SELL  Price: 61.7486 Cost:  94.06 Comm: 0.07 Size:   -1.4461 
2023-07-06, AAVE-USDT BUY   Price: 73.8691 Cost:  97.24 Comm: 0.08 Size:    1.3163 
2023-07-06, AAVE-USDT BUY   Price: 73.8691 Cost:  96.81 Comm: 0.08 Size:    1.3105 
2023-07-10, AAVE-USDT BUY   Price: 70.3935 Cost:  98.71 Comm: 0.08 Size:    1.4023 
2023-08-01, AAVE-USDT BUY   Price: 63.9554 Cost:  95.79 Comm: 0.08 Size:    1.4978 
2023-08-01, AAVE-USDT BUY   Price: 63.9554 Cost:  94.38 Comm: 0.08 Size:    1.4757 
2023-08-01, AAVE-USDT BUY   Price: 63.9554 Cost:  92.25 Comm: 0.07 Size:    1.4425 
2023-08-02, AAVE-USDT SELL  Price: 64.3892 Cost: 575.18 Comm: 0.44 Size:   -8.4451 
2023-10-03, AAVE-USDT BUY   Price: 67.5518 Cost:  97.80 Comm: 0.08 Size:    1.4477 
2023-10-03, AAVE-USDT BUY   Price: 67.5518 Cost:  96.28 Comm: 0.08 Size:    1.4253 
2023-10-04, AAVE-USDT BUY   Price: 64.9367 Cost:  98.43 Comm: 0.08 Size:    1.5157 
2023-10-04, AAVE-USDT BUY   Price: 64.9367 Cost:  96.50 Comm: 0.08 Size:    1.4860 
2023-10-13, AAVE-USDT SELL  Price: 63.4378 Cost: 389.00 Comm: 0.30 Size:   -5.8748 
2023-10-25, AAVE-USDT BUY   Price: 83.4728 Cost:  98.56 Comm: 0.08 Size:    1.1808 
2023-10-26, AAVE-USDT BUY   Price: 82.0772 Cost:  98.98 Comm: 0.08 Size:    1.2060 
2023-10-28, AAVE-USDT SELL  Price: 80.1142 Cost: 197.54 Comm: 0.15 Size:   -2.3867 
2023-10-31, AAVE-USDT BUY   Price: 81.9492 Cost:  98.84 Comm: 0.08 Size:    1.2061 
2023-10-31, AAVE-USDT BUY   Price: 81.9492 Cost:  97.20 Comm: 0.08 Size:    1.1861 
2023-12-15, AAVE-USDT SELL  Price: 110.0129 Cost:  24.30 Comm: 0.03 Size:   -0.2965 
2024-02-02, AAVE-USDT SELL  Price: 84.2158 Cost: 171.74 Comm: 0.14 Size:   -2.0957 
2024-03-01, AAVE-USDT BUY   Price: 108.7384 Cost: 100.00 Comm: 0.08 Size:    0.9196 
2024-03-14, AAVE-USDT SELL  Price: 139.5675 Cost:  25.00 Comm: 0.03 Size:   -0.2299 
2024-03-20, AAVE-USDT SELL  Price: 113.2177 Cost:  75.00 Comm: 0.06 Size:   -0.6897 
2024-04-13, AAVE-USDT BUY   Price: 93.1812 Cost:  90.24 Comm: 0.07 Size:    0.9684 
2024-04-13, AAVE-USDT BUY   Price: 93.1812 Cost:  88.69 Comm: 0.07 Size:    0.9518 
2024-04-15, AAVE-USDT SELL  Price: 86.8857 Cost: 178.92 Comm: 0.13 Size:   -1.9202 

enter image description here

Look at the multiple buy orders which not make sense because the position is already open. Logically it can’t open a new one.
Thanks to everybody that have a look!

New contributor

Dev-Joe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật