Hi, I am using 'Order.Stop' in the following code for a simple strategy. But, the stops orders are not executing as expected after the first two trades. I am including the minimal reproducible code here first and then I'll describe the problem in detail.
import backtrader as bt
import pandas as pd
pd.core.common.is_list_like = pd.api.types.is_list_like
import datetime as dt
import matplotlib
from tabulate import tabulate
print (matplotlib.rcParams['backend'])
### main strategy:
class maCross(bt.Strategy):
'''
oneplot = Force all datas to plot on the same master.
'''
params = (
('sma1', 10),
('sma2', 50),
('newMinMax', 10), # period for new close's highs or lows
('oneplot', True),
('atrPeriod', 20),
('lPerc', 0.01),
('sPerc', 0.015),
('tPerc', 0.02),
('RiskPerc', 0.006)
)
def __init__(self):
self.o = dict() # orders per data (main, stop, limit, manual-close)
self.holding = dict() # holding periods per data
self.tradeHigh = dict() # high during the current trade
self.tradeLow = dict() # low during the current trade
self.tempHigh = dict() # temperory variable
self.tempLow = dict() # temperory variable
self.tradeATR = dict()
self.longTrailStop = dict()
self.shortTrailStop = dict()
self.pos = dict()
self.tradePrice = dict()
self.atBarOpen = {}
'''
Create an dictionary of indicators so that we can dynamically add the
indicators to the strategy using a loop. This mean the strategy will
work with any numner of data feeds.
'''
self.inds = dict()
for i, d in enumerate(self.datas):
self.inds[d] = dict()
self.inds[d]['sma1'] = bt.indicators.SimpleMovingAverage(
d.close, period=self.params.sma1)
self.inds[d]['sma2'] = bt.indicators.SimpleMovingAverage(
d.close, period=self.params.sma2)
self.inds[d]['upTrend'] = self.inds[d]['sma1'] > self.inds[d]['sma2']
self.inds[d]['downTrend'] = self.inds[d]['sma1'] < self.inds[d]['sma2']
self.inds[d]['newLow'] = bt.ind.Lowest(d.close, period = self.params.newMinMax)
self.inds[d]['newHigh'] = bt.ind.Highest(d.close, period = self.params.newMinMax)
self.inds[d]['ATR'] = bt.ind.ATR(d, period = self.params.atrPeriod)
self.atBarOpen[d._name] = None
if i > 0: #Check we are not on the first loop of data feed:
if self.p.oneplot == True:
d.plotinfo.plotmaster = self.datas[0]
def next(self):
for i, d in enumerate(self.datas):
dt, dn = self.datetime.date(), d._name
self.pos[d] = self.getposition(d).size
if self.pos[d] == 0: # no existing positions:
self.tradeHigh[d] = self.tempHigh[d] = 0
self.tradeLow[d] = self.tempLow[d] = 10000000
self.tradeATR[d] = self.inds[d]['ATR'][-1]
qty = 1000000 * self.params.RiskPerc / self.tradeATR[d]
qty = round (qty, 1)
# if self.getdatabyname(dn).close[0] >= self.inds[d]['newHigh'][0]:
if self.inds[d]['upTrend'][0] ==1 and d.close[0] >= self.inds[d]['newHigh'][0]:
print("______________")
print("{}: time to buy on this date: {}" .format(dn, dt) )
self.buy(data=d, size=qty)
elif self.inds[d]['downTrend'][0] ==1 and d.close[0] <= self.inds[d]['newLow'][0]:
# print(self.inds[d]['downTrend'][0])
print("______________")
print("{}: time to sell on this date: {}" .format(dn, dt) )
self.sell(data=d, size=qty)
else:
# print("No trades today: {}" .format(dt))
notrade = 1
else: # already have a position:
self.tradeLen = len(self) - self.atBarOpen[d._name]
if self.tradeLen ==0 :
self.tradePrice[d] = d.open[0]
self.longTrailStop[d] = self.tradePrice[d] - self.tradeATR[d]
self.shortTrailStop[d] = self.tradePrice[d] + self.tradeATR[d]
print('\r\n $$$ New trade initiated for: {} on {} -- current positions: {},opened at: {}'
.format (dn, dt, round(self.pos[d],2), round(self.tradePrice[d],3 )))
else:
self.tradeHigh[d] = max(d.high[0], self.tradeHigh[d], self.tradePrice[d])
self.tradeLow[d] = min(self.tradeLow[d], d.low[0], self.tradePrice[d])
if d.close[0] >= self.longTrailStop[d] + self.tradeATR[d]:
self.longTrailStop[d] = self.tradeHigh[d] - self.tradeATR[d]
if d.close[0] <= self.shortTrailStop[d] - self.tradeATR[d]:
self.shortTrailStop[d] = self.tradeLow[d] + self.tradeATR[d]
print('\r\n ### {} {} current positions: {}, trade length: {}, ATR: {}, trade high: {},
trade low: {}, close: {}, longStop: {}, shortStop: {}'
.format (dt, dn, round(self.pos[d],2), self.tradeLen, round(self.tradeATR[d],3),
round(self.tradeHigh[d],2), round(self.tradeLow[d],2), round(d.close[0],3),
round(self.longTrailStop[d], 3), round(self.shortTrailStop[d],3) ))
if self.order is None:
if (self.pos[d] > 0 and self.tradeLen >0 ):
self.sell(data=d, size=self.pos[d], exectype = bt.Order.Stop,
price = self.longTrailStop[d], valid = 0)
if (self.pos[d] < 0 and self.tradeLen >0 ):
self.buy(data=d, size=-self.pos[d], exectype = bt.Order.Stop,
price = self.shortTrailStop[d], valid =0)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def notify_order(self, order):
dt = self.data.datetime.date()
if order.status in [order.Submitted, order.Accepted]:
print('\r\n {}, Status {}: Ref: {}, Size: {}, Price: {}'.format(
dt,
order.Status[order.status],
order.ref,
round(order.size, 2),
'NA' if not order.price else round(order.price,5)
))
# 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, %.2f' % order.executed.price )
self.log(' Just completed BUY order with ref id: %d' % order.ref)
elif order.issell():
self.log(' SELL EXECUTED, %.2f' % order.executed.price)
self.log(' Just completed SELL order with ref id: %d' % order.ref)
self.bar_executed = len(self)
# print (self.bar_executed)
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):
dt = self.data.datetime.date()
self.atBarOpen[trade.data._name] = trade.baropen
if trade.isopen:
tradeLength = trade.barlen
tradeDuration = len(trade.data)
print ('\r\n +++Openning Trade Notification: {} opened on bar {} at price: {} trade duration: {}
at bar no.: {}' .format(trade.data._name, self.atBarOpen[trade.data._name],
round( trade.price,3), tradeLength, tradeDuration) )
if trade.isclosed:
print('\r\n ---Closing Trade Notification: {} {} Opened at {}, Current Valuet {}: PnL Gross {}, Net {}'
.format( dt,
trade.data._name,
round(trade.price,3),
trade.value,
round(trade.pnl,2),
round(trade.pnlcomm,2)))
#Create an instance of cerebro
cerebro = bt.Cerebro()
#Add our strategy
cerebro.addstrategy(maCross, oneplot=False)
start = dt.datetime(2017, 10, 1)
end = dt.datetime(2018,2, 28)
# chagne the path for your own data:
datapath = 'F:\\Michael\\Python\\backtrader\\'
skiprows = 0
header = 0
# data symbol used for this example:
s = "IH_CFE"
datafile = datapath + s + '.csv'
dataframe = pd.read_csv(datafile,
skiprows=skiprows,
header=header,
encoding = 'gbk',
parse_dates=True,
index_col=0)
dataframe.columns = ( ' WINDCODE', 'open', 'high', 'low', 'close', 'volume')
dataframe = dataframe.loc[start: end]
dataframe = dataframe.fillna(method = 'backfill')
data = bt.feeds.PandasData(dataname= dataframe)
cerebro.adddata(data, name = s) # Give the data to cerebro
#Variable for our starting cash
startcash = 1000000
# Set our desired cash start
cerebro.broker.setcash(startcash)
cerebro.addobserver(bt.observers.BuySell)
cerebro.addobserver(bt.observers.Trades)
#add executed file:
cerebro.addwriter(bt.WriterFile, csv=True, out = "trade_history.csv" )
# cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
# cerebro.addanalyzer(trade_list, _name='trade_list')
# Run over everything
result = cerebro.run(stdstats=False, tradehistory=True)
Ticker I am using in this example is "IH_CFE". The output of the first trade re as follows. I included many print statements in an attempt to resovle the issue. The first two trades executed as as expected. The stop order is sent out on the second day of each trade with 'valid=0', so the order is only good for that day. Then the stop price is updated based on the day's data then send out again the next day. This works well as show in this trade output initiated on 2018-01-02:
______________
IH_CFE: time to buy on this date: 2018-01-02
2018-01-03, Status Submitted: Ref: 3, Size: 143.7, Price: NA
2018-01-03, Status Accepted: Ref: 3, Size: 143.7, Price: NA
2018-01-03, BUY EXECUTED, 2904.79
2018-01-03, Just completed BUY order with ref id: 3
+++Openning Trade Notification: IH_CFE opened on bar 62 at price: 2904.786 trade duration: 0 at bar no.: 62
$$$ New trade initiated for: IH_CFE on 2018-01-03 -- current positions: 143.7,opened at: 2904.786
### 2018-01-03 IH_CFE current positions: 143.7, trade length: 0, ATR: 41.765, trade high: 0, trade low: 10000000, close: 2906.986, longStop: 2863.022, shortStop: 2946.551
### 2018-01-04 IH_CFE current positions: 143.7, trade length: 1, ATR: 41.765, trade high: 2924.99, trade low: 2901.99, close: 2913.186, longStop: 2883.222, shortStop: 2946.551
2018-01-05, Status Submitted: Ref: 4, Size: -143.7, Price: 2883.22178
2018-01-05, Status Accepted: Ref: 4, Size: -143.7, Price: 2883.22178
### 2018-01-05 IH_CFE current positions: 143.7, trade length: 2, ATR: 41.765, trade high: 2930.99, trade low: 2901.99, close: 2926.786, longStop: 2889.222, shortStop: 2946.551
2018-01-08, Status Submitted: Ref: 5, Size: -143.7, Price: 2889.22178
2018-01-08, Status Accepted: Ref: 5, Size: -143.7, Price: 2889.22178
### 2018-01-08 IH_CFE current positions: 143.7, trade length: 3, ATR: 41.765, trade high: 2943.39, trade low: 2901.99, close: 2934.186, longStop: 2901.622, shortStop: 2946.551
2018-01-09, Status Submitted: Ref: 6, Size: -143.7, Price: 2901.62178
2018-01-09, Status Accepted: Ref: 6, Size: -143.7, Price: 2901.62178
### 2018-01-09 IH_CFE current positions: 143.7, trade length: 4, ATR: 41.765, trade high: 2963.79, trade low: 2901.99, close: 2963.786, longStop: 2922.022, shortStop: 2946.551
2018-01-10, Status Submitted: Ref: 7, Size: -143.7, Price: 2922.02178
2018-01-10, Status Accepted: Ref: 7, Size: -143.7, Price: 2922.02178
### 2018-01-10 IH_CFE current positions: 143.7, trade length: 5, ATR: 41.765, trade high: 2987.39, trade low: 2901.99, close: 2984.386, longStop: 2945.622, shortStop: 2946.551
2018-01-11, Status Submitted: Ref: 8, Size: -143.7, Price: 2945.62178
2018-01-11, Status Accepted: Ref: 8, Size: -143.7, Price: 2945.62178
### 2018-01-11 IH_CFE current positions: 143.7, trade length: 6, ATR: 41.765, trade high: 2995.19, trade low: 2901.99, close: 2982.986, longStop: 2945.622, shortStop: 2946.551
2018-01-12, Status Submitted: Ref: 9, Size: -143.7, Price: 2945.62178
2018-01-12, Status Accepted: Ref: 9, Size: -143.7, Price: 2945.62178
### 2018-01-12 IH_CFE current positions: 143.7, trade length: 7, ATR: 41.765, trade high: 3010.59, trade low: 2901.99, close: 3010.586, longStop: 2968.822, shortStop: 2946.551
2018-01-15, Status Submitted: Ref: 10, Size: -143.7, Price: 2968.82178
2018-01-15, Status Accepted: Ref: 10, Size: -143.7, Price: 2968.82178
### 2018-01-15 IH_CFE current positions: 143.7, trade length: 8, ATR: 41.765, trade high: 3055.59, trade low: 2901.99, close: 3033.186, longStop: 3013.822, shortStop: 2946.551
2018-01-16, Status Submitted: Ref: 11, Size: -143.7, Price: 3013.82178
2018-01-16, Status Accepted: Ref: 11, Size: -143.7, Price: 3013.82178
### 2018-01-16 IH_CFE current positions: 143.7, trade length: 9, ATR: 41.765, trade high: 3058.19, trade low: 2901.99, close: 3057.786, longStop: 3016.422, shortStop: 2946.551
2018-01-17, Status Submitted: Ref: 12, Size: -143.7, Price: 3016.42178
2018-01-17, Status Accepted: Ref: 12, Size: -143.7, Price: 3016.42178
### 2018-01-17 IH_CFE current positions: 143.7, trade length: 10, ATR: 41.765, trade high: 3117.39, trade low: 2901.99, close: 3062.986, longStop: 3075.622, shortStop: 2946.551
2018-01-18, Status Submitted: Ref: 13, Size: -143.7, Price: 3075.62178
2018-01-18, Status Accepted: Ref: 13, Size: -143.7, Price: 3075.62178
2018-01-18, SELL EXECUTED, 3075.62
2018-01-18, Just completed SELL order with ref id: 13
---Closing Trade Notification: 2018-01-18 IH_CFE Opened at 2904.786, Current Valuet 0.0: PnL Gross 24549.05, Net 24549.05
As we can see, each day the stop order is send out with a new Ref no. (a new order). And on 2018-01-18, the stop order is executed based on the stop price and the trade is closed. However , on the next trade, things started to behave strangely.
______________
IH_CFE: time to buy on this date: 2018-01-18
2018-01-19, Status Submitted: Ref: 14, Size: 152.9, Price: NA
2018-01-19, Status Accepted: Ref: 14, Size: 152.9, Price: NA
2018-01-19, BUY EXECUTED, 3124.98
2018-01-19, Just completed BUY order with ref id: 14
+++Openning Trade Notification: IH_CFE opened on bar 74 at price: 3124.982 trade duration: 0 at bar no.: 74
$$$ New trade initiated for: IH_CFE on 2018-01-19 -- current positions: 152.9,opened at: 3124.982
### 2018-01-19 IH_CFE current positions: 152.9, trade length: 0, ATR: 39.232, trade high: 0, trade low: 10000000, close: 3109.982, longStop: 3085.75, shortStop: 3164.214
### 2018-01-22 IH_CFE current positions: 152.9, trade length: 1, ATR: 39.232, trade high: 3129.58, trade low: 3099.18, close: 3121.782, longStop: 3085.75, shortStop: 3138.414
2018-01-23, Status Submitted: Ref: 15, Size: -152.9, Price: 3085.74991
2018-01-23, Status Accepted: Ref: 15, Size: -152.9, Price: 3085.74991
### 2018-01-23 IH_CFE current positions: 152.9, trade length: 2, ATR: 39.232, trade high: 3172.18, trade low: 3099.18, close: 3171.182, longStop: 3132.95, shortStop: 3138.414
2018-01-24, Status Submitted: Ref: 16, Size: -152.9, Price: 3132.94991
2018-01-24, Status Accepted: Ref: 16, Size: -152.9, Price: 3132.94991
### 2018-01-24 IH_CFE current positions: 152.9, trade length: 3, ATR: 39.232, trade high: 3189.18, trade low: 3099.18, close: 3164.182, longStop: 3132.95, shortStop: 3138.414
2018-01-25, Status Submitted: Ref: 17, Size: -152.9, Price: 3132.94991
2018-01-25, Status Accepted: Ref: 17, Size: -152.9, Price: 3132.94991
2018-01-25, SELL EXECUTED, 3132.95
2018-01-25, Just completed SELL order with ref id: 16
2018-01-25, SELL EXECUTED, 3132.95
2018-01-25, Just completed SELL order with ref id: 17
---Closing Trade Notification: 2018-01-25 IH_CFE Opened at 3124.982, Current Valuet 0.0: PnL Gross 1218.32, Net 1218.32
+++Openning Trade Notification: IH_CFE opened on bar 78 at price: 3132.95 trade duration: 0 at bar no.: 78
$$$ New trade initiated for: IH_CFE on 2018-01-25 -- current positions: -152.9,opened at: 3164.382
### 2018-01-25 IH_CFE current positions: -152.9, trade length: 0, ATR: 39.232, trade high: 3189.18, trade low: 3099.18, close: 3140.982, longStop: 3125.15, shortStop: 3203.614
### 2018-01-26 IH_CFE current positions: -152.9, trade length: 1, ATR: 39.232, trade high: 3189.18, trade low: 3099.18, close: 3165.182, longStop: 3149.95, shortStop: 3203.614
2018-01-29, Status Submitted: Ref: 18, Size: 152.9, Price: 3203.61372
2018-01-29, Status Accepted: Ref: 18, Size: 152.9, Price: 3203.61372
### 2018-01-29 IH_CFE current positions: -152.9, trade length: 2, ATR: 39.232, trade high: 3189.18, trade low: 3093.98, close: 3103.182, longStop: 3149.95, shortStop: 3133.214
2018-01-30, Status Submitted: Ref: 19, Size: 152.9, Price: 3133.21372
2018-01-30, Status Accepted: Ref: 19, Size: 152.9, Price: 3133.21372
2018-01-30, SELL EXECUTED, 3085.75
2018-01-30, Just completed SELL order with ref id: 15
Everything was hehaving correctly, until 2018-01-25, both stop order Ref 16 and Ref 17 got executed. But order Ref 16 was from 2018-01-24 and should have been cancelled by the end of the day like many other orders before or did I misunderstand something? This happened again on 2018-01-30 when order ref 15 executed which was sent out on 2018-01-23. What am I missing? Thanks for any help from another pair of eyes. Sorry for the long post, but it was necessary to show the problem.
I can include the data file if anyone is interested.. thanks.
Here is the sample of the input data for the results shown above:
2018/1/2 IH.CFE 2857.186363 2903.986363 2857.186363 2902.386363 9954
2018/1/3 IH.CFE 2904.786363 2935.186363 2898.386363 2906.986363 11252
2018/1/4 IH.CFE 2911.986363 2924.986363 2901.986363 2913.186363 9763
2018/1/5 IH.CFE 2913.186363 2930.986363 2908.186363 2926.786363 8995
2018/1/8 IH.CFE 2929.986363 2943.386363 2918.986363 2934.186363 9043
2018/1/9 IH.CFE 2934.186363 2963.786363 2933.186363 2963.786363 8449
2018/1/10 IH.CFE 2962.986363 2987.386363 2952.186363 2984.386363 10191
2018/1/11 IH.CFE 2979.586363 2995.186363 2971.186363 2982.986363 9443
2018/1/12 IH.CFE 2986.586363 3010.586363 2982.786363 3010.586363 9367
2018/1/15 IH.CFE 3019.186363 3055.586363 3009.186363 3033.186363 11325
2018/1/16 IH.CFE 3029.586363 3058.186363 3024.186363 3057.786363 10790
2018/1/17 IH.CFE 3060.986363 3117.386363 3048.986363 3062.986363 12257
2018/1/18 IH.CFE 3083.986363 3111.586363 3073.586363 3098.586363 8170
2018/1/19 IH.CFE 3124.981818 3131.581818 3093.181818 3109.981818 12271
2018/1/22 IH.CFE 3101.181818 3129.581818 3099.181818 3121.781818 11730
2018/1/23 IH.CFE 3132.981818 3172.181818 3132.981818 3171.181818 12746
2018/1/24 IH.CFE 3176.581818 3189.181818 3139.181818 3164.181818 14310
2018/1/25 IH.CFE 3164.381818 3164.581818 3116.181818 3140.981818 13495
2018/1/26 IH.CFE 3139.181818 3188.981818 3136.981818 3165.181818 12791
2018/1/29 IH.CFE 3175.781818 3187.781818 3093.981818 3103.181818 14934
2018/1/30 IH.CFE 3105.181818 3114.981818 3062.781818 3063.381818 12005
2018/1/31 IH.CFE 3055.781818 3103.981818 3051.581818 3101.181818 13673
thanks again!!!