Page 1 of 1

Simulation Monte Carlo on corelation gold/silver price

Posted: Mon Aug 03, 2026 5:13 pm
by PTScalper
Hi scalpers,

i prepared for you simulation of corelation on gold/silver price scalping.

Its written in Python.

Code: Select all

 import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import qmc, norm

def generate_data(iterations: int = 2_000_000):
    """Zkrácená verze QMC simulace z předchozího kroku pro získání dat."""
    S0_gold, S0_silver = 2350.0, 30.0
    mu_gold, vol_gold = 0.05, 0.15
    mu_silver, vol_silver = 0.07, 0.28
    correlation, T = 0.82, 1.0

    corr_matrix = np.array([[1.0, correlation], [correlation, 1.0]])
    L = np.linalg.cholesky(corr_matrix)

    sampler = qmc.Sobol(d=2, scramble=True)
    uniform_samples = sampler.random(n=iterations)
    z_independent = norm.ppf(uniform_samples)
    
    z_correlated = (L @ z_independent.T).T
    x_gold, x_silver = z_correlated[:, 0], z_correlated[:, 1]

    prices_gold = S0_gold * np.exp((mu_gold - 0.5 * vol_gold**2) * T + vol_gold * np.sqrt(T) * x_gold)
    prices_silver = S0_silver * np.exp((mu_silver - 0.5 * vol_silver**2) * T + vol_silver * np.sqrt(T) * x_silver)
    
    return prices_gold, prices_silver

# 1. Vygenerování dat
prices_gold, prices_silver = generate_data(2_000_000)

# 2. Nastavení vizuálního stylu Seaborn
sns.set_theme(style="darkgrid")
plt.figure(figsize=(10, 8))

# 3. Vykreslení 2D histogramu
# Používáme bins=150 pro detailní rozlišení. 
# cmap="inferno" nebo "magma" poskytuje skvělý kontrast pro teplotní mapy (od tmavé po světlou)
sns.histplot(
    x=prices_gold, 
    y=prices_silver, 
    bins=150, 
    cmap="magma", 
    cbar=True,
    cbar_kws={'label': 'Hustota výskytu (počet iterací v sektoru)'}
)

# 4. Přidání metadat grafu
plt.title("QMC Simulace: Korelace ceny Zlata a Stříbra (2 000 000 iterací)", fontsize=14, pad=15)
plt.xlabel("Cena Zlata (USD)", fontsize=12)
plt.ylabel("Cena Stříbra (USD)", fontsize=12)

# Vykreslení přímky lineární regrese (volitelné, ukazuje směr korelace)
# Kreslíme ji tenkou, aby nepřekryla samotný histogram
sns.regplot(
    x=prices_gold, 
    y=prices_silver, 
    scatter=False, 
    color="cyan", 
    line_kws={"linewidth": 1, "linestyle": "--"}
)

plt.tight_layout()
plt.show()
Please let me know, what do you think :-)

Re: Simulation Monte Carlo on corelation gold/silver price

Posted: Fri Sep 11, 2026 7:36 pm
by LondonScalper
PTScalper wrote:i prepared for you simulation of corelation on gold/silver price scalping. Its written in Python.
Nice to see someone putting correlation in code instead of a hand-wavy “they move together” slide.

One caution from the desk: a constant ρ≈0.82 over a year-long GBM path is a regime average, not what you trade on M1. Gold/silver correlation compresses and breaks around US data, London cash open, and when one metal is driven by a single headline. Monte Carlo with fixed corr will understate the days when the pair trade stops being a pair.

Practical checks I’d add before trusting the heatmaps:
  • Rolling correlation (e.g. 30–90 minute windows) as well as full-sample
  • Stress a corr shock day (ρ collapses toward 0 or goes negative briefly)
  • Separate London vs Asia vol — silver often misbehaves more in thin hours
Sobol QMC is fine for sampling; just don’t confuse path elegance with executable edge. Correlation is a risk overlay for me, not an entry signal.

Are you using the sim to size a gold/silver spread book, or to decide when not to double up long both?

Re: Simulation Monte Carlo on corelation gold/silver price

Posted: Sun Sep 27, 2026 10:42 am
by PropScalpDesk
Two practical things in the code, then one about the model.

sns.regplot with its default settings bootstraps a 95% confidence band (n_boot=1000) even when scatter=False. On two million points, that's where most of the runtime goes. Passing ci=None draws the same dashed line in a fraction of the time. And scipy's Sobol sampler warns when n isn't a power of two, because the balance properties only hold at 2^m. sampler.random_base2(m=21) gives you 2,097,152 points with no warning.

