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.
-
Hi, I might sound dumb here but just wanted to tell you Im working with FOREX data (Currency Pairs) Ex - EURUSD. So my question is if the starting cash value is 10000 and I am triggering a buy or a sell upon a crossover to the upside or downside respectively then can the first signal be a sell signal (say if the first crossover is on the downside) ??
-
@Sajil-Thamban said in I am new to backtrader and want to build a strategy, details below:
I just ran the code you sent and noticed that upon a crossover to the downside it was not going to the sell condition.
The bug is here:
self.crossover = bt.ind.CrossUp(self.ema_fast, self.ema_slow)
should be:
self.crossover = bt.ind.CrossOver(self.ema_fast, self.ema_slow)
-
@Sajil-Thamban said in I am new to backtrader and want to build a strategy, details below:
can the first signal be a sell signal (say if the first crossover is on the downside)
I the
__init__
section include a variable set to None:self.trades_occured = False
Then include this in the long section to reject the trade if it's the first crossover. The second crossover must be the opposite so this should work.
if not self.position and self.crossover == 1 and self.trades_occured:
Then once the first sell happens change self.trades_occured to True.
if not self.position and self.crossover == -1: self.trades_occured = True self.short_sell_order = self.sell_bracket(....