跳转至

差分进化优化:非线性参数寻优

1. 核心目标

针对交易系统中“规则化策略参数优化”的场景(如移动平均交叉系统的“长期/短期回溯期”“开仓阈值”),通过种群进化+局部爬山的混合算法,高效寻找全局最优参数组合,避免传统“网格搜索”效率低、易陷局部最优的问题。

2. 核心原理

(1)种群初始化:过度初始化提升质量

  • 生成“种群规模+过度初始化数量”的参数组合(如种群100个,过度初始化100个),每个参数组合需满足“交易数≥min_trades”(避免“少数幸运交易获利”的无效参数);
  • 过度初始化逻辑:对超出种群规模的参数组合,仅保留性能优于当前种群中最差个体的组合,提升初始种群整体质量,加速后续进化收敛。

(2)进化迭代:变异+交叉+选择(微分进化核心流程)

  • 父代选择:为每个个体(参数组合)随机选择3个不同父代(parent2、diff1、diff2);
  • 变异操作:生成子代参数:$ child_j = parent2_j + mutate_dev*(diff1_j - diff2_j) $(书中标准变异公式,mutate_dev控制变异幅度),确保参数在合法边界内;
  • 交叉操作:随机选择起始参数,按cross_prob概率用变异参数替换父代参数,保证子代兼具父代与变异特性;
  • 选择操作:若子代性能(由trade_system_criter评估)优于父代,则替换父代,确保种群整体性能迭代提升。

(3)混合优化:局部爬山加速收敛

  • 每代迭代后,按climb_prob概率对个体(优先当前最优个体)执行“单参数爬山优化”:
  • 整数参数(如均线回溯期):逐值搜索上下边界,找到性能最优值;
  • 实数参数(如开仓阈值):用简化Brent算法(三分法)快速定位局部最优;
  • 该步骤结合“全局种群进化”与“局部精细搜索”,既避免陷局部最优,又提升最优参数的精度(“混合优化提升效率”的思想)。

(4)终止条件与结果输出

  • 当“连续max_bad_gen代无性能提升”时停止迭代,输出全局最优参数组合及其性能(由get_best获取),符合“稳定收敛即停止”的优化准则。

(5)性能评估接口: trade_system_criter

  • 输入:待评估的参数组合(如均线回溯期、阈值)+ 最小交易数约束;
  • 内部逻辑:按参数复现交易策略(如均线交叉开仓/平仓),计算回测期间性能(书中支持的总收益、夏普比率、盈利因子等),若交易数不足则返回无效值;
  • 输出:量化性能评分,供优化算法比较不同参数组合的优劣。
#include <cmath>
#include <cstring>
#include <iostream>
#include <random>
#include <vector>

// 错误码定义
enum class DiffEvolutionErr {
  SUCCESS = 0,
  INVALID_PARAM = 1,        // 参数范围非法、种群大小过小等
  INSUFFICIENT_TRADES = 2,  // 交易次数未达最小要求
  MEMORY_FAILED = 3,        // 内存分配失败
  MAX_EVALS_EXCEEDED = 4    // 性能评估次数超出上限
};

// 性能评估函数类型(输入参数集,输出性能值)
typedef double (*PerformanceCriter)(const double* params, int min_trades,
                                    void* user_data);

class DiffEvolution {
 public:
  /**
   * @brief 构造函数:初始化差分进化优化器
   * @param n_params 优化参数数
   * @param n_int_params 前n_int_params个参数为整数(其余为实数)
   * @param pop_size 种群大小(建议≥50)
   * @param over_init 过初始化次数(建议=种群大小)
   * @param min_trades 最小交易次数(低于此的参数集被拒绝)
   * @param max_evals 最大性能评估次数(安全阈值)
   * @param max_bad_gens 最大无改进代数(收敛判断)
   * @param mutate_dev 变异系数(建议0.1~0.5)
   * @param cross_prob 交叉概率(建议0.1~0.5)
   * @param climb_prob 爬山法概率(建议0~0.2)
   * @param low_bounds 参数下界
   * @param high_bounds 参数上界
   * @param criter 性能评估函数
   * @param user_data 性能评估的用户数据(如市场价格)
   * @param err_code 输出错误码指针(可为nullptr)
   */
  DiffEvolution(int n_params, int n_int_params, int pop_size, int over_init,
                int min_trades, int max_evals, int max_bad_gens,
                double mutate_dev, double cross_prob, double climb_prob,
                const double* low_bounds, const double* high_bounds,
                PerformanceCriter criter, void* user_data,
                DiffEvolutionErr* err_code = nullptr)
      : n_params_(n_params),
        n_int_params_(n_int_params),
        pop_size_(pop_size),
        over_init_(over_init),
        min_trades_(min_trades),
        max_evals_(max_evals),
        max_bad_gens_(max_bad_gens),
        mutate_dev_(mutate_dev),
        cross_prob_(cross_prob),
        climb_prob_(climb_prob),
        criter_(criter),
        user_data_(user_data),
        pop1_(nullptr),
        pop2_(nullptr),
        best_params_(nullptr),
        rng_(std::random_device{}()),
        initialized_(false) {
    DiffEvolutionErr err =
        initialize(n_params, n_int_params, pop_size, over_init, min_trades,
                   max_evals, max_bad_gens, mutate_dev, cross_prob, climb_prob,
                   low_bounds, high_bounds);
    if (err_code) *err_code = err;
  }

