Adding indicators to your trading strategy
In this post, we show how to add indicators in to your strategy.
- We add a simple indicator such as the Simple Moving Average (SMA)
- We apply a strategy that buys (sells) when the closing price of the bar is above (below) the SMA.
- We print the size of each position when a trade is executed.
- We add a x6 indicators to be added to the
cerebro.plot()
output.
The code additions here are in the following cells:
- Strategy Class
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¶
In the __init__
function, we add a Simple Moving Average (SMA) indicator. We inform the strategy that the SMA indicator should be based off the most current datapoint (i.e., self.datas[0]), and that the length of the moving average is based off the parameters maperiod
which is set at 15. We also add a whole set of indicators using bt.indicators
to be added to the backtest result plot function (i.e., cerebro.plot()
).
In the next
function, our strategy is such that when the current closing price is greater (less) than the SMA, we buy (sell).
In the notify
function, we also print out the size of the order based on order.executed.size
.
class Strat2_BGTMA_SLSMA(bt.Strategy):
params = (
('maperiod',15), # 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
# Add SimpleMovingAverage indicator for use in the trading strategy
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.maperiod)
# Add ExpMA, WtgMA, StocSlow, MACD, ATR, RSI indicators for plotting.
bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
bt.indicators.WeightedMovingAverage(self.datas[0], period=25,subplot = True)
bt.indicators.StochasticSlow(self.datas[0])
bt.indicators.MACDHisto(self.datas[0])
rsi = bt.indicators.RSI(self.datas[0])
bt.indicators.SmoothedMovingAverage(rsi, period=10)
bt.indicators.ATR(self.datas[0], plot = False)
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}, Size: {1:8.2f} Cost: {2:8.2f}, Comm: {3:8.2f}'.format(
order.executed.price,
order.executed.size,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else:
self.log('SELL EXECUTED, {0:8.2f}, Size: {1:8.2f} Cost: {2:8.2f}, Comm{3:8.2f}'.format(
order.executed.price,
order.executed.size,
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.sma[0]:
self.log('BUY CREATE {0:8.2f}'.format(self.dataclose[0]))
self.order = self.buy()
else: # in the market
if self.dataclose[0] < self.sma[0]:
self.log('SELL CREATE, {0:8.2f}'.format(self.dataclose[0]))
self.order = self.sell()
Backtest settings¶
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(Strat2_BGTMA_SLSMA, maperiod=15)
cerebro.addsizer(bt.sizers.FixedSize,stake=10)
cerebro.broker.setcash(1000.0)
cerebro.broker.setcommission(commission=0.0)
Run backtest¶
We see that the Ending Portfolio Value is about $975 showing that the strategy is losing money.
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¶
We can see that the below plot now has x7 charts
- Accumulation of wealth. Shows the equity and cash value of your account.
- Profitability of each trade. Shows net profit/loss of each trade.
- Price chart. Shows the prices, buy/sell signals, the SimpleMA and the ExpMA, and the volume of trades.
- Weighted Moving Average
- Stochastic Slow
- MACD
- RSI
%matplotlib inline
cerebro.plot()
Conclusion¶
In this post, we learnt how to add indicators to the trading strategy as follows:
- We learnt how to add indicators by using the
bt.indicators
. - We learnt how to include additional indicators in the the
cerebro.plot()
function.
In the next post, we will look at optimizing the parameters of the strategy.
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()