ppt

核心任务

制作一个根据投资者需求提供投资组合建议的交互式界面,利用机器学习与多因子结合选股,用portfolio analytics包进行组合优化,对组合进行可视化分析,通过shiny页面进行交互。

分工协作

陈酉琰-–数据获取与机器学习

芮子涵-–投资组合优化学习

林文佳-–数据可视化

张亚文-–Shiny交互页面学习

目前进展

行情数据获取

导入提前获取好的沪深300成分股的代码。

Code
df <- read.csv("hs300_stocks.csv", fileEncoding = "GBK")
stock_codes <- df$code

使用pedquant包获取数据

使用者运行程序,就会自动获取最近两个月的行情数据。

Code
# 获取当前日期
end_date <- Sys.Date()
start_date <- end_date - months(2)

# 循环获取每支股票的行情数据并添加到结果数据框中
for (code in stock_codes) {
  tryCatch({
    # 获取股票行情数据
    stock_data <- md_stock(code, from = start_date, to = end_date, source = '163', adjust = 'dividend')
    if (!is.null(stock_data)) {
      print(paste("成功获取股票", code, "的行情数据"))
      
      # 将获取到的股票数据转换为数据框格式并存储到结果数据框中
      stock_data_df <- stock_data[[1]] %>% as.data.frame()
      print("获取到的数据结构信息:")
      str(stock_data_df)
      
      result_df <- rbind(result_df, stock_data_df)
    } else {
      print(paste("未能获取股票", code, "的行情数据。"))
    }
  }, error = function(e) {
    print(paste("获取股票", code, "的行情数据时出错:", e))
  })
}

结果如下:

机器学习

构建动量因子

  • 5日乖离率 BIAS5:(收盘价-收盘价的N日简单平均)/ 收盘价的N日简单平均*100,在此取n=5

  • 60日变动速率 ROC60(Price Rate of Change):①AX=今天的收盘价—20天前的收盘价 ②BX=60天前的收盘价 ③ROC=AX/BX*100

  • 单日价量趋势 single_day_VPT:(今日收盘价 - 昨日收盘价)/ 昨日收盘价 * 当日成交量

(复权方法为基于当日前复权)

  • 阿隆(Aroon)指标

Aroon指标下轨 arron_down_25:Aroon(下降)=[(计算期天数-最低价后的天数)/计算期天数]*100

Aroon指标上轨 arron_up_25:Aroon(上升)=[(计算期天数-最高价后的天数)/计算期天数]*100

  • BBI 动量:BBI(3, 6, 12, 24) 的算数平均/ 收盘价

BBI 为常用技术指标类因子“多空均线”

构建出的动量因子表:

构建目标变量:涨跌幅

  • 目标变量fluctuation = (第二天的收盘价-第一天的收盘价)/第一天的收盘价
  • 人为划分训练集和测试集:

今日行情作为预测集(predi),用于预测明日涨跌幅;其余数据作为训练集(train)

Code
train <- momentum_s %>% filter(momentum_s$date < Sys.Date()) %>% na.omit()
predi <- anti_join(momentum_s, train, by = names(momentum_s)) %>% na.omit()

机器学习:LGBM模型与k折交叉验证

  • 设置特征和目标变量:行情数据和因子作为样本的特征;次日收益率(涨跌幅)fluctuation作为目标变量:。
Code
# 把行情数据和因子作为样本的特征,去掉不需要的字段
X <- as.matrix(train[,!(names(train) %in% c("name", "open", "high", "low", "close", "fluctuation", "signal"))])

# 把fluctuation作为标签
y <- train$fluctuation
  • 设置参数:

objective = "regression":目标任务是回归任务;

metric = "rmse":设定用于评估模型性能的指标为均方根误差 RMSE (预测值与真实值差值的平方的平均值的平方根)

