import json from scripts.render_factor_weight_grid_report import generate_html_report, render_report def _grid_payload(): candidate = { "config": {"buy_bull_weight": 0.03, "buy_bear_weight": -0.01}, "passed": True, "gate": { "checks": { "accuracy_delta": True, "buy_precision_delta": True, "signal_ratio": True, "buy_count_ratio": True, }, "signal_ratio": 1.04, "buy_count_ratio": 1.25, }, "validation": { "metrics": { "accuracy": 41.16, "direction_accuracy": 46.27, "buy_precision": 50.46, "sell_precision": 39.83, "signal_count": 2671, "coverage": 0.7949, }, "deltas": { "accuracy_delta_pp": 2.83, "direction_accuracy_delta_pp": 2.09, "buy_precision_delta_pp": 1.24, "sell_precision_delta_pp": 0.79, "signal_count_delta": 111, "coverage_delta": 0.033, }, }, } return { "experiment": "factor_weight_grid_search", "generated_at": "2026-05-18T00:00:00+00:00", "stocks": ["2330", "0050"], "factors": {"bull_column": "oldwang_bull_score", "bear_column": "oldwang_bear_score"}, "grid_preset": "wide", "result": { "split": { "tune_events": 10, "validation_events": 8, "tune_date_range": ["2025-01-01", "2025-06-01"], "validation_date_range": ["2025-06-02", "2025-12-31"], }, "grid_size": 63504, "thresholds": { "min_accuracy_delta_pp": 2.0, "min_buy_precision_delta_pp": 0.0, "min_signal_ratio": 0.7, "max_buy_count_ratio": 1.3, }, "baseline": { "validation": { "accuracy": 38.33, "direction_accuracy": 44.18, "buy_precision": 49.23, "sell_precision": 39.04, "signal_count": 2560, "coverage": 0.7619, } }, "passing_count": 139, "best_passing": candidate, "top_passing": [candidate], "top_overall": [candidate], }, } def test_generate_html_report_contains_validation_details(): html = generate_html_report( _grid_payload(), popular_payload={ "source": "yahoo_rank", "count": 2, "fallback_used": True, "stocks": [{"rank": 1, "code": "2330", "name": "台積電", "source": "yahoo_rank"}], }, raw_json_path="docs/result.json", command="venv/bin/python scripts/search_factor_weight_grid.py ...", ) assert "Factor Weight Grid Search Report" in html assert "Grid Size" in html assert "63504" in html assert "oldwang_bull_score" in html assert "buy_bull_weight" in html assert "台積電" in html assert "venv/bin/python" in html def test_render_report_writes_html(tmp_path): input_json = tmp_path / "grid.json" popular_json = tmp_path / "popular.json" output_html = tmp_path / "report.html" input_json.write_text(json.dumps(_grid_payload(), ensure_ascii=False)) popular_json.write_text( json.dumps({"source": "fixture", "stocks": [{"code": "2330", "rank": 1}]}, ensure_ascii=False) ) output = render_report(input_json=input_json, popular_json=popular_json, output_html=output_html) assert output.exists() assert "Top Passing Candidates" in output.read_text() def test_generate_html_report_includes_external_factor_metadata(): payload = _grid_payload() payload["experiment"] = "external_factor_weight_grid_search" payload["external_factors"] = { "csv_paths": ["data/external/chipk_scores.csv", "data/external/crowd_scores.csv"], "stock_column": "stock", "date_column": "date", "bull_column": "bull_score", "bear_column": "bear_score", "combine": "sum", "merge_summary": {"event_count": 100, "matched_event_count": 80, "coverage": 0.8}, "promotion_note": "validation-only", } html = generate_html_report(payload) assert "External Factor Source" in html assert "chipk_scores.csv" in html assert "80 / 100" in html assert "validation-only" in html