跳转至

无偏交易模拟

无偏交易模拟的核心目标是消除 “未来数据泄露” 和 “过拟合” 对交易系统性能评估的干扰,通过模拟真实交易场景下的 “训练 - 测试” 流程,获取对系统未来表现的无偏估计。其核心原理与思想可概括为以下四点:

  1. 严格的时间序列数据划分逻辑 样本内(IS)与样本外(OOS)分离:必须按时间戳对市场数据(如对数价格 K 线)进行排序,确保样本内数据完全早于样本外数据,杜绝 “用未来数据训练模型” 的未来泄露。划分时需控制样本内占比(通常 0.5~0.8),且保证两者均有足够数据量(至少各 1 个有效数据点),避免因数据量不足导致的评估偏差。 守卫缓冲区(Guard Buffer)机制:针对指标回溯期(如移动平均计算所需的 K 线数)和目标前瞻期(如预测未来 N 根 K 线收益),在样本内与样本外的边界处设置 “守卫缓冲区”(大小 = min (回溯期,前瞻期)-1)。例如,若指标需回溯 20 根 K 线、目标前瞻 10 根 K 线,则缓冲区为 9 根 K 线,避免样本内数据与样本外数据因 “指标计算重叠” 导致的隐性未来泄露。
  2. 前向检验(Walkforward Analysis):模拟真实交易迭代流程 滑动窗口训练 - 测试:将历史数据按 “训练窗口 + 测试窗口” 划分为连续的 “折叠(Fold)”,每个折叠内先用训练窗口数据优化系统参数(如移动平均周期),再用测试窗口数据计算样本外收益,随后将整个窗口向前滑动(滑动步长 = 测试窗口大小),重复流程直至遍历所有数据。 贴合真实交易场景:每轮训练仅使用 “截至当前时间点的历史数据”,测试仅针对 “未来有限时间窗口”,完全复现真实交易中 “定期更新模型、逐步执行交易” 的过程,避免一次性用全量数据训练导致的过拟合,同时能捕捉市场非平稳性对系统性能的影响。
  3. 交叉验证(Cross-Validation):高效利用数据的补充评估 时间序列适配的折分逻辑:将数据按时间划分为 N 个连续的 “折(Fold)”,每个折轮流作为测试集,剩余折(排除测试集及守卫缓冲区)作为训练集。与传统交叉验证不同,需在测试集的 “前后两侧” 均设置守卫缓冲区,防止因指标回溯 / 前瞻导致的样本内 - 测试集数据重叠。 平衡数据利用率与无偏性:解决前向检验 “早期训练数据量少” 的问题,通过多轮训练 - 测试充分利用历史数据,但需注意其局限性 —— 因包含 “测试集未来的数据” 用于训练,可能对非平稳市场的评估引入轻微乐观偏差,故通常作为前向检验的补充,而非替代。
  4. 嵌套前向检验:处理多层优化的选择偏差 两层优化分离:针对 “系统参数优化(内层)+ 多系统选择(外层)” 的场景,需通过嵌套结构消除 “选择偏差”。内层用前向检验评估每个竞争系统(如不同参数的移动平均策略)的样本外性能,外层基于内层的无偏结果选择最优系统,再用独立的外层测试窗口验证最终性能。 避免 “幸存者偏差”:若直接从多个系统中选择 “样本内表现最好的系统”,易因 “偶然的运气成分” 高估性能;嵌套结构通过 “内层用 OOS 结果筛选系统、外层用新 OOS 结果验证”,确保选中的系统是 “真实具备泛化能力” 而非 “偶然拟合噪声” 的系统。
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <numeric>
#include <vector>

// 错误码定义(无偏模拟的异常场景)
enum class SimErrorCode : int32_t {
  SUCCESS = 0,              // 成功
  INVALID_RATIO = 1,        // 样本内占比非法(≤0或≥1)
  INSUFFICIENT_DATA = 2,    // 数据量不足(无法满足窗口/折数要求)
  EMPTY_TRAIN_DATA = 3,     // 训练集为空(排除守卫缓冲区后)
  EMPTY_SYSTEM_LIST = 4,    // 竞争系统列表为空
  INVALID_WINDOW_SIZE = 5,  // 窗口大小非法(外层训练窗口超限或内层周期非法)
  INVALID_MA_PARAM = 6,     // 均线参数非法(短均线最大周期≥长均线最大周期)
  TIMESTAMP_ORDER_ERR = 7   // 时间戳非递增(数据乱序,可能导致未来泄露)
};

