How to use a variable as length?

Hello, and thank you very much for any help you may be able to offer! :)

I'm trying to use a variable (# of bars since last high/low based on ZigZag) to check back to see if the trend has changed (MA changed directions) since the last high/low. I have everything working except how to check <within a variable>.

The DidChange function is what I need.

I'm getting the error:
Only constants expected here: BarCount CL constant function parameter 'length' at 43:36

Code:
#counts bars since last Cycle High/Low
def NewCycle = close[1] == zzoth1;
def BarCount = if NewCycle then 1 else BarCount[1] + 1;
AddLabel (debug, "Bars Since last wave: " + BarCount);

def TrendChange = SMA[1] > SMA[2] and SMA < SMA[1] or SMA[1] < SMA[2] and SMA > SMA[1];
def DidChange = TrendChange within BarCount bars;
AddLabel(debug, "DidChange: " + DidChange);

Thank you again for looking at this and for your time thinking about it.
 
Solution
Try one of these;

DidChange = Sum(TrendChange, Length) >= Yes;

or

DidChange = fold i = 0 to Length with x = no while x == no do GetValue(TrendChange, i);

this one will tell you how many bars ago that the trend changed;

DidChange = fold i = 0 to Length with x = double.nan while isNaN(x) do if GetValue(TrendChange, i) then i else double.nan;

beware I typed these right here, not in tos
Thinkscript doesn't like variables for lengths. I've never found a good work around. Just a limitation we live with for the rest of the good of ToS charting.

-mashume
 
if you are looking for true/false within a reasonable number of days, you can do a long if else if else if else if ... else chain where you look day by day through the ifs, but I wouldn't go overboard on that one for reasons of processor cycles. and it just gets annoying to debug. ;-)

You can also use brackets that way (days > 5 and days <= 10, days > 10 and days <= 15, etc...)

-mashume
 
Try one of these;

DidChange = Sum(TrendChange, Length) >= Yes;

or

DidChange = fold i = 0 to Length with x = no while x == no do GetValue(TrendChange, i);

this one will tell you how many bars ago that the trend changed;

DidChange = fold i = 0 to Length with x = double.nan while isNaN(x) do if GetValue(TrendChange, i) then i else double.nan;

beware I typed these right here, not in tos
 
Last edited:
Solution
Try one of these;

DidChange = Sum(TrendChange, Length) >= Yes;

or

DidChange = fold i = 0 to Length with x = no while x == no do GetValue(TrendChange, i);

this one will tell you how many bars ago that the trend changed;

DidChange = fold i = 0 to Length with x = double.nan while x == double.nan do if GetValue(TrendChange, i) then i else double.nan;

beware I typed these right here, not in tos
Brilliant! Thanks, Joshua! This helps!

Maybe I can store the number of bars since last high/low and the number of bars since the last trend change and create DidChange function based on if high/low is greater than trend change!
 
Hello, and thank you very much for any help you may be able to offer! :)

I'm trying to use a variable (# of bars since last high/low based on ZigZag) to check back to see if the trend has changed (MA changed directions) since the last high/low. I have everything working except how to check <within a variable>.

The DidChange function is what I need.

I'm getting the error:
Only constants expected here: BarCount CL constant function parameter 'length' at 43:36

Code:
#counts bars since last Cycle High/Low
def NewCycle = close[1] == zzoth1;
def BarCount = if NewCycle then 1 else BarCount[1] + 1;
AddLabel (debug, "Bars Since last wave: " + BarCount);

def TrendChange = SMA[1] > SMA[2] and SMA < SMA[1] or SMA[1] < SMA[2] and SMA > SMA[1];
def DidChange = TrendChange within BarCount bars;
AddLabel(debug, "DidChange: " + DidChange);

Thank you again for looking at this and for your time thinking about it.


if you are counting bars after a zigzag signal, then looking to see if something happened during that count, it's the same as did something happen after the last zigzag signal. the count is irrelevant.
every time that a ma reverse occurs, it will be within the counter.

