跳转至

置换检验验证策略收益

置换检验通过随机重排样本标签(如交易收益的正负标记),破坏原数据中的潜在关联,生成 “无关联” 的参考分布,进而判断原数据的统计显著性。在交易系统中,常用于验证 “策略收益是否显著优于随机”:

  1. 计算原始策略的核心统计量(如平均收益、夏普比率)。
  2. 多次重排收益的 “方向标签”(如将 “盈利” 与 “亏损” 随机交换),生成大量 “随机策略” 的统计量。
  3. 统计原始统计量在参考分布中的分位数,判断其是否显著偏离随机水平。
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

// 错误码定义
enum class PermTestErr {
  SUCCESS = 0,
  INVALID_DATA = 1,   // 收益数据为空或长度<2
  INVALID_PERMS = 2,  // 置换次数<100(统计稳定性要求)
  MEMORY_FAILED = 3   // 内存分配失败
};

class PermutationTest {
 public:
  /**
   * @brief 构造函数:初始化置换检验器
   * @param returns 原始收益数据(bar-by-bar或trade-by-trade)
   * @param n_returns 收益数量
   */
  PermutationTest(const double* returns, int n_returns)
      : n_returns_(n_returns),
        raw_returns_(nullptr),
        rng_(std::random_device{}()) {
    if (n_returns_ < 2) {
      error_code_ = PermTestErr::INVALID_DATA;
      return;
    }
    if (!returns) {
      error_code_ = PermTestErr::INVALID_DATA;
      return;
    }

    // 复制原始收益数据
    raw_returns_ = new (std::nothrow) double[n_returns_]{};
    if (!raw_returns_) {
      error_code_ = PermTestErr::MEMORY_FAILED;
      return;
    }
    memcpy(raw_returns_, returns, n_returns_ * sizeof(double));
    error_code_ = PermTestErr::SUCCESS;
  }

  ~PermutationTest() { delete[] raw_returns_; }

  /**
   * @brief 检查构造函数是否成功
   * @return PermTestErr 错误码
   */
  PermTestErr get_error_code() const { return error_code_; }

  /**
   * @brief 执行置换检验
   * @param n_perms 置换次数(建议≥1000)
   * @param stat_type 统计量类型(0=平均收益,1=夏普比率,2=盈利概率)
   * @param raw_stat 输出:原始数据的统计量
   * @param perm_stats 输出:所有置换样本的统计量(长度n_perms)
   * @param p_value 输出:p值(原始统计量优于置换统计量的比例)
   * @return PermTestErr 错误码
   */
  PermTestErr run(int n_perms, int stat_type, double& raw_stat,
                  double* perm_stats, double& p_value) {
    if (error_code_ != PermTestErr::SUCCESS) {
      return error_code_;
    }
    if (n_perms < 100) return PermTestErr::INVALID_PERMS;
    if (!perm_stats) return PermTestErr::INVALID_DATA;

    // 1. 计算原始数据的统计量
    if (!calc_statistic(raw_returns_, n_returns_, stat_type, raw_stat)) {
      return PermTestErr::INVALID_DATA;
    }

    // 2. 生成置换样本并计算统计量
    std::vector<double> perm_returns(n_returns_);
    memcpy(perm_returns.data(), raw_returns_, n_returns_ * sizeof(double));

    int count_better = 0;  // 原始统计量优于置换统计量的次数
    for (int p = 0; p < n_perms; ++p) {
      // 重排收益标签(破坏原始关联)
      std::shuffle(perm_returns.begin(), perm_returns.end(), rng_);

      // 计算置换样本的统计量
      double perm_stat;
      if (!calc_statistic(perm_returns.data(), n_returns_, stat_type,
                          perm_stat)) {
        return PermTestErr::INVALID_DATA;
      }
      perm_stats[p] = perm_stat;

      // 统计原始统计量是否更优(单侧检验)
      if ((stat_type == 0 && raw_stat > perm_stat) ||  // 平均收益:越大越优
          (stat_type == 1 && raw_stat > perm_stat) ||  // 夏普比率:越大越优
          (stat_type == 2 && raw_stat > perm_stat)) {  // 盈利概率:越大越优
        count_better++;
      }
    }

    // 3. 计算p值
    p_value = static_cast<double>(count_better) / n_perms;
    return PermTestErr::SUCCESS;
  }

 private:
  /**
   * @brief 计算统计量
   * @param returns 收益数据
   * @param n 收益数量
   * @param stat_type 统计量类型
   * @param stat 输出:统计量值
   * @return bool 是否计算成功
   */
  bool calc_statistic(const double* returns, int n, int stat_type,
                      double& stat) {
    if (n < 2) return false;

    switch (stat_type) {
      case 0: {  // 平均收益
        double sum = 0.0;
        for (int i = 0; i < n; ++i) sum += returns[i];
        stat = sum / n;
        break;
      }
      case 1: {  // 夏普比率(无风险利率=0)
        double sum = 0.0, sum_sq = 0.0;
        for (int i = 0; i < n; ++i) {
          sum += returns[i];
          sum_sq += returns[i] * returns[i];
        }
        double mean = sum / n;
        double var = (sum_sq / n) - (mean * mean);
        if (var < 1e-60) var = 1e-60;  // 防止除以零
        stat = mean / std::sqrt(var);
        break;
      }
      case 2: {  // 盈利概率(正收益次数占比)
        int win_count = 0;
        for (int i = 0; i < n; ++i) {
          if (returns[i] > 0) win_count++;
        }
        stat = static_cast<double>(win_count) / n;
        break;
      }
      default:
        return false;
    }
    return true;
  }

