Add offline workflow and coverage tests
This commit is contained in:
@@ -413,3 +413,92 @@ def test_feature_aware_alpha_reads_joined_feature_column(tmp_path):
|
||||
assert (result["alpha_name"] == "feature_run").all()
|
||||
last = result[result["date"] == result["date"].max()]
|
||||
assert last.set_index("symbol_id")["weight"].idxmax() == "sh600519"
|
||||
|
||||
|
||||
def test_feature_paths_join_multiple_files_and_normalize_dates(tmp_path):
|
||||
module_path = tmp_path / "multi_feature_alpha.py"
|
||||
module_path.write_text(textwrap.dedent('''
|
||||
import pandas as pd
|
||||
from pipeline.alpha.base import BaseAlpha
|
||||
from pipeline.alpha.registry import register_alpha
|
||||
|
||||
@register_alpha
|
||||
class MultiFeatureAlpha(BaseAlpha):
|
||||
name = "multi_feature_test_alpha"
|
||||
|
||||
def signal_from_data(
|
||||
self,
|
||||
data: pd.DataFrame,
|
||||
close: pd.DataFrame,
|
||||
) -> pd.DataFrame:
|
||||
data = data.copy()
|
||||
data["combined_feature"] = data["toy_a"] + data["toy_b"]
|
||||
signal = data.pivot_table(
|
||||
index="date",
|
||||
columns="symbol_id",
|
||||
values="combined_feature",
|
||||
aggfunc="first",
|
||||
)
|
||||
return signal.reindex(index=close.index, columns=close.columns)
|
||||
'''))
|
||||
|
||||
data = _make_data(n_days=8)
|
||||
symbol_score = {"sh600000": 1.0, "sz000001": 2.0, "sh600519": 3.0}
|
||||
|
||||
feature_a = data[["symbol_id", "date"]].copy()
|
||||
feature_a["date"] = feature_a["date"] + pd.Timedelta(hours=15)
|
||||
feature_a["toy_a"] = feature_a["symbol_id"].map(symbol_score)
|
||||
|
||||
feature_b = data[["symbol_id", "date"]].copy()
|
||||
feature_b["date"] = feature_b["date"].dt.strftime("%Y-%m-%d 09:30:00")
|
||||
feature_b["toy_b"] = feature_b["symbol_id"].map(symbol_score) * 10.0
|
||||
|
||||
feature_a_path = tmp_path / "toy_a.pq"
|
||||
feature_b_path = tmp_path / "toy_b.pq"
|
||||
feature_a.to_parquet(feature_a_path, index=False)
|
||||
feature_b.to_parquet(feature_b_path, index=False)
|
||||
|
||||
load_alpha_module(str(module_path))
|
||||
result = compute_alpha(
|
||||
data,
|
||||
"multi_feature_run",
|
||||
"multi_feature_test_alpha",
|
||||
feature_paths=[str(feature_a_path), str(feature_b_path)],
|
||||
)
|
||||
|
||||
assert list(result.columns) == ALPHA_COLUMNS
|
||||
assert (result["alpha_name"] == "multi_feature_run").all()
|
||||
last = result[result["date"] == result["date"].max()]
|
||||
assert last.set_index("symbol_id")["weight"].idxmax() == "sh600519"
|
||||
|
||||
|
||||
def test_compute_alpha_rejects_duplicate_feature_frame_columns():
|
||||
data = _make_data()
|
||||
duplicate_columns = pd.DataFrame(
|
||||
[["sh600000", pd.Timestamp("2024-01-01"), 1.0, 2.0]],
|
||||
columns=["symbol_id", "date", "toy_feature", "toy_feature"],
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="duplicate columns"):
|
||||
compute_alpha(
|
||||
data,
|
||||
"bad_features",
|
||||
"reversal",
|
||||
feature_frames=[duplicate_columns],
|
||||
)
|
||||
|
||||
|
||||
def test_compute_alpha_rejects_feature_path_collision_with_daily_data(tmp_path):
|
||||
data = _make_data()
|
||||
close_collision = data[["symbol_id", "date"]].copy()
|
||||
close_collision["close"] = 1.0
|
||||
close_collision_path = tmp_path / "close_collision.pq"
|
||||
close_collision.to_parquet(close_collision_path, index=False)
|
||||
|
||||
with pytest.raises(ValueError, match="conflict"):
|
||||
compute_alpha(
|
||||
data,
|
||||
"close_collision",
|
||||
"reversal",
|
||||
feature_paths=[str(close_collision_path)],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user