Merge remote-tracking branch 'origin/main' into codex/research-alpha158-phase5-formula-con (#12)
CI / lite (push) Successful in 7s

This commit was merged in pull request #12.
This commit is contained in:
2026-08-28 18:32:18 +08:00
parent 03e38d5123
commit 2bc8aea435
2 changed files with 309 additions and 0 deletions
+173
View File
@@ -17,6 +17,9 @@ from quant_engine.alpha_factors import (
ALPHA158_PHASE4_FORMULA_CATALOG_SHA256,
ALPHA158_PHASE4_FORMULA_CONTRACT_VERSION,
ALPHA158_PHASE4_FORMULA_SPECS,
ALPHA158_PHASE5_FORMULA_CATALOG_SHA256,
ALPHA158_PHASE5_FORMULA_CONTRACT_VERSION,
ALPHA158_PHASE5_FORMULA_SPECS,
alpha_001,
alpha_002,
alpha_003,
@@ -179,10 +182,12 @@ from quant_engine.alpha_factors import (
evaluate_phase2_operator,
evaluate_phase3_formula,
evaluate_phase4_formula,
evaluate_phase5_formula,
list_phase1_operators,
list_phase2_operators,
list_phase3_formulas,
list_phase4_formulas,
list_phase5_formulas,
correlation,
covariance,
decay_linear,
@@ -1789,3 +1794,171 @@ def test_phase4_dispatch_rejects_implicit_series_alignment():
high=inputs["high"],
low=misaligned_low,
)
# ── Alpha158 Phase 5: versioned alpha101-alpha150 formula contract ──────────
def test_phase5_formula_catalog_is_versioned_exact_and_content_addressed():
import hashlib
import json
from collections import Counter
expected_ids = tuple(f"alpha_{number:03d}" for number in range(101, 151))
expected_fields = {
"name",
"contract_version",
"formula",
"category",
"complexity",
"parameters",
"description",
"references",
"call_inputs",
"formula_inputs",
"input_category",
}
assert ALPHA158_PHASE5_FORMULA_CONTRACT_VERSION == "1.0.0"
assert list_phase5_formulas() == expected_ids
assert tuple(ALPHA158_PHASE5_FORMULA_SPECS) == expected_ids
assert Counter(
spec["input_category"] for spec in ALPHA158_PHASE5_FORMULA_SPECS.values()
) == {"pair": 33, "triple": 14, "quadruple": 3}
for alpha_id, spec in ALPHA158_PHASE5_FORMULA_SPECS.items():
assert set(spec) == expected_fields
assert spec["name"] == alpha_id
assert spec["contract_version"] == ALPHA158_PHASE5_FORMULA_CONTRACT_VERSION
assert spec["formula"] == ALPHA158_REGISTRY[alpha_id]["formula"]
serializable_specs = {
alpha_id: {
field: list(value) if isinstance(value, tuple) else value
for field, value in spec.items()
}
for alpha_id, spec in ALPHA158_PHASE5_FORMULA_SPECS.items()
}
encoded = json.dumps(
serializable_specs,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
).encode()
assert hashlib.sha256(encoded).hexdigest() == ALPHA158_PHASE5_FORMULA_CATALOG_SHA256
assert ALPHA158_PHASE5_FORMULA_CATALOG_SHA256 == (
"3368796169c9fbd39c4a34ea137e569964b15882fbf4de25124790d548db6533"
)
def test_phase5_formula_catalog_is_recursively_immutable():
import operator
with pytest.raises(TypeError):
operator.setitem(ALPHA158_PHASE5_FORMULA_SPECS, "alpha_101", {})
with pytest.raises(TypeError):
operator.setitem(
ALPHA158_PHASE5_FORMULA_SPECS["alpha_101"],
"formula",
"changed",
)
with pytest.raises(TypeError):
operator.setitem(
ALPHA158_PHASE5_FORMULA_SPECS["alpha_101"]["call_inputs"],
0,
"volume",
)
def test_phase5_catalog_freezes_callable_and_formula_inputs():
import inspect
for alpha_id, spec in ALPHA158_PHASE5_FORMULA_SPECS.items():
function = getattr(alpha_factors_module, alpha_id)
signature_inputs = tuple(
"open" if name == "open_" else name
for name in inspect.signature(function).parameters
)
assert spec["call_inputs"] == signature_inputs
assert spec["formula_inputs"] == tuple(ALPHA158_REGISTRY[alpha_id]["inputs"])
assert ALPHA158_PHASE5_FORMULA_SPECS["alpha_101"]["call_inputs"] == (
"close",
"high",
"low",
)
def test_phase5_dispatch_matches_all_existing_alpha101_alpha150_functions():
inputs = _phase3_market_inputs()
for alpha_id, spec in ALPHA158_PHASE5_FORMULA_SPECS.items():
call_inputs = spec["call_inputs"]
function = getattr(alpha_factors_module, alpha_id)
expected = function(*(inputs[name] for name in call_inputs))
actual = evaluate_phase5_formula(
alpha_id,
**{name: inputs[name] for name in reversed(call_inputs)},
)
pd.testing.assert_series_equal(actual, expected)
def test_phase5_dispatch_rejects_unknown_missing_extra_and_non_series_inputs():
inputs = _phase3_market_inputs()
with pytest.raises(KeyError, match="not registered"):
evaluate_phase5_formula("alpha_100", close=inputs["close"])
with pytest.raises(KeyError, match="not registered"):
evaluate_phase5_formula("alpha_151", close=inputs["close"])
with pytest.raises(ValueError, match=r"missing inputs.*low"):
evaluate_phase5_formula(
"alpha_101",
close=inputs["close"],
high=inputs["high"],
)
with pytest.raises(ValueError, match=r"unexpected inputs.*vwap"):
evaluate_phase5_formula(
"alpha_101",
close=inputs["close"],
high=inputs["high"],
low=inputs["low"],
vwap=inputs["vwap"],
)
with pytest.raises(TypeError, match="high must be a pandas Series"):
evaluate_phase5_formula( # type: ignore[arg-type]
"alpha_101",
close=inputs["close"],
high=[1.0, 2.0],
low=inputs["low"],
)
def test_phase5_dispatch_rejects_length_and_index_alignment_errors():
inputs = _phase3_market_inputs()
shorter_low = inputs["low"].iloc[:-1]
misaligned_high = inputs["high"].rename(index={79: 80})
with pytest.raises(ValueError, match="low length must match close"):
evaluate_phase5_formula(
"alpha_101",
close=inputs["close"],
high=inputs["high"],
low=shorter_low,
)
with pytest.raises(ValueError, match="high index must align with close"):
evaluate_phase5_formula(
"alpha_101",
close=inputs["close"],
high=misaligned_high,
low=inputs["low"],
)
def test_phase5_contract_is_publicly_exported():
assert {
"ALPHA158_PHASE5_FORMULA_CONTRACT_VERSION",
"ALPHA158_PHASE5_FORMULA_CATALOG_SHA256",
"ALPHA158_PHASE5_FORMULA_SPECS",
"list_phase5_formulas",
"evaluate_phase5_formula",
} <= set(alpha_factors_module.__all__)