神经Boid优化算法2(NOA2):混合集群智能与神经适应的架构基础(基础篇)
🧠

神经Boid优化算法2(NOA2):混合集群智能与神经适应的架构基础(基础篇)

(1/3)·当鸟群学会用神经网络调参,传统优化器的探索开发失衡能否被自愈

进阶 第 1/3 篇

多数交易者把优化器当成黑箱,扔进去参数等结果,却不知道群体智能为何卡在局部极值。把神经网络塞进每个Boid个体,意味着探索策略开始自我进化,而不是靠你手调权重。

「NOA2 在 MT5 里的实现与回测起点」

神经 Boid 优化算法 2(NOA2)是一套面向 MetaTrader 5 的群体智能优化思路,把 Boid 模型里的分离、对齐、聚合三类行为映射到参数空间搜索上,用神经网络做行为权重自适应。 原文作者 Andrey Dik 在 2026 年 3 月 16 日 14:36 发布了该实现,社区页面显示 586 次查看、0 条评论,说明这套东西还停留在少数人看过、没人公开挑刺的阶段。 从结构看,NOA2 分三块:概述描述数学动机,算法实现给具体代码,测试结果晒历史回测。想验证它是否真比网格搜索省算力,直接开 MT5 把算法实现那段拷进 EA 模板跑一次 EURUSD 的 H1 优化,比看概述实在。 外汇与贵金属品种波动受杠杆和流动性影响,任何优化器给出的历史表现都只是概率倾向,实盘前务必用策略测试器做样本外校验。

把鸟群智能塞进神经网络的优化器

做优化算法的人大多卡在一个点上:群体搜索能铺得开空间,但记不住哪片地形有用;神经网络能学模式,却容易在复杂适应度面上迷路。把两者焊在一起,是这几年比较实在的一条路。 具体做法是用经典 Boid 三规则(凝聚力、分离、对齐)让智能体自组织探索,再给每个智能体挂一个多层神经网络。网络从智能体自己的移动经验里学,按所在位置的适应度特征改移动策略——早期偏探索,撞到有前景区域后逐渐转开发。 有意思的是特化是自发出来的:贴着高产区的个体演化出加强局部开发的神经权重,待在稀疏区的个体反而维持探索倾向。没有全局协调,群体自然变成异质行为。外汇与贵金属参数优化属高风险实验,这种异构搜索可能比网格遍历更快收敛,但是否过拟合要看你自己的回测样本。

◍ 神经Boid智能体怎么在MT5里跑起来

神经Boid算法把雷诺兹鸟群三规则(聚合、分离、对齐)和每智能体一个小神经网络焊在一起:网络既调速度,也动态改三条规则的权重。这样每个点在解空间里既随大流又不盲从,探索与开发靠经验自平衡。 代码层先用 S_NeuroBoids_Agent 结构体扛住单智能体:x[] 存坐标、dx[] 存速度、inputs[]/outputs[]/weights[]/biases[] 跑三层网络,memory[] 记历史坐标与适应度。Init 把数组清0或随机小值;UpdateInputs 填坐标、速度、到最优距离、当前适应度;前向用 Tanh/ReLU/Sigmoid 算输出;Learn 仅在当前适应度优于记忆时才回更新权重。 C_AO_NOA2 类管种群。Init 里神经规模 = 坐标数×2 + 2(dist_to_best、current_fitness),distanceMax 按各轴范围差的平方和开方。停滞逻辑在 AdaptiveExploration:fB 与 m_prevBestFitness 差小于 0.000001 算无进展,计数器加;超 20 次把 explorationRate 抬到上限 0.5;每 50 次停滞迭代重启 70% 智能体(rangeMin~rangeMax 随机),留 30% 顶尖不动。 ApplyNeuralControl 是神经影响落点:对每个坐标 c,若 outputs 索引有效,dx[c] += boid.outputs[c] * neuralInfluence;聚合/分离/对齐因子越界则默认 0.5,再乘 base 权重。若随机值低于 explorationRate,按质量无关原则给某坐标加 [-perturbation_size, perturbation_size] 扰动。外汇/贵金属参数优化属高风险,回测过拟合可能放大实盘亏损。

「聚合、分离、对齐:Boid 群体三大行为的内核」

