# Optimizing parameters of the trading strategy

In this post, we show how to optimize variables in your strategy. In our previous post on introducing indicators in to the backtester, we found that moving average over 15 days led to the a losing trading strategy. Thus, we might want to evalute which is the best period (i.e., 5, 10, 15, 20) to use to ensure that our trading strategy is profitable. As a rule, it is best not to over optimize your trading strategy as it will not generalize well in an out-of-sample test.

1. We set printlog to False in the Strategy as we do not want a print of trade in each backtest that is applied to each parameter that is being optimized.
2. We add an if statement to the log function in the Strategy class that only logs data if the settings are set to True.
3. We use cerebro.optstrategy() instead of cerebro.addstrategy(). We optimize the strategy over a range of MA periods from 10 to 31.

The code additions here are in the following cells:

1. Strategy Class
2. Backtest Settings

## Import modules¶

In [7]:
import datetime
import os.path
import sys
import matplotlib.pyplot as plt


## Data¶

In [8]:
homepath = os.getenv('HOME')

data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
fromdate=datetime.datetime(2000,1,1),
todate = datetime.datetime(2000,12,31),
reverse=False)


## Strategy Class¶

In params, set the printlog to False. This is because when we are optimizing over different parameters, we don't want to see all the trades that are executed each time a different backtest is applied to each parameter. We just want to see what the Final value of the portfolio is when each parameter is applied in the backtest.

In the log function, we add an additional if statement to only log data if self.params.printlog or doprint are True.

We add a stop function, that prints out the MA period that is being backtested and the corresponding portfolio value.

In [9]:
class Strat2_BGTMA_SLSMA(bt.Strategy):

params = (
('maperiod',15), # Tuple of tuples containing any variable settings required by the strategy.
('printlog',False), # Stop printing the log of the trading 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.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.maperiod)

def log(self, txt, dt=None, doprint=False):
if self.params.printlog or doprint: # Add if statement to only log of printlog or doprint is True
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]:
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))

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

return

self.log('OPERATION PROFIT, GROSS {0:8.2f}, NET {1:8.2f}'.format(

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]:
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()

def stop(self):
self.log('MA Period: {0:8.2f} Ending Value: {1:8.2f}'.format(
self.params.maperiod,
self.broker.getvalue()),
doprint=True)


## Backtest settings¶

Rather than using cerebro.addstrategy(), we now use cerebro.optstrategy() as we are optimizing the strategy over a variety of parameters in our SMA indicator. In this case we are testing all the maperiod from 10, 11, 12, ..., 30, 31 bars.

In [10]:
cerebro = bt.Cerebro()
strats = cerebro.optstrategy(
Strat2_BGTMA_SLSMA,
maperiod=range(10,31),
printlog=False)
cerebro.broker.setcash(1000.0)
cerebro.broker.setcommission(commission=0.0)


## Run backtest¶

The maxcpus allows us to trigger how many cpus to run in parallel when performing the optimization. We find that the SMA strategy is profitable when we use values between 18 to 25 days.

In [11]:
cerebro.run(maxcpus=1)

2000-12-29,MA Period:    10.00 Ending Value:   877.50
2000-12-29,MA Period:    11.00 Ending Value:   878.70
2000-12-29,MA Period:    12.00 Ending Value:   839.80
2000-12-29,MA Period:    13.00 Ending Value:   899.90
2000-12-29,MA Period:    14.00 Ending Value:   902.50
2000-12-29,MA Period:    15.00 Ending Value:   975.60
2000-12-29,MA Period:    16.00 Ending Value:   961.90
2000-12-29,MA Period:    17.00 Ending Value:   952.60
2000-12-29,MA Period:    18.00 Ending Value:  1011.00
2000-12-29,MA Period:    19.00 Ending Value:  1039.40
2000-12-29,MA Period:    20.00 Ending Value:  1073.20
2000-12-29,MA Period:    21.00 Ending Value:  1055.10
2000-12-29,MA Period:    22.00 Ending Value:  1057.60
2000-12-29,MA Period:    23.00 Ending Value:  1021.50
2000-12-29,MA Period:    24.00 Ending Value:  1018.80
2000-12-29,MA Period:    25.00 Ending Value:  1012.40
2000-12-29,MA Period:    26.00 Ending Value:   998.30
2000-12-29,MA Period:    27.00 Ending Value:   983.10
2000-12-29,MA Period:    28.00 Ending Value:   976.90
2000-12-29,MA Period:    29.00 Ending Value:   984.20
2000-12-29,MA Period:    30.00 Ending Value:   980.80

Out[11]:
[[<backtrader.cerebro.OptReturn at 0x7d54a5bb05c0>],
[<backtrader.cerebro.OptReturn at 0x7d54a5a65c18>]]

## Conclusion¶

In this post, we learnt how to optimize parameters in our trading strategy.

1. We learnt how to use cerebro.optstrategy where we indicate which parameter we want to optimize and the range of parameters to evaluate the strategy's performance.
2. We altered the log function of the Strategy class to only print the values of the parameter being optimized and its corresponding portfolio value.
In [ ]: