2018 年 MSCI 发布了中国 A 股全市场股票模型。与传统的时间序列回归模型有所不同,多因子风险模型能够更高效准确地捕捉横截面上机构头寸在各种因子(包括市值等风格因子)上的暴露。并且当模型中纳入具有时序记忆的变量时,它可以共享截面回归和时序回归模型的一些优良性质。该模型采用多层次的因子体系,能够更精细地预测和解释中国股票市场的风险,对中国 A 股的风险评估、组合优化和量化策略产生了积极且广泛的影响。
本文将详细介绍通过 DolphinDB
1. 多因子风险模型简介
1.1 多因子风险模型
多因子风险模型基于多因子回归体系,将风格因子、市场因子和行业因子与收益率进行联合建模,以获取收益率和特质收益率。本文构建的多因子风险模型以 CNE6 模型为准。
CNE6 模型是面向中国股票市场的多因子模型。该模型考虑了一个国家因子、多个行业因子以及多个风格因子。假设市场中共有 N 支股票,P 个行业,以及 Q 个风格因子。在任意给定时间点,该模型使用因子暴露和个股收益率构建截面回归(cross-sectional regression)如下:

其中
1.2 多因子风险收益模型
多因子风险模型的核心目的在于准确评估个股和因子的风险,并通过时序的收益风险评估辅助投资判断。基于 CNE6 模型,可以简化个股收益率为因子收益率和个股特异性收益率的线性组合。其中,
对上述等式左右两边分别求协方差矩阵得到如下公式,将个股协方差矩阵
若从股票收益率的协方差矩阵的角度评估风险,可能存在由于股票数 N 远大于交易日期数 252 导致股票收益率协方差矩阵满秩的问题,且需要计算 N*(N+1)/2 次,复杂度很高。基于多因子风险模型,为求得股票收益率的风险矩阵
, 只需要分别求得因子收益率的风险矩阵
2. 基于 DolphinDB 的因子合成
本文首先得到用于建模的多因子窄表,具体流程如下:
- 基于
getXXXX函数计算风格因子。 - 基于
getIndustryFactors函数计算行业因子。 - 基于
getRegTable函数对风格因子、行业因子合并,并填充缺失值得到用于单因子模型检验的回归因子表。 - 基于
getFactorsValidation函数针对回归因子表生成每个因子对应的 IC 、FSC 指标。 - 针对不同因子加权方法,基于
getFSLevelFactor函数合成三级因子得到用于建立多因子风险模型的一级因子窄表。

2.1 风格因子计算
首先计算单个三级风格因子,一级、二级风格因子基于如下风格因子关系表合成得到。其中具有预测属性的 ETOPF_STD, ETOPF, EGRLF, DTOPF 等因子对模型的影响不稳定,暂不考虑。


在调用计算单个三级风格因子时,采用 get + 因子名(首字母大写),例如,Blev(Beta Leverage), Stom(Size Turnover Momentum), Stoq(Stock Quality)因子计算如下:
getBlev(startTime = 2022.01.03,windows = 365,endTime = 2023.01.02)
getStom(startTime = 2022.01.03,windows = 21,endTime = 2023.01.02)
getStoq(startTime = 2022.01.03,windows = 63,endTime = 2023.01.02)单个因子计算函数的返回结果均是窄表,返回结果大致如下:

