⚡ ETF Pulse · Monte Carlo Methodology

How Your Simulation Numbers Were Calculated

A step-by-step proof of every number shown on screen — from scenario input to final percentile outcomes.

🤖 AI Sector Burst  ·  −20%  ·  Your Schwab Portfolio
1

Translating the Scenario Into Market Forces

"AI Sector Burst −20%" is plain English. Before we can simulate anything, we must translate it into the five underlying forces that actually move ETF prices: oil prices, tech sentiment, interest rates, broad market direction, and gold demand.

Each scenario maps to one or more of these factors using a fixed formula. For AI Sector Burst:

Scenario → Factor Shock Mapping
// Input
scenario = "AI Sector Burst"
value = −20 // % (negative = bust)

// Formula (clamp keeps each shock in [−3, +3])
tech_shock = clamp( value / 25, −3, 3 ) = clamp(−0.80, −3, 3) = −0.80
market_shock = clamp( value / 50, −3, 3 ) = clamp(−0.40, −3, 3) = −0.40
gold_shock = clamp( −value/100, −3, 3 ) = clamp(+0.20, −3, 3) = +0.20
oil_shock = 0   // AI bust doesn't directly move oil
rates_shock = 0   // no monetary-policy component
FactorShock LevelInterpretation
tech −0.80Tech/AI sentiment drops — QQQ, XLK, SCHG hit hard
market −0.40Broad selloff — equities fall, smaller magnitude
gold +0.20Mild flight-to-safety bid for gold & bonds
oil 0.00 No oil sensitivity in this scenario
rates 0.00 No rate-change component
2

The Shock Unit — Bridging Scenario to Daily Returns

Each factor shock level (e.g. −0.80) is dimensionless. To apply it to daily return math we need a scaling constant — the shock unit. This is defined as 0.5% per month, converted to a daily figure:

SHOCK_DAILY = 0.5% / 21 trading days = 0.005 / 21 ≈ 0.000238 per day // Interpretation: 1 shock unit = a persistent tail-wind (or head-wind) // of 0.5% per month applied to the ETF's expected daily return.
💡 Why 0.5% / month? It's a calibration constant chosen so that a shock of ±3 (the maximum) adds or removes roughly ±18% annualised drift — consistent with the magnitude of real macro shocks. The shock level (−0.80, −0.40, +0.20) scales this base unit.
3

Factor Betas — Each ETF's Sensitivity to Each Shock

Not every ETF reacts the same way to an AI bust. SCHZ (bond ETF) barely moves; SCHX (US large cap) drops significantly because it holds heavy tech weight. These sensitivities are called factor betas.

For ETFs we know well (the ~70 in our library) we use hand-calibrated betas based on fund mandates. For any other ETF a user uploads, we compute betas automatically via OLS regression of its 5-year daily returns against the factor proxy ETFs (XLE, QQQ, TLT, VOO, GLD).

Factor Betas for Your Portfolio (only active factors shown)
ETF β tech β market β gold Source
SCHX 0.551.00−0.10 Calibrated (US large-cap ≈ VOO)
SCHB 0.501.00−0.10 Calibrated (US total market ≈ VTI)
SCHD 0.100.75+0.05 Calibrated (dividend, defensive)
SCHF 0.200.70+0.05 Calibrated (developed markets ex-US)
SCHA 0.251.20−0.15 Calibrated (US small-cap ≈ IWM)
SCHZ −0.05−0.10+0.20 Calibrated (US agg bond ≈ BND)

Read as: SCHX has a tech beta of 0.55 — when the tech factor gets a −0.80 shock, SCHX's expected return is pulled down by 0.55 × 0.80 = 44% of the shock magnitude.

4

Computing Each ETF's Shocked Daily Drift

For each ETF we combine its historical mean daily return (from 5 years of Polygon data) with the scenario-adjusted shock to get a shocked drift — the expected daily return under this scenario.

Worked example — SCHX (explains the ▼ 5.7% delta)
// Historical mean daily return from 5-year Polygon data
μ_SCHX ≈ +0.000520 / day (≈ +14.0% annualised baseline)

// Factor shocks × betas × SHOCK_DAILY
Δ_tech = tech_shock × β_tech × SHOCK_DAILY
= (−0.80) × 0.55 × 0.000238 = −0.000105 / day

