Spaces:
Running on Zero
Running on Zero
| """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)]) | |