2.2 行业因子计算
采用申万一级 SW_2021、中信一级 CITIC 行业分类哑变量因子,CNLT 依据市值给出权重,接口中提供行业因子市值加权。
| 接口 | 说明 |
|---|---|
| getIndustry | 获取原始行业因子 步骤一:获取月末交易日和行业交易代码。 步骤二:基于交易日数据获取行业数据。 步骤三:基于 onehot 编码生成宽表并求和汇总。 |
| getIndusrtyWeighted | 获取行业因子权重 步骤一:获取月末交易日和行业交易代码。 步骤二:基于交易日数据获取各行业数据、市场数据。步骤三:关联上述两表,计算每个行业占总行业的权重,并找出在指定行业中占比最大的个股。 |
| getIndusrtyFactor | 获取加权后的行业因子 步骤一:getIndustry 获取原始行业因子。 步骤二:getIndusrtyWeighted 获取行业权重。 |
部分返回结果如下:
getIndustry(startTime = 2022.01.01,endTime = 2023.01.02,method = 'SW_2021')
getIndustryWeighted(startTime = 2022.01.03,endTime = 2023.01.02,method = 'SW_2021')
getIndustryFactor(startTime = 2022.01.03,endTime = 2023.01.02,method = 'SW_2021')
2.3 因子预处理
在对原始三级风格因子进行计算后,通常需要经过一定的标准化的数据清洗流程才能进一步合成因子,DolphinDB 使用了 MAD 法去极值并采用 CNLT 中的市值标准化流程。
2.3.1 风格因子 MAD 法去极值
MAD 方法(median absolute deviation 方法),该方法使用中位数,较于传统的均值标准差具有稳健(robust)性质,不会被极端异常值影响结果,是对减均值后标准差处理的改进方法。MAD 方法相对于其他离群值检测方法的优点在于它对离群值的鲁棒性较强。本文中 MAD 方法对应接口函数 winsorized 。
/* winsorized
对因子表进行winsorized处理
因子表必须包含三列:记录日期、股票代码和原始因子,其中原始因子必须是第三列
Input :
tbName
Output:
factor table after winsorized */MAD 方法的具体实现步骤如下:
- 计算数据集的中位数(Median)作为数据的中心位置。
- 对每个数据点,计算其与中位数的绝对偏差(Absolute Deviation)。
- 计算所有绝对偏差的中位数,即 Median Absolute Deviation。
4. 确定离群值的阈值。通常,离群值被定义为与中位数的偏差超过一定倍数(如 3 倍)的 MAD 的数据点。
5. 鉴定离群值。对于超过阈值的数据点,可以将其标记为离群值或进行进一步的分析。
2.3.2 风格因子市值加权标准化
市值加权标准化是为了去除极端市值对因子计算贡献产生影响。该方法是为了将因子量纲统一在相接近的水平。对于第 n 个个股的第 k 个原始因子暴露
其中
本文市值加权标准化对应接口函数 standardized,其中 adjusted=true表示采纳市值中性化处理。
/* standardized
对因子表进行标准化和市值中性化处理
因子表必须有三列:记录日期、股票代码和原始因子,原始因子必须是第三列
Input :
tbName
adjusted Market-neutralize or not
Output:
factor table after standardized */2.3.3 风格因子、行业因子合并
以上两种接口函数 winsorized, standardized 嵌入在 getAllFactors 函数中,getAllFactors 通过指定参数 normlizing 和 scaling 分别实现标准化和去极值的过程。此外在得到行业因子、风格因子后,基于 getAllFactors 函数可以得到合并了上述所有因子的宽表,可进一步用于单因子的有效性检验。
/* getAllFactors
获取所有因子
Input: normlizing true (Default) 标准化
scaling true (Default) 去极值
decap true (Default) 市值中性化
industry_weighted true (Default) 行业市值权重加权
industry_method 'CITIC' (Default)、'SW_2021'
startTime 2022.01.03(Default)
endTime 2023.01.02(Default)
Output:
factor table */Factors = getAllFactors(st=st,et =et, normlizing = true,
scaling = true,decap = false,industry_method = 'CITIC',
industry_weighted = false)
select * from Factors limit 1002.3.4 因子缺失值处理
在构建因子回归模型前,为避免因子缺失值对于回归模型结果的影响,需要对因子缺失值做进一步的处理。本文中针对所有原始因子的缺失值处理的函数接口为 getRegTable,本文选择采用均值填充以处理缺失值。
/* getRegTable
获取用于回归的经过处理的回归因子表、包含个股收益率、因子暴露、行业因子、行业变量、回归权重
Input:
factorsTable false (Default) 是否使用提供的初始因子表
tbName NULL (Default) 初始因子表
normlizing true (Default) 标准化
scaling true (Default) 去极值
decap true (Default) 回归市值中性化
industry_weighted true (Default) 行业因子加权重
industry_method 'CITIC' (Default)、'SW_2021'
st 2022.01.03(Default)
et 2023.01.02(Default)
Output:
regression table
*/注意:初始因子表 tbName,当且仅当 factorsTable 为 true 时,需要传入已经使用 getAllFactors 函数标准化过、去极值过、市值中性化过、行业因子加权重过的全因子表(或者筛选过股票后的部分个股因子表)。并且若提供因子表,该函数相应参数 normlizing, scaling, decap, weighted 是 false 或 true 对结果没有影响,即 tmpReg = getRegTable(factorsTable = true,tbName = Factors,st= st,et = et)。
通过调用:
fTable = getRegTable(factorsTable = true,tbName = Factors,st= st,
et = et,normlizing = normlizing ,scaling = scaling,
decap = decap, industry_method = industry_method,
industry_weighted = industry_weighted)执行上述命令可以实现如下效果:
处理之前,getAllFactors 得到的原始三级因子宽表:

处理之后,getRegTable处理后的三级因子宽表:

