DockerSpace / scripts /backtest_c15.py
DennisChan0909's picture
feat: integrate local architecture with HF Space
e610a2f
Raw
History Blame Contribute Delete
3.41 kB
#!/usr/bin/env python3
"""C15: Trend quality features β€” trend_efficiency_5d (directional purity of 5d move)
+ return_accel_5d (momentum acceleration). Pure OHLCV, no external data dependencies.
Source: arxiv.org/html/2512.12924v1 β€” "trending vs choppy behavior" as top feature class.
Pass gate: dir_accuracy > 43.5% AND up_precision > 54%.
"""
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",
}]
C15_FEATURES = CURRENT_FEATURES + [
"trend_efficiency_5d", # |return_5d| / (vol_20d * sqrt(5)) β€” clean trend vs noise
"return_accel_5d", # return_5d.diff(5) β€” momentum acceleration
]
PASS_DIR_ACC = 43.5
PASS_UP_PREC = 54.0
def main():
print(f"C15: baseline={len(CURRENT_FEATURES)} β†’ candidate={len(C15_FEATURES)} features")
print(f"New: trend_efficiency_5d, return_accel_5d\n")
cmp = run_comparison(
feature_sets={"baseline": CURRENT_FEATURES, "c15_trend": C15_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("c15_trend", {})
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" c15_trend 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": "C15_trend_quality",
"new_features": ["trend_efficiency_5d", "return_accel_5d"],
"aggregate": cmp["aggregate"],
"results": cmp["results"],
"passed": passed,
}
Path("docs").mkdir(exist_ok=True)
with open("docs/c15_result.json", "w") as f:
json.dump(result, f, indent=2)
print(f" Saved β†’ docs/c15_result.json")
if passed:
print("\n ACTION: Add to FEATURE_COLUMNS in models/predictor.py:")
for f in ["trend_efficiency_5d", "return_accel_5d"]:
print(f" \"{f}\",")
if __name__ == "__main__":
main()