Repaints MTF AGAIG Today's Expected Movement (MMM & IV) Of The SPX For ThinkOrSwim

Repaints

csricksdds

Trader Educator
VIP
TODAY’S EXPECTED MOVEMENT (MMM & IV) OF THE SPX​

FoRlepq.png


TOS doesn’t give expected movement (MMM - MarketMakerMove) for the SPX although it does give the Implied Volatility for the day. The ETF SPY, however, does give MMM as well as IV for the day. Since the SPY and the SPX are closely related, I built and indicator for the SPX which would take data from SPY and convert it to correspond with the SPX. In essence it is a SPX “synthetic” which should closely approximate the trading boundaries for the day. When short trading a spread on SPX 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 SPX 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/!XJEfJZEq
Code:
# ============================================================
#  SPX Synthetic MMM by Ratio — C. Ricks 6/17/26
#  Since GetMarketMakerMove() returns 0 on SPX (no single-
#  company earnings catalyst to trigger excess volatility),
#  this study derives an SPX-equivalent MMM by:
#
#   1. Reading SPY's own live IV-based expected move
#   2. Reading SPY's own live MMM (works fine on SPY)
#   3. Computing the ratio  MMM_ratio = SPY_MMM / SPY_IVmove
#   4. Reading SPX's own live IV-based expected move
#      (imp_volatility() works fine on SPX, unrestricted)
#   5. SPX synthetic MMM = SPX_IVmove * MMM_ratio
#
#  This assumes the SPY MMM/IV relationship (excess vol as a
#  fraction of full expected move) transfers reasonably to
#  SPX since both track the same underlying index, just
#  scaled by share price.
# ============================================================

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 — SPY SIDE (the known/reference side)
# ============================================================
def spyClose   = close(symbol = "SPY");
def spyOpenPx  = open(symbol = "SPY", period = AggregationPeriod.DAY);

# SPY's own blended implied volatility (whole-underlying average)
def spyIVraw   = imp_volatility(symbol = "SPY");
def spyIV      = if spyIVraw < 3 then spyIVraw * 100 else spyIVraw;

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

# SPY IV-based expected move for today's session
def spyIVmove  = spyOpenPx * (spyIV / 100) * timeFrac;

# SPY's real Market Maker Move (this DOES populate on SPY when
# excess volatility is present; otherwise reads 0)
def spyMMMraw  = GetMarketMakerMove(symbol = "SPY");

# Guard against divide-by-zero / no-MMM days
def spyMMMratioLive = if spyIVmove > 0 and spyMMMraw > 0
                       then spyMMMraw / spyIVmove
                       else Double.NaN;

# Lock the ratio at the open as well — MMM itself is computed
# pre-market / at-open by Schwab and shouldn't drift all day
def spyMMMratio;
spyMMMratio = if isFirstBarOfDay then spyMMMratioLive
              else if IsNaN(spyMMMratio[1]) then spyMMMratioLive
              else spyMMMratio[1];

# ============================================================
# SECTION 2 — SPX SIDE (the side we are actually charting)
# ============================================================
def spxOpenPx  = open(symbol = "SPX", period = AggregationPeriod.DAY);
def spxIVraw   = imp_volatility(symbol = "SPX");
# imp_volatility() can return either a decimal (0.152) or a
# percentage (15.2) depending on platform/symbol behavior.
# Normalize: if the raw value is less than 3, assume decimal
# form and convert to percentage; otherwise use as-is.
def spxIV      = if spxIVraw < 3 then spxIVraw * 100 else spxIVraw;

# Live SPX trade price — used both for the live IV move calc
# and as the anchor source at the moment of locking
def spxPriceNow = close(symbol = "SPX");
def spxIVmoveLive = spxPriceNow * (spxIV / 100) * timeFrac;

# Lock IV move at the open — captured once on first bar of day,
# then held firm for the rest of the session
def spxIVmove;
spxIVmove = if isFirstBarOfDay then spxIVmoveLive
            else if IsNaN(spxIVmove[1]) then spxIVmoveLive
            else spxIVmove[1];

# Anchor to the actual SPX trade price at the first regular
# session bar, rather than the daily open() aggregation, which
# can occasionally reflect a pre-market or stale print depending
# on how the daily bar resolves mid-session
def spxOpenLocked;
spxOpenLocked = if isFirstBarOfDay then spxPriceNow
                else if IsNaN(spxOpenLocked[1]) then spxPriceNow
                else spxOpenLocked[1];

