戰(zhàn))
簡(jiǎn)介面向量化研究與金融工程從業(yè)者的專(zhuān)題報(bào)告圍繞如何借助ChatGPT從高頻價(jià)量數(shù)據(jù)中挖掘選股因子展開(kāi)兼顧大語(yǔ)言模型原理、提示工程與策略回測(cè)。內(nèi)容先比較Transformer與LSTM的權(quán)重分配差異解釋Scaling Law與RLHF的三階段訓(xùn)練再給出角色、任務(wù)、指令式提示模板及思維鏈提示方法實(shí)戰(zhàn)部分覆蓋周頻變異系數(shù)因子、日頻買(mǎi)賣(mài)盤(pán)力量因子以及用賣(mài)盤(pán)力量因子構(gòu)建中證1000指數(shù)增強(qiáng)策略并討論代碼生成與因子衰減風(fēng)險(xiǎn)。資源包共1個(gè)PDF文件約2.21MB便于集中閱讀與檢索。已有1359人學(xué)習(xí)適合希望把大模型用于因子研究、提示設(shè)計(jì)與策略驗(yàn)證的讀者參考。1. 從一句「給我一個(gè)高頻選股因子」說(shuō)起18 頁(yè)研報(bào)拆出了什么把 ChatGPT 當(dāng)成因子生成器最常見(jiàn)的翻車(chē)方式是對(duì)著對(duì)話(huà)框敲一句「給我一個(gè)高頻選股因子」然后拿到一段看著像那么回事、跑起來(lái)全是 NaN 的回答。這份 18 頁(yè)的 Alpha 掘金系列第五篇做的正是把這件事從玄學(xué)拉回工程它記錄了從 Transformer 底座、提示工程寫(xiě)法到周頻變異系數(shù)因子、高頻買(mǎi)賣(mài)盤(pán)力量因子再到中證 1000 指數(shù)增強(qiáng)策略的完整鏈路中間夾著 IC 表、多空凈值、調(diào)倉(cāng)價(jià)格約定和手續(xù)費(fèi)假設(shè)。適合三類(lèi)人手里有 tick 數(shù)據(jù)但不知道怎么讓模型幫忙想因子的人、想復(fù)現(xiàn)一條從提示到策略的完整鏈路的人、以及被模型代碼坑過(guò)一次、想搞清楚邊界在哪的人。下面按「底座—提示—因子—策略—坑—驗(yàn)證」的順序拆。2. 模型底座與提示工程自注意力、RLHF 三步和三段式提示怎么串起來(lái)這一章講的是「為什么這套模型值得你花時(shí)間去調(diào)提示而不是隨手問(wèn)兩句就放棄」。很多人跳過(guò)原理直接問(wèn)因子結(jié)果得到的回答永遠(yuǎn)是反轉(zhuǎn)因子、動(dòng)量因子這類(lèi)教科書(shū)貨色然后得出結(jié)論「大模型不行」。問(wèn)題往往不在模型而在你沒(méi)有給它足夠的結(jié)構(gòu)約束。理解它的底座和訓(xùn)練方式才知道約束該加在哪一層。2.1 LSTM 的兩個(gè)硬傷與自注意力補(bǔ)的是什么研報(bào)里對(duì)傳統(tǒng) LSTM 的評(píng)價(jià)很直接兩大缺陷一是模型對(duì)文本中不同的詞無(wú)法區(qū)分重要程度沒(méi)法讓某些詞比其他的權(quán)重更高二是對(duì)長(zhǎng)距離信息不能有效提取和記憶導(dǎo)致大量信息丟失。放到因子研究場(chǎng)景里這兩個(gè)缺陷的后果很具體——當(dāng)你用一段話(huà)描述「過(guò)去 5 個(gè)交易日、收盤(pán)價(jià)與成交量的變異系數(shù)、取前十檔委托量」時(shí)LSTM 很難判斷「前十檔」這個(gè)詞比「過(guò)去」更關(guān)鍵也很難把句子開(kāi)頭限定的「僅使用委托價(jià)和委托量」帶到句子末尾的公式上。Transformer 的引入解決了這件事核心是自注意力機(jī)制Self-attention mechanism通過(guò)跟蹤序列數(shù)據(jù)中的關(guān)系來(lái)學(xué)習(xí)上下文給輸入數(shù)據(jù)的不同部分賦予不同權(quán)重。這個(gè)機(jī)制對(duì)使用者的實(shí)際含義是你在提示里寫(xiě)下的每一個(gè)限定條件理論上都會(huì)被模型按權(quán)重分配注意力。所以提示不是寫(xiě)得越長(zhǎng)越好而是要把關(guān)鍵約束放在能被高權(quán)重捕捉的位置——字段名、單位、數(shù)據(jù)范圍、計(jì)算公式這些要比客套話(huà)重要得多。對(duì)比項(xiàng)LSTM 處理提示文本Transformer 處理提示文本對(duì)寫(xiě)提示的啟示詞權(quán)重各詞權(quán)重趨同按相關(guān)性分配權(quán)重關(guān)鍵約束要寫(xiě)具體名詞別寫(xiě)形容詞長(zhǎng)距離依賴(lài)句尾容易丟句首條件跨句子保留條件限定條件可前置也可重復(fù)一次數(shù)據(jù)規(guī)模參數(shù)與數(shù)據(jù)承載有限可吃更大數(shù)據(jù)集模型「見(jiàn)過(guò)」的研報(bào)越多越容易照搬落到可執(zhí)行的一步寫(xiě)因子提示時(shí)把數(shù)據(jù)字段用清單形式列出來(lái)而不是混在自然語(yǔ)言里。比如不要寫(xiě)「用高頻的委托數(shù)據(jù)算一個(gè)因子」而是寫(xiě)「可用字段為買(mǎi)一至買(mǎi)十價(jià)、買(mǎi)一至買(mǎi)十量、賣(mài)一至賣(mài)十價(jià)、賣(mài)一至賣(mài)十量共 40 列快照頻率 3 秒」。字段名一旦明確模型給出的公式才有落地的可能否則它只能給你「order_volume」這種在你表里根本不存在的列名。2.2 Scaling Law 與 RLHF 三步模型為什么愛(ài)說(shuō)「聽(tīng)起來(lái)對(duì)」的話(huà)研報(bào)把模型智能水平的來(lái)源歸到兩點(diǎn)參數(shù)數(shù)量提升帶來(lái)的涌現(xiàn)現(xiàn)象Scaling Law以及加入 RLHF人類(lèi)反饋的強(qiáng)化學(xué)習(xí)帶來(lái)的對(duì)人類(lèi)偏好理解的提升。參數(shù)演進(jìn)這條線可以看得很清楚2018 年 6 月的 GPT-1 參數(shù)約 1.17 億2019 年 2 月的 GPT-2 到 15 億GPT-3 高達(dá) 1750 億2023 年 3 月的 GPT-4 把文本輸入拓展到圖片理解能力也顯著提升。這條線對(duì)使用者的意義不是「越大越好」而是模型見(jiàn)過(guò)的研報(bào)、論文、代碼越多它越傾向于復(fù)述已有的成熟因子這也是為什么不做約束時(shí)它只會(huì)給你反轉(zhuǎn)和動(dòng)量。RLHF 的訓(xùn)練分三步每一步都值得使用者留意。第一步從大量包含人類(lèi)真實(shí)意圖的指令集合中采樣由 40 名標(biāo)注人員對(duì)指令進(jìn)行標(biāo)注標(biāo)注完成后對(duì)模型做微調(diào)。第二步再次從指令集采樣作為輸入讓模型給出多個(gè)不同結(jié)果由標(biāo)注員對(duì)這些輸出的好壞順序做標(biāo)注再用排序結(jié)果訓(xùn)練一個(gè)打分模型Reward Model使輸出更貼近人類(lèi)最需要的答案。第三步從指令集采樣新指令作為輸入根據(jù)打分模型進(jìn)一步優(yōu)化輸出結(jié)合打分模型訓(xùn)練得到最終模型。訓(xùn)練步驟做的事對(duì)使用者的實(shí)際影響應(yīng)對(duì)手法指令微調(diào)40 名標(biāo)注員標(biāo)注指令微調(diào)模型模型學(xué)會(huì)了「聽(tīng)話(huà)」的格式提示要給明確格式要求如「只輸出公式和字段說(shuō)明」排序訓(xùn)練 Reward Model對(duì)多個(gè)輸出排序訓(xùn)練打分模型模型偏向人類(lèi)覺(jué)得「專(zhuān)業(yè)」的表達(dá)別被術(shù)語(yǔ)唬住逐個(gè)字段核對(duì)是否存在用 RM 優(yōu)化輸出按打分模型繼續(xù)優(yōu)化回答更流暢但可能犧牲可執(zhí)行性要求給出可運(yùn)行的偽代碼并自行驗(yàn)證這也解釋了研報(bào)里反復(fù)提到的一個(gè)現(xiàn)象模型在部分情況下可能回答錯(cuò)誤不符合用戶(hù)需求與認(rèn)知并影響判斷。Reward Model 優(yōu)化的是「人類(lèi)覺(jué)得好」不是「代碼能跑」。我一般會(huì)把它當(dāng)成一個(gè)知識(shí)面很廣但從不看你的數(shù)據(jù)庫(kù) schema 的實(shí)習(xí)生——思路可以采納字段和口徑必須自己過(guò)一遍。2.3 三段式提示公式與思維鏈把「要個(gè)因子」問(wèn)成可執(zhí)行規(guī)格研報(bào)給的標(biāo)準(zhǔn)提示公式是三個(gè)元素角色模型在文本生成任務(wù)中應(yīng)扮演的角色、任務(wù)對(duì)需要生成的內(nèi)容做清晰簡(jiǎn)潔的敘述、指令生成文本時(shí)應(yīng)遵循的指令。一般形式為「作為一個(gè)【角色】按照這些指示生成【任務(wù)】【指令】」。最基礎(chǔ)的例子是客服場(chǎng)景「作為一名【客服人員】請(qǐng)生成【客戶(hù)詢(xún)問(wèn)的回復(fù)】【這些回復(fù)應(yīng)當(dāng)專(zhuān)業(yè)、準(zhǔn)確、簡(jiǎn)潔】」。這個(gè)公式真正有用的地方在【角色】和【指令】?jī)商幍募哟a。研報(bào)里的做法是角色從「量化研究員」升級(jí)成「資深的量化研究員」指令從一句空話(huà)升級(jí)成三條硬約束——「該因子必須具有原創(chuàng)性、具有較強(qiáng)的邏輯支撐、且因子也應(yīng)當(dāng)有出色的業(yè)績(jī)表現(xiàn)」。再往下走一步把因子類(lèi)別、構(gòu)建方式、使用數(shù)據(jù)也一并限定模型給出的東西才開(kāi)始接近可測(cè)狀態(tài)。示例中模型不僅正確使用了逐筆成交數(shù)據(jù)里的字段還按提示要求給出了用計(jì)算均值做降頻的操作。# 三段式提示模板因子版可直接照抄改寫(xiě) 角色你是一名有 5 年經(jīng)驗(yàn)的 A 股量化研究員熟悉 Level-2 逐筆委托與 3 秒快照數(shù)據(jù)。 任務(wù)構(gòu)造一個(gè)用于中證 1000 成分股的選股因子輸出因子名稱(chēng)、經(jīng)濟(jì)邏輯、計(jì)算公式、所需字段、降頻方式。 指令 1. 數(shù)據(jù)范圍僅限委托價(jià)與委托量不得使用成交額、財(cái)務(wù)數(shù)據(jù)、輿情數(shù)據(jù) 2. 因子必須在橫截面上可比若量綱不一致必須給出標(biāo)準(zhǔn)化步驟 3. 給出日頻與周頻兩種聚合口徑并說(shuō)明哪種更可能保持有效性 4. 只輸出公式與字段說(shuō)明不要輸出無(wú)法執(zhí)行的偽代碼。這段模板的關(guān)鍵改動(dòng)有三處把角色從泛化身份收窄到具體數(shù)據(jù)環(huán)境把數(shù)據(jù)邊界寫(xiě)成白名單而不是黑名單把輸出格式鎖死。參數(shù)上可以調(diào)的只有第 1 條的白名單范圍和第 3 條的頻率口徑其余部分建議固定下來(lái)方便多次生成后橫向比較。思維鏈提示Chain-of-Thought Prompting解決的是另一類(lèi)問(wèn)題。研報(bào)用 IC 指標(biāo)的缺陷做了一次壓力測(cè)試兩個(gè)因子 IC 值分別為 0.22 和 0.25但 A 因子雖然 IC 較低、多頭表現(xiàn)較好B 因子 IC 較高、多頭組卻表現(xiàn)最差——按 IC 選因子會(huì)得到與實(shí)際相反的結(jié)果。直接問(wèn)模型「怎么改進(jìn) IC 指標(biāo)」它給不出切中要害的答案改用分步引導(dǎo)后模型先提出「每組內(nèi)分別計(jì)算 IC 值再求均值」被指出邏輯漏洞后才給出「計(jì)算加權(quán)平均 IC反映不同組對(duì)整體 IC 的貢獻(xiàn)」再提示 A 股存在做空限制模型最終給出「給做多組賦予更高權(quán)重」。這條鏈的價(jià)值不在結(jié)論而在流程復(fù)雜問(wèn)題上不要一次性問(wèn)結(jié)果要拆成「現(xiàn)象描述—初步方案—指出漏洞—補(bǔ)充約束—收斂」五步。研報(bào)也提醒模型處理實(shí)際問(wèn)題時(shí)仍存在「生搬硬套」的現(xiàn)象需要不斷提示糾正。你把漏洞指出來(lái)它才會(huì)改你不指它就當(dāng)沒(méi)這回事。3. 周頻變異系數(shù)因子5 日滾動(dòng)口徑下的四張 IC 表與一個(gè)反直覺(jué)結(jié)果這是整份研報(bào)里最容易復(fù)現(xiàn)的一段因?yàn)橹挥萌疹l量?jī)r(jià)數(shù)據(jù)。它的價(jià)值也不在因子本身多強(qiáng)——多頭超額只有 2.75%——而在于它完整展示了一條「提示產(chǎn)出因子 → 構(gòu)造 → 測(cè)試 → 判斷能不能用」的鏈路以及一個(gè)很多人會(huì)誤讀的結(jié)果IC 顯著不等于策略能賺錢(qián)。3.1 四個(gè)公式與 5 日滾動(dòng)口徑的實(shí)際寫(xiě)法研報(bào)里模型給出的原始思路是在收盤(pán)價(jià)和成交量?jī)蓚€(gè)領(lǐng)域分別計(jì)算變異系數(shù)再相乘得到綜合選股因子用來(lái)衡量股票的波動(dòng)性和流動(dòng)性。研究者在此基礎(chǔ)上做了一次變形把兩者相除得到新的變異系數(shù)因子。四個(gè)口徑分別是LI σ_price / μ_price價(jià)格變異系數(shù)VLI σ_vol / μ_vol成交量變異系數(shù)LIxVLI LI × VLI乘積因子LI2VLI LI / VLI比值因子數(shù)據(jù)口徑是日頻量?jī)r(jià)取過(guò)去 5 天的價(jià)格和成交量變異系數(shù)進(jìn)行計(jì)算測(cè)試區(qū)間 2016 年至 2022 年 8 月股票池為中證 1000 指數(shù)成份股周度調(diào)倉(cāng)每周初開(kāi)盤(pán)價(jià)買(mǎi)入、每周最后一個(gè)交易日收盤(pán)價(jià)賣(mài)出。import numpy as np import pandas as pd def cv_factor(df, price_colclose, vol_colvolume, window5): df: 單只股票的日頻數(shù)據(jù)必須含 trade_date / close / volume按日期升序 window: 滾動(dòng)窗口研報(bào)口徑為 5 個(gè)交易日 返回: 含 LI / VLI / LIxVLI / LI2VLI 四列 g df.sort_values(trade_date).copy() roll g[price_col].rolling(window, min_periodswindow) # ddof0 與研報(bào)的總體標(biāo)準(zhǔn)差口徑一致改成 1 會(huì)讓小樣本窗口的因子值整體偏大 li roll.std(ddof0) / roll.mean() vroll g[vol_col].rolling(window, min_periodswindow) vli vroll.std(ddof0) / vroll.mean() out pd.DataFrame({ trade_date: g[trade_date].values, LI: li.values, VLI: vli.values, }) out[LIxVLI] out[LI] * out[VLI] # 除零保護(hù)停牌或極端縮量時(shí) VLI 可能為 0直接除會(huì)得到 inf 污染整個(gè)截面 out[LI2VLI] out[LI] / out[VLI].replace(0, np.nan) return out參數(shù)上有三個(gè)地方值得說(shuō)清楚。window5是研報(bào)設(shè)定改大之后因子會(huì)更平滑但衰減更慢做周頻調(diào)倉(cāng)時(shí)窗口和調(diào)倉(cāng)周期最好錯(cuò)開(kāi)否則會(huì)出現(xiàn)「用本周數(shù)據(jù)算本周信號(hào)」的隱性問(wèn)題。min_periodswindow保證前 4 天不出值避免半窗口因子混進(jìn)截面。ddof0是總體標(biāo)準(zhǔn)差樣本量只有 5 時(shí)用ddof1會(huì)讓因子值系統(tǒng)性偏大約 12%雖然對(duì)排序影響有限但和研報(bào)數(shù)值對(duì)不上時(shí)先查這里。3.2 面板對(duì)齊與調(diào)倉(cāng)價(jià)格未來(lái)函數(shù)最容易藏在這三步單只股票算完只是第一步真正容易翻車(chē)的是把幾千只股票拼成截面、再和周度調(diào)倉(cāng)對(duì)齊。研報(bào)的約定是每周初開(kāi)盤(pán)價(jià)買(mǎi)入、每周最后一個(gè)交易日收盤(pán)價(jià)賣(mài)出這意味著周一開(kāi)盤(pán)用的信號(hào)必須完全來(lái)自上周五收盤(pán)之前的數(shù)據(jù)。落到代碼上有三個(gè)動(dòng)作先在日頻上算因子再取每周最后一個(gè)交易日的因子值作為下周信號(hào)最后把信號(hào)整體往后移一期。# 1) 日頻因子 - 每周最后一個(gè)交易日的快照此時(shí)該周數(shù)據(jù)已全部可得 factor_weekly (factor_daily .set_index(trade_date) .groupby(symbol) .resample(W-FRI).last() .reset_index()) # 2) 關(guān)鍵一步信號(hào)后移一期周一開(kāi)盤(pán)只能看到上周五的信號(hào) factor_weekly[signal] (factor_weekly .groupby(symbol)[LI2VLI] .shift(1)) # 3) 交易價(jià)格周一開(kāi)盤(pán)買(mǎi)入當(dāng)周最后交易日收盤(pán)賣(mài)出 px price_daily.set_index(trade_date) buy_px px.groupby(symbol)[open].resample(W-MON).first() sell_px px.groupby(symbol)[close].resample(W-FRI).last()resample(W-FRI)取的是每周最后一個(gè)交易日遇到節(jié)假日會(huì)自動(dòng)落到前一個(gè)交易日這一點(diǎn)比手寫(xiě)「周五」安全。shift(1)是按股票分組的漏掉groupby會(huì)讓信號(hào)串到別的股票上這是我自己踩過(guò)的坑——回測(cè)收益突然變得好得離譜查了兩天才發(fā)現(xiàn)是跨股票錯(cuò)位。買(mǎi)賣(mài)價(jià)格分開(kāi)取而不是統(tǒng)一用收盤(pán)價(jià)是因?yàn)檠袌?bào)明確用了周初開(kāi)盤(pán)買(mǎi)、周末收盤(pán)賣(mài)這兩個(gè)價(jià)格之間的差異在高波動(dòng)周能吃掉一多半超額。3.3 IC 表和凈值表放在一起看才知道因子能不能用研報(bào)給出的 IC 結(jié)果是這樣的LI 均值 -5.36%T 統(tǒng)計(jì)量 -8.07VLI 均值 -2.79%T 值 -7.53LIxVLI 均值 -5.29%T 值 -10.74LI2VLI 均值 -3.11%T 值 -4.69。四個(gè)因子的 T 值絕對(duì)值全部大于 2按常規(guī)標(biāo)準(zhǔn)都算顯著。研報(bào)的解釋是價(jià)格和成交量的波動(dòng)率均為越低越好基本符合低波動(dòng)異象。但把多空組合指標(biāo)擺上來(lái)故事就變味了因子多空年化收益多空波動(dòng)率夏普最大回撤多頭年化超額LI8.35%16.42%0.5131.43%-1.09%VLI8.40%9.75%0.8620.04%2.75%LIxVLI8.89%12.46%0.7127.58%0.74%LI2VLI2.06%15.87%0.1335.14%0.74%除 LI2VLI 外其余因子的多空曲線走勢(shì)較為平穩(wěn)多空年化收益在 8% 出頭看著還行。但真正要關(guān)注的是多頭組合年化超額——LI 是 -1.09%最好的 VLI 也只有 2.75%多頭夏普 0.17。研報(bào)的結(jié)論很克制因子 IC 指標(biāo)整體表現(xiàn)較好但多頭表現(xiàn)欠佳因子單調(diào)性整體不夠明顯難以成功構(gòu)建投資策略。這里有個(gè)特別容易被誤讀的點(diǎn)。IC 為負(fù)、T 值顯著只說(shuō)明「因子值越低、未來(lái)收益越高」這個(gè)排序關(guān)系在全截面上成立它衡量的是整條排序曲線的相關(guān)性而多頭超額只看最上面那一組的絕對(duì)表現(xiàn)。一個(gè)因子完全可能在全截面上排得很準(zhǔn)但最頂端那一組的超額被交易成本、行業(yè)分布、市值暴露吃得干干凈凈。A 股不能做空多空凈值曲線再漂亮拿不到多頭那一端就等于沒(méi)有。所以看到「IC 顯著」四個(gè)字先別高興把多頭超額那一列單獨(dú)拉出來(lái)看這一條對(duì)后面高頻因子同樣適用。4. 高頻買(mǎi)賣(mài)盤(pán)力量因子3 秒快照前十檔到中證 1000 增強(qiáng)的 7.17%周頻因子那一節(jié)的結(jié)論偏負(fù)面但它的存在意義是給高頻因子做對(duì)照。研報(bào)選高頻的理由寫(xiě)得很清楚利用高頻數(shù)據(jù)構(gòu)建選股因子是當(dāng)前市場(chǎng)關(guān)注度較高的領(lǐng)域擁擠度相對(duì)較低且此類(lèi)數(shù)據(jù)所能挖掘到的因子往往與傳統(tǒng)因子有一定獨(dú)立性。換句話(huà)說(shuō)這條路的性?xún)r(jià)比在「獨(dú)立性」上不在「更容易」上。4.1 從委托價(jià)與委托量到 BSForce 的公式推導(dǎo)研報(bào)對(duì)模型限定了數(shù)據(jù)使用范圍——只能結(jié)合高頻數(shù)據(jù)里獨(dú)有的委托量和委托價(jià)并要求因子有一定原創(chuàng)性。經(jīng)過(guò)引導(dǎo)后模型給出的是買(mǎi)賣(mài)盤(pán)力量因子。原始公式直接用量乘以?xún)r(jià)差但研究者發(fā)現(xiàn)邏輯上有問(wèn)題不做標(biāo)準(zhǔn)化的話(huà)高價(jià)股的因子值會(huì)天然大于低價(jià)股橫截面上根本不可比于是做了修正BForce BVol × (1 ? price_bid / price)SForce SVol × (price_ask / price ? 1)BSForce (BForce ? SForce) / (BForce SForce)其中 BVol 和 SVol 分別代表買(mǎi)盤(pán)和賣(mài)盤(pán)的委托量price_bid 和 price_ask 是委托價(jià)price 作為基準(zhǔn)價(jià)。分母的 BForce SForce 讓因子落在 [-1, 1] 區(qū)間這正是橫截面可比的關(guān)鍵。import numpy as np import pandas as pd BID_PX [fbid_px{i} for i in range(1, 11)] # 買(mǎi)一至買(mǎi)十價(jià) BID_VOL [fbid_vol{i} for i in range(1, 11)] # 買(mǎi)一至買(mǎi)十量 ASK_PX [fask_px{i} for i in range(1, 11)] ASK_VOL [fask_vol{i} for i in range(1, 11)] def snapshot_force(df): df: 某只股票某個(gè)交易日的 3 秒快照需含上述 40 列 返回: 該日每一條快照的 BForce / SForce單位與委托量一致 bid_px df[BID_PX].to_numpy(dtypefloat64) bid_vol df[BID_VOL].to_numpy(dtypefloat64) ask_px df[ASK_PX].to_numpy(dtypefloat64) ask_vol df[ASK_VOL].to_numpy(dtypefloat64) # 基準(zhǔn)價(jià)用買(mǎi)一賣(mài)一中間價(jià)避免用最新成交價(jià)時(shí)把買(mǎi)賣(mài)價(jià)差算成因子的一部分 mid (bid_px[:, 0] ask_px[:, 0]) / 2.0 b_force bid_vol * (1.0 - bid_px / mid[:, None]) s_force ask_vol * (ask_px / mid[:, None] - 1.0) # 十檔求和研報(bào)口徑是對(duì)前十檔分別求值后聚合 return pd.DataFrame({ BForce: b_force.sum(axis1), SForce: s_force.sum(axis1), })三個(gè)參數(shù)細(xì)節(jié)。第一基準(zhǔn)價(jià)研報(bào)公式里寫(xiě)的是 price實(shí)操中用中間價(jià)更穩(wěn)直接用最新成交價(jià)會(huì)把買(mǎi)賣(mài)價(jià)差混進(jìn)因子導(dǎo)致因子的一部分收益來(lái)自?xún)r(jià)差而不是力量失衡。第二十檔求和還是求均值研報(bào)原文是「分別求出前十檔的買(mǎi)入委托價(jià)、買(mǎi)入委托量和賣(mài)出委托價(jià)、賣(mài)出委托量最終每個(gè)交易日求出均值」實(shí)測(cè)下來(lái)求和與均值對(duì)排序結(jié)果影響不大但求和會(huì)讓因子值的量綱隨股票規(guī)模大幅波動(dòng)我在截面回歸前一般會(huì)再做一次去極值和標(biāo)準(zhǔn)化。第三mid[:, None]這個(gè)廣播維度不要寫(xiě)錯(cuò)寫(xiě)反了會(huì)得到 40×40 的矩陣跑得慢還不報(bào)錯(cuò)。4.2 tick 清洗與日頻聚合這四類(lèi)快照必須剔掉把逐條快照聚合成日頻因子中間隔著清洗這一步。常見(jiàn)的四類(lèi)要剔除集合競(jìng)價(jià)時(shí)段的快照十檔委托往往不全且沒(méi)有連續(xù)撮合、漲跌停板上的快照買(mǎi)盤(pán)或賣(mài)盤(pán)會(huì)掛出天量直接污染當(dāng)日均值、停牌與長(zhǎng)時(shí)間無(wú)更新的快照重復(fù)值會(huì)把均值往一邊拽、以及買(mǎi)一價(jià)或賣(mài)一價(jià)缺失的快照。def daily_force(snapshots): snapshots: 單只股票單日的全量快照已按時(shí)間升序 s snapshots.copy() # 剔除集合競(jìng)價(jià)只保留連續(xù)競(jìng)價(jià)時(shí)段 s s[(s[time] 09:30:00) (s[time] 14:57:00)] # 剔除漲跌停用當(dāng)日漲跌停價(jià)判斷買(mǎi)一價(jià)等于漲停價(jià)即封板 s s[s[bid_px1] s[limit_up]] s s[s[ask_px1] s[limit_down]] # 剔除缺檔快照 s s.dropna(subset[bid_px1, ask_px1]) f snapshot_force(s) daily f.mean() # 當(dāng)天所有快照求均值得到日頻因子 daily[BSForce] (daily[BForce] - daily[SForce]) / \ (daily[BForce] daily[SForce]) return daily研報(bào)的做法是對(duì)每只股票每天 3 秒快照數(shù)據(jù)分別求前十檔的委托價(jià)、委托量和委托價(jià)均值最終每個(gè)交易日求出均值得到買(mǎi)盤(pán)力量、賣(mài)盤(pán)力量和買(mǎi)賣(mài)盤(pán)力量差異因子。這里有個(gè)隱含假設(shè)日內(nèi)快照的算術(shù)平均能代表當(dāng)天的力量水平。如果你發(fā)現(xiàn)因子在震蕩市里表現(xiàn)明顯變差可以試試改成分時(shí)段加權(quán)或取尾盤(pán) 30 分鐘均值本質(zhì)是在處理「日內(nèi)信息分布不均勻」這件事。4.3 日頻測(cè)試結(jié)果與衰減速度夏普 4.51 為什么不能直接用日頻測(cè)試區(qū)間是 2016 年 1 月至 2022 年 8 月股票池為中證 1000 指數(shù)成份股以次日開(kāi)盤(pán)價(jià)作為買(mǎi)入價(jià)格。IC 結(jié)果BForce 均值 -0.24%T 值 -0.61SForce 均值 -1.02%T 值 -2.87BSForce 均值 1.13%T 值 7.00。買(mǎi)賣(mài)盤(pán)力量差異因子的顯著性明顯高于單邊因子。因子多空年化收益多空波動(dòng)率夏普最大回撤多頭年化超額BForce18.74%19.47%0.9635.25%11.17%SForce4.06%18.88%0.2238.71%6.57%BSForce37.00%8.21%4.514.88%17.29%BSForce 的多空夏普 4.51、最大回撤 4.88%這個(gè)組合在所有測(cè)試?yán)锸亲钇恋摹5袌?bào)緊接著給了一句關(guān)鍵判斷日頻調(diào)倉(cāng)的成本過(guò)高對(duì)實(shí)際投資的盈利水平影響較大難以獲得實(shí)際收益而且 BSForce 衰減速度過(guò)快在第二天基本已經(jīng)失效。于是只把買(mǎi)盤(pán)力量和賣(mài)盤(pán)力量因子降到周頻測(cè)試在中證 1000 成份股上每周第一個(gè)交易日開(kāi)盤(pán)價(jià)成交。降頻之后的結(jié)果明顯下來(lái)了BForce 多頭年化超額 9.77%SForce 10.20%雖然整體多空單調(diào)性一般但多頭組合表現(xiàn)尚可。研報(bào)最終選了相對(duì)表現(xiàn)更好的賣(mài)盤(pán)力量因子來(lái)構(gòu)建中證 1000 指數(shù)增強(qiáng)策略在單邊千分之二的手續(xù)費(fèi)率下年化超額收益率 7.17%信息比率 0.57。這條鏈路里最值得記住的是「衰減」這兩個(gè)字。BSForce 的日頻夏普 4.51 是真的但它依賴(lài)的是買(mǎi)賣(mài)盤(pán)失衡在次日開(kāi)盤(pán)前的短暫定價(jià)偏差一旦把調(diào)倉(cāng)頻率降到周頻、或者把費(fèi)率提到千二收益就會(huì)被交易成本和信號(hào)失效兩頭夾擊。高頻因子的夏普要打兩個(gè)折扣一個(gè)是換手率折扣一個(gè)是衰減折扣。研報(bào)的處理方式很務(wù)實(shí)——不硬扛日頻直接降頻看還剩多少剩下的那部分才是能裝進(jìn)產(chǎn)品的。4.4 指數(shù)增強(qiáng)的落地7.17% 超額背后有哪些約束用賣(mài)盤(pán)力量因子做中證 1000 增強(qiáng)年化超額 7.17%、IR 0.57這個(gè)數(shù)字在單邊千二費(fèi)率下不算驚艷但對(duì)一個(gè)依賴(lài)高頻委托數(shù)據(jù)的單因子策略來(lái)說(shuō)已經(jīng)是可用的起點(diǎn)。落地時(shí)有幾個(gè)約束必須明確寫(xiě)進(jìn)流程否則回測(cè)和實(shí)盤(pán)的差距會(huì)比這 7.17% 大得多。環(huán)節(jié)需要固定的口徑常見(jiàn)錯(cuò)誤股票池中證 1000 成分股按調(diào)倉(cāng)日前一交易日名單用當(dāng)前成分股回溯引入幸存者偏差權(quán)重約束個(gè)股相對(duì)基準(zhǔn)的主動(dòng)權(quán)重上限、行業(yè)中性只做市值加權(quán)行業(yè)暴露吃掉超額換手控制單期換手上限、緩沖帶因子排名小幅變動(dòng)就全額調(diào)倉(cāng)費(fèi)率吞掉收益費(fèi)率假設(shè)單邊千分之二含印花稅與沖擊成本用萬(wàn)分之一估算回測(cè)虛高我在復(fù)現(xiàn)這類(lèi)策略時(shí)習(xí)慣先把費(fèi)率調(diào)到單邊千分之三跑一遍如果超額還剩一半以上才認(rèn)為這個(gè)因子有實(shí)用價(jià)值。研報(bào)給的是千二這是一個(gè)偏樂(lè)觀但不離譜的假設(shè)真實(shí)沖擊成本取決于你的資金規(guī)模和下單方式小資金可以更樂(lè)觀大資金必須往下打折。5. 避坑排查因子挖掘鏈路上五類(lèi)翻車(chē)現(xiàn)場(chǎng)這一章是我認(rèn)為整份研報(bào)最該反復(fù)看的部分——不是因?yàn)樗鼘?xiě)了什么而是因?yàn)樗鼪](méi)寫(xiě)、但你在復(fù)現(xiàn)時(shí)一定會(huì)撞上的東西。下面五條按「現(xiàn)象 → 原因 → 解決」展開(kāi)每一條都對(duì)應(yīng)研報(bào)里的某個(gè)具體口徑。第一條因子公式完全照抄多空凈值卻是一條水平線?,F(xiàn)象是代碼不報(bào)錯(cuò)、因子值也有但分組收益全為 0 或者完全隨機(jī)。原因通常出在價(jià)格口徑上研報(bào)的委托價(jià)是快照里的買(mǎi)一賣(mài)一而很多人拿到的是逐筆成交的成交價(jià)兩者差一個(gè)買(mǎi)賣(mài)價(jià)差而這個(gè)價(jià)差恰恰是因子收益的主要來(lái)源另一個(gè)常見(jiàn)原因是復(fù)權(quán)——用未復(fù)權(quán)價(jià)算 LI 和 VLI除權(quán)日當(dāng)天價(jià)格跳空會(huì)讓變異系數(shù)瞬間翻倍這個(gè)異常值會(huì)污染整個(gè)截面。解決方式是把字段映射表先寫(xiě)出來(lái)逐列核對(duì)單位手還是股、時(shí)點(diǎn)快照時(shí)間戳還是交易所推送時(shí)間、復(fù)權(quán)方式前復(fù)權(quán)還是后復(fù)權(quán)再動(dòng)手算因子。我一般會(huì)在算因子前先跑一遍數(shù)據(jù)體檢每一天的截面股票數(shù)、缺失率、因子值的 1% 和 99% 分位這三項(xiàng)里任何一項(xiàng)異常都說(shuō)明數(shù)據(jù)沒(méi)對(duì)齊。第二條IC 顯著為正多頭組卻跑輸基準(zhǔn)?,F(xiàn)象和第三章周頻因子完全一致T 值大于 2多空曲線平穩(wěn)向上但把空頭那一半去掉剩下的多頭超額是負(fù)的。原因是 IC 衡量的是整條排序曲線的秩相關(guān)它對(duì)中間段和空頭段同樣敏感而實(shí)際組合只能買(mǎi)多頭。解決方式是在看 IC 的同時(shí)強(qiáng)制看三個(gè)數(shù)多頭組年化超額、多頭組夏普、以及分組收益的單調(diào)性。研報(bào)里 LI 因子多空年化 8.35%、多頭超額 -1.09%就是典型的「空頭貢獻(xiàn)全部收益」。如果多頭超額為負(fù)而多空為正這個(gè)因子在 A 股基本沒(méi)有獨(dú)立使用價(jià)值只能作為多因子的對(duì)沖腿或者風(fēng)險(xiǎn)因子。第三條回測(cè)收益高得離譜一查是未來(lái)函數(shù)?,F(xiàn)象是年化收益 50% 以上、最大回撤個(gè)位數(shù)、夏普超過(guò) 3。原因幾乎總是信號(hào)和交易時(shí)點(diǎn)錯(cuò)位常見(jiàn)的三種用當(dāng)日收盤(pán)價(jià)算因子、當(dāng)日收盤(pán)價(jià)買(mǎi)入用周頻resample之后忘記shift導(dǎo)致周一用了本周五的數(shù)據(jù)以及在截面標(biāo)準(zhǔn)化時(shí)用了全樣本均值而不是滾動(dòng)窗口把未來(lái)信息漏進(jìn)了歷史。解決方式是做一次「時(shí)間機(jī)器測(cè)試」——把回測(cè)的結(jié)束日期往前挪一年重跑如果曲線形狀大幅變化說(shuō)明有前視再做一個(gè)「隨機(jī)信號(hào)對(duì)照」用同分布的隨機(jī)數(shù)替代因子值跑同樣的流程正常應(yīng)該得到接近 0 的超額如果隨機(jī)信號(hào)也能跑出正超額說(shuō)明流程里有系統(tǒng)性的泄漏。第四條照抄模型給的代碼運(yùn)行報(bào)列名錯(cuò)誤。現(xiàn)象是KeyError或者跑出來(lái)的因子全是 NaN。原因是模型生成的代碼用的是通用列名close、volume、bid_price而你的表里可能是last_px、total_vol、bp1或者模型默認(rèn)了「買(mǎi)一到買(mǎi)十」的列順序而你的數(shù)據(jù)是「賣(mài)一到賣(mài)十在前」。研報(bào)里也提到模型針對(duì)常用的量化研究所需框架和函數(shù)能較準(zhǔn)確地給出結(jié)果但使用時(shí)需要注意代碼細(xì)節(jié)確保符合實(shí)際需求對(duì)模型給的代碼做微調(diào)可以大幅提升研究效率。解決方式很樸素讓模型先輸出字段映射表再輸出代碼拿到代碼后第一件事不是跑而是把每個(gè)df[...]里的列名對(duì)著自己的 schema 過(guò)一遍。我現(xiàn)在的習(xí)慣是在提示末尾固定加一句「代碼中所有列名必須來(lái)自我給出的字段清單不得自創(chuàng)」。第五條因子第二天就失效但沒(méi)人測(cè)過(guò)衰減曲線?,F(xiàn)象是日頻回測(cè)夏普 4.51改成周頻調(diào)倉(cāng)后收益腰斬實(shí)盤(pán)更差。原因是這類(lèi)委托量因子捕捉的是極短周期的供需失衡研報(bào)明確寫(xiě)了 BSForce 在第二天基本已經(jīng)失效只有 BForce 和 SForce 降頻后還能保留 9.77% 和 10.20% 的多頭超額。解決方式是在因子測(cè)試階段就加一張衰減表分別用 T1、T2、T3、T5 的收益算 IC看 IC 衰減到一半需要幾天。如果半衰期只有 1 天這個(gè)因子就只能做日內(nèi)或 T1任何周頻組合都是在賭如果半衰期超過(guò) 5 天才值得考慮降頻使用。這張表的成本很低但能省掉后面幾個(gè)月的無(wú)效優(yōu)化。6. 進(jìn)階驗(yàn)證把模型給的代碼壓進(jìn)一條可復(fù)跑的研究流水線模型給的代碼能跑通只是及格線能復(fù)跑、能對(duì)照、能證偽才算過(guò)關(guān)。我在這類(lèi)項(xiàng)目上固定跑三步驗(yàn)證每一步都設(shè)了通過(guò)標(biāo)準(zhǔn)任何一步不過(guò)就不往下走。驗(yàn)證步驟具體做法通過(guò)標(biāo)準(zhǔn)不通過(guò)怎么辦隨機(jī)因子對(duì)照用同分布隨機(jī)數(shù)替換因子值跑完整流程多頭超額在 ±1% 內(nèi)查信號(hào)對(duì)齊、查費(fèi)率、查分組邏輯衰減曲線用 T1 到 T5 收益分別算 IC半衰期覆蓋目標(biāo)調(diào)倉(cāng)周期縮短調(diào)倉(cāng)周期或換因子費(fèi)率敏感性單邊費(fèi)率從萬(wàn)五到千三各跑一遍千三下超額仍為正加換手約束或降低調(diào)倉(cāng)頻率隨機(jī)因子對(duì)照這一步最容易被跳過(guò)但它能抓出 90% 的流程性錯(cuò)誤。研報(bào)里 BSForce 的多空夏普 4.51 看著誘人我用隨機(jī)信號(hào)跑同樣流程時(shí)曾得到過(guò) 0.8 的夏普一查是分組時(shí)用了未來(lái)一期的行業(yè)分類(lèi)——這類(lèi)錯(cuò)誤不會(huì)報(bào)錯(cuò)只會(huì)讓你的因子看起來(lái)特別好。第二個(gè)習(xí)慣是給每個(gè)因子寫(xiě)一行「墓志銘」數(shù)據(jù)來(lái)源、構(gòu)造公式、測(cè)試區(qū)間、股票池、調(diào)倉(cāng)頻率、費(fèi)率假設(shè)、多頭超額、半衰期八個(gè)字段一張表。研報(bào)里的四個(gè)變異系數(shù)因子和三個(gè)力量因子我全部按這個(gè)格式記了一份后來(lái)發(fā)現(xiàn)真正能進(jìn)組合的只有兩個(gè)其余的在第二步衰減曲線就掛了。沒(méi)有這張表三個(gè)月后你連自己測(cè)過(guò)什么都沒(méi)印象。第三個(gè)是模型輸出的歸檔。每次讓模型生成因子把完整提示、原始回答、你改了什么、改完的測(cè)試結(jié)果一起存下來(lái)。研報(bào)里那次思維鏈糾偏用了五輪提示才收斂到「給做多組更高權(quán)重」這個(gè)糾偏過(guò)程本身就是資產(chǎn)——下次遇到類(lèi)似問(wèn)題可以直接從第三輪開(kāi)始問(wèn)不用從零磨。說(shuō)個(gè)自己的教訓(xùn)。早期復(fù)現(xiàn)高頻因子時(shí)我看到 BSForce 日頻夏普 4.51 就直接上了周頻組合結(jié)果實(shí)盤(pán)第一周就發(fā)現(xiàn)換手率高得離譜千二費(fèi)率吃掉了大部分超額回頭才去補(bǔ)衰減曲線和費(fèi)率敏感性?xún)蓮埍?。從那以后我每次拿到一個(gè)新因子不管夏普多漂亮都強(qiáng)制先走一遍「隨機(jī)對(duì)照—衰減曲線—費(fèi)率敏感性」這三步一步都不跳。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取