 private:
  DiffEvolutionErr initialize(int n_params, int n_int_params, int pop_size,
                              int over_init, int min_trades, int max_evals,
                              int max_bad_gens, double mutate_dev,
                              double cross_prob, double climb_prob,
                              const double* low_bounds,
                              const double* high_bounds) {
    // 参数合法性检查
    if (n_params <= 0 || n_int_params < 0 || n_int_params > n_params ||
        pop_size < 10 || over_init < 0 || min_trades < 1 ||
        max_evals < pop_size + over_init || max_bad_gens < 10 ||
        mutate_dev <= 0 || cross_prob < 0 || cross_prob > 1 || climb_prob < 0 ||
        climb_prob > 1 || !low_bounds || !high_bounds) {
      return DiffEvolutionErr::INVALID_PARAM;
    }
    for (int i = 0; i < n_params; ++i) {
      if (low_bounds[i] >= high_bounds[i]) {
        return DiffEvolutionErr::INVALID_PARAM;
      }
    }

    // 内存分配(每个个体:n_params个参数 + 1个性能值)
    int individual_size = n_params + 1;
    pop1_ = new (std::nothrow) double[pop_size * individual_size]{};
    pop2_ = new (std::nothrow) double[pop_size * individual_size]{};
    best_params_ = new (std::nothrow) double[individual_size]{};
    if (!pop1_ || !pop2_ || !best_params_) {
      return DiffEvolutionErr::MEMORY_FAILED;
    }

    // 保存参数
    n_params_ = n_params;
    n_int_params_ = n_int_params;
    pop_size_ = pop_size;
    over_init_ = over_init;
    min_trades_ = min_trades;
    max_evals_ = max_evals;
    max_bad_gens_ = max_bad_gens;
    mutate_dev_ = mutate_dev;
    cross_prob_ = cross_prob;
    climb_prob_ = climb_prob;

    // 保存参数边界
    low_bounds_.resize(n_params);
    high_bounds_.resize(n_params);
    memcpy(low_bounds_.data(), low_bounds, n_params * sizeof(double));
    memcpy(high_bounds_.data(), high_bounds, n_params * sizeof(double));

    initialized_ = true;
    return DiffEvolutionErr::SUCCESS;
  }

 public:
  ~DiffEvolution() {
    delete[] pop1_;
    delete[] pop2_;
    delete[] best_params_;
  }