if you asked, did the ma trend change 6 bars after a zigzag reversal, that would be different. then a count would be useful.

it seems you are looking to see when a ma trend reversed, and maybe how many.

--------------------------

i made a study that counts ma (moving average) reversals, between zigzag peaks and valleys.

i started with your partial code, but ended up not using it. i added a zigzag code and an average code.

https://usethinkscript.com/threads/zigzag-high-low-stats-for-thinkorswim.1073/page-6#post-90105
ZigZag High Low Stats for ThinkorSwim
post117

. draws lines from the zigzag peaks and valleys.
. draw an average line
. draw colored points on the ma line, to indicate when a reversal happens.

there are 4 test bubbles to show different values.
test1 - shows ma reversal counts, between zigzag peaks and valleys


one of the test bubbles shows barnumbers.
barnumbers of ma trend changes and zigzag reversals could be saved and compared, to determine when something happened.

Ruby:
# trend_chg_after_zigzag_00b

def bn = barnumber();
def na = double.nan;

# -------------------------------------

# https://usethinkscript.com/threads/zigzag-high-low-stats-for-thinkorswim.1073/page-6#post-90105
# ZigZag High Low Stats for ThinkorSwim
# post117

input Period = AggregationPeriod.FIFTEEN_MIN;
input method = {default average, high_low};

def bubbleoffset = .0005;
def percentamount = .01;
def revAmount = .05;
def atrreversal = 2.0;
def atrlength = 5;
def pricehigh = high(period = Period);
def pricelow = low(period = Period);
def averagelength = 5;
def averagetype = AverageType.EXPONENTIAL;
def mah = MovingAverage(averagetype, pricehigh, averagelength);
def mal = MovingAverage(averagetype, pricelow, averagelength);
def priceh = if method == method.high_low then pricehigh else mah;
def pricel = if method == method.high_low then pricelow else mal;

def zzhl = ZigZagHighLow("price h" = priceh, "price l" = pricel, "percentage reversal" = percentamount, "absolute reversal" = revAmount, "atr length" = atrlength, "atr reversal" = atrreversal);


# bn of zz peaks and valleys
def zzchg_bn = if bn == 1 then 0
  else if !isnan(zzhl) then bn
  else zzchg_bn[1];

# price of zz peaks and valleys
def zzchg_price = if bn == 1 then 0
  else if !isnan(zzhl) then round(zzhl,2)
  else zzchg_price[1];

# direction of zz lines
#   =1 then dip, rev going up
#   =0 then peak, rev going down
def zzdir = if bn == 1 then 0
  else if (!isnan(zzhl) and zzchg_price[1] > zzchg_price) then 1
  else if (!isnan(zzhl) and zzchg_price[1] < zzchg_price) then 0
  else zzdir[1];

# -----------------------------------

input avg1_len = 30;
input avg1_type =  AverageType.simple;
def ma1 = MovingAverage(avg1_type, close, avg1_len);
def sma = ma1;

plot maline = ma1;
maline.setdefaultcolor(color.magenta);

# chg so it looks at future bar
def matrend =
  if bn == 1 then 0
  else if ma1 < ma1[-1] then 1
  else if ma1 > ma1[-1] then -1
  else 0;


def marev = if bn == 1 then 0
  else if matrend != matrend[1] then 1
  else 0;

def machg_bn = if bn == 1 then 0
  else if matrend != matrend[1] then bn
  else machg_bn[1];

input ma_trend_change_points = yes;
plot maup = if (ma_trend_change_points and matrend != matrend[1] and matrend == 1) then ma1 else na;
maup.SetPaintingStrategy(PaintingStrategy.POINTS);
#maup.SetPaintingStrategy(PaintingStrategy.BOOLEAN_WEDGE_up);
maup.SetDefaultColor(Color.green);
maup.setlineweight(4);
maup.hidebubble();