Code
# 设置模型参数
params <- list(
  objective = "regression",
  metric = "rmse",
  n_estimators = 1000,
  verbosity = -1,      # 控制模型训练过程中,不输出过多的中间状态等提示信息
  random_state = 0     # 确保结果的可重复性
)
  • 交叉验证和训练模型

❓WHY交叉验证

股价数据是复杂且动态变化的。简单地将数据集划分为训练集和测试集来评估模型,可能会因为数据划分的非随机性而导致评估结果不够准确。使用交叉验证的方法可以避免单次划分训练集和测试集可能带来的偏差。、

❓WHY LGBM

分布式训练支持

LGBM 模型支持分布式训练,适合股票市场数据海量的情况。通过分布式训练,模型可以在多个计算节点上同时进行训练,加速整个训练过程。

可扩展性强

随着新的股票数据不断产生,LGBM模型可以很容易地对新数据进行整合和重新训练。

Code
# 设置交叉验证的折数,这里取5折
n_folds <- 5

# 进行交叉验证
cv_results <- lgb.cv(
  params = params,
  data = dtrain,
  nfold = n_folds,
  stratified = FALSE,
  verbose = -1  
)

# 训练模型
model <- lgb.train(
  params = params,
  data = dtrain
)
  • 查看效果:均方根误差(RMSE)

RMES值越小表示模型的预测效果越好

Code
# 效果
# 在训练集上进行
y_pred_train <- predict(model, newdata = X)
# 计算均方根误差(RMSE)
rmse_train <- sqrt(mean((y - y_pred_train)^2))
[1] "训练集均方根误差(RMSE): 0.00532665709016357"
  • 预测
Code
# 对预测集进行预测
predi <- as.matrix(predi[,!(names(predi) %in% c("name", "open", "high", "low", "close", "signal", "fluctuation"))])

prediction <- predict(model, newdata = predi)

# 创建一个新的数据框,包含股票代码列和预测结果列
result_df <- data.frame(symbol = predi[, 1], 
                        prediction = prediction, 
                        stringsAsFactors = FALSE) %>% 
  arrange(desc(prediction))
Code
datatable(result_df, options = list(
  pageLength = 10
))

投资组合优化

步骤一:预期收益率

整理股票数据,得到有收益率及预期收益信息的数据框stock_data

Code
merge_df=merge(momentum_s,result_df,by="symbol")
View(merge_df)
stock_data=merge_df <- merge_df[ , -c(4:17)]
stock_data$date<-as.Date(stock_data$date)
stock_data <- stock_data %>%
  arrange(symbol,date)  # 按日期排序
head(stock_data)
     symbol     name       date  fluctuation prediction
1 000001.SZ 平安银行 2024-09-30  0.046270724 0.02275461
2 000001.SZ 平安银行 2024-10-08 -0.079205991 0.02275461
3 000001.SZ 平安银行 2024-10-09  0.039137338 0.02275461
4 000001.SZ 平安银行 2024-10-10 -0.017931872 0.02275461
5 000001.SZ 平安银行 2024-10-11  0.021068417 0.02275461
6 000001.SZ 平安银行 2024-10-14 -0.008253479 0.02275461

按预期收益率降序排序,获取前五名股票(下用top_5代称)

     symbol prediction
1 002555.SZ 0.07341090
2 600276.SH 0.07165852
3 601988.SH 0.06979743
4 001965.SZ 0.06112326
5 002230.SZ 0.05904452

步骤二:协方差矩阵

获取top_5的日收益率数据,并计算得到协方差矩阵

[1] "Covariance Matrix:"
             002555.SZ    600276.SH    601988.SH    001965.SZ    002230.SZ
002555.SZ 0.0043901014 0.0016792468 0.0007838756 0.0021941316 0.0031113477
600276.SH 0.0016792468 0.0010736731 0.0003322849 0.0008498078 0.0012237302
601988.SH 0.0007838756 0.0003322849 0.0002432053 0.0005358282 0.0004428812
001965.SZ 0.0021941316 0.0008498078 0.0005358282 0.0015059169 0.0013340672
002230.SZ 0.0031113477 0.0012237302 0.0004428812 0.0013340672 0.0027651779