  /**
   * @brief 执行差分进化优化
   * @param best_params 输出:最优参数集(n_params个参数 + 1个性能值)
   * @return DiffEvolutionErr 错误码
   */
  DiffEvolutionErr optimize(double* best_params) {
    if (!initialized_) return DiffEvolutionErr::INVALID_PARAM;
    if (!best_params) return DiffEvolutionErr::INVALID_PARAM;

    int individual_size = n_params_ + 1;
    int eval_count = 0;  // 性能评估计数器
    int failures = 0;    // 连续失败次数(参数集不满足最小交易次数)

    // 1. 初始化种群
    for (int ind = 0; ind < pop_size_ + over_init_; ++ind) {
      double* curr_ind =
          (ind < pop_size_) ? (pop1_ + ind * individual_size) : pop2_;
      bool valid = false;

      // 生成随机参数集(整数参数四舍五入,实数参数均匀分布)
      while (!valid) {
        // 检查评估次数上限
        if (eval_count >= max_evals_) {
          return DiffEvolutionErr::MAX_EVALS_EXCEEDED;
        }

        // 生成参数
        for (int i = 0; i < n_params_; ++i) {
          std::uniform_real_distribution<double> dist(low_bounds_[i],
                                                      high_bounds_[i]);
          double val = dist(rng_);
          // 整数参数处理
          if (i < n_int_params_) {
            val = std::round(val);
            val = std::max(low_bounds_[i], std::min(high_bounds_[i], val));
          }
          curr_ind[i] = val;
        }

        // 评估性能
        double perf = criter_(curr_ind, min_trades_, user_data_);
        eval_count++;
        curr_ind[n_params_] = perf;

        // 检查有效性(性能>0且交易次数达标)
        if (perf > 0) {
          valid = true;
          failures = 0;
        } else {
          failures++;
          // 连续失败500次,降低最小交易次数
          if (failures >= 500) {
            min_trades_ = std::max(1, min_trades_ * 9 / 10);
            failures = 0;
          }
        }
      }

      // 过初始化:替换种群中最差个体
      if (ind >= pop_size_) {
        // 找到种群中最差个体
        int worst_idx = 0;
        double worst_perf = pop1_[worst_idx * individual_size + n_params_];
        for (int i = 1; i < pop_size_; ++i) {
          double perf = pop1_[i * individual_size + n_params_];
          if (perf < worst_perf) {
            worst_perf = perf;
            worst_idx = i;
          }
        }
        // 替换最差个体
        if (curr_ind[n_params_] > worst_perf) {
          memcpy(pop1_ + worst_idx * individual_size, curr_ind,
                 individual_size * sizeof(double));
        }
      }
    }

    // 2. 初始化最优个体
    memcpy(best_params_, pop1_, individual_size * sizeof(double));
    for (int i = 1; i < pop_size_; ++i) {
      double* ind = pop1_ + i * individual_size;
      if (ind[n_params_] > best_params_[n_params_]) {
        memcpy(best_params_, ind, individual_size * sizeof(double));
      }
    }

    // 3. 进化迭代
    double* old_gen = pop1_;
    double* new_gen = pop2_;
    int bad_gens = 0;   // 无改进代数
    int n_tweaked = 0;  // 爬山法优化的参数计数
    std::uniform_real_distribution<double> unif_dist(0.0, 1.0);

    while (bad_gens < max_bad_gens_) {
      bool improved = false;
      double gen_best_perf = best_params_[n_params_];

      // 遍历种群中每个个体作为父代1
      for (int ind = 0; ind < pop_size_; ++ind) {
        double* parent1 = old_gen + ind * individual_size;
        double* child = new_gen + ind * individual_size;

        // 随机选择父代2、差分1、差分2(互不相同且≠父代1)
        int p2_idx, d1_idx, d2_idx;
        do {
          p2_idx = (int)(unif_dist(rng_) * pop_size_);
        } while (p2_idx == ind);
        do {
          d1_idx = (int)(unif_dist(rng_) * pop_size_);
        } while (d1_idx == ind || d1_idx == p2_idx);
        do {
          d2_idx = (int)(unif_dist(rng_) * pop_size_);
        } while (d2_idx == ind || d2_idx == p2_idx || d2_idx == d1_idx);
        double* parent2 = old_gen + p2_idx * individual_size;
        double* diff1 = old_gen + d1_idx * individual_size;
        double* diff2 = old_gen + d2_idx * individual_size;

        // 变异与交叉
        int start_idx = (int)(unif_dist(rng_) * n_params_);  // 随机起始参数
        bool used_mutate = false;
        for (int i = 0; i < n_params_; ++i) {
          int j = (start_idx + i) % n_params_;
          if ((i == 0 && !used_mutate) || (unif_dist(rng_) < cross_prob_)) {
            // 变异:child[j] = parent2[j] + mutate_dev*(diff1[j] - diff2[j])
            double val = parent2[j] + mutate_dev_ * (diff1[j] - diff2[j]);
            // 边界与类型修正
            if (j < n_int_params_) {
              val = std::round(val);
            }
            val = std::max(low_bounds_[j], std::min(high_bounds_[j], val));
            child[j] = val;
            used_mutate = true;
          } else {
            // 保留父代1参数
            child[j] = parent1[j];
          }
        }

        // 评估子代性能
        if (eval_count >= max_evals_) {
          return DiffEvolutionErr::MAX_EVALS_EXCEEDED;
        }
        double child_perf = criter_(child, min_trades_, user_data_);
        eval_count++;
        child[n_params_] = child_perf;

        // 子代优于父代则替换
        if (child_perf > parent1[n_params_]) {
          // 检查是否更新全局最优
          if (child_perf > best_params_[n_params_]) {
            memcpy(best_params_, child, individual_size * sizeof(double));
            improved = true;
            n_tweaked = 0;  // 重置爬山法计数
          }
        } else {
          // 子代较差,保留父代
          memcpy(child, parent1, individual_size * sizeof(double));
        }

        // 可选爬山法优化
        if (climb_prob_ > 0 &&
            (ind == get_best_idx(old_gen) || unif_dist(rng_) < climb_prob_)) {
          if (ind == get_best_idx(old_gen)) {
            // 对当前最优个体,循环优化每个参数
            int tweak_idx = (n_tweaked++) % n_params_;
            hill_climb(child, tweak_idx);
          } else {
            // 对随机个体,优化随机参数
            int tweak_idx = (int)(unif_dist(rng_) * n_params_);
            hill_climb(child, tweak_idx);
          }
          // 检查是否更新全局最优
          if (child[n_params_] > best_params_[n_params_]) {
            memcpy(best_params_, child, individual_size * sizeof(double));
            improved = true;
            n_tweaked = 0;
          }
        }
      }

      // 收敛判断:无改进则增加bad_gens,否则重置
      if (improved) {
        bad_gens = 0;
      } else {
        bad_gens++;
      }

      // 交换新旧种群
      std::swap(old_gen, new_gen);
    }

    // 输出最优参数
    memcpy(best_params, best_params_, individual_size * sizeof(double));
    return DiffEvolutionErr::SUCCESS;
  }

