thinkingEverytime/QuantOracle
1
1#!/usr/bin/env python32"""Build a feature table from local OHLCV store.3 4Writes: data/features.parquet5"""6 7# ruff: noqa: E402 (sys.path bootstrap must run before local imports)8 9from __future__ import annotations10 11import argparse12from pathlib import Path13 14import pandas as pd15 16import sys17 18from pathlib import Path as _Path19 20# Scripts are executed with `scripts/` as sys.path[0]. Add repo root + frontend for imports.21_ROOT = str(_Path(__file__).resolve().parents[1])22if _ROOT not in sys.path:23 sys.path.insert(0, _ROOT)24_FRONTEND = str(_Path(__file__).resolve().parents[1] / "frontend")25if _FRONTEND not in sys.path:26 sys.path.insert(0, _FRONTEND)27 28from services.store import data_dir, read_ohlcv29 30from quant.features import build_features, build_targets31 32try:33 import duckdb # type: ignore34except Exception: # pragma: no cover35 duckdb = None36 37 38def main():39 ap = argparse.ArgumentParser()40 ap.add_argument("--horizon", type=int, default=5)41 ap.add_argument("--out", default="", help="Output parquet path (default: data/features.parquet)")42 args = ap.parse_args()43 44 root = data_dir()45 ohlcv_dir = root / "ohlcv"46 if not ohlcv_dir.exists():47 raise SystemExit(f"Missing {ohlcv_dir} (run scripts/ingest_eod.py first)")48 49 rows = []50 for p in sorted(ohlcv_dir.glob("*.parquet")):51 sym = p.stem52 h = read_ohlcv(sym)53 if h.empty or "Close" not in h:54 continue55 feats = build_features(h)56 if feats.empty:57 continue58 y = build_targets(h["Close"], horizon=args.horizon).reindex(feats.index)59 feats = feats.assign(symbol=sym, target=y)60 rows.append(feats.reset_index().rename(columns={"index": "Date"}))61 62 if not rows:63 raise SystemExit("No features produced")64 65 out = pd.concat(rows, ignore_index=True).dropna()66 out["Date"] = pd.to_datetime(out["Date"])67 out_path = Path(args.out) if args.out else (root / "features.parquet")68 out_path.parent.mkdir(parents=True, exist_ok=True)69 if duckdb is None:70 raise SystemExit("duckdb is required to write Parquet (install: `pip install duckdb`).")71 con = duckdb.connect(database=":memory:")72 con.register("df", out)73 path = str(out_path).replace("'", "''")74 con.execute(f"COPY df TO '{path}' (FORMAT PARQUET)")75 con.close()76 print(f"Wrote {len(out):,} rows -> {out_path}")77 78 79if __name__ == "__main__":80 main()81 