# ============================================================ # MIZAN presents: Statute — volatility range — thinkScript · v1.0 # mizanquant.com/free · free & open source · a QWERTY Options Inc. tool # # WHAT IT DOES # Measures how much of a normal move price has already spent, on two # clocks at once. Each period (week and month by default) gets an # allowance: the prior period's smoothed true range, centered on the # current period's open. Price's position inside that allowance is # normalized so +100 and -100 are the rails. Beyond the statute, # price is operating outside the law of its own volatility: # over-extended above +100, undervalued below -100. It describes # stretch; it does not advise. # # ORIGINALITY NOTE # Original in-house implementation of a common volatility-range # concept; no code shared with any circulating version. # # TECHNICAL NOTE, FOR THE CURIOUS # The smoothing runs on chart bars over stepped higher-timeframe # values, the standard thinkScript convention for this class of tool, # so the effective weighting leans on the last one to two periods. # That matches the behavior traders already validate on their charts. # # INSTALL (thinkorswim desktop) # Charts > Studies > Edit Studies > Create > delete the template, # paste this whole file, name it, OK, Apply. Intraday or daily charts # below the chosen aggregations. # ============================================================ declare lower; # ----- INPUTS ----- input slowPeriod = AggregationPeriod.MONTH; input fastPeriod = AggregationPeriod.WEEK; input smoothType = AverageType.WILDERS; input smoothLength = 14; input showSlowRange = yes; input showFastRange = yes; input showInnerRails = no; # optional +/-90 early-warning rails input showPeriodLines = no; input periodLineOn = {WEEK, default MONTH}; input showStatusLabel = yes; # ----- SLOW CLOCK (monthly by default) ----- def hiS = high(period = slowPeriod)[1]; def loS = low(period = slowPeriod)[1]; def clS = close(period = slowPeriod)[1]; def opS = open(period = slowPeriod); def atrS = MovingAverage(smoothType, TrueRange(hiS, clS, loS), smoothLength); def spanS = 2 * atrS; def posS = if spanS > 0 then ((close - (opS + atrS)) / spanS) * 100 else 0; plot SlowRange = if showSlowRange then (posS + 50) * 2 else Double.NaN; SlowRange.SetDefaultColor(Color.PINK); SlowRange.SetLineWeight(1); # ----- FAST CLOCK (weekly by default) ----- def hiF = high(period = fastPeriod)[1]; def loF = low(period = fastPeriod)[1]; def clF = close(period = fastPeriod)[1]; def opF = open(period = fastPeriod); def atrF = MovingAverage(smoothType, TrueRange(hiF, clF, loF), smoothLength); def spanF = 2 * atrF; def posF = if spanF > 0 then ((close - (opF + atrF)) / spanF) * 100 else 0; plot FastRange = if showFastRange then (posF + 50) * 2 else Double.NaN; FastRange.SetDefaultColor(Color.CYAN); FastRange.SetLineWeight(1); # ----- THE STATUTE: the rails ----- plot UpperLimit = 100; UpperLimit.SetDefaultColor(Color.RED); UpperLimit.SetLineWeight(1); plot LowerLimit = -100; LowerLimit.SetDefaultColor(Color.GREEN); LowerLimit.SetLineWeight(1); plot UpperInner = if showInnerRails then 90 else Double.NaN; UpperInner.SetDefaultColor(Color.DARK_RED); plot LowerInner = if showInnerRails then -90 else Double.NaN; LowerInner.SetDefaultColor(Color.DARK_GREEN); # breach shading, routed through the plots AddCloud(if FastRange > UpperLimit then FastRange else Double.NaN, UpperLimit, Color.LIGHT_RED, Color.LIGHT_RED); AddCloud(if SlowRange > UpperLimit then SlowRange else Double.NaN, UpperLimit, Color.LIGHT_RED, Color.LIGHT_RED); AddCloud(LowerLimit, if FastRange < LowerLimit then FastRange else Double.NaN, Color.LIGHT_GREEN, Color.LIGHT_GREEN); AddCloud(LowerLimit, if SlowRange < LowerLimit then SlowRange else Double.NaN, Color.DARK_GREEN, Color.DARK_GREEN); AddVerticalLine(showPeriodLines and ((periodLineOn == periodLineOn.WEEK and GetWeek() <> GetWeek()[1]) or (periodLineOn == periodLineOn.MONTH and GetMonth() <> GetMonth()[1])), "", Color.ORANGE, Curve.FIRM); # ----- STATUS ----- def fastVR = (posF + 50) * 2; def slowVR = (posS + 50) * 2; AddLabel(showStatusLabel, "STATUTE | W " + Round(fastVR, 1) + " | M " + Round(slowVR, 1), if fastVR > 100 or slowVR > 100 then Color.RED else if fastVR < -100 or slowVR < -100 then Color.GREEN else Color.LIGHT_GRAY); # ----- ALERTS ----- Alert(fastVR > 100 and fastVR[1] <= 100, "Statute: weekly range over-extended past +100", Alert.BAR, Sound.Bell); Alert(fastVR < -100 and fastVR[1] >= -100, "Statute: weekly range undervalued past -100", Alert.BAR, Sound.Bell); Alert(slowVR > 100 and slowVR[1] <= 100, "Statute: monthly range over-extended past +100", Alert.BAR, Sound.Ding); Alert(slowVR < -100 and slowVR[1] >= -100, "Statute: monthly range undervalued past -100", Alert.BAR, Sound.Ding); # ----- SCAN EXPORTS ----- plot ScanFastVR = fastVR; ScanFastVR.Hide(); plot ScanSlowVR = slowVR; ScanSlowVR.Hide(); plot ScanOverExtended = if fastVR > 100 or slowVR > 100 then 1 else 0; ScanOverExtended.Hide(); plot ScanUndervalued = if fastVR < -100 or slowVR < -100 then 1 else 0; ScanUndervalued.Hide(); # Educational tool. It describes market stretch; it does not give # advice, and no output is a recommendation to buy or sell anything. # Trading involves substantial risk of loss. mizanquant.com/terms