"""make_blind — export the CERTIFIED BTC/ETH 1d feed as ANONYMIZED, OVERLAID curves. The blind-signal fleet (~50 "signal expert" agents) must NOT know the series are BTC/ETH crypto — otherwise they pattern-match the 2020 covid crash / 2022 bear / 2024 halving from memory instead of finding a real, transferable timing edge. So we strip every tell: * relabel BTC->"A", ETH->"B" (no ticker anywhere) * REBASE each series to 100 at its first bar (multiply all OHLC by 100/open[0]) -> constant rescale, returns/backtest UNCHANGED, but the price LEVEL no longer says "this is $60k bitcoin". Both curves now start at 100 = literally "curve sovrapposte". * synthetic DAILY calendar starting 2001-01-01 (so 1 bar = 1 day for annualization, but no 2020/2022 era to recognize). * normalize volume to its own median (=1) -> shape kept, scale anonymized. Split: first SPLIT_FRAC of bars = TRAIN (handed to the agents), the rest = TEST (held out; only the orchestrator ever evaluates on it -> a true out-of-sample PnL/DD). Outputs (data/blind/, gitignored-friendly): blind_A_train.parquet blind_B_train.parquet <- agent-visible blind_A_full.parquet blind_B_full.parquet <- orchestrator-only (full series, for OOS eval with proper warmup) blind_meta.json <- split index, lengths (NO mapping to BTC/ETH in plain sight) overlay.png <- the two overlaid anonymized curves (for the human) """ from __future__ import annotations import json import sys from pathlib import Path import numpy as np import pandas as pd sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt") import altlib as al # noqa: E402 OUT = Path("/opt/docker/PythagorasGoal/data/blind") SPLIT_FRAC = 0.70 SYNTH_START = "2001-01-01" # mapping kept OUT of the agent-visible meta; only here in source for our own audit. _REAL = {"A": "BTC", "B": "ETH"} def _anonymize(df: pd.DataFrame, n_bars: int) -> pd.DataFrame: df = df.reset_index(drop=True).copy() base = float(df["open"].iloc[0]) scale = 100.0 / base out = pd.DataFrame() synth = pd.date_range(SYNTH_START, periods=len(df), freq="1D", tz="UTC") out["timestamp"] = (synth.view("int64") // 1_000_000).astype("int64") for col in ("open", "high", "low", "close"): out[col] = df[col].values.astype(float) * scale vmed = float(np.nanmedian(df["volume"].values)) or 1.0 out["volume"] = df["volume"].values.astype(float) / vmed out["datetime"] = synth return out def main() -> None: OUT.mkdir(parents=True, exist_ok=True) meta = {"split_frac": SPLIT_FRAC, "series": {}} curves = {} for label, asset in _REAL.items(): raw = al.get(asset, "1d") anon = _anonymize(raw, len(raw)) n = len(anon) cut = int(n * SPLIT_FRAC) anon.to_parquet(OUT / f"blind_{label}_full.parquet", index=False) anon.iloc[:cut].reset_index(drop=True).to_parquet( OUT / f"blind_{label}_train.parquet", index=False) meta["series"][label] = {"n_bars": n, "train_bars": cut, "test_bars": n - cut} curves[label] = anon["close"].values print(f" Series {label}: {n} bars train={cut} test={n-cut} " f"(rebased start=100, level now {anon['close'].iloc[-1]:.0f})") (OUT / "blind_meta.json").write_text(json.dumps(meta, indent=2)) # overlay chart for the human (agents work on the numbers, not the png) try: import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(12, 5)) for label, c in curves.items(): ax.plot(np.arange(len(c)), c, label=f"Series {label}", lw=0.8) ax.axvline(int(min(len(c) for c in curves.values()) * SPLIT_FRAC), ls="--", color="k", alpha=0.4, label="train/test cut") ax.set_yscale("log") ax.set_title("Anonymized overlaid curves (rebased to 100) — train | held-out test") ax.legend() fig.tight_layout() fig.savefig(OUT / "overlay.png", dpi=110) print(f" overlay.png written") except Exception as e: print(f" (chart skipped: {e})") print(f"\n wrote -> {OUT}") if __name__ == "__main__": main()