神经Boid优化算法2(NOA2)·进阶篇
🧠

神经Boid优化算法2(NOA2)·进阶篇

(2/3)·传统群智能缺记忆、神经网络弱探索,NOA2 用个体神经网络改写 Boid 行为规则

进阶 第 2/3 篇
把群体搜索当成纯随机游走,往往在前几代就耗尽算力却找不到窄峰。给每个智能体装一个会随位置改策略的神经网络,探索与开发的比例就能自己长出来,而不是靠你手调参数。

「从记忆体里挑最优再回灌权重」

这段逻辑干了两件事:先在共享记忆数组里扫一遍,找出历史适应度最高的那条经验,再决定是否拿它去推权重。memory 被压成一维数组,每个个体占 coords_count+1 个槽,末位存适应度,所以取第 i 个个体的适应度要用 i*(coords_count+1)+coords_count 来偏移。 扫描从 i=1 开始,首条 best_fitness 直接取 memory[coords_count],也就是 0 号个体。若 fitness > best_fitness 就刷新 best_index,遍历完 memory_size 条后拿到全局最优记忆位。若 best_fitness <= best_local_fitness 直接 return,说明这次经验没超越本地最优,权重原样不动。 过了门槛才进权重更新:output_size 和 input_size 分别由 ArraySize(outputs) / ArraySize(inputs) 拿到,双层循环按 weight_index = o*input_size + i 写回 weights[weight_index] += learning_rate * outputs[o] * inputs[i],偏置 biases[o] 另加 learning_rate * outputs[o]。这是最朴素的梯度式增量,learning_rate 你调大一点收敛快但外汇贵金属回测里容易过冲,调小则稳但慢。 MemorizeExperience 负责把当前坐标 x[c] 和 fitness 写进环形缓冲 memory_index,写满 memory_size 后取模回卷;UpdateBestPosition 只在 current_fitness > fB 时把 x 整组拷进 cB。开 MT5 把 memory_size 设成 50 跑一遍 EURUSD 的 M15,能直接看到 fB 的爬升节奏。

