
一句話回答動量輪動的核心是「每期只持有最強的 N 只定期把轉弱的換掉」。用 AlphaFeed 的klines.batch批量取前復權日線算出每只標的的動量如過去 60 日收益排名月末決策、次月開盤換倉避免未來函數(shù)扣手續(xù)費幾十行就能回測一個多標的輪動組合并和等權持有對比。為什么會有這個問題 / 傳統(tǒng)做法的痛點單只股票的擇時回測很常見但「一籃子標的按強弱輪動」才是 ETF 輪動、板塊輪動的真實玩法。難點在于① 要橫向比較多只的動量并排名②調倉時點要避免未來函數(shù)用當期數(shù)據(jù)決策、下一期開盤成交③ 換倉有手續(xù)費。把這三點處理好就是一個可信的輪動回測框架。分步驟解決每步配可運行代碼第 1 步批量取多只標的的前復權日線fromalphafeedimportAlphaFeedimportpandasaspd afAlphaFeed()# 讀取 ALPHAFEED_API_KEY# 一籃子候選示例用幾只寬基/行業(yè) ETF可換成任意股票池codes[510300.SH,510500.SH,159915.SZ,512880.SH,518880.SH]dataaf.klines.batch(codes,period1d,count500,adjustforward,to_dataframeTrue)# dict{symbol: DataFrame}# 拼成“收盤價寬表”行交易日列標的closespd.DataFrame({s:k.sort_values(trade_date).set_index(trade_date)[close]fors,kindata.items()}).dropna(howall)openspd.DataFrame({s:k.sort_values(trade_date).set_index(trade_date)[open]fors,kindata.items()})print(closes.tail(3))第 2 步算動量并取每月末的排名LOOKBACK60# 動量回看天數(shù)TOP_N2# 每期持有最強的 N 只momcloses.pct_change(LOOKBACK)# 過去 LOOKBACK 日收益動量idxpd.to_datetime(closes.index)month_endcloses.groupby([idx.year,idx.month]).tail(1).index# 每月最后交易日# 每個月末選動量最高的 TOP_N 只作為下月持倉holdings{}fordinmonth_end:rowmom.loc[d].dropna()holdings[d]list(row.nlargest(TOP_N).index)iflen(row)TOP_Nelse[]參數(shù)含義本文取值LOOKBACK動量回看窗口日60TOP_N每期持有只數(shù)2調倉頻率決策周期月度月末決策成交時點避免未來函數(shù)次月首個交易日開盤第 3 步次月開盤換倉計算帶手續(xù)費的組合凈值FEE0.0006# ETF 單邊約 0.06%無印花稅按實際調整dateslist(closes.index)me_listlist(month_end)equity1.0records[]prev_hold[]fori,dinenumerate(me_list[:-1]):holdholdings[d]# 下一個交易日次月開盤到下個月末持有 hold 等權startdates[dates.index(me_list[i])1]ifdates.index(me_list[i])1len(dates)elseNoneendme_list[i1]ifstartisNoneornothold:continue# 區(qū)間收益各持倉從 start 開盤 到 end 收盤 的等權收益seg[]forsinhold:p0opens[s].get(start);p1closes[s].get(end)ifpd.notna(p0)andpd.notna(p1):seg.append(p1/p0-1)retsum(seg)/len(seg)ifsegelse0.0# 換手成本與上期持倉不同的部分計費近似換手比例×2×FEEturnoverlen(set(hold)^set(prev_hold))/max(len(hold),1)ret-turnover*FEE equity*(1ret)records.append({調倉日:str(d)[:10],持倉:hold,區(qū)間收益:round(ret,4),凈值:round(equity,4)})prev_holdhold btpd.DataFrame(records)print(bt.tail(6).to_string(indexFalse))第 4 步與等權持有對比# 基準全部標的等權買入持有benchcloses.mean(axis1)bench_retbench.iloc[-1]/bench.iloc[0]-1strat_retequity-1print(f動量輪動累計{strat_ret:.2%}| 等權持有累計{bench_ret:.2%}| 調倉{len(bt)}次)關鍵坑與注意事項避免未來函數(shù)用月末數(shù)據(jù)決策次月開盤才換倉成交別用當期收盤價當期成交。前復權價跨除權日必須用adjustforward否則動量與收益失真。對齊交易日不同標的停牌會造成缺失拼寬表后按需dropna/前值處理注意別引入未來數(shù)據(jù)。手續(xù)費與換手輪動天然高換手務必扣費ETF 無印花稅、個股有按品種設FEE。樣本與幸存者偏差候選池應包含當時真實存在的標的別用「現(xiàn)在的贏家」回測歷史前視偏差。參數(shù)敏感LOOKBACK、TOP_N、調倉頻率對結果影響大需多組穩(wěn)健性測試避免過擬合。本文為簡化模型未完全模擬漲跌停無法成交、停牌、資金分配取整等實盤更保守。常見問題FAQQ為什么用月末決策、次月開盤成交A月末才知道當月動量排名用它決定下月持倉并在下月第一個交易日開盤成交——這樣不使用未來信息可真實執(zhí)行。Q動量用多久的回看窗口A常見 60/120/250 日。短窗口反應快但噪音大長窗口穩(wěn)但滯后。建議多測幾組看穩(wěn)健性。Q能用于股票池而不是 ETF 嗎A可以把codes換成股票代碼即可注意股票有印花稅、漲跌停與停牌FEE和成交假設要更保守。Q需要付費嗎A少量標的批量取數(shù)有免費額度大候選池 / 全市場輪動klines.batchuniverses需 Starter 及以上。詳見定價頁。小結動量輪動回測的關鍵不在選股邏輯而在框架的嚴謹性批量取前復權日線拼寬表、橫向排名選最強 N 只、月末決策次月開盤換倉避免未來函數(shù)、扣手續(xù)費、和等權持有對比。用 AlphaFeed 的klines.batch幾十行就能搭出這個多標的輪動回測框架客觀檢驗「追強棄弱」在你的標的池上是否有效。參考AlphaFeed Python SDK 文檔https://docs.alphafeed.org/zh-Hans/sdk/python-quickstartAlphaFeed 官網(wǎng)https://alphafeed.org/