Evaluate weights against next-period returns to avoid look-ahead
Weights formed from close[t] now earn the t→t+1 return: forward returns are computed on the full market calendar before selecting signal dates, so a sparse signal grid earns the next available return rather than the next signal date, and the final signal date (no forward return) is dropped. Signal pct_change uses fill_method=None so suspended names do not inherit stale prices. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+25
-1
@@ -65,7 +65,7 @@ def _make_data(n_days: int = 40, symbols=_SYMBOLS, start="2024-01-01",
|
||||
def _make_weights(data: pd.DataFrame, name="combo") -> pd.DataFrame:
|
||||
"""Demeaned per-date signed weights so the cross-section is dollar-neutral."""
|
||||
close = data.pivot_table(index="date", columns="symbol_id", values="close").sort_index()
|
||||
raw = -close.pct_change(5)
|
||||
raw = -close.pct_change(5, fill_method=None)
|
||||
demeaned = raw.sub(raw.mean(axis=1), axis=0)
|
||||
long = demeaned.reset_index().melt(id_vars="date", var_name="symbol_id",
|
||||
value_name="weight").dropna()
|
||||
@@ -535,3 +535,27 @@ def test_evaluate_portfolio_keys_no_ic():
|
||||
assert key in metrics
|
||||
assert "ic" not in metrics
|
||||
assert "rank_ic" not in metrics
|
||||
|
||||
|
||||
def test_evaluate_portfolio_excludes_signal_without_forward_return():
|
||||
dates = pd.date_range("2024-01-01", periods=3)
|
||||
data = pd.DataFrame([
|
||||
{"symbol_id": sym, "date": d, "close": price}
|
||||
for d, prices in zip(dates, [(100.0, 100.0), (100.0, 100.0), (200.0, 100.0)])
|
||||
for sym, price in zip(("sh600000", "sz000001"), prices)
|
||||
])
|
||||
positions = pd.DataFrame({
|
||||
"symbol_id": ["sh600000", "sz000001", "sh600000", "sz000001"],
|
||||
"date": [dates[1], dates[1], dates[2], dates[2]],
|
||||
"portfolio_name": ["run1"] * 4,
|
||||
"target_weight": [0.5, -0.5, -0.5, 0.5],
|
||||
"target_value": [500.0, -500.0, -500.0, 500.0],
|
||||
"target_shares": [5.0, -5.0, -2.5, 5.0],
|
||||
"position_shares": [5, -5, -2, 5],
|
||||
"position_value": [500.0, -500.0, -400.0, 500.0],
|
||||
"price": [100.0, 100.0, 200.0, 100.0],
|
||||
})
|
||||
|
||||
metrics = evaluate_portfolio(positions, data)
|
||||
|
||||
assert metrics["n_dates"] == 1
|
||||
|
||||
Reference in New Issue
Block a user