"""Synthetic market data with known statistics. A geometric random walk is used rather than a recorded price series because the coverage test needs a distribution whose true quantiles are known in closed form. Real prices would make that test a measurement of the market rather than of the arithmetic. """ from __future__ import annotations import numpy as np import pandas as pd def synth(n: int = 600, tf: str = "1h", seed: int = 7, s0: float = 50000.0, sigma: float = 0.01, mu: float = 0.0) -> pd.DataFrame: rng = np.random.default_rng(seed) r = rng.normal(mu, sigma, n) close = s0 * np.exp(np.cumsum(r)) openp = np.concatenate([[s0], close[:-1]]) hi = np.maximum(openp, close) * (1 + np.abs(rng.normal(0, sigma / 3, n))) lo = np.minimum(openp, close) * (1 - np.abs(rng.normal(0, sigma / 3, n))) vol = np.abs(rng.normal(1000, 200, n)) + 1.0 step = pd.Timedelta("1h") if tf == "1h" else pd.Timedelta("1d") ts = pd.date_range("2025-01-01", periods=n, freq=step, tz="UTC") return pd.DataFrame({"ts": ts, "open": openp, "high": hi, "low": lo, "close": close, "volume": vol}) def future_ts(context: pd.DataFrame, horizon: int) -> pd.DatetimeIndex: ts = pd.to_datetime(context["ts"], utc=True) step = ts.diff().dropna().mode().iloc[0] return pd.DatetimeIndex([ts.iloc[-1] + step * (i + 1) for i in range(horizon)])