plot madwn = if (ma_trend_change_points and matrend != matrend[1] and matrend == -1) then ma1 else na;
madwn.SetPaintingStrategy(PaintingStrategy.POINTS);
#madwn.SetPaintingStrategy(PaintingStrategy.BOOLEAN_WEDGE_DOWN);
madwn.SetDefaultColor(Color.red);
madwn.setlineweight(4);
madwn.hidebubble();

# --------------------------------------

input draw_zz_lines = yes;
plot zz1 = if draw_zz_lines then zzhl else na;
zz1.EnableApproximation();
zz1.setdefaultcolor(color.gray);

# ------------------------------------
# ------------------------------------
# original code .  not used

#counts bars since last Cycle High/Low
#def NewCycle = close[1] == zzoth1;
def newcycle = !isnan(zzhl);

# count bars after zz rev
def BarCount = if NewCycle then 1 else BarCount[1] + 1;

AddLabel (1, "Bars Since last wave: " + BarCount);
#def TrendChange = SMA[1] > SMA[2] and SMA < SMA[1] or SMA[1] < SMA[2] and SMA > SMA[1];
#def DidChange = TrendChange within BarCount bars;
def DidChange = marev;
#AddLabel(1, "DidChange: " + DidChange);
# ------------------------------------
# ------------------------------------

#count count ma revs , and have the total on the zigzag bar
def marevcnt = if !isnan(zzhl[1]) then marev
else if marev then marevcnt[1] + 1
else marevcnt[1];
#then on the bar after the zigzag, reset cnt to 0 , unless a ma rev


input test1_ma_reverse_count = yes;
def en1 = if test1_ma_reverse_count and !isnan(zzhl) then 1 else 0;

addchartbubble(en1, zzhl,
#round(zzhl,2) + " zz\n" +
 "ma cnt\n" +
 marevcnt
, ( if marevcnt > 0 then color.magenta else color.gray)
, (if zzdir then no else yes));


input test2_zzhl_bubbles = no;
addchartbubble(test2_zzhl_bubbles, zzhl,
 zzhl,
 ( if !isnan(zzhl) then color.yellow else color.gray), (if zzdir then no else yes));


input test3_zzdir_bubbles = no;
addchartbubble(test3_zzdir_bubbles, low,
zzchg_price + " zz $\n" +
 zzdir + " zzdir"
, color.yellow, no);

#addlabel(1, bn + " " + machg_bn + " " + BarCount, color.yellow);

input test4_bubbles_bar_cnts = no;
addchartbubble(test4_bubbles_bar_cnts, low*0.996,
 bn + " bn\n" +
 machg_bn + " ma bn\n" +
 (bn - machg_bn) + " ma cnt\n" +
 BarCount + " zz cnt"
, color.yellow, no);
#


test1 = yes , shows the count of ma reversals between zigzag peaks and valleys

RAoY4uo.jpg
 
Last edited:
if you are counting bars after a zigzag signal, then looking to see if something happened during that count, it's the same as did something happen after the last zigzag signal. the count is irrelevant.
every time that a ma reverse occurs, it will be within the counter.

if you asked, did the ma trend change 6 bars after a zigzag reversal, that would be different. then a count would be useful.

it seems you are looking to see when a ma trend reversed, and maybe how many.

--------------------------

i made a study that counts ma (moving average) reversals, between zigzag peaks and valleys.

i started with your partial code, but ended up not using it. i added a zigzag code and an average code.

https://usethinkscript.com/threads/zigzag-high-low-stats-for-thinkorswim.1073/page-6#post-90105
ZigZag High Low Stats for ThinkorSwim
post117

. draws lines from the zigzag peaks and valleys.
. draw an average line
. draw colored points on the ma line, to indicate when a reversal happens.

there are 4 test bubbles to show different values.
test1 - shows ma reversal counts, between zigzag peaks and valleys


one of the test bubbles shows barnumbers.
barnumbers of ma trend changes and zigzag reversals could be saved and compared, to determine when something happened.

Ruby:
# trend_chg_after_zigzag_00b

