@@ -131,17 +131,25 @@ def init_db(self) -> None:
131131 # ------------------------------------------------------------------
132132
133133 def _get_constituents (self ) -> List [str ]:
134- """Return list of ts_codes for the configured universe index."""
135- end = datetime .now ()
136- start = end - timedelta (days = 31 )
134+ """Return the index constituents as of the backtest start date.
135+
136+ Queries a 31-day window anchored at config.START_DATE so that the
137+ universe reflects the index composition at the *beginning* of the
138+ study period. Using today's constituents would introduce look-ahead
139+ bias because the CSI 300 continuously replaces weaker stocks with
140+ stronger ones, leaking future information into the stock universe.
141+ """
142+ start_dt = datetime .strptime (config .START_DATE , "%Y%m%d" )
143+ end_dt = start_dt + timedelta (days = 31 )
137144 df = self .pro .index_weight (
138145 index_code = config .UNIVERSE_INDEX ,
139- start_date = start .strftime ("%Y%m%d" ),
140- end_date = end .strftime ("%Y%m%d" ),
146+ start_date = start_dt .strftime ("%Y%m%d" ),
147+ end_date = end_dt .strftime ("%Y%m%d" ),
141148 )
142149 if df is None or df .empty :
143150 raise RuntimeError (
144- f"No constituents returned for index '{ config .UNIVERSE_INDEX } '. "
151+ f"No constituents returned for index '{ config .UNIVERSE_INDEX } ' "
152+ f"around start date { config .START_DATE } . "
145153 "Check Tushare permissions (index_weight requires ~2000 points) "
146154 "or try '399300.SZ' instead of '000300.SH' in config.py."
147155 )
0 commit comments