// 市场数据结构体(使用对数价格确保收益尺度一致性)
struct MarketData {
  double log_price;  // 对数价格(用于收益计算,避免价格绝对值偏差)
  int timestamp;     // 时间戳(确保数据按时间顺序处理,杜绝未来泄露)
};

// 交易系统抽象类(策略核心接口,支持参数优化与收益生成)
class TradingSystem {
 public:
  virtual ~TradingSystem() = default;

  /**
   * @brief 样本内参数优化(输入样本内对数价格数据)
   * @param in_sample_data 样本内市场数据(按时间戳排序)
   */
  virtual void optimizeParams(
      const std::vector<MarketData>& in_sample_data) = 0;

  /**
   * @brief 生成交易收益(输出每根K线的对数收益,无持仓时收益为0)
   * @param data 输入市场数据(按时间戳排序)
   * @return std::vector<double> 单根K线对数收益序列
   */
  virtual std::vector<double> generateReturns(
      const std::vector<MarketData>& data) const = 0;

  /**
   * @brief 获取最优参数(返回优化后的策略参数,用于后续分析)
   * @return std::vector<double> 最优参数序列(如均线周期、阈值等)
   */
  virtual std::vector<double> getOptimalParams() const = 0;
};

class UnbiasedSimulation {
 public:
  /**
   * @brief 1. 样本内/外数据划分(按时间戳排序,避免未来泄露)
   * @param full_data 完整市场数据
   * @param in_sample_ratio 样本内数据占比(0 < ratio < 1)
   * @param out_in_sample 输出:样本内数据(按时间戳排序)
   * @param out_out_sample 输出:样本外数据(按时间戳排序)
   * @return SimErrorCode 错误码
   */
  static SimErrorCode splitInOutSample(
      const std::vector<MarketData>& full_data, double in_sample_ratio,
      std::vector<MarketData>& out_in_sample,
      std::vector<MarketData>& out_out_sample) {
    // 校验样本内占比(必须在(0,1)范围内,确保样本内/外均有数据)
    if (in_sample_ratio <= 0.0 || in_sample_ratio >= 1.0) {
      return SimErrorCode::INVALID_RATIO;
    }

    // 按时间戳排序(避免数据乱序导致的未来泄露)
    std::vector<MarketData> sorted_data = full_data;
    std::sort(sorted_data.begin(), sorted_data.end(),
              [](const MarketData& a, const MarketData& b) {
                return a.timestamp < b.timestamp;
              });

    // 校验时间戳递增(二次检查,确保排序后无乱序)
    for (size_t i = 1; i < sorted_data.size(); ++i) {
      if (sorted_data[i].timestamp <= sorted_data[i - 1].timestamp) {
        return SimErrorCode::TIMESTAMP_ORDER_ERR;
      }
    }

    const size_t total_size = sorted_data.size();
    // 校验数据量(至少2根K线,确保样本内/外各有1根)
    if (total_size < 2) {
      return SimErrorCode::INSUFFICIENT_DATA;
    }

    // 计算划分边界(避免浮点误差,确保样本内至少1根、样本外至少1根)
    size_t split_idx =
        static_cast<size_t>(std::round(total_size * in_sample_ratio));
    split_idx = std::max<size_t>(split_idx, 1);       // 样本内至少1根
    split_idx = std::min(split_idx, total_size - 1);  // 样本外至少1根

    // 分割数据并赋值输出
    out_in_sample.assign(sorted_data.begin(), sorted_data.begin() + split_idx);
    out_out_sample.assign(sorted_data.begin() + split_idx, sorted_data.end());
    return SimErrorCode::SUCCESS;
  }