def bn = barnumber();
def na = double.nan;

# -------------------------------------

# https://usethinkscript.com/threads/zigzag-high-low-stats-for-thinkorswim.1073/page-6#post-90105
# ZigZag High Low Stats for ThinkorSwim
# post117

input Period = AggregationPeriod.FIFTEEN_MIN;
input method = {default average, high_low};

def bubbleoffset = .0005;
def percentamount = .01;
def revAmount = .05;
def atrreversal = 2.0;
def atrlength = 5;
def pricehigh = high(period = Period);
def pricelow = low(period = Period);
def averagelength = 5;
def averagetype = AverageType.EXPONENTIAL;
def mah = MovingAverage(averagetype, pricehigh, averagelength);
def mal = MovingAverage(averagetype, pricelow, averagelength);
def priceh = if method == method.high_low then pricehigh else mah;
def pricel = if method == method.high_low then pricelow else mal;

def zzhl = ZigZagHighLow("price h" = priceh, "price l" = pricel, "percentage reversal" = percentamount, "absolute reversal" = revAmount, "atr length" = atrlength, "atr reversal" = atrreversal);


# bn of zz peaks and valleys
def zzchg_bn = if bn == 1 then 0
  else if !isnan(zzhl) then bn
  else zzchg_bn[1];

# price of zz peaks and valleys
def zzchg_price = if bn == 1 then 0
  else if !isnan(zzhl) then round(zzhl,2)
  else zzchg_price[1];

# direction of zz lines
#   =1 then dip, rev going up
#   =0 then peak, rev going down
def zzdir = if bn == 1 then 0
  else if (!isnan(zzhl) and zzchg_price[1] > zzchg_price) then 1
  else if (!isnan(zzhl) and zzchg_price[1] < zzchg_price) then 0
  else zzdir[1];

# -----------------------------------

input avg1_len = 30;
input avg1_type =  AverageType.simple;
def ma1 = MovingAverage(avg1_type, close, avg1_len);
def sma = ma1;

plot maline = ma1;
maline.setdefaultcolor(color.magenta);

# chg so it looks at future bar
def matrend =
  if bn == 1 then 0
  else if ma1 < ma1[-1] then 1
  else if ma1 > ma1[-1] then -1
  else 0;


def marev = if bn == 1 then 0
  else if matrend != matrend[1] then 1
  else 0;

def machg_bn = if bn == 1 then 0
  else if matrend != matrend[1] then bn
  else machg_bn[1];

input ma_trend_change_points = yes;
plot maup = if (ma_trend_change_points and matrend != matrend[1] and matrend == 1) then ma1 else na;
maup.SetPaintingStrategy(PaintingStrategy.POINTS);
#maup.SetPaintingStrategy(PaintingStrategy.BOOLEAN_WEDGE_up);
maup.SetDefaultColor(Color.green);
maup.setlineweight(4);
maup.hidebubble();

plot madwn = if (ma_trend_change_points and matrend != matrend[1] and matrend == -1) then ma1 else na;
madwn.SetPaintingStrategy(PaintingStrategy.POINTS);
#madwn.SetPaintingStrategy(PaintingStrategy.BOOLEAN_WEDGE_DOWN);
madwn.SetDefaultColor(Color.red);
madwn.setlineweight(4);
madwn.hidebubble();

# --------------------------------------

input draw_zz_lines = yes;
plot zz1 = if draw_zz_lines then zzhl else na;
zz1.EnableApproximation();
zz1.setdefaultcolor(color.gray);

# ------------------------------------
# ------------------------------------
# original code .  not used

#counts bars since last Cycle High/Low
#def NewCycle = close[1] == zzoth1;
def newcycle = !isnan(zzhl);

# count bars after zz rev
def BarCount = if NewCycle then 1 else BarCount[1] + 1;

