本文构建宏观基本面因子并使用机器学习方法对中债10年期国债、中债10年期国开债、中债10年期AAA级地方政府债、中债10年期AAA级城投债以及中债10年期AAA级企业债的价格进行定价并建立预测模型。
固定收益证券定价的驱动因素有五个层面,俗称“五碗面”,即基本面、政策面、供求面、资金面、情绪面。五个层面中的每一项都包含了众多具体的因素,难以简单地划归为“好”的基本面、“坏”的基本面或者“好坏”的其他层面,所以如果建模的话很容易建成高维模型。五个层面之间的相关性又很强,典型的是经济基本面与货币政策面之间的内生关系——央行依据经济基本面制定货币政策,货币政策的目的又是为了调节经济基本面。由于经济结构在变化、市场制度和参与者结构在变化、央行制定政策的依据和逻辑在变化、投资者的决策逻辑在变化、以及其他等等变化因素,固定收益的定价模型很明显也在变化中。影响债券价格的最重要因素是宏观因素,其最典型的数据频率是季度和月度,数据的相对低频导致定价逻辑中归纳的成分被缩小,演绎和博弈的成分被放大。以上因素导致目前市场中对于用量化方法研究固定收益证券并不看好。
计量方法在定价与预测中存在若干不足。首先由于经济与市场结构在变化,模型的结构也在发生变化,不同时期资产价格的驱动因素不同,在线性模型中这表现为模型系数的变化。举例说我国10年国债收益率,在2000年到08年间与CPI相关性很明显,之后这种相关性有所减弱。第二是解释变量之间相关性的问题,在计量模型中会导致有偏估计。我们想要尽可能多尝试一些新的数据构造解释变量,但不可避免地会陷入维度灾难。对于宏观因子,由于经济变量之间有千丝万缕的关系,相关性则会更高一些。滚动地使用Lasso或者使用动态系数模型或许有助于在计量经济方法范围内解决以上问题,但这已经削弱了模型可解释性。第三点是研究对象系统的非线性性质难以用计量模型挖掘。第四是难以确认计量模型的失效究竟是隐藏变量因素还是非线性关系因素。还有一些其他原因,但以上理由足以使得机器学习方法具有吸引力。
1. 因子体系
由于重点在于“预测”,所以解释变量均为现在时刻的信息,被解释变量均为未来时刻的信息,鉴于各类债券内在宏观驱动因素的一致性以及相互之间存在着内在联系,加上所应用的方法对于共线性问题有一定的克服能力,因此判断可以使用同一套因子体系进行几类债券的建模。
具体因子体系如下:
-
债券价格:中债10年期国债、中债10年期国开债、中债10年期AAA级地方政府债、中债10年期AAA级城投债以及中债10年期AAA级企业债的到期收益率,2年期国债和5年期国债的价格,以及美国10年期国债的价格作为国外市场因素影响的代理变量。
-
技术因素:用10年期国债在过去30天的平均收益率作为动量指标。
-
股票市场:采用了沪深300指数及指数的收益率。
-
经济增长:采用了经济增加值同比、实际消费同比和实际固定资产投资同比,其中实际消费同比和实际固定资产投资同比是分别使用CPI和固定资产投资价格指数构造而来的,均为月度数据。
-
房地产市场:采用房地产开发投资、房屋施工面积、房屋新开工面积、房屋竣工面积、商品房销售面积以及商品房销售额的月度同比。
-
价格指数:包括CPI、食品价格指数、能源价格指数、服务业价格指数、PPI和PPIRM。
-
商品价格:包括IPE、WTI和Oman的石油现价,以及上海期货交易所的螺纹钢主力合约价格和COMEX的黄金主力合约价格。
-
货币发行量:包括M2、社融和信贷总额的同比增长率。
-
资金市场:由于DR007波动性太强,仅采用了Shibor1W。
在进行模型训练和预测时,以上解释变量会取值预测时长相应阶数的滞后项。
2. 模型与参数
本文采用的主要模型为随机森林、LightGBM和BP神经网络。
模型参数调参过程均以预测22天后10年期国债价格的参数为基准,调参后,其余预测对象或预测期限采用相同的模型参数。
经过调参,随机森林的参数有:n_estimators= 120, max_features='sqrt', max_depth=15。
LightGBM的主要参数为:
params = {
'num_leaves': 51,
'max_depth':27,
'n_estimators': 300,
'feature_fraction': 0.75,
}
神经网络的结构如下所示,其中模型输入的长度为35,模型只有一层,为避免模型自由度而仅设置了38个神经元。第一层的激活函数采用ReLu,第二层dropout的比率设置为0.1,第三层输出层选用线性激活函数。
Layer (type) | Output Shape | Param # |
---|---|---|
dense_22 (Dense) | (None, 38) | 1368 |
dropout_23 (Dropout) | (None, 38) | 0 |
dense_23 (Dense) | (None, 1) | 39 |
Total params: 1,407 | ||
Trainable params: 1,407 | ||
Non-trainable params: 0 |
神经网络模型的编译器中采用'mean_squared_error'作为损失测度,选用’Adam’作为优化器。模型训练730轮,batch_size为128。
3. 预测结果与模型比较
为比较模型准确性,采用模型在测试集上的RMSE作为表现的测度。
鉴于债券利率具有连续性特征,将当前利率后移预测时长的时间阶数,并与实际的未来利率之间求RMSE作为baseline的表现分数。
本文主要预测10天(两周)、22天(一个月)、65天(三个月)、130天(半年)这几个时间长度。
预测结果以表格整理如下:
表 中债10年期国债的预测表现
随机森林 | LightGBM | 神经网络 | |
---|---|---|---|
baseline-10d | 0.0923 | ||
RMSE-10d | 0.0340 | 0.0446 | 0.0808 |
baseline-1M | 0.1400 | ||
RMSE-1M | 0.0337 | 0.0431 | 0.1004 |
baseline-3M | 0.2665 | ||
RMSE-3M | 0.0356 | 0.0433 | 0.1203 |
baseline-6M | 0.3907 | ||
RMSE-6M | 0.0358 | 0.0441 | 0.1010 |
表 中债10年期国开债的预测表现
随机森林 | LightGBM | 神经网络 | |
---|---|---|---|
baseline-10d | 0.1245 | ||
RMSE-10d | 0.0454 | 0.0619 | 0.1060 |
baseline-1M | 0.2018 | ||
RMSE-1M | 0.0413 | 0.0551 | 0.1348 |
baseline-3M | 0.3989 | ||
RMSE-3M | 0.0440 | 0.0547 | 0.1613 |
baseline-6M | 0.5958 | ||
RMSE-6M | 0.0446 | 0.0558 | 0.1430 |
表 中债10年期AAA级地方政府债的预测表现
随机森林 | LightGBM | 神经网络 | |
---|---|---|---|
baseline-10d | 0.0952 | ||
RMSE-10d | 0.0344 | 0.0452 | 0.0872 |
baseline-1M | 0.1516 | ||
RMSE-1M | 0.0333 | 0.0491 | 0.1114 |
baseline-3M | 0.3025 | ||
RMSE-3M | 0.0353 | 0.0455 | 0.1021 |
baseline-6M | 0.4585 | ||
RMSE-6M | 0.0351 | 0.0463 | 0.1154 |
表 中债10年期AAA级城投债的预测表现
随机森林 | LightGBM | 神经网络 | |
---|---|---|---|
baseline-10d | 0.1187 | ||
RMSE-10d | 0.0331 | 0.0419 | 0.0948 |
baseline-1M | 0.2021 | ||
RMSE-1M | 0.0315 | 0.0427 | 0.1346 |
baseline-3M | 0.3909 | ||
RMSE-3M | 0.0365 | 0.0502 | 0.1611 |
baseline-6M | 0.5842 | ||
RMSE-6M | 0.0363 | 0.0551 | 0.1633 |
表 中债10年期AAA级企业债的预测表现
随机森林 | LightGBM | 神经网络 | |
---|---|---|---|
baseline-10d | 0.1180 | ||
RMSE-10d | 0.0342 | 0.0440 | 0.0937 |
baseline-1M | 0.1993 | ||
RMSE-1M | 0.0326 | 0.0424 | 0.1361 |
baseline-3M | 0.3814 | ||
RMSE-3M | 0.0367 | 0.0502 | 0.1595 |
baseline-6M | 0.5636 | ||
RMSE-6M | 0.0356 | 0.0510 | 0.1404 |
通过对模型的对比可以看出:
(1)随机森林是本文框架内表现最优的模型,LightGBM次之,本文构建的简单神经网络虽优于基准,但明显劣于集成学习模型。
(2)预测误差随时间拉长会有所增加,但误差增加幅度缓慢。这也反映出经济信号向债券市场传导是逐步过程。
(3)如果以baseline分数除以模型RMSE作为模型表现的度量的话,从品种角度来讲构建的模型对城投债的预测能力是最好的。我们认为是指标体系中包含的地产市场指标与城投债相关性强而导致的。实际上,通过随机森林模型中的feature_importances_也能看出城投债的预测模型中房地产投资和房屋施工面积指标有相对高的重要性。
虽然本文模型测试结果中神经网络表现不佳,但这不能否认该方法的潜力。首先,本文使用的神经网络结构过于简单,参数也未进行精细化调整,这或许妨碍了模型表现。其次,我们尚未尝试LSTM等神经网络的时序方法,这些时序特征在现有因子体系下是通过随机森林等算法难以充分挖掘的。
4. 示例代码
以下是用国债实现的预测22天的示例代码。
dat = pd.concat([T10Y,GK10Y,GB10YAAA,UIB10YAAA,CB10YAAA,...],axis=1)
dat = dat.fillna(method='ffill')
dat = dat.dropna()
timelens = 2250
predictlens = 22
npx = dat.shift(predictlens).dropna().tail(timelens).values
# npxx = dat.drop('T10Y',axis=1).shift(predictlens).dropna().tail(timelens).values
npy = dat.T10Y.tail(timelens).values
# npyy = (dat.T10Y.diff(1)*100).tail(timelens).values
baselinescore = mean_squared_error(dat.shift(predictlens).T10Y.tail(timelens).values,dat.T10Y.tail(timelens).values)**0.5
print('The baseline score is:',baselinescore)
# Randomforest
from sklearn.ensemble import RandomForestRegressor
X_train,X_test, y_train, y_test = train_test_split(npx,npy,test_size=0.25, random_state=0)
rfreg = RandomForestRegressor(n_estimators= 120, max_features='sqrt', max_depth=15, random_state=0)
rfreg.fit(X_train, y_train)
rfy_pred = rfreg.predict(X_test)
print('RMSE for randomforest is:',mean_squared_error(rfy_pred,y_test)**0.5)
# LightGBM
import lightgbm as lgb
# 转换为Dataset数据格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# 参数
params = {
'task': 'train',
'boosting_type': 'gbdt', # 设置提升类型
'objective': 'regression', # 目标函数
'metric': {'l2', 'mse'}, # 评估函数
'lambda_l1': 0, # 使用L1正则化可降低过拟合
'num_leaves': 51, # 叶子节点数
'max_depth':27, # 限制树的深度
'n_estimators': 300,
'learning_rate': 0.03, # 学习速率
'feature_fraction': 0.75, # 建树的特征选择比例
'bagging_fraction': 0.5, # 建树的样本采样比例
'bagging_freq': 5, # k 意味着每 k 次迭代执行bagging
'verbose': 1 # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
}
# 模型训练
gbm = lgb.train(params, lgb_train, num_boost_round=200, valid_sets=lgb_eval,early_stopping_rounds=5)
# 模型预测
gbmy_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration)
# 模型评估
print('The rmse of prediction for LightGBM is:', mean_squared_error(y_test, gbmy_pred) ** 0.5)
# Neural Network
from keras import models
from keras import layers
# from keras import regularizers
x_shape = len(npx[0])
# 归一化
min_max_scaler = MinMaxScaler()
npxx = min_max_scaler.fit_transform(npx)
#npyy = min_max_scaler.fit_transform(npy.reshape(-1,1))
# 重新构造训练集合
nnX_train,nnX_test, nny_train, nny_test = train_test_split(npxx,npy,test_size=0.25, random_state=0)
model = models.Sequential()
# 神经输入层,x_shape个输入数值
model.add(layers.Dense(38, activation='relu', input_shape=(x_shape,)))
model.add(layers.Dropout(0.1))
# 输出层激活函数使用linear
model.add(layers.Dense(1, activation='linear'))
model.compile(loss='mean_squared_error',
optimizer='Adam')
history = model.fit(nnX_train, nny_train, epochs=730, batch_size=128, validation_data=(nnX_test,nny_test))
nny_pred = model.predict(nnX_test)
# print(y_pred[-5:])
# 绘制训练损失和验证损失
import matplotlib.pyplot as plt
loss = history.history['loss'][100:]
val_loss = history.history['val_loss'][100:]
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
print('The rmse of prediction for Neural Network is:', mean_squared_error(nny_test, nny_pred) ** 0.5)
print('The rmse of prediction for LightGBM is:', mean_squared_error(y_test, gbmy_pred) ** 0.5)
print('RMSE for randomforest is:',mean_squared_error(rfy_pred,y_test)**0.5)
print('The baseline score is:',baselinescore)