  /**
   * @brief 2. 前向检验(滑动窗口模拟真实交易,生成无偏样本外收益)
   * @param full_data 完整市场数据
   * @param train_window_size 训练窗口大小(样本内数据量)
   * @param test_window_size 测试窗口大小(样本外数据量)
   * @param system 交易系统(需实现TradingSystem接口)
   * @param out_oos_returns 输出:样本外收益序列(对数收益)
   * @return SimErrorCode 错误码
   */
  static SimErrorCode walkForwardAnalysis(
      const std::vector<MarketData>& full_data, size_t train_window_size,
      size_t test_window_size, TradingSystem& system,
      std::vector<double>& out_oos_returns) {
    // 按时间戳排序(文件基础要求,确保无未来泄露)
    std::vector<MarketData> sorted_data = full_data;
    std::sort(sorted_data.begin(), sorted_data.end(),
              [](const MarketData& a, const MarketData& b) {
                return a.timestamp < b.timestamp;
              });

    // 校验时间戳递增
    for (size_t i = 1; i < sorted_data.size(); ++i) {
      if (sorted_data[i].timestamp <= sorted_data[i - 1].timestamp) {
        return SimErrorCode::TIMESTAMP_ORDER_ERR;
      }
    }

    const size_t total_size = sorted_data.size();
    // 校验窗口大小(训练+测试窗口不超过总数据量,确保每次迭代有完整窗口)
    if (train_window_size == 0 || test_window_size == 0 ||
        train_window_size + test_window_size > total_size) {
      return SimErrorCode::INSUFFICIENT_DATA;
    }

    out_oos_returns.clear();
    size_t current_start = 0;

    // 滑动窗口迭代(批量处理逻辑,无冗余数据拷贝)
    while (current_start + train_window_size + test_window_size <= total_size) {
      // 步骤1:提取当前训练窗口(样本内数据)
      std::vector<MarketData> train_data(
          sorted_data.begin() + current_start,
          sorted_data.begin() + current_start + train_window_size);
      // 样本内参数优化(更新系统的最优参数)
      system.optimizeParams(train_data);

      // 步骤2:提取当前测试窗口(样本外数据)
      std::vector<MarketData> test_data(
          sorted_data.begin() + current_start + train_window_size,
          sorted_data.begin() + current_start + train_window_size +
              test_window_size);
      // 生成样本外收益并追加到结果
      std::vector<double> oos_returns = system.generateReturns(test_data);
      out_oos_returns.insert(out_oos_returns.end(), oos_returns.begin(),
                             oos_returns.end());

      // 步骤3:滑动窗口(前进测试窗口大小,避免数据重叠导致的偏差)
      current_start += test_window_size;
    }

    return SimErrorCode::SUCCESS;
  }

