For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

I am new to backtrader and want to build a strategy, details below



  • -- Buying Rules

    (1).Buy when 8 ema crosses the 21 ema to the upside.

    (2).Place your stop loss below the most recent swing low point.

    (3).Your take profit target, aim for 200 pips.

    -- Selling Rule

    You do the exact opposite of the buy rule when you sell.

    (1).Sell when 8 ema crosses the 21 ema to the downside.

    (2).Place your stop loss below the most recent swing high point.

    (3).For your take profit target, aim for 200 pips.

    Can anyone help?



  • You could start here for the crosses...

    Look here for bracket orders.



  • Thanks for your help, I went through the whole document but even after using brackets the sharpe ratio is negative and I cant figure out why.
    @backtrader can you help?



  • It would be great if you could share you code.



  • @Sajil-Thamban
    We definitely want to help just show us your code and we'll point you in the right direction.



  • import backtrader as bt
    import datetime
    
    
    class MyStrategy(bt.Strategy):
        def __init__(self):
            # Keep a reference to the "close" line in the data[0] dataseries
            self.dataclose = self.datas[0].close
    
        def log(self, txt, dt=None):
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))  # Print date and close
    
        def next(self):
            # Log closing price to 2 decimals
            self.log('Close: %.2f' % self.dataclose[0])
    
    
    class MAcrossover(bt.Strategy):
        # Moving average parameters
        params = (('pfast', 8), ('pslow', 21),)
    
        def log(self, txt, dt=None):
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            self.dataclose = self.datas[0].close
            # Order variable will contain ongoing order details/status
            self.order = None
            # Instantiate moving averages
            self.slow_sma = bt.indicators.EMA(self.datas[0],
                                                              period=self.p.pslow)
            self.fast_sma = bt.indicators.EMA(self.datas[0],
                                                              period=self.p.pfast)
    
            self.crossover = bt.indicators.CrossOver(self.fast_sma, self.slow_sma)
            self.datalow = self.datas[0].low
            self.datahigh = self.datas[0].high
            self.recent_low = ''
            self.recent_high = ''
            self.order_signals = []
    
    
        def notify_trade(self, trade):
            date = self.data.datetime.datetime()
            if trade.isclosed:
                print('-' * 32, ' NOTIFY TRADE ', '-' * 32)
    
                print('-- {} ---{}, Close Price: {}, Profit, Gross {}, Net {}'.format(self.order_signals,
                    date,
                    trade.price,
                    round(trade.pnl, 2),
                    round(trade.pnlcomm, 2)))
                print('-' * 80)
                self.order_signals = []
    
        def notify_order(self, order):
            if order.status in [order.Submitted, order.Accepted]:
                # Active Buy/Sell order submitted/accepted - Nothing to do
                return
            # Check if an order has been completed
            # Attention: broker could reject order if not enough cash
            if order.status in [order.Completed]:
                if order.isbuy():
                    self.order_signals.append('BUY')
                elif order.issell():
                    self.order_signals.append('SELL')
                self.bar_executed = len(self)
            elif order.status in [order.Canceled, order.Margin, order.Rejected]:
                self.order = None
    
        def next(self):
            if self.order:
                return
            close = self.data.close[0]
            try:
                highs = self.data.high.get(size=5)
                lows = self.data.low.get(size=5)
                if highs.pop(2) > max(highs):
                    self.recent_high = highs.pop(2)
                if lows.pop(2) < min(lows):
                    self.recent_low = lows.pop(2)
            except Exception as e:
                self.log(e)
                
            # Check if we are in the market
            if not self.position:
                # if self.crossover > 0:
                if self.fast_sma[0] > self.slow_sma[0] and self.fast_sma[-1] < self.slow_sma[-1]:
                    long_tp = close + 0.02
                    long_stop = self.recent_low
                    self.order = self.buy_bracket(limitprice=long_tp, stopprice=long_stop,exectype=bt.Order.Market)
    
            # elif self.crossover < 0:
            elif self.fast_sma[0] < self.slow_sma[0] and self.fast_sma[-1] > self.slow_sma[-1]:
                short_tp = close - 0.02
                short_stop = self.recent_high
                self.order = self.sell_bracket(limitprice=short_tp, stopprice=short_stop, exectype=bt.Order.Market)
             
    
    cerebro = bt.Cerebro()
    
    complete_data = bt.feeds.YahooFinanceCSVData(dataname='EURUSD=X-yahoo.csv', round=False)
    
    cerebro.adddata(complete_data)
    
    # Add strategy to Cerebro
    cerebro.addstrategy(MAcrossover)
    
    # Add strategy to Cerebro
    cerebro.addanalyzer(bt.analyzers.SharpeRatio)
    cerebro.addanalyzer(bt.analyzers.DrawDown)
    cerebro.addanalyzer(bt.analyzers.PyFolio)
    if __name__ == '__main__':
        # Run Cerebro Engine
        start_portfolio_value = cerebro.broker.getvalue()
        thestrats = cerebro.run()
        print('Sharpe Ratio:', thestrats[0].analyzers.sharperatio.get_analysis()['sharperatio'])
        print('Max Draw Down:', thestrats[0].analyzers.drawdown.get_analysis()['max']['drawdown'])
        # print('PyFolio:', thestrats[0].analyzers.pyfolio.get_analysis())
        end_portfolio_value = cerebro.broker.getvalue()
        pnl = end_portfolio_value - start_portfolio_value
        print('Starting Portfolio Value: %.2f' % start_portfolio_value)
        print('Final Portfolio Value: %.2f' % end_portfolio_value)
        print('PnL: %.2f' % pnl)
    
        # print("Sharpe Ratio ", sharpe['sharperatio'])
    
        cerebro.plot(style='candlestick', barup='green', subplot=True)
    


  • @vladisld I just shared the Code,please take a look and tell me what my mistakes / blunders are



  • @run-out I just shared the Code,please take a look and tell me what my mistakes / blunders are



  • I thought it might be easier just to generate the code that should be very close to what you are looking for.

    I wasn't sure what you were trying to accomplish with the 'recent_highs' and lows so I just took the max/min over get 5 days.

    import backtrader as bt
    
    class MAcrossoveer(bt.Strategy):
        # Moving average parameters
        params = (('pfast', 8), ('pslow', 21),)
    
        def __init__(self):
            # Keeps track of bracket orders.
            self.o_li = list()
    
            self.ema_fast = bt.ind.EMA(self.data, period=self.p.pfast)
            self.ema_slow = bt.ind.EMA(self.data, period=self.p.pslow)
    
            self.crossover = bt.ind.CrossUp(self.ema_fast, self.ema_slow)
    
    
        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 print_signal(self):
            """ Prints OHLCV """
            self.log(
                "o {:5.2f}\th {:5.2f}\tl {:5.2f}\tc {:5.2f}\tv {:5.0f}".format(
                    self.datas[0].open[0],
                    self.datas[0].high[0],
                    self.datas[0].low[0],
                    self.datas[0].close[0],
                    self.datas[0].volume[0],
                )
            )
    
        def notify_order(self, order):
            """Triggered upon changes to orders. Notifications on order changes are here."""
    
            # Suppress notification if it is just a submitted order.
            if order.status == order.Submitted:
                return
    
            # Print out the date, security name, order number and status.
            dt, dn = self.datetime.date(), order.data._name
    
            self.log(
                "{} Order {} Status {}".format(dn, order.ref, order.getstatusname())
            )
    
            # Check if an order has been completed
            # Attention: broker could reject order if not enough cash
            if order.status in [order.Completed, order.Margin]:
                if order.isbuy():
                    self.log(
                        "BUY EXECUTED for {}, Price: {:.2f}, Cost: {:.2f}, Comm {:.2f}".format(
                            dn,
                            order.executed.price,
                            order.executed.value,
                            order.executed.comm,
                        )
                    )
                else:  # Sell
                    self.log(
                        "SELL EXECUTED for {}, Price: {:.2f}, Cost: {:.2f}, Comm {:.2f}".format(
                            dn,
                            order.executed.price,
                            order.executed.value,
                            order.executed.comm,
                        )
                    )
    
            # Cleans up the order list.
            if not order.alive() and order in self.o_li:
                self.o_li.remove(order)
    
        def notify_trade(self, trade):
            if not trade.isclosed:
                return
    
            self.log("OPERATION PROFIT, GROSS %.2f, NET %.2f" % (trade.pnl, trade.pnlcomm))
    
        def next(self):
            self.print_signal()
    
            # pending opening orders do nothing
            if len(self.o_li) > 0:
                return
    
            if not self.position and self.crossover == 1:
                self.long_buy_order = self.buy_bracket(
                    data=self.datas[0],
                    size=1,
                    exectype=bt.Order.Market,
                    stopprice=min(self.data.low.get(size=5)),
                    stopexec=bt.Order.Stop,
                    limitprice=self.datas[0].close[0] + 200,
                    limitexec=bt.Order.Limit,
                )
    
                self.log(
                    "LONG BUY at market {}: Oref {} / Buy at {}".format(
                        self.datetime.date(),
                        self.long_buy_order[0].ref,
                        self.data.close[0],
                    )
                )
    
                # Store orders in a list
                self.o_li = [o for o in self.long_buy_order]
    
            else:
                pass
    
            if not self.position and self.crossover == -1:
                self.short_sell_order = self.sell_bracket(
                    data=self.datas[0],
                    size=1,
                    exectype=bt.Order.Market,
                    stopprice=max(self.data.high.get(size=5)),
                    stopexec=bt.Order.Stop,
                    limitprice=self.datas[0].close[0] - 200,
                    limitexec=bt.Order.Limit,
                )
    
                self.log(
                    "SHORT SELL at market {}: Oref {} / Sell at {}".format(
                        self.datetime.date(),
                        self.long_buy_order[0].ref,
                        self.data.close[0],
                    )
                )
    
                # Store orders in a list
                self.o_li = [o for o in self.short_sell_order]
    
            else:
                pass
    
    
    
    if __name__ == "__main__":
    
        cerebro = bt.Cerebro()
    
        data = bt.feeds.GenericCSVData(
            dataname="data/2006-day-001.txt",
            dtformat=("%Y-%m-%d"),
            timeframe=bt.TimeFrame.Days,
            compression=1,
        )
        cerebro.adddata(data)
    
        cerebro.addstrategy(MAcrossoveer)
    
        # Execute
        cerebro.run()
    
    


  • Thanks for that quick response. I was trying to find the swing low point in case of a buy and swing high point in case of a sell.

    I just ran the code you sent and noticed that upon a crossover to the downside it was not going to the sell condition.



  • OK well see if you can debug it from there. If you get stuck, let us know.



  • @run-out Yes sure, thank you very much for your help. Really appreciate it.


Log in to reply
 

});