 private:
  int n_returns_;                                  // 收益数量
  double* raw_returns_;                            // 原始收益数据
  std::mt19937 rng_;                               // 随机数生成器(用于重排)
  PermTestErr error_code_ = PermTestErr::SUCCESS;  // 错误码
};
#include "PermutationTest.h"

// 测试函数
void test_permutation_test() {
  std::cout << "=== Permutation Test Implementation Test ===" << std::endl;

  // 测试数据:模拟一些交易收益
  double test_returns[] = {0.02,  -0.01, 0.03, -0.005, 0.015,
                           -0.02, 0.025, 0.01, -0.01,  0.04};
  int n_returns = sizeof(test_returns) / sizeof(test_returns[0]);

  const int n_perms = 1000;
  double perm_stats[n_perms];
  double raw_stat, p_value;

  // 测试1: 正常情况下的置换检验
  std::cout << "\nTest 1: Normal permutation test" << std::endl;
  PermutationTest pt(test_returns, n_returns);
  if (pt.get_error_code() == PermTestErr::SUCCESS) {
    PermTestErr err = pt.run(n_perms, 0, raw_stat, perm_stats, p_value);
    if (err == PermTestErr::SUCCESS) {
      std::cout << " Mean return: " << std::fixed << std::setprecision(4)
                << raw_stat << std::endl;
      std::cout << " P-value: " << std::fixed << std::setprecision(4) << p_value
                << std::endl;
    } else {
      std::cout << " Error in permutation test: " << static_cast<int>(err)
                << std::endl;
    }
  } else {
    std::cout << " Error creating PermutationTest: "
              << static_cast<int>(pt.get_error_code()) << std::endl;
  }

  // 测试2: 夏普比率统计量
  std::cout << "\nTest 2: Sharpe ratio statistic" << std::endl;
  PermutationTest pt2(test_returns, n_returns);
  if (pt2.get_error_code() == PermTestErr::SUCCESS) {
    PermTestErr err = pt2.run(n_perms, 1, raw_stat, perm_stats, p_value);
    if (err == PermTestErr::SUCCESS) {
      std::cout << " Sharpe ratio: " << std::fixed << std::setprecision(4)
                << raw_stat << std::endl;
      std::cout << " P-value: " << std::fixed << std::setprecision(4) << p_value
                << std::endl;
    } else {
      std::cout << " Error in permutation test: " << static_cast<int>(err)
                << std::endl;
    }
  } else {
    std::cout << " Error creating PermutationTest: "
              << static_cast<int>(pt2.get_error_code()) << std::endl;
  }

  // 测试3: 盈利概率统计量
  std::cout << "\nTest 3: Win probability statistic" << std::endl;
  PermutationTest pt3(test_returns, n_returns);
  if (pt3.get_error_code() == PermTestErr::SUCCESS) {
    PermTestErr err = pt3.run(n_perms, 2, raw_stat, perm_stats, p_value);
    if (err == PermTestErr::SUCCESS) {
      std::cout << " Win probability: " << std::fixed << std::setprecision(4)
                << raw_stat << std::endl;
      std::cout << " P-value: " << std::fixed << std::setprecision(4) << p_value
                << std::endl;
    } else {
      std::cout << " Error in permutation test: " << static_cast<int>(err)
                << std::endl;
    }
  } else {
    std::cout << " Error creating PermutationTest: "
              << static_cast<int>(pt3.get_error_code()) << std::endl;
  }

  // 测试4: 错误情况 - 数据过少
  std::cout << "\nTest 4: Invalid data (too few returns)" << std::endl;
  double small_data[] = {0.01};
  PermutationTest pt4(small_data, 1);
  std::cout << " Error code for insufficient data: "
            << static_cast<int>(pt4.get_error_code()) << std::endl;

  // 测试5: 错误情况 - 空指针
  std::cout << "\nTest 5: Invalid data (null pointer)" << std::endl;
  PermutationTest pt5(nullptr, 10);
  std::cout << " Error code for null data: "
            << static_cast<int>(pt5.get_error_code()) << std::endl;

  // 测试6: 错误情况 - 置换次数不足
  std::cout << "\nTest 6: Insufficient permutations" << std::endl;
  PermutationTest pt6(test_returns, n_returns);
  if (pt6.get_error_code() == PermTestErr::SUCCESS) {
    PermTestErr err =
        pt6.run(50, 0, raw_stat, perm_stats, p_value);  // Only 50 perms < 100
    std::cout << " Error code for insufficient permutations: "
              << static_cast<int>(err) << std::endl;
  }

  std::cout << "\n=== Test Complete ===" << std::endl;
}

int main() {
  test_permutation_test();
  return 0;
}