Thanks alot. I will do some reading into these articles. Also will be diving into connection with IB.
Pls stay safe in the meantime. And really appreciate your help.
Thanks alot. I will do some reading into these articles. Also will be diving into connection with IB.
Pls stay safe in the meantime. And really appreciate your help.
sorry, misclicked. I really want the old trade observer back. Where the profits are in blue dots, losses in red dots. Now I only get blue dots even though they are negative pnl.
class BaseStrategy(bt.Strategy): # Strategy is a lines object
params = dict(order_percentage=0.25, ticker='eurusd', fast=10, slow=30)
parent_order = None # default value for a potential order
stoploss = None
def __init__(self):
self.order = None # To keep track of pending orders, init with None
self.buyprice = None
self.buycomm = None
self.bar_executed = None
self.size = None
self.lendata1 = 0
self.data = self.datas[0]
self.data1 = self.datas[1]
self.fast_sma = bt.indicators.SMA(self.data.close, period=self.params.fast)
self.slow_sma = bt.indicators.SMA(self.data.close, period=self.params.slow)
self.crossover = bt.indicators.CrossOver(self.fast_sma, self.slow_sma)
self.fast1_sma = bt.indicators.SMA(self.data1.close, period=self.params.fast)
self.slow1_sma = bt.indicators.SMA(self.data1.close, period=self.params.slow)
self.crossover1 = bt.indicators.CrossOver(self.fast1_sma, self.slow1_sma)
self.data1.plotinfo.plot = False
self.fast1_sma.plotinfo.plot = False
self.slow1_sma.plotinfo.plot = False
self.crossover1.plotinfo.plot = False
def notify_order(self, order): # called when a buy or sell is requested
if order.status in [order.Submitted]: # status 1
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
print('Order Submitted...', order.ref)
return
if order.status in [order.Accepted]: # status 2
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
print('Order Accepted...', order.ref)
return
if order.status in [order.Margin, order.Rejected]:
print('Order Margin/Rejected', order.ref)
return
# Check if an order has been completed, Attention: broker could reject order if not enough cash
if order.status in [order.Completed]: # status 4
print('-'*50, 'EXECUTED ORDER', '-'*50)
print('{} EXE-D, REF {}, AT {}: {:d} shares of {}, Price: {}, Value: {:.2f}, Comm: {:.2f}, PNL: {}'.format(
order.info['name'], order.ref, bt.num2date(order.executed.dt), order.executed.size, self.p.ticker,
order.executed.price, order.executed.value, order.executed.comm, order.executed.pnl))
print('-' * 116)
def nextstart(self):
self.lendata1 = len(self.data1) # if this a 240min timeframe, len of self.data1 is 2.
# super(BaseStrategy, self).nextstart() -->do i even need this?
# print(len(self.data1))
def next(self):
# print('len self.datas:', len(self.datas))
# print('len self.data1, lendata1', len(self.data1), self.lendata1)
txt = list()
txt.append('Data0')
txt.append('{}'.format(len(self.data)))
txt.append('{}'.format(self.data.datetime.datetime(0)))
txt.append('{:.5f}'.format(self.data.open[0]))
txt.append('{:.5f}'.format(self.data.high[0]))
txt.append('{:.5f}'.format(self.data.low[0]))
txt.append('{:.5f}'.format(self.data.close[0])
# print(', '.join(txt))
if len(self.data1) > self.lendata1:
self.lendata1 += 1
print('new higher TF bar')
if len(self.datas) > 1 and len(self.data1): # len datas is always at 2 becasue i got min and 240 min data, len data1 is increasing from 2 to ...
txt = list()
txt.append('Data1')
txt.append('{}'.format(len(self.data1)))
txt.append('{}'.format(self.data1.datetime.datetime(0)))
txt.append('{:.5f}'.format(self.data1.open[0]))
txt.append('{:.5f}'.format(self.data1.high[0]))
txt.append('{:.5f}'.format(self.data1.low[0]))
txt.append('{:.5f}'.format(self.data1.close[0])
# print(', '.join(txt))
can_trade = check_timefx(self.data.datetime.date(0), self.data.datetime.time(0))
if self.crossover > 0 and self.crossover1 > 0:
action = 'long'
elif self.crossover < 0 and self.crossover1 < 0:
action = 'short'
else:
action = 'not sure yet'
# Check if we are in the market
if can_trade:
if action == 'long' or action == 'short':
if self.position:
self.cancel(self.stoploss)
print('{} {} canceled.'.format(self.stoploss.info['name'], self.stoploss.ref))
self.close(name='Trend Change - take profit...')
self.stoploss = None
if self.parent_order: # something was pending, cancelling parent cancels stoploss too
self.cancel(self.parent_order)
print('{} {} canceled.'.format(self.parent_order.info['name'], self.parent_order.ref))
self.parent_order = None
self.stoploss = None
# Now I am no more in a position
amount_to_invest = (self.p.order_percentage * self.broker.cash)
self.size = math.floor(amount_to_invest / self.data.close)
if action == 'long':
self.parent_order = self.buy(size=self.size, exectype=bt.Order.Market, transmit=False, valid=None)
self.stoploss = self.sell(price=self.data.close, exectype=bt.Order.Stop, size=self.parent_order.size,
parent=self.parent_order, transmit=True, valid=None)
self.parent_order.addinfo(name='Parent Long Order')
self.stoploss.addinfo(name='Child Long Stop Loss Order')
elif action == 'short':
self.parent_order = self.sell(size=self.size, exectype=bt.Order.Market, transmit=False, valid=None)
self.stoploss = self.buy(price=self.data.close, exectype=bt.Order.Stop, size=self.parent_order.size,
parent=self.parent_order, transmit=True, valid=None)
self.parent_order.addinfo(name='Parent Short Order')
self.stoploss.addinfo(name='Child Short Stop Loss Order')
print('=' * 50, 'CREATE ORDER', '=' * 50)
print('{} CREATE, REF {}, CURRENT PX: {:.5f}, {} AT {:.5f}, STOPLOSS AT {:.5f}'.format(
self.parent_order.info['name'], self.parent_order.ref, self.data.close[0],
'LONG' if 'Long' in self.parent_order.info['name'] else 'SHORT',
self.data.close[0], self.data.close[-1]))
print('=' * 114)
elif action == 'not sure yet':
print('not sure what action to take')
else:
print('no action2 - meaning regular trading')
else: # past time 3pm, not trading anymore, look to close position
# print('Last hour or Closed....')
if self.position:
if times_upfx(self.data.datetime.time(0)): # 15:46 times up look to just close.
self.cancel(self.stoploss)
print('{} {} canceled.'.format(self.stoploss.info['name'], self.stoploss.ref))
self.close(name='Times Up, Close final position')
self.stoploss = None
self.parent_order = None
else: # 15:01 to 15:46 look for next turning point to sell so maximise profit
if action == 'long' or action == 'short':
self.cancel(self.stoploss)
print('{} {} canceled.'.format(self.stoploss.info['name'], self.stoploss.ref))
self.close(name='Trend Change, Take profit')
self.stoploss = None
self.parent_order = None
def main():
cerebro = bt.Cerebro(stdstats=True)
# Add a strategy
cerebro.addstrategy(BaseStrategy)
df = pd.read_csv('datas\\EURUSD2019Jan.csv', sep=',', header=0, index_col=0, parse_dates=True)
# Create a Data Feed and add to cerebro
data = bt.feeds.PandasData(dataname=df, timeframe=bt.TimeFrame.Minutes, compression=1)
cerebro.adddata(data, name='data')
# cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=1)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=240, name='data1')
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.0001)
strat = cerebro.run(maxcpus=3)
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot(style='candlestick', barup='green', bardown='red', fmt_x_ticks='%d-%b %H:%M', fmt_x_data='%d-%b %H:%M', volume=False) # Plot the result
if __name__ == '__main__':
main()
This is what I get. I only need the smaller timeframe plotted with its indicators. Hope a kind soul can help me out here. Thanks.
Hi
I like to ask if anyone has experience with creating a single trail order in oanda. I tried the below but got the following error:
self.long_trailstop = self.sell(price=trail_stop_px, exectype=bt.Order.StopTrail, size=stop_size, trailamount=self.ATR_at_exe * 1.5, id='888')
but the above does not work.
I got an error which bascially is an error when issuing stoptrail orders without brackets.
okwargs['type'] = self._ORDEREXECS[order.exectype]
KeyError: 5
Will some kind souls help me out with this?
Thanks alot!
changed to this:
self.exe_size = btoandav20.sizers.OandaV20Risk(risk_percents=2, stoploss=sl_pips).getsizing(data=self.data0.close[0], isbuy=True)
which returns error:
comminfo = self.broker.getcommissioninfo(data)
AttributeError: 'NoneType' object has no attribute 'getcommissioninfo'
and added this in runstrat:
comminfo = forexSpreadCommisionScheme(spread=3, acc_counter_currency=False)
cerebro.broker.addcommissioninfo(comminfo)
Error is still the same.
comminfo = self.broker.getcommissioninfo(data)
AttributeError: 'NoneType' object has no attribute 'getcommissioninfo'
Hi !
Does anyone have experience using the btoandav20.sizers.OandaV20Risk ?
I think it needs to run in strategy class, next method and not in the run strat right?
I say this because this sizer requires stoploss pips which is dynamic.
I tried this which probably is rubbish.
# in Strategy class I have params dict(sizer = None)
# and in initi the below. I am just following bt docs.
def __init__(self):
if self.p.sizer is not None:
self.sizer = self.p.sizer
def next(self):
if self.one_test_trade:
globe.action = 'long'
self.one_test_trade = False
price = self.data0.close[0] - 0.0001
sl_px = price - self.atr[0] * 1.5
sl_pips = abs(sl_px - price) / 0.0001
limit_px = price + self.atr[0]
self.ATR_at_exe = self.atr[0]
self.exe_size = self.sizer.getsizing(btoandav20.sizers.OandaV20Risk, risk_percents=2, stoploss=sl_pips)
the above returns error although risk_percents is a valid param for this sizer:
TypeError: getsizing() got an unexpected keyword argument 'risk_percents'
Would some kind souls pls help me with some sample code how this is to be used?
source code of the sizer is here: https://github.com/ftomassetti/backtrader-oandav20/blob/master/btoandav20/sizers/oandav20sizer.py
Thanks a miliion!
Hi ! Hoping everyone is keeping safe in these times.
Need some help with forex oanda commision. I cannot fully understand the docs.
The commission is in the spread, but under a million shares traded, the commision is $50. Lets say $5 per trade for easy calculation.
Margin for my country is 5% and so I believe leverage is 20x.
Can I ask how do you set cerebro broker is this case?
#This code cannot work... meaning no orders executed, beleive commtype=bt.CommInfo.COMM_FIXED is the issue?
cerebro.broker.setcommission(leverage=20, mult=1.0, stocklike=False, automargin=0.05, commtype=bt.CommInfoBase.COMM_FIXED, commission=5.0)
a few more questions
amount_to_invest = (0.5 * self.broker.cash)
self.size_to_exe = math.floor(amount_to_invest / self.data0.close)
Thanks a million!
Hi Guys,
I like some advice from more experienced guys. Trying to see how to avoid this.
Testing a breakout strategy.
at min 10, I place a stop order to buy at 2.00, place a stoploss at A
at min 15 I place a stop order to sell at 1.50, place a stoploss at B
at min 17, both stop orders got triggered on the same bar (when the price movement is this big) and they close out the position.
Earlier, I experienced both orders triggered, but on different bar. I am able to
add code in if order.status in [order completed], if I have gone into 1 position, I just cancel the other pending. But happening on the same bar left me scratching my head.
Any ideas or suggestions to workaround this?
this is the docs: What I want to do is to tag the new stop loss to the open position. If there are other ways of implementation, kindly share with me! Thanks!
IBBroker - Trading Live
Note
Following a request a tradeid functionality was implemented in the broker simulation available in backtrader. This allows to keep track of trades being executed in paralled on the same asset correctly allocating commissions to the appropriate tradeid
Such notion is not supported in this live broker because commissions are reported by the broker at times at which it would be impossible to separate them for the different tradeid values.
tradeid can still be specified but it makes no longer sense.
Hi
I have read the tradeid mentioned in live trading but I do not fully understand.
The aim is to trade live using IB. And I am trying to implement a dynamic change of stop loss.
I sent the order in using a parent order with stop loss and limit. Then I want to change the stoploss and limit later as the trade goes on.
I have not been able to successfully do this as cancelling the old stoploss and then adding a new one, does not tag it to the open trade. The stop loss is like a whole new order on its own.
I have searched through the forum and find maybe I can do a tradeid = tradeid in:
self.cancel(self.long_stoploss)
self.long_stoploss = self.sell(tradeid=tradeid, exectype=bt.Order.StopTrail, size=self.position.size, trailpercent=0.0005, transmit=False, valid=None)
self.long_stoplimit = self.sell(exectype=bt.Order.StopTrailLimit, size=self.position.size, trailpercent=0.0005, plimit=plimit, parent=self.long_stoploss, transmit=True, valid=None)
But can this be taken to live trading in IB as something that was mentioned in the docs about tradeid?
Hi
I am having some trouble on resampled data and custom indicator.
class BaseStrategy(bt.Strategy):
def __init__(self):
self.zz = ZigZag(self.data0, period=2, retrace=0.05, minbars=2)
def next(self):
print('{},{:.5f},{:.5f},{:.5f}'.format(self.data0.datetime.datetime(0),
self.zz.l.zigzag[0], self.zz.l.last_high[0], self.zz.l.last_low[0]))
def main():
cerebro = bt.Cerebro(oldtrades=True)
cerebro.addstrategy(BaseStrategy)
df = pd.read_csv('datas\\EURUSD.csv', sep=',', header=0, index_col=0, parse_dates=True)
data0 = bt.feeds.PandasData(dataname=df, timeframe=bt.TimeFrame.Minutes)
cerebro.adddata(data0)
cerebro.resampledata(data0, timeframe=bt.TimeFrame.Minutes, compression=240)
cerebro.run(maxcpus=3, stdstats=False, runonce=True)
if __name__ == '__main__':
main()
Simple enough code that passes in 2 datas. Run an indicator on init and run it on data0 and print out the line values in the next() method. However, the above code does not work. The zz.l.zigzag[0] are always nan. If I remove resampleddata() line, it works and it works with only this single change.
Code of the indicator is below, courtesy of Nikolai. https://community.backtrader.com/topic/1771/zigzag-indicator-for-live-trading/25
class ZigZag(bt.ind.PeriodN): # periodN controls number lines to skip i think, so period is 2 so curr_idx start at 2
lines = ('trend', 'last_high', 'last_low', 'zigzag',)
plotinfo = dict(
subplot=False,
plotlinelabels=True, plotlinevalues=True, plotvaluetags=True,
)
plotlines = dict(
trend=dict(_plotskip=True),
last_high=dict(color='green', ls='-', _plotskip=True),
last_low=dict(color='black', ls='-', _plotskip=True),
zigzag=dict(_name='zz', color='lightblue', ls='-', _skipnan=True), # linewidth=2.0, ls='-',
)
params = (dict(period=2, retrace=0.25, minbars=2, _autoplot=True))
def __init__(self):
super(ZigZag, self).__init__()
assert self.p.retrace > 0, 'Retracement should be above zero.'
assert self.p.minbars >= 0, 'Minimal bars should be >= zero.'
self.ret = self.data.close * self.p.retrace / 100
self.minbars = self.p.minbars
self.count_bars = 0
self.last_pivot_t = 0
self.last_pivot_ago = 0
def prenext(self): # prenext called before the minimum period required for the indicator, this case 2. So called just once.
self.l.trend[0] = 0
self.l.last_high[0] = self.data.high[0]
self.l.last_low[0] = self.data.low[0]
self.l.zigzag[0] = (self.data.high[0] + self.data.low[0]) / 2
# print('prenext: ', self.l.trend[0], self.l.last_high[0], self.l.last_low[0], self.l.zigzag[0] )
def next(self): # this starts at 2nd line of data
curr_idx = len(self.data)
self.ret = self.data.close[0] * self.p.retrace / 100
self.last_pivot_ago = curr_idx - self.last_pivot_t
self.l.trend[0] = self.l.trend[-1]
self.l.last_high[0] = self.l.last_high[-1]
self.l.last_low[0] = self.l.last_low[-1]
self.l.zigzag[0] = float('nan')
# print('next1:',curr_idx,self.data.close[0], self.ret, self.last_pivot_t,self.last_pivot_ago,self.l.trend[0],self.l.last_high[0],self.l.last_low[0])
# Search for trend
if self.l.trend[-1] == 0:
if self.l.last_low[0] < self.data.low[0] and self.l.last_high[0] < self.data.high[0]: # if current bar is higher than last high and last low
self.l.trend[0] = 1
self.l.last_high[0] = self.data.high[0]
self.last_pivot_t = curr_idx
elif self.l.last_low[0] > self.data.low[0] and self.l.last_high[0] > self.data.high[0]: # if current bar is higher than last high and last low
self.l.trend[0] = -1
self.l.last_low[0] = self.data.low[0]
self.last_pivot_t = curr_idx
# Up trend
elif self.l.trend[-1] == 1:
if self.data.high[0] > self.l.last_high[-1]:
self.l.last_high[0] = self.data.high[0]
self.count_bars = self.minbars
self.last_pivot_t = curr_idx
elif self.count_bars <= 0 and self.l.last_high[0] - self.data.low[0] > self.ret and self.data.high[0] < self.l.last_high[0]:
self.l.trend[0] = -1
self.count_bars = self.minbars
self.l.last_low[0] = self.data.low[0]
self.l.zigzag[-self.last_pivot_ago] = self.l.last_high[0]
self.last_pivot_t = curr_idx
elif self.count_bars < self.minbars and self.data.close[0] < self.l.last_low[0]:
self.l.trend[0] = -1
self.count_bars = self.minbars
self.l.last_low[0] = self.data.low[0]
self.l.zigzag[-self.last_pivot_ago] = self.l.last_high[0]
self.last_pivot_t = curr_idx
# Down trend
elif self.l.trend[-1] == -1:
if self.data.low[0] < self.l.last_low[-1]:
self.l.last_low[0] = self.data.low[0]
self.count_bars = self.minbars
self.last_pivot_t = curr_idx
elif self.count_bars <= 0 and self.data.high[0] - self.l.last_low[0] > self.ret and self.data.low[0] > self.l.last_low[0]:
self.l.trend[0] = 1
self.count_bars = self.minbars
self.l.last_high[0] = self.data.high[0]
self.l.zigzag[-self.last_pivot_ago] = self.l.last_low[0]
self.last_pivot_t = curr_idx
elif self.count_bars < self.minbars and self.data.close[0] > self.l.last_high[-1]:
self.l.trend[0] = 1
self.count_bars = self.minbars
self.l.last_high[0] = self.data.high[0]
self.l.zigzag[-self.last_pivot_ago] = self.l.last_low[0]
self.last_pivot_t = curr_idx
# Decrease minbars counter
self.count_bars -= 1
Need some advice why this is happening, even though I have specified that the indicator run on data0. And how can I change the indicator code so that it can run even if resampled data is passed (no need to run on resampled data, but even just running on lower timeframe data would be great!) ?
Thanks
@ab_trader i am in a dilema on this too. With the way backtrader is designed its not possible to do it right? Next() has to go to notify_order().
I mean i can close my position and then jump straight to a new buy. But my cash available is less because the trade that was closed have not been 'processed'.