C_AO_NOA2 里的 Cohesion 方法靠两个辅助量驱动:numNeighbors 记邻域智能体数,sumMass 记其质量总和用于质心标准化。第一层循环从 0 到 popSize 累加除自身(索引 pos)外所有个体质量;第二层循环检查距离是否小于 maxDist * cohesionDist,满足则按质量加权累加 centerX 并递增计数器。 邻域存在(numNeighbors>0 且 sumMass>0.0)时,质心坐标除以 sumMass 完成标准化,随后当前智能体速度 dx 按 cohesionWeight 向质心偏移。cohesionDist 管生效半径,cohesionWeight 管质心拉力强度,这是群体聚合的核心机制。 Separation 用 moveX 数组存排斥位移,先全置 0 防随机值累积。遍历种群,Distance 小于 maxDist*separationDist 时,对每个坐标执行 boid坐标减邻体坐标,生成背向向量。遍历完乘 separationWeight 累加到 dx;该值越大避碰越硬,separationDist 控制排斥半径。 Alignment 用 avgDX 存邻域平均速度,跳过自身后距离小于 maxDist*alignmentDist 就累加邻体 dx 并计 numNeighbors。有邻域则 avgDX 除以数量得均值,再按 alignmentWeight 比率把自身速度拉向均值,减少方向突跳。 LimitSpeed 中 speed 为速度向量长:各坐标 dx 平方累加后 MathSqrt。若 speed>1e-10 且低于 speedMax[0]*minSpeed 则标准化后提到最小速;超 speedMax[0]*maxSpeed 则钳到最大速;近 0 时用 u.RNDfromCI(-1.0,1.0)*maxSpeed 填随机微速。minSpeed/maxSpeed 可配。 KeepWithinBounds 按坐标查 boid.x[c] 越 rangeMin[c] 则 dx[c]*=-1.0 并加 (rangeMax[c]-rangeMin[c])*0.001 推回;越 rangeMax[c] 同理减推力。Distance 则循环累加 (boid1.x[c]-boid2.x[c])^2 再开方得欧氏距离,支持任意维。 Revision 遍历种群 a,若 a[i].f>fB 则刷新 fB 与 cB 坐标、停滞计数器归零。进展判定用 MathAbs(fB-m_prevBestFitness)>0.000001,有明显改进才更新 m_prevBestFitness 并下调 explorationRate。下方结构体给出智能体字段基底,开 MT5 把代码贴入可对照上述方法逐字段验证。

MQL5 / C++
class="kw">struct S_NeuroBoids_Agent
{
  class="type">class="kw">double x       [];        class=class="str">"cmt">// current coordinates
  class="type">class="kw">double dx      [];        class=class="str">"cmt">// current speeds
  class="type">class="kw">double inputs  [];        class=class="str">"cmt">// neuron inputs
  class="type">class="kw">double outputs [];        class=class="str">"cmt">// neuron outputs
  class="type">class="kw">double weights [];        class=class="str">"cmt">// neuron weights
  class="type">class="kw">double biases  [];        class=class="str">"cmt">// neuron biases
  class="type">class="kw">double memory  [];        class=class="str">"cmt">// memory of previous positions and their fitnesses
  class="type">int    memory_size;       class=class="str">"cmt">// memory size for accumulating experience
  class="type">int    memory_index;      class=class="str">"cmt">// current index in memory
  class="type">class="kw">double best_local_fitness; class=class="str">"cmt">// best local agent fitness
};

群智能代理的初始化与激活落地

把粒子群里的单个 boid 抽象成可训练代理,第一步是内存布局。结构体里 m 存质量(默认 0.5),cB 数组记历史最优坐标,fB 存适应度初值,统一用 -DBL_MAX 占位,保证任何真实回测结果都能覆盖它。 Init 函数按 coords 维度开 x、dx、cB 三组数组并清零;输入层大小固定为 coords*2+2(坐标、速度、到最优距离、当前适应度),输出层为 coords+3(速度修正加三个自适应因子)。权重矩阵规模为 input_size*output_size,偏置按 output_size 开,全部先置 0 再注入 ±0.01 以内的均匀随机值——MathRand()/32767.0*2.0-1.0 这条把整型随机映射到 [-1,1],乘 0.01 后量级可控,避免初期梯度爆炸。 记忆区 memory_size 设为 10,存最近 10 组「坐标+适应度」用于经验回放,环形索引 memory_index 从 0 起步。这些硬编码数字直接决定 agent 的探索广度,在 MT5 里改 memory_size 或 m 能直观看到种群收敛节奏变化。 激活函数先给 Tanh 实现,直接包 MathTanh;ReLU 仅留注释未写体,意味着你若接分类头需自己补一段 max(input,0) 逻辑。外汇与贵金属波动大,这类自适应代理过拟合风险高,参数改动后务必用历史 tick 验证而非轻信单次结果。

