@backtrader said in Multi Assets Multi Order Executions Problem:
@jabbarabdullah said in Multi Assets Multi Order Executions Problem:
Appreciate if you could explain further on what do you mean by 'NO'.
You say the orders are getting executed twice and the answer is: NO
@ab_trader said in Multi Assets Multi Order Executions Problem:
You have two data feeds, you cycle thru them twice and print same info twice. Check if data name is same as order data name, then print.
On the other side you don't need to go thru all data feeds. Just print order info.
I quoted the code in notify_order where you do exactly what @ab_trader is telling you. You print the execution twice (which is not the same as two executions), because you loop through the datas in notify_order
I see, got it. Removed the loop in notify_order and it worked. Thanks @backtrader and @ab_trader !
Just one more thing. when I tried executing the code with different date which the take profit and stop loss in my code should work, the numbers went crazy and it did not really stop the trade as expected. It worked great for a single asset data feed code but when I change it to multi assets data feeds, it went haywire. What went wrong? The single data feed code for comparison as follows:
from __future__ import (absolute_import, division, print_function, unicode_literals)
import backtrader as bt
import datetime as dt
import pytz
import math
cn1='UUUU'
cn2='VVVV'
csh=100000
stdt=dt.datetime(2019,06,28,9,30)
enddt=dt.datetime(2019,06,28,16,00)
SL=0.01
TP=2*SL
SU=0.005
SUpct=SU*100
prop=1 #proportion of portfolio
batch=10 #rounding
entrytime = dt.time(9,45)
exittime = dt.time(15,55)
stakesize=10
lwbnd=40
upbnd=85
commis=0.05
TPpct=TP*100
SLpct=SL*100
def rounddown(x):
return int(math.floor(x / 1)) * 1
class IBCommission(bt.CommInfoBase):
"""A :class:`IBCommision` charges the way interactive brokers does.
"""
params = (('stocklike', True), ('commtype', bt.CommInfoBase.COMM_FIXED),)
def _getcommission(self, size, price, pseudoexec):
return self.p.commission
class PropSizer(bt.Sizer):
"""A position sizer that will buy as many stocks as necessary for a certain proportion of the portfolio
to be committed to the position, while allowing stocks to be bought in batches (say, 100)"""
params = {"prop": prop, "batch": batch}
def _getsizing(self, comminfo, cash, data, isbuy):
"""Returns the proper sizing"""
if isbuy: # Buying
target = csh * self.params.prop # Ideal total value of the position
price = data.close[0]
shares_ideal = target / price # How many shares are needed to get target
batches = int(shares_ideal / self.params.batch) # How many batches is this trade?
shares = batches * self.params.batch # The actual number of shares bought
if shares * price > cash:
return 0 # Not enough money for this trade
else:
return shares
else: # Selling
return self.broker.getposition(data).size # Clear the position
class TestStrategy(bt.Strategy):
data_live = False
def notify_data(self, data, status, *args, **kwargs):
print('*' * 5, 'DATA NOTIF:', data._getstatusname(status),
*args)
if status == data.LIVE:
self.data_live = True
def log(self, txt, dt=None, vlm=None):
dt = dt or self.datas[0].datetime.datetime(0)
vlm = vlm or self.data.volume[0]
print('%s, %s, %s, Volume, %s' % (len(self), dt.isoformat(), txt, vlm))
def __init__(self):
self.dataclose = self.datas[0].close
self.order = None
self.buyprice = None
self.buycomm = None
self.sma0 = bt.indicators.SimpleMovingAverage(self.datas[0], period=20)
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=54)
self.rsi = bt.indicators.RelativeStrengthIndex(period=14,safediv=True, upperband=upbnd,lowerband=lwbnd)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
self.last_executed_price = order.executed.price
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
self.last_executed_price = order.executed.price
else: # Sell
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.last_executed_price = order.executed.price
self.bar_executed = len(self)
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):
pv = self.broker.get_value()
self.stakes = abs(rounddown(pv/self.dataclose[0]))
target = csh * prop # Ideal total value of the position
price = self.data.close[0]
shares_ideal = target / price # How many shares are needed to get target
batches = int(shares_ideal / batch) # How many batches is this trade?
shares = batches * batch
if self.order:
return
if not self.position:
if (pv >= csh*(1+TP)):
return
if (pv <= csh*(1-SL)):
return
if (self.data.datetime.time(0) >= exittime):
return
if ((self.rsi[0] <= lwbnd)and(pv > csh*(1-SL))and(self.data.datetime.time(0) >= entrytime)):
print('rsi:', self.rsi[0])
print('pv: ', pv)
self.log('BUY CREATE, %.2f, VLM BOUGHT: %.2f' % (self.dataclose[0], shares))
self.order = self.buy()
else:
if (self.dataclose[0] >= (self.last_executed_price*(1+SU))):
print('rsi:', self.rsi[0])
print('pv: ', pv)
self.log('SELL CREATE (>%.2fpct), %.2f, VLM BOUGHT: %.2f' % (SUpct, self.dataclose[0], self.position.size))
self.order = self.sell(exectype=bt.Order.Stop)
else:
if (self.rsi[0] >= upbnd):
print('rsi:', self.rsi[0])
print('pv: ', pv)
self.log('SELL CREATE (RSI>Upbnd), %.2f, VLM BOUGHT: %.2f' % (self.dataclose[0], self.position.size))
self.order = self.close()
else:
if (pv >= csh*(1+TP)):
print('rsi:', self.rsi[0])
print('pv: ', pv)
self.log('TAKE PROFIT VAL CREATE (>%.2fpct), %.2f, VLM BOUGHT: %.2f' % (TPpct, self.dataclose[0], self.position.size))
self.order = self.sell(exectype=bt.Order.StopLimit, price=self.dataclose[0])
else:
if (pv <= csh*(1-SL)):
print('rsi:', self.rsi[0])
print('pv: ', pv)
self.log('STOPLOSS VAL CREATE (<%.2fpct), %.2f, VLM BOUGHT: %.2f' % (SLpct, self.dataclose[0], self.position.size))
self.order = self.sell(exectype=bt.Order.StopLimit, price=self.dataclose[0])
return
else:
if (self.data.datetime.time(0) >= exittime):
print('rsi:', self.rsi[0])
print('pv: ', pv)
self.log('EOD STOP, %.2f, VLM BOUGHT: %.2f' % (self.dataclose[0], self.position.size))
self.order = self.close(exectype=bt.Order.Stop)
def runstrat():
# Get a pandas dataframe
cerebro = bt.Cerebro()
ibstore = bt.stores.IBStore(port=7497, host='127.0.0.1', clientId=12345)
data0 = ibstore.getdata(dataname=cn1,fromdate=stdt, historical =True, useRTH=True, tz = pytz.timezone('US/Eastern'),
todate=enddt, timeframe=bt.TimeFrame.Seconds, compression=15)
cerebro.adddata(data0, name=cn1)
data1 = ibstore.getdata(dataname=cn2,fromdate=stdt, historical =True, useRTH=True, tz = pytz.timezone('US/Eastern'),
todate=enddt, timeframe=bt.TimeFrame.Seconds, compression=15)
cerebro.adddata(data1, name=cn2)
data1.plotinfo.plotmaster = data0
cerebro.broker.setcash(csh)
comminfo = IBCommission(commission=commis)
cerebro.broker.addcommissioninfo(comminfo)
cerebro.addwriter(bt.WriterFile, csv=True, rounding=2, out="C:\\Users\\User\\Desktop\\Backtest Library\\TestResults.csv")
start_value = cerebro.broker.getvalue()
cerebro.addstrategy(TestStrategy)
cerebro.addsizer(PropSizer)
# Run over everything
cerebro.run()
# Plot the result
cerebro.plot(volume=False)
# Print out the starting conditions
print(' ')
print('--',cn1,'--')
print('Start capital: %.2f' % start_value)
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
print('Final PnL Value: %.2f' % (cerebro.broker.getvalue()-start_value))
if __name__ == '__main__':
runstrat()
Appreciate your comment on this. Thanks!