序特征處理與LSTM:美股歷史數(shù)據(jù)預(yù)測(cè)全流程復(fù)盤)
做時(shí)序預(yù)測(cè)的人應(yīng)該都見過(guò)這種場(chǎng)面把美股日線數(shù)據(jù)原樣丟給LSTM訓(xùn)練集上的loss曲線漂亮得可以當(dāng)教科書配圖準(zhǔn)確率一度逼近90%可一換到測(cè)試集就原形畢露甚至趕不上“昨天漲了今天接著猜漲”的無(wú)腦策略。我一開始以為是模型結(jié)構(gòu)配錯(cuò)了層數(shù)、hidden size、dropout輪著調(diào)了小一個(gè)月后來(lái)才意識(shí)到問(wèn)題大頭根本不在神經(jīng)網(wǎng)絡(luò)而在數(shù)據(jù)準(zhǔn)備和特征構(gòu)造上。這篇文章是我基于雅虎財(cái)經(jīng)數(shù)據(jù)接口做美股歷史數(shù)據(jù)預(yù)測(cè)的全流程復(fù)盤核心就圍繞兩個(gè)關(guān)鍵詞時(shí)序特征處理和LSTM。我會(huì)把從原始K線到模型輸入特征、再到標(biāo)簽設(shè)計(jì)和最終回測(cè)評(píng)估的每個(gè)環(huán)節(jié)都拆開細(xì)講該給代碼給代碼該給坑位給坑位。適合正在入門金融時(shí)序預(yù)測(cè)的人也適合那些跑通了demo但結(jié)果不穩(wěn)定、想知道自己哪里做錯(cuò)的朋友。1. 項(xiàng)目定位預(yù)測(cè)漲跌方向比預(yù)測(cè)具體價(jià)格更有意義1.1 為什么我選了雅虎財(cái)經(jīng)數(shù)據(jù)接口做實(shí)驗(yàn)場(chǎng)做美股歷史數(shù)據(jù)研究數(shù)據(jù)源是第一道關(guān)卡。雅虎財(cái)經(jīng)的數(shù)據(jù)接口幾乎算是個(gè)人研究者的默認(rèn)選擇免費(fèi)、日頻覆蓋時(shí)間夠長(zhǎng)、字段完整Open、High、Low、Close、Volume一應(yīng)俱全還自帶復(fù)權(quán)后的調(diào)整價(jià)格。用Python生態(tài)里的開源封裝庫(kù)拉數(shù)據(jù)幾行代碼就能把十幾年的歷史數(shù)據(jù)整理成DataFrame比自己去爬行情網(wǎng)站或者申請(qǐng)商業(yè)數(shù)據(jù)要省太多事。當(dāng)然免費(fèi)的代價(jià)是偶爾會(huì)有接口限流和延遲但作為構(gòu)建預(yù)測(cè)模型的原料它的結(jié)構(gòu)和穩(wěn)定性完全夠用。更重要的是日線級(jí)別的數(shù)據(jù)天然適合LSTM這類序列模型來(lái)實(shí)驗(yàn)——樣本量不會(huì)少到?jīng)]法訓(xùn)練又保留了明顯的時(shí)序依賴結(jié)構(gòu)。我在這類項(xiàng)目里養(yǎng)成的習(xí)慣是先用雅虎數(shù)據(jù)把整個(gè)特征工程和模型框架跑通確認(rèn)信號(hào)有效之后再考慮要不要換更高頻或者更干凈的數(shù)據(jù)源。1.2 LSTM在這個(gè)項(xiàng)目里的真實(shí)定位很多人一聽到“用LSTM做股票預(yù)測(cè)”第一反應(yīng)就是“AI要跑贏市場(chǎng)了”。這個(gè)預(yù)期必須先校準(zhǔn)。金融時(shí)序數(shù)據(jù)是出了名的低信噪比有效信號(hào)可能就藏在幾個(gè)百分點(diǎn)的統(tǒng)計(jì)優(yōu)勢(shì)里任何模型都不可能穩(wěn)定地“預(yù)測(cè)明天漲還是跌”到很高的準(zhǔn)確率。LSTM在這里的角色不是點(diǎn)石成金的神器而是一個(gè)比傳統(tǒng)時(shí)序模型更能吸收多特征、更能捕捉長(zhǎng)期依賴的特征抽取器。舉個(gè)例子ARIMA這類線性模型的強(qiáng)項(xiàng)是單變量時(shí)間序列的自回歸關(guān)系面對(duì)多變量交叉作用和突變型的非線性模式非常吃力。LSTM通過(guò)門控機(jī)制選擇性地記住和遺忘信息可以把過(guò)去十幾天甚至幾十天的波動(dòng)狀態(tài)、動(dòng)量變化揉進(jìn)當(dāng)前預(yù)測(cè)里這是它比傳統(tǒng)模型更適配這個(gè)場(chǎng)景的核心原因。所以整條技術(shù)路線可以表述為用特征工程把原始價(jià)格數(shù)據(jù)變成有金融含義的輸入再用LSTM去挖掘其中跨時(shí)間的非線性規(guī)律最后以方向預(yù)測(cè)的勝率作為真實(shí)評(píng)價(jià)標(biāo)準(zhǔn)。1.3 項(xiàng)目完整流程速覽整個(gè)項(xiàng)目大致分成六個(gè)環(huán)節(jié)環(huán)環(huán)相扣任何一環(huán)偷懶都會(huì)在最終結(jié)果上現(xiàn)形數(shù)據(jù)獲取與清洗拉取日線數(shù)據(jù)處理復(fù)權(quán)、時(shí)區(qū)、缺失值。特征構(gòu)造從原始價(jià)格轉(zhuǎn)向收益率、滾動(dòng)統(tǒng)計(jì)、技術(shù)指標(biāo)、相對(duì)強(qiáng)弱等特征。標(biāo)簽設(shè)計(jì)定義未來(lái)N日的漲跌方向標(biāo)簽。訓(xùn)練集/驗(yàn)證集/測(cè)試集切分嚴(yán)格按時(shí)間順序禁止隨機(jī)打亂。LSTM模型搭建窗口樣本生成、歸一化、網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)和訓(xùn)練。評(píng)測(cè)與回測(cè)方向準(zhǔn)確率、AUC、模擬持倉(cāng)策略并重點(diǎn)排查預(yù)測(cè)滯后問(wèn)題。下面按這個(gè)順序把每個(gè)環(huán)節(jié)里我實(shí)際踩過(guò)的坑和有效做法一一說(shuō)出來(lái)。2. 拉數(shù)據(jù)與清洗復(fù)權(quán)、時(shí)區(qū)和停牌才是第一道坎2.1 字段里最容易被忽略的是復(fù)權(quán)價(jià)格用雅虎財(cái)經(jīng)的數(shù)據(jù)接口拉數(shù)據(jù)時(shí)最基礎(chǔ)的代碼長(zhǎng)這樣import yfinance as yf df yf.download( XXX, # 這里填股票代碼 start2015-01-01, end2025-01-01, auto_adjustTrue, ) print(df.columns)很多人拿到數(shù)據(jù)以后只看Close列忽略了一個(gè)致命細(xì)節(jié)當(dāng)上市公司分紅或者拆股時(shí)股價(jià)會(huì)出現(xiàn)一個(gè)不自然的跳空缺口。如果沒(méi)有做復(fù)權(quán)處理模型會(huì)把這個(gè)因?yàn)榉旨t引起的價(jià)格跳空當(dāng)成一次真實(shí)的暴跌或者暴漲特征序列里就會(huì)長(zhǎng)出一堆假信號(hào)。常見的做法是打開自動(dòng)復(fù)權(quán)參數(shù)這樣拿到的Close實(shí)際上是“后復(fù)權(quán)/前復(fù)權(quán)處理過(guò)的價(jià)格”或者單獨(dú)使用數(shù)據(jù)里的復(fù)權(quán)收盤價(jià)列。我強(qiáng)烈建議在任何歷史回測(cè)里面都使用復(fù)權(quán)價(jià)格否則股息除權(quán)日前后模型的預(yù)測(cè)錯(cuò)誤率會(huì)高得離譜而且這類錯(cuò)誤往往很隱蔽因?yàn)槔塾?jì)收益曲線看起來(lái)只是多了一兩次小回撤。2.2 時(shí)區(qū)問(wèn)題別讓“今天”的數(shù)據(jù)跑進(jìn)昨天的窗口美股交易是美東時(shí)區(qū)北京時(shí)間跟它有12到13個(gè)小時(shí)的時(shí)差。雅虎財(cái)經(jīng)返回的日線數(shù)據(jù)默認(rèn)是美東日歷日對(duì)齊的也就是說(shuō)每個(gè)交易日只產(chǎn)出一根K線這一根K線代表的是美東當(dāng)天完整交易時(shí)段。這里有個(gè)隱藏的坑如果你習(xí)慣在盤前用“當(dāng)天”數(shù)據(jù)做預(yù)測(cè)拿到的可能是一個(gè)未收盤的不完整序列而如果你把它當(dāng)成已收盤數(shù)據(jù)來(lái)訓(xùn)練特征窗口和標(biāo)簽之間就等于混入了未來(lái)信息。從實(shí)踐角度最穩(wěn)妥的做法是訓(xùn)練和預(yù)測(cè)統(tǒng)一使用“收盤后”的數(shù)據(jù)時(shí)間戳并且明確把特征序列截?cái)嘣诮灰兹誘標(biāo)簽對(duì)應(yīng)TN的未來(lái)收益。不要使用盤中實(shí)時(shí)數(shù)據(jù)去套用收盤模型也不要因?yàn)闀r(shí)差把當(dāng)前這根不完整的K線納入特征。這個(gè)問(wèn)題在構(gòu)建任何真實(shí)策略時(shí)都很致命但在不少教程里都被一筆帶過(guò)。2.3 停牌、長(zhǎng)假和缺失值的處理方案美股雖然交易日密集但依然有假日休市和各種突發(fā)停牌。如果直接用數(shù)據(jù)接口返回的結(jié)果可能某一天就是憑空消失的而不是空值。這會(huì)帶來(lái)兩個(gè)問(wèn)題一是LSTM的樣本必須是連續(xù)時(shí)間步中間缺一天序列結(jié)構(gòu)就斷了二是如果使用重采樣把非交易日強(qiáng)行補(bǔ)進(jìn)去等于塞入大量無(wú)意義的填充數(shù)據(jù)。我習(xí)慣的做法是這樣的先拉取目標(biāo)時(shí)段的完整交易日列表用這個(gè)列表去做數(shù)據(jù)對(duì)齊缺失的交易日直接剔除保留連續(xù)的交易日序列如果數(shù)據(jù)里出現(xiàn)NaN再細(xì)分處理缺失場(chǎng)景推薦處理方式原因單日停牌導(dǎo)致個(gè)別字段缺失前向填充并增加一個(gè)“是否缺失”標(biāo)記特征保持序列連續(xù)同時(shí)讓模型知道當(dāng)天沒(méi)有成交連續(xù)多日無(wú)交易數(shù)據(jù)剔除或單獨(dú)標(biāo)記不做后向填充后向填充等于偷看未來(lái)信息指標(biāo)計(jì)算時(shí)產(chǎn)生的開頭缺失直接刪除這些不完整窗口窗口數(shù)量足夠不值得冒險(xiǎn)補(bǔ)值補(bǔ)上“是否缺失”這個(gè)標(biāo)記特征是我覺(jué)得很有用的小技巧。這樣一來(lái)LSTM能主動(dòng)學(xué)習(xí)“某天沒(méi)有交易”這個(gè)事件本身的影響力而不是單純把它當(dāng)成一個(gè)噪聲。3. 特征構(gòu)造把K線變成LSTM真正看得懂的信號(hào)3.1 第一層從原始價(jià)格到收益率序列原始價(jià)格序列有一個(gè)天然缺陷非平穩(wěn)。十年前某股票的價(jià)格是5美元現(xiàn)在是200美元模型如果直接在價(jià)格數(shù)字上學(xué)規(guī)律就需要同時(shí)適應(yīng)完全不同的數(shù)值量級(jí)還得額外處理長(zhǎng)期通脹和公司成長(zhǎng)帶來(lái)的漂移。一個(gè)更自然的選擇是把價(jià)格轉(zhuǎn)換成收益率序列也就是每天的變化比例或者取對(duì)數(shù)差。收益序列在統(tǒng)計(jì)性質(zhì)上平穩(wěn)得多也更加接近金融資產(chǎn)交易的本質(zhì)——我們賺的本來(lái)就不是“價(jià)格數(shù)字”而是“價(jià)格變化”。我實(shí)際使用的收益率有兩類單日收益率ret_1 close.pct_change()描述隔夜到當(dāng)日的邊際變化。多周期動(dòng)量ret_5 close.pct_change(5)、ret_20 close.pct_change(20)分別代表一周和一個(gè)月區(qū)間的累計(jì)漲跌幅。多周期收益率的意義在于給模型提供了不同時(shí)間尺度的“動(dòng)量狀態(tài)”日線級(jí)別的短期趨勢(shì)往往噪聲大而20日收益能反映更穩(wěn)定的中期方向。LSTM雖然有記憶能力但讓模型自己從原始價(jià)格里硬學(xué)出“20日動(dòng)量”這個(gè)概念需要相當(dāng)多的數(shù)據(jù)和參數(shù)直接作為特征喂進(jìn)去等于把人類已有的金融常識(shí)預(yù)置給模型訓(xùn)練負(fù)擔(dān)小得多效果也更穩(wěn)。3.2 第二層滾動(dòng)統(tǒng)計(jì)特征與技術(shù)指標(biāo)第二層特征是我花時(shí)間最多的地方也是這個(gè)項(xiàng)目里“時(shí)序特征處理”的核心部分。光有收益率還不夠市場(chǎng)狀態(tài)不僅體現(xiàn)在漲跌幅本身還體現(xiàn)在波動(dòng)率、價(jià)格所處區(qū)間、短期動(dòng)能是否衰竭這些維度。我常用的是下面這幾類滾動(dòng)波動(dòng)率rolling_std ret_1.rolling(20).std()。金融數(shù)據(jù)有明顯的波動(dòng)聚集現(xiàn)象高波動(dòng)后面往往還是高波動(dòng)低波動(dòng)后面往往還是低波動(dòng)。這個(gè)特征對(duì)判斷“當(dāng)前市場(chǎng)是否處于劇烈分化期”非常有效。均線偏離度(close - close.rolling(20).mean()) / close.rolling(20).mean()。價(jià)格偏離中期均線的程度可以近似表示短期情緒是不是過(guò)熱或者過(guò)度悲觀。布林帶位置把價(jià)格在布林帶中的相對(duì)位置算出來(lái)即(close - 下軌) / (上軌 - 下軌)。這個(gè)指標(biāo)本質(zhì)上是把波動(dòng)率和趨勢(shì)融合在一起數(shù)值在0到1之間可以直觀表達(dá)“價(jià)格處在統(tǒng)計(jì)區(qū)間的什么位置”。RSI類動(dòng)能指標(biāo)經(jīng)典的相對(duì)強(qiáng)弱指標(biāo)可以自己用滾動(dòng)平均實(shí)現(xiàn)用來(lái)刻畫一段時(shí)間內(nèi)上漲力量和下跌力量的對(duì)比。我通常取14日窗口。這些特征不需要引入額外的金融數(shù)據(jù)庫(kù)純pandas就能算完。關(guān)鍵是理解它們背后的直覺(jué)LSTM本質(zhì)上是一個(gè)函數(shù)逼近器你喂給它的特征里如果已經(jīng)包含了“波動(dòng)率聚集”“價(jià)格偏離均值”這些被市場(chǎng)反復(fù)驗(yàn)證的規(guī)律它學(xué)起來(lái)會(huì)比從零開始摸索高效得多。3.3 第三層相對(duì)強(qiáng)弱與周期編碼這個(gè)項(xiàng)目里還有一個(gè)很容易被忽略的維度個(gè)股和市場(chǎng)整體的關(guān)系。在系統(tǒng)性行情里大盤漲的時(shí)候很多股票跟著漲大盤跌的時(shí)候再好的個(gè)股也難獨(dú)善其身。所以我會(huì)把標(biāo)的的收益率和同期大盤指數(shù)的收益率放在一起構(gòu)造一個(gè)相對(duì)收益特征相對(duì)收益ret_1 - 大盤ret_1。這個(gè)特征過(guò)濾掉市場(chǎng)整體的beta成分留下股票自己跑贏或跑輸大盤的部分。它對(duì)捕捉個(gè)股的alpha很有幫助。除此之外還有日歷效應(yīng)。雖然很多人覺(jué)得“星期一效應(yīng)”已經(jīng)消失了但作為特征星期幾和月份這些信息幾乎沒(méi)有成本。我推薦用周期性編碼而不是直接填整數(shù)比如把星期幾映射成兩個(gè)周期函數(shù)sin(2 * pi * weekday / 7)和cos(2 * pi * weekday / 7)。這樣設(shè)計(jì)的原因很簡(jiǎn)單星期一是1星期日是0如果用整數(shù)編碼模型會(huì)莫名其妙學(xué)到“數(shù)字越大越怎么樣”的無(wú)意義順序而用sin/cos編碼星期一和星期日之間的距離在數(shù)值上又很接近符合“周末相鄰”的真實(shí)含義。3.4 特征工程的減法原則與泄漏邊界我見過(guò)不少項(xiàng)目把特征堆到三四十個(gè)結(jié)果訓(xùn)練集表現(xiàn)尚可測(cè)試集直接崩盤。核心原因之一就是特征太多、太冗余LSTM的參數(shù)量會(huì)被迫膨脹過(guò)擬合風(fēng)險(xiǎn)急速上升。以日線級(jí)別的單標(biāo)的預(yù)測(cè)來(lái)說(shuō)我個(gè)人經(jīng)驗(yàn)是15到20個(gè)特征已經(jīng)非常充裕關(guān)鍵是特征之間的互補(bǔ)性和信息增量而不是數(shù)量。還有一個(gè)所有做時(shí)序特征的人都必須警惕的雷區(qū)滾動(dòng)特征計(jì)算時(shí)的未來(lái)信息泄漏。比如要計(jì)算當(dāng)天的20日均線只能用截止到當(dāng)天收盤的數(shù)據(jù)也就是必須用rolling(20).mean().shift(0)——這個(gè)沒(méi)問(wèn)題但如果操作不當(dāng)用了包含未來(lái)幾天數(shù)據(jù)的窗口去算指標(biāo)模型就等于在訓(xùn)練時(shí)偷看了“未來(lái)的收盤價(jià)”測(cè)試時(shí)自然被打回原形。注意凡是涉及滾動(dòng)窗口的特征一定要在心里畫一條時(shí)間軸反復(fù)確認(rèn)“當(dāng)前時(shí)刻T這個(gè)特征值是否只依賴T以及T以前的真實(shí)數(shù)據(jù)”。別讓任何未來(lái)數(shù)據(jù)偷偷爬進(jìn)你的訓(xùn)練特征。4. 標(biāo)簽設(shè)計(jì)與數(shù)據(jù)切分時(shí)序泄漏比模型錯(cuò)誤更容易毀掉結(jié)果4.1 回歸連續(xù)價(jià)格還是分類漲跌方向剛開始做這個(gè)項(xiàng)目時(shí)我試過(guò)直接預(yù)測(cè)“下一天的收盤價(jià)”損失函數(shù)用MSE。結(jié)果是訓(xùn)練過(guò)程看起來(lái)很順暢預(yù)測(cè)曲線跟真實(shí)價(jià)格曲線簡(jiǎn)直貼在一起準(zhǔn)確率感人。但我后來(lái)意識(shí)到這完全是錯(cuò)覺(jué)價(jià)格序列的自相關(guān)極強(qiáng)模型只要把預(yù)測(cè)值做到“非常接近昨天的價(jià)格”MSE就已經(jīng)很小了。這本質(zhì)上是在做一個(gè)復(fù)制昨天的任務(wù)而不是在預(yù)測(cè)未來(lái)。這也是我后來(lái)轉(zhuǎn)而做“方向分類”的根本原因。更合理的標(biāo)簽不是連續(xù)價(jià)格而是未來(lái)h個(gè)交易日后的漲跌方向df[label] (df[close].shift(-h) df[close]).astype(int)h 1代表預(yù)測(cè)明天漲不漲h 5代表預(yù)測(cè)未來(lái)一周的方向是否向上。二分類是最簡(jiǎn)潔的設(shè)計(jì)也可以擴(kuò)展成三分類或五分類大漲、小漲、橫盤、小跌、大跌但類別越多樣本平衡和模型收斂就越難。我的建議是先從二分類入手把整個(gè)流程跑通再逐步增加標(biāo)簽粒度。4.2 訓(xùn)練集/驗(yàn)證集/測(cè)試集必須按時(shí)間順序切分做過(guò)普通機(jī)器學(xué)習(xí)的人一開始很可能會(huì)習(xí)慣性調(diào)用隨機(jī)切分函數(shù)這是時(shí)序預(yù)測(cè)項(xiàng)目里最毒的一個(gè)錯(cuò)誤。隨機(jī)切分會(huì)把2021年的樣本和2023年的樣本混在一起訓(xùn)練和驗(yàn)證造成一種“模型見過(guò)未來(lái)”的幻覺(jué)評(píng)估結(jié)果嚴(yán)重虛高。正確的做法是完全按時(shí)間順序切分前70%作為訓(xùn)練集中間15%作為驗(yàn)證集用來(lái)調(diào)超參數(shù)和做早停最后15%作為測(cè)試集只允許最后評(píng)估一次。更嚴(yán)格一點(diǎn)可以采用walk-forward驗(yàn)證把窗口往前滾動(dòng)重復(fù)“訓(xùn)練一段→驗(yàn)證預(yù)測(cè)下一段→把驗(yàn)證段納入訓(xùn)練”的過(guò)程。這樣能更真實(shí)地模擬模型在實(shí)戰(zhàn)中的更新節(jié)奏。對(duì)于起步項(xiàng)目簡(jiǎn)單的時(shí)間比例切分已經(jīng)能避免大部分自欺欺人的評(píng)估。4.3 三個(gè)最容易忽略的泄漏源數(shù)據(jù)泄漏在時(shí)序項(xiàng)目里是無(wú)孔不入的我至少真實(shí)翻車過(guò)三次總結(jié)下來(lái)最常見的三個(gè)來(lái)源第一歸一化在完整數(shù)據(jù)集上fit。合理的做法是只用訓(xùn)練集的統(tǒng)計(jì)量擬合標(biāo)準(zhǔn)化器然后對(duì)驗(yàn)證集和測(cè)試集只做transform。如果對(duì)整個(gè)數(shù)據(jù)集求最小值和最大值再做歸一化測(cè)試集的數(shù)值范圍信息就提前混進(jìn)了訓(xùn)練過(guò)程。第二特征里混入了未來(lái)數(shù)據(jù)。前面提到滾動(dòng)窗口計(jì)算時(shí)如果沒(méi)用對(duì)偏移量“未來(lái)幾天的價(jià)格”就會(huì)進(jìn)到當(dāng)天的特征里。這個(gè)坑往往非常隱蔽因?yàn)槟阒豢磫涡袛?shù)據(jù)根本發(fā)現(xiàn)不了必須寫代碼去抽樣核查特征和時(shí)間標(biāo)簽的對(duì)應(yīng)關(guān)系。第三缺失值填充用了后向填充。例如某一天停牌為了“讓序列連續(xù)”而用后一天的價(jià)格來(lái)填空這就是實(shí)打?qū)嵉奈磥?lái)數(shù)據(jù)泄漏。正確的做法是用前向填充或者干脆刪除該樣本。這三件事每一項(xiàng)都是“看起來(lái)沒(méi)什么大不了實(shí)際上能把結(jié)果從50%抬到60%甚至更高”的隱患。評(píng)估模型之前先把它們?nèi)颗挪楦蓛簟?. LSTM模型搭建窗口、門控和訓(xùn)練配置的組合邏輯5.1 為什么LSTM在這里確實(shí)能學(xué)到東西回到最核心的問(wèn)題特征和標(biāo)簽都準(zhǔn)備好了為什么用LSTM而不是普通全連接神經(jīng)網(wǎng)絡(luò)答案在于金融時(shí)序的“狀態(tài)依賴”特性。今天的市場(chǎng)行為往往跟過(guò)去幾個(gè)星期甚至幾個(gè)月的上下文有關(guān)——比如一個(gè)連續(xù)上漲后突然放量下跌的形態(tài)和一個(gè)長(zhǎng)期橫盤后的放量突破背后代表的狀態(tài)完全不同。如果只看今天的特征向量MLP根本區(qū)分不了這兩種情況。LSTM的核心是引入了細(xì)胞狀態(tài)和三個(gè)門控遺忘門、輸入門、輸出門。它可以在長(zhǎng)序列中決定哪些舊信息需要保留哪些新信息需要寫入。這就意味著模型有機(jī)會(huì)自己學(xué)會(huì)“注意過(guò)去的波動(dòng)率狀態(tài)”“記住過(guò)去一段時(shí)間的動(dòng)量”而不是把每個(gè)時(shí)間步孤立地看待。從工程角度說(shuō)LSTM在具有長(zhǎng)期依賴的多維時(shí)間序列上確實(shí)比MLP和普通RNN都更可靠。5.2 樣本窗口與三維輸入的數(shù)據(jù)結(jié)構(gòu)LSTM的輸入是三維張量(樣本數(shù)量, 時(shí)間步數(shù), 特征數(shù)量)。也就是說(shuō)模型不只看“今天”的特征還要看過(guò)去連續(xù)T天的特征組合。這個(gè)T就是滑動(dòng)窗口長(zhǎng)度。窗口長(zhǎng)度的選擇需要平衡兩個(gè)方向太短的窗口比如5天學(xué)不到中期背景太長(zhǎng)的窗口比如120天一方面會(huì)讓樣本數(shù)量大幅減少另一方面會(huì)把太遙遠(yuǎn)的、跟當(dāng)前關(guān)系不大的噪聲也裝進(jìn)來(lái)。以日線數(shù)據(jù)為例我實(shí)際操作下來(lái)感覺(jué)30到60個(gè)交易日是比較合理的區(qū)間既能覆蓋一個(gè)半月的市場(chǎng)背景又不至于讓序列過(guò)于笨重。構(gòu)造樣本的典型代碼這樣寫import numpy as np def make_sequences(features, labels, window40): X, y [], [] for i in range(len(features) - window): X.append(features[i : i window]) y.append(labels[i window]) return np.array(X), np.array(y)注意這里的對(duì)齊邏輯第i個(gè)樣本的輸入是i到i window - 1的數(shù)據(jù)對(duì)應(yīng)的標(biāo)簽是i window那一天的未來(lái)漲跌方向。這種錯(cuò)位是刻意為之的保證模型在預(yù)測(cè)時(shí)“看不到”自己需要預(yù)測(cè)的那一天。5.3 歸一化、網(wǎng)絡(luò)結(jié)構(gòu)與損失函數(shù)的選擇特征進(jìn)入LSTM之前必須先做歸一化否則不同特征之間的數(shù)值尺度差異比如價(jià)格是幾百收益率只有0.01會(huì)讓門控計(jì)算失衡。我通常對(duì)每個(gè)特征列分別做MinMax歸一化而且嚴(yán)格限定在訓(xùn)練集上擬合。網(wǎng)絡(luò)結(jié)構(gòu)方面我踩過(guò)很多次坑之后形成了一個(gè)感覺(jué)比較穩(wěn)的模板import torch.nn as nn class LSTMModel(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout, ) self.classifier nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, 1), ) def forward(self, x): out, _ self.lstm(x) out out[:, -1, :] # 取最后一個(gè)時(shí)間步的輸出 return self.classifier(out)這是一個(gè)兩層的LSTM加一個(gè)輕量分類頭。hidden_size取64在日線場(chǎng)景下通常是夠用的增大到128未必帶來(lái)收益反而會(huì)明顯拖慢訓(xùn)練并增加過(guò)擬合風(fēng)險(xiǎn)。最后一層用sigmoid輸出配合二元交叉熵?fù)p失。優(yōu)化器用Adam初始學(xué)習(xí)率1e-3附近比較合適。5.4 訓(xùn)練時(shí)容易遇到的三個(gè)實(shí)際問(wèn)題訓(xùn)練LSTM的過(guò)程里面有幾個(gè)小問(wèn)題幾乎人人都會(huì)遇到提前說(shuō)清楚能省很多精神內(nèi)耗。一是隨機(jī)種子問(wèn)題。同一個(gè)模型配置不同隨機(jī)種子跑出來(lái)的準(zhǔn)確率可能波動(dòng)兩三個(gè)百分點(diǎn)。這不是代碼寫錯(cuò)了是深度學(xué)習(xí)在小數(shù)據(jù)集上的正?,F(xiàn)象。應(yīng)對(duì)方式是固定所有隨機(jī)種子同時(shí)對(duì)一個(gè)配置跑3到5個(gè)不同種子取平均結(jié)果再下結(jié)論。二是類別不平衡問(wèn)題。雖然漲跌天數(shù)通常接近五五開但特定行情階段可能出現(xiàn)明顯的偏斜。如果發(fā)現(xiàn)訓(xùn)練集里上漲樣本占60%以上可以在損失函數(shù)里給少數(shù)類加上比重大于1的權(quán)重或者用分層抽樣來(lái)構(gòu)造訓(xùn)練批次。三是loss在初期紋絲不動(dòng)的情況。如果訓(xùn)練了十幾個(gè)epochloss曲線幾乎水平先不要急著加大學(xué)習(xí)率。先檢查數(shù)據(jù)是不是有問(wèn)題比如標(biāo)簽列是不是全是同一個(gè)值、特征是不是全為NaN、歸一化是不是做反了。這些基礎(chǔ)問(wèn)題排查完再考慮調(diào)整模型結(jié)構(gòu)。6. 評(píng)測(cè)與回測(cè)別讓LSTM的“滯后抄作業(yè)”騙了你6.1 預(yù)測(cè)滯后現(xiàn)象的識(shí)別與根因用LSTM做股票方向預(yù)測(cè)時(shí)最容易出現(xiàn)的一種“虛假成功”就是預(yù)測(cè)結(jié)果比真實(shí)漲跌滯后一拍。舉個(gè)例子假如把模型預(yù)測(cè)的概率值和真實(shí)累計(jì)收益畫在一起很多人會(huì)看到兩條曲線形狀高度類似只是預(yù)測(cè)曲線整體晚了一兩天。這時(shí)候如果只看相關(guān)性會(huì)覺(jué)得模型很厲害但實(shí)際上模型學(xué)到的是“今天的漲跌和明天的漲跌高度相關(guān)”這個(gè)簡(jiǎn)單的慣性規(guī)律本質(zhì)上只是在復(fù)制當(dāng)天的收益方向。怎么識(shí)別這個(gè)問(wèn)題一個(gè)簡(jiǎn)單有效的辦法是看模型在“反轉(zhuǎn)日”的表現(xiàn)假如今天是上漲日模型給出的明天上漲概率是多少假如今天是下跌日模型給出的明天上漲概率又是多少。如果模型對(duì)這兩類情況的預(yù)測(cè)幾乎完全相反而且準(zhǔn)確率主要來(lái)自順延行情基本可以斷定它陷入了滯后抄作業(yè)。緩解滯后現(xiàn)象的思路有三個(gè)一是把預(yù)測(cè)標(biāo)簽改為未來(lái)5日方向強(qiáng)迫模型不能只靠今天推斷明天二是在特征中增加更多與當(dāng)日收益方向正交的信息比如量?jī)r(jià)關(guān)系、波動(dòng)率變化三是增大dropout降低模型對(duì)單日自相關(guān)模式的依賴。從我的經(jīng)驗(yàn)看改標(biāo)簽周期效果最明顯代價(jià)是訓(xùn)練難度上升。6.2 用方向準(zhǔn)確率、AUC和策略回測(cè)一起評(píng)估模型的最終價(jià)值不應(yīng)該是“圖好看”而應(yīng)該是能不能在實(shí)際策略里產(chǎn)生正收益。我建立了一套三層評(píng)估體系第一層看分類指標(biāo)。方向準(zhǔn)確率超過(guò)55%在日線預(yù)測(cè)模型里已經(jīng)算可圈可點(diǎn)同時(shí)要關(guān)注AUCAUC能更好地衡量模型在不同閾值下的區(qū)分能力。第二層看預(yù)測(cè)概率的累計(jì)分布模型給出的上漲概率如果集中在0.4到0.6之間說(shuō)明模型整體很猶豫信號(hào)價(jià)值有限如果能相對(duì)分散說(shuō)明模型在不同市場(chǎng)狀態(tài)下有區(qū)分度。第三層也是最關(guān)鍵的一層做策略回測(cè)。一個(gè)最簡(jiǎn)單的回測(cè)方案是這樣當(dāng)預(yù)測(cè)上漲概率超過(guò)0.55時(shí)持有多頭倉(cāng)位當(dāng)?shù)陀?.45時(shí)空倉(cāng)或做空每天根據(jù)信號(hào)調(diào)倉(cāng)。計(jì)算累計(jì)收益時(shí)要考慮手續(xù)費(fèi)和滑點(diǎn)# signal: 1做多, -1做空, 0空倉(cāng) strategy_daily_return signal.shift(1) * asset_daily_return strategy_equity (1 strategy_daily_return.fillna(0)).cumprod() benchmark_equity (1 asset_daily_return.fillna(0)).cumprod()信號(hào)用shift(1)是因?yàn)楫?dāng)天的預(yù)測(cè)信號(hào)最早也只能在下一個(gè)交易日?qǐng)?zhí)行這個(gè)處理能避免前視偏差。對(duì)比策略凈值和“買入持有”基準(zhǔn)的差距才能真正說(shuō)明模型預(yù)測(cè)有沒(méi)有轉(zhuǎn)化為賺錢能力。6.3 我實(shí)測(cè)下來(lái)最值得優(yōu)化的三個(gè)方向做完整個(gè)項(xiàng)目之后如果讓我重新做一遍我會(huì)把時(shí)間優(yōu)先投入這三個(gè)方向第一從單標(biāo)的預(yù)測(cè)轉(zhuǎn)向多標(biāo)的聯(lián)合訓(xùn)練。把同一時(shí)間段內(nèi)多只股票的數(shù)據(jù)放在一起訓(xùn)練能顯著提升模型的泛化能力。不同股票之間共享的交易模式和波動(dòng)特征相當(dāng)于天然的“數(shù)據(jù)增強(qiáng)”而且模型還能學(xué)到一些跨標(biāo)的的相對(duì)現(xiàn)象。第二不要迷信LSTM本身把特征驗(yàn)證放在調(diào)參之前。每新增一個(gè)特征先在同樣的模型配置下回測(cè)對(duì)比如果策略收益沒(méi)有提升就果斷拿掉。這個(gè)流程比盲目堆疊網(wǎng)絡(luò)層數(shù)有意義得多。第三多試幾個(gè)標(biāo)簽周期和閾值策略。預(yù)測(cè)未來(lái)1日、5日、10日的信號(hào)規(guī)律往往完全不同不要默認(rèn)某一種就一定更好。用回測(cè)結(jié)果反推標(biāo)簽設(shè)計(jì)是金融時(shí)序預(yù)測(cè)里非常實(shí)用的一招。做了一圈實(shí)驗(yàn)下來(lái)我最大的感受是在這個(gè)項(xiàng)目里數(shù)據(jù)意識(shí)和時(shí)序敬畏感比任何花哨的模型結(jié)構(gòu)都重要。只要訓(xùn)練特征里混入了一點(diǎn)點(diǎn)未來(lái)信息再好的模型也會(huì)給你一個(gè)漂亮的假象而只有老老實(shí)實(shí)地把特征、標(biāo)簽、切分和評(píng)估每一環(huán)都做好LSTM的價(jià)值才真正顯現(xiàn)出來(lái)。順帶分享一個(gè)我自己的小習(xí)慣每次跑完一組實(shí)驗(yàn)我都會(huì)寫一行代碼把預(yù)測(cè)結(jié)果畫成散點(diǎn)圖標(biāo)出連續(xù)三日以上的預(yù)測(cè)正確區(qū)間——圖上看一眼模型是不是只會(huì)順著昨天的方向說(shuō)廢話比看一百行指標(biāo)報(bào)表都直觀。這個(gè)技巧看著簡(jiǎn)單真的能幫你避開很多自我感動(dòng)。