AddLabel (1, "Bars Since last wave: " + BarCount);
#def TrendChange = SMA[1] > SMA[2] and SMA < SMA[1] or SMA[1] < SMA[2] and SMA > SMA[1];
#def DidChange = TrendChange within BarCount bars;
def DidChange = marev;
#AddLabel(1, "DidChange: " + DidChange);
# ------------------------------------
# ------------------------------------

#count count ma revs , and have the total on the zigzag bar
def marevcnt = if !isnan(zzhl[1]) then marev
else if marev then marevcnt[1] + 1
else marevcnt[1];
#then on the bar after the zigzag, reset cnt to 0 , unless a ma rev


input test1_ma_reverse_count = yes;
def en1 = if test1_ma_reverse_count and !isnan(zzhl) then 1 else 0;

addchartbubble(en1, zzhl,
#round(zzhl,2) + " zz\n" +
 "ma cnt\n" +
 marevcnt
, ( if marevcnt > 0 then color.magenta else color.gray)
, (if zzdir then no else yes));


input test2_zzhl_bubbles = no;
addchartbubble(test2_zzhl_bubbles, zzhl,
 zzhl,
 ( if !isnan(zzhl) then color.yellow else color.gray), (if zzdir then no else yes));


input test3_zzdir_bubbles = no;
addchartbubble(test3_zzdir_bubbles, low,
zzchg_price + " zz $\n" +
 zzdir + " zzdir"
, color.yellow, no);

#addlabel(1, bn + " " + machg_bn + " " + BarCount, color.yellow);

input test4_bubbles_bar_cnts = no;
addchartbubble(test4_bubbles_bar_cnts, low*0.996,
 bn + " bn\n" +
 machg_bn + " ma bn\n" +
 (bn - machg_bn) + " ma cnt\n" +
 BarCount + " zz cnt"
, color.yellow, no);
#


test1 = yes , shows the count of ma reversals between zigzag peaks and valleys

RAoY4uo.jpg
Thank you so much, Halcyoguy! I definitely be able to improve my code with this!

Would you happen to know how to get the newest ZigZag wave high to only count if the low of the wave high candle low is completely above the previous wave high candle high?