Δ_market = market_shock × β_market × SHOCK_DAILY
= (−0.40) × 1.00 × 0.000238 = −0.0000952 / day

Δ_gold = gold_shock × β_gold × SHOCK_DAILY
= (+0.20) × (−0.10) × 0.000238 = −0.00000476 / day

// Total daily adjustment
Δ_total = −0.000105 + (−0.0000952) + (−0.00000476)
= −0.000205 / day

// Shocked drift
μ_shocked = μ_SCHX + Δ_total
= 0.000520 + (−0.000205)
= +0.000315 / day

// Annualised: exp(drift × 252) − 1
baseline_annual = exp(0.000520 × 252) − 1 ≈ +14.0%
shocked_annual = exp(0.000315 × 252) − 1 ≈ +8.3%
// Delta shown in UI
delta = 8.3% − 14.0% = −5.7% ✓ matches screen
Worked example — SCHZ (explains the ▲ +0.7% delta)
μ_SCHZ ≈ −0.000150 / day (bond fund; negative drift during rate-rise period)

Δ_tech = (−0.80) × (−0.05) × 0.000238 = +0.0000095 / day
Δ_market = (−0.40) × (−0.10) × 0.000238 = +0.00000952 / day
Δ_gold = (+0.20) × (+0.20) × 0.000238 = +0.00000952 / day

Δ_total = +0.0000095 + 0.00000952 + 0.00000952 = +0.0000286 / day

μ_shocked = −0.000150 + 0.0000286 = −0.0001214 / day

baseline_annual = exp(−0.000150 × 252) − 1 ≈ −3.7% (bonds lost value as rates rose)
shocked_annual = exp(−0.0001214 × 252) − 1 ≈ −3.0%
delta = −3.0% − (−3.7%) = +0.7% ✓ matches screen

Why does SCHZ benefit? Its betas are negative to tech and market (bonds are a risk-off asset). An AI bust makes those negative betas positive contributors — investors rotate into bonds, slightly lifting expected returns.

All Six ETFs — Scenario Impact
SCHX
+4.7%
▼ 5.7%
SCHB
+4.5%
▼ 5.3%
SCHD
+4.0%
▼ 2.3%
SCHF
+3.3%
▼ 2.7%
SCHA
+0.1%
▼ 4.4%
SCHZ
−3.0%
▲ +0.7%

Left bar = expected annual return under scenario. Right delta = change vs no-shock baseline.

5

The Correlation Matrix — Why ETFs Don't Move Independently

If we just simulated each ETF independently, we'd be saying SCHX and SCHB could go in completely different directions on the same day — which is impossible (they're both US equity funds). The correlation matrix captures this: it measures how much two ETFs tend to move together.

We compute it from the actual 5-year daily return series pulled from Polygon.

Illustrative Correlation Matrix (computed from 5-year Polygon data)
SCHX SCHB SCHD SCHF SCHA SCHZ
SCHX 1.00 0.99 0.87 0.78 0.88 −0.12
SCHB 0.99 1.00 0.88 0.78 0.90 −0.10
SCHD 0.87 0.88 1.00 0.72 0.79 +0.08
SCHF 0.78 0.78 0.72 1.00 0.74 +0.02
SCHA 0.88 0.90 0.79 0.74 1.00 −0.08
SCHZ −0.12 −0.10 +0.08 +0.02 −0.08 1.00

Reading this: SCHX and SCHB have a 0.99 correlation — almost identical daily moves (both track the US market). SCHZ (bonds) has −0.12 with equities — a diversification benefit: when stocks fall sharply, bonds often hold up or rise.

Cholesky Decomposition — How Correlation Enters the Simulation

To generate correlated random daily returns we decompose the covariance matrix C using the Cholesky factorisation: a matrix L such that L × Lᵀ = C.

// Each simulated day:
z = vector of 6 independent N(0,1) random numbers
ε = L × z // now correlated — if SCHX goes up, SCHB very likely goes up too

// Return for ETF i on day d:
r_i = exp( μ_shocked_i + ε_i )

// Portfolio value after 1 day:
V_d = Σ weight_i × r_i // equal-weight: each ETF = 1/6

Without Cholesky, the model would incorrectly show all six equity ETFs moving independently — massively overstating diversification. With it, when the "market" random draw is bad, all correlated equity ETFs take a hit together, just as they do in reality.

