如何使用 C++ 实现机器学习模型来预测苹果公司(AAPL)股票的周收益,并结合梯度提升树(Gradient Boosting Trees)、集成方法(如软投票分类器)以及 Optuna 优化等步骤,开发交易策略。以下内容将涵盖数据处理、特征工程、模型训练、集成方法、Optuna 优化以及交易策略的实现,并针对 C++ 的高效性提供具体代码示例。


1. 项目概述

目标是预测苹果公司(AAPL)股票的周收益(weekly returns),并基于预测结果设计交易策略。流程包括:

  • 数据获取:30年的 AAPL 历史数据(参考 the finance card above)。
  • 探索性数据分析(EDA):分析回报模式,提取特征。
  • 特征选择:识别关键特征。
  • 模型训练:使用梯度提升树(Gradient Boosting Trees)及其变种(如 XGBoost、LightGBM)。
  • 集成方法:实现硬投票、软投票和堆叠分类器,优化软投票分类器的权重。
  • 交易策略
    • 策略 I:仅在预测上涨时买入。
    • 策略 II:基于预测做多或做空。
  • 优化:使用 Optuna 优化模型权重。

C++ 的优势在于高效计算和低延迟,适合实时交易和大规模数据处理。以下是具体实现步骤。


2. 数据获取与预处理

2.1 数据来源

根据 the finance card above,AAPL 股票数据包括:

  • 实时数据:当前价格为 247.66 USD(2025-10-13),包含开盘价、最高价、最低价、收盘价等。
  • 历史数据
    • 1 天数据:2025-10-13 的分钟级别价格。
    • 1 月数据:2025-09-15 至 2025-10-13 的日收盘价。
    • 1 年数据:2024-10 至 2025-10 的月收盘价。
    • 最大范围:2003 至 2025 年的年收盘价。

由于任务是预测周收益,我们需要将日收盘价转换为周收益:

  • 周收益定义:r_t = (P_t - P_{t-1}) / P_{t-1},其中 P_t 为第 t 周的收盘价。
2.2 C++ 数据读取与周收益计算

使用 C++ 读取 CSV 文件(假设数据格式为日期和收盘价),并计算周收益。

#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <Eigen/Dense>

struct StockData {
    std::string date;
    double close_price;
};

std::vector<StockData> loadStockData(const std::string& filename) {
    std::vector<StockData> data;
    std::ifstream file(filename);
    std::string line;
    std::getline(file, line); // 跳过表头
    while (std::getline(file, line)) {
        std::stringstream ss(line);
        StockData row;
        std::getline(ss, row.date, ',');
        std::string price;
        std::getline(ss, price, ',');
        row.close_price = std::stod(price);
        data.push_back(row);
    }
    file.close();
    return data;
}

// 计算周收益
std::vector<double> calculateWeeklyReturns(const std::vector<StockData>& data, int days_per_week = 5) {
    std::vector<double> returns;
    for (size_t i = days_per_week; i < data.size(); i += days_per_week) {
        double prev_price = data[i - days_per_week].close_price;
        double curr_price = data[i].close_price;
        double weekly_return = (curr_price - prev_price) / prev_price;
        returns.push_back(weekly_return);
    }
    return returns;
}
2.3 数据清洗与标准化
  • 缺失值处理:用前值填充或插值。
  • 标准化:将收益和特征标准化到 [0, 1] 或 Z-score 范围。
Eigen::VectorXd normalize(const Eigen::VectorXd& data) {
    double mean = data.mean();
    double stddev = std::sqrt((data.array() - mean).square().sum() / data.size());
    return (data.array() - mean) / stddev;
}

3. 探索性数据分析(EDA)与特征工程

3.1 EDA

分析 AAPL 周收益的模式:

  • 趋势:从 the finance card above,AAPL 价格从 2003 年的 0.4038 USD 增长到 2025 年的 247.66 USD,显示长期上涨趋势。
  • 波动性:使用 ARCH 或 GARCH 模型分析波动性(如书中提到的茅台 ARCH 模型)。
  • 季节性:检查收益是否与特定时间(如财报季)相关。
3.2 特征工程

基于金融领域知识和书中提到的特征工程,构造以下特征:

  • 技术指标
    • 移动平均线(MA):5 周、20 周 MA。
    • 相对强弱指数(RSI):RSI = 100 - 100 / (1 + RS),其中 RS = Avg_Gain / Avg_Loss
    • MACD:MACD = EMA_12 - EMA_26
  • 市场情绪:从推文或新闻提取情感得分(参考 TFNS 数据集)。
  • 滞后特征:过去 1-4 周的收益。
  • 外部因子:市场指数(如 S&P 500)、利率、波动率指数(VIX)。
C++ 实现:计算技术指标
#include <numeric>