MQL5 / C++
class="type">class="kw">double m;                      class=class="str">"cmt">// boid mass
class="type">class="kw">double cB [];                  class=class="str">"cmt">// best position coordinates
class="type">class="kw">double fB;                     class=class="str">"cmt">// fitness function value
class=class="str">"cmt">// Agent initialization
class="type">void Init(class="type">int coords, class="type">int neuron_size)
{
  ArrayResize(x, coords);
  ArrayResize(dx, coords);
  ArrayInitialize(x, class="num">0.0);
  ArrayInitialize(dx, class="num">0.0);
  class=class="str">"cmt">// Initialize the best position structure
  ArrayResize(cB, coords);
  ArrayInitialize(cB, class="num">0.0);
  fB = -DBL_MAX;
  class=class="str">"cmt">// Inputs: coordinates, speeds, distance to best, etc.
  class="type">int input_size = coords * class="num">2 + class="num">2; class=class="str">"cmt">// x, dx, dist_to_best, current_fitness
  ArrayResize(inputs, input_size);
  ArrayInitialize(inputs, class="num">0.0);
  class=class="str">"cmt">// Outputs: Speed correction and adaptive factors for flock rules
  class="type">int output_size = coords + class="num">3; class=class="str">"cmt">// dx_correction + class="num">3 adaptive parameters
  ArrayResize(outputs, output_size);
  ArrayInitialize(outputs, class="num">0.0);
  class=class="str">"cmt">// Weights and biases for each output
  ArrayResize(weights, input_size * output_size);
  ArrayInitialize(weights, class="num">0.0);
  ArrayResize(biases, output_size);
  ArrayInitialize(biases, class="num">0.0);
  class=class="str">"cmt">// Initialize weights and biases with small random values
  for (class="type">int i = class="num">0; i < ArraySize(weights); i++)
  {
    weights [i] = class="num">0.01 * (MathRand() / class="num">32767.0 * class="num">2.0 - class="num">1.0);
  }
  for (class="type">int i = class="num">0; i < ArraySize(biases); i++)
  {
    biases [i] = class="num">0.01 * (MathRand() / class="num">32767.0 * class="num">2.0 - class="num">1.0);
  }
  class=class="str">"cmt">// Initialize memory for accumulating experience
  memory_size = class="num">10;
  ArrayResize(memory, memory_size * (coords + class="num">1)); class=class="str">"cmt">// coordinates + fitness
  ArrayInitialize(memory, class="num">0.0);
  memory_index = class="num">0;
  best_local_fitness = -DBL_MAX;
  class=class="str">"cmt">// Initialize mass
  m = class="num">0.5;
}
class=class="str">"cmt">// Activation function - hyperbolic tangent
class="type">class="kw">double Tanh(class="type">class="kw">double input_val)
{
  class="kw">return MathTanh(input_val);
}
class=class="str">"cmt">// ReLU activation function

◍ 把粒子状态塞进神经网络的前向通路

在 MT5 里跑粒子群类策略时,网络输入不是拍脑袋拼的。UpdateInputs 把坐标 x[c]、速度 dx[c]、到全局最优的欧氏距离、以及当前适应度 current_fitness 依次写进 inputs 数组,输入维度随 coords_count 线性增长,比如 2 维问题就会先填 2 个坐标 + 2 个速度,再加 1 个距离和 1 个适应度,共 6 个输入。 ReLU 和 Sigmoid 是两套最轻量的激活实现:ReLU 对正数原样输出、负数截断为 0;Sigmoid 用 1/(1+exp(-x)) 把任意实数压到 (0,1)。ForwardPass 里对前 coords_count 个输出走 Tanh 修正速度,其余走 Sigmoid 生成自适应参数,权重寻址用 o*input_size+i 做一维展开。 Learn 函数开头就卡了一道 memory_index < memory_size 的早退:经验样本没攒够就不反向更新,避免用噪声梯度乱改 weights 和 biases。外汇与贵金属市场高杠杆、跳空频繁,这类网络若直接接实盘 tick,过拟合历史片段的概率偏高,建议先在策略测试器用 2020—2023 年 XAUUSD 的 M15 数据做关滑点回测。