  /**
   * @brief 3. 交叉验证(高效利用数据,处理时间序列IS/OOS重叠)
   * @param full_data 完整市场数据
   * @param n_folds 折数(≥2,确保训练/测试集划分有效)
   * @param lookback 指标回溯期(如均线周期,用于计算守卫缓冲区)
   * @param lookahead 目标前瞻期(如持仓周期,用于计算守卫缓冲区)
   * @param system 交易系统(需实现TradingSystem接口)
   * @param out_oos_returns 输出:样本外收益序列(对数收益)
   * @return SimErrorCode 错误码
   */
  static SimErrorCode crossValidation(const std::vector<MarketData>& full_data,
                                      size_t n_folds, size_t lookback,
                                      size_t lookahead, TradingSystem& system,
                                      std::vector<double>& out_oos_returns) {
    // 按时间戳排序(避免未来泄露)
    std::vector<MarketData> sorted_data = full_data;
    std::sort(sorted_data.begin(), sorted_data.end(),
              [](const MarketData& a, const MarketData& b) {
                return a.timestamp < b.timestamp;
              });

    // 校验时间戳递增
    for (size_t i = 1; i < sorted_data.size(); ++i) {
      if (sorted_data[i].timestamp <= sorted_data[i - 1].timestamp) {
        return SimErrorCode::TIMESTAMP_ORDER_ERR;
      }
    }

    const size_t total_size = sorted_data.size();
    // 校验折数和数据量(折数≥2,数据量≥折数,确保每折有足够数据)
    if (n_folds < 2 || total_size < n_folds) {
      return SimErrorCode::INSUFFICIENT_DATA;
    }

    // 计算守卫缓冲区(核心逻辑:避免IS/OOS重叠,取min(lookback, lookahead)-1)
    const size_t guard_size = (std::min(lookback, lookahead) > 0)
                                  ? (std::min(lookback, lookahead) - 1)
                                  : 0;
    const size_t fold_size = total_size / n_folds;  // 每折的基础大小
    out_oos_returns.clear();

    // 迭代每个折作为测试集
    for (size_t fold_idx = 0; fold_idx < n_folds; ++fold_idx) {
      // 计算当前折的测试集范围
      const size_t test_start = fold_idx * fold_size;
      const size_t test_end =
          (fold_idx == n_folds - 1) ? total_size : (fold_idx + 1) * fold_size;

      // 构建训练集(排除测试集及守卫缓冲区,避免IS/OOS重叠)
      std::vector<MarketData> train_data;
      // 测试集左侧的训练数据(排除左侧守卫缓冲区)
      if (test_start > guard_size) {
        train_data.insert(train_data.end(), sorted_data.begin(),
                          sorted_data.begin() + test_start - guard_size);
      }
      // 测试集右侧的训练数据(排除右侧守卫缓冲区)
      if (test_end + guard_size < total_size) {
        train_data.insert(train_data.end(),
                          sorted_data.begin() + test_end + guard_size,
                          sorted_data.end());
      }

      // 校验训练集非空(无训练数据无法优化参数)
      if (train_data.empty()) {
        return SimErrorCode::EMPTY_TRAIN_DATA;
      }

      // 步骤1:样本内参数优化
      system.optimizeParams(train_data);
      // 步骤2:提取当前折的测试集
      std::vector<MarketData> test_data(sorted_data.begin() + test_start,
                                        sorted_data.begin() + test_end);
      // 步骤3:生成样本外收益并追加
      std::vector<double> oos_returns = system.generateReturns(test_data);
      out_oos_returns.insert(out_oos_returns.end(), oos_returns.begin(),
                             oos_returns.end());
    }

    return SimErrorCode::SUCCESS;
  }

