# ============================================================ # MIZAN Dual-Tape Pressure (ES + NQ) — thinkScript · v2.0 # mizanquant.com/free · free & open source · a QWERTY Options Inc. tool # # WHAT IT DOES # Measures volatility-adjusted buy and sell volume pressure on the # chart symbol AND a second correlated instrument (default /NQ), on # the chart timeframe AND a higher timeframe, gated by an ADX trend # read. Arrows print only when every reading agrees. A label shows # the current bias. It suggests nothing; it reports. # # INSTALL (thinkorswim desktop) # Charts > Studies > Edit Studies > Create > delete the template, # paste this whole file, name it, OK, Apply. # # v2.0 CHANGES FROM THE ORIGINAL # 1. DMI corrected to the Wilder standard. The original counted both # +DM and -DM on the same bar, which inflates both DIs and softens # the ADX gate. Now only the dominant move counts. # 2. Signal arrows were plotted at price levels (low - 2 / high + 2) # inside a lower pane that is scaled to volume. They now hug the # pressure lines, so they are visible where the study lives. # 3. The "Suggest: Buy a Call / Buy a Put" label is now "Bias: Long / # Bias: Short / Neutral". A free public tool should describe the # tape, not instruct strangers to buy options. # 4. Dead inputs fixed: neutralityThreshold now actually drives the # dominance test (the original hardcoded 1.05 and ignored the # input). spreadLimitMultiplier was never read anywhere; removed. # 5. New input normalizeLegs (default yes): ES and NQ volume live on # different scales, so raw averaging let the louder contract # dominate the combined lines. Each leg now normalizes to its own # baseline before combining. Set to no for the legacy behavior. # 6. New input cvdAnchor: Session (resets each day; the meaningful # intraday read) or Cumulative (legacy running total). # 7. Alerts added for confirmed buy and sell prints. # 8. ADX regime label added so the trend gate is visible. # ============================================================ declare lower; # ----- INPUTS ----- input length = 20; input volLookback = 40; input volatilitySensitivity = 0.5; input higherTimeframe = AggregationPeriod.TEN_MIN; input secondarySymbol = "/NQ"; input neutralityThreshold = 0.05; input normalizeLegs = yes; input cvdAnchor = {default Session, Cumulative}; input scenarioPriority = {default BuyBrightSellBright, BuyBrightSellNormal, BuyNormalSellBright, BuyNormalSellNormal}; # ----- PRIMARY TIMEFRAME (chart symbol) ----- def price = close; def volumeES = volume; def volumeBuy_ES = if price > price[1] then volumeES else 0; def volumeSell_ES = if price < price[1] then volumeES else 0; def currentVol_ES = ExpAverage(TrueRange(high, close, low), length); def baselineVol_ES = ExpAverage(TrueRange(high, close, low), volLookback); def volRatio_ES = currentVol_ES / Max(baselineVol_ES, 0.0001); def volatilityFactor_ES = 1 + (volRatio_ES - 1) * volatilitySensitivity; def avgBuy_ES = ExpAverage(volumeBuy_ES, length); def avgSell_ES = ExpAverage(volumeSell_ES, length); def adjustedBuy_ES = avgBuy_ES * volatilityFactor_ES * (1 + currentVol_ES); def adjustedSell_ES = avgSell_ES * volatilityFactor_ES * (1 + currentVol_ES); def buyMomentum_ES = adjustedBuy_ES - adjustedBuy_ES[1]; def sellMomentum_ES = adjustedSell_ES - adjustedSell_ES[1]; def buyControl_ES_5m = buyMomentum_ES >= 0 and sellMomentum_ES <= 0; def sellControl_ES_5m = sellMomentum_ES >= 0 and buyMomentum_ES <= 0; # ----- HIGHER TIMEFRAME (chart symbol) ----- def htPrice_ES = close(period = higherTimeframe); def htVol_ES = volume(period = higherTimeframe); def htUpVol_ES = if htPrice_ES > htPrice_ES[1] then htVol_ES else 0; def htDownVol_ES = if htPrice_ES < htPrice_ES[1] then htVol_ES else 0; def htCurrentVol_ES = ExpAverage(TrueRange(high(period = higherTimeframe), htPrice_ES, low(period = higherTimeframe)), length); def htAvgVolumeBuy_ES = ExpAverage(htUpVol_ES, length); def htAvgVolumeSell_ES = ExpAverage(htDownVol_ES, length); def htAdjustedBuy_ES = htAvgVolumeBuy_ES * (1 + htCurrentVol_ES); def htAdjustedSell_ES = htAvgVolumeSell_ES * (1 + htCurrentVol_ES); def htBuyMomentum_ES = htAdjustedBuy_ES - htAdjustedBuy_ES[1]; def htSellMomentum_ES = htAdjustedSell_ES - htAdjustedSell_ES[1]; def htBuyControl_ES = htBuyMomentum_ES >= 0 and htSellMomentum_ES <= 0; def htSellControl_ES = htSellMomentum_ES >= 0 and htBuyMomentum_ES <= 0; # ----- PRIMARY TIMEFRAME (secondary symbol) ----- def nqPrice = close(symbol = secondarySymbol); def nqVol = volume(symbol = secondarySymbol); def nqUpVol = if nqPrice > nqPrice[1] then nqVol else 0; def nqDownVol = if nqPrice < nqPrice[1] then nqVol else 0; def nqCurrentVol = ExpAverage(TrueRange(high(symbol = secondarySymbol), nqPrice, low(symbol = secondarySymbol)), length); def nqBaselineVol = ExpAverage(TrueRange(high(symbol = secondarySymbol), nqPrice, low(symbol = secondarySymbol)), volLookback); def nqVolRatio = nqCurrentVol / Max(nqBaselineVol, 0.0001); def nqVolFactor = 1 + (nqVolRatio - 1) * volatilitySensitivity; def nqAvgUpVol = ExpAverage(nqUpVol, length); def nqAvgDownVol = ExpAverage(nqDownVol, length); def nqAdjBuy = nqAvgUpVol * nqVolFactor * (1 + nqCurrentVol); def nqAdjSell = nqAvgDownVol * nqVolFactor * (1 + nqCurrentVol); def nqBuyMomentum = nqAdjBuy - nqAdjBuy[1]; def nqSellMomentum = nqAdjSell - nqAdjSell[1]; def nqBuyControl_5m = nqBuyMomentum >= 0 and nqSellMomentum <= 0; def nqSellControl_5m = nqSellMomentum >= 0 and nqBuyMomentum <= 0; # ----- HIGHER TIMEFRAME (secondary symbol) ----- def htPrice_NQ = close(symbol = secondarySymbol, period = higherTimeframe); def htVol_NQ = volume(symbol = secondarySymbol, period = higherTimeframe); def htUpVol_NQ = if htPrice_NQ > htPrice_NQ[1] then htVol_NQ else 0; def htDownVol_NQ = if htPrice_NQ < htPrice_NQ[1] then htVol_NQ else 0; def htCurrentVol_NQ = ExpAverage(TrueRange(high(symbol = secondarySymbol, period = higherTimeframe), htPrice_NQ, low(symbol = secondarySymbol, period = higherTimeframe)), length); def htVolRatio_NQ = htCurrentVol_NQ / Max(ExpAverage(TrueRange(high(symbol = secondarySymbol, period = higherTimeframe), htPrice_NQ, low(symbol = secondarySymbol, period = higherTimeframe)), volLookback), 0.0001); def htVolFactor_NQ = 1 + (htVolRatio_NQ - 1) * volatilitySensitivity; def htAvgUpVol_NQ = ExpAverage(htUpVol_NQ, length); def htAvgDownVol_NQ = ExpAverage(htDownVol_NQ, length); def htAdjBuy_NQ = htAvgUpVol_NQ * htVolFactor_NQ * (1 + htCurrentVol_NQ); def htAdjSell_NQ = htAvgDownVol_NQ * htVolFactor_NQ * (1 + htCurrentVol_NQ); def htBuyMomentum_NQ = htAdjBuy_NQ - htAdjBuy_NQ[1]; def htSellMomentum_NQ = htAdjSell_NQ - htAdjSell_NQ[1]; def htBuyControl_NQ = htBuyMomentum_NQ >= 0 and htSellMomentum_NQ <= 0; def htSellControl_NQ = htSellMomentum_NQ >= 0 and htBuyMomentum_NQ <= 0; # ----- ADX TREND GATE (Wilder-correct) ----- def DMIlen = 14; def htHigh_ES = high(period = higherTimeframe); def htLow_ES = low(period = higherTimeframe); def htClose_ES = close(period = higherTimeframe); def TRHT = TrueRange(htHigh_ES, htClose_ES, htLow_ES); def upMove = htHigh_ES - htHigh_ES[1]; def downMove = htLow_ES[1] - htLow_ES; def plusDM = if upMove > downMove and upMove > 0 then upMove else 0; def minusDM = if downMove > upMove and downMove > 0 then downMove else 0; def SmPlusDM = WildersAverage(plusDM, DMIlen); def SmMinusDM = WildersAverage(minusDM, DMIlen); def SmTR = WildersAverage(TRHT, DMIlen); def PlusDI = 100 * SmPlusDM / Max(SmTR, 0.0001); def MinusDI = 100 * SmMinusDM / Max(SmTR, 0.0001); def DX = 100 * AbsValue(PlusDI - MinusDI) / Max((PlusDI + MinusDI), 0.0001); def ADX = WildersAverage(DX, DMIlen); def isTrending = ADX > 25; def uptrend = isTrending and PlusDI > MinusDI; def downtrend = isTrending and MinusDI > PlusDI; # ----- LEG NORMALIZATION & COMBINED PRESSURE ----- def nBuy_ES = if normalizeLegs then adjustedBuy_ES / Max(ExpAverage(adjustedBuy_ES, volLookback), 0.0001) else adjustedBuy_ES; def nSell_ES = if normalizeLegs then adjustedSell_ES / Max(ExpAverage(adjustedSell_ES, volLookback), 0.0001) else adjustedSell_ES; def nBuy_htES = if normalizeLegs then htAdjustedBuy_ES / Max(ExpAverage(htAdjustedBuy_ES, volLookback), 0.0001) else htAdjustedBuy_ES; def nSell_htES = if normalizeLegs then htAdjustedSell_ES / Max(ExpAverage(htAdjustedSell_ES, volLookback), 0.0001) else htAdjustedSell_ES; def nBuy_NQ = if normalizeLegs then nqAdjBuy / Max(ExpAverage(nqAdjBuy, volLookback), 0.0001) else nqAdjBuy; def nSell_NQ = if normalizeLegs then nqAdjSell / Max(ExpAverage(nqAdjSell, volLookback), 0.0001) else nqAdjSell; def nBuy_htNQ = if normalizeLegs then htAdjBuy_NQ / Max(ExpAverage(htAdjBuy_NQ, volLookback), 0.0001) else htAdjBuy_NQ; def nSell_htNQ = if normalizeLegs then htAdjSell_NQ / Max(ExpAverage(htAdjSell_NQ, volLookback), 0.0001) else htAdjSell_NQ; def combinedBuy = (nBuy_ES + nBuy_htES + nBuy_NQ + nBuy_htNQ) / 4; def combinedSell = (nSell_ES + nSell_htES + nSell_NQ + nSell_htNQ) / 4; # ----- SIGNAL CONFIRMATION ----- def finalBuySignal = buyControl_ES_5m and htBuyControl_ES and nqBuyControl_5m and htBuyControl_NQ and uptrend; def finalSellSignal = sellControl_ES_5m and htSellControl_ES and nqSellControl_5m and htSellControl_NQ and downtrend; def buyDominant = adjustedBuy_ES > adjustedSell_ES * (1 + neutralityThreshold); def sellDominant = adjustedSell_ES > adjustedBuy_ES * (1 + neutralityThreshold); def confirmedBuy = finalBuySignal and buyDominant; def confirmedSell = finalSellSignal and sellDominant; # ----- PLOTS ----- plot DynamicThresholdBuyLine = combinedBuy; DynamicThresholdBuyLine.SetDefaultColor(Color.GREEN); DynamicThresholdBuyLine.SetLineWeight(2); plot DynamicThresholdSellLine = combinedSell; DynamicThresholdSellLine.SetDefaultColor(Color.RED); DynamicThresholdSellLine.SetLineWeight(2); plot ConfirmedBuySignal = if confirmedBuy then Min(combinedBuy, combinedSell) * 0.97 else Double.NaN; ConfirmedBuySignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP); ConfirmedBuySignal.SetDefaultColor(Color.GREEN); plot ConfirmedSellSignal = if confirmedSell then Max(combinedBuy, combinedSell) * 1.03 else Double.NaN; ConfirmedSellSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN); ConfirmedSellSignal.SetDefaultColor(Color.RED); # ----- SCENARIO CLOUD ----- def buyVolIncreasing = adjustedBuy_ES > adjustedBuy_ES[1]; def sellVolIncreasing = adjustedSell_ES > adjustedSell_ES[1]; def scenario_BB = buyVolIncreasing and sellVolIncreasing; def scenario_BN = buyVolIncreasing and !sellVolIncreasing; def scenario_NB = !buyVolIncreasing and sellVolIncreasing; def scenario_NN = !buyVolIncreasing and !sellVolIncreasing; def scenarioActive = if scenarioPriority == scenarioPriority.BuyBrightSellBright then scenario_BB else if scenarioPriority == scenarioPriority.BuyBrightSellNormal then scenario_BN else if scenarioPriority == scenarioPriority.BuyNormalSellBright then scenario_NB else scenario_NN; def chosenUp = if scenarioActive then combinedBuy else Double.NaN; def chosenDown = if scenarioActive then combinedSell else Double.NaN; AddCloud(chosenUp, chosenDown, Color.GREEN, Color.RED); # ----- INTERSECT MARKER ----- def linesCross = DynamicThresholdBuyLine crosses DynamicThresholdSellLine; def bullishCross = DynamicThresholdBuyLine > DynamicThresholdSellLine; plot IntersectMarker = if linesCross then (combinedBuy + combinedSell) / 2 else Double.NaN; IntersectMarker.SetPaintingStrategy(PaintingStrategy.POINTS); IntersectMarker.AssignValueColor(if bullishCross then Color.GREEN else Color.RED); # ----- CVD ----- def rawDelta = volumeBuy_ES - volumeSell_ES; def newDay = GetDay() <> GetDay()[1]; def sessionCVD = CompoundValue(1, if newDay then rawDelta else sessionCVD[1] + rawDelta, rawDelta); def cvd = if cvdAnchor == cvdAnchor.Session then sessionCVD else TotalSum(rawDelta); # ----- BIAS, LABELS, ALERTS ----- def bias = if confirmedBuy then 1 else if confirmedSell then -1 else 0; AddLabel(yes, "CVD (" + (if cvdAnchor == cvdAnchor.Session then "session" else "cumulative") + "): " + Round(cvd, 0) + " | Buy: " + Round(adjustedBuy_ES, 1) + " | Sell: " + Round(adjustedSell_ES, 1), if bias == 1 then Color.GREEN else if bias == -1 then Color.RED else Color.LIGHT_GRAY); AddLabel(yes, if uptrend then "TREND UP (ADX " + Round(ADX, 0) + ")" else if downtrend then "TREND DOWN (ADX " + Round(ADX, 0) + ")" else "CHOP (ADX " + Round(ADX, 0) + ")", if uptrend then Color.GREEN else if downtrend then Color.RED else Color.LIGHT_GRAY); AddLabel(yes, if bias == 1 then "Bias: Long" else if bias == -1 then "Bias: Short" else "Neutral", if bias == 1 then Color.GREEN else if bias == -1 then Color.RED else Color.LIGHT_GRAY); Alert(confirmedBuy, "Dual-Tape Pressure: confirmed buy print", Alert.BAR, Sound.Ding); Alert(confirmedSell, "Dual-Tape Pressure: confirmed sell print", Alert.BAR, Sound.Ding); # Educational tool. It describes the tape; it does not give advice. # Trading involves substantial risk of loss. mizanquant.com/terms