Percent Price Oscillator conversion
-
Hello,
I am new to the backtrader community and need help with a specific indicator that I am trying to convert. Indicator is used for finding market tops and bottom based on the calculations.
I am struggling to replicate those calculations in backtrader. Below is the specific function that I am not sure how to convert. As you can see in the function it used L0[1], I am not sure how to get this value. I was thinking of putting this value in an array but how would I reference it. or I should create an array the first time it will be zero and for the next candle it will have an value at index 1 and keep track of the candle number and use always candle-1. just thinking out loud
lag(g, p) =>
L0 = (1 - g)p+gnz(L0[1])
L1 = -gL0+nz(L0[1])+gnz(L1[1])
L2 = -gL1+nz(L1[1])+gnz(L2[1])
L3 = -gL2+nz(L2[1])+gnz(L3[1])
f = (L0 + 2L1 + 2L2 + L3)/6
fLink to the Indicator
https://www.tradingview.com/script/ngr0qRmw-CM-Laguerre-PPO-PercentileRank-Mkt-Tops-Bottoms/ -
@amit this looks like recursive indicator, so I would search docs and arcticles for this approach:
https://backtrader.com/docu/
https://backtrader.com/blog/Look on how the recursive built-in indicators are developed.
https://github.com/mementum/backtrader/tree/master/backtrader/indicatorsAlso search the forum, IIRC Laguerre indicators were discussed here.
-
@ab_trader Thank you for having a look. I will keep digging and share if I find anything
-
-
-
@ab_trader Hello, thank you for the reference and apologies for late reply. I was trying to make the code changes and make it work.
As in the suggested post I managed to get the array values correctly populated. thank you for that.
Now I am stuck at PercentRank indicatot. its returning no value. and when I do next() on pctRankT it gives Divide by zero error. I looked in the lambda function, is it that it has to have all the lookback values before it can be used. I am using ccxtfeed to get the data from Binance.
pctRankT = bt.indicators.PercentRank(ppoT, period=self.params.LookBackTop)
pctRankB = bt.indicators.PercentRank(ppoB, period=self.params.LookBackBottom) * -1if(pctRankT >= self.params.ExtPercent or (pctRankT >= self.params.WarPercent and pctRankT < self.params.ExtPercent)):
top = 1Thank you very much for your help.
-
@ab_trader Just wanted to check, is there anywhere I can send you the code. I just need bit of guidance.
Regards
-
@amit said in Percent Price Oscillator conversion:
Just wanted to check, is there anywhere I can send you the code. I just need bit of guidance.
usually if somebody needs help, they post the scripts here.
-
@ab_trader hello, here is the full code.
import backtrader as bt
import mathclass CustomStrategy(bt.Strategy):
# Moving average parameters
params = (('OscPeriod',50),('AdxPeriod',14), ('AtrPeriod',24),
('ExtPercent',70),('WarPercent',90),
('PPOShort',0.4),('PPOLong',0.8),
('LookBackTop',200),('LookBackBottom',200),
('AdxRange',20))def log(self, txt, dt=None): dt = dt or self.datas[0].datetime.date(0) print(f'{dt.isoformat()} {txt}') # Comment this line when running optimization def __init__(self): self.L0S = [0,0] self.L1S=[0,0] self.L2S=[0,0] self.L3S=[0,0] self.L0L = [0,0] self.L1L=[0,0] self.L2L=[0,0] self.L3L=[0,0] self.dataclose = self.datas[0].close # Order variable will contain ongoing order details/status self.order = None # Adx values self.pDi = bt.indicators.AverageDirectionalMovementIndex(self.datas[0],period=self.params.AdxPeriod).DIplus self.mDi = bt.indicators.AverageDirectionalMovementIndex(self.datas[0],period=self.params.AdxPeriod).DIminus # ATR values self.atr = bt.indicators.AverageTrueRange(self.datas[0],period=self.params.AtrPeriod) #Calculate TP self.tpBuy = self.datas[0].close + self.atr *0.95 self.tpSell = self.datas[0].close - self.atr *0.95 #calculate stop loss #self.fractal = bt.studies.contrib.fractal.Fractal(self.datas, period=2, bardist=0.01).lines #self.fup = self.fractal.fractal_bullish[0] #self.fdown = self.fractal.fractal_bearish[0] #Previous Day Close self.close = LastClose(self.datas[1]) #hl2 self.hl2=(self.data.high+self.data.low)/2 self.text =0 def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # An active Buy/Sell order has been submitted/accepted - 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(f'BUY EXECUTED, {order.executed.price:.2f}') elif order.issell(): self.log(f'SELL EXECUTED, {order.executed.price:.2f}') self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') # Reset orders self.order = None def lagS(self,g,p): self.L0S.insert(0, (1 - g) * p + g * self.L0S[1] ) self.L1S.insert(0, -g * self.L0S[0] + self.L0S[1] + g * self.L1S[1]) self.L2S.insert(0, -g * self.L1S[0] + self.L1S[1] + g * self.L2S[1]) self.L3S.insert(0, -g * self.L2S[0] + self.L2S[1] + g * self.L3S[1]) f = (self.L0S[0] + 2 * self.L1S[0] + 2 * self.L2S[0] + self.L3S[0]) / 6 return f def lagL(self,g,p): self.L0L.insert(0, (1 - g) * p + g * self.L0L[1] ) self.L1L.insert(0, -g * self.L0L[0] + self.L0L[1] + g * self.L1L[1]) self.L2L.insert(0, -g * self.L1L[0] + self.L1L[1] + g * self.L2L[1]) self.L3L.insert(0, -g * self.L2L[0] + self.L2L[1] + g * self.L3L[1]) f = (self.L0L[0] + 2 * self.L1L[0] + 2 * self.L2L[0] + self.L3L[0]) / 6 return f def next(self): lmas=0 lmal=0 top =0 bottom =0 #Top & Bottom calculations lmal = self.lagL(self.params.PPOLong,self.hl2[0]) lmas = self.lagS(self.params.PPOShort,self.hl2[0]) #Remove any data beyond 2 iterations del self.L0S[2:] del self.L1S[2:] del self.L2S[2:] del self.L3S[2:] del self.L0L[2:] del self.L1L[2:] del self.L2L[2:] del self.L3L[2:] pctileB = self.params.ExtPercent * -1 wrnpctileB = self.params.WarPercent * -1 ppoT = (lmas-lmal)/lmal*100 ppoB = (lmal - lmas)/lmal*100 pctRankT = bt.indicators.PercentRank(ppoT, period=self.params.LookBackTop) pctRankB = bt.indicators.PrettyGoodOscillator .PercentRank(ppoB, period=self.params.LookBackBottom) * -1 if(pctRankT >= self.params.ExtPercent or (pctRankT >= self.params.WarPercent and pctRankT < self.params.ExtPercent)): top = 1 if(pctRankB <= pctileB or (pctRankB <= wrnpctileB and pctRankB > pctileB)): bottom = 1 # Check for open orders if self.order: return # Check if we are in the market if not self.position: # We are not in the market, look for a signal to OPEN trades #Calculate Top and Bottom if the Color on HMA is changed if(top == 1 and self.pdi > self.params.AdxRange): self.log(f'BUY CREATE {self.dataclose[0]:2f}') # Keep track of the created order to avoid a 2nd order self.order = self.buy() elif (bottom == 1 and self.mdi > self.params.AdxRange): self.log(f'SELL CREATE {self.dataclose[0]:2f}') # Keep track of the created order to avoid a 2nd order self.order = self.sell() else: if ( (self.position.size>0) and (self.dataclose[0] >= self.tpBuy) ): self.log(f'CLOSE CREATE {self.dataclose[0]:2f}') self.order = self.close() elif ( (self.position.size<0) and (self.dataclose[0] <= self.tpSell) ): self.log(f'CLOSE CREATE {self.dataclose[0]:2f}') self.order = self.close()
-
@ab_trader hello, I have been trying to set it up from last few days, please help. Do I need to put the calcs as separate Indicator?
Regards
-
@amit i think it will be more convenient to have it as an indicator, but nobody prevents you from making calcs in the strategy. By the way, if you check the indicators in the docs, than you will find out that some of the indicators are already included in
bt
:https://backtrader.com/docu/indautoref/#laguerrefilter
https://backtrader.com/docu/indautoref/#laguerrersi -
@ab_trader thx for the reply. I will try to get the cals going in the strategy itself and then clean up the code as separate indicator. Could you please take a look why the PercentRank is not returning anything in the next call. This is where I am stuck. Very much appreciate your help.
Regards
-
@ab_trader Sorry for not paying attention. I see the cals are already implemented in laguerrefilter. I will give it a go now, also it will be great full if you can share an example of Fractal use. I have tried using
self.fractal = bt.studies.contrib.fractal.Fractal(self.datas, period=2, bardist=0.01)
but get nan in the self.fractal.fractal_bearish[0]. what am I doing wrong? I am using fractals for StopLoss.
Regards
-
@amit said in Percent Price Oscillator conversion:
but get nan in the self.fractal.fractal_bearish[0]. what am I doing wrong? I am using fractals for StopLoss.
you are not trying to figure out what fractal indicator actually delivers and why it should deliver non-
nan
value. honestly i have no idea what does that indicator delivers, but simple fractal definition suggests that most of the bars should havenan
and only rare fractal bars should delivertrue
or certain price values. -
@ab_trader Thank you for the answer. Could you please suggest how should I look for the recent low or high, I want to use that as stop loss.
Regards
-