Skip to content

Monte Carlo Simulation Guide

Overview

The Monte Carlo simulation now includes market-calibrated expected move integration and intelligent strategy execution rules based on actual market conditions.

What Changed

1. Expected Move Calibration

The simulation can now accept the expected move percentage calculated from market straddle prices (from calculator.py) and automatically calibrate the earnings jump distribution.

Before:

earnings_jump_std=0.05,  # Hardcoded 5%

After:

expected_move_pct=8.5,  # Market-implied 8.5% from straddle
# Automatically sets earnings_jump_std=0.085

2. Strategy Execution Rules

The simulation now applies realistic trading rules based on market conditions:

Rule 1: Profit Target Exit (Existing)

  • Exit when P&L reaches 25% of max profit (configurable)
  • Standard profit-taking strategy

Rule 2: Large Move Exit (NEW)

  • When: After earnings announcement
  • Condition: If actual price move > 1.5x expected move
  • Rationale: Outsized moves may indicate market repricing; lock in gains/limit losses
  • Example: With 8.5% expected move, exits if actual move > 12.75%

Rule 3: Stop Loss Exit (NEW)

  • When: After earnings announcement
  • Condition: Position down 75%+ of initial cost
  • Rationale: Cut losses on severely adverse scenarios
  • Example: If initial cost is $2.00, exits if loss > $1.50

Rule 4: Expiration Exit (Default)

  • Hold to front month expiration if no other rule triggers

New Parameters

SimulationParams

@dataclass
class SimulationParams:
    # ... existing parameters ...

    # NEW: Expected move from straddle pricing
    expected_move_pct: float | None = None  # e.g., 8.5 for 8.5%

    # NEW: Strategy execution flags
    use_dynamic_strikes: bool = True  # Adjust strikes based on expected move
    early_exit_on_large_move: bool = True  # Exit if move exceeds 1.5x expected

Parameter Details

expected_move_pct (Optional) - Type: float | None - Example: 8.5 for 8.5% expected move - Source: Get from calculator.py straddle pricing - Effect: Auto-calibrates earnings_jump_std and enables market-aware strategy rules - If None: Uses manually specified earnings_jump_std

use_dynamic_strikes (Default: True) - Type: bool - Purpose: Enable strike adjustment based on expected move - Current implementation: Keeps ATM (reserved for future enhancements) - Future: May adjust to OTM based on expected move magnitude

early_exit_on_large_move (Default: True) - Type: bool - Purpose: Exit positions early when actual move exceeds 1.5x expected move - Threshold: expected_move_pct * 1.5 - Requires: expected_move_pct to be set

How to Get Expected Move

From calculator.py:

uv run python scripts/calculator.py
# Enter ticker: NVDA
# Output includes: Expected Move: 8.5%

From strategy.py directly:

from trade_calc.strategy import compute_recommendation

result = compute_recommendation("NVDA")
expected_move_pct = result["expected_move_pct"]  # e.g., 8.5

Usage Examples

Example 1: Basic Usage with Expected Move

from scripts.monte_carlo import SimulationParams, CalendarSpreadSimulator

# Get expected move from calculator.py for NVDA: 8.5%
params = SimulationParams(
    spot_price=135,
    strike_price=135,
    front_dte=7,
    back_dte=37,
    pre_earnings_iv=0.65,
    post_earnings_iv=0.32,
    back_month_pre_iv=0.45,
    back_month_post_iv=0.38,
    earnings_jump_mean=0.0,
    earnings_jump_std=0.05,  # Will be overridden
    drift_rate=0.0,
    risk_free_rate=0.05,
    num_simulations=5000,
    profit_target=0.25,
    expected_move_pct=8.5,  # From market straddle
)

simulator = CalendarSpreadSimulator(params)
results = simulator.run()
stats = simulator.analyze_results(results)
simulator.plot_results(results, stats)

Example 2: Different Expected Moves for Different Stocks

# High volatility stock (e.g., TSLA)
high_vol_params = SimulationParams(
    spot_price=245,
    strike_price=245,
    # ... other params ...
    expected_move_pct=12.0,  # TSLA typically has larger expected moves
)

