#!/usr/bin/env python3 """3-minute candles from the 1-minute files: MASSIVE_ROOT/minute//.json.gz → MASSIVE_ROOT/minute3//.json.gz (same format: timestamp of the candle start in UTC, open, high, low, close, volume). Candles start at :00, :03, :06 ... New York time, the way the balance-method test builds them. python make_3min.py """ from __future__ import annotations import gzip import json import os from datetime import datetime, timezone from pathlib import Path from zoneinfo import ZoneInfo ROOT = Path(os.environ.get("MASSIVE_ROOT", "massive_data")) ET = ZoneInfo("America/New_York") def main() -> None: files = sorted((ROOT / "minute").glob("*/*.json.gz")) for k, p in enumerate(files): out = ROOT / "minute3" / p.parent.name / p.name if out.exists(): continue bars = json.load(gzip.open(p, "rt")) cs = [] for b in bars: t = datetime.fromisoformat(b["timestamp"].replace("Z", "+00:00")).astimezone(ET) key = (t.hour * 60 + t.minute) // 3 if cs and cs[-1]["_k"] == key: c = cs[-1] c["high"] = max(c["high"], b["high"]); c["low"] = min(c["low"], b["low"]); c["close"] = b["close"]; c["volume"] += b["volume"] else: start = t.replace(minute=(t.minute // 3) * 3, second=0).astimezone(timezone.utc) cs.append({"_k": key, "timestamp": start.strftime("%Y-%m-%dT%H:%M:%SZ"), "open": b["open"], "high": b["high"], "low": b["low"], "close": b["close"], "volume": b["volume"]}) out.parent.mkdir(parents=True, exist_ok=True) with gzip.open(out, "wt") as fh: json.dump([{k2: v for k2, v in c.items() if k2 != "_k"} for c in cs], fh) if k % 20000 == 0: print(f" {k}/{len(files)}", flush=True) print(f"done: {len(files)} files", flush=True) if __name__ == "__main__": main()