Spaces:
Sleeping
Sleeping
File size: 6,456 Bytes
e610a2f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | import pandas as pd
from scripts.optimize_factor_weights import (
WeightConfig,
apply_weight_config,
compute_event_metrics,
load_stock_codes_file,
optimize_weights,
pass_decision,
)
def _events(rows):
return pd.DataFrame(rows)
def test_apply_weight_config_can_flip_to_buy_with_bull_factor():
events = _events(
[
{
"stock": "2330",
"date": "2025-04-01",
"y_true": 1,
"base_pred": 0,
"p_buy": 0.42,
"p_hold": 0.46,
"p_sell": 0.12,
"bull_score": 3.0,
"bear_score": 0.0,
}
]
)
pred = apply_weight_config(events, WeightConfig(buy_bull_weight=0.02))
assert pred.tolist() == [1]
def test_apply_weight_config_bear_gate_blocks_risky_buy():
events = _events(
[
{
"stock": "2330",
"date": "2025-04-01",
"y_true": 0,
"base_pred": 1,
"p_buy": 0.62,
"p_hold": 0.31,
"p_sell": 0.07,
"bull_score": 0.0,
"bear_score": 2.0,
}
]
)
pred = apply_weight_config(events, WeightConfig(buy_bear_gate=2.0))
assert pred.tolist() == [0]
def test_compute_event_metrics_reports_precision_and_coverage():
events = _events(
[
{"y_true": 1},
{"y_true": 0},
{"y_true": -1},
{"y_true": 1},
]
)
metrics = compute_event_metrics(events, pred=pd.Series([1, 0, -1, -1]).to_numpy())
assert metrics["accuracy"] == 75.0
assert metrics["direction_accuracy"] == 66.6667
assert metrics["buy_precision"] == 100.0
assert metrics["sell_precision"] == 50.0
assert metrics["coverage"] == 0.75
def test_optimize_weights_uses_earlier_events_then_validates_later_events():
rows = []
for i in range(6):
risky_false_buy = i < 4
rows.append(
{
"stock": "2330",
"date": f"2025-04-0{i + 1}",
"y_true": 0 if risky_false_buy else 1,
"base_pred": 1,
"p_buy": 0.62 if risky_false_buy else 0.58,
"p_hold": 0.31,
"p_sell": 0.07 if risky_false_buy else 0.11,
"bull_score": 0.0 if risky_false_buy else 3.0,
"bear_score": 2.0 if risky_false_buy else 0.0,
}
)
for i in range(4):
risky_false_buy = i < 2
rows.append(
{
"stock": "2330",
"date": f"2025-04-1{i + 1}",
"y_true": 0 if risky_false_buy else 1,
"base_pred": 1,
"p_buy": 0.62 if risky_false_buy else 0.58,
"p_hold": 0.31,
"p_sell": 0.07 if risky_false_buy else 0.11,
"bull_score": 0.0 if risky_false_buy else 3.0,
"bear_score": 2.0 if risky_false_buy else 0.0,
}
)
result = optimize_weights(
_events(rows),
optimize_ratio=0.6,
min_signal_ratio=0.3,
grid={
"buy_bear_gate": [None, 2.0],
"buy_bull_weight": [0.0],
"buy_bear_weight": [0.0],
"sell_bear_weight": [0.0],
"sell_bull_weight": [0.0],
"sell_bull_gate": [None],
"hold_bias": [0.0],
},
)
assert result["best_config"]["buy_bear_gate"] == 2.0
assert result["split"]["tune_events"] == 6
assert result["split"]["validation_events"] == 4
assert result["validation"]["deltas"]["accuracy_delta_pp"] == 50.0
assert result["validation"]["deltas"]["buy_precision_delta_pp"] == 50.0
def test_pass_decision_requires_validation_improvement():
result = {
"validation": {
"baseline": {"signal_count": 4},
"candidate": {"signal_count": 4},
"deltas": {"accuracy_delta_pp": 3.0, "buy_precision_delta_pp": 0.0},
}
}
decision = pass_decision(result, min_accuracy_delta_pp=3.0, min_signal_ratio=0.6)
assert decision["passed"] is True
def test_precision_first_rejects_buy_count_expansion_and_prefers_precision():
rows = []
for i in range(10):
rows.append(
{
"stock": "2330",
"date": f"2025-04-{i + 1:02d}",
"y_true": 1 if i in {0, 1, 2, 3} else 0,
"base_pred": 1 if i in {0, 1} else 0,
"p_buy": 0.52 if i in {0, 1} else 0.48,
"p_hold": 0.50 if i not in {0, 1} else 0.44,
"p_sell": 0.02,
"bull_score": 3.0 if i in {0, 1, 2, 3, 4, 5} else 0.0,
"bear_score": 2.0 if i in {4, 5} else 0.0,
}
)
result = optimize_weights(
_events(rows),
optimize_ratio=0.6,
min_signal_ratio=0.3,
objective="precision-first",
max_buy_count_ratio=1.3,
grid={
"buy_bear_gate": [None, 2.0],
"buy_bull_weight": [0.0, 0.02],
"buy_bear_weight": [0.0],
"sell_bear_weight": [0.0],
"sell_bull_weight": [0.0],
"sell_bull_gate": [None],
"hold_bias": [0.0],
},
)
assert result["objective"] == "precision-first"
assert result["max_buy_count_ratio"] == 1.3
assert result["best_config"]["buy_bull_weight"] == 0.0
def test_pass_decision_can_enforce_buy_count_ratio():
result = {
"validation": {
"baseline": {"signal_count": 10, "buy_count": 10},
"candidate": {"signal_count": 10, "buy_count": 14},
"deltas": {"accuracy_delta_pp": 5.0, "buy_precision_delta_pp": 0.0},
}
}
decision = pass_decision(
result,
min_accuracy_delta_pp=5.0,
min_signal_ratio=0.7,
max_buy_count_ratio=1.3,
)
assert decision["passed"] is False
assert decision["checks"]["buy_count_ratio"] is False
assert decision["buy_count_ratio"] == 1.4
def test_load_stock_codes_file_accepts_popular_stock_payload(tmp_path):
path = tmp_path / "popular.json"
path.write_text(
"""
{
"codes": ["2330", "0050", "2330"],
"stocks": [{"code": "ignored"}]
}
"""
)
assert load_stock_codes_file(path) == ["2330", "0050"]
|