LUCIDA:如何利用多因子策略構建強大的加密資產投資組合(因子有效性檢驗篇)

2024-01-24 16:01:08

前言

書接上回,關於《用多因子模型構建強大的加密資產投資組合》系列文章中,我們已經發布了兩篇: 《理論基礎篇》 、 《數據預處理篇》

本篇是第三篇:因子有效性檢驗。

在求出具體的因子值後,需要先對因子進行有效性檢驗,篩選符合顯著性、穩定性、單調性、收益率要求的因子;因子有效性檢驗通過分析本期因子值與預期收益率的關系,從而確定因子的有效性。主要有 3 種經典方法:

  • IC / IR 法:IC / IR 值為因子值與預期收益率的相關系數,越大因子表現越好。

  • T 值(回歸法):T 值體現下期收益率對本期因子值线性回歸後系數的顯著性,通過比較該回歸系數是否通過 t 檢驗,來判斷本期因子值對下期收益率的貢獻程度,通常用於多元(即多因子)回歸模型。

  • 分層回測法:分層回測法基於因子值對 token 分層,再計算每層 token 的收益率,從而判斷因子的單調性

一、IC / IR 法

(1)IC / IR 的定義

IC:即信息系數 Information Coefficient,代表因子預測 Tokens 收益的能力。某一期 IC 值為本期因子值和下期收益率的相關系數。

IC 越接近 1 ,說明因子值和下期收益率的正相關性越強,IC= 1 ,表示該因子選幣 100% 准確,對應的是排名分最高的 token,選出來的 token 在下個調倉周期中,漲幅最大;

IC 越接近-1 ,說明因子值和下期收益率的負相關性越強,如果 IC=-1 ,則代表排名分最高的 token,在下個調倉周期中,跌幅最大,是一個完全反指的指標;

若 IC 越接近 0 ,則說明該因子的預測能力極其弱,表明該因子對於 token 沒有任何的預測能力。

IR:信息比率 information ratio,代表因子獲取穩定 Alpha 的能力。IR 為所有期 IC 均值除以所有期 IC 標准差。

當 IC 的絕對值大於 0.05 (0.02) 時,因子的選股能力較強。當 IR 大於 0.5 時,因子穩定獲取超額收益能力較強。

