AGAIG Today's Expected Movement (MMM & IV) of NDX for ThinkOrSwim

csricksdds

Trader Educator
VIP
This is similar to the Synthetic Indicator for SPX I posted a few days ago.

TOS doesn’t give expected movement (MMM - MarketMakerMove) for the NDX although it does give the Implied Volatility for the day. The ETF NDX, however, does give MMM as well as IV for the day. Since the NDX and the QQQ are closely related, I built and indicator for the NDX which would take data from QQQ and convert it to correspond with the NDX. In essence it is a NDX “synthetic” which should closely approximate the trading boundaries for the day. When short trading a spread on NDX I would go a few points outside the boundaries to place a trade, or, I would direction trade using my Stop and Reverse Indicator, or my AGAIG AI Intraday Optimizer Indicator.

The boundaries are set a few minutes after open. The MMM is shown as a solid CYAN line and the IV Boundaries as a dashed red line. The do not paint pre-market or after market and are setting probable boundaries for day trading purposes only. This particular indicator is established for the NDX only. Also included is a solid yellow VWAP and dashed lines representing 1SD and 2SD.

This represents a little more trading information for the SPX than is available on the TOS platform?

There are several data labels with this indicator and I usually say no on the “show diagnostics” in Added studies and strategies where you can also change and position the Label Size.

Indicator Link: http://tos.mx/!jStOMErP

Code:
Code:
# ============================================================
# NDX Synthetic MMM by Ratio — C. Ricks 7/2/26
# Since GetMarketMakerMove() returns 0 on NDX (no single-
# company earnings catalyst to trigger excess volatility),
# this study derives an NDX-equivalent MMM by:
#
# 1. Reading QQQ's own live IV-based expected move
# 2. Reading QQQ's own live MMM (works fine on QQQ)
# 3. Computing the ratio MMM_ratio = QQQ_MMM / QQQ_IVmove
# 4. Reading NDX's own live IV-based expected move
# (imp_volatility() works fine on NDX, unrestricted)
# 5. NDX synthetic MMM = NDX_IVmove * MMM_ratio
#
# This assumes the QQQ MMM/IV relationship (excess vol as a
# fraction of full expected move) transfers reasonably to
# NDX since both track the same underlying index, just
# scaled by share price. NDX/QQQ ratio is roughly 40x.
# ============================================================

input tradingDaysPerYear = 252;
input showLabels = yes;
input showDiagnostics = no;
input showLines = yes;
input showOnlyRTH = yes;
input LabelSize = fontsize.medium;
input LabelLocation = location.Bottom_left;

# Detect the first bar of the REGULAR trading session each day,
# using the standard community pattern: a date rollover combined
# with crossing from before-open to inside-open. This is more
# robust through backfill/history than relying on RTH start/end
# alone, since it explicitly anchors to the YYYYMMDD date change.
def todayDate = GetYYYYMMDD();
def isRollover = todayDate != todayDate[1];
def rthStart = RegularTradingStart(todayDate);
def rthEnd = RegularTradingEnd(todayDate);
def beforeRTH = GetTime() < rthStart;
def afterRTH = GetTime() > rthEnd;
def inRTH = !beforeRTH and !afterRTH;

def isFirstBarOfDay = (beforeRTH[1] and !beforeRTH) or (isRollover and !beforeRTH);

# ============================================================
# SECTION 1 — QQQ SIDE (reference ETF for MMM/IV ratio)
# ============================================================
def qqqClose = close(symbol = "QQQ");
def qqqOpenPx = open(symbol = "QQQ", period = AggregationPeriod.DAY);

# QQQ blended implied volatility
def qqqIVraw = imp_volatility(symbol = "QQQ");
def qqqIV = if qqqIVraw < 3 then qqqIVraw * 100 else qqqIVraw;

# Time fraction for a 0DTE session — full day used at open
def timeFrac = Sqrt(1) / Sqrt(tradingDaysPerYear);

# QQQ IV-based expected move for today's session
def qqqIVmove = qqqOpenPx * (qqqIV / 100) * timeFrac;

# QQQ Market Maker Move — populates when excess volatility
# is detected; returns 0 on ordinary sessions
def qqqMMMraw = GetMarketMakerMove(symbol = "QQQ");

# Guard against divide-by-zero / no-MMM days
def qqqMMMratioLive = if qqqIVmove > 0 and qqqMMMraw > 0
then qqqMMMraw / qqqIVmove
else Double.NaN;

# Lock the ratio at the open — captured once and held all day
def qqqMMMratio;
qqqMMMratio = if isFirstBarOfDay then qqqMMMratioLive
else if IsNaN(qqqMMMratio[1]) then qqqMMMratioLive
else qqqMMMratio[1];

# ============================================================
# SECTION 2 — NDX SIDE (the index we are charting)
# ============================================================
def ndxOpenPx = open(symbol = "NDX", period = AggregationPeriod.DAY);
def ndxIVraw = imp_volatility(symbol = "NDX");
# imp_volatility() can return a decimal (0.152) or percentage
# (15.2) depending on symbol. Normalize to percentage form.
def ndxIV = if ndxIVraw < 3 then ndxIVraw * 100 else ndxIVraw;

# Live NDX price — anchor source at the moment of locking
def ndxPriceNow = close(symbol = "NDX");
def ndxIVmoveLive = ndxPriceNow * (ndxIV / 100) * timeFrac;

# Lock IV move at the open — held firm for the session
def ndxIVmove;
ndxIVmove = if isFirstBarOfDay then ndxIVmoveLive
else if IsNaN(ndxIVmove[1]) then ndxIVmoveLive
else ndxIVmove[1];