On the model: every draw is a terminal price one year out (T=1.0), so the heatmap mostly hands back the 0.82 you put in, spread by the two annual vols. For scalping, the interesting question is what the correlation of one-minute returns does around US data or the London open, and a single fixed ρ can't show that. If you want to keep it Monte Carlo, simulate minute steps with two correlation regimes (say 0.85 normal and 0.3 on event days) and look at how often a gold/silver hedge actually loses on both legs.

Re: Simulation Monte Carlo on corelation gold/silver price

Posted: Sun Sep 27, 2026 3:14 pm
by PTScalper
PropScalpDesk wrote: Sun Sep 27, 2026 10:42 am Two practical things in the code, then one about the model.

sns.regplot with its default settings bootstraps a 95% confidence band (n_boot=1000) even when scatter=False. On two million points, that's where most of the runtime goes. Passing ci=None draws the same dashed line in a fraction of the time. And scipy's Sobol sampler warns when n isn't a power of two, because the balance properties only hold at 2^m. sampler.random_base2(m=21) gives you 2,097,152 points with no warning.

On the model: every draw is a terminal price one year out (T=1.0), so the heatmap mostly hands back the 0.82 you put in, spread by the two annual vols. For scalping, the interesting question is what the correlation of one-minute returns does around US data or the London open, and a single fixed ρ can't show that. If you want to keep it Monte Carlo, simulate minute steps with two correlation regimes (say 0.85 normal and 0.3 on event days) and look at how often a gold/silver hedge actually loses on both legs.
Hi PropScalpDesk,

You are spot on across the board. The seaborn ci parameter and the QMC power-of-two constraints are two of the most common performance traps in quantitative Python, and your critique of the model's timeframe hits exactly why standard terminal Monte Carlo fails for high-frequency trading.

A 1-year terminal jump completely masks the microstructure risks of pair trading. In scalping, the danger isn't that gold and silver drift apart over a year; it's that during a liquidity vacuum (like US CPI data or the London open), correlation breaks down, bid-ask spreads widen, and a supposedly market-neutral book bleeds out on both sides simultaneously.

Re: Simulation Monte Carlo on corelation gold/silver price

Posted: Sun Sep 27, 2026 3:15 pm
by PTScalper
Here is an optimized, high-frequency Euler-Maruyama simulation that addresses all three points. It steps through a 60-minute window around a major data release, drops the correlation from 0.85 to 0.30 during the event, utilizes random_base2 for perfect Sobol balancing, and strips the bootstrapping from the regression plot.

Code: Select all

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import qmc, norm

def simulate_event_scalp(m=16, steps=60, event_start=30, event_duration=10):
    """
    Simulates a 60-minute window around an economic data release using QMC.
    m=16 generates 2^16 (65,536) paths. Increase to m=21 for 2,097,152 paths.
    """
    n_paths = 2**m
    dt = 1 / (252 * 1440) # 1-minute steps
    
    # Asset parameters (Annualized vols)
    vol_g, vol_s = 0.15, 0.25 
    
    # 1. Optimal QMC Sampling (Fixing the Sobol power-of-two warning)
    # We need 2 dimensions per time step (one for Gold, one for uncorrellated Silver component)
    sampler = qmc.Sobol(d=steps * 2, scramble=True)
    sobol_draws = sampler.random_base2(m=m)
    
    # Convert uniform Sobol draws to standard normals
    Z = norm.ppf(sobol_draws)
    Z_G = Z[:, :steps]
    Z_uncorr = Z[:, steps:]
    
    # 2. Regime-Switching Correlation
    # Normal correlation is 0.85, but drops to 0.30 during the data event
    rho = np.full(steps, 0.85)
    rho[event_start : event_start + event_duration] = 0.30
    
    # Correlate the standard normals dynamically across the time steps
    Z_S = rho * Z_G + np.sqrt(1 - rho**2) * Z_uncorr
    
    # 3. Simulate Price Paths (Vectorized Geometric Brownian Motion)
    # Using log returns: d(lnS) = -0.5 * vol^2 * dt + vol * sqrt(dt) * Z
    log_returns_G = -0.5 * vol_g**2 * dt + vol_g * np.sqrt(dt) * Z_G
    log_returns_S = -0.5 * vol_s**2 * dt + vol_s * np.sqrt(dt) * Z_S
    
    # Accumulate returns to get price paths (normalized to 100 at t=0)
    prices_G = 100 * np.exp(np.cumsum(log_returns_G, axis=1))
    prices_S = 100 * np.exp(np.cumsum(log_returns_S, axis=1))
    
    # 4. Evaluate the Hedge 
    # Assume we entered a dollar-neutral pair trade at t=0: Long Gold, Short Silver
    # Calculate minute-by-minute PnL for each leg
    pnl_G = prices_G - 100
    pnl_S = 100 - prices_S # Short position
    
    # Identify paths where BOTH legs are losing simultaneously at the end of the event
    event_end = event_start + event_duration
    double_loss_mask = (pnl_G[:, event_end] < 0) & (pnl_S[:, event_end] < 0)
    double_loss_rate = np.mean(double_loss_mask)
    
    print(f"Paths simulated: {n_paths:,}")
    print(f"Probability of both legs losing right after the event: {double_loss_rate:.2%}")
    
    # 5. Fast Plotting (Fixing the seaborn bootstrap bottleneck)
    # Downsample for plotting clarity if we have millions of points
    plot_idx = np.random.choice(n_paths, min(n_paths, 10000), replace=False)
    
    fig, axes = plt.subplots(1, 2, figsize=(14, 5))
    
    # Plot A: The Correlation Breakdown
    axes[0].plot(rho, color='red', drawstyle='steps-post', linewidth=2)
    axes[0].axvspan(event_start, event_start + event_duration, color='gray', alpha=0.2, label='Data Release')
    axes[0].set(title="Regime-Switching Correlation ($\\rho$)", xlabel="Minute", ylabel="Correlation")
    axes[0].legend()
    
    # Plot B: Scatter of Returns at the end of the event with FAST regression line
    sns.scatterplot(
        x=log_returns_G[plot_idx, event_end] * 10000, # In basis points
        y=log_returns_S[plot_idx, event_end] * 10000, 
        alpha=0.2, ax=axes[1], color='blue', edgecolor=None
    )
    # ci=None prevents the bootstrap loop, plotting the line instantly
    sns.regplot(
        x=log_returns_G[plot_idx, event_end] * 10000, 
        y=log_returns_S[plot_idx, event_end] * 10000, 
        scatter=False, ci=None, color='black', line_kws={'linestyle':'--'}, ax=axes[1]
    )
    axes[1].set(
        title="Gold vs Silver Returns at Event End (bps)",
        xlabel="Gold Return (bps)", 
        ylabel="Silver Return (bps)"
    )
    plt.tight_layout()
    plt.show()

