Customizing the trading strategy
In this post, we introduce other settings to customize the trading strategy as follows:
- Creating a tuple to easily alter static parameters in our trading strategy.
- Change the position size of each trade.
The code additions here are in the following cells:
- Strategy Class
- Backtest settings
Import modules¶
import datetime
import os.path
import sys
import backtrader as bt
import matplotlib.pyplot as plt
Data¶
homepath = os.getenv('HOME')
datapath = os.path.join(homepath, 'github/backtrader/datas/orcl-1995-2014.txt')
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
fromdate=datetime.datetime(2000,1,1),
todate = datetime.datetime(2000,12,31),
reverse=False)
Strategy Class¶
We add a tuple of tuples called params
at the top of the class so we can easily update these strategy variables. The exitbars
is set to 10 instead of 5.
We also update the sell signal from (self.bar_executed+5)
to (self.bar_executed+self.params.exitbars)
.
class Strat1_2BD_5BH(bt.Strategy):
params = (
('exitbars',10), # Tuple of tuples containing any variable settings required by the strategy.
)
def __init__(self):
self.dataclose= self.datas[0].close # Keep a reference to the "close" line in the data[0] dataseries
self.order = None # Property to keep track of pending orders. There are no orders when the strategy is initialized.
self.buyprice = None
self.buycomm = None
def log(self, txt, dt=None):
# Logging function for the strategy. 'txt' is the statement and 'dt' can be used to specify a specific datetime
dt = dt or self.datas[0].datetime.date(0)
print('{0},{1}'.format(dt.isoformat(),txt))
def notify_order(self, order):
# 1. If order is submitted/accepted, do nothing
if order.status in [order.Submitted, order.Accepted]:
return
# 2. If order is buy/sell executed, report price executed
if order.status in [order.Completed]:
if order.isbuy():
self.log('BUY EXECUTED, Price: {0:8.2f}, Cost: {1:8.2f}, Comm: {2:8.2f}'.format(
order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else:
self.log('SELL EXECUTED, {0:8.2f}, Cost: {1:8.2f}, Comm{2:8.2f}'.format(
order.executed.price,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self) #when was trade executed
# 3. If order is canceled/margin/rejected, report order canceled
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
self.log('OPERATION PROFIT, GROSS {0:8.2f}, NET {1:8.2f}'.format(
trade.pnl, trade.pnlcomm))
def next(self):
# Log the closing prices of the series from the reference
self.log('Close, {0:8.2f}'.format(self.dataclose[0]))
if self.order: # check if order is pending, if so, then break out
return
# since there is no order pending, are we in the market?
if not self.position: # not in the market
if self.dataclose[0] < self.dataclose[-1]:
if self.dataclose[-1] < self.dataclose[-2]:
self.log('BUY CREATE {0:8.2f}'.format(self.dataclose[0]))
self.order = self.buy()
else: # in the market
if len(self) >= (self.bar_executed+self.params.exitbars):
self.log('SELL CREATE, {0:8.2f}'.format(self.dataclose[0]))
self.order = self.sell()
Backtest settings¶
Parameters that are defined in the trading strategy can also be modified in the cerebro.addstrategy()
function and this will override the parameters set within the Strategy class. We see that we have set exitbars=10
.
For the size of each trade, we have set a cerebro.addsizer
that sets the number of units (e.g., futures contracts, share lots) to be purchased. In the below code, we have set the stake=10
such that 10 units of the asset is purchased each time there is a successful Buy signal.
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(Strat1_2BD_5BH, exitbars=10)
cerebro.addsizer(bt.sizers.FixedSize,stake=10)
cerebro.broker.setcash(100000.0)
cerebro.broker.setcommission(commission=0.001)
Run backtest¶
We can see from below that the Buy signal is still the same as buy orders are created after the closing price is down twice in a row, except that Sell signal is triggered only after 10 trading days (i.e, Buy is on 2000-01-06, and Sell is on 2000-01-21). The trading strategy continues to be profitable with a Final value of $100155.
print('Starting Portfolio Value: {0:8.2f}'.format(cerebro.broker.getvalue()))
cerebro.run()
print('Final Portfolio Value: {0:8.2f}'.format(cerebro.broker.getvalue()))
Plot backtest results¶
%matplotlib inline
cerebro.plot()
Conclusion¶
In this post, we learnt how to customize the trading strategy as follows:
- We learnt how to set static parameters using a tuple of tuples at the top of the strategy class. These parameters can also be modified in the
cerebro.addstrategy()
. - We also set the number of units/contracts for the trading strategy to enter.
In the next post, we will look at adding indicators.
The below code needs to be run to execute cerebro.plot()
successfullly
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(0,100,size=(100,4)),columns = list('ABCD'))
df.head()
df.plot()
Comments
Comments powered by Disqus