MQL5 / C++
class="type">class="kw">double best_fitness = memory [coords_count]; class=class="str">"cmt">// The first fitness function in memory
for (class="type">int i = class="num">1; i < memory_size; i++)
{
  class="type">class="kw">double fitness = memory [i * (coords_count + class="num">1) + coords_count];
  if (fitness > best_fitness)
  {
    best_fitness = fitness;
    best_index = i;
  }
}
class=class="str">"cmt">// If the current experience is not better than the previous one, do not update the weights
if (best_fitness <= best_local_fitness) class="kw">return;
best_local_fitness = best_fitness;
class=class="str">"cmt">// Simple method for updating weights
class="type">int input_size = ArraySize(inputs);
class="type">int output_size = ArraySize(outputs);
class=class="str">"cmt">// Simple form of gradient update
for (class="type">int o = class="num">0; o < output_size; o++)
{
  for (class="type">int i = class="num">0; i < input_size; i++)
  {
    class="type">int weight_index = o * input_size + i;
    if (weight_index < ArraySize(weights))
    {
      weights [weight_index] += learning_rate * outputs [o] * inputs [i];
    }
  }
  class=class="str">"cmt">// Update offsets
  biases [o] += learning_rate * outputs [o];
}
}
class=class="str">"cmt">// Save current experience(coordinates and fitness)
class="type">void MemorizeExperience(class="type">class="kw">double fitness, class="type">int coords_count)
{
  class="type">int offset = memory_index * (coords_count + class="num">1);
  class=class="str">"cmt">// Save the coordinates
  for (class="type">int c = class="num">0; c < coords_count; c++)
  {
    memory [offset + c] = x [c];
  }
  class=class="str">"cmt">// Save the fitness function
  memory [offset + coords_count] = fitness;
  class=class="str">"cmt">// Update the memory index
  memory_index = (memory_index + class="num">1) % memory_size;
}
class=class="str">"cmt">// Update the agent&class="macro">#x27;s best position
class="type">void UpdateBestPosition(class="type">class="kw">double current_fitness, class="type">int coords_count)
{
  if (current_fitness > fB)
  {
    fB = current_fitness;
    ArrayCopy(cB, x, class="num">0, class="num">0, WHOLE_ARRAY);
  }
}
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Class of neuron-like optimization algorithm inherited from C_AO
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class C_AO_NOA2 : class="kw">public C_AO
{
  class="kw">public: class=class="str">"cmt">//--------------------------------------------------------------------
  ~C_AO_NOA2() { }
  C_AO_NOA2()
  {
    ao_name = "NOA2";
    ao_desc = "Neuroboids Optimization Algorithm class="num">2 (joo)";
    ao_link = "[MQL5官方文档]

◍ 群体智能算法的初始参数与回灌机制

这套基于群体行为的模型在初始化阶段定义了 12 个核心变量,种群规模 popSize 固定为 50,意味着每代并行演化的个体数量。凝聚权重 cohesionWeight 取 0.6,而分离权重 separationWeight 仅 0.005,说明个体更倾向向群体中心靠拢,而非彼此散开。 速度边界被压得很窄:maxSpeed=0.001、minSpeed=0.0001,配合 alignmentDist=0.1 的对齐感知半径,整体运动偏平滑、低噪声。神经部分 learningRate=0.01、neuralInfluence=0.3,表示网络输出对轨迹的干预占比三成,剩余七成由群体规则驱动。 代码里用 ArrayResize(params,12) 开辟参数槽,再把每个变量名和数值写回结构体,方便后续优化器直接读写。停滞计数器 m_stagnationCounter 归零、m_prevBestFitness 设为 -DBL_MAX,是为新一轮搜索清空历史最优锚点。 SetParams() 做反向操作:把 params[] 里的值灌回各全局变量。你在 MT5 里改完 params 数组再调 SetParams(),不用动算法主体就能换一套行为特征,外汇与贵金属市场波动剧烈,此类参数组合仅代表历史回测倾向,实盘需自担高风险。

MQL5 / C++
  popSize            = class="num">50;     class=class="str">"cmt">// population size
  cohesionWeight   = class="num">0.6;     class=class="str">"cmt">// cohesion weight
  cohesionDist     = class="num">0.001;   class=class="str">"cmt">// cohesion distance
  separationWeight = class="num">0.005;   class=class="str">"cmt">// separation weight
  separationDist   = class="num">0.03;    class=class="str">"cmt">// separation distance
  alignmentWeight  = class="num">0.1;     class=class="str">"cmt">// alignment weight
  alignmentDist    = class="num">0.1;     class=class="str">"cmt">// alignment distance
  maxSpeed         = class="num">0.001;   class=class="str">"cmt">// maximum speed
  minSpeed         = class="num">0.0001;  class=class="str">"cmt">// minimum speed 
  learningRate     = class="num">0.01;    class=class="str">"cmt">// neural network learning speed
  neuralInfluence  = class="num">0.3;     class=class="str">"cmt">// influence of the neural network on movement
  explorationRate  = class="num">0.1;     class=class="str">"cmt">// random exploration probability
  ArrayResize(params, class="num">12);
  params [class="num">0].name  = "popSize";          params [class="num">0].val = popSize;
  params [class="num">1].name  = "cohesionWeight";   params [class="num">1].val  = cohesionWeight;
  params [class="num">2].name  = "cohesionDist";     params [class="num">2].val  = cohesionDist;
  params [class="num">3].name  = "separationWeight"; params [class="num">3].val  = separationWeight;
  params [class="num">4].name  = "separationDist";   params [class="num">4].val  = separationDist;
  params [class="num">5].name  = "alignmentWeight";  params [class="num">5].val  = alignmentWeight;
  params [class="num">6].name  = "alignmentDist";    params [class="num">6].val  = alignmentDist;
  params [class="num">7].name  = "maxSpeed";         params [class="num">7].val  = maxSpeed;
  params [class="num">8].name  = "minSpeed";         params [class="num">8].val  = minSpeed;
  params [class="num">9].name  = "learningRate";     params [class="num">9].val  = learningRate;
  params [class="num">10].name = "neuralInfluence";  params [class="num">10].val = neuralInfluence;
  params [class="num">11].name = "explorationRate";  params [class="num">11].val = explorationRate;
  class=class="str">"cmt">// Initialize the stagnation counter and the previous best fitness value
  m_stagnationCounter = class="num">0;
  m_prevBestFitness   = -DBL_MAX;
}
class="type">void SetParams()
{
  popSize            = (class="type">int)params [class="num">0].val;
  cohesionWeight   = params [class="num">1].val;
  cohesionDist     = params [class="num">2].val;
  separationWeight = params [class="num">3].val;
  separationDist   = params [class="num">4].val;
  alignmentWeight  = params [class="num">5].val;
  alignmentDist    = params [class="num">6].val;
  maxSpeed         = params [class="num">7].val;

NeuroBoids 智能体的参数与私有方法布局

这段结构定义了一个基于群体行为(boids)的代理系统,参数从 params 数组的第 8 到第 11 索引读取:minSpeed 控制最小速度,learningRate 是神经网络学习速率,neuralInfluence 决定网络对运动的干预强度,explorationRate 则是随机探索概率。 公开字段里能看到三类经典群体规则权重与距离:cohesion(聚合)、separation(分离)、alignment(对齐),各自配了 weight 和 dist 双变量;maxSpeed 与 minSpeed 框定个体速率上下限,m_stagnationCounter 和 m_prevBestFitness 用来跟踪优化停滞。 私有段暴露了实际计算链路:CalculateMass 算质量,Cohesion/Separation/Alignment 是三条规则实现,LimitSpeed 与 KeepWithinBounds 做边界约束,Distance 提供两两距离,ApplyNeuralControl 把神经网络输出灌进运动决策。 在 MT5 里若想调敏感性,优先动 params[10](neuralInfluence)和 params[11](explorationRate):前者偏高会让价格模拟更依赖网络推断,后者偏高则增加随机扰动,外汇与贵金属市场波动剧烈,这类参数组合需先在历史数据回测验证稳定性。

MQL5 / C++
minSpeed           = params [class="num">8].val;
learningRate       = params [class="num">9].val;
neuralInfluence    = params [class="num">10].val;
explorationRate    = params [class="num">11].val;
}
class="type">bool Init(const class="type">class="kw">double &rangeMinP [], const class="type">class="kw">double &rangeMaxP [], const class="type">class="kw">double &rangeStepP [], const class="type">int epochsP = class="num">0);
class="type">void Moving();
class="type">void Revision();
class=class="str">"cmt">//----------------------------------------------------------------------------
class="type">class="kw">double cohesionWeight;     class=class="str">"cmt">// cohesion weight
class="type">class="kw">double cohesionDist;       class=class="str">"cmt">// cohesion distance
class="type">class="kw">double separationWeight;   class=class="str">"cmt">// separation weight
class="type">class="kw">double separationDist;     class=class="str">"cmt">// separation distance
class="type">class="kw">double alignmentWeight;    class=class="str">"cmt">// alignment weight
class="type">class="kw">double alignmentDist;      class=class="str">"cmt">// alignment distance
class="type">class="kw">double minSpeed;           class=class="str">"cmt">// minimum speed
class="type">class="kw">double maxSpeed;           class=class="str">"cmt">// maximum speed
class="type">class="kw">double learningRate;       class=class="str">"cmt">// neural network learning speed
class="type">class="kw">double neuralInfluence;    class=class="str">"cmt">// influence of the neural network on movement
class="type">class="kw">double explorationRate;    class=class="str">"cmt">// random exploration probability
class="type">int m_stagnationCounter;   class=class="str">"cmt">// stagnation counter
class="type">class="kw">double m_prevBestFitness;  class=class="str">"cmt">// previous best fitness value
S_NeuroBoids_Agent agent []; class=class="str">"cmt">// agents(boids)
class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
class="type">class="kw">double distanceMax;     class=class="str">"cmt">// maximum distance
class="type">class="kw">double speedMax [];     class=class="str">"cmt">// maximum speeds by measurements
class="type">int neuron_size;        class=class="str">"cmt">// neuron size(number of inputs)
class="type">void   CalculateMass();                                             class=class="str">"cmt">// calculation of agent masses 
class="type">void   Cohesion(S_NeuroBoids_Agent &boid, class="type">int pos); class=class="str">"cmt">// cohesion rule
class="type">void   Separation(S_NeuroBoids_Agent &boid, class="type">int pos); class=class="str">"cmt">// separation rule
class="type">void   Alignment(S_NeuroBoids_Agent &boid, class="type">int pos); class=class="str">"cmt">// alignment rule
class="type">void   LimitSpeed(S_NeuroBoids_Agent &boid);                            class=class="str">"cmt">// speed limit
class="type">void   KeepWithinBounds(S_NeuroBoids_Agent &boid);                            class=class="str">"cmt">// keep within bounds
class="type">class="kw">double Distance(S_NeuroBoids_Agent &boid1, S_NeuroBoids_Agent &boid2); class=class="str">"cmt">// calculate distance
class="type">void   ApplyNeuralControl(S_NeuroBoids_Agent &boid, class="type">int pos); class=class="str">"cmt">// apply neural control

「初始化里的群体参数落地」

C_AO_NOA2::Init 负责把外部传入的搜索区间和步长接住,先调 StandardInit 做基础校验,失败直接返 false。随后按 coords*2+2 算神经元尺寸——多出的两项留给到最优点的距离与当前适应度,这是后面个体移动时的关键状态位。 种群代理用 ArrayResize(agent, popSize) 开好,再逐个 Init(coords, neuron_size)。distanceMax 不是拍脑袋给的,而是遍历各维度 speedMax[c]=rangeMax[c]-rangeMin[c],平方和开根得出,代表解空间对角线长度,后续速度归一化会用到。

  • 个群体行为参数(凝聚、分离、对齐权重与距离,最大最小速度,学习率,神经影响,探索率)全部写进全局变量,键名带数字前缀方便 EA 其他模块读取。m_stagnationCounter 清零、m_prevBestFitness 设 -DBL_MAX,相当于把停滞保护机制重置回冷启动状态。

外汇与贵金属市场波动剧烈、杠杆风险高,这类群体优化器参数若未随品种波动率重标定,搜索可能长期偏离实盘可行域。开 MT5 把 Init 里的 rangeMin/rangeMax 换成 EURUSD 的 ATR(14) 区间跑一次,能直接看出 popSize 与 epochs 对收敛速度的影响。

MQL5 / C++
class="type">bool C_AO_NOA2::Init(const class="type">class="kw">double &rangeMinP  [], class=class="str">"cmt">// minimum search range
                      const class="type">class="kw">double &rangeMaxP  [], class=class="str">"cmt">// maximum search range
                      const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">// search step
                      const class="type">int     epochsP)       class=class="str">"cmt">// number of epochs
{
  if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return false;
  class=class="str">"cmt">// Determine the size of a neuron
  neuron_size = coords * class="num">2 + class="num">2; class=class="str">"cmt">// x, dx, dist_to_best, current_fitness
  class=class="str">"cmt">// Initialize the agents
  ArrayResize(agent, popSize);
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    agent [i].Init(coords, neuron_size);
  }
  distanceMax = class="num">0;
  ArrayResize(speedMax, coords);
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    speedMax [c] = rangeMax [c] - rangeMin [c];
    distanceMax += MathPow(speedMax [c], class="num">2);
  }
  distanceMax = MathSqrt(distanceMax);
  class=class="str">"cmt">// Reset the stagnation counter and the previous best fitness value
  m_stagnationCounter = class="num">0;
  m_prevBestFitness   = -DBL_MAX;
  GlobalVariableSet("class="macro">#reset", class="num">1.0);
  GlobalVariableSet("1cohesionWeight",   params [class="num">1].val);
  GlobalVariableSet("2cohesionDist",     params [class="num">2].val);
  GlobalVariableSet("3separationWeight", params [class="num">3].val);
  GlobalVariableSet("4separationDist",   params [class="num">4].val);
  GlobalVariableSet("5alignmentWeight",  params [class="num">5].val);
  GlobalVariableSet("6alignmentDist",    params [class="num">6].val);
  GlobalVariableSet("7maxSpeed",         params [class="num">7].val);
  GlobalVariableSet("8minSpeed",         params [class="num">8].val);
  GlobalVariableSet("9learningRate",     params [class="num">9].val);
  GlobalVariableSet("10neuralInfluence", params [class="num">10].val);
  GlobalVariableSet("11explorationRate", params [class="num">11].val);
  class="kw">return true;
}