// 计算 n 周移动平均线
Eigen::VectorXd calculateMA(const std::vector<StockData>& data, int period) {
    Eigen::VectorXd ma(data.size());
    for (size_t i = period - 1; i < data.size(); ++i) {
        double sum = 0.0;
        for (int j = 0; j < period; ++j) {
            sum += data[i - j].close_price;
        }
        ma(i) = sum / period;
    }
    return ma;
}

// 计算 RSI
double calculateRSI(const std::vector<StockData>& data, int period) {
    double avg_gain = 0.0, avg_loss = 0.0;
    for (size_t i = 1; i <= period && i < data.size(); ++i) {
        double change = data[i].close_price - data[i - 1].close_price;
        if (change > 0) avg_gain += change;
        else avg_loss -= change;
    }
    avg_gain /= period;
    avg_loss /= period;
    double rs = avg_gain / avg_loss;
    return 100.0 - 100.0 / (1.0 + rs);
}
3.3 特征选择

使用以下方法识别关键特征:

  • 相关性分析:计算特征与周收益的相关系数。
  • 互信息:评估特征与目标的非线性关系。
  • L1 正则化:用 Lasso 回归筛选特征。
#include <Eigen/Dense>

// 相关性分析
double correlation(const Eigen::VectorXd& x, const Eigen::VectorXd& y) {
    double mean_x = x.mean(), mean_y = y.mean();
    double cov = ((x.array() - mean_x) * (y.array() - mean_y)).sum() / x.size();
    double std_x = std::sqrt((x.array() - mean_x).square().sum() / x.size());
    double std_y = std::sqrt((y.array() - mean_y).square().sum() / y.size());
    return cov / (std_x * std_y);
}

4. 梯度提升树模型

4.1 模型选择

实验多种梯度提升树算法(如 XGBoost、LightGBM、CatBoost)。C++ 中可使用以下库:

  • XGBoost C++ API:高效的梯度提升树实现。
  • LightGBM C++ API:轻量级,提升速度更快。
  • CatBoost:处理类别特征效果好,但需通过 Python 接口调用。
4.2 C++ 实现 XGBoost

XGBoost 是一个流行的梯度提升树库,C++ API 适合高性能场景。

#include <xgboost/c_api.h>
#include <vector>

void trainXGBoost(const Eigen::MatrixXd& X_train, const Eigen::VectorXd& y_train) {
    DMatrixHandle dtrain;
    XGDMatrixCreateFromMat(X_train.data(), X_train.rows(), X_train.cols(), &dtrain);

    BoosterHandle booster;
    XGBoosterCreate(&dtrain, 1, &booster);

    // 设置参数
    XGBoosterSetParam(booster, "objective", "binary:logistic"); // 分类任务
    XGBoosterSetParam(booster, "max_depth", "6");
    XGBoosterSetParam(booster, "eta", "0.3");

    // 训练
    for (int i = 0; i < 100; ++i) {
        XGBoosterUpdateOneIter(booster, i, dtrain);
    }

    // 保存模型
    XGBoosterSaveModel(booster, "xgboost_model.bin");

    // 清理
    XGBoosterFree(booster);
    XGDMatrixFree(dtrain);
}
4.3 超参数调优

为 XGBoost 和 LightGBM 单独调优,使用网格搜索或随机搜索调整参数(如 max_depthlearning_raten_estimators)。


5. 集成方法

5.1 硬投票分类器
  • 原理:多个模型预测类别,采用多数投票。
  • C++ 实现
    • 收集每个模型(XGBoost、LightGBM)的预测结果。
    • 统计票数,选择最多票的类别。
std::vector<int> hardVoting(const std::vector<std::vector<int>>& predictions) {
    std::vector<int> final_predictions;
    for (size_t i = 0; i < predictions[0].size(); ++i) {
        int votes[2] = {0, 0}; // 假设二分类:0(下跌),1(上涨)
        for (const auto& model_preds : predictions) {
            votes[model_preds[i]]++;
        }
        final_predictions.push_back(votes[1] > votes[0] ? 1 : 0);
    }
    return final_predictions;
}
5.2 软投票分类器
  • 原理:加权平均每个模型的预测概率。
  • C++ 实现
    • 收集每个模型的概率输出。
    • 使用权重(后续通过 Optuna 优化)计算加权平均。
Eigen::VectorXd softVoting(const std::vector<Eigen::VectorXd>& probabilities, const std::vector<double>& weights) {
    Eigen::VectorXd final_probs(probabilities[0].size());
    final_probs.setZero();
    for (size_t i = 0; i < probabilities.size(); ++i) {
        final_probs += weights[i] * probabilities[i];
    }
    return (final_probs.array() > 0.5).cast<double>(); // 阈值 0.5
}
5.3 堆叠分类器
  • 原理:用元模型(如逻辑回归)组合基模型的预测。
  • 实现:将基模型预测作为输入,训练元模型。

