# ============================================================ # MIZAN presents: Redirect — RSI console — thinkScript · v2.0 # mizanquant.com/free · free & open source · a QWERTY Options Inc. tool # # WHAT IT DOES # One RSI, questioned properly. Base signals print when RSI crosses # back through its rails, either the strict way (a real visit below # or above first) or the momentum way (a fast snap back). Then the # follow-up: after a base signal, Redirect watches for the pullback # that holds, the higher low in RSI after a buy signal or the lower # high after a sell, and prints the entry arrow. Optional adaptive # rails set the overbought and oversold lines from the RSI's own # recent range instead of fixed 70/30, with a range-confirmation mode # that only signals when fixed and adaptive agree within a few bars. # It describes momentum; it does not advise. # # v2.0 CHANGES FROM THE OWNER'S ORIGINAL # 1. CONSOLIDATED: the original ran three identical RSI blocks with # identical defaults, three overlapping lines rendering as one, # and roughly 45 inputs. This is one engine with every mechanism # the three blocks had, 19 inputs, one line. Want two lengths? # Load it twice; that is what studies are for. # 2. CORRECTNESS: the bars-since counters started at zero on bar one, # so entry arrows could fire off startup noise before any real # base signal existed. Seeded properly; entries now require a # genuine signal first. # 3. All recursion declared with def + CompoundValue (house rule; the # rec keyword is banned after the AddCloud incident). # 4. Alerts distinguish base from entry by sound and say which is # which. Status label reads the RSI, the active rail mode, and # the live rail values. # 5. Hidden scan exports for base and entry signals both directions. # 6. Full battery passed: zero case collisions, zero unused inputs, # zero escapes, zero recs, no builtin shadowing. # # INSTALL (thinkorswim desktop) # Charts > Studies > Edit Studies > Create > delete the template, # paste this whole file, name it, OK, Apply. # ============================================================ declare lower; # ----- INPUTS ----- input rsiLength = 14; input priceSource = close; input avgType = AverageType.WILDERS; input fixedOverbought = 70; input fixedOversold = 30; input useAdaptiveRails = no; # rails from RSI's own recent range input adaptiveLength = 150; input adaptivePercentile = 0.82; input requireRangeConfirm = no; # fixed and adaptive must agree input confirmWithinBars = 2; input signalMode = {default STRICT_PLUS_MOMENTUM, SIMPLE_CROSS}; input momentumThresh = 1.5; input strictLookback = 2; input showEntries = yes; input fastEntries = yes; input entryMaxWait = 10; input entryMinRebound = 0.4; input entryPivotLen = 3; input showStatusLabel = yes; # ----- THE RSI ----- def netChg = MovingAverage(avgType, priceSource - priceSource[1], rsiLength); def totChg = MovingAverage(avgType, AbsValue(priceSource - priceSource[1]), rsiLength); def chgRatio = if totChg != 0 then netChg / totChg else 0; def rsiVal = 50 * (chgRatio + 1); plot RSI = rsiVal; RSI.SetDefaultColor(Color.WHITE); RSI.SetLineWeight(1); # ----- THE RAILS ----- plot FixedOB = fixedOverbought; FixedOB.SetDefaultColor(Color.LIGHT_GRAY); plot FixedOS = fixedOversold; FixedOS.SetDefaultColor(Color.LIGHT_GRAY); def rHi = Highest(rsiVal, adaptiveLength); def rLo = Lowest(rsiVal, adaptiveLength); def rSpan = Max(0.000001, rHi - rLo); def adaptOB = rHi - (1 - adaptivePercentile) * rSpan; def adaptOS = rLo + (1 - adaptivePercentile) * rSpan; plot AdaptiveOB = if useAdaptiveRails or requireRangeConfirm then adaptOB else Double.NaN; AdaptiveOB.SetDefaultColor(Color.CYAN); AdaptiveOB.SetStyle(Curve.MEDIUM_DASH); plot AdaptiveOS = if useAdaptiveRails or requireRangeConfirm then adaptOS else Double.NaN; AdaptiveOS.SetDefaultColor(Color.CYAN); AdaptiveOS.SetStyle(Curve.MEDIUM_DASH); def obRail = if useAdaptiveRails then adaptOB else fixedOverbought; def osRail = if useAdaptiveRails then adaptOS else fixedOversold; # ----- BASE SIGNALS: the examination ----- def simpleBuy = rsiVal crosses above osRail; def simpleSell = rsiVal crosses below obRail; def strictBuy = simpleBuy and Lowest(rsiVal[1], strictLookback) < osRail; def strictSell = simpleSell and Highest(rsiVal[1], strictLookback) > obRail; def momoBuy = rsiVal > osRail and rsiVal[1] <= osRail and (rsiVal - rsiVal[2]) > momentumThresh; def momoSell = rsiVal < obRail and rsiVal[1] >= obRail and (rsiVal[2] - rsiVal) > momentumThresh; def rawBuy = if signalMode == signalMode.SIMPLE_CROSS then simpleBuy else (strictBuy or momoBuy); def rawSell = if signalMode == signalMode.SIMPLE_CROSS then simpleSell else (strictSell or momoSell); # range-confirmation: fixed and adaptive must agree within a few bars def fixedBuyX = rsiVal crosses above fixedOversold; def fixedSellX = rsiVal crosses below fixedOverbought; def adaptBuyX = rsiVal crosses above adaptOS; def adaptSellX = rsiVal crosses below adaptOB; def sinceFixedBuy = CompoundValue(1, if fixedBuyX then 0 else if sinceFixedBuy[1] >= 0 then sinceFixedBuy[1] + 1 else -1, -1); def sinceFixedSell = CompoundValue(1, if fixedSellX then 0 else if sinceFixedSell[1] >= 0 then sinceFixedSell[1] + 1 else -1, -1); def sinceAdaptBuy = CompoundValue(1, if adaptBuyX then 0 else if sinceAdaptBuy[1] >= 0 then sinceAdaptBuy[1] + 1 else -1, -1); def sinceAdaptSell = CompoundValue(1, if adaptSellX then 0 else if sinceAdaptSell[1] >= 0 then sinceAdaptSell[1] + 1 else -1, -1); def confirmedBuy = (fixedBuyX and sinceAdaptBuy >= 0 and sinceAdaptBuy <= confirmWithinBars) or (adaptBuyX and sinceFixedBuy >= 0 and sinceFixedBuy <= confirmWithinBars); def confirmedSell = (fixedSellX and sinceAdaptSell >= 0 and sinceAdaptSell <= confirmWithinBars) or (adaptSellX and sinceFixedSell >= 0 and sinceFixedSell <= confirmWithinBars); def buySignal = if requireRangeConfirm then confirmedBuy else rawBuy; def sellSignal = if requireRangeConfirm then confirmedSell else rawSell; plot BuyArrow = if buySignal then rsiVal else Double.NaN; BuyArrow.SetPaintingStrategy(PaintingStrategy.ARROW_UP); BuyArrow.SetDefaultColor(Color.GREEN); BuyArrow.SetLineWeight(3); plot SellArrow = if sellSignal then rsiVal else Double.NaN; SellArrow.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN); SellArrow.SetDefaultColor(Color.RED); SellArrow.SetLineWeight(3); # ----- THE FOLLOW-UP: redirect entries, properly seeded ----- def sinceBuy = CompoundValue(1, if buySignal then 0 else if sinceBuy[1] >= 0 then sinceBuy[1] + 1 else -1, -1); def sinceSell = CompoundValue(1, if sellSignal then 0 else if sinceSell[1] >= 0 then sinceSell[1] + 1 else -1, -1); def refLowAfterBuy = CompoundValue(1, if buySignal then Lowest(rsiVal[1], 5) else refLowAfterBuy[1], Double.NaN); def refHighAfterSell = CompoundValue(1, if sellSignal then Highest(rsiVal[1], 5) else refHighAfterSell[1], Double.NaN); def pivotLo = rsiVal[1] == Lowest(rsiVal[1], entryPivotLen); def pivotHi = rsiVal[1] == Highest(rsiVal[1], entryPivotLen); def buyEntryStrict = showEntries and sinceBuy > 0 and sinceBuy <= entryMaxWait and pivotLo and rsiVal > rsiVal[1] and rsiVal[1] > refLowAfterBuy; def sellEntryStrict = showEntries and sinceSell > 0 and sinceSell <= entryMaxWait and pivotHi and rsiVal < rsiVal[1] and rsiVal[1] < refHighAfterSell; def buyEntryFast = showEntries and sinceBuy > 0 and sinceBuy <= entryMaxWait and rsiVal[2] > rsiVal[1] and rsiVal > rsiVal[1] and (rsiVal - rsiVal[1]) >= entryMinRebound and rsiVal[1] > refLowAfterBuy; def sellEntryFast = showEntries and sinceSell > 0 and sinceSell <= entryMaxWait and rsiVal[2] < rsiVal[1] and rsiVal < rsiVal[1] and (rsiVal[1] - rsiVal) >= entryMinRebound and rsiVal[1] < refHighAfterSell; def entryBuyGo = if fastEntries then buyEntryFast else buyEntryStrict; def entrySellGo = if fastEntries then sellEntryFast else sellEntryStrict; plot BuyEntry = if entryBuyGo then rsiVal else Double.NaN; BuyEntry.SetPaintingStrategy(PaintingStrategy.ARROW_UP); BuyEntry.SetDefaultColor(Color.CYAN); BuyEntry.SetLineWeight(2); plot SellEntry = if entrySellGo then rsiVal else Double.NaN; SellEntry.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN); SellEntry.SetDefaultColor(Color.ORANGE); SellEntry.SetLineWeight(2); # ----- STATUS ----- AddLabel(showStatusLabel, "REDIRECT | RSI " + Round(rsiVal, 1) + " | rails " + (if useAdaptiveRails then "adaptive " + Round(osRail, 0) + "/" + Round(obRail, 0) else "fixed " + Round(fixedOversold, 0) + "/" + Round(fixedOverbought, 0)) + (if requireRangeConfirm then " | confirm on" else ""), if rsiVal > obRail then Color.RED else if rsiVal < osRail then Color.GREEN else Color.LIGHT_GRAY); # ----- ALERTS ----- Alert(buySignal, "Redirect: base buy signal, RSI reclaimed the oversold rail", Alert.BAR, Sound.Ding); Alert(sellSignal, "Redirect: base sell signal, RSI lost the overbought rail", Alert.BAR, Sound.Ding); Alert(entryBuyGo, "Redirect: follow-up buy entry, pullback held", Alert.BAR, Sound.Bell); Alert(entrySellGo, "Redirect: follow-up sell entry, bounce failed", Alert.BAR, Sound.Bell); # ----- SCAN EXPORTS ----- plot ScanBuySignal = if buySignal then 1 else 0; ScanBuySignal.Hide(); plot ScanSellSignal = if sellSignal then 1 else 0; ScanSellSignal.Hide(); plot ScanBuyEntry = if entryBuyGo then 1 else 0; ScanBuyEntry.Hide(); plot ScanSellEntry = if entrySellGo then 1 else 0; ScanSellEntry.Hide(); # Educational tool. It describes momentum conditions; 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