I've tried, with mediocre results (it doesn't work really). I subtract 1 on a failed wave (low isn't above close of previous high) which works to keep the count I want, but I can't properly define a failed wave.

#def failed wave high/low
def FailedWave = SMA > SMA[1] and zzoth > zzoth[1] and zzoth <= zzoth2 or SMA < SMA[1] and zzoth < zzoth[1] and zzoth >= zzoth2;

Then

def CycleCount = if DidChange then 1 else if !DidChange and FailedWave and zzoth != zzoth[1] then CycleCount[1] - 1 else if NewCycle then CycleCount[1] + 1 else CycleCount[1];

I typed the code in the site here.

Thanks a bunch!

Link to image of chart... Thanks!

Here's the whole script in progress...

Code:
declare upper;

input price = close;
input reversalAmount = 0.3;
input reversalMode = {default price, percent};
input SMA_price = close;
input SMA_length = 50;
input SMA_displace = 0;
input show_wave_bubble = yes;
input bubbleoffset = .001;
input debug = no;

def mode = if  reversalMode ==  reversalMode.price then ZigZagTrendSign(price = price, reversalAmount = reversalAmount) else ZigZagTrendPercent(price = price, reversalAmount = reversalAmount);

def SMA = Average(SMA_price[-SMA_displace], SMA_length);

plot wave =  if  reversalMode ==  reversalMode.price then ZigZagSign(price = price, reversalAmount = reversalAmount, "Show Bubbles" = yes) else ZigZagPercent(price = price, reversalAmount = reversalAmount);

wave.EnableApproximation();
wave.SetDefaultColor(Color.YELLOW);
wave.SetLineWeight(1);

#Prior Bubbles defined--------------------------------------------

def zzoth  = if !IsNaN(wave) then wave else zzoth[1];
def zzoth1 = if zzoth != zzoth[1] then zzoth[1] else zzoth1[1];
def zzoth2 = if zzoth1 != zzoth1[1] then zzoth1[1] else zzoth2[1];
def zzoth3 = if zzoth2 != zzoth2[1] then zzoth2[1] else zzoth3[1];
AddLabel(debug, zzoth + " " + zzoth1 + " " + zzoth2);
AddLabel(debug, "[ ] " + zzoth + " " + zzoth[1] + " " + zzoth[2]);

#counts bars since last Cycle High/LowestWeighted
def NewCycle = close[1] == zzoth[1];
def CycleBarCount = if NewCycle then 1 else CycleBarCount[1] + 1;
AddLabel (debug, "Bars Since last wave: " + CycleBarCount);

#Count bars since last trendBarCount change
def TrendChange = SMA[1] > SMA[2] and SMA < SMA[1] or SMA[1] < SMA[2] and SMA > SMA[1];
def TrendBarCount = if TrendChange then 1 else TrendBarCount[1] + 1;

#Check to see if trend changed
def DidChange = TrendBarCount <= CycleBarCount;
AddLabel (debug, "Bars Since last Trend: " + TrendBarCount);
AddLabel(debug, "DidChange: " + DidChange);

#def failed wave high/low
def FailedWave = SMA > SMA[1] and zzoth > zzoth[1] and zzoth <= zzoth2 or SMA < SMA[1] and zzoth < zzoth[1] and zzoth >= zzoth2; AddLabel(debug, "FailedWave: " + FailedWave + " ");

#trying to fix first cycleBarCount in trend count
#def Conflict = SMA > SMA[1] and zzoth < zzoth1 or SMA < SMA[1] and zzoth > zzoth1;
#AddLabel(debug, "Conflict: " + Conflict + " ");

#def CycleCount = if DidChange then 1 else if FailedWave and NewCycle then CycleCount[1] - 1 else if CycleCount[1] == 1 and Conflict and NewCycle then 1 else if NewCycle then CycleCount[1] + 1 else CycleCount[1];

def CycleCount = if DidChange then 1 else if !DidChange and FailedWave and zzoth != zzoth[1] then CycleCount[1] - 1 else if NewCycle then CycleCount[1] + 1 else CycleCount[1];

addlabel(debug, " CycleCount Count: " + CycleCount);

AddChartBubble(!IsNaN(wave) and show_wave_bubble,
                if zzoth > zzoth[1]
                then high * (1 + bubbleoffset)
                else Double.NaN   ,
                (if wave == close then "" else "") + CycleCount,
                if !IsNaN(wave) > !IsNaN(wave[1]) then Color.GRAY else Color.GRAY,
                if zzoth > zzoth[1] then yes else no);

AddChartBubble(!IsNaN(wave) and show_wave_bubble,
                if zzoth < zzoth[1]
                then low * (1 - bubbleoffset)
                else Double.NaN   ,
                (if wave == close then "" else "") + CycleCount,
                if !IsNaN(wave) > !IsNaN(wave[1]) then Color.GRAY else Color.GRAY,
                if zzoth > zzoth[1] then yes else no);

#wave.HideBubble();

#AddChartBubble(!IsNaN(wave) and show_wave_bubble,
               # if zzoth > zzoth[1]
               # then high * (1 + bubbleoffset)
               # else Double.NaN   ,
               # (if wave == close then "" else "") + CycleCount,
               # if !IsNaN(wave) > !IsNaN(wave[1]) then Color.LIGHT_GRAY else Color.LIGHT_GRAY,
               # if zzoth > zzoth[1] then yes else no);

#AddChartBubble(!IsNaN(wave) and show_wave_bubble,
               # if zzoth < zzoth[1]
               # then low * (1 - bubbleoffset)
               # else Double.NaN   ,
               # (if wave == close then "" else "") + CycleCount,
                #if !IsNaN(wave) > !IsNaN(wave[1]) then Color.LIGHT_GRAY else Color.LIGHT_GRAY,
               # if zzoth > zzoth[1] then yes else no);
 
Last edited:

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
561 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