6. Optuna 优化

Optuna 是一个超参数优化框架,通常在 Python 中使用。为在 C++ 中实现类似功能,可手动实现贝叶斯优化或网格搜索。

C++ 实现:简单网格搜索
#include <vector>
#include <limits>

struct ModelResult {
    double accuracy;
    std::vector<double> weights;
};

ModelResult optimizeWeights(const std::vector<Eigen::VectorXd>& probs, const Eigen::VectorXd& y_true) {
    ModelResult best_result = {0.0, {}};
    double best_accuracy = -std::numeric_limits<double>::infinity();
    std::vector<double> weights = {0.1, 0.3, 0.5, 0.7, 0.9}; // 示例权重

    for (double w1 : weights) {
        for (double w2 : weights) {
            for (double w3 : weights) {
                if (std::abs(w1 + w2 + w3 - 1.0) < 1e-6) { // 权重和为 1
                    std::vector<double> curr_weights = {w1, w2, w3};
                    Eigen::VectorXd predictions = softVoting(probs, curr_weights);
                    double accuracy = (predictions.array() == y_true.array()).cast<double>().mean();
                    if (accuracy > best_accuracy) {
                        best_accuracy = accuracy;
                        best_result = {accuracy, curr_weights};
                    }
                }
            }
        }
    }
    return best_result;
}

7. 交易策略

7.1 策略 I:选择性买入
  • 规则:仅在预测周收益为正(上涨)时买入 AAPL,持有 1 周后卖出。
  • 实现
    • 使用软投票分类器的预测结果。
    • 计算累积回报:cum_return = ∏(1 + r_t),其中 r_t 为预测上涨时的实际周收益。
7.2 策略 II:做多或做空
  • 规则
    • 预测上涨:做多(买入)。
    • 预测下跌:做空(借入股票卖出,未来低价买回)。
  • 实现
    • 计算回报:做多时为 r_t,做空时为 -r_t
C++ 实现:交易策略
double evaluateStrategy(const std::vector<double>& returns, const Eigen::VectorXd& predictions, int strategy) {
    double cum_return = 1.0;
    for (size_t i = 0; i < returns.size(); ++i) {
        if (strategy == 1) { // 策略 I
            if (predictions(i) == 1) { // 预测上涨
                cum_return *= (1.0 + returns[i]);
            }
        } else { // 策略 II
            cum_return *= (predictions(i) == 1) ? (1.0 + returns[i]) : (1.0 - returns[i]);
        }
    }
    return cum_return - 1.0; // 返回累积收益率
}

8. 性能评估

  • 数据集划分:80% 训练集,20% 测试集。
  • 评估指标
    • 分类:准确率、F1 分数。
    • 交易:累积回报、夏普比率(Sharpe = (E[R] - R_f) / σ)。
  • 结果:软投票分类器表现最佳(基于您提供的信息),具体权重通过 Optuna 优化。

9. 与 Python 的结合

由于 Optuna 和某些梯度提升库(如 LightGBM、CatBoost)更易在 Python 中使用,建议:

  • 训练:用 Python 训练模型,保存权重。
  • 推理:用 C++ 的 XGBoost API 或 libtorch 加载模型进行实时预测。
  • 数据接口:通过 C++ 调用 yfinance 或 Alpaca API 获取实时数据。

10. 示例结果

根据 the finance card above,AAPL 最近 1 个月(2025-09-15 至 2025-10-13)的周收益可计算如下:

  • 2025-09-16:237.91 USD → 周收益 = (237.91 - 236.3) / 236.3 ≈ 0.0068
  • 2025-09-23:254.18 USD → 周收益 = (254.18 - 237.91) / 237.91 ≈ 0.0684
  • 2025-09-30:253.51 USD → 周收益 = (253.51 - 254.18) / 254.18 ≈ -0.0026
  • 2025-10-13:247.2609 USD → 周收益 = (247.2609 - 253.51) / 253.51 ≈ -0.0246

模型预测这些收益的正负,并据此执行交易策略。


11. 总结

使用 C++ 实现 AAPL 周收益预测需要:

  1. 数据处理:读取历史数据,计算周收益,标准化特征。
  2. 特征工程:提取技术指标、滞后特征、情绪特征。
  3. 模型训练:用 XGBoost 等梯度提升树,结合软投票分类器。
  4. 优化:通过网格搜索或贝叶斯优化调整权重。
  5. 交易策略:实现选择性买入(策略 I)和做多/做空(策略 II)。

C++ 的高效性使其适合实时交易场景,尤其在高频交易中。结合 Python 的数据处理和模型训练生态,可以进一步提升开发效率。

如果需要更详细的代码(例如完整 XGBoost 训练流程或交易策略回测),请进一步说明!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