  /**
   * @brief 4. 嵌套前向检验(处理两层优化:系统选择+参数优化)
   * @param full_data 完整市场数据
   * @param inner_train_window 内层训练窗口大小(系统参数优化用)
   * @param inner_test_window 内层测试窗口大小(系统性能评估用)
   * @param outer_train_window 外层训练窗口大小(系统选择用)
   * @param systems 竞争系统列表(需实现TradingSystem接口)
   * @param out_final_returns 输出:最终样本外收益序列(对数收益)
   * @return SimErrorCode 错误码
   */
  static SimErrorCode nestedWalkForward(
      const std::vector<MarketData>& full_data, size_t inner_train_window,
      size_t inner_test_window, size_t outer_train_window,
      const std::vector<TradingSystem*>& systems,
      std::vector<double>& out_final_returns) {
    // 校验竞争系统列表非空(无系统无法进行选择)
    if (systems.empty()) {
      return SimErrorCode::EMPTY_SYSTEM_LIST;
    }

    // 按时间戳排序(文件要求,避免未来泄露)
    std::vector<MarketData> sorted_data = full_data;
    std::sort(sorted_data.begin(), sorted_data.end(),
              [](const MarketData& a, const MarketData& b) {
                return a.timestamp < b.timestamp;
              });

    // 校验时间戳递增
    for (size_t i = 1; i < sorted_data.size(); ++i) {
      if (sorted_data[i].timestamp <= sorted_data[i - 1].timestamp) {
        return SimErrorCode::TIMESTAMP_ORDER_ERR;
      }
    }

    const size_t total_size = sorted_data.size();
    const size_t inner_cycle =
        inner_train_window + inner_test_window;  // 内层前向检验周期
    // 校验窗口大小(外层训练窗口≤总数据量,内层周期≤外层训练窗口)
    if (outer_train_window == 0 || inner_train_window == 0 ||
        inner_test_window == 0 || outer_train_window > total_size ||
        inner_cycle > outer_train_window) {
      return SimErrorCode::INVALID_WINDOW_SIZE;
    }

    out_final_returns.clear();
    size_t outer_current_start = 0;

    // 外层前向检验(迭代选择最优系统)
    while (outer_current_start + outer_train_window + inner_test_window <=
           total_size) {
      // 步骤1:提取外层训练数据(用于系统选择)
      std::vector<MarketData> outer_train_data(
          sorted_data.begin() + outer_current_start,
          sorted_data.begin() + outer_current_start + outer_train_window);

      // 步骤2:内层前向检验:评估每个系统的性能得分
      std::vector<double> system_scores(systems.size(),
                                        -std::numeric_limits<double>::max());
      for (size_t sys_idx = 0; sys_idx < systems.size(); ++sys_idx) {
        std::vector<double> inner_returns;
        // 调用内层前向检验,生成系统的样本外收益
        SimErrorCode err = walkForwardAnalysis(
            outer_train_data, inner_train_window, inner_test_window,
            *systems[sys_idx], inner_returns);
        if (err != SimErrorCode::SUCCESS) {
          continue;  // 跳过评估失败的系统
        }
        // 计算系统得分(总对数收益,常用的评估指标)
        system_scores[sys_idx] =
            std::accumulate(inner_returns.begin(), inner_returns.end(), 0.0);
      }

      // 步骤3:选择最优系统(得分最高的系统)
      auto best_sys_iter =
          std::max_element(system_scores.begin(), system_scores.end());
      if (*best_sys_iter == -std::numeric_limits<double>::max()) {
        return SimErrorCode::EMPTY_SYSTEM_LIST;  // 所有系统评估失败
      }
      size_t best_sys_idx = best_sys_iter - system_scores.begin();
      TradingSystem* best_sys = systems[best_sys_idx];

      // 步骤4:外层测试:生成最终样本外收益
      std::vector<MarketData> outer_test_data(
          sorted_data.begin() + outer_current_start + outer_train_window,
          sorted_data.begin() + outer_current_start + outer_train_window +
              inner_test_window);
      std::vector<double> oos_returns =
          best_sys->generateReturns(outer_test_data);
      out_final_returns.insert(out_final_returns.end(), oos_returns.begin(),
                               oos_returns.end());

      // 步骤5:滑动外层窗口(前进内层测试窗口大小)
      outer_current_start += inner_test_window;
    }

    return SimErrorCode::SUCCESS;
  }
};
#include "UnbiasedSimulation.h"

// 移动平均交叉策略(示例策略,经典均线交叉逻辑)
class MACrossSystem : public TradingSystem {
 public:
  /**
   * @brief 构造函数:初始化短/长期均线最大周期
   * @param max_short_lookback 短均线最大周期(如1~20)
   * @param max_long_lookback 长均线最大周期(如2~40)
   */
  MACrossSystem(size_t max_short_lookback, size_t max_long_lookback)
      : max_short_lookback_(max_short_lookback),
        max_long_lookback_(max_long_lookback),
        opt_short_lookback_(1),
        opt_long_lookback_(2) {}

  /**
   * @brief 校验均线参数合法性(短均线最大周期必须<长均线最大周期)
   * @return SimErrorCode 错误码
   */
  SimErrorCode validateParams() const {
    if (max_short_lookback_ >= max_long_lookback_) {
      return SimErrorCode::INVALID_MA_PARAM;
    }
    return SimErrorCode::SUCCESS;
  }