# ============================================================
# SECTION 3 — SYNTHETIC SPX MMM
# Apply SPY's measured MMM-to-IV ratio onto SPX's own IV move
# ============================================================
def spxSyntheticMMM = if !IsNaN(spyMMMratio)
                       then spxIVmove * spyMMMratio
                       else Double.NaN;

# Cross-check ratio: how SPX's IV move compares to SPY's IV move
# scaled by price (sanity check — should track close to the
# SPX/SPY price ratio, roughly 10–11x, if both are pricing
# the same underlying volatility consistently)
def priceRatio    = spxOpenPx / spyOpenPx;
def ivMoveRatio   = if spyIVmove > 0 then spxIVmove / spyIVmove else Double.NaN;

# ============================================================
# SECTION 4 — PLOT LEVELS ON SPX CHART
# ============================================================
plot SPX_IV_High  = if showLines and (!showOnlyRTH or inRTH) then spxOpenLocked + spxIVmove else Double.NaN;
plot SPX_IV_Low   = if showLines and (!showOnlyRTH or inRTH) then spxOpenLocked - spxIVmove else Double.NaN;
SPX_IV_High.SetDefaultColor(Color.RED);
SPX_IV_Low.SetDefaultColor(Color.RED);
SPX_IV_High.SetStyle(Curve.SHORT_DASH);
SPX_IV_Low.SetStyle(Curve.SHORT_DASH);
SPX_IV_High.HideBubble();
SPX_IV_Low.HideBubble();

plot SPX_MMM_High = if showLines and (!showOnlyRTH or inRTH) and !IsNaN(spxSyntheticMMM) then spxOpenLocked + spxSyntheticMMM else Double.NaN;
plot SPX_MMM_Low  = if showLines and (!showOnlyRTH or inRTH) and !IsNaN(spxSyntheticMMM) then spxOpenLocked - spxSyntheticMMM else Double.NaN;
SPX_MMM_High.SetDefaultColor(Color.CYAN);
SPX_MMM_Low.SetDefaultColor(Color.CYAN);
SPX_MMM_High.SetStyle(Curve.FIRM);
SPX_MMM_Low.SetStyle(Curve.FIRM);
SPX_MMM_High.HideBubble();
SPX_MMM_Low.HideBubble();

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

AddLabel(showLabels and IsNaN(spxSyntheticMMM),
    "SPX Synth MMM: N/A (SPY has no MMM signal today)",
    Color.GRAY, LabelLocation, LabelSize
);

AddLabel(showLabels,
    "SPX IV Move: +/-" + Round(spxIVmove, 2) +
    "  (" + Round(spxOpenLocked - spxIVmove, 2) + " / " + Round(spxOpenLocked + spxIVmove, 2) + ")",
    Color.RED, LabelLocation, LabelSize
);

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

AddLabel(showDiagnostics,
    "Locked Anchor: " + Round(spxOpenLocked, 2) + "  Live SPX: " + Round(spxPriceNow, 2) +
    "  FirstBar: " + isFirstBarOfDay,
    Color.ORANGE, LabelLocation, LabelSize
);

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

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

# ============================================================
# NOTES
# ============================================================
# - showOnlyRTH (default yes) hides the IV/MMM lines entirely
#   outside regular trading hours (9:30am-4:00pm ET), so they
#   will not paint through pre-market or after-hours bars even
#   if your chart has extended sessions turned on. Set to no
#   if you want the locked lines to keep displaying around the
#   clock once they're set.
# - The first-bar-of-day detector now uses RegularTradingStart()
#   rather than a simple calendar-day check, since calendar-day
#   rollover does not align with the actual 9:30am session open
#   on charts that include extended-hours data.
# - IV move, MMM ratio, and the open price anchor are all
#   captured ONCE on the first bar of each trading day and
#   held firm (CompoundValue) for the rest of the session.
#   The bands will not drift intraday as live IV fluctuates;
#   they reflect conditions exactly as they were at the open.
# - spyMMMraw will read 0 on most ordinary SPY sessions since
#   MMM is built to detect single-name earnings-style excess
#   volatility, which SPY rarely exhibits. On those days the
#   synthetic SPX MMM will show N/A rather than a false number.
# - imp_volatility() returns a blended average across the
#   underlying's options, not the exact ATM single-expiration
#   number the option chain displays for one specific date.
# - The price ratio and IV-move ratio labels are a sanity
#   check: if SPX is genuinely 10-11x SPY's price and both
#   IV moves scale consistently, ivMoveRatio should land
#   close to priceRatio. A large divergence between the two
#   suggests SPX and SPY options are pricing different risk
#   that day (term structure, dividend effects, skew, etc).
 
Last edited by a moderator:

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