Add offline workflow and coverage tests

This commit is contained in:
Yuxuan Yan
2026-06-16 17:37:16 +08:00
parent 8d908477e2
commit 31baa18ce5
16 changed files with 2104 additions and 9 deletions
+98
View File
@@ -0,0 +1,98 @@
"""Malformed parquet/input tests for phase boundary contracts."""
from __future__ import annotations
import pandas as pd
import pytest
from pipeline.alpha.compute import compute_alpha
from pipeline.combo.combine import combine_alphas
from pipeline.derived.compute import validate_derived_frame
from pipeline.portfolio.construct import construct_positions
from pipeline.portfolio.simulator import ReferenceSimulator
from tests.helpers import make_generated_daily_bars
def test_alpha_compute_rejects_daily_data_without_close():
daily = make_generated_daily_bars().drop(columns=["close"])
with pytest.raises(KeyError, match="close"):
compute_alpha(daily, "bad", "reversal", lookback=3)
def test_alpha_feature_path_rejects_duplicate_symbol_dates(tmp_path):
daily = make_generated_daily_bars()
feature = pd.DataFrame({
"symbol_id": ["sh600000", "sh600000"],
"date": ["2024-01-02 09:30:00", "2024-01-02 15:00:00"],
"toy_feature": [1.0, 2.0],
})
feature_path = tmp_path / "duplicate_feature_keys.pq"
feature.to_parquet(feature_path, index=False)
with pytest.raises(ValueError, match="duplicate symbol_id,date"):
compute_alpha(
daily,
"bad_features",
"reversal",
lookback=3,
feature_paths=[str(feature_path)],
)
def test_derived_validation_rejects_bool_value_columns():
derived = pd.DataFrame({
"symbol_id": ["sh600000"],
"date": [pd.Timestamp("2024-01-02")],
"is_good": [True],
})
with pytest.raises(ValueError, match="numeric"):
validate_derived_frame(derived)
def test_combo_combine_rejects_missing_weight_column(tmp_path):
bad_alpha = pd.DataFrame({
"symbol_id": ["sh600000"],
"date": [pd.Timestamp("2024-01-02")],
"alpha_name": ["bad"],
})
bad_alpha_path = tmp_path / "bad_alpha.pq"
bad_alpha.to_parquet(bad_alpha_path, index=False)
with pytest.raises(KeyError, match="weight"):
combine_alphas([str(bad_alpha_path)], "bad_combo")
def test_portfolio_build_rejects_weights_without_symbol_id():
daily = make_generated_daily_bars()
bad_weights = pd.DataFrame({
"date": [pd.Timestamp("2024-01-02")],
"combo_name": ["bad"],
"weight": [1.0],
})
with pytest.raises(KeyError, match="symbol_id"):
construct_positions(
bad_weights,
daily,
booksize=1_000_000.0,
portfolio_name="bad_portfolio",
)
def test_portfolio_simulate_rejects_positions_without_position_shares():
daily = make_generated_daily_bars()
bad_positions = pd.DataFrame({
"symbol_id": ["sh600000"],
"date": [pd.Timestamp("2024-01-02")],
"portfolio_name": ["bad"],
"target_weight": [1.0],
"target_value": [1000.0],
"target_shares": [100.0],
"position_value": [1000.0],
"price": [10.0],
})
with pytest.raises(KeyError, match="position_shares"):
ReferenceSimulator().run(bad_positions, daily)