  /**
   * @brief 样本内参数优化:遍历均线组合,选择总对数收益最大的组合
   * @param in_sample_data 样本内市场数据(按时间戳排序)
   */
  void optimizeParams(const std::vector<MarketData>& in_sample_data) override {
    const size_t data_size = in_sample_data.size();
    // 数据量不足时不优化(需至少长均线最大周期的K线)
    if (data_size < max_long_lookback_) {
      return;
    }

    double best_total_ret = -std::numeric_limits<double>::max();
    // 遍历所有合法均线组合(短均线周期 < 长均线周期)
    for (size_t short_lb = 1; short_lb <= max_short_lookback_; ++short_lb) {
      for (size_t long_lb = short_lb + 1; long_lb <= max_long_lookback_;
           ++long_lb) {
        // 计算当前均线组合的总对数收益
        double current_ret =
            calculateTotalReturn(in_sample_data, short_lb, long_lb);
        // 更新最优参数(总收益更大时)
        if (current_ret > best_total_ret) {
          best_total_ret = current_ret;
          opt_short_lookback_ = short_lb;
          opt_long_lookback_ = long_lb;
        }
      }
    }
  }

  /**
   * @brief 生成收益:基于最优均线周期计算单根K线对数收益
   * @param data 输入市场数据(按时间戳排序)
   * @return std::vector<double> 单根K线对数收益序列
   */
  std::vector<double> generateReturns(
      const std::vector<MarketData>& data) const override {
    return calculateReturnDetail(data, opt_short_lookback_, opt_long_lookback_);
  }

  /**
   * @brief 获取最优参数:短/长期均线周期
   * @return std::vector<double> 最优参数(短均线周期、长均线周期)
   */
  std::vector<double> getOptimalParams() const override {
    return {static_cast<double>(opt_short_lookback_),
            static_cast<double>(opt_long_lookback_)};
  }

 private:
  size_t max_short_lookback_;  // 短均线最大周期
  size_t max_long_lookback_;   // 长均线最大周期
  size_t opt_short_lookback_;  // 优化后的短均线周期
  size_t opt_long_lookback_;   // 优化后的长均线周期

  /**
   * @brief 计算总对数收益(用于参数优化)
   * @param data 输入市场数据(按时间戳排序)
   * @param short_lb 短均线周期
   * @param long_lb 长均线周期
   * @return double 总对数收益
   */
  double calculateTotalReturn(const std::vector<MarketData>& data,
                              size_t short_lb, size_t long_lb) const {
    double total_ret = 0.0;
    const size_t data_size = data.size();
    // 从长均线周期后开始计算(确保能完整计算两条均线)
    for (size_t i = long_lb; i < data_size - 1; ++i) {
      // 计算短/长期均线(对数价格均值,避免价格尺度偏差)
      double short_ma = 0.0, long_ma = 0.0;
      for (size_t j = 0; j < short_lb; ++j) {
        short_ma += data[i - j].log_price;
      }
      for (size_t j = 0; j < long_lb; ++j) {
        long_ma += data[i - j].log_price;
      }
      short_ma /= short_lb;
      long_ma /= long_lb;

      // 生成交易信号与收益(短均线上穿做多,下穿做空,无交叉无持仓)
      double ret = 0.0;
      if (short_ma > long_ma) {
        // 多头收益:下一根K线对数价格 - 当前K线对数价格
        ret = data[i + 1].log_price - data[i].log_price;
      } else if (short_ma < long_ma) {
        // 空头收益:当前K线对数价格 - 下一根K线对数价格
        ret = data[i].log_price - data[i + 1].log_price;
      }
      total_ret += ret;
    }
    return total_ret;
  }

  /**
   * @brief 计算详细收益(用于样本外测试,返回单根K线收益)
   * @param data 输入市场数据(按时间戳排序)
   * @param short_lb 短均线周期
   * @param long_lb 长均线周期
   * @return std::vector<double> 单根K线对数收益序列
   */
  std::vector<double> calculateReturnDetail(const std::vector<MarketData>& data,
                                            size_t short_lb,
                                            size_t long_lb) const {
    std::vector<double> returns;
    const size_t data_size = data.size();
    // 预分配内存,提升性能(收益数量 = 数据量 - 长均线周期)
    returns.reserve(data_size - long_lb);

    for (size_t i = long_lb; i < data_size - 1; ++i) {
      // 计算短/长期均线
      double short_ma = 0.0, long_ma = 0.0;
      for (size_t j = 0; j < short_lb; ++j) {
        short_ma += data[i - j].log_price;
      }
      for (size_t j = 0; j < long_lb; ++j) {
        long_ma += data[i - j].log_price;
      }
      short_ma /= short_lb;
      long_ma /= long_lb;

      // 计算单根K线收益
      double ret = 0.0;
      if (short_ma > long_ma) {
        ret = data[i + 1].log_price - data[i].log_price;
      } else if (short_ma < long_ma) {
        ret = data[i].log_price - data[i + 1].log_price;
      }
      returns.push_back(ret);
    }
    return returns;
  }
};

