# Laguerre ZigZag - A responsive, non-repainting ZigZag indicator
# Authored by whoDAT - April 1, 2025
# A HL2 input is typically better to reduce the number of zigs and zags.
# The Gamma is the a dampening function.
# - Increasing Gamma values (approaching 1.0) will dampen the input more, reducing the number of zigs and zags.
# - Lower Gamma values (approaching 0.0) will make a noisy output with more zigs and zags.
# The LowHighLookBack defines the how far back to look for the lowest or highest value to use when painting the ZigZag line.
input Price = HL2;
input Gamma = 0.80;
input LowHighLookback = 10;
input useRejection = yes;
input rejectPercent = 0.0001;
# Laguerre filter
def L0 = (1 - Gamma) * Price + (Gamma * L0[1]);
def L1 = (-1 * Gamma * L0) + L0[1] + (Gamma * L1[1]);
def L2 = (-1 * Gamma * L1) + L1[1] + (Gamma * L2[1]);
def L3 = (-1 * Gamma * L2) + L2[1] + (Gamma * L3[1]);
def Filt = (L0 + (2 * L1) + (2 * L2) + L3) / 6;
def FIR = (Price + (2 * Price[1]) + (2 * Price[2]) + Price[3]) / 6;
# If the Laguerre filter is increasing (or decreasing), this is an uptick (downtick). That is used to make the upward or downward signal to find the zigs and zags.
def rejUp = if useRejection then (Filt + Price * rejectPercent / 100) else Filt;
def rejDn = if useRejection then (Filt - Price * rejectPercent / 200) else Filt;
def Uptick = if (L0 >= rejUp) then 1 else 0;
def Downtick = if (L0 <= rejDn) then 1 else 0;
def UpSig = if Downtick[1] == 1 and Uptick == 1 then low else Double.NaN;
def DnSig = if Uptick[1] == 1 and Downtick == 1 then high else Double.NaN;
#def UpSig = if Downtick[1] == 1 and Uptick == 1 then Lowest(low, LowHighLookback) else Double.NaN;
#def DnSig = if Uptick[1] == 1 and Downtick == 1 then Highest(high, LowHighLookback) else Double.NaN;
def ZZL = if !IsNaN(UpSig) then UpSig else if !IsNaN(DnSig) then DnSig else Double.NaN;
plot ZigZagLaguerre = ZZL;
ZigZagLaguerre.EnableApproximation();
plot FilterResponse = Filt;
plot FiniteImpulseResponse = FIR;
plot UpSignal = UpSig;
plot DownSignal = DnSig;
##New testing code##################################################################################
#############################################################################
#############################################################
### Determine a flat market
#############################################################
input TradeInFlatRange = Yes;
input BarsForFlatRange = 15;
input BarsReqToStayInRange = 13;
def HH = Highest(high[1], BarsForFlatRange);
def LL = Lowest(low[1], BarsForFlatRange);
def maxH = Highest(HH, BarsReqToStayInRange);
def maxL = Lowest(LL, BarsReqToStayInRange);
def HHn = if maxH == maxH[1] or maxL == maxL then maxH else HHn[1];
def LLn = if maxH == maxH[1] or maxL == maxL then maxL else LLn[1];
def Bh = if high <= HHn and HHn == HHn[1] then HHn else Double.NaN;
def Bl = if low >= LLn and LLn == LLn[1] then LLn else Double.NaN;
def CountH = if IsNaN(Bh) or IsNaN(Bl) then 2 else CountH[1] + 1;
def CountL = if IsNaN(Bh) or IsNaN(Bl) then 2 else CountL[1] + 1;
def ExpH = if BarNumber() == 1 then Double.NaN else
if CountH[-BarsReqToStayInRange] >= BarsReqToStayInRange then HHn[-BarsReqToStayInRange] else
if high <= ExpH[1] then ExpH[1] else Double.NaN;
def ExpL = if BarNumber() == 1 then Double.NaN else
if CountL[-BarsReqToStayInRange] >= BarsReqToStayInRange then LLn[-BarsReqToStayInRange] else
if low >= ExpL[1] then ExpL[1] else Double.NaN;
plot BoxHigh = if !IsNaN(ExpL) and !IsNaN(ExpH) then ExpH else Double.NaN;
plot BoxLow = if !IsNaN(ExpL) and !IsNaN(ExpH) then ExpL else Double.NaN;
input showFlatCloud = yes;
AddCloud(if showFlatCloud then BoxHigh else Double.NaN, BoxLow, Color.GRAY, Color.GRAY);
#addcloud( BoxHigh, BoxLow, color.gray, color.gray);
def Flat = if (!IsNaN(BoxHigh[1]) and !IsNaN(BoxLow[1])) and !TradeInFlatRange then 1 else 0;
#addChartBubble(Flat==1 and isNan(Flat[1]),BoxHigh[1],"Flat Market",color.gray,yes);
############################################
## Define BuySignal and SellSignal above
## or uncomment them out below and set
## them to your buy/sell conditions
##
## If using stops, define them below
############################################
###------------------------------------------------------------------------------------------
input showSignals = yes;
input showLabels = yes;
input showBubbles = yes;
input useStops = no;
input useAlerts = no;
###------------------------------------------------------------------------------------------
############################################
## Create Signals -
## FILL IN THIS SECTION IF NOT DEFINED ABOVE
##
############################################
#Laguerre ZigZag Buy Sell
def BuySignal = !IsNaN(UpSig) and !Flat ; # insert condition to create long position
def SellSignal = !IsNaN(DnSig) and !Flat; # insert condition to create short position
def BuyStop = if !useStops then 0 else if !IsNaN(DnSig) and !Flat then 1 else 0;
def SellStop = if !useStops then 0 else if !IsNaN(UpSig) and !Flat then 1 else 0;
#def BuyStop = if !useStops then 0 else if state == state.long and (haOpen > haClose) and Flat then 1 else 0 ; # insert condition to stop in place of the 0<0
#def SellStop = if !useStops then 0 else if state == state.short and (haOpen < haClose) and Flat then 1 else 0 ; # insert condition to stop in place of the 0>0
#######################################
## Maintain the position of trades
#######################################
def CurrentPosition; # holds whether flat = 0 long = 1 short = -1
if (BarNumber() == 1) or IsNaN(CurrentPosition[1]) {
CurrentPosition = 0;
} else {
if CurrentPosition[1] == 0 { # FLAT
if (BuySignal) {
CurrentPosition = 1;
} else if (SellSignal) {
CurrentPosition = -1;
} else {
CurrentPosition = CurrentPosition[1];
}
} else if CurrentPosition[1] == 1 { # LONG
if (SellSignal) {
CurrentPosition = -1;
} else if (BuyStop and useStops) {
CurrentPosition = 0;
} else {
CurrentPosition = CurrentPosition[1];
}
} else if CurrentPosition[1] == -1 { # SHORT
if (BuySignal) {
CurrentPosition = 1;
} else if (SellStop and useStops) {
CurrentPosition = 0;
} else {
CurrentPosition = CurrentPosition[1];
}
} else {
CurrentPosition = CurrentPosition[1];
}
}
def isLong = if CurrentPosition == 1 then 1 else 0;
def isShort = if CurrentPosition == -1 then 1 else 0;
def isFlat = if CurrentPosition == 0 then 1 else 0;
# If not already long and get a BuySignal
plot BuySig = if (!isLong[1] and BuySignal and showSignals) then 1 else 0;
#BuySig.AssignValueColor(Color.CYAN);
BuySig.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
BuySig.SetLineWeight(5);
Alert(BuySig and useAlerts, "Buy Signal", Alert.BAR, Sound.Ding);
Alert(BuySig and useAlerts, "Buy Signal", Alert.BAR, Sound.Ding);
# If not already short and get a SellSignal
plot SellSig = if (!isShort[1] and SellSignal and showSignals) then 1 else 0;
#SellSig.AssignValueColor(Color.YELLOW);
SellSig.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
SellSig.SetLineWeight(5);
Alert(SellSig and useAlerts, "Sell Signal", Alert.BAR, Sound.Ding);
Alert(SellSig and useAlerts, "Sell Signal", Alert.BAR, Sound.Ding);
# If long and get a BuyStop
plot BuyStpSig = if (BuyStop and isLong[1] and showSignals and useStops) then 1 else 0;
BuyStpSig.AssignValueColor(Color.LIGHT_GRAY);
BuyStpSig.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
BuyStpSig.SetLineWeight(3);
# If short and get a SellStop
plot SellStpSig = if (SellStop and isShort[1] and showSignals and useStops) then 1 else 0;
SellStpSig.AssignValueColor(Color.LIGHT_GRAY);
SellStpSig.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
SellStpSig.SetLineWeight(3);
#######################################
## Orders
#######################################
def isOrder = if CurrentPosition == CurrentPosition[1] then 0 else 1; # Position changed so it's a new order
# If there is an order, then the price is the next days close
#def orderPrice = if (isOrder and (BuySignal or SellSignal)) then close else orderPrice[1];
def orderPrice = if (isOrder and BuySignal) then high else if (isOrder and SellSignal) then low else orderPrice[1];
#######################################
## Price and Profit
#######################################
def profitLoss;
if (!isOrder or orderPrice[1] == 0) {
profitLoss = 0;
} else if ((isOrder and isLong[1]) and (SellSig or BuyStpSig)) {
profitLoss = close - orderPrice[1];
} else if ((isOrder and isShort[1]) and (BuySig or SellStpSig)) {
profitLoss = orderPrice[1] - close;
} else {
profitLoss = 0;
}
# Total Profit or Loss
input ProfitLeverageMultiple = 1;
def profitLossSum = CompoundValue(1, if IsNaN(isOrder) or BarNumber() == 1 then 0 else if isOrder then profitLossSum[1] + profitLoss else profitLossSum[1], 0);
# How many trades won or lost
def profitWinners = CompoundValue(1, if IsNaN(profitWinners[1]) or BarNumber() == 1 then 0 else if isOrder and profitLoss > 0 then profitWinners[1] + 1 else profitWinners[1], 0);
def profitLosers = CompoundValue(1, if IsNaN(profitLosers[1]) or BarNumber() == 1 then 0 else if isOrder and profitLoss < 0 then profitLosers[1] + 1 else profitLosers[1], 0);
def profitPush = CompoundValue(1, if IsNaN(profitPush[1]) or BarNumber() == 1 then 0 else if isOrder and profitLoss == 0 then profitPush[1] + 1 else profitPush[1], 0);
def profitLongWinners = CompoundValue(1, if IsNaN(profitLongWinners[1]) or BarNumber() == 1 then 0 else if isOrder and profitLoss > 0 and isLong[1] then profitLongWinners[1] + 1 else profitLongWinners[1], 0);
def profitShortWinners = CompoundValue(1, if IsNaN(profitShortWinners[1]) or BarNumber() == 1 then 0 else if isOrder and profitLoss > 0 and isShort[1] then profitShortWinners[1] + 1 else profitShortWinners[1], 0);
def orderCount = (profitWinners + profitLosers + profitPush) - 1;
# Current Open Trade Profit or Loss
def TradePL = if isLong then Round(((close - orderPrice) / TickSize()) * TickValue()) else if isShort then Round(((orderPrice - close) / TickSize()) * TickValue()) else 0;
# Convert to actual dollars based on Tick Value for bubbles
def dollarProfitLoss = if orderPrice[1] == 0 or IsNaN(orderPrice[1]) then 0 else Round((profitLoss / TickSize()) * TickValue() * ProfitLeverageMultiple);
# Closed Orders dollar P/L
def dollarPLSum = Round((profitLossSum / TickSize()) * TickValue() * ProfitLeverageMultiple);
# Split profits or losses by long and short trades
def profitLong = CompoundValue(1, if IsNaN(profitLong[1]) or BarNumber() == 1 then 0 else if isOrder and isLong[1] then profitLong[1] + dollarProfitLoss else profitLong[1], 0);
def profitShort = CompoundValue(1, if IsNaN(profitShort[1]) or BarNumber() == 1 then 0 else if isOrder and isShort[1] then profitShort[1] + dollarProfitLoss else profitShort[1], 0);
def countLong = CompoundValue(1, if IsNaN(countLong[1]) or BarNumber() == 1 then 0 else if isOrder and isLong[1] then countLong[1] + 1 else countLong[1], 0);
def countShort = CompoundValue(1, if IsNaN(countShort[1]) or BarNumber() == 1 then 0 else if isOrder and isShort[1] then countShort[1] + 1 else countShort[1], 0);
# What was the biggest winning and losing trade
def biggestWin = CompoundValue(1, if IsNaN(biggestWin[1]) or BarNumber() == 1 then 0 else if isOrder and (dollarProfitLoss > 0) and (dollarProfitLoss > biggestWin[1]) then dollarProfitLoss else biggestWin[1], 0);
def biggestLoss = CompoundValue(1, if IsNaN(biggestLoss[1]) or BarNumber() == 1 then 0 else if isOrder and (dollarProfitLoss < 0) and (dollarProfitLoss < biggestLoss[1]) then dollarProfitLoss else biggestLoss[1], 0);
# What percent were winners
def PCTWin = Round((profitWinners / orderCount) * 100, 2);
def PCTLongWin = Round((profitLongWinners / countLong) * 100, 2);
def PCTShortWin = Round((profitShortWinners / countShort) * 100, 2);
# Average trade
def avgTrade = Round((dollarPLSum / orderCount), 2);
#######################################
## Create Labels
#######################################
#AddLabel(showLabels, GetSymbol()+" Tick Size: "+TickSize()+" Value: "+TickValue(), color.white, location = Location.BOTTOM_LEFT);
AddLabel(showLabels, "L-Orders: " + orderCount + " P/L: " + AsDollars(dollarPLSum), if dollarPLSum > 0 then Color.GREEN else if dollarPLSum < 0 then Color.RED else Color.GRAY, location = Location.BOTTOM_LEFT);
#AddLabel(if !IsNan(orderPrice) and showLabels then 1 else 0, "Closed+Open P/L: "+ AsDollars(TradePL+dollarPLSum), if ((TradePL+dollarPLSum) > 0) then color.green else if ((TradePL+dollarPLSum) < 0) then color.red else color.gray, location = Location.BOTTOM_LEFT);
#AddLabel(showLabels, "Avg per Trade: "+ AsDollars(avgTrade), if avgTrade > 0 then Color.Green else if avgTrade < 0 then Color.RED else Color.GRAY, location = Location.BOTTOM_LEFT);
AddLabel(showLabels, "Winners: " + PCTWin + "%", if PCTWin > 50 then Color.GREEN else if PCTWin > 40 then Color.YELLOW else Color.GRAY, location = Location.BOTTOM_LEFT);
AddLabel(showLabels, "Long Win: " + PCTLongWin + "%", if PCTLongWin > 50 then Color.GREEN else if PCTLongWin > 40 then Color.YELLOW else Color.GRAY, location = Location.BOTTOM_LEFT);
AddLabel(showLabels, "Short Win: " + PCTShortWin + "%", if PCTShortWin > 50 then Color.GREEN else if PCTShortWin > 40 then Color.YELLOW else Color.GRAY, location = Location.BOTTOM_LEFT);
#AddLabel(showLabels, "MaxUp: "+ AsDollars(biggestWin) +" MaxDown: "+AsDollars(biggestLoss), color.white, location = Location.BOTTOM_LEFT);
AddLabel(showLabels, "Long Profit: " + AsDollars(profitLong), if profitLong > 0 then Color.GREEN else if profitLong < 0 then Color.RED else Color.GRAY, location = Location.BOTTOM_LEFT);
AddLabel(showLabels, "Short Profit: " + AsDollars(profitShort), if profitShort > 0 then Color.GREEN else if profitShort < 0 then Color.RED else Color.GRAY, location = Location.BOTTOM_LEFT);
#AddLabel(if !IsNan(CurrentPosition) and showLabels then 1 else 0, "Open: "+ (If isLong then "Bought" else "Sold") + " @ "+orderPrice, color.white, location = Location.BOTTOM_LEFT);
#AddLabel(if !IsNan(orderPrice) and showLabels then 1 else 0, "Open Trade P/L: "+ AsDollars(TradePL), if (TradePL > 0) then color.green else if (TradePl < 0) then color.red else color.gray, location = Location.BOTTOM_LEFT);
#######################################
## Chart Bubbles for Profit/Loss
#######################################
AddChartBubble(showSignals and showBubbles and isOrder and isLong[1], low, "$" + dollarProfitLoss, if dollarProfitLoss == 0 then Color.LIGHT_GRAY else if dollarProfitLoss > 0 then Color.GREEN else Color.RED, 0);
AddChartBubble(showSignals and showBubbles and isOrder and isShort[1], high, "$" + dollarProfitLoss, if dollarProfitLoss == 0 then Color.LIGHT_GRAY else if dollarProfitLoss > 0 then Color.GREEN else Color.RED, 1);
#AssignPriceColor(if CurrentPosition == 1 then color.green else if CurrentPosition == -1 then color.red else color.gray);
#-- END of CODE