步骤三:创建投资组合并进行优化

创建投资组合对象top_5_portfolio,约束条件设置为“完全投资”“非负权重”,并添加目标函数从而最大化预期收益并最小化风险。

Code
# 添加约束条件
top_5_portfolio <- add.constraint(top_5_portfolio, type = "full_investment")
top_5_portfolio <- add.constraint(top_5_portfolio, type = "long_only")

# 添加目标函数:最大化预期收益并最小化风险
top_5_portfolio <- add.objective(top_5_portfolio, type = "return", name = "mean")
top_5_portfolio <- add.objective(top_5_portfolio, type = "risk", name = "StdDev")

利用ROI包进行优化求解。

最终得到优化后的权重如下:

    002555.SZ     600276.SH     601988.SH     001965.SZ     002230.SZ 
 1.285425e-01  8.714575e-01  0.000000e+00  0.000000e+00 -6.110961e-17 

步骤四:优化结果分析

根据优化后的权重,我们可以计算出该组合的预期收益和风险。

Code
# 计算优化后的预期收益和风险
top_5_expected_return_ROI <- sum(top_5_weights_ROI * top_5_stocks$prediction)
top_5_risk_ROI <- sqrt(t(as.matrix(top_5_weights_ROI)) %*% top_5_cov_matrix %*% as.matrix(top_5_weights_ROI))
[1] "ROI - Expected Return: 0.0718837717473201"
[1] "ROI - Risk (Standard Deviation): 0.0355547869671811"

可视化部分

根据portfolio analytics包的运行结果,我们可以获得今日最适宜购买的n支股票,储存在top_5_stocks数据框中。

计算夏普比率

根据top_5的股票代码,在hs300数据框中筛选top_5的股票信息,接着根据(明日开盘价-今日收盘价)/今日收盘价,计算出他们的收益率,接着按照pa包给出的权重进行组合配比,计算出组合的标准差和收益率,计算出组合在2个月内的夏普比率变动情况。

同时对HS300数据框的数据也做同样处理,进行对比。

计算卡尔玛比率

跟夏普相同的计算逻辑,卡尔玛比率是年化收益率与区间最大回撤的比率,代表的含义是:一个投资组合每承担一份最大回撤,获取的收益有多大。

通过对比,我们可以知道我们选取的组合与基准市场组的收益差距有多大。

Shiny

通过学习《mastering shiny》,我们小组已经了解shinyApp的基础结构。

包括:

1、UI界面的布局设计

2、UI界面的函数插入

3、绘图结果展示

部分代码展示:

1、完善shinyApp各部分功能,将前一阶段已完成的核心代码改写、代入shinyApp中。

2、美化shinyApp。

下阶段安排

机器学习

  • LGBM的优势在于调参速度。因子(特征)数量增加,lgbm模型会更拥优势。目前我们传入LGBM的特征只包含动量因子。我们已经用pedquant包的md_stock_financials()函数获取了财务数据,在下阶段我们将尝试往模型中加入估值因子、成长因子、资本结构因子……

投资组合优化

  1. 股票数据量不统一:部分股票数据有缺失,因此在提取收益率时难以创建矩阵来一一对应,最终选择使用列表来存储数据,并忽略数据量上的差异,直接计算出协方差矩阵。

  2. 精度调整:由于精度问题,即使已经设立约束条件,权重结果仍可能包含很小的负数。在尝试更换求解器未果后,决定手动调整精度,再进行归一化处理。

    可视化

以上两种综合考虑风险和收益的投资组合评价,后期会引入与基准投资组合相比较的评价指标,如beta系数(衡量相关性) alpha系数(超额收益)等。也会适当引入基础指标,如收益率、波动率等。