 private:
  /**
   * @brief 爬山法优化单个参数
   * @param ind 待优化个体
   * @param param_idx 待优化参数索引
   */
  void hill_climb(double* ind, int param_idx) {
    if (param_idx < 0 || param_idx >= n_params_) return;

    double original_val = ind[param_idx];
    double original_perf = ind[n_params_];
    double best_val = original_val;
    double best_perf = original_perf;

    // 整数参数:步长1,搜索上下界
    if (param_idx < n_int_params_) {
      // 向上搜索
      for (double val = original_val + 1; val <= high_bounds_[param_idx];
           val++) {
        ind[param_idx] = val;
        double perf = criter_(ind, min_trades_, user_data_);
        if (perf > best_perf) {
          best_perf = perf;
          best_val = val;
        } else {
          break;  // 性能下降则停止
        }
      }
      // 向下搜索
      for (double val = original_val - 1; val >= low_bounds_[param_idx];
           val--) {
        ind[param_idx] = val;
        double perf = criter_(ind, min_trades_, user_data_);
        if (perf > best_perf) {
          best_perf = perf;
          best_val = val;
        } else {
          break;  // 性能下降则停止
        }
      }
    } else {
      // 实数参数:用Brent算法精细搜索
      double left = low_bounds_[param_idx];
      double right = high_bounds_[param_idx];
      double tol = 1e-6;
      int max_iter = 100;

      // Brent算法核心逻辑(简化版)
      double a = left, b = right;
      double c = b, d = 0, e = 0;
      double fa = eval_perf(ind, param_idx, a);
      double fb = eval_perf(ind, param_idx, b);
      double fc = fb;

      for (int iter = 0; iter < max_iter; ++iter) {
        if (fb > fa && fb > fc) break;  // 当前b已是最优
        double tol1 = tol * std::fabs(b) + 1e-10;
        double xm = 0.5 * (a + c);

        // 黄金分割或抛物线插值
        if (std::fabs(e) > tol1) {
          double r = (b - a) * (fb - fc);
          double q = (b - c) * (fb - fa);
          double p = (b - c) * q - (b - a) * r;
          q = 2 * (q - r);
          if (q > 0) p = -p;
          q = std::fabs(q);
          double etemp = e;
          e = d;
          if (std::fabs(p) < std::fabs(0.5 * q * etemp) && p > q * (a - b) &&
              p < q * (b - c)) {
            d = p / q;  // 抛物线插值
          } else {
            d = (b < xm) ? tol1 : -tol1;  // 黄金分割
          }
        } else {
          d = (b < xm) ? tol1 : -tol1;
        }

        double u = b + d;
        double fu = eval_perf(ind, param_idx, u);
        if (fu > fb) {
          best_perf = fu;
          best_val = u;
          a = b;
          fa = fb;
          b = u;
          fb = fu;
        } else {
          c = b;
          fc = fb;
          b = u;
          fb = fu;
        }
      }
    }

    // 恢复最优值
    ind[param_idx] = best_val;
    ind[n_params_] = best_perf;
  }

