#!/usr/bin/env python3 """C13: Add SOX + TNX cross-asset features (sox_ret_1d, sox_ret_5d, sox_ma20_ratio, tnx_level, tnx_change_5d). Already computed in fetch_df, just not in FEATURE_COLUMNS. Pass gate: dir_accuracy > 43.5% AND up_precision > 54% (updated post-C5 thresholds). """ import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) try: from dotenv import load_dotenv; load_dotenv(ROOT / ".env") except ImportError: pass import warnings; warnings.filterwarnings("ignore") import json sys.path.insert(0, str(ROOT / "scripts")) from improvement_harness import BASELINE_FEATURES, run_comparison STOCKS = ["2330", "0050", "2317", "2454", "2881"] CURRENT_FEATURES = [f for f in BASELINE_FEATURES if f not in { "macd_cross_up", "macd_cross_down", "price_volume_div", "foreign_net_vol_ratio", "trust_net_vol_ratio", "dealer_net_vol_ratio", "institutional_net_vol_ratio", "institutional_5d_net_vol_ratio", "institutional_20d_zscore", "foreign_trust_alignment", "institutional_streak", }] SOX_TNX_FEATURES = CURRENT_FEATURES + [ "sox_ret_1d", "sox_ret_5d", "sox_ma20_ratio", "tnx_level", "tnx_change_5d", ] PASS_DIR_ACC = 43.5 PASS_UP_PREC = 54.0 def main(): print(f"C13: baseline={len(CURRENT_FEATURES)} features → candidate={len(SOX_TNX_FEATURES)} features") print(f"New features: sox_ret_1d, sox_ret_5d, sox_ma20_ratio, tnx_level, tnx_change_5d\n") cmp = run_comparison( feature_sets={"baseline": CURRENT_FEATURES, "sox_tnx": SOX_TNX_FEATURES}, label_mode="triple_barrier", stocks=STOCKS, output_path=None, pass_criterion={"dir_accuracy": PASS_DIR_ACC}, verbose=True, ) b = cmp["aggregate"].get("baseline", {}) c = cmp["aggregate"].get("sox_tnx", {}) dir_delta = c.get("dir_accuracy", 0) - b.get("dir_accuracy", 0) prec_delta = c.get("up_precision", 0) - b.get("up_precision", 0) passed = ( c.get("dir_accuracy", 0) >= PASS_DIR_ACC and c.get("up_precision", 0) >= PASS_UP_PREC and dir_delta >= -0.5 and prec_delta >= -0.5 ) print("\n── Results ──────────────────────────────────────") print(f" baseline dir_acc={b.get('dir_accuracy')}% up_prec={b.get('up_precision')}%") print(f" sox_tnx dir_acc={c.get('dir_accuracy')}% up_prec={c.get('up_precision')}%") print(f" Δ dir_acc={dir_delta:+.1f}pp Δ up_prec={prec_delta:+.1f}pp") print(f" Pass gate: dir>{PASS_DIR_ACC}% AND up_prec>{PASS_UP_PREC}%") print(f" Result: {'PASSED' if passed else 'FAILED'}") result = { "experiment": "C13_sox_tnx", "new_features": ["sox_ret_1d", "sox_ret_5d", "sox_ma20_ratio", "tnx_level", "tnx_change_5d"], "aggregate": cmp["aggregate"], "results": cmp["results"], "passed": passed, } Path("docs").mkdir(exist_ok=True) with open("docs/c13_result.json", "w") as f: json.dump(result, f, indent=2) print(f" Saved → docs/c13_result.json") if passed: print("\n ACTION: Add these to FEATURE_COLUMNS in models/predictor.py:") for f in ["sox_ret_1d", "sox_ret_5d", "sox_ma20_ratio", "tnx_level", "tnx_change_5d"]: print(f" \"{f}\",") if __name__ == "__main__": main()