# Lock the open price anchor at the first RTH bar
def ndxOpenLocked;
ndxOpenLocked = if isFirstBarOfDay then ndxPriceNow
else if IsNaN(ndxOpenLocked[1]) then ndxPriceNow
else ndxOpenLocked[1];

# ============================================================
# SECTION 3 — SYNTHETIC NDX MMM
# Apply QQQ's MMM/IV ratio onto NDX's own IV move
# ============================================================
def ndxSyntheticMMM = if !IsNaN(qqqMMMratio)
then ndxIVmove * qqqMMMratio
else Double.NaN;

# Sanity check: NDX/QQQ price ratio vs IV-move ratio
# should track close to ~40x if both price the same risk
def priceRatio = ndxOpenPx / qqqOpenPx;
def ivMoveRatio = if qqqIVmove > 0 then ndxIVmove / qqqIVmove else Double.NaN;

# ============================================================
# SECTION 4 — PLOT LEVELS ON NDX CHART
# ============================================================
plot NDX_IV_High = if showLines and (!showOnlyRTH or inRTH) then ndxOpenLocked + ndxIVmove else Double.NaN;
plot NDX_IV_Low = if showLines and (!showOnlyRTH or inRTH) then ndxOpenLocked - ndxIVmove else Double.NaN;
NDX_IV_High.SetDefaultColor(Color.RED);
NDX_IV_Low.SetDefaultColor(Color.RED);
NDX_IV_High.SetStyle(Curve.SHORT_DASH);
NDX_IV_Low.SetStyle(Curve.SHORT_DASH);
NDX_IV_High.HideBubble();
NDX_IV_Low.HideBubble();

plot NDX_MMM_High = if showLines and (!showOnlyRTH or inRTH) and !IsNaN(ndxSyntheticMMM) then ndxOpenLocked + ndxSyntheticMMM else Double.NaN;
plot NDX_MMM_Low = if showLines and (!showOnlyRTH or inRTH) and !IsNaN(ndxSyntheticMMM) then ndxOpenLocked - ndxSyntheticMMM else Double.NaN;
NDX_MMM_High.SetDefaultColor(Color.CYAN);
NDX_MMM_Low.SetDefaultColor(Color.CYAN);
NDX_MMM_High.SetStyle(Curve.FIRM);
NDX_MMM_Low.SetStyle(Curve.FIRM);
NDX_MMM_High.HideBubble();
NDX_MMM_Low.HideBubble();

# ============================================================
# SECTION 5 — LABELS
# ============================================================
AddLabel(showLabels and !IsNaN(ndxSyntheticMMM),
"NDX Synth MMM: +/-" + Round(ndxSyntheticMMM, 2) +
" (" + Round(ndxOpenLocked - ndxSyntheticMMM, 2) + " / " + Round(ndxOpenLocked + ndxSyntheticMMM, 2) + ")",
Color.CYAN, LabelLocation, LabelSize
);

AddLabel(showLabels and IsNaN(ndxSyntheticMMM),
"NDX Synth MMM: N/A (no MMM signal today)",
Color.GRAY, LabelLocation, LabelSize
);

AddLabel(showLabels,
"NDX IV Move: +/-" + Round(ndxIVmove, 2) +
" (" + Round(ndxOpenLocked - ndxIVmove, 2) + " / " + Round(ndxOpenLocked + ndxIVmove, 2) + ")",
Color.RED, LabelLocation, LabelSize
);

# ── Diagnostic labels — off by default, toggle showDiagnostics to debug ──
AddLabel(showDiagnostics,
"NDX Open: " + Round(ndxOpenPx, 2) + " IV raw: " + Round(ndxIVraw, 4) + " IV used: " + Round(ndxIV, 1) + "%",
Color.GRAY, LabelLocation, LabelSize
);

AddLabel(showDiagnostics,
"Locked Anchor: " + Round(ndxOpenLocked, 2) + " Live NDX: " + Round(ndxPriceNow, 2) +
" FirstBar: " + isFirstBarOfDay,
Color.ORANGE, LabelLocation, LabelSize
);

AddLabel(showDiagnostics,
"QQQ MMM/IV Ratio: " + (if !IsNaN(qqqMMMratio) then AsPercent(qqqMMMratio) else "N/A"),
Color.YELLOW, LabelLocation, LabelSize
);

AddLabel(showDiagnostics,
"Price Ratio NDX/QQQ: " + Round(priceRatio, 2) +
"x IV-Move Ratio: " + Round(ivMoveRatio, 2) + "x",
Color.WHITE, LabelLocation, LabelSize
);

# ============================================================
# NOTES
# ============================================================
# - showOnlyRTH (default yes) hides lines outside 9:30-4:00 ET
# so they do not paint through pre-market or after-hours bars.
# - isFirstBarOfDay uses RegularTradingStart() for robust RTH
# detection on charts that include extended-hours data.
# - IV move, MMM ratio, and open price anchor are captured ONCE
# at the open and held firm all session via self-referencing
# def pattern — no CompoundValue to avoid stale-seed issues.
# - qqqMMMraw returns 0 on ordinary sessions; synthetic MMM
# shows N/A rather than a false number on those days.
# - imp_volatility() returns a blended average across all
# options, not the exact ATM single-expiration IV shown in
# the option chain for one specific expiry date.
# - Price ratio and IV-move ratio diagnostics should track
# close to ~40x; large divergence suggests NDX and QQQ
# options are pricing different risk that day.

#End Code
 
Last edited by a moderator:
"A picture is worth a thousand words" Would it be possoble to post a picture of your hard work? What timeframe works best?
 

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