# Run with 65,536 paths for a quick test. Change m=21 for 2 million paths.
simulate_event_scalp(m=16)

Re: Simulation Monte Carlo on corelation gold/silver price

Posted: Sun Sep 27, 2026 3:16 pm
by PTScalper
Why this structure works better for scalping

Dimension Handling in QMC: By stepping minute-by-minute over an hour, our simulation requires 120 dimensions (60 steps $\times$ 2 assets). scipy.stats.qmc.Sobol handles up to 21,201 dimensions by default, making it highly effective for short-duration, high-frequency path generation.

Vectorized Correlation Mapping: Instead of a static Cholesky matrix, mapping Z_S = rho * Z_G + sqrt(1 - rho**2) * Z_uncorr allows $\rho$ to be an array that changes at specific timesteps, simulating the liquidity vacuum perfectly without slow loops.

Exposing the "Double Loss": When $\rho$ drops from 0.85 to 0.30, a beta-weighted pair trade suddenly behaves like two unhedged directional bets. The script calculates exactly how often you get caught on the wrong side of both simultaneously during that chaos.

Re: Simulation Monte Carlo on corelation gold/silver price

Posted: Mon Sep 28, 2026 12:39 pm
by PropScalpDesk
Here is roughly what I meant by two regimes, small enough to run in a second:

Code: Select all

import numpy as np
rng = np.random.default_rng(7)
n_days, mins = 2000, 60          # first hour after the open
vol_g, vol_s = 0.00035, 0.00060  # per-minute return vol, gold / silver
beta = vol_g / vol_s * 0.85      # hedge ratio fitted on normal days
def day(rho):
    z1 = rng.standard_normal(mins)
    z2 = rho * z1 + np.sqrt(1 - rho**2) * rng.standard_normal(mins)
    g, s = vol_g * z1, vol_s * z2
    return g.sum(), (g - beta * s).sum()
event = rng.random(n_days) < 0.2  # one day in five has a big release
res = np.array([day(0.3 if e else 0.85) for e in event])
for name, mask in (("normal", ~event), ("event", event)):
    print(name, round(np.std(res[mask, 1]) / np.std(res[mask, 0]), 2))
It prints the hedged book's volatility as a fraction of plain gold. With these inputs I get 0.53 on normal days and 1.12 on event days. The hedge removes about half the noise when you don't need it, and on release days the book ends up noisier than gold alone, because the silver leg stops offsetting and just adds its own swings.

The vols are placeholders; put in per-minute numbers from your own feed. It still assumes normal returns and ignores the spread paid on both legs, which on event days is the bigger bill anyway.