目录
- 前言
- 因子相关性带来的影响
- 因子正交化的理论推导和过程
- 不同因子正交化方法的python实现
- 因子正交化在商品多因子组合中的运用实践
- 总结
(一) 前言
在进行商品量化多因子加权时,我们通常会从动量、持仓、期限结构、基本面等多个维度分别选择表现比较好的因子进行加权,但是这些因子之间可能存在着多重共线性,这会导致加权之后的组合在某些因子上的重复暴露,从而影响组合长期的表现。因此有必要对因子进行共线性的剔除。
因子正交化是剔除因子共线性的一种常用方法,基于正交之后的因子再进行加权可以提高投资组合的长期稳定表现。接下来我们会提供一套因子正交化的统一分析方法,并对不同的因子正交方法进行对比和比较。
(二)因子相关性带来的影响
用多因子模型进行投资组合构建时,我们通常会从多个维度为每个资产进行综合评估,从而筛选出有效的单因子,再进行多因子的组合。这种评估方法的隐含假设是因子之间的相关性较低。虽然这些因子属于不同的维度,但是有些因子之间存在明显的相关性,不仅是同一类别下的不同因子有明显的高相关性,不同类别的因子也有一定的高相关性。因此,在多因子组合中同时加入具有相关性比较高的因子,组合在某个因素上的暴露就会变大。因此,当我们在用多因子加权时,如果不对因子的相关性做处理,就可能使组合对于某些因子有重复的暴露,从而影响组合长期的表现。
有时候我们会发现增加因子数量可能比减少因子表现更差,这主要是因为因子之间的多重共性性导致组合在特定的一些因素上的重复暴露,同时因子相关性会导致模型估计时,模型参数的不稳定或者参数估计不准确。因此,在做多因子加权时,有必要对因子的相关性进行处理。一种处理因子共线性的方法是对因子进行线性变换,得到一组新的因子组合,这些新的因子包含原有因子特征,而两两之间互相没有相关性,即得到一组互相正交的因子。基于这样一组新的因子进行多因子的组合加权,就能更好控制组合在各个因子之间的暴露,降低因子共线性对于组合的影响。
(三)因子正交化的理论推导和过程
在每个时间截面上,我们假设所有的因子已经做了截面标准化处理,即均值为0,标准差为1。我们的目标是找到一组新的因子,使得这些新的因子两两之间的相关性为0。我们可以用矩阵的方法来进行因子正交化。假设我们有K个因子,每个因子有N个品种,我们可以将这些因子的截面数据组成一个N∗KN*KN∗K的矩阵FFF,其中每一列代表一个因子,每一行代表一个品种。我们的目标是找到一个N∗KN*KN∗K的矩阵F,使得FTF=IF^TF=IFTF=I,其中III是单位矩阵。这样我们就得到了一组新的因子,使得这些新的因子两两之间的相关性为0。我们可以用矩阵的方法来进行因子正交化。
我们定义一个过渡矩阵为正交矩阵的SK∗KS_{K*K}SK∗K,,原来的因子可以用FN∗KF_{N*K}FN∗K表示,旋转变换之后的因子我们用F~N∗K\widetilde{F}_{N*K}FN∗K表示,那么F~N∗K\widetilde{F}_{N*K}FN∗K一定是正交矩阵,其中,
F~N∗K=FN∗KSK∗K公式(1)\widetilde{F}_{N*K} = F_{N*K}S_{K*K}\qquad \text{公式(1)}FN∗K=FN∗KSK∗K公式(1)
那么,
(F~N∗K)TF~N∗K(\widetilde{F}_{N*K})^T\widetilde{F}_{N*K}(FN∗K)TFN∗K
=(FN∗KSK∗K)TFN∗KSK∗K =(F_{N*K}S_{K*K})^TF_{N*K}S_{K*K}=(FN∗KSK∗K)TFN∗KSK∗K
=SK∗KTFN∗KTFN∗KSK∗K公式(2)=S_{K*K}^TF_{N*K}^TF_{N*K}S_{K*K}\qquad \text{公式(2)}=SK∗KTFN∗KTFN∗KSK∗K公式(2)
令MK∗K=FN∗KTFN∗KM_{K*K} = F_{N*K}^TF_{N*K}MK∗K=FN∗KTFN∗K,可得,
SK∗KTFN∗KTFN∗KSK∗KS_{K*K}^TF_{N*K}^TF_{N*K}S_{K*K}SK∗KTFN∗KTFN∗KSK∗K
=SK∗KTMK∗KSK∗K= S_{K*K}^TM_{K*K}S_{K*K} =SK∗KTMK∗KSK∗K
=SK∗K−1MK∗KSK∗K=S_{K*K}^{-1}M_{K*K}S_{K*K} =SK∗K−1MK∗KSK∗K
=IK∗K=I_{K*K}=IK∗K
由上式可得,
SK∗KSK∗K−1=SK∗KSK∗KT=MK∗K−1公式(3)S_{K*K}S_{K*K}^{-1} = S_{K*K}S_{K*K}^{T} = M_{K*K}^{-1} \qquad \text{公式(3)} SK∗KSK∗K−1=SK∗KSK∗KT=MK∗K−1公式(3)
满足上述条件的矩阵SK∗KS_{K*K}SK∗K就是我们要求的正交矩阵,上述公式的通解可以表示为:
SK∗K=MK∗K−1/2CK∗K公式(4)S_{K*K} = M_{K*K}^{-1/2}C_{K*K}\qquad\text{公式(4)}SK∗K=MK∗K−1/2CK∗K公式(4)
满足上述条件的矩阵SK∗KS_{K*K}SK∗K就是我们要求的正交矩阵。
我们可以通过特征值分解的方法来求解SK∗KS_{K*K}SK∗K。
我们可以将MK∗KM_{K*K}MK∗K分解为MK∗K=QK∗KΛK∗KQK∗KTM_{K*K} = Q_{K*K}\Lambda_{K*K}Q_{K*K}^TMK∗K=QK∗KΛK∗KQK∗KT,其中QK∗KQ_{K*K}QK∗K是MK∗KM_{K*K}MK∗K的特征向量矩阵,ΛK∗K\Lambda_{K*K}ΛK∗K是MK∗KM_{K*K}MK∗K的特征值矩阵。
我们可以得到
MK∗K=QK∗KΛK∗KQK∗KTM_{K*K} = Q_{K*K}\Lambda_{K*K}Q_{K*K}^T MK∗K=QK∗KΛK∗KQK∗KT
MK∗K−1=QK∗KΛK∗K−1QK∗KTM_{K*K}^{-1}=Q_{K*K}\Lambda_{K*K}^{-1}Q_{K*K}^TMK∗K−1=QK∗KΛK∗K−1QK∗KT
MK∗K−1/2MK∗K−1/2=QK∗KΛK∗K−1/2QK∗KTQK∗KΛK∗K−1/2QK∗KTM_{K*K}^{-1/2}M_{K*K}^{-1/2}= Q_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^TQ_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^T \\
MK∗K−1/2MK∗K−1/2=QK∗KΛK∗K−1/2QK∗KTQK∗KΛK∗K−1/2QK∗KT
可以得到MK∗K−1/2M_{K*K}^{-1/2}MK∗K−1/2的一个特解为
MK∗K−1/2=QK∗KΛK∗K−1/2QK∗KT公式(5)M_{K*K}^{-1/2} = Q_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^{T} \qquad \text{公式(5)}MK∗K−1/2=QK∗KΛK∗K−1/2QK∗KT公式(5)
将公式(5)代入公式(4)中,我们可以得到
SK∗K=QK∗KΛK∗K−1/2QK∗KTCK∗K公式(6)S_{K*K} = Q_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^{T}C_{K*K}\qquad\text{公式(6)}SK∗K=QK∗KΛK∗K−1/2QK∗KTCK∗K公式(6)
根据公式(6),选择不同的正交矩阵CK∗KC_{K*K}CK∗K对原始的因子进行正交化,我们可以得到不同的正交化结果。其中施密特正交化对应的CK∗KC_{K*K}CK∗K为一个上三角的过渡矩阵,规范正交化对应的CK∗K=QK∗KC_{K*K}=Q_{K*K}CK∗K=QK∗K,对称正交化对应的CK∗K=IK∗KC_{K*K}=I_{K*K}CK∗K=IK∗K。接下来我们将对这三种正交化方法进行具体的实现。
(四)不同因子正交化方法的python实现
- (1)施密特正交化(Gram-Schmidt Orthogonalization)
从一组线性无关的因子列向量,f1,f2,...fKf_1,f_2,...f_Kf1,f2,...fK,我们可以得到一组正交的因子列向量u1,u2,...uKu_1,u_2,...u_Ku1,u2,...uK。施密特正交化的具体步骤如下:
u1=f1 u_1 = f_1 u1=f1
u2=f2−f2Tu1u1Tu1u1 u_2 = f_2 - \frac{f_2^Tu_1}{u_1^Tu_1}u_1 u2=f2−u1Tu1f2Tu1u1
u3=f3−f3Tu1u1Tu1u1−f3Tu2u2Tu2u2 u_3 = f_3 - \frac{f_3^Tu_1}{u_1^Tu_1}u_1 - \frac{f_3^Tu_2}{u_2^Tu_2}u_2 u3=f3−u1Tu1f3Tu1u1−u2Tu2f3Tu2u2
... ... ...
uK=fK−fKTu1u1Tu1u1−fKTu2u2Tu2u2−...−fKTuK−1uK−1TuK−1uK−1 u_K = f_K - \frac{f_K^Tu_1}{u_1^Tu_1}u_1 - \frac{f_K^Tu_2}{u_2^Tu_2}u_2 - ... - \frac{f_K^Tu_{K-1}}{u_{K-1}^Tu_{K-1}}u_{K-1}
uK=fK−u1Tu1fKTu1u1−u2Tu2fKTu2u2−...−uK−1TuK−1fKTuK−1uK−1
因此可以得到一组互相正交的因子列向量u1,u2,...uKu_1,u_2,...u_Ku1,u2,...uK,在对其单位化后,就得到了一组正交的因子,这组因子的两两之间的相关性为0。
ek=uk∣∣uk∣∣(k=1,2,3,...,K)e_{k} = \frac{u_k}{||u_k||}(k = 1,2,3,...,K)ek=∣∣uk∣∣uk(k=1,2,3,...,K)
施密特正交化是一种有顺序的正交方法,不同的因子顺序可能会得到不同的正交结果。同时,第k个因子的正交化需要用到前k-1个因子,因此施密特正交化的计算量较大,由于计算过程中存在除法运算,因此当因子之间的相关性较大时,施密特正交化的计算精度会受到影响。
下面是斯密特正交的具体实现:
def gram_schmidt(matrix):
F = np.array(matrix,dtype=np.float64)
U = np.array(matrix,dtype=np.float64)
n = matrix.shape[1]
for i in range(n):
for j in range(i):
F[:, i] = F[:, i] - (np.dot(F[:, i], U[:, j]) * U[:, j]) / np.dot(U[:, j], U[:, j])
U[:, i] = F[:, i] / np.sqrt(np.dot(F[:, i], F[:, i]))
return U
在进行斯密特正交化过程后,部分因子全为NaN,这可能是由于以下原因:
-
输入的数据中包含NaN值。在进行正交化之前,应该先处理这些NaN值,例如通过插值或者删除含有NaN的行。
-
在正交化过程中,可能出现了除以零的情况,导致结果为NaN。这通常发生在输入的向量组不是线性无关的情况下。
处理这种情况的方法有:
-
清理数据:在进行正交化之前,先清理输入的数据,处理或删除含有NaN的行。
-
检查输入:确保输入的向量组是线性无关的。如果向量组不是线性无关的,那么Gram-Schmidt正交化过程可能会失败。
施密特(Gram-Schmidt)正交化过程理论上应该能得到一组正交向量,但在实际计算中,由于浮点数的精度问题,可能会导致计算结果并不完全正交。这是因为计算机在处理浮点数时,会有一定的舍入误差,这种误差在进行大量计算时可能会累积,导致最终的结果并不完全满足正交条件。
如果你发现施密特正交化后的向量并不满足正交,可以尝试以下方法:
-
检查你的输入向量是否线性无关。施密特正交化过程要求输入的向量组是线性无关的。如果输入的向量组不是线性无关的,那么这个过程可能会失败。
-
增加浮点数的精度。Python的
numpy
库提供了多种不同精度的浮点数类型,如float32
、float64
等。使用更高精度的浮点数可能会得到更准确的结果。 -
使用更稳定的正交化方法。施密特正交化过程对于数值稳定性不太好,当处理的向量近似线性相关,或者向量的数量非常大时,可能会积累较大的数值误差。在这种情况下,可以考虑使用更稳定的正交化方法,如QR分解。
-
对结果进行修正。如果正交化后的向量并不完全满足正交条件,可以对结果进行修正,使其满足正交条件。例如,可以通过对每个向量除以其模长来使其成为单位向量,然后再次进行施密特正交化过程。
由于我们无法确保输入的因子是线性无关的,这导致斯密特正交化可能失败。
- (2)规范正交化(Canonical Orthogonalization)
规范正交化,取CK∗K=QK∗KC_{K*K}=Q_{K*K}CK∗K=QK∗K,其中QK∗KQ_{K*K}QK∗K是MK∗KM_{K*K}MK∗K的特征向量矩阵。则有
SK∗K=QK∗KΛK∗K−1/2QK∗KTCK∗K=QK∗KΛK∗K−1/2QK∗KTQK∗K=QK∗KΛK∗K−1/2公式(7) S_{K*K} = Q_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^{T}C_{K*K} \\ = Q_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^{T}Q_{K*K} \\ = Q_{K*K}\Lambda_{K*K}^{-1/2}\qquad\text{公式(7)} SK∗K=QK∗KΛK∗K−1/2QK∗KTCK∗K=QK∗KΛK∗K−1/2QK∗KTQK∗K=QK∗KΛK∗K−1/2公式(7)
其中ΛK∗K\Lambda_{K*K}ΛK∗K是MK∗KM_{K*K}MK∗K的特征值矩阵,ΛK∗K\Lambda_{K*K}ΛK∗K是一个对角矩阵,对角线上的元素是MK∗KM_{K*K}MK∗K的特征值。QK∗KQ_{K*K}QK∗K是MK∗KM_{K*K}MK∗K的特征向量矩阵,QK∗KQ_{K*K}QK∗K的每一列是MK∗KM_{K*K}MK∗K的一个特征向量。
正交化和主成分分析(PCA)都是数据处理中常用的技术,它们都可以用于降维和提取特征,但是它们的方法和目标有所不同。
-
正交化:正交化的目标是将一组线性无关的向量转换为一组正交的向量(即这些向量之间的内积为零)。这个过程不会改变向量空间的维度,只是改变了基的选择。正交化的一个常用方法是Gram-Schmidt过程。
-
主成分分析(PCA):PCA的目标是找到一组新的正交向量(即主成分),这些向量能够最大化原始数据在其上的方差。PCA通常用于降维,即将数据从高维空间映射到低维空间,同时尽可能保留数据的变异性。PCA的一个重要应用是特征提取,它可以找到最能代表数据的特征。
总的来说,正交化和PCA都可以使得向量正交,但是PCA除了使得向量正交之外,还会选择那些能够最大化方差的方向作为主成分,从而实现降维和特征提取。
下面是规范正交化的具体实现:
def canonical(matrix):
F = np.array(matrix,dtype=np.float64)
Lambda,Q = np.linalg.eigh(np.dot(F.T, F))
D = np.diag(Lambda ** (-0.5))
D = np.where(np.isnan(D), 0, D)
Fhat = np.dot(F,Q)
Fhat = np.dot(Fhat,D)
return Fhat
由于规范正交过程中需要计算特征值的除法,因子之间的相关性较大时,规范正交化的计算精度会受到影响。
特征值分解之后,每个截面的因子没有稳定的对应关系,特征值变化会导致新的因子每一列对应的因子有变化,因此规范正交化的结果不唯一。
- (3)对称正交化(Symmetric Orthogonalization)
从前面两种正交方式的表现来看,施密特正交由于在过去若干个截面上都取同样的因子正交顺序,因此正交后的因子和原始因子有显式的对应关系,而规范正交在每个截面上特征值对应关系有变化,这使得新的因子和原始因子没有稳定的对应关系。正交后组合的效果很大一部分取决于正交前后因子是否有稳定的对应关系。一种正交的思路是尽可能减少对原始因子矩阵FN∗KF_{N*K}FN∗K的修改而得到一组正交基。这样能够最大程度地持正交后因子和原因子的相似性。并且,我们希望对每个因子平等对待,避免像施密特正交法中偏向正交顺序中靠前的因子。
我们取CK∗K=IK∗KC_{K*K}=I_{K*K}CK∗K=IK∗K,则有
SK∗K=MK∗K−1/2CK∗KS_{K*K} = M_{K*K}^{-1/2}C_{K*K} SK∗K=MK∗K−1/2CK∗K
=MK∗K−1/2=QK∗KΛK∗K−1/2QK∗KT公式(8)= M_{K*K}^{-1/2} = Q_{K*K}\Lambda_{K*K}^{-1/2}Q_{K*K}^{T}\qquad\text{公式(8)}=MK∗K−1/2=QK∗KΛK∗K−1/2QK∗KT公式(8)
在所有正交过渡矩阵中,对称正交后的矩阵和原始矩阵的相似性最大,即正交前后矩阵的距离最小。
下面是对称正交的具体实现:
def symmetric(matrix):
F = np.array(matrix,dtype=np.float64)
# 求$M_{K*K}$的特征值和特征向量
Lambda,Q = np.linalg.eigh(np.dot(F.T, F))
D = np.diag(Lambda ** (-0.5))
D = np.where(np.isnan(D), 0, D)
Fhat = np.dot(F,Q)
Fhat = np.dot(Fhat,D)
Fhat = np.dot(Fhat,Q.T)
for i in range(Fhat.shape[1]):
Fhat[:,i] = Fhat[:,i] / np.sqrt(np.dot(Fhat[:,i], Fhat[:,i]))
return Fhat
对称正交和施密特(Gram-Schmidt)正交都是正交化的方法,它们各有优缺点:
- 施密特正交(Gram-Schmidt Orthogonalization):
优点:
算法简单,易于理解和实现。
缺点:
1. 对于数值稳定性不够好。当处理的向量近似线性相关,或者向量的数量非常大时,可能会积累较大的数值误差。
2. 生成的正交向量依赖于原始向量的顺序,改变输入向量的顺序可能会得到不同的结果。
3. 计算时间较长。
- 对称正交(Symmetric Orthogonalization):
优点:
(1). 数值稳定性更好,不容易受到数值误差的影响。
(2). 生成的正交向量不依赖于原始向量的顺序。
缺点:
(1). 算法复杂度较高,需要计算矩阵的特征值和特征向量,对于大规模问题可能会比较慢。
(2). 算法理解和实现相对复杂。
(3). 矩阵分解后,可能会积累较大的数值误差。
下图是斯密特正交与对称正交的示意图,
从图中可以看到,相对于施密特正交,对称正交对每个因子是平等看待的,它们各自旋转了同样的角度来得到一组正交基 𝑓̃1, 𝑓̃2,并且正交前后的因子对应关系保持得最好,正交前后因子的相关性相比于施密特正交也保持得更高,因此可解释度也更强。另外相比于施密特正交,对称正交只需要截面上因子取值的数据即可计算,不需要依赖其他历史数据来确定因子正交的顺序。
(五)因子正交化在商品多因子组合中的运用实践
我们以商品多因子组合为例,对不同的因子正交化方法进行对比和比较。我们选取了基差因子、库存因子、期限结构因子、动量因子、持仓因子等多个维度的因子,对这些因子进行正交化处理,然后对正交化后的因子进行多因子组合加权,比较不同正交化方法的组合表现。
用original_rets表示原始因子的组合表现,gram_schmidt_rets表示施密特正交化后的因子组合表现,canonical_rets表示规范正交化后的因子组合表现,symmetric_rets表示对称正交化后的因子组合表现。neufacotrs__rets表示正交因子进行一次截面标准化后的新因子的因子组合表现。我们可以看到,施密特正交化、规范正交化后的因子组合表现最差,对称正交化后的因子组合表现最好。同时,我们也发现因子正交之后,直接进行因子加权的表现好于正交之后截面标准化的因子加权组合表现。
从表现上来看,对称正交化后的因子组合表现最好,这是因为对称正交化后的因子组合保持了最好的因子对应关系,因子之间的关联性也保持得最好,同时还控制了因子之间共有因素的暴露,因此对称正交化后的因子组合表现最好。
在回测中,通过构建多空对冲的市值中性组合,回测时间为2019年1月1日至2023年9月30日,每日早上9点05分进行调仓,每次调仓时,根据因子值进行多因子组合加权,然后进行等权持仓。回测结果如下表所示:
- (1)基差因子正交化回测结果
-
(2)库存因子正交化回测结果
-
(3)期限结构因子正交化回测结果
- (4)持仓因子正交化回测结果
- (5)动量因子正交化回测结果
从上述回测结果可以看出,对于原始因子,堆成正交化后的因子收益率和夏普比率指标均有所提升,尤其是对于动量因子,正交化后的因子收益率和风险指标提升最为明显。因此,对于因子正交化的方法,可以有效提升因子的收益率和风险指标,提高因子的有效性。
(六) 总结
在实际应用中,因子正交化的方法可以有效提升因子的有效性,在多因子加权时,由于因子之间可能存在多重共线性,进而导致加权后的组合整体在某些因子上的重复暴露,从而影响组合的长期表现。因此,有必要对因子进行正交化,基于正交后的因子进行加权,可以提升组合长期表现的稳定性。
因子正交化本质上是对原始因子进行旋转,旋转后得到一组两两正交的新因子,它们之间的相关性为零并且对于收益的解释度保持不变。本文提出了一套因子正交的统一框架,介绍三种特殊的因子正交方法,包括施密特正交、规范正交和对称正交。以商品多因子为例,回测结果表明使用对称正交表现最好,对称正交是使正交前后矩阵距离最小的正交方式,对正交前后的因子相关性保持得非常好,可解释性较高,并且计算不依赖历史数据以及收益率数据,因此我们推荐使用对称正交方法来剔除因子间的多重共线性。
欢迎各位读者关注作者的微信公众号,
免责申明
本文所提供的任何内容和结论仅限于作者自身认知和理解,并不对这些信息的准确性、真实性、完整性做任何保证,任何人根据本文的内容做出任何决策产生的后果与本文作者无关。