最后由于前面的计算都是基于等权重,所以后期会加入pa包跑出来的权重计算。

     symbol    weight
1 002555.SZ 0.1285425
2 600276.SH 0.1285425
3 601988.SH 0.1285425
4 001965.SZ 0.1285425
5 002230.SZ 0.1285425

Shiny

1、完善shinyApp各部分功能,将前一阶段已完成的核心代码改写、代入shinyApp中。

2、美化shinyApp。

问题与困难

机器学习

获取的行情数据中,某几只股票的数据不全,导致innerjoin过后,这几只股票丢失。之后放入模型的训练集也不包含这些数据。暂时没有想好如何补全这部分数据?


000001.SZ 000002.SZ 000063.SZ 000100.SZ 000157.SZ 000166.SZ 000301.SZ 000333.SZ 
       39        39        39        39        39        39        39        39 
000338.SZ 000408.SZ 000425.SZ 000538.SZ 000568.SZ 000596.SZ 000617.SZ 000625.SZ 
       39        39        39        39        39        39        39        39 
000651.SZ 000661.SZ 000708.SZ 000725.SZ 000733.SZ 000768.SZ 000776.SZ 000786.SZ 
       39        39        39        39        39        39        39        39 
000792.SZ 000800.SZ 000807.SZ 000858.SZ 000876.SZ 000895.SZ 000938.SZ 000963.SZ 
       39        39        39        39        39        39        39        39 
000977.SZ 000983.SZ 000999.SZ 001289.SZ 001965.SZ 001979.SZ 002001.SZ 002007.SZ 
       39        39        39        39        39        39        39        39 
002027.SZ 002049.SZ 002050.SZ 002074.SZ 002129.SZ 002142.SZ 002179.SZ 002180.SZ 
       39        39        39        39        39        39        39        39 
002230.SZ 002236.SZ 002241.SZ 002252.SZ 002271.SZ 002304.SZ 002311.SZ 002352.SZ 
       39        39        39        39        39        39        39        39 
002371.SZ 002410.SZ 002415.SZ 002459.SZ 002460.SZ 002466.SZ 002475.SZ 002493.SZ 
       39        39        39        39        39        39        39        39 
002555.SZ 002594.SZ 002601.SZ 002603.SZ 002648.SZ 002709.SZ 002714.SZ 002736.SZ 
       39        39        39        39        39        39        39        39 
002812.SZ 002821.SZ 002841.SZ 002916.SZ 002920.SZ 002938.SZ 003816.SZ 300014.SZ 
       39        39        39        39        39        39        39        39 
300015.SZ 300033.SZ 300059.SZ 300122.SZ 300124.SZ 300142.SZ 300223.SZ 300274.SZ 
       39        39        39        39        39        39        39        39 
300308.SZ 300316.SZ 300347.SZ 300408.SZ 300413.SZ 300418.SZ 300433.SZ 300442.SZ 
       39        39        39        39        39        39        39        39 
300450.SZ 300454.SZ 300496.SZ 300498.SZ 300628.SZ 300661.SZ 300750.SZ 300751.SZ 
       39        39        39        39        39        39        39        39 
300759.SZ 300760.SZ 300782.SZ 300832.SZ 300896.SZ 300919.SZ 300957.SZ 300979.SZ 
       39        39        39        39        39        39        39        39 
300999.SZ 301269.SZ 600009.SH 600010.SH 600011.SH 600015.SH 600016.SH 600018.SH 
       39        27        18        39        39        39        39        39 
600019.SH 600023.SH 600025.SH 600026.SH 600027.SH 600028.SH 600029.SH 600030.SH 
       39        39        39        39        39        39        39        39 
600031.SH 600036.SH 600039.SH 600048.SH 600050.SH 600061.SH 600085.SH 600089.SH 
       39        39        39        39        39        39        39        39 