  /**
   * @brief 评估参数值对应的性能
   * @param ind 个体
   * @param param_idx 参数索引
   * @param val 参数值
   * @return double 性能值
   */
  double eval_perf(double* ind, int param_idx, double val) {
    double original_val = ind[param_idx];
    ind[param_idx] = val;
    // 修正参数类型与边界
    if (param_idx < n_int_params_) {
      ind[param_idx] = std::round(val);
    }
    ind[param_idx] =
        std::max(low_bounds_[param_idx],
                 std::min(high_bounds_[param_idx], ind[param_idx]));
    // 评估性能
    double perf = criter_(ind, min_trades_, user_data_);
    // 恢复原始值(避免影响其他计算)
    ind[param_idx] = original_val;
    return perf;
  }

  /**
   * @brief 获取种群中最优个体的索引
   * @param gen 种群
   * @return int 最优个体索引
   */
  int get_best_idx(const double* gen) const {
    int best_idx = 0;
    int individual_size = n_params_ + 1;
    double best_perf = gen[best_idx * individual_size + n_params_];
    for (int i = 1; i < pop_size_; ++i) {
      double perf = gen[i * individual_size + n_params_];
      if (perf > best_perf) {
        best_perf = perf;
        best_idx = i;
      }
    }
    return best_idx;
  }

 private:
  int n_params_;              // 优化参数数
  int n_int_params_;          // 整数参数数
  int pop_size_;              // 种群大小
  int over_init_;             // 过初始化次数
  int min_trades_;            // 最小交易次数
  int max_evals_;             // 最大性能评估次数
  int max_bad_gens_;          // 最大无改进代数
  double mutate_dev_;         // 变异系数
  double cross_prob_;         // 交叉概率
  double climb_prob_;         // 爬山法概率
  PerformanceCriter criter_;  // 性能评估函数
  void* user_data_;           // 用户数据

  std::vector<double> low_bounds_;   // 参数下界
  std::vector<double> high_bounds_;  // 参数上界
  double* pop1_;                     // 种群1(父代)
  double* pop2_;                     // 种群2(子代)
  double* best_params_;              // 当前最优个体
  std::mt19937 rng_;                 // 随机数生成器
  bool initialized_;                 // 是否初始化成功
};
#include "DiffEvolution.h"

// 测试用性能评估函数示例:寻找使函数f(x,y) = -(x-3)^2-(y-4)^2最大的点
double testFunction(const double* params, int min_trades, void* user_data) {
  double x = params[0];
  double y = params[1];

  // 模拟交易次数检查
  if (min_trades > 10) return -1;  // 不满足最小交易次数

  // 目标函数:寻找最大值点(3,4)
  double result = -((x - 3) * (x - 3) + (y - 4) * (y - 4));
  return -result;  // 返回负值因为算法寻找最大值
}

// 测试函数
void runTest() {
  std::cout << "开始差分进化算法测试...\n";

  // 定义参数
  const int n_params = 2;
  const int n_int_params = 0;
  const int pop_size = 50;
  const int over_init = 50;
  const int min_trades = 5;
  const int max_evals = 1000;
  const int max_bad_gens = 50;
  const double mutate_dev = 0.5;
  const double cross_prob = 0.3;
  const double climb_prob = 0.1;

  // 参数边界
  double low_bounds[n_params] = {-10.0, -10.0};
  double high_bounds[n_params] = {10.0, 10.0};

  // 创建优化器实例(不使用异常)
  DiffEvolutionErr err_code;
  DiffEvolution optimizer(n_params, n_int_params, pop_size, over_init,
                          min_trades, max_evals, max_bad_gens, mutate_dev,
                          cross_prob, climb_prob, low_bounds, high_bounds,
                          testFunction, nullptr, &err_code);

  if (err_code != DiffEvolutionErr::SUCCESS) {
    std::cout << "优化器初始化失败,错误码: " << static_cast<int>(err_code)
              << "\n";
    return;
  }

  // 执行优化
  double best_params[n_params + 1];
  DiffEvolutionErr result = optimizer.optimize(best_params);

  if (result == DiffEvolutionErr::SUCCESS) {
    std::cout << "优化成功!\n";
    std::cout << "最佳参数: x = " << best_params[0]
              << ", y = " << best_params[1] << "\n";
    std::cout << "最佳函数值: " << -best_params[2] << "\n";  // 转换回原函数值
  } else {
    std::cout << "优化失败,错误码: " << static_cast<int>(result) << "\n";
  }
}

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