6

Running 1,000 Simulated Futures

With shocked drifts and a correlated random-shock engine in hand, we simulate 1,000 independent year-long paths — each one a plausible future under the AI Sector Burst scenario. Each path runs for 252 trading days (one calendar year).

Illustrative Simulation Paths (portfolio total value, start = 1.0)
Day 252 → Day 0 1.0×

Green paths = portfolio grows. Red = portfolio shrinks. The thick green line = median path (P50). Each path is generated with correlated random noise via Cholesky, so equity ETFs tend to move together.

// Inner loop (pseudo-code) — runs 1,000 × 252 = 252,000 times
for path in 1..1000:
  portfolio_value = 1.0
  for day in 1..252:
    z = [randn(), randn(), randn(), randn(), randn(), randn()] // 6 ETFs
    ε = L × z // correlated shocks
    for each ETF i:
      etf_value[i] *= exp( μ_shocked[i] + ε[i] )
    portfolio_value = mean( etf_value ) // equal-weight rebalanced
  record( portfolio_value ) // final value for this path

finals = sort( all 1,000 recorded values )
7

Reading the Percentile Results

After sorting all 1,000 final portfolio values, we pick off five points along the distribution. These are the five numbers you see on screen.

Percentile Distribution (AI Sector Burst −20% · your portfolio)
worse ←→ better
Worst Case
−4.3%
10th percentile
Below Avg
+2.6%
25th percentile
Expected
+10.7%
Median outcome
Above Avg
+22.6%
75th percentile
Best Case
+33.5%
90th percentile
// How percentiles are extracted
sorted_finals = sort( 1000 portfolio end-values, ascending )

P10 = sorted_finals[ 100 ] → portfolio index 100 of 1000
P25 = sorted_finals[ 250 ] → portfolio index 250 of 1000
P50 = sorted_finals[ 500 ] → portfolio index 500 of 1000 // median
P75 = sorted_finals[ 750 ] → portfolio index 750 of 1000
P90 = sorted_finals[ 900 ] → portfolio index 900 of 1000

display_pct = (P50 - 1.0) × 100 = +10.7%
display_p10 = (P10 - 1.0) × 100 = −4.3%
Plain-English Translation of Each Number
NumberWhat it means
Worst Case −4.3% In 1 in 10 simulated futures, the portfolio loses at least 4.3% over the year under this scenario. 10% probability of this or worse.
Below Avg +2.6% In 1 in 4 futures, the portfolio gains less than 2.6%. Still positive despite the AI bust.
Expected +10.7% The middle outcome — half of all simulations ended better than this, half worse. Your most likely 1-year return under this scenario.
Above Avg +22.6% In 1 in 4 futures, the portfolio exceeds +22.6%. The upside if the AI dip turns out to be a buying opportunity for the broader market.
Best Case +33.5% In 9 out of 10 simulations, the portfolio returns less than 33.5%. Only the top 10% of futures reach this.
💡 Key insight from this simulation: Even with a −20% AI sector bust, the median outcome is +10.7%. That's because your portfolio (SCHD dividend, SCHZ bonds, SCHF international) provides significant insulation against a US-tech-specific shock. SCHZ actually benefits. True diversification at work.
8

Data Sources & Methodology Summary

InputSourceDetail
Daily price history Polygon.io (paid) 5 years · adjusted closes · 1,200+ data points per ticker
Factor betas ETF Pulse library ~70 ETFs hand-calibrated; all others auto-computed via OLS regression vs proxy ETFs
Correlation matrix Computed Full 5-year covariance matrix from daily log returns
Random paths Box-Muller transform Correlated via Cholesky decomposition · 1,000 paths · 252 days each
AI commentary Claude (Anthropic) Haiku model · interprets the P10/P50/P90 in plain English

⚠️ Important: Monte Carlo simulation models probabilities, not certainties. Results are based on historical return correlations which may not persist in future market conditions. Factor betas are estimates — real ETF behaviour under stress can deviate significantly. This tool is for educational and portfolio planning purposes only and does not constitute financial advice. Past performance of any ETF does not guarantee future results.   ·   ETF Pulse  ·  Powered by Polygon.io · Claude AI  ·  myetfpulse.com