Skip to content

Commit bae20c3

Browse files
committed
upd: 修复了没有有效因子时滚动回归报错的 bug
1 parent 4c1c515 commit bae20c3

4 files changed

Lines changed: 50 additions & 45 deletions

File tree

analyze_main.py

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -175,49 +175,52 @@ def main() -> None:
175175
# ------------------------------------------------------------------
176176
_step("Step 5 / 5 — Synthetic factor")
177177

178-
_info(f"Effective alphas: {effective_alphas}")
179-
#_info(f"Using ALL factors for OLS combination")
180-
_info(f"Rolling window = {COMBINE_WINDOW} trading days | orthogonalize = True")
181-
182-
# Use cross-sectional percentile rank of forward_return as the OLS
183-
# regression target. This removes the market-wide daily move (beta)
184-
# from the label, so the model learns relative stock selection ability
185-
# rather than fitting to the overall market direction.
186-
target_flat = target_df.reset_index()[["trade_date", "ts_code", "forward_return"]]
187-
target_cs_rank = target_flat.copy()
188-
target_cs_rank["forward_return"] = target_cs_rank.groupby("trade_date")[
189-
"forward_return"
190-
].rank(pct=True)
191-
_info(" OLS target: cross-sectional pct-rank of forward_return (per trade_date)")
192-
193-
synth_df = rolling_linear_combine(
194-
factors_df[['trade_date', 'ts_code', *effective_alphas]],
195-
target_cs_rank,
196-
factor_cols=effective_alphas,
197-
window=COMBINE_WINDOW,
198-
orthogonalize=True,
199-
forward_days=FORWARD_DAYS,
200-
)
201-
_ok(f"Synthetic factor : {synth_df.shape[0]:>7,} rows "
202-
f"(dates: {synth_df['trade_date'].nunique()})")
203-
204-
ic_series = calc_ic(synth_df, target_df)
205-
206-
metrics = calc_ic_metrics(ic_series)
207-
_info(f" IC Mean : {metrics['ic_mean']:>+.4f}")
208-
_info(f" IC Std : {metrics['ic_std']:>.4f}")
209-
_info(f" ICIR : {metrics['icir']:>+.4f}")
210-
211-
save_path = PLOTS_DIR / f"synthetic_factor_ic.png"
212-
plot_ic(ic_series, factor_name="synthetic_factor", show=SHOW_PLOTS, save_path=save_path)
213-
_info(f" IC chart saved.")
214-
215-
bt_synth = LayeredBacktester(synth_df, target_df, forward_days=FORWARD_DAYS, plots_dir=PLOTS_DIR)
216-
perf_synth = bt_synth.run_backtest()
217-
_info(" Backtest metrics (synthetic factor):")
218-
_p(perf_synth.to_string())
219-
bt_synth.plot(show=SHOW_PLOTS)
220-
_info(" Synthetic backtest plot saved.")
178+
if len(effective_alphas) == 0:
179+
_info("No effective alphas found. Rolling OLS skipped.")
180+
else:
181+
_info(f"Effective alphas: {effective_alphas}")
182+
#_info(f"Using ALL factors for OLS combination")
183+
_info(f"Rolling window = {COMBINE_WINDOW} trading days | orthogonalize = True")
184+
185+
# Use cross-sectional percentile rank of forward_return as the OLS
186+
# regression target. This removes the market-wide daily move (beta)
187+
# from the label, so the model learns relative stock selection ability
188+
# rather than fitting to the overall market direction.
189+
target_flat = target_df.reset_index()[["trade_date", "ts_code", "forward_return"]]
190+
target_cs_rank = target_flat.copy()
191+
target_cs_rank["forward_return"] = target_cs_rank.groupby("trade_date")[
192+
"forward_return"
193+
].rank(pct=True)
194+
_info(" OLS target: cross-sectional pct-rank of forward_return (per trade_date)")
195+
196+
synth_df = rolling_linear_combine(
197+
factors_df[['trade_date', 'ts_code', *effective_alphas]],
198+
target_cs_rank,
199+
factor_cols=effective_alphas,
200+
window=COMBINE_WINDOW,
201+
orthogonalize=True,
202+
forward_days=FORWARD_DAYS,
203+
)
204+
_ok(f"Synthetic factor : {synth_df.shape[0]:>7,} rows "
205+
f"(dates: {synth_df['trade_date'].nunique()})")
206+
207+
ic_series = calc_ic(synth_df, target_df)
208+
209+
metrics = calc_ic_metrics(ic_series)
210+
_info(f" IC Mean : {metrics['ic_mean']:>+.4f}")
211+
_info(f" IC Std : {metrics['ic_std']:>.4f}")
212+
_info(f" ICIR : {metrics['icir']:>+.4f}")
213+
214+
save_path = PLOTS_DIR / f"synthetic_factor_ic.png"
215+
plot_ic(ic_series, factor_name="synthetic_factor", show=SHOW_PLOTS, save_path=save_path)
216+
_info(f" IC chart saved.")
217+
218+
bt_synth = LayeredBacktester(synth_df, target_df, forward_days=FORWARD_DAYS, plots_dir=PLOTS_DIR)
219+
perf_synth = bt_synth.run_backtest()
220+
_info(" Backtest metrics (synthetic factor):")
221+
_p(perf_synth.to_string())
222+
bt_synth.plot(show=SHOW_PLOTS)
223+
_info(" Synthetic backtest plot saved.")
221224

222225
_p(f"\n{'=' * 62}")
223226
_p(" Phase 2 factor analysis complete.")

plots/shap_beeswarm.png

259 Bytes
Loading

result.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,9 @@ L-S -0.6170 -0.1807 0.1039 -2.0284 -0.6419
279279
* alpha101
280280

281281
==============================================================
282-
Step 5 / 5 — Synthetic factor (rolling OLS + symmetric orthogonalization)
282+
Step 5 / 5 — Synthetic factor
283283
==============================================================
284-
[ ] Effective alphas (IC/ICIR screen, for reference): ['alpha038', 'alpha042', 'alpha054', 'alpha101']
284+
[ ] Effective alphas: ['alpha038', 'alpha042', 'alpha054', 'alpha101']
285285
[ ] Rolling window = 3 trading days | orthogonalize = True
286286
[ ] OLS target: cross-sectional pct-rank of forward_return (per trade_date)
287287
[OK] Synthetic factor : 356,919 rows (dates: 1209)

src/targets.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ def calc_forward_return(prices_df: pd.DataFrame, d: int) -> pd.DataFrame:
2626
"""
2727
close_wide = prices_df.pivot(index="trade_date", columns="ts_code", values="close")
2828

29+
# open_wide = prices_df.pivot(index="trade_date", columns="ts_code", values="open")
30+
2931
# shift(-d) brings future price back to current index position
3032
fwd_wide = close_wide.shift(-d) / close_wide - 1
3133

0 commit comments

Comments
 (0)