Spaces:
Running
Running
| import json | |
| import numpy as np | |
| import pandas as pd | |
| from scripts.run_ml_method_ladder import ( | |
| _date_group_sizes, | |
| _ranking_metrics, | |
| _ranker_relevance, | |
| _split_panel, | |
| build_sequence_features, | |
| render_html_report, | |
| ) | |
| def test_build_sequence_features_uses_current_and_prior_rows_only(): | |
| df = pd.DataFrame( | |
| { | |
| "close": [10, 11, 12, 11, 13, 14, 15, 15, 16, 18, 19, 20], | |
| "high": [11, 12, 13, 12, 14, 15, 16, 16, 17, 19, 20, 21], | |
| "low": [9, 10, 11, 10, 12, 13, 14, 14, 15, 17, 18, 19], | |
| "volume": [100, 110, 120, 130, 140, 160, 180, 190, 200, 220, 240, 260], | |
| } | |
| ) | |
| seq = build_sequence_features(df) | |
| assert "seq_ret_5_sum" in seq | |
| assert "seq_sax_up_ratio_10" in seq | |
| assert len(seq) == len(df) | |
| assert np.isfinite(seq.to_numpy()).all() | |
| def test_split_panel_embargoes_label_horizon_between_train_and_validation(): | |
| rows = [] | |
| for stock in ["2330", "2454"]: | |
| for idx in range(180): | |
| rows.append( | |
| { | |
| "stock": stock, | |
| "date": f"2026-01-{(idx % 28) + 1:02d}", | |
| "row_idx": idx, | |
| "label": 1 if idx % 3 == 0 else 0, | |
| "f": float(idx), | |
| } | |
| ) | |
| panel = pd.DataFrame(rows) | |
| train, valid, split = _split_panel(panel, train_ratio=0.7) | |
| assert not train.empty | |
| assert not valid.empty | |
| for stock, info in split["stocks"].items(): | |
| assert info["validation_start_idx"] - info["train_end_idx"] > 1 | |
| assert train[train["stock"] == stock]["row_idx"].max() < valid[valid["stock"] == stock]["row_idx"].min() | |
| def test_ranking_metrics_scores_top_k_hits_by_date(): | |
| frame = pd.DataFrame( | |
| { | |
| "date": ["2026-01-01"] * 4 + ["2026-01-02"] * 4, | |
| "stock": list("ABCDEFGH"), | |
| "label": [1, 0, -1, 1, 0, 1, -1, 1], | |
| "p_buy": [0.9, 0.8, 0.1, 0.2, 0.2, 0.95, 0.1, 0.8], | |
| "pred": [1, 1, -1, 0, 0, 1, -1, 1], | |
| } | |
| ) | |
| metrics = _ranking_metrics(frame, top_k=2) | |
| assert metrics["dates"] == 2 | |
| assert metrics["hit_rate"] == 75.0 | |
| def test_ranker_helpers_build_date_groups_and_non_negative_relevance(): | |
| frame = pd.DataFrame( | |
| { | |
| "date": ["2026-01-01", "2026-01-01", "2026-01-02"], | |
| "label": [-1, 0, 1], | |
| } | |
| ) | |
| assert _date_group_sizes(frame) == [2, 1] | |
| assert _ranker_relevance(frame["label"]).to_list() == [0, 1, 2] | |
| def test_render_html_report_contains_method_rows(): | |
| payload = { | |
| "generated_at": "2026-05-21T00:00:00+00:00", | |
| "stocks": ["2330"], | |
| "split": {"validation_date_range": ["2026-01-01", "2026-02-01"]}, | |
| "methods": { | |
| "panel_global_model": { | |
| "baseline": {"accuracy": 40, "up_precision": 50}, | |
| "candidate": {"accuracy": 44, "up_precision": 51, "n_signals": 10}, | |
| "deltas": {"accuracy_delta_pp": 4, "up_precision_delta_pp": 1}, | |
| "gate": {"passed": True}, | |
| }, | |
| "ranking_from_panel": {"ranking": {"baseline": {}, "candidate": {}, "deltas": {}}}, | |
| }, | |
| } | |
| html = render_html_report(payload) | |
| assert "ML Method Ladder Validation" in html | |
| assert "panel_global_model" in html | |
| assert "PASS" in html | |