put anchored VWAPs at the apex of non-repaint ZigZag

StoneMan

Member
Plus
https://usethinkscript.com/threads/...rre-zigzag-for-thinkorswim.20823/#post-157866
Because this zigzag doesn't repaint, I'm really curious if you could put anchored VWAPs at the close of the apex points.

Something like the 5 most recent apexes track a VWAP until the oldest one rolls off and resets to the most recent apex. It might be better to explain the idea as the chart always containing a VWAP that is 1, 2, 3, 4, and 5 apexes old. Whatever can be codified to work within the Thinkscript constraints.

Maybe you could even make the VWAP tracking ignore the gray zone (chop) like the trading strategy does. It might make this idea have a better signal to noise ratio.

Another idea could be looking at the absolute value of the difference between two apexes, taking that as a percentage of the close of the more recent apex, then using that value to analyze how "strong" the VWAP it generates might be. This can be standardized across assets by converting those "VWAP strength" readings to a Z-score. From here arises the potential to scan for assets approaching the highest "VWAP strength Z-score" for mean reversion plays.

Instead of just looking at the most recent apexes you could change the VWAP tracking approach to look at something like 20 of the most recent "VWAP strength Z-score" values and always have a VWAP anchored to the top 5 of those values. Your VWAP lines become "VWAP strength Z score rank 1, 2..."