600104.SH 600111.SH 600115.SH 600132.SH 600150.SH 600161.SH 600176.SH 600183.SH 
       39        39        39        39        39        39        39        39 
600188.SH 600196.SH 600219.SH 600233.SH 600276.SH 600309.SH 600332.SH 600346.SH 
       39        39        39        39        39        39        39        39 
600362.SH 600372.SH 600406.SH 600415.SH 600426.SH 600436.SH 600438.SH 600460.SH 
       39        39        39        39        39        39        39        39 
600489.SH 600515.SH 600519.SH 600547.SH 600570.SH 600584.SH 600585.SH 600588.SH 
       39        39        39        39        39        39        39        39 
600600.SH 600660.SH 600674.SH 600690.SH 600732.SH 600741.SH 600745.SH 600760.SH 
       39        39        39        39        39        39        39        39 
600795.SH 600803.SH 600809.SH 600837.SH 600845.SH 600875.SH 600886.SH 600887.SH 
       39        39        39        36        39        39        39        39 
600893.SH 600900.SH 600905.SH 600918.SH 600919.SH 600926.SH 600938.SH 600941.SH 
       39        39        39        39        39        39        39        39 
600958.SH 600989.SH 600999.SH 601006.SH 601009.SH 601012.SH 601021.SH 601059.SH 
       39        39        39        39        39        39        39        39 
601066.SH 601088.SH 601100.SH 601111.SH 601117.SH 601138.SH 601166.SH 601169.SH 
       39        39        39        39        39        39        39        39 
601186.SH 601211.SH 601225.SH 601229.SH 601236.SH 601238.SH 601288.SH 601318.SH 
       39        36        39        39        39        39        39        39 
601319.SH 601328.SH 601336.SH 601360.SH 601377.SH 601390.SH 601398.SH 601600.SH 
       39        39        39        39        39        39        39        39 
601601.SH 601607.SH 601618.SH 601628.SH 601633.SH 601658.SH 601668.SH 601669.SH 
       39        39        39        39        39        39        39        39 
601688.SH 601689.SH 601698.SH 601699.SH 601728.SH 601766.SH 601788.SH 601799.SH 
       39        39        39        39        39        39        39        39 
601800.SH 601808.SH 601816.SH 601818.SH 601838.SH 601857.SH 601865.SH 601868.SH 
       39        39        39        39        39        39        39        39 
601872.SH 601877.SH 601878.SH 601881.SH 601888.SH 601898.SH 601899.SH 601901.SH 
       39        39        39        39        39        39        39        39 
601916.SH 601919.SH 601939.SH 601985.SH 601988.SH 601989.SH 601995.SH 601998.SH 
       39        39        39        39        39        39        39        39 
603019.SH 603195.SH 603259.SH 603260.SH 603288.SH 603296.SH 603369.SH 603392.SH 
       39        39        39        39        39        39        39        39 
603501.SH 603659.SH 603799.SH 603806.SH 603833.SH 603899.SH 603986.SH 603993.SH 
       39        39        39        39        39        39        39        39 
605117.SH 605499.SH 688008.SH 688009.SH 688012.SH 688036.SH 688041.SH 688082.SH 
       39        39        39        39        39        39        39        39 
688111.SH 688126.SH 688187.SH 688223.SH 688256.SH 688271.SH 688303.SH 688363.SH 
       39        39        39        39        39        39        39        39 
688396.SH 688599.SH 688981.SH 
       39        39        39 

投资组合优化

  1. 股票数据量不统一:部分股票数据有缺失,因此在提取收益率时难以创建矩阵来一一对应,最终选择使用列表来存储数据,并忽略数据量上的差异,直接计算出协方差矩阵。
  2. 精度调整:由于精度问题,即使已经设立约束条件,权重结果仍可能包含很小的负数。在尝试更换求解器未果后,决定手动调整精度,再进行归一化处理。