// 测试代码实现
void testSplitInOutSample() {
  std::cout << "Testing splitInOutSample..." << std::endl;

  // 测试正常情况
  std::vector<MarketData> data = {
      {1.0, 1}, {2.0, 2}, {3.0, 3}, {4.0, 4}, {5.0, 5}};

  std::vector<MarketData> in_sample, out_sample;
  auto result =
      UnbiasedSimulation::splitInOutSample(data, 0.6, in_sample, out_sample);

  assert(result == SimErrorCode::SUCCESS);
  assert(in_sample.size() == 3);
  assert(out_sample.size() == 2);
  assert(in_sample[0].timestamp < out_sample[0].timestamp);

  // 测试无效比例
  result =
      UnbiasedSimulation::splitInOutSample(data, 1.5, in_sample, out_sample);
  assert(result == SimErrorCode::INVALID_RATIO);

  result =
      UnbiasedSimulation::splitInOutSample(data, -0.1, in_sample, out_sample);
  assert(result == SimErrorCode::INVALID_RATIO);

  // 测试数据量不足
  std::vector<MarketData> small_data = {{1.0, 1}};
  result = UnbiasedSimulation::splitInOutSample(small_data, 0.5, in_sample,
                                                out_sample);
  assert(result == SimErrorCode::INSUFFICIENT_DATA);

  // 测试时间戳非递增
  std::vector<MarketData> unordered_data = {{1.0, 2}, {2.0, 1}};
  result = UnbiasedSimulation::splitInOutSample(unordered_data, 0.5, in_sample,
                                                out_sample);
  assert(result == SimErrorCode::TIMESTAMP_ORDER_ERR);

  std::cout << "splitInOutSample tests passed." << std::endl;
}

void testWalkForwardAnalysis() {
  std::cout << "Testing walkForwardAnalysis..." << std::endl;

  // 创建测试数据
  std::vector<MarketData> data;
  for (int i = 1; i <= 20; ++i) {
    data.push_back({static_cast<double>(i), i});
  }

  // 创建交易系统
  MACrossSystem system(3, 5);

  std::vector<double> oos_returns;
  auto result =
      UnbiasedSimulation::walkForwardAnalysis(data, 10, 5, system, oos_returns);

  assert(result == SimErrorCode::SUCCESS);

  // 测试窗口大小非法
  result =
      UnbiasedSimulation::walkForwardAnalysis(data, 0, 5, system, oos_returns);
  assert(result == SimErrorCode::INSUFFICIENT_DATA);

  result = UnbiasedSimulation::walkForwardAnalysis(data, 15, 10, system,
                                                   oos_returns);
  assert(result == SimErrorCode::INSUFFICIENT_DATA);

  // 测试时间戳非递增
  std::vector<MarketData> unordered_data = {{1.0, 2}, {2.0, 1}};
  result = UnbiasedSimulation::walkForwardAnalysis(unordered_data, 1, 1, system,
                                                   oos_returns);
  assert(result == SimErrorCode::TIMESTAMP_ORDER_ERR);

  std::cout << "walkForwardAnalysis tests passed." << std::endl;
}