◍ 用全局变量接管群体参数的初始化与步进

这段逻辑把算法的交互配置完全挂到 MT5 全局变量上,外部脚本或面板改一个值,EA 下一 tick 就能读到。注意变量名用数字前缀(如 "1cohesionWeight")只是为了避免命名冲突,GlobalVariableGet 拿到的就是实时权重,不需要重编译。 revision 标志控制是否冷启动。第一次跑的时候 revision=false,代码会给每个 agent 在 rangeMin~rangeMax 区间内撒随机坐标,速度则取区间跨度乘 ±1 的随机值再乘 0.001,也就是初始扰动被压到千分之一量级,避免一上来就乱飞。

MQL5 / C++
revision = false;
GlobalVariableSet("class="macro">#reset", class="num">0.0);
}
class=class="str">"cmt">// Get parameters from global variables for interactive configuration
cohesionWeight   = GlobalVariableGet("1cohesionWeight");
cohesionDist     = GlobalVariableGet("2cohesionDist");
separationWeight = GlobalVariableGet("3separationWeight");
separationDist   = GlobalVariableGet("4separationDist");
alignmentWeight  = GlobalVariableGet("5alignmentWeight");
alignmentDist    = GlobalVariableGet("6alignmentDist");
maxSpeed         = GlobalVariableGet("7maxSpeed");
minSpeed         = GlobalVariableGet("8minSpeed");
learningRate     = GlobalVariableGet("9learningRate");
neuralInfluence  = GlobalVariableGet("10neuralInfluence");
explorationRate  = GlobalVariableGet("11explorationRate");
class=class="str">"cmt">// Initialization of initial positions and speeds
if (!revision)
{
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    for (class="type">int c = class="num">0; c < coords; c++)
    {
      agent [i].x [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);
      agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI(-class="num">1.0, class="num">1.0) * class="num">0.001;
      a [i].c [c] = u.SeInDiSp(agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
  revision = true;
  class="kw">return;
}
class=class="str">"cmt">// Adaptive research depending on stagnation
AdaptiveExploration();
class=class="str">"cmt">//----------------------------------------------------------------------------
class=class="str">"cmt">// Main loop of boid movement
for (class="type">int i = class="num">0; i < popSize; i++)
{
  class=class="str">"cmt">// Save the current experience
  agent [i].MemorizeExperience(a [i].f, coords);
  class=class="str">"cmt">// Update the agent&class="macro">#x27;s best position
  agent [i].UpdateBestPosition(a [i].f, coords);
  class=class="str">"cmt">// Update the neural network inputs
  agent [i].UpdateInputs(cB, a [i].f, coords);
  class=class="str">"cmt">// Forward propagation through the neural network
  agent [i].ForwardPass(coords);
  class=class="str">"cmt">// Learning from accumulated experience
  agent [i].Learn(learningRate, coords);
}
class=class="str">"cmt">// Calculate masses
CalculateMass();
class=class="str">"cmt">// Application of rules and movement
for (class="type">int i = class="num">0; i < popSize; i++)
{
  class=class="str">"cmt">// Standard rules of the boid algorithm
  Cohesion(agent [i], i);
  Separation(agent [i], i);
  Alignment(agent [i], i);
  class=class="str">"cmt">// Apply neural control
  ApplyNeuralControl(agent [i], i);
  class=class="str">"cmt">// Speed limit and keeping within bounds
  LimitSpeed(agent [i]);
  KeepWithinBounds(agent [i]);
  class=class="str">"cmt">// Update positions
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    agent [i].x [c] += agent [i].dx [c];
    a [i].c [c] = u.SeInDiSp(agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
  }
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_NOA2::CalculateMass()
逐行拆解几个关键点:GlobalVariableSet("#reset",0.0) 是把重置开关清掉;u.RNDfromCI 是闭区间均匀随机;u.SeInDiSp 是把连续坐标离散化到网格索引,方便后续查表。主循环里每个 agent 先记经验、更新历史最优、灌神经网络输入、前向传播、再学习,这套顺序不能乱,否则神经权重可能吃到未更新的适应度。 非冷启动分支会先跑 AdaptiveExploration,也就是连续多代没改进时自动加大 explorationRate,外汇与贵金属行情下这种自适应探索可能缓解局部最优,但参数空间若设得太宽,回测过拟合概率会明显上升,属高风险操作。 想验证的话,在 MT5 终端全局变量窗口手动建 "1cohesionWeight" 等 11 个变量,给个 0.5~2.0 之间的数,EA 加载后不用改代码就能看到群体行为变化。

MQL5 / C++
revision = false;
GlobalVariableSet("class="macro">#reset", class="num">0.0);
}
class=class="str">"cmt">// Get parameters from global variables for interactive configuration
cohesionWeight   = GlobalVariableGet("1cohesionWeight");
cohesionDist     = GlobalVariableGet("2cohesionDist");
separationWeight = GlobalVariableGet("3separationWeight");
separationDist   = GlobalVariableGet("4separationDist");
alignmentWeight  = GlobalVariableGet("5alignmentWeight");
alignmentDist    = GlobalVariableGet("6alignmentDist");
maxSpeed         = GlobalVariableGet("7maxSpeed");
minSpeed         = GlobalVariableGet("8minSpeed");
learningRate     = GlobalVariableGet("9learningRate");
neuralInfluence  = GlobalVariableGet("10neuralInfluence");
explorationRate  = GlobalVariableGet("11explorationRate");
class=class="str">"cmt">// Initialization of initial positions and speeds
if (!revision)
{
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    for (class="type">int c = class="num">0; c < coords; c++)
    {
      agent [i].x [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);
      agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI(-class="num">1.0, class="num">1.0) * class="num">0.001;
      a [i].c [c] = u.SeInDiSp(agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
  revision = true;
  class="kw">return;
}
class=class="str">"cmt">// Adaptive research depending on stagnation
AdaptiveExploration();
class=class="str">"cmt">//----------------------------------------------------------------------------
class=class="str">"cmt">// Main loop of boid movement
for (class="type">int i = class="num">0; i < popSize; i++)
{
  class=class="str">"cmt">// Save the current experience
  agent [i].MemorizeExperience(a [i].f, coords);
  class=class="str">"cmt">// Update the agent&class="macro">#x27;s best position
  agent [i].UpdateBestPosition(a [i].f, coords);
  class=class="str">"cmt">// Update the neural network inputs
  agent [i].UpdateInputs(cB, a [i].f, coords);
  class=class="str">"cmt">// Forward propagation through the neural network
  agent [i].ForwardPass(coords);
  class=class="str">"cmt">// Learning from accumulated experience
  agent [i].Learn(learningRate, coords);
}
class=class="str">"cmt">// Calculate masses
CalculateMass();
class=class="str">"cmt">// Application of rules and movement
for (class="type">int i = class="num">0; i < popSize; i++)
{
  class=class="str">"cmt">// Standard rules of the boid algorithm
  Cohesion(agent [i], i);
  Separation(agent [i], i);
  Alignment(agent [i], i);
  class=class="str">"cmt">// Apply neural control
  ApplyNeuralControl(agent [i], i);
  class=class="str">"cmt">// Speed limit and keeping within bounds
  LimitSpeed(agent [i]);
  KeepWithinBounds(agent [i]);
  class=class="str">"cmt">// Update positions
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    agent [i].x [c] += agent [i].dx [c];
    a [i].c [c] = u.SeInDiSp(agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]);
  }
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_NOA2::CalculateMass()

把神经网络输出接进群体搜索的实操段

这段代码把神经网络的输出直接叠加到 boid 的速度分量上,再按输出自适应缩放聚合、分离、对齐三类权重。注意叠加前用 ArraySize 做了边界保护,c 超出 outputs 长度就不改对应 dx,避免 EA 在参数维度不匹配时直接越界崩掉。 局部权重计算用了 0.5 作基线:local_cohesion = cohesionWeight * (0.5 + cohesion_factor),也就是神经输出为 0 时只保留一半基础权重,输出到 0.5 才回到原始权重。这种写法让网络对群体行为的干预是渐进的,不会一上来就颠覆原算法。 停滞检测那段给了明确阈值:当最优适应度变化小于 0.000001 时计数加一,连续超过 20 代才触发探索概率提升。外汇与贵金属市场波动突变频繁,把 20 和 0.000001 直接抄进 MT5 回测,看你的品种在横盘期会不会过早放大随机扰动。 探索扰动幅度跟个体质量挂钩:(1.0 - boid.m) 意味着适应度越低的 agent 被扰得越狠,高质量个体几乎不动。m 来自上一段把适应度缩放到 0.1~1.0 的结果,所以最差个体扰动系数接近 0.9 倍量程的 1%,最优个体只有 0.1 倍。

MQL5 / C++
class="type">class="kw">double maxMass = -DBL_MAX;
class="type">class="kw">double minMass = DBL_MAX;
class=class="str">"cmt">// Check for data presence before calculations
if (popSize <= class="num">0) class="kw">return;
for (class="type">int i = class="num">0; i < popSize; i++)
{
  if (a [i].f > maxMass) maxMass = a [i].f;
  if (a [i].f < minMass) minMass = a [i].f;
}
for (class="type">int i = class="num">0; i < popSize; i++)
{
  agent [i].m = u.Scale(a [i].f, minMass, maxMass, class="num">0.1, class="num">1.0);
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Apply neural control to a boid
class="type">void C_AO_NOA2::ApplyNeuralControl(S_NeuroBoids_Agent &boid, class="type">int pos)
{
  class=class="str">"cmt">// Use the neural network outputs to correct the speed
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    class=class="str">"cmt">// Make sure the index is not outside the array bounds 
    if (c < ArraySize(boid.outputs))
    {
      class=class="str">"cmt">// Apply neural speed correction with a given influence
      boid.dx [c] += boid.outputs [c] * neuralInfluence;
    }
  }
  class=class="str">"cmt">// Use the neural network outputs to adapt the flock parameters
  class=class="str">"cmt">// Check that the indices do not go beyond the array bounds
  class="type">int output_size = ArraySize(boid.outputs);
  class="type">class="kw">double cohesion_factor   = (coords < output_size) ? boid.outputs [coords] : class="num">0.5;
  class="type">class="kw">double separation_factor = (coords + class="num">1 < output_size) ? boid.outputs [coords + class="num">1] : class="num">0.5;
  class="type">class="kw">double alignment_factor  = (coords + class="num">2 < output_size) ? boid.outputs [coords + class="num">2] : class="num">0.5;
  class=class="str">"cmt">// Scale the base weights considering neural adaptation
  class=class="str">"cmt">// These variables are local and do not change global parameters
  class="type">class="kw">double local_cohesion   = cohesionWeight * (class="num">0.5 + cohesion_factor);
  class="type">class="kw">double local_separation = separationWeight * (class="num">0.5 + separation_factor);
  class="type">class="kw">double local_alignment  = alignmentWeight * (class="num">0.5 + alignment_factor);
  class=class="str">"cmt">// Random study with a given probability
  if (u.RNDprobab() < explorationRate)
  {
    class=class="str">"cmt">// Select a random coordinate for perturbation
    class="type">int c = (class="type">int)u.RNDfromCI(class="num">0, coords - class="num">1);
    class=class="str">"cmt">// Adaptive perturbation size depending on mass(fitness)
    class="type">class="kw">double perturbation_size = (class="num">1.0 - boid.m) * (rangeMax [c] - rangeMin [c]) * class="num">0.01;
    class=class="str">"cmt">// Add random perturbation
    boid.dx [c] += u.RNDfromCI(-perturbation_size, perturbation_size);
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Adaptive research depending on stagnation
class="type">void C_AO_NOA2::AdaptiveExploration()
{
  class=class="str">"cmt">// Determine if there is progress in the search
  if (MathAbs(fB - m_prevBestFitness) < class="num">0.000001)
  {
    m_stagnationCounter++;
  }
  else
  {
    m_stagnationCounter = class="num">0;
    m_prevBestFitness = fB;
  }
  class=class="str">"cmt">// Increase research during stagnation
  if (m_stagnationCounter > class="num">20)
  {
    class=class="str">"cmt">// Increase the probability of random exploration
把重复跑基准的任务交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同优化器在多周期上的稳定性分布,你只需核对 NOA2 在你常做品种上的过拟合迹象。

常见问题

通常含智能体坐标、速度、局部适应度梯度以及邻居聚合/分离/对齐的统计量,网络据此输出对标准 Boid 三规则权重的动态调整。
传统 Boids 缺乏从经历中学习的机制,易整体陷入局部峰;NOA2 的个体网络让边缘智能体保持探索、中心智能体转向开发,形成异质搜索。
目前小布内置的是诊断与可视化,不直接暴露优化器内核;你可把 NOA2 产出的参数集贴入品种页,让小布比对历史波动下的回测脆弱点。
层数过深会拖慢每代推断,且易让智能体过早过拟合到早期噪声景观,反而削弱群体层面的探索多样性。
可能该区域智能体密度低或景观平坦,网络判断碰撞风险小、更鼓励聚合以共享有前景的局部信息。