(2)IC 的計算方式

  • Normal IC (Pearson correlation):計算皮爾森相關系數,最經典的一種相關系數。但該計算方式存在較多假設前提:數據連續,正態分布,兩個變量滿足线性關系等等。

  • Rank IC (Spearman's rank coefficient of correlation):計算斯皮爾曼秩相關系數,先對兩個變量排序,再根據排序後的結果求皮爾森相關系數。 斯皮爾曼秩相關系數評估的是兩個變量之間的單調關系,並且由於轉換為排序值,受數據異常值影響較小; 而皮爾森相關系數評估的是兩個變量之間的线性關系,不僅對原始數據有一定的前提條件,並且受數據異常值影響較大。在現實計算中,求 rank IC 更符合。

(3)IC / IR 法代碼實現

創建一個按日期時間升序排列的唯一日期時間值的列表--記錄調倉日期 def choosedate(dateList, cycle)

class TestAlpha(object):
def __init__(self, ini_data):
self.ini_data = ini_data
def chooseDate(self, cycle, start_date, end_date):
'''
cycle: day, month, quarter, year
df: 原始數據框 df,date 列的處理
'''
chooseDate = []
dateList = sorted(self.ini_data[self.ini_data['date'].between(start_date, end_date)]['date'].drop_duplicates().values)
dateList = pd.to_datetime(dateList)
for i in range(len(dateList)-1):
if getattr(dateList[i], cycle) != getattr(dateList[i + 1 ], cycle):
chooseDate.append(dateList[i])

chooseDate.append(dateList[-1 ])
chooseDate = [date.strftime('%Y-%m-%d') for date in chooseDate]
return chooseDate
def ICIR(self, chooseDate, factor):
# 1.先展示每個調倉日期的 IC,即 ICt
testIC = pd.DataFrame(index=chooseDate, columns=['normalIC','rankIC'])
dfFactor = self.ini_data[self.ini_data['date'].isin(chooseDate)][['date','name','price', factor]]
for i in range(len(chooseDate)-1):
# ( 1) normalIC
X = dfFactor[dfFactor['date'] == chooseDate[i]][['date','name','price', factor]].rename(columns={'price':'close 0'})
Y = pd.merge(X, dfFactor[dfFactor['date'] == chooseDate[i+ 1 ]][['date','name','price']], on=['name']).rename(columns={'price':'close 1'})
Y['returnM'] = (Y['close 1'] - Y['close 0']) / Y['close 0']
Yt = np.array(Y['returnM'])
Xt = np.array(Y[factor])
Y_mean = Y['returnM'].mean()
X_mean = Y[factor].mean()
num = np.sum((Xt-X_mean)*(Yt-Y_mean))
den = np.sqrt(np.sum((Xt-X_mean)** 2)*np.sum((Yt-Y_mean)** 2))
normalIC = num / den # pearson correlation
# ( 2) rankIC
Yr = Y['returnM'].rank()
Xr = Y[factor].rank()
rankIC = Yr.corr(Xr)
testIC.iloc[i] = normalIC, rankIC
testIC  =testIC[:-1 ]
# 2.基於 ICt,求['IC_Mean', 'IC_Std','IR','IC<0 佔比--因子方向','|IC|>0.05 比例']
'''
ICmean: |IC|>0.05, 因子的選幣能力較強,因子值與下期收益率相關性高。|IC|<0.05,因子的選幣能力較弱,因子值與下期收益率相關性低
IR: |IR|>0.5,因子選幣能力較強, IC 值較穩定。|IR|<0.5, IR 值偏小,因子不太有效。若接近 0,基本無效
IClZero(IC less than Zero): IC<0 佔比接近一半->因子中性.IC>0 超過一大半,為負向因子,即因子值增加,收益率降低
ICALzpF(IC abs large than zero poin five): |IC|>0.05 比例偏高,說明因子大部分有效
'''
IR = testIC.mean()/testIC.std()
IClZero = testIC[testIC<0 ].count()/testIC.count()
ICALzpF = testIC[abs(testIC)>0.05 ].count()/testIC.count()
combined =pd.concat([testIC.mean(), testIC.std(), IR, IClZero, ICALzpF], axis= 1)
combined.columns = ['ICmean','ICstd','IR','IClZero','ICALzpF']
# 3.IC 調倉期內 IC 的累積圖
print("Test IC Table:")
print(testIC)
print("Result:")
print('normal Skewness:', combined['normalIC'].skew(),'rank Skewness:', combined['rankIC'].skew())
print('normal Skewness:', combined['normalIC'].kurt(),'rank Skewness:', combined['rankIC'].kurt())
return combined, testIC.cumsum().plot()

二、T 值檢驗(回歸法)

T 值法同樣檢驗本期因子值和下期收益率關系,但與 ICIR 法分析二者的相關性不同,t 值法將下期收益率作為因變量 Y,本期因子值作為自變量 X,由 Y 對 X 回歸,對回歸出因子值的回歸系數進行 t 檢驗,檢驗其是否顯著異於 0 ,即本期因子是否影響下期收益率。

該方法本質是對雙變量回歸模型的求解,具體公式如下:

(1)回歸法理論

(2)回歸法代碼實現

def regT(self, chooseDate, factor, return_ 24 h):
testT = pd.DataFrame(index=chooseDate, columns=['coef','T'])
for i in range(len(chooseDate)-1):
X = self.ini_data[self.ini_data['date'] == chooseDate[i]][factor].values
Y = self.ini_data[self.ini_data['date'] == chooseDate[i+ 1 ]][return_ 24 h].values
b, intc = np.polyfit(X, Y, 1) # 斜率
ut = Y - (b * X + intc)
# 求 t 值 t = (\hat{b} - b) / se(b)
n = len(X)
dof = n - 2 # 自由度
std_b = np.sqrt(np.sum(ut** 2) / dof)
t_stat = b / std_b
testT.iloc[i] = b, t_stat
testT = testT[:-1 ]
testT_mean = testT['T'].abs().mean()
testT L1 96 = len(testT[testT['T'].abs() > 1.96 ]) / len(testT)

print('testT_mean:', testT_mean)
print('T 值大於 1.96 的佔比:', testT L1 96)
return testT

三、分層回測法

分層指對所有 token 分層,回測指計算每層 token 組合的收益率。

(1)分層

首先獲取 token 池對應的因子值,通過因子值對 token 進行排序。升序排序,即因子值較小的排在前面,根據排序對 token 進行等分。第 0 層 token 的因子值最小,第 9 層 token 的的因子值最大。

理論上“等分”是指均等分拆 token 的個數,即每層 token 個數相同,借助分位數實現。現實中 token 總數不一定是層數的倍數,即每層 token 個數不一定相等。

(2)回測

將 token 按因子值升序分完 10 組後,开始計算每組 token 組合的收益率。該步驟將每層的 token 當成一個投資組合(不同回測期,每層的 token 組合所含的 token 都會有變化),並計算該組合整體的 下期收益率 。ICIR、t 值分析的是當期因子值和 下期整體的收益率 ,但分層回測需要計算 回測時間內每個交易日的分層組合收益率 。由於有很多回測期有很多期,在每一期都需要進行分層和回測。最後對每一層的 token 收益率進行累乘,計算出 token 組合的累積收益率。

理想狀態下,一個好的因子,第 9 組的曲线收益最高,第 0 組的曲线收益最低。

第 9 組減去第 0 組(即多空收益)曲线呈現單調遞增。

(3)分層回測法代碼實現

def layBackTest(self, chooseDate, factor):
f = {}
returnM = {}
for i in range(len(chooseDate)-1):
df 1 = self.ini_data[self.ini_data['date'] == chooseDate[i]].rename(columns={'price':'close 0'})
Y = pd.merge(df 1, self.ini_data[self.ini_data['date'] == chooseDate[i+ 1 ]][['date','name','price']], left_on=['name'], right_on=['name']).rename(columns={'price':'close 1'})
f[i] = Y[factor]
returnM[i] = Y['close 1'] / Y['close 0'] -1
labels = ['0','1','2','3','4','5','6','7','8','9']
res = pd.DataFrame(index=['0','1','2','3','4','5','6','7','8','9','LongShort'])
res[chooseDate[ 0 ]] = 1
for i in range(len(chooseDate)-1):
dfM = pd.DataFrame({'factor':f[i],'returnM':returnM[i]})
dfM['group'] = pd.qcut(dfM['factor'], 10, labels=labels)
dfGM = dfM.groupby('group').mean()[['returnM']]
dfGM.loc['LongShort'] = dfGM.loc['0']- dfGM.loc['9']res[chooseDate[i+ 1 ]] = res[chooseDate[ 0 ]] * ( 1 + dfGM['returnM']) data = pd.DataFrame({'分層累積收益率':res.iloc[: 10,-1 ],'Group':[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]})
df 3 = data.corr()
print("Correlation Matrix:")
print(df 3)
return res.T.plot(title='Group backtest net worth curve')

原文鏈接

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。

推薦文章

Meme修煉手冊:幾點是Meme神盤的黃金時間?

原創 | Odaily星球日報( @OdailyChina ) 作者|南枳( @Assassin_...

星球日報
7 13小時前

把握下一個千倍幣,Meme玩家必備掃鏈工具指南

最近一飛衝天的 meme 太多,風起雲湧一飛衝天的 meme 層出不窮。無論是賽道、還是想象空間,...

星球日報
7 13小時前

聚光燈外的OTC交易:解讀加密VC們的另類遊戲

當前加密貨幣風投的現狀 最近,Meme 幣的表現超過了許多由風險投資(VC)支持的項目,這引發了市...

星球日報
7 13小時前

Matrixport投研:BTC與黃金或將成為2025年機構資產配置的首選

第五次 BTC 牛市正以驚人的速度展开,目前 BTC 現在已經接近 100, 000 美元大關,並...

星球日報
7 13小時前

Filecoin:去中心化人工智能的基礎設施

近日,Fast Company 將 Filecoin Network 列入第四屆年度科技領域下一個...

星球日報
7 13小時前

pump直播妖魔化:一場由吉尼斯記錄創造者加速的鬧劇

原創 | Odaily星球日報( @OdailyChina ) 作者|南枳( @Assassin_...

星球日報
6 13小時前