void testCrossValidation() {
  std::cout << "Testing crossValidation..." << std::endl;

  // 创建测试数据
  std::vector<MarketData> data;
  for (int i = 1; i <= 20; ++i) {
    data.push_back({static_cast<double>(i), i});
  }

  MACrossSystem system(2, 4);

  std::vector<double> oos_returns;
  auto result =
      UnbiasedSimulation::crossValidation(data, 4, 2, 1, system, oos_returns);

  assert(result == SimErrorCode::SUCCESS);

  // 测试折数非法
  result =
      UnbiasedSimulation::crossValidation(data, 1, 2, 1, system, oos_returns);
  assert(result == SimErrorCode::INSUFFICIENT_DATA);

  // 测试数据量不足
  std::vector<MarketData> small_data = {{1.0, 1}, {2.0, 2}};
  result = UnbiasedSimulation::crossValidation(small_data, 3, 1, 1, system,
                                               oos_returns);
  assert(result == SimErrorCode::INSUFFICIENT_DATA);

  // 测试时间戳非递增
  std::vector<MarketData> unordered_data = {{1.0, 2}, {2.0, 1}};
  result = UnbiasedSimulation::crossValidation(unordered_data, 2, 1, 1, system,
                                               oos_returns);
  assert(result == SimErrorCode::TIMESTAMP_ORDER_ERR);

  std::cout << "crossValidation tests passed." << std::endl;
}

void testNestedWalkForward() {
  std::cout << "Testing nestedWalkForward..." << std::endl;

  // 创建测试数据
  std::vector<MarketData> data;
  for (int i = 1; i <= 30; ++i) {
    data.push_back({static_cast<double>(i), i});
  }

  // 创建多个交易系统
  MACrossSystem system1(2, 4);
  MACrossSystem system2(3, 6);
  std::vector<TradingSystem*> systems = {&system1, &system2};

  std::vector<double> final_returns;
  auto result = UnbiasedSimulation::nestedWalkForward(data, 8, 4, 12, systems,
                                                      final_returns);

  assert(result == SimErrorCode::SUCCESS);

  // 测试系统列表为空
  std::vector<TradingSystem*> empty_systems;
  result = UnbiasedSimulation::nestedWalkForward(data, 5, 3, 10, empty_systems,
                                                 final_returns);
  assert(result == SimErrorCode::EMPTY_SYSTEM_LIST);

  // 测试窗口大小非法
  result = UnbiasedSimulation::nestedWalkForward(data, 0, 3, 10, systems,
                                                 final_returns);
  assert(result == SimErrorCode::INVALID_WINDOW_SIZE);

  result = UnbiasedSimulation::nestedWalkForward(data, 5, 3, 0, systems,
                                                 final_returns);
  assert(result == SimErrorCode::INVALID_WINDOW_SIZE);

  // 测试时间戳非递增
  std::vector<MarketData> unordered_data = {{1.0, 2}, {2.0, 1}};
  result = UnbiasedSimulation::nestedWalkForward(unordered_data, 1, 1, 1,
                                                 systems, final_returns);
  assert(result == SimErrorCode::TIMESTAMP_ORDER_ERR);

  std::cout << "nestedWalkForward tests passed." << std::endl;
}

void testMACrossSystem() {
  std::cout << "Testing MACrossSystem..." << std::endl;

  // 测试参数合法性
  MACrossSystem valid_system(2, 5);
  assert(valid_system.validateParams() == SimErrorCode::SUCCESS);

  MACrossSystem invalid_system(5, 3);
  assert(invalid_system.validateParams() == SimErrorCode::INVALID_MA_PARAM);

  // 测试优化参数
  std::vector<MarketData> data;
  for (int i = 1; i <= 10; ++i) {
    data.push_back({static_cast<double>(i), i});
  }

  valid_system.optimizeParams(data);
  auto params = valid_system.getOptimalParams();
  assert(params.size() == 2);

  // 测试生成收益
  auto returns = valid_system.generateReturns(data);
  assert(returns.size() == data.size() - 5);  // 长周期为5

  std::cout << "MACrossSystem tests passed." << std::endl;
}

int main() {
  std::cout << "Starting tests..." << std::endl;

  testSplitInOutSample();
  testWalkForwardAnalysis();
  testCrossValidation();
  testNestedWalkForward();
  testMACrossSystem();

  std::cout << "All tests passed!" << std::endl;

  return 0;
}