@run-out
sorry for not posting the whole thing.....Ive cut it down to something easier to understand and follow.
Ive added some debugging print statements to follow along.
The code attempts to use the custom indicator (copying the idea in the dynamic highest example, setting the param to True/False, 0 otherwise), and also calculating the stop price in the strategy next to show that it works outside of the custom indicator.
note that if you copy/paste it directly after one week, you'll need to update the dates in the yfinance statement as it only goes back 7 days.
I learned that the indicator runs through all of the data before the strategy does (printing len shows 388 from next in the indicator before the strategy next begins printing), and so the title is probably now incorrect
but the thing I was trying to achieve still remains...how do I set the indicator parameter to True from the strategy? it doesnt seem to work for me as it does in the Dynamic Indicator example.
I understand that I cant see the self.l.trailstop line on the chart because it is zero throughout (according to strategy print, it is nan according to indicator print), and so outside of the plotted area. I had wanted to see the trailstop appear in the visible range during the period that the param is set to True.
Any thoughts or suggestions on why the custom indicator isnt working?
representative output:
***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 385***
***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 386***
***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 387***
***INDICATOR NEXT self.l.trailatr[0] = nan, tradeopen param = False, at len: 388***
***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 22***
******BUYING, SET PARAM TRUE*****
stop price = 142.62
***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 23***
stop price updated = 142.78
***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 24***
stop price updated = 142.89
***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 25***
***STRAT NEXT self.TrailStop.trailstop[0] = 0.0, at len: 26***
import backtrader.feeds as btfeed
import pandas as pd
import backtrader as bt
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
class TrailStop(bt.Indicator):
lines = ('trailatr','trailstop',)
params = ( ('tradeopen',False),('atr_period', 10),('trail_mult', 4),)
plotinfo = dict(subplot=False)
plotlines = dict(trailstop=dict(color='blue', ls='--',_plotskip=False,),
trailatr=dict(color='black', ls='-', _plotskip=False),
)
def init(self):
self.l.trailatr = bt.indicators.AverageTrueRange(period=self.p.atr_period)
def next(self):
print('***INDICATOR NEXT self.l.trailatr[0] = {}, tradeopen param = {}, at len: {}***'.format(self.l.trailatr[0], self.p.tradeopen, len(self.l.trailatr)))
if self.p.tradeopen == True: # using "if True:" this gets accessed and result is self.l.trailstop[0] = 1. suggests uim using the wrong way to access param?
self.l.trailstop[0] = max(self.dataclose[0] - self.p.trail_mult * self.atr[0], self.l.trailstop[-1])
#print('inside indicator if statement')
else:
self.l.trailstop[0] = min(0,1)
#print('in indicator else statement')
# Create a Stratey
class TestStrategy(bt.Strategy):
params = (
('fast_ma',20),
('trail_mult', 4),
)
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)) # #-out to turn logging off
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.atr = bt.indicators.AverageTrueRange(self.datas[0]) # using for manually calculating exit in strategy next
self.stopprice = 0 # for manually working out the stop in strategy next
self.TrailStop = TrailStop(self.datas[0]) # instantiate the TrailStop Class
# To keep track of pending orders and buy price/commission
self.order = None
self.buyprice = None
self.buycomm = None
# Add a MovingAverageSimple indicator
self.sma = bt.indicators.ExponentialMovingAverage(self.datas[0].close, period=self.p.fast_ma)
self.buysig = bt.indicators.AllN(self.datas[0].low > self.sma, period=3)
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, 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
else: # Sell
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
# Keep track of which bar execution took place
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):
# Simply log the closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
print('***STRAT NEXT self.TrailStop.trailstop[0] = {}, at len: {}***'.format(self.TrailStop.trailstop[0], len(self.datas[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:
# Not yet ... we MIGHT BUY if ...
if self.buysig:
# BUY, BUY, BUY!!! (with default parameters)
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
self.TrailStop.p.tradeopen = True
print('******BUYING, SET PARAM TRUE*****')
self.stopprice = self.data.close[0] - self.p.trail_mult * self.atr[0]
print('stop price = {:.2f}'.format(self.stopprice))
elif self.data.close[0] < self.stopprice :
self.close()
self.stopprice = 0
self.TrailStop.p.tradeopen = False
print('******SELLING, SET PARAM FALSE*****')
if self.stopprice < self.dataclose[0] - self.p.trail_mult * self.atr[0]: # if old price < new price
self.stopprice = self.dataclose[0] - self.p.trail_mult * self.atr[0] # assign new price
print('stop price updated = {:.2f}'.format(self.stopprice))
starting_balance=50000
if __name__ == '__main__':
ticker_id = 'AAPL'
dataframe = pdr.get_data_yahoo(ticker_id, period='1d', interval='1m', start='2021-07-20',end='2021-07-21',prepost=False)
dataframe = dataframe[dataframe.index.strftime('%H:%M') < '20:00']
data = bt.feeds.PandasData(dataname=dataframe)
# Create a cerebro entity, add strategy
cerebro = bt.Cerebro()
cerebro.addstrategy(TestStrategy)
# Add the Data Feed to Cerebro, set desired cash, set desired commission
cerebro.adddata(data)
cerebro.broker.setcash(starting_balance)
cerebro.broker.setcommission(commission=0.0)
# Add a FixedSize sizer according to the stake
cerebro.addsizer(bt.sizers.PercentSizer, percents=10)
# Run over everything
cerebro.run()
cerebro.plot(style='candlestick')