# Low volatility stock (e.g., AAPL)
low_vol_params = SimulationParams(
    spot_price=185,
    strike_price=185,
    # ... other params ...
    expected_move_pct=6.5,  # AAPL typically has smaller expected moves
)

Example 3: Disable Strategy Rules (Pure Monte Carlo)

# Traditional Monte Carlo without strategy execution rules
params = SimulationParams(
    # ... other params ...
    expected_move_pct=None,  # Don't use market calibration
    use_dynamic_strikes=False,  # No dynamic adjustments
    early_exit_on_large_move=False,  # No early exits
)

Output Changes

Console Output

Adjusted DTEs to land on Fridays:
  Front: 7 -> 12 days
  Back: 37 -> 40 days

Calibrated earnings jump from expected move:
  Expected move: 8.50%
  Earnings jump std: 0.0500 -> 0.0850
  Dynamic strike selection: ENABLED (based on 8.50% expected move)
  Early exit on large moves: ENABLED (threshold: 12.75%)

Results Include Exit Reasons

Exit Reason Breakdown:
  Profit Target: 54.9%   # Hit profit target
  Expiration: 13.8%      # Held to expiration
  Large Move: 31.3%      # Exited due to large move
  Stop Loss: 0.0%        # Hit stop loss

Visualization Updates

The summary statistics panel now includes: - Expected move percentage (if configured) - Exit reason breakdown - Strategy execution rules applied

Workflow Integration

Complete Workflow:

  1. Find candidate with calculator.py:

    uv run python scripts/calculator.py
    # Enter NVDA
    # Note: Expected Move: 8.5%
    

  2. Create simulation with market data:

    params = SimulationParams(
        spot_price=135,  # From calculator
        strike_price=135,
        # IVs from calculator term structure
        pre_earnings_iv=0.65,
        post_earnings_iv=0.32,
        # Use expected move
        expected_move_pct=8.5,
        # ... other params
    )
    

  3. Run simulation:

    uv run python scripts/run_yearly_analysis.py
    

  4. Analyze results with strategy execution metrics

Benefits

1. Market Calibration

  • Uses actual market expectations instead of guesses
  • More realistic price distributions
  • Better risk assessment

2. Strategy Realism

  • Simulates actual trading decisions
  • Models when you'd actually exit positions
  • Shows distribution of exit reasons

3. Risk Management

  • Automatic stop losses on bad trades
  • Early exits on abnormal moves
  • Reduces tail risk

4. Comparative Analysis

  • Compare different stocks' expected moves
  • See how different volatility profiles affect strategy
  • Optimize entry criteria based on expected move

Important Notes

  1. Expected move represents ~1 standard deviation of the distribution implied by straddle pricing

  2. Earnings jump std is auto-calibrated when expected_move_pct is provided, overriding manual earnings_jump_std

  3. Strategy rules only apply when enabled - you can still run pure Monte Carlo by setting parameters to None/False

  4. Date constraints remain active - Earnings only on weekdays, options expire on Fridays

  5. Multiple trades per year - The yearly simulation chains multiple calendar spreads sequentially

Advanced Usage

Custom Exit Rules

To add your own exit rules, modify the run_single_simulation method in CalendarSpreadSimulator:

# Strategy Rule 4: Your custom rule
if exit_day is None and your_condition:
    exit_day = day
    exit_pnl = pnl
    exit_reason = "custom_rule"
    break

Strike Selection Enhancement

Currently strikes stay ATM. To implement dynamic strike selection:

if p.expected_move_pct is not None and p.use_dynamic_strikes:
    # Example: Go slightly OTM for high expected moves
    if p.expected_move_pct > 10.0:
        strike_price = p.spot_price * 1.02  # 2% OTM call
    else:
        strike_price = p.spot_price  # ATM

Troubleshooting

Q: Simulation shows different results with same parameters A: Monte Carlo uses random sampling. Run more simulations (increase num_simulations) for stable statistics.

Q: Expected move calibration not working A: Ensure expected_move_pct is set as a float (e.g., 8.5, not "8.5%"). Check console output for calibration message.

Q: No large move exits occurring A: Verify early_exit_on_large_move=True and expected_move_pct is set. Check if moves in simulation exceed 1.5x threshold.

Q: All trades hitting stop loss A: Your IVs or expected move may be too high/low. Verify parameters match actual market conditions.