For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/
Kernel Freeze! Can someone help to vet through my first-ever backtrader code?
-
Trading Instrument : Futures
Position sizing : 1% of total capital / Margin per lot; rounding down to nearest whole number
Indicator(s):- Donchian Channel : 20 periods
- ATR : 20 periods
- Exp Moving Avg : 25 and 350 EMA
Trading Plan:
- Buy if closing Price breaks above Donchian Upper Band & 25 EMA > 350 EMA
- Sell if closing price breaks below Donchian Lower Band & 25 EMA < 350 EMA
- Profit Target & Stop Loss : 2 x ATR(20) of the entry price
Code Snippets:
import backtrader as bt import datetime # For datetime objects # Create a custom datafeed from csv class customCSV(bt.feeds.GenericCSVData): params = ( ('fromdate', datetime.datetime(2000, 1, 1)), ('todate', datetime.datetime(2020, 8, 31)), ('dtformat', '%Y-%m-%d'), ('datetime', 0), #index column ('open', 1), ('high', 2), ('low', 3), ('close', 4), ('volume', 5), ('openinterest', -1), #did not import this column ) class FixedPerc(bt.Sizer): '''This sizer simply returns a fixed size for any operation Params: - ``perc`` (default: ``0.20``) Perc of cash to allocate for operation - ``margin`` (default : 5000) Margin per lot ''' params = ( ('perc', 0.01), ('margin', 5000) # margin per lot ) def _getsizing(self, comminfo, cash, data, isbuy): cashtouse = self.p.perc * cash size = cashtouse // self.p.margin return size # Donchian Channel Indicator class DonchianChannels(bt.Indicator): ''' Params Note: - ``lookback`` (default: -1) If `-1`, the bars to consider will start 1 bar in the past and the current high/low may break through the channel. If `0`, the current prices will be considered for the Donchian Channel. This means that the price will **NEVER** break through the upper/lower channel bands. ''' alias = ('DCH', 'DonchianChannel',) lines = ('dcm', 'dch', 'dcl',) # dc middle, dc high, dc low params = dict( period=20, lookback=-1, # consider current bar or not ) plotinfo = dict(subplot=False) # plot along with data plotlines = dict( dcm=dict(ls='--'), # dashed line dch=dict(_samecolor=True), # use same color as prev line (dcm) dcl=dict(_samecolor=True), # use same color as prev line (dch) ) def __init__(self): hi, lo = self.data.high, self.data.low if self.p.lookback: # move backwards as needed hi, lo = hi(self.p.lookback), lo(self.p.lookback) self.l.dch = bt.ind.Highest(hi, period=self.p.period) self.l.dcl = bt.ind.Lowest(lo, period=self.p.period) self.l.dcm = (self.l.dch + self.l.dcl) / 2.0 # avg of the above # Create a Strategy class TestStrategy(bt.Strategy): # Set the parameters params = ( ('fast_ema_period', 25), ('slow_ema_period', 350), ('donchian_period', 20), ('ATR_period', 20), ('ATR_dist', 2.0) ) def log(self, txt, dt = None): ''' Logging Function for this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # Keep a reference to the 'Close' line in the data[0] dataseries self.dataclose = self.data.close[0] # Set up the indicators self.emafast = bt.indicators.ExponentialMovingAverage(self.dataclose, period = self.p.fast_ema_period) self.emaslow = bt.indicators.ExponentialMovingAverage(self.dataclose, period = self.p.slow_ema_period) self.donchian = DonchianChannels() self.atr = bt.indicators.ATR(self.datas[0], period=self.p.ATR_period) # Keep track of pending order + price + commission self.order = None self.buyprice = None self.buycomm = None def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted by broker. Do nothing return #Check if order has been completed if order.status in [Order.Completed]: if order.isbuy(): self.log( 'LONG EXECUTED, Price: %.2f, Cost: %.2f, Comm: %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm else: # Sell self.log( 'SHORT EXECUTED, Price: %.2f, Cost: %.2f, Comm: %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') self.order = None def notify_trade(self, trade): if not trade.isclosed: return # open position PnL self.log('OPERATION PROFIT, Gross %.2f, Net %.2f' % (trade.pnl, trade.pnlcomm)) def next(self): # Log closing price for reference self.log('Close, %.2f' % self.dataclose) # Check if an order is pending, if yes then we do nothing if self.order: return # set up parameters for SL and TP pdist = self.atr[0] * self.p.ATR_dist long_stop = self.dataclose - pdist short_stop = self.dataclose + pdist long_profit = self.dataclose + pdist short_profit = self.dataclose - pdist # Check if we are in the market if not self.position: # not in the market # Trade Logic if self.dataclose > self.donchian.dch[0]: if self.emafast[0] > self.emaslow[0]: # Place BUY trade : 1% of portfolio value self.log('BUY CREATE, %.2f' % self.dataclose) self.order_target_percent(target = 0.01) self.buy() elif self.dataclose < self.donchian.dcl[0]: if self.emafast[0] < self.emaslow[0]: # Place SELL trade : 1% of portfolio value self.log('SELL CREATE, %.2f' % self.dataclose) self.order_target_percent(target = 0.01) self.sell() else: # in the market if self.position.size > 0: # For long positions if self.dataclose < long_stop or self.dataclose > long_profit: self.close() elif self.position.size < 0: # For short positions if self.dataclose > short_stop or self.dataclose < short_profit: self.close() else: return # Setting the Cash if __name__ == '__main__': # Create a cerebro entity cerebro = bt.Cerebro() # Add a strategy cerebro.addstrategy(TestStrategy) # Create a data feed data = customCSV(dataname = 'CrudeDaily.csv') # Add the data feed to cerebro cerebro.adddata(data) # Set our desired cash start cerebro.broker.setcash(500000.0) # Add a sizer according to stake cerebro.addsizer(FixedPerc) # Set the commission cerebro.broker.setcommission(commission = 5.0, margin = 5000.0) # Print out the starting conditions print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue()) # Run over everything cerebro.run() # Print out the final result print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
Thank You for the help!