MQL5 / C++
class="type">class="kw">double ReLU(class="type">class="kw">double input_val)
{
  class="kw">return (input_val > class="num">0.0) ? input_val : class="num">0.0;
}
class=class="str">"cmt">// Sigmoid activation function
class="type">class="kw">double Sigmoid(class="type">class="kw">double input_val)
{
  class="kw">return class="num">1.0 / (class="num">1.0 + MathExp(-input_val));
}
class=class="str">"cmt">// Updating neural network inputs - Corrected version
class="type">void UpdateInputs(class="type">class="kw">double &global_best [], class="type">class="kw">double current_fitness, class="type">int coords_count)
{
  class="type">int input_index = class="num">0;
  class=class="str">"cmt">// Coordinates
  for (class="type">int c = class="num">0; c < coords_count; c++)
  {
    inputs [input_index++] = x [c];
  }
  class=class="str">"cmt">// Speeds
  for (class="type">int c = class="num">0; c < coords_count; c++)
  {
    inputs [input_index++] = dx [c];
  }
  class=class="str">"cmt">// Distance to the best global solution
  class="type">class="kw">double dist_to_best = class="num">0;
  for (class="type">int c = class="num">0; c < coords_count; c++)
  {
    dist_to_best += MathPow(x [c] - global_best [c], class="num">2);
  }
  dist_to_best = MathSqrt(dist_to_best);
  inputs [input_index++] = dist_to_best;
  class=class="str">"cmt">// Current fitness function
  inputs [input_index++] = current_fitness;
}
class=class="str">"cmt">// Direct distribution over the network
class="type">void ForwardPass(class="type">int coords_count)
{
  class="type">int input_size = ArraySize(inputs);
  class="type">int output_size = ArraySize(outputs);
  class=class="str">"cmt">// For each output, calculate the weighted sum of the inputs + bias
  for (class="type">int o = class="num">0; o < output_size; o++)
  {
    class="type">class="kw">double sum = biases [o];
    for (class="type">int i = class="num">0; i < input_size; i++)
    {
      sum += inputs [i] * weights [o * input_size + i];
    }
    class=class="str">"cmt">// Apply different activation functions depending on the output
    if (o < coords_count) class=class="str">"cmt">// Use the hyperbolic tangent to correct the speed
    {
      outputs [o] = Tanh(sum);
    }
    else class=class="str">"cmt">// Use sigmoid for adaptive parameters
    {
      outputs [o] = Sigmoid(sum);
    }
  }
}
class=class="str">"cmt">// Learning from accumulated experience
class="type">void Learn(class="type">class="kw">double learning_rate, class="type">int coords_count)
{
  if (memory_index < memory_size) class="kw">return; class=class="str">"cmt">// Insufficient experience
  class=class="str">"cmt">// Find the best experience in memory
  class="type">int best_index = class="num">0;
把重复劳动交给小布
这些多智能体搜索的诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到参数空间的实时聚类,你专注决策而不是跑基准。

常见问题

每个 Boid 配备独立神经网络,从自身运动历史与适应度反馈中局部学习,无全局协调,群体特化是涌现结果。
凝聚力、分离、对齐仍保留,但神经网络会按情境动态调整三条规则的影响力,早期偏探索后期偏开发。
算法倾向提升空间探索效率,但外汇贵金属属高风险市场,任何优化结果都只是概率优势,需样本外验证。
目前小布内置的是聚类与空间诊断视图,NOA2 类混合搜索可作为自定义模块接入,具体见品种页的 AIGC 面板说明。
PSO 依赖全局最优牵引,NOA2 靠个体神经适应产生异质行为,稀疏区智能体自发加强探索,密集区转为开发。