Solved!
The problem was in init(self):
self.l.OB = OB = (self.l.d / self.l.d) * self.p.OB
self.l.OS = OS = ((self.l.d / self.l.d) * 100) - self.l.OB
Added aline of code to have an integer as a line.
Solved!
The problem was in init(self):
self.l.OB = OB = (self.l.d / self.l.d) * self.p.OB
self.l.OS = OS = ((self.l.d / self.l.d) * 100) - self.l.OB
Added aline of code to have an integer as a line.
@frankiec Forgot to post Custom MACD
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import backtrader as bt
from backtrader.indicators import EMA
class MACD(bt.Indicator):
lines = ('macd', 'signal', 'histo', 'zero', 'delta')
params = (('period_me1', 12), ('period_me2', 26), ('period_signal', 9))
def __init__(self):
me1 = EMA(self.data, period=self.p.period_me1)
me2 = EMA(self.data, period=self.p.period_me2)
self.l.macd = macd = me1 - me2
self.l.signal = signal = EMA(self.l.macd, period=self.p.period_signal)
self.l.delta = macd - signal
self.l.histo = self.l.macd - self.l.signal
self.l.zero = me1 - me1
Hey there,
Can anyone test this indicator I made up to include double smoothing of stochastic but with SafeDiv on?
Apparently it doesn't work on my data as I always get and error for float division by zero.
class Stochastic_DoubleSmooth(bt.Indicator):
'''
Doesn't need to follow the OHLC standard naming and it includes the SafeDiv
'''
lines = ('k', 'd', 'OB', 'OS')
params = (('period',14),('pd',3),('pdslow',2),('OB',71),
('movav',bt.indicators.SMA),('slowav',None),
('safediv', True), ('safezero', 0.0))
def __init__(self):
# Get highest from period k from 1st data
highesthigh = bt.ind.Highest(self.data.high, period=self.p.period)
# Get lowest from period k from 2nd data
lowestlow = bt.ind.Lowest(self.data.low, period=self.p.period)
#SafeDiv not working???
knum = self.data.close - lowestlow
kden = highesthigh - lowestlow
if self.p.safediv:
kraw = 100.0 * bt.DivByZero(knum, kden, zero=self.p.safezero)
else:
kraw = 100.0 * (knum / kden)
# The standard k in the indicator is a smoothed versin of K
self.l.k = k = self.p.movav(kraw, period=self.p.pd)
# Smooth k => d
slowav = self.p.slowav or self.p.movav # chose slowav
self.l.d = slowav(k, period=self.p.pdslow)
self.l.OB = OB = (self.l.d / self.l.d) * self.p.OB
self.l.OS = OS = ((self.l.d / self.l.d) * 100) - self.l.OB
This is the error I get.
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3441, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-9dfd15ad4cb9>", line 1, in <module>
runfile('/Users/frankie/PycharmProjects/PycharmProjectsShared/TradingProject/StrategyScripts/CAStrategy/CA_Macd_Stoch_Backtesting.py', wdir='/Users/frankie/PycharmProjects/PycharmProjectsShared/TradingProject/StrategyScripts/CAStrategy')
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "User/Fc/PycharmProjects/PycharmProjectsShared/TradingProject/StrategyScripts/Strategy/Macd_Stoch_Backtesting.py", line 411, in <module>
Cerebro = cerebro.run(tradehistory=True)
File "/usr/local/lib/python3.9/site-packages/backtrader/cerebro.py", line 1127, in run
runstrat = self.runstrategies(iterstrat)
File "/usr/local/lib/python3.9/site-packages/backtrader/cerebro.py", line 1293, in runstrategies
self._runonce(runstrats)
File "/usr/local/lib/python3.9/site-packages/backtrader/cerebro.py", line 1652, in _runonce
strat._once()
File "/usr/local/lib/python3.9/site-packages/backtrader/lineiterator.py", line 297, in _once
indicator._once()
File "/usr/local/lib/python3.9/site-packages/backtrader/lineiterator.py", line 297, in _once
indicator._once()
File "/usr/local/lib/python3.9/site-packages/backtrader/linebuffer.py", line 631, in _once
self.once(self._minperiod, self.buflen())
File "/usr/local/lib/python3.9/site-packages/backtrader/linebuffer.py", line 755, in once
self._once_op(start, end)
File "/usr/local/lib/python3.9/site-packages/backtrader/linebuffer.py", line 772, in _once_op
dst[i] = op(srca[i], srcb[i])
ZeroDivisionError: float division by zero
I really can't figure it out!!
Have the same problem here!!
Tried using a list of float for devfactor.
if __name__ == '__main__':
# Variable for our starting cash
startcash = 100000
# Create a cerebro entity
cerebro = bt.Cerebro(optreturn=False)
# Add a strategy. ADDED A LIST OF FLOAT INSTEAD OF NUMPY
cerebro.optstrategy(TestStrategy, period=range(10, 41, 1), devfactor=[1.0, 2.0, 3.0])
data = btfeeds.GenericCSVData(
dataname='CME_MINI_NQ1!Actual.csv',
fromdate=fromdate,
todate=todate,
sessionstart=datetime.time(13, 00),
sessionend=datetime.time(18, 00),
dtformat=('%Y-%m-%d %H:%M:%S'),
datetime=1,
high=3,
low=4,
open=2,
close=5,
volume=6,
openinterest=-1,
timeframe=bt.TimeFrame.Minutes,
compression=1
)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
cerebro.addanalyzer(TotalReturns, _name='PNLS')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='TradeAnalyzer')
cerebro.addanalyzer(bt.analyzers.SQN, _name='SQN')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='DrawDown')
# Set our desired cash start
cerebro.broker.setcash(startcash)
# Set the commission
cerebro.broker.setcommission(commission=0.00014, mult=20)
# Run over everything
print('Run Begin')
# # -------------------------------- SET the cpus -------------------------------- # #
opt_runs = cerebro.run(maxcpus=12)
print('Runs completed: ' + str(len(opt_runs)))
Now I got this.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3437, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-c7f43e7322da>", line 1, in <module>
runfile('/Users/frankie/PycharmProjects/pythonProject3/BollingerOPT.py', wdir='/Users/frankie/PycharmProjects/pythonProject3')
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/frankie/PycharmProjects/pythonProject3/BollingerOPT.py", line 353, in <module>
opt_runs = cerebro.run(maxcpus=12)
File "/usr/local/lib/python3.9/site-packages/backtrader/cerebro.py", line 1143, in run
for r in pool.imap(self, iterstrats):
File "/usr/local/Cellar/python@3.9/3.9.2_4/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/pool.py", line 870, in next
raise value
AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Strateg' object has no attribute 'order'
I'm trying to run a simple Bollinger strategy that buys when close crossover bottom BBand and sells when cross under BBand.
Unfortunately I get this.
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3437, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-c7f43e7322da>", line 1, in <module>
runfile('/Users/frankie/PycharmProjects/pythonProject3/BollingerOPT.py', wdir='/Users/frankie/PycharmProjects/pythonProject3')
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/frankie/PycharmProjects/pythonProject3/BollingerOPT.py", line 353, in <module>
opt_runs = cerebro.run(maxcpus=12)
File "/usr/local/lib/python3.9/site-packages/backtrader/cerebro.py", line 1143, in run
for r in pool.imap(self, iterstrats):
File "/usr/local/Cellar/python@3.9/3.9.2_4/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/pool.py", line 870, in next
raise value
TypeError: only size-1 arrays can be converted to Python scalars
Any hint?
I had problems by changing the parameter "devfactor" to "multiplier" but as soon as I changed that I had another problem.
I post here the code.
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import time
start_time = time.time()
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
import pandas as pd
import matplotlib
import os
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
import pytz
import numpy as np
from pandas import DataFrame
import backtrader as bt
from collections import OrderedDict
import tabulate
# DataImportVar
fromdate = datetime.datetime(2021, 2, 7, 12, 00)
todate = datetime.datetime(2021, 4, 9, 19, 00)
# SessionTimeFilter
Session_begin = datetime.time(12, 0)
Session_end = datetime.time(19, 0)
if os.getcwd()=='/Users/frankie/PycharmProjects/pythonProject3':
Directory = '/Users/frankie'
else:
Directory='/Users/francescocerutti_imac_hs/Desktop/FrankieHD27'
class TotalReturns(bt.Analyzer):
'''This analyzer reports the total pnl for strategy
Params:
- timeframe (default: ``None``)
If ``None`` then the timeframe of the 1st data of the system will be
used
- compression (default: ``None``)
Only used for sub-day timeframes to for example work on an hourly
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
If ``None`` then the compression of the 1st data of the system will be
used
Methods:
- get_analysis
Returns a dictionary with returns as values and the datetime points for
each return as keys
'''
def __init__(self):
self.total_pnl = 0
self.unrealized = 0 # Unrealized pnl for all positions all strategies
self.positions = OrderedDict.fromkeys([d._name or 'Data%d' % i
for i, d in enumerate(self.datas)], 0) # Current strategy positions
def start(self):
tf = min(d._timeframe for d in self.datas)
self._usedate = tf >= bt.TimeFrame.Minutes
def notify_order(self, order):
if order.status in [order.Completed, order.Partial]:
self.total_pnl += order.executed.pnl - order.executed.comm
self.positions[order.data._name] += order.executed.size
def next(self):
if self._usedate:
self.rets[self.strategy.datetime.date()] = self.total_pnl
else:
self.rets[self.strategy.datetime.datetime()] = self.total_pnl
def stop(self):
for dname in self.positions:
self.unrealized += (self.strategy.dnames[dname].close[0] -
self.strategy.positionsbyname[dname].price) * \
self.strategy.positionsbyname[dname].size
def get_analysis(self):
return self.rets
'''
class BOLLStrat(bt.Strategy):
This is a simple mean reversion bollinger band strategy.
Entry Criteria:
- Long:
- Price closes below the lower band
- Stop Order entry when price crosses back above the lower band
- Short:
- Price closes above the upper band
- Stop order entry when price crosses back below the upper band
Exit Critria
- Long/Short: Price touching the median line
params = (
("period", 20),
("devfactor", 2),
("size", 20),
("debug", False)
)
def __init__(self):
self.boll = bt.indicators.BollingerBands(period=self.p.period, devfactor=self.p.devfactor)
# self.sx = bt.indicators.CrossDown(self.data.close, self.boll.lines.top)
# self.lx = bt.indicators.CrossUp(self.data.close, self.boll.lines.bot)
def next(self):
orders = self.broker.get_orders_open()
# Cancel open orders so we can track the median line
if orders:
for order in orders:
self.broker.cancel(order)
if not self.position:
if self.data.close > self.boll.lines.top:
self.sell(exectype=bt.Order.Stop, price=self.boll.lines.top[0], size=self.p.size)
if self.data.close < self.boll.lines.bot:
self.buy(exectype=bt.Order.Stop, price=self.boll.lines.bot[0], size=self.p.size)
else:
if self.position.size > 0:
self.sell(exectype=bt.Order.Limit, price=self.boll.lines.mid[0], size=self.p.size)
else:
self.buy(exectype=bt.Order.Limit, price=self.boll.lines.mid[0], size=self.p.size)
if self.p.debug:
print('---------------------------- NEXT ----------------------------------')
print("1: Data Name: {}".format(data._name))
print("2: Bar Num: {}".format(len(data)))
print("3: Current date: {}".format(data.datetime.datetime()))
print('4: Open: {}'.format(data.open[0]))
print('5: High: {}'.format(data.high[0]))
print('6: Low: {}'.format(data.low[0]))
print('7: Close: {}'.format(data.close[0]))
print('8: Volume: {}'.format(data.volume[0]))
print('9: Position Size: {}'.format(self.position.size))
print('--------------------------------------------------------------------')
def notify_trade(self, trade):
if trade.isclosed:
dt = self.data.datetime.date()
print('---------------------------- TRADE ---------------------------------')
print("1: Data Name: {}".format(trade.data._name))
print("2: Bar Num: {}".format(len(trade.data)))
print("3: Current date: {}".format(dt))
print('4: Status: Trade Complete')
print('5: Ref: {}'.format(trade.ref))
print('6: PnL: {}'.format(round(trade.pnl, 2)))
print('--------------------------------------------------------------------')
'''
# Create a Strategy
class TestStrategy(bt.Strategy):
params = (
("period", 20),
("devfactor", 2.0)
)
# The first data in the list self.datas[0] is the default data for trading operations and to keep all strategy elements synchronized (it’s the system clock)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
dt1 = self.data0.datetime.time(0)
print('%s,%s, %s' % (dt.isoformat(), dt1.isoformat(), txt))
'''
This is a simple mean reversion bollinger band strategy.
Entry Critria:
- Long:
- Price closes below the lower band
- Stop Order entry when price crosses back above the lower band
- Short:
- Price closes above the upper band
- Stop order entry when price crosses back below the upper band
Exit Critria
- Long/Short: Price touching the median line
'''
def __init__(self):
init_time = time.time()
self.startcash = self.broker.getvalue()
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# Keep a reference to the "close" line in the data[0] dataseries
self.boll = btind.BollingerBands(period=self.p.period, devfactor=self.p.devfactor)
# Signal Booleans
self.sx = btind.CrossDown(self.data.close, self.boll.lines.top)
self.lx = btind.CrossUp(self.data.close, self.boll.lines.bot)
# Indicators for the plotting show
# bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
# bt.indicators.WeightedMovingAverage(self.datas[0], period=25,subplot=True)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - 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.log(
'BUY EXECUTED, Size: %.2f, OpenPrice: %.2f, Closeprice: %.2f, Comm %.2f' %
(order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
self.opsize = order.executed.size
else: # Sell
self.log(
'SELL EXECUTED, Size: %.2f, OpenPrice: %.2f, Closeprice: %.2f, Comm %.2f' %
(order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
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):
if self.data.datetime.time() < Session_begin:
self.close() # don't operate until x
return #
if self.data.datetime.time() > Session_end:
self.close() # don't operate after y
return #
if self.lx == 1:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.close()
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
elif self.sx == 1:
# SELL, SELL, SELL!!! (with all possible default parameters)
self.close()
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
def stop(self):
pnl = round(self.broker.getvalue() - self.startcash, 3)
print('SuTR Period: {} SuTR Mult: {}Final PnL: {}'.format(
self.params.period, self.params.devfactor, pnl))
if __name__ == '__main__':
# Variable for our starting cash
startcash = 100000
# Create a cerebro entity
cerebro = bt.Cerebro(optreturn=False)
# Add a strategy
cerebro.optstrategy(TestStrategy, period=range(10, 41, 1), devfactor=np.linspace(1.0, 4.0, 31))
data = btfeeds.GenericCSVData(
dataname='CME_MINI_NQ1!Actual.csv',
fromdate=fromdate,
todate=todate,
sessionstart=datetime.time(13, 00),
sessionend=datetime.time(18, 00),
dtformat=('%Y-%m-%d %H:%M:%S'),
datetime=1,
high=3,
low=4,
open=2,
close=5,
volume=6,
openinterest=-1,
timeframe=bt.TimeFrame.Minutes,
compression=1
)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
cerebro.addanalyzer(TotalReturns, _name='PNLS')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='TradeAnalyzer')
cerebro.addanalyzer(bt.analyzers.SQN, _name='SQN')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='DrawDown')
# Set our desired cash start
cerebro.broker.setcash(startcash)
# Set the commission
cerebro.broker.setcommission(commission=0.00014, mult=20)
# Run over everything
print('Run Begin')
# # -------------------------------- SET the cpus -------------------------------- # #
opt_runs = cerebro.run(maxcpus=20)
print('Runs completed: ' + str(len(opt_runs)))
Thanks in advance
I'm running my code to optimize a strategy with a single indicator and 2 parameters as variables (Period and Multiplier).
So far I managed to get the final PNL for each strategy (Thanks to the codes posted on this community..."Thanks Guys!!"). By final I mean the total sum of profit and losses in 1 min chart data of 40 days of Nasdaq Futures.
Now I need to get the data of daily PNL for each combination of variables to have the progression of 40 days for each one of the 961 combinations (31 period x 31 multiplier).
Can someone help me as I've seen some code about the topic but I have no clue on how to implement it into my Optimization.
# Create a Strategy
class TestStrategy(bt.Strategy):
params = (('period', 18), ('multiplier', 2.8))
# The first data in the list self.datas[0] is the default data for trading operations and to keep all strategy elements synchronized (it’s the system clock)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
dt1 = self.data0.datetime.time(0)
print('%s,%s, %s' % (dt.isoformat(), dt1.isoformat(), txt))
def __init__(self):
init_time = time.time()
self.startcash = self.broker.getvalue()
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.Superbands = SuperTrendBand(period=self.params.period, multiplier=self.params.multiplier)
self.Supertrend = SuperTrend(period=self.params.period, multiplier=self.params.multiplier)
# To keep track of pending orders and buy price/commission
self.order = None
self.buyprice = None
self.buycomm = None
# Indicators for the plotting show
#bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
#bt.indicators.WeightedMovingAverage(self.datas[0], period=25,subplot=True)
#bt.indicators.ATR(self.datas[0], plot=False)
self.buyp_sellm = bt.ind.CrossOver(self.dataclose,self.Supertrend)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - 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.log(
'BUY EXECUTED, Size: %.2f, OpenPrice: %.2f, Closeprice: %.2f, Comm %.2f' %
(order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
self.opsize = order.executed.size
else: # Sell
self.log(
'SELL EXECUTED, Size: %.2f, OpenPrice: %.2f, Closeprice: %.2f, Comm %.2f' %
(order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
if self.data.datetime.time() > Session_begin:
if hasattr(self, 'total_pnl'):
self.total_pnl += order.executed.pnl - order.executed.comm
else:
self.log(
'PNL, Size: %.2f' %
(self.total_pnl
))
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
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):
if self.data.datetime.time() < Session_begin:
self.close()# don't operate until x
return #
if self.data.datetime.time() > Session_end:
self.close()# don't operate after y
return #
# Simply log the closing price of the series from the reference
#self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# Check if we are in the market
#if not self.position:
#CB0=self.dataclose[0] > self.Supertrend[0]
#CB1=self.dataclose[-1] < self.Supertrend [-1]
# Not yet ... we MIGHT BUY if ...
if self.buyp_sellm==1:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.close()
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
elif self.buyp_sellm==-1:
# SELL, SELL, SELL!!! (with all possible default parameters)
self.close()
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
def stop(self):
pnl = round(self.broker.getvalue() - self.startcash, 3)
print('SuTR Period: {} SuTR Mult: {}Final PnL: {}'.format(
self.params.period,self.params.multiplier, pnl))
if __name__ == '__main__':
# Variable for our starting cash
startcash = 100000
# Create a cerebro entity
cerebro = bt.Cerebro(optreturn=False)
# Add a strategy
cerebro.optstrategy(TestStrategy, period=range(10, 41, 1), multiplier=np.linspace(1.0,4.0,31))
data = btfeeds.GenericCSVData(
dataname='CME_MINI_NQ1!Actual.csv',
fromdate=fromdate,
todate=todate,
sessionstart=datetime.time(14, 00),
sessionend=datetime.time(19, 00),
dtformat=('%Y-%m-%d %H:%M:%S'),
datetime=1,
high=3,
low=4,
open=2,
close=5,
volume=-1,
openinterest=-1,
timeframe=bt.TimeFrame.Minutes,
compression=1
)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
cerebro.addanalyzer(TotalReturns, _name='PNLS')
# Set our desired cash start
cerebro.broker.setcash(startcash)
# Set the commission
cerebro.broker.setcommission(commission=0.00014, mult=20)
# Run over everything
opt_runs = cerebro.run()
# Generate results list
final_results_list = []
for run in opt_runs:
for strategy in run:
value = round(strategy.broker.get_value(), 2)
PnL = round(value - startcash, 2)
period = strategy.params.period
multiplier= round(strategy.params.multiplier,2)
final_results_list.append([period,multiplier, PnL])
# Analyzers results
strategy.analyzers.TotalReturns.print()
# Sort Results List
by_period = sorted(final_results_list, key=lambda x: x[0])
by_PnL = sorted(final_results_list, key=lambda x: x[2], reverse=False)
# Print results
print('Results: Ordered by Profit:')
for result in by_PnL:
print('SuTR Period: {}, SuTR Mult: {},Final PnL: {}'.format(result[0], result[1], result[2]))
print("Opt Start on ",fromdate, "and ends on",todate, 'trading session between', Session_begin, 'and', Session_end)
Have tried to add this observer (found it in the community by @oalexandere
) to the strategy but I can't figure out how to get it into a list/dataframe of 40x961 or 961x40.
class TotalReturns(bt.Analyzer):
'''This analyzer reports the total pnl for strategy
Params:
- timeframe (default: ``None``)
If ``None`` then the timeframe of the 1st data of the system will be
used
- compression (default: ``None``)
Only used for sub-day timeframes to for example work on an hourly
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
If ``None`` then the compression of the 1st data of the system will be
used
Methods:
- get_analysis
Returns a dictionary with returns as values and the datetime points for
each return as keys
'''
def __init__(self):
self.total_pnl = 0
self.unrealized = 0 # Unrealized pnl for all positions all strategies
self.positions = OrderedDict.fromkeys([d._name or 'Data%d' % i
for i, d in enumerate(self.datas)], 0) # Current strategy positions
def start(self):
tf = min(d._timeframe for d in self.datas)
self._usedate = tf >= bt.TimeFrame.Minutes
def notify_order(self, order):
if order.status in [order.Completed, order.Partial]:
self.total_pnl += order.executed.pnl - order.executed.comm
self.positions[order.data._name] += order.executed.size
def next(self):
if self._usedate:
self.rets[self.strategy.datetime.date()] = self.total_pnl
else:
self.rets[self.strategy.datetime.datetime()] = self.total_pnl
def stop(self):
for dname in self.positions:
self.unrealized += (self.strategy.dnames[dname].close[0] -
self.strategy.positionsbyname[dname].price) * \
self.strategy.positionsbyname[dname].size
def get_analysis(self):
return dict(PNL=self.unrealized)
Thanks in advance
F.C
Hello everybody,
I'm trying to set up a code to run in order optimizations (changing two parameters on a single indicator.
I'm trying to set up the code to have the optimization running for different timeframes during the day (I'm using 1min data frame on Nasdaq Futures).
This is my BacktraderOPT .py file.
import backtrader.indicators as btind
import matplotlib
import os
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
import pytz
import numpy as np
from pandas import DataFrame
## SuperTrend Indicator Import ##
from SupertrendBacktrader import *
# Create a Strategy
class TestStrategy(bt.Strategy):
params = (('period', 18), ('multiplier', 2.8))
# The first data in the list self.datas[0] is the default data for trading operations and to keep all strategy elements synchronized (it’s the system clock)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
dt1 = self.data0.datetime.time(0)
print('%s,%s, %s' % (dt.isoformat(), dt1.isoformat(), txt))
def __init__(self):
init_time = time.time()
self.startcash = self.broker.getvalue()
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.Superbands = SuperTrendBand(period=self.params.period, multiplier=self.params.multiplier)
self.Supertrend = SuperTrend(period=self.params.period, multiplier=self.params.multiplier)
# To keep track of pending orders and buy price/commission
self.order = None
self.buyprice = None
self.buycomm = None
# Indicators for the plotting show
#bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
#bt.indicators.WeightedMovingAverage(self.datas[0], period=25,subplot=True)
#bt.indicators.ATR(self.datas[0], plot=False)
self.buyp_sellm = bt.ind.CrossOver(self.dataclose,self.Supertrend)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - 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.log(
'BUY EXECUTED, Size: %.2f, OpenPrice: %.2f, Closeprice: %.2f, Comm %.2f' %
(order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
self.opsize = order.executed.size
else: # Sell
self.log(
'SELL EXECUTED, Size: %.2f, OpenPrice: %.2f, Closeprice: %.2f, Comm %.2f' %
(order.executed.size,
order.executed.price,
order.executed.value,
order.executed.comm))
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
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):
if self.data.datetime.time() < Session_begin:
self.close()# don't operate until x
return #
if self.data.datetime.time() > Session_end:
self.close()# don't operate after y
return #
# Simply log the closing price of the series from the reference
#self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# Check if we are in the market
#if not self.position:
#CB0=self.dataclose[0] > self.Supertrend[0]
#CB1=self.dataclose[-1] < self.Supertrend [-1]
# Not yet ... we MIGHT BUY if ...
if self.buyp_sellm==1:
# BUY, BUY, BUY!!! (with all possible default parameters)
self.close()
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
elif self.buyp_sellm==-1:
# SELL, SELL, SELL!!! (with all possible default parameters)
self.close()
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
def stop(self):
pnl = round(self.broker.getvalue() - self.startcash, 3)
print('SuTR Period: {} SuTR Mult: {}Final PnL: {}'.format(
self.params.period,self.params.multiplier, pnl))
if __name__ == '__main__':
# Variable for our starting cash
startcash = 100000
# Create a cerebro entity
cerebro = bt.Cerebro(optreturn=False)
# Add a strategy
cerebro.optstrategy(TestStrategy, period=range(10, 41, 1), multiplier=np.linspace(1.0,4.0,31))
data = btfeeds.GenericCSVData(
dataname='CME_MINI_NQ1!Date.csv',
fromdate=fromdate,
todate=todate,
sessionstart=datetime.time(14, 00),
sessionend=datetime.time(19, 00),
dtformat=('%Y-%m-%d %H:%M:%S'),
datetime=1,
high=3,
low=4,
open=2,
close=5,
volume=8,
openinterest=-1,
timeframe=bt.TimeFrame.Minutes,
compression=1
)
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(startcash)
# Set the commission
cerebro.broker.setcommission(commission=0.00014, mult=20)
# Run over everything
opt_runs = cerebro.run()
# Generate results list
final_results_list = []
for run in opt_runs:
for strategy in run:
value = round(strategy.broker.get_value(), 2)
PnL = round(value - startcash, 2)
period = strategy.params.period
multiplier= round(strategy.params.multiplier,2)
final_results_list.append([period,multiplier, PnL])
# Sort Results List
by_period = sorted(final_results_list, key=lambda x: x[0])
by_PnL = sorted(final_results_list, key=lambda x: x[2], reverse=False)
# Print results
print('Results: Ordered by Profit:')
for result in by_PnL:
print('SuTR Period: {}, SuTR Mult: {},Final PnL: {}'.format(result[0], result[1], result[2]))
print("Opt Start on ",fromdate, "and ends on",todate)
#print(fromdate,todate, Session_begin,Session_end)
print("Total script time is --- %s minutes ---" % ((time.time() - start_time)/60))
My code recalls SuperTrend custom indicator from another file and gives as an output a pivot data frame as .csv with period as rows and multiplier as columns with PnL as values.
ResultsListdf = DataFrame(final_results_list)
print(ResultsListdf)
ResultsListdf=ResultsListdf.pivot(index=0,columns=1,values=2)
fromdate_Time=fromdate.strftime("%H:%M-")
fromdate_Date=fromdate.strftime("%d:%m-")
fromdate_Year=fromdate.strftime("%Y-")
fromdate_DateTime = fromdate.strftime("%d-%m-%Y<%H:%M")
todate_Time=todate.strftime("%H:%M<")
todate_Date=todate.strftime("%d-%m<")
todate_Year=todate.strftime("%Y")
todate_DateTime = todate.strftime("%d-%m-%Y>%H:%M")
outFileName=("SXTR-"+fromdate_Time+todate_Time+fromdate_Date+todate_Date+fromdate_Year+todate_Year+'.csv')
ResultsListdf.to_csv(r'/PycharmProjects/pythonProject3/'+outFileName,sep=',')
So far my code runs smoothly and each optimization takes roughly 28min for 18 days of 1 min data with a filter to open positions only between 14:00 and 19:00. The strategy open a long/short position when signal is given by closing previous (if any) and opening a new one to follow the trend.
What I'm trying to do is to set different session times and run in loop optimization to get different .csv files. This way I can see for different time of the day which set of parameters performs better. This by just clicking Run and leave the computer doing calculations (while the fans start to sound like a jet engine).
#%%
import matplotlib
import os
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
import pytz
import numpy as np
from pandas import DataFrame
#%%. ERROR
#DataImportVar
fromdate = datetime.datetime(2021, 3, 3, 14, 00)
todate = datetime.datetime(2021, 3, 3, 19, 00)
#SessionTimeFilter
Session_begin = datetime.time(14, 0)
Session_end = datetime.time(19, 0)
exec(open('BacktraderOPT.py').read())
#Divide two parts with cell (Not Working)
#%%
#DataImportVar
fromdate = datetime.datetime(2021, 3, 3, 15, 00)
todate = datetime.datetime(2021, 3, 3, 19, 00)
#SessionTimeFilter
Session_begin = datetime.time(16, 0)
Session_end = datetime.time(19, 0)
exec(open('BacktraderOPT.py').read())
To get faster result I've set to optimize only one day.
Whenever I run the second cell I get the following error.
Tried to run this in Jupyter but it really takes forever.
AttributeError: Can't get attribute 'TestStrategy' on <module 'main' (built-in)>
Any suggestion on how to solve this and save time by running this only one time?
Have posted whole code for suggestions on how to improve it. I'm not really good at coding and I reckon that this code has plenty of room for improvement.
Thanks in advance
F.C
@ab_trader said in Source Error when Reading in Custom CSV Data:
data = btfeeds.GenericCSVData( dataname='CME_MINI_NQ1!Date.csv', fromdate=datetime.datetime(2021, 1, 1), dtformat=('%Y-%m-%d %H:%M:%S'), datetime=1, high=3, low=4, open=2, close=5, volume=8, openinterest=-1, timeframe=bt.TimeFrame.Minutes compression=1 )
Thank you very much!! You're great! Solved
I cannot upload this CSV file
data = btfeeds.GenericCSVData(
dataname='CME_MINI_NQ1!Date.csv',
fromdate=datetime.datetime(2021, 1, 1),
dtformat=('%Y-%m-%d %H:%M:%S'),
time=1,
high=2,
low=3,
open=1,
close=4,
volume=7,
openinterest=-1,
compression=1
)
When I run this code I get :
re.error: redefinition of group name 'H' as group 7; was group 4 at position 159
if I change it to this:
data = btfeeds.GenericCSVData(
dataname='CME_MINI_NQ1!Date.csv',
fromdate=datetime.datetime(2021, 1, 1),
dtformat=('%Y-%m-%d'),
tmformat=(' %H:%M:%S'),
time=1,
high=2,
low=3,
open=1,
close=4,
volume=7,
openinterest=-1,
compression=1
)
I get :
ValueError: time data '0T2021-02-07 23:00:00' does not match format '%Y-%m-%dT %H:%M:%S'
Anybody has any clue why this is happening and what can I do to solve it?
Thanks in advance