2.4 单因子模型检验
2.4.1 WLS 回归模型
- 针对风格因子 s,单因子的检验回归模型如下:
此处使用 WLS(Weighted Least Squares,加权最小二乘)以
- 针对行业因子 i,单因子的检验回归模型如下:
此处使用 WLS以
本文的 WLS回归模型的接口为 getOneFactorValidate,函数嵌套在 styleValidate 风格因子检验函数、industryValidate 行业因子检验函数中。
/* getOneFactorValidate
获取moving wls单因子回归结果统计量的聚合函数
Input: y 因变量
x 自变量
w 权重
Output:
wls stat "beta","tstat","R2","AdjustedR2", "Residual" */2.4.2 T 检验
t 值检验是检验对应变量的因子收益是否显著区别于 0,可以用来衡量因子的有效性以及一致性。其计算方法为: getOneFactorValidate 接口的 WLS 回归模型得到的 t 统计量进行评估。
2.4.3 Factor Stability Coefficient
在构建多因子模型时,必须要考虑到因子暴露的稳定性。如果因子暴露矩阵每次计算时的变化特别大,那么该模型的稳健性较差。故本文引入因子稳定性系数(Factor Stability Coefficient, FSC)指标,其定义为这个月的因子暴露矩阵与下个月因子暴露矩阵的相关性。
通常情况下,FSC 指标是通过比较从不同数据集或不同时间点上进行的、两个独立的因子分析所得到的因子载荷来进行计算的。本文通过 getFactorsValidation 函数基于斯皮尔曼相关系数(Spearman's Rank Correlation Coefficient)计算 FSC 指标,令

则 FSC系数计算方法为:

研究人员通常使用阈值来解释 Factor Stability Coefficient。例如,FSC 大于 0.8 通常被认为表示较好的稳定性或一致性,而低于 0.5 的值可能表示稳定性较差。
2.4.4 Information Coefficient
Information Coefficient(IC)值是因子对下期收益率的预测与下期实际收益率的相关性,IC 代表的是预测值和实现值之间的相关性,通常用于评价预测能力(即选股能力)。在实际计算中,因子 的 IC 值一般是指个股第 期在因子 上的暴露度与 +1 期的收益率的相关系数。因子 IC 值反映的是个股下期收益率和本期因子暴露度的线性相关程度,表示使用该因子进行收益率预测的稳健性。IC 的计算方式有两种:normal IC、rank IC。本文在 getFactorsValidation 函数基于 Spearman 相关系数计算 Rank IC 值。

IC 值被认为在 0.03 上时与市场存在一致或相反的波动规律,此时该因子存在一定的有效性。
2.4.5 基于 DolphinDB 实现单因子有效性检验
本文的单因子有效性检验的对应接口函数 getFactorsValidation。该函数通过输入计算得到的全因子表,给出了每一个单因子模型的单因子收益,t 统计量,拟合优度,FSC 指标和 IC 值。通过这些值可以全面系统地对所有因子进行分析,如下列图所示:
Input:
factorsTable false (Default) 是否使用提供的因子表
tbName NULL (Default) 回归因子表
normlizing true (Default) 标准化
scaling true (Default) 去极值
decap true (Default) 市值中性化
industry_weighted true (Default) 行业因子加权重
industry_method 'CITIC' (Default)、'SW_2021'
st 2022.01.03(Default)
et 2023.01.02(Default)
Output:
factor test table factor_return、tstat、R2、fsc、IC
*/- 步骤一:首先获取所有因子的检验指标。
// 获取单风格因子有效性、一致性、稳定性检验
factorsValid = getFactorsValidation(factorsTable = true,tbName = out,st=2022.01.03,
et =2023.01.02, normlizing = true,scaling = true,
decap = true,industry_method = 'CITIC',
industry_weighted = true)- 步骤二:绘制因子的 FSC 月频时序图,评价因子稳定性。
tmp = select record_date,valueType.regexReplace("_stat","") as valueType,
fsc from factorsValid
tmppivot = select fsc from tmp pivot by record_date,valueType
tbfsc = sql(select = sqlCol(tmppivot.columnNames()[11:20]),from = tmppivot).eval()
plot(tbfsc,tmppivot.record_date,extras={multiYAxes: false},title = "因子fsc 月频时序图")
FSC 大部分处于 0.8 以上的因子被公认为因子具备较高的稳定性。上图中除 em, dastd, dtoa 外的其他因子具备较好的稳定性。
- 步骤三:绘制因子的 IC 月频时序图,评价因子一致性。IC 值被认为在 0.03 上时与市场存在一致或相反的波动规律,此时该因子存在一定的有效性。
tmp1 = select record_date,valueType.regexReplace("_stat","") as valueType,
abs(ic) as ic from factorsValid
tmppivot1 = select ic from tmp1 pivot by record_date,valueType
tbic = sql(select = sqlCol(tmppivot1.columnNames()[2:10]),from = tmppivot1).eval()
baseline = take(0.03,(shape tbic)[0])
plot(table(tbic,baseline),tmppivot1.record_date,
extras={multiYAxes: false},title = "因子ic 月频时序图")
可以观察到 atvr(Annualized Traded Value Ratio,年交易比值)因子在 2017 年前保持着较强的相关性,而 2018-2022 年期间 8 个因子呈现周期性起伏的规律。
- 步骤四:绘制因子 t_stat,评价因子有效性。
tmp2 = select record_date,valueType.regexReplace("_stat","") as valueType,
tstat from factorsValid
tmppivot2 = select tstat from tmp2 pivot by record_date,valueType
tbstat = sql(select = sqlCol(tmppivot2.columnNames()[11:20]),from = tmppivot2).eval()
baseline_neg = take(-0.03,(shape tbstat)[0])
baseline_pos = take(0.03,(shape tbstat)[0])
plot(table(tbstat,baseline_neg,baseline_pos),tmppivot2.record_date,
extras={multiYAxes: false},title = "因子t_stat 月频时序图")
图中大部分因子的 t 值距离基线 0.03 和 -0.03 有相当的距离。例如 dastd 和 cmra,一个显著高于 0.03,另一个则显著低于 -0.03。由此可得结论:在 18 年之前,这两个因子与市场存在着显著的正负相关性。
2.5 多因子合成
针对已经得到的三级因子和因子的有效性识别的结果,用户可以进行下一步因子的合成。因子合成的方法参考华泰证券中给出的部分方法,例如等权法和历史信息法。
- 等权法(equal),对相应的三级因子等权合成二级、一级因子。例如对 ABS、ACF_TTM 这两个因子各赋1/2的权重合成 Earnings Quality 因子。
- 历史收益率加权方法(ir),根据在单因子模型检验获得中的三级因子收益率标准化后得到加权系数加权得到二级因子。
- 信息系数比率法(ic_ir),由因子检验获得的 IC 值对因子进行合成。举例来说,设
维的矩阵 为过去 个截面期上的 个因子的 IC 矩阵, 是矩阵 的行均值, 的矩阵 是 的协方差阵,则可以取 作为权重加权得到各级因子。
本文因子合成的对应接口函数 getFSLevelFactor,其中 firstFactors 中二级因子的名称需要与 secondFactors 中的二级因子的名称一一对应;secondFactors 中的三级因子需要与输入的因子表 factorsTable 字段名对应(支持大小写匹配)。
Input:
factorsTable NULL (Default) (getRegTable函数返回的全因子表)
factorsValid NULL (Default) (getFactorsValdition接口返回的单因子收益和检验表)
firstFactors NULL 一级因子二级因子关系json
secondFactors NULL 二级因子三级因子关系json
normlizing true 是否对合成的因子标准化
method "equal"等权、 "ir"历史收益率、"ic_ir"信息系数比率 合成因子方法
level "S"、"F" 指定一级二级因子 合成二级(S)一级(F)风格因子
Output:
factorsTable 返回按指定关系合成的(可直接用于回归模型的)风格大类因子和行业因子st = 2022.01.03
et = 2023.01.02
normlizing = true
scaling = true
decap = true
industry_method = 'CITIC'
industry_weighted = true
Factors = getAllFactors(st= st,et = et, normlizing = normlizing,scaling = scaling,
decap = decap,industry_method = industry_method,
industry_weighted = industry_weighted)
select * from Factors limit 100
// 对原始因子宽表的缺失值进行处理
fTable = getRegTable(factorsTable = true,tbName = Factors,st= st,et = et,
normlizing = normlizing ,scaling = scaling ,
decap = decap,industry_method = industry_method,
industry_weighted = industry_weighted)
// 因子有效性检验
// 获取单风格因子有效性、一致性、稳定性检验
factorsValid = getFactorsValidation(factorsTable = true,tbName = Factors,st = st,
et = et , normlizing = normlizing,scaling = scaling,
decap = decap,industry_method = industry_method,
industry_weighted = industry_weighted)
factorsValid
update factorsValid set tstat = abs(tstat)
// 计算fsc
tmp = select record_date,valueType.regexReplace("_stat","") as valueType,
fsc from factorsValid
tmppivot = select fsc from tmp pivot by record_date,valueType
tbfsc = sql(select = sqlCol(tmppivot.columnNames()[11:20]),from = tmppivot).eval()
plot(tbfsc,tmppivot.record_date,extras={multiYAxes: false},title = "因子fsc 月频时序图")
// 计算ic
tmp1 = select record_date,valueType.regexReplace("_stat","") as valueType,
abs(ic) as ic from factorsValid
tmppivot1 = select ic from tmp1 pivot by record_date,valueType
tbic = sql(select = sqlCol(tmppivot1.columnNames()[2:10]),from = tmppivot1).eval()
baseline = take(0.03,(shape tbic)[0])
plot(table(tbic,baseline),tmppivot1.record_date,
extras={multiYAxes: false},title = "因子ic 月频时序图")
// 计算tstat
tmp2 = select record_date,valueType.regexReplace("_stat","") as valueType,
tstat from factorsValid
tmppivot2 = select tstat from tmp2 pivot by record_date,valueType
tbstat = sql(select = sqlCol(tmppivot2.columnNames()[11:20]),from = tmppivot2).eval()
baseline_neg = take(-0.03,(shape tbstat)[0])
baseline_pos = take(0.03,(shape tbstat)[0])
plot(table(tbstat,baseline_neg,baseline_pos),
tmppivot2.record_date,extras={multiYAxes: false},
title = "因子t_stat 月频时序图")由如下的合成一级因子结果可以看到'abs','acf_ttm','acf_lyr','vsal_ttm','vsal_lyr'等因子被合成为 Quality 等一级因子。
合成因子前:

合成后的一级因子:

2.6 自定义因子的多因子合成
2.6.1 基于对自定义因子预处理
- 对已有因子数据做预处理,可以调用因子预处理的
winsorized、standardized等函数以实现对风格因子实现极值化、标准化处理。 - 对上述步骤处理后的因子数据处理缺失值,避免因子缺失值对于回归模型结果的影响。
假设最后的因子表为 Factors 表,则可以通过如下方式调用 getRegTable 函数进行缺失值处理。
fTable= getRegTable(factorsTable = true,tbName = Factors,st= st,et = et)2.6.2 对自定义进行单因子模型检验
假设最后的因子表为 Factors 表,则可以通过如下方式调用 getFactorsValidation函数进行单因子模型检验,进一步筛选有效因子。
factorsValid= getFactorsValidation(factorsTable = true,tbName = Factors,st= st,et = et)2.6.3 基于自定义因子进行多因子合成
在指定与自定义因子一一对应的一级因子、二级因子后,通过getFSLevelFactor函数生成基于自定义因子合成后的一级、二级因子。
getFSLevelFactor(fTable,factorsValid,firstFactors,secondFactors,false , "ir",level = "F")
getFSLevelFactor(fTable,factorsValid,firstFactors,secondFactors,false , "ir",level = "S")3. 基于 DolphinDB 的收益风险模型
合成一级因子后,用户就可以基于全部的因子给出收益和风险的模型构建过程,包括所有因子收益、偏差统计量和拟合优度。偏差统计量可以给出模型预测的风险与实际风险的偏差。而拟合优度则反应的是所有因子对市场收益的解释力度,拟合优度越接近于 1 说明模型的稳健性,准确程度越高。
本文收益风险模型对应接口函数 getRetTable,函数输出为:计算返回因子风险协方差矩阵、特质性收益风险协方差矩阵、bias_statistic、stR2、tstat 和因子收益率。通过这些返回值可以给出因子风险和特异性风险,以及模型估计的风险准确性评估(bias),模型的解释力度评估(stR2)。
/* getRetTable
Input:
facTable NULL (Default) (getFSLevelFactor函数返回的)全因子表
adjust true (Default) 是否进行Newey_West调整
shrink true (Default) 是否进行贝叶斯收缩
eigenfactor true (Default) 是否进行特征因子调整
Output:
factorsRetTable
*/3.1 基于 WLS 构建多因子风险模型
本文首先通过 moving wls 多因子回归模型,函数接口为 getAllFactorValidate。
/* getAllFactorValidate
获取moving wls多因子回归结果统计量的聚合函数
Input: y 因变量
x 自变量
w 权重
Output:
wls stat "beta","tstat","R2","AdjustedR2","Residual" */在构建得到多因子风险回归模型后,再基于 getRetTable 函数计算如下模型评估指标:
- stR2:该值为多因子风险模型中定义的 R_2 ,计算方法为:

其中
2.
3. 偏差统计量(bias_statistic):该统计量是一种通用的衡量模型预测程度的统计量,直观来看,该统计量计算的是实际的风险比上预测的风险。令

其中

因此当观测发现
4. Q 统计量(Q_statistic):基于偏差统计量中计算的

3.2 风险调整
在构建得到多因子风险回归模型后,若要求解多因子风险模型中的个股收益率协方差矩阵,则需要分别求解因子收益率协方差矩阵
、特质收益协方差矩阵
3.2.1 因子收益率风险调整
3.2.1.1 Newey_West 调整Delta
多因子风险模型必须进行 Newey-West 协方差调整的原因主要有以下两点:
- 多因子风险模型是日频的,然而风险预测模型是月频的,因此需要对日频的协方差矩阵通过尺度变化,转化成月频的协方差矩阵,而这个过程必须考虑日频因子收益率的自相关性。
- 实际应用中,基于多因子风险模型预测的因子收益率往往存在时序相关性。此时样本风险矩阵并不是真实收益率风险矩阵的相合估计(随着样本数增加,相合估计会收敛于真实值,这有助于计算估计量的估计误差),因此需要调整由计算出来的日收益率存在
阶序列性导致因子收益率的风险矩阵。
本文 Newey-West 协方差调整的接口为 Newye_West 函数,在 getRetTable 函数的 adjust=true 时调用。
/* Newye_West
Newye_West调整得到协方差矩阵
Input:
ret 收益率表
q 收益率自相关阶数
Output:
cov Newye_West调整后的协方差阵
*/
上述接口主要实现步骤如下:
- 步骤一,假设
可满足 过程,则可先基于移动平均过程进行简单校验,如下:

其中

为不考虑自相关性的样本协方差矩阵;

代表着由当期的收益率向量以及滞后i期的收益率向量所得到的自协方差矩阵,但
- 步骤二,对
的修正加入 Bartlett 权重系数 ,该系数与滞后期成反比,若收益率向量间的滞后期越长,则赋予的权重则越小。经过证明可以发现,该修正后所得到的样本风险矩阵 是真实的风险矩阵的相合估计,且是半正定矩阵。

3.2.1.2 Eigenfactor 调整
令
- eigenfactor 之间彼此独立,两两之间的协方差为零。
- 方差最小的 eigenfactor 代表以最小化组合方差为目标函数实现的组合,而方差最大的 eigenfactor 代表以最大化组合方差为目标函数实现的组合。
然而若直接特征分解会存在偏差,其中风险越小的 eigenfactor portfolio 的偏差反而较大,因此需要进行 Eigenfactor 调整。本文 Eigenfactor 调整对应的接口为 eigenCovAdjusted 函数,在 getRetTable 函数中当参数 eigenfactor=true 时被调用。
/* eigenCovAdjusted
eigenCovAdjusted调整风格因子协方差
Input:
cov 因子收益率协方差阵
M 蒙特卡罗模拟:重采样次数
Output:
cov eigenCovAdjusted调整后的协方差阵
*/上述接口的主要实现步骤如下:
- 步骤一,首先基于蒙特卡洛模拟构造出相对于“真实值”的具有偏差的协方差矩阵
,即”模拟协方差矩阵“。 - 生成服从均值为 0,协方差为
的 维的多元正态因子收益 ,每一行代表一个 eigenfactor 的收益率序列。因此 代表了原因子的模拟的收益时间序列,其对应的协方差矩阵 即为得到的模拟协方差矩阵。 - 进一步特征分解,即可得到模拟的 eigenfactor 的协方差矩阵
。 - 步骤二,以上模拟的 eigenfactor
实际上是以样本协方差矩阵 作为真实的因子收益率协方差矩阵的得到的,其中 可以理解为 的真实值, 是 的无偏估计;因此模拟的”真实协方差矩阵“为 。 - 步骤三,计算缩放量/偏差调整系数。由以上得到的
和 的对角元的 和 ,可以计算得到偏差调整系数:

其中
- 步骤四,调整基于样本计算得到的 eigenfactor矩阵
,其中是由 生成的对角矩阵,最终可以给出经过调整后的协方差矩阵为 。
3.2.2 基于贝叶斯收缩的个股特异性收益率风险调整
贝叶斯收缩(Bayesian Shrinkage)是一个常见的将先验和样本估计值结合起来的手段。
本文的贝叶斯收缩的接口为 BayesShrinkage 函数,在 getRetTable 函数中当参数 shrink=true 时被调用。
/* BayesShrinkage
BayesShrinkage调整特质波动率
Input:
cov 特质收益率协方差阵
weight 市值权重
q λ_F 压缩系数
Output:
cov BayesShrinkage调整后的协方差阵
*/上述接口的实现步骤如下:
- 步骤一,计算样本估计值——基于 WLS 预测得到的特异性收益的风险
。 - 步骤二,计算先验值——首先选择与目标个股处在同一市值的所有股票,再基于市值加权计算的特异性收益率的波动的平均值。

其中多因子风险模型把所有个股按照市值分成十档,
表示对应的市值档位,
- 步骤三,贝叶斯收缩(使样本估计值向先验靠拢),计算后验估计。


其中

为
3.3 基于 DolphinDB 的收益风险模型展示
如下,首先基于 getRetTable 接口得到的收益风险模型,并绘制得到的对应的模型评估指标(R2、T 统计量、Bias 统计量等)。如下绘制模型的 R_2 ,可知 12 年至 22 年模型的解释力最低为 5%,最高为 84%,平均为 37%,因此模型的解释力度较高。
// 一级因子收益率回归
retOut1 = getRetTable(facTable1,adjust = true)
// adjust采用Newey-West协方差调整、当市场收益率存在序列自相关时采用此方法
retOut1 = getRetTable(facTable1,adjust = false,shrink = false)
// shrink采用贝叶斯收缩调整特异性风险、推荐使用
retOut1 = getRetTable(facTable1,adjust = true,shrink = true)
// 特异性风险采用bayesian shrinkage
retOut1 = getRetTable(facTable1,adjust = true,shrink = true,eigenfactor = true)
// 因子风险采用eigenfactor adjust,当市场关联密切时采用此方法
retOut1 = getRetTable(facTable1,adjust = true,shrink = true,eigenfactor = true)
// 综上,推荐使用
retOut1 = getRetTable(facTable1,adjust = false,shrink = true,eigenfactor = true)
// 综上,推荐使用
retOut1 = getRetTable(facTable1,adjust = false,shrink = true,eigenfactor = true)
// 综上,推荐使用
retOut1 = getRetTable(fTable,adjust = false,shrink = true,eigenfactor = false)
// 综上,推荐使用
undef(`retOut)
retOut = getRetTable(facTable1,adjust = true,shrink = false ,eigenfactor = false)
// 综上,推荐使用
retOut1.stock_risk[string(2022.12.30)] // 12.30的特质收益协方差矩阵
retOut1.fac_risk[string(2022.12.30)] // 12.30的风险因子协方差矩阵
retOut1.R2 // R2
retOut1.res // 特质收益
retOut1.tstat // t-stat
retOut1.fac_ret // 因子收益
retOut1.bias // bias统计量
plot(retOut1.R2.stR2,retOut1.R2.record_date," R2 月频时序图")
4. 基于 DolphinDB 的多因子风险模型应用
4.1 预测个股收益
预测个股收益的方法比较多样,可以通过经济学或其他模型实现通过时间序列


注意:到第
,
本文因此基于上述模型预测个股收益的对应接口函数 getPredicOut,假设待预测的因子为全因子表的最后一期(全因子表前t-1数据必须完整),则基于本月初(即上月末的因子暴露预测本月末的个股收益)可计算预期因子收益率,并返回预期模型的因子协方差矩阵(因子风险)、特质性收益协方差矩阵(个股风险)、拟合优度
/* getPredicOut
Input:
facTable NULL (Default) (必须)因子表
Output:
predictRetTable
*/如下为调用getPredicOut接口的具体脚本:
predictOut = getPredicOut(facTable1)
pr = select * from predictOut.predict_ret // 利用本期因子暴露预测最后一期的收益率
predictOut.R2 // 预测模型R2
predictOut.res // 预测模型特质收益
predictOut.tstat // 预测模型t-stat
predictOut.fac_ret // 预测模型因子收益
predictOut.bias // 预测模型bias统计量注意:predictOut = getPredicOut(facTable1) 的结果较多。如需查看,推荐持久化后再查看结果,或者可以取出来部分较少的结果,除去预测收益外,包含 R2、t-stat、月频的因子收益、特质收益、风险因子协方差矩阵、特质风险表、bias 统计量的所有结果。
4.2 组合权重优化
组合权重优化在多因子模型中起到了至关重要的作用。组合权重优化的目的在于将组合的风险特征完全定量化,使得投资经理可以清楚地了解组合的收益来源和风险暴露。权重优化的目标函数,优化目标多种多样,例如可以控制最小预测收益并最小组合风险、控制最小本期收益并最小组合风险、控制最大风险并最大化预测收益、控制最大风险并最大化本期收益等等。组合权重优化的过程包含 2 个因素:目标函数和约束条件。
4.2.1 目标函数
假设投资组合中的每只股票的特质因子收益率与共同因子收益率不相关,并且每只股票的特质因子收益率也不相关,因此若
4.2.2 约束条件
- 行业中性
行业中性是指多头组合的行业配置与对冲基准的行业配置相一致。行业中性配置的目的在于剔除行业因子对策略收益的影响,仅考察行业内部个股的超额收益。假设
- 风格因子中性
风格因子中性是指多头组合的风格因子较之对冲基准的风险暴露为 0。风格因子中性配置的目的是消除投资组合与市场风格因子之间的风险暴露,使投资组合的收益主要来源于阿尔法收益,而不是某一特定的市场风格。假设
- 现金中性
现金中性是指多空市值保持一致,不留方向性敞口。则现金中性的权重
- 最小收益预测
控制最小预测收益是多因子风险模型中组合权重优化的一个约束条件,其目的是确保投资组合具备一定的最低收益水平,并帮助控制风险、避免非理性配置以及维持模型一致性。这样可以使投资组合更符合投资目标和风险偏好,并提高整体投资组合的稳定性和可预测性。假设
为基准收益率,则投资组合的权重
4.2.3 基于 DolphinDB 实现组合权重优化
本文给出行业基准中性和风格基准中性下的控制最小预测收益并最小组合风险方法,对应接口函数 getOptimizeWeights,即实现如下的优化目标。在此接口函数中,可以通过 deIndustry 和 deStyle 指定是否行业中性和风格中性:

/* getOptimizeWeights
组合权重优化中的聚合函数
Input:
covf 因子收益协方差矩阵
delta 特质收益协方差矩阵
st 2022.01.03(Default)
et 2023.01.02(Default)
ret 预期收益
r 0.05 设置的最小预测收益
tbName 因子暴露
deIndustry true 行业中性
deStyle true 风格中性
Output:
weightTable 返回预测的资产配置权重
*/基于 getOptimizeWeights 接口,可以通过如下脚本实现在控制最小收益下的最小化风险:
optionCode = exec stock_code from getPredicOut(facTable1).predict_ret
order by return_day desc limit 20
// 初步筛选stock1
optionCode = exec stock_code from getPredicOut(facTable2).predict_ret
order by return_day desc limit 20
// 控制收益、最小化风险模型
portWeight1 = getOptimizeWeights(facTable = facTable1,retOut = retOut1,
st = st,et = et, method ="minRiskControlRet",
r = 0.05,optionCode = optionCode)
// 获得权重组合
portWeight2 = getOptimizeWeights(facTable = facTable2,retOut = retOut2,
st = st,et = et, method ="minRiskControlRet",
r = 0.05, optionCode = optionCode)
index_code = '000300'
CodePre = set(exec stock_code from getPredicOut(facTable1).predict_ret
order by return_day desc limit 200)
// 初步筛选stock2
CodeWeight = set(exec stock_code
from getBenchMark(st=st,et=et,code = index_code)
where i_weight != 0)
CodeFac =set(exec stock_code from facTable1 )
optionCode = (CodePre&CodeWeight&CodeFac).keys()
portWeight3 = getOptimizeWeights(facTable = facTable1,retOut = retOut1,
st = st,et = et, method ="minRiskControlRet",
r = 0.005,deStyle = true,optionCode = optionCode)
// 获得权重组合,并实现在风格上的风险敞口为0
portWeight3 = getOptimizeWeights(facTable = facTable1,retOut = retOut1,st = st,
et = et, method ="minRiskControlRet",r = 0.005,
deIndustry = true,optionCode = optionCode)
// 获得权重组合,并实现在行业上的风险敞口为0
portWeight4 = getOptimizeWeights(facTable = facTable2,retOut = retOut2,st = st,
et = et, method ="minRiskControlRet",r = 0.05,
optionCode = optionCode)4.3 资产配置评估
4.3.1 评估事后资产配置
事后资产配置指在实际收益数据可用之后,根据实际的历史收益数据进行的资产配置。这个过程发生在投资决策之后,基于实际观察到的历史收益数据对资产进行重新配置。因此根据市值或者是等权法评估已有指数的 Bias,可以计算出指定组合的偏差统计量和 Q 统计量,以对事后资产配置进行评估。
- 因子(组合)Bias
- 资产(组合)Bias
本文基于 getFacSpecialBias 函数 ,计算事后资产配置的 Bias 统计量,以评估事后资产配置。
/*
获取因子的Bias时序统计量和获取个股的特质收益统计量
Input:
retOut getRetTable()函数返回的结果
index_name 指数代码
method 等权方法或者流通市值方法 'equal','float_market'
Output:
Bias统计量
*/4.3.1.1 事后因子组合评估
如下脚本,假设 facTable1 为实际投资的所有因子组合表,基于 getFacSpecialBias 计算因子收益率的 Bias、个股特异性的 Bias 以评估事后因子组合。
// 因子组合
retOut = getRetTable(facTable1,adjust = true,shrink = false ,eigenfactor = false)
// 综上,推荐使用
// 获取所有因子的时序bias统计量的值和所有个股的时序bias统计量值
biasOut = getFacSpecialBias(retOut)
// 因子bias
tmpfBias = select bias_stat from biasOut.fac_bias pivot by record_date,valueType
tmpfBias = tmpfBias[23:]
tbfBias = sql(select = sqlCol(tmpfBias.columnNames()[1:9]),from = tmpfBias).eval()
plot(tbfBias,tmpfBias.record_date,extras={multiYAxes: false},
title = "因子模型因子Bias统计量时序图")
plot(tbfBias,tmpfBias.record_date,extras={multiYAxes: false})
code0 = parseExpr("rowAvg("+ concat(tmpfBias.columnNames()[1:],',') + ")")
avgfBias = sql(select = sqlColAlias(code0,'avg_bias_stat'),from = tmpfBias ).eval()
plot(avgfBias,tmpfBias.record_date,extras={multiYAxes: false},
title = "因子均值Bias统计量时序图")
plot(avgfBias,tmpfBias.record_date,extras={multiYAxes: false})
// 个股特异bias
tmpsBias = select mean(bias_stat) from biasOut.stock_bias group by record_date
tmpsBias = tmpsBias[23:]
plot(tmpsBias.avg_bias_stat,tmpsBias.record_date,
extras={multiYAxes: false},title = "因子模型特异风险Bias统计量时序图")如下,分别绘制事后多因子模型的因子 Bias 统计量时序图以及因子均值 Bias 统计量时序图,其中 CNLT 月频模型的八类风格因子 Bias 统计量长期来看均处于 1 附近,均值为 0.9962,模型对因子的风险预估较为准确,而因子均值 Bias 统计量也长期稳定在1附近,因此可以看出,不论是从因子风险角度还是特异风险角度,本模型的估计均较为准确。

因子均值 Bias 曲线:

因子模型特异风险 Bias 统计量评估:

4.3.1.2 事后资产组合评估
本文以沪深 300 指数的等权资产组合为例,基于 getFacSpecialBias 计算其 Bias 统计量,并绘制沪深 300 的等权资产组合 Bias 如下所示。
/* 简单资产配置评估 */
// 计算指数配置的Bias统计量
tmpIndexbiasbn = getFacSpecialBias(retOut,'000300','equal').stock_bias
tmpIndexBias = select wavg(bias_stat,weight) from tmpIndexbiasbn group by record_date
plot(tmpIndexBias.wavg_bias_stat,tmpIndexBias.record_date,extras={multiYAxes: false})
tmpIndexbiasbn = getFacSpecialBias(retOut,'000300','float_market').stock_bias
tmpIndexBias = select wavg(bias_stat,weight) from tmpIndexbiasbn group by record_date
plot(tmpIndexBias.wavg_bias_stat,tmpIndexBias.record_date,extras={multiYAxes: false})
上图中 Bias 均值为 1.08,说明对沪深 300 的等权资产配置风险预测与实际风险较为一致。

上图中 Bias 均值为 1.08,说明对沪深 300 的流通市值加权资产配置风险预测与实际风险也较为一致。
4.3.2 评估事前预测模型资产配置
事前资产配置指在实际收益数据可用之前,根据模型的预测和假设进行的资产配置。这个过程发生在投资决策之前,基于模型的预测结果和投资者的目标、约束条件等进行资产配置。根据已经由优化目标得到组合权重或是给定的组合权重,可以计算出指定组合的偏差统计量和 Q 统计量,观察指定资产配置组合权重的合理性或是评估优化权重的好坏。
本文基于 getPortfolioAccuracy 接口以评估事前资产配置组合。
/* getPortfolioAccuracy
计算资产组合的时序偏差统计量、Q-统计量
Input:
facTable NULL (Default) (getFSLevelFactor函数返回的)全因子回归表
retOut NULL (Default) (getRetTable函数返回的)全因子收益表
st 2022.01.03(Default)
et 2023.01.02(Default)
index_code 指定资产组合 '000300'、'399101'
method 权重配比方法 "float_value" 流通市值加权,"equal" 等权
Output:
accuracyTable
*/假设以沪深 300 指数的等权资产组合为例,基于 getPortfolioAccuracy 函数计算事前预测资产组合的偏差,发现
/* 计算预测模型的bias、q统计量 */
index_code = '000300'
st = 2022.01.03
et = 2023.01.02
outAccurary = getPortfolioAccuracy(st,et,facTable1,retOut,index_code,'equal')
outAccurary = outAccurary[2:]
baseline = take(1,(shape outAccurary)[0])
plot(table(outAccurary.bias_statistic,baseline),outAccurary.record_date,
extras={multiYAxes: false})
mean(outAccurary) 
5. 总结
本文基于 DolphinDB 内置的丰富统计分析函数库、分布式架构下的高性能查询、便捷的向量化编程等特性,完整实现了多因子风险模型 CNLT 的整个流程。DolphinDB 将自身的强大功能与多因子风险模型进行深度融合,帮助用户更准确地分析市场因子对投资组合的影响,同时进一步优化投资策略以实现更高的投资回报。
6. 参考文献
[1] 通联数据.通联数据风险模型评测报告[R].中国:通联数据,2022.
[2] 通联数据.风险模型库介绍[R].中国:通联数据,2023.
[3] 刘富兵,李辰.基于组合权重优化的风格中性多因子选股策略[R].中国:国泰君安证券,2015.04.26.
[4] Newey, W. K. and K. D. West (1987). A simple, positive semi-definite, heteroskedasticity and autocorrelation consistent covariance matrix. Econometrica, Vol. 55(3), 703 – 708.
[5] Ledoit, Olivier, and Michael Wolf. "Improved estimation of the covariance matrix of stock returns with an application to portfolio selection." Journal of empirical finance 10.5 (2003): 603-621.
[6] 林晓明,陈烨.因子合成方法实证分析华泰多因子系列之十[R].中国:华泰证券,2018.
[7] 林晓明,陈烨.华泰多因子模型体系初华泰多因子系列之一[R].中国:华泰证券,2016.