Aside from just drawing VWAPs, trading strategy wise, only taking the trade if the "VWAP strength Z score rank" (which would just be a measure of trend length because we aren't putting a VWAP on it) is top 5 could be an improvement.

These are just some ideas, I haven't touched Thinkscript in a very long time and am curious if any of this is doable.
 
Last edited by a moderator:
https://usethinkscript.com/threads/...rre-zigzag-for-thinkorswim.20823/#post-157866
Because this zigzag doesn't repaint, I'm really curious if you could put anchored VWAPs at the close of the apex points.

Something like the 5 most recent apexes track a VWAP until the oldest one rolls off and resets to the most recent apex. It might be better to explain the idea as the chart always containing a VWAP that is 1, 2, 3, 4, and 5 apexes old. Whatever can be codified to work within the Thinkscript constraints.

Maybe you could even make the VWAP tracking ignore the gray zone (chop) like the trading strategy does. It might make this idea have a better signal to noise ratio.

Another idea could be looking at the absolute value of the difference between two apexes, taking that as a percentage of the close of the more recent apex, then using that value to analyze how "strong" the VWAP it generates might be. This can be standardized across assets by converting those "VWAP strength" readings to a Z-score. From here arises the potential to scan for assets approaching the highest "VWAP strength Z-score" for mean reversion plays.

Instead of just looking at the most recent apexes you could change the VWAP tracking approach to look at something like 20 of the most recent "VWAP strength Z-score" values and always have a VWAP anchored to the top 5 of those values. Your VWAP lines become "VWAP strength Z score rank 1, 2..."

Aside from just drawing VWAPs, trading strategy wise, only taking the trade if the "VWAP strength Z score rank" (which would just be a measure of trend length because we aren't putting a VWAP on it) is top 5 could be an improvement.

These are just some ideas, I haven't touched Thinkscript in a very long time and am curious if any of this is doable.
I had some fun working on this over the past hour. I focused on getting clean, non-repainting vwaps anchored to the five most recent zigzag apexes. Each new apex replaces the oldest one, and every vwap correctly resets at its own pivot without jumping from the start of the chart. I didn’t add the extra stuff yet (chop filtering, ranking pivots, z-score logic, etc.), just wanted the core system solid first. Let me know what you think. Here is the code:
Ruby:
input showZigZag = no;

# --- ZigZag Core ---
input Price = HL2;
input Gamma = 0.80;
input LowHighLookback = 10;

# 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 Uptick = if (L0 > Filt) then 1 else Double.NaN;
def Downtick = if (L0 < Filt) then 1 else Double.NaN;

def UpSig = if Downtick[1] == 1 and Uptick[0] == 1 then Lowest(low, LowHighLookback) else Double.NaN;
def DnSig = if Uptick[1] == 1 and Downtick[0] == 1 then Highest(high, LowHighLookback) else Double.NaN;

def ZZL = if !IsNaN(UpSig) then UpSig else if !IsNaN(DnSig)  then DnSig else Double.NaN;

# === Identify Pivot Bars ===
def iszz = !isnan(zzl);
def bn = barnumber();

def pivotbar = if iszz then bn else pivotbar[1];

# Last 5 pivots (most recent = pivot1)
def pivot1 = highestall(if pivotbar == highestall(pivotbar) then pivotbar else 0);
def pivot2 = highestall(if pivotbar < pivot1 then pivotbar else 0);
def pivot3 = highestall(if pivotbar < pivot2 then pivotbar else 0);
def pivot4 = highestall(if pivotbar < pivot3 then pivotbar else 0);
def pivot5 = highestall(if pivotbar < pivot4 then pivotbar else 0);

# --- Anchored VWAP Script (mostly from charles schwab) ---
script anchoredVWAP {
    input anchorbar = 0;

    def bn = barnumber();
    def start = bn == anchorbar;

    def volSum = CompoundValue(1,
        if start then volume else volSum[1] + volume,
        volume);

    def volPriceSum = CompoundValue(1,
        if start then volume * vwap else volPriceSum[1] + volume * vwap,
        volume * vwap);

    plot out = if bn < anchorbar then double.nan else (volpricesum / volsum);
}

# --- 5 Anchored VWAPs from last 5 ZigZag pivots ---
plot v1 = anchoredvwap(pivot1);
plot v2 = anchoredvwap(pivot2);
plot v3 = anchoredvwap(pivot3);
plot v4 = anchoredvwap(pivot4);
plot v5 = anchoredvwap(pivot5);

v1.setdefaultcolor(color.blue);
v2.setdefaultcolor(color.green);
v3.setdefaultcolor(color.yellow);
v4.setdefaultcolor(color.orange);
v5.setdefaultcolor(color.red);

# OPTIONAL: Show ZigZag line visually
plot zigzaglaguerre = if showzigzag then zzl else double.nan;
zigzaglaguerre.enableapproximation();

Screenshot 2025-11-18 at 8.04.45 PM.png

Cyan vertical lines are where each new anchored vwap starts.
 
I added a rejection to reject small changes as a trend. Otherwise the Laguerre is noisy in flat periods.

I went a slightly different route for the anchored VWAP. The anchors are each pivot, painted individually. A concept shown by SleepyZ here: https://usethinkscript.com/threads/anchored-vwap-indicator-for-thinkorswim.171/page-19#post-155405

Enjoy!

Code:
# Laguerre ZigZag - A responsive, non-repainting ZigZag indicator
# with Anchored VWAP at the pivots - Concept by SleepyZ = https://usethinkscript.com/threads/anchored-vwap-indicator-for-thinkorswim.171/page-19#post-155405
# Authored by whoDAT - November 18, 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.25;

# 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 ZZL = if !IsNaN(UpSig) then UpSig else if !IsNaN(DnSig)  then DnSig else Double.NaN;

plot ZigZagLaguerre = ZZL;
ZigZagLaguerre.EnableApproximation();


# Adding VWAP anchored at the pivot points

input showarrows  = yes;
input showbubbles = yes;
input ticks  = 0.0;

input PivotHigh_Basis = {default High, OHLC4};
#def h = if PivotHigh_Basis==PivotHigh_Basis.OHLC4 then Ohlc4 else High;
def h = high;
def na = Double.NaN;

plot Up = UpSig;
Up.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
Up.SetLineWeight(3);
Up.SetDefaultColor(Color.WHITE);
Up.SetHiding(!showarrows);
AddChartBubble(showbubbles and Up, low, "VB", Color.GREEN, no);

plot Dn = DnSig;
Dn.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
Dn.SetLineWeight(3);
Dn.SetDefaultColor(Color.ORANGE);
Dn.SetHiding(!showarrows);
AddChartBubble(showbubbles and Dn, h, "VS", Color.RED);

def LocH = (h + (TickSize() * ticks)) * volume;
def LocL = (low  - (TickSize() * ticks)) * volume;
def LocC = close * volume;

rec PC;
rec VC;
rec PC2;
rec VC2;
rec PH;
rec VH;
rec PL;
rec VL;
rec PH2;
rec VH2;
rec PL2;
rec VL2;

if !isNaN(Dn) or !isNaN(Up) {
    PC = LocC;
    VC = volume;
    PC2 = PC[1];
    VC2 = VC[1];
} else {
    PC = CompoundValue(1, LocC + PC[1], na);
    VC = CompoundValue(1, volume + VC[1], na);
    PC2 = CompoundValue(1, LocC + PC2[1], na);
    VC2 = CompoundValue(1, volume + VC2[1], na);
}


if !isNaN(Dn) {
    PH = LocH;
    VH = volume;
    PH2 = PH[1];
    VH2 = VH[1];
} else {
    PH = CompoundValue(1, LocH + PH[1], na);
    VH = CompoundValue(1, volume + VH[1], na);
    PH2 = CompoundValue(1, LocH + PH2[1], na);
    VH2 = CompoundValue(1, volume + VH2[1], na);
}
if !isNaN(Up)  {
    PL = LocL;
    VL = volume;
    PL2 = PL[1];
    VL2 = VL[1];
} else {
    PL = CompoundValue(1, LocL + PL[1], na);
    VL = CompoundValue(1, volume + VL[1], na);
    PL2 = CompoundValue(1, LocL + PL2[1], na);
    VL2 = CompoundValue(1, volume + VL2[1], na);
}

plot VwapC = if !isNaN(Dn[-1]) or !isNaN(Up[-1]) then na else PC / VC;
plot VwapC2 = if !isNaN(Dn[-1]) or !isNaN(Up[-1]) then na else PC2 / VC2;
plot VwapH = if !isNaN(Dn[-1]) then na else PH / VH;
plot VwapL = if !isNaN(Up[-1]) then na else PL / VL;
plot VwapH2 = if !isNaN(Dn[-1]) then na else PH2 / VH2;
plot VwapL2 = if !isNaN(Up[-1]) then na else PL2 / VL2;

VwapC.SetDefaultColor(Color.YELLOW);
VwapC.SetLineWeight(2);
VwapC.HideBubble();

VwapC2.SetDefaultColor(Color.YELLOW);
VwapC2.SetLineWeight(2);
VwapC2.SetStyle(Curve.SHORT_DASH);
VwapC2.HideBubble();

VwapH.SetDefaultColor(Color.DARK_RED);
VwapH.HideBubble();
VwapH.SetLineWeight(5);

VwapL.SetDefaultColor(Color.DARK_GREEN);
VwapL.HideBubble();
VwapL.SetLineWeight(5);

VwapH2.SetDefaultColor(Color.DARK_RED);
VwapH2.SetStyle(Curve.SHORT_DASH);
VwapH2.HideBubble();

VwapL2.SetDefaultColor(Color.DARK_GREEN);
VwapL2.SetStyle(Curve.SHORT_DASH);
VwapL2.HideBubble();
 

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
3734 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top