辩证搜索(DA)(基础篇)
📘

辩证搜索(DA)(基础篇)

第 1/2 篇

辩证搜索在 MT5 里的落地雏形

辩证搜索(DA)是一套把候选参数空间反复做对立面验证的算法思路,在 MetaTrader 5 中可作为交易系统优化器之外的补充筛选层。它不依赖单次回测的漂亮曲线,而是让一组参数在「盈利假设」与「反向证伪」之间来回碰撞。 原文作者 Andrey Dik 在 2025 年 12 月 3 日 10:01 发布了该主题,截至统计时点获得 392 次查看、0 条评论,说明这类偏底层算法内容在零售交易者中曝光有限但具备深挖价值。 该小节仅给出「概述 / 算法的实现 / 测试结果」三段骨架,尚未展开代码与回测数值;若你要验证可行性,可先在 MT5 策略测试器外用自定义 DLL 跑一轮参数对立搜索,再比对原生遗传优化结果。外汇与贵金属品种波动跳空频繁,任何算法筛选都只是降低过拟合概率,不消除实盘高风险。

◍ 把矛盾对立写进寻优循环

辩证唯物主义讲发展来自对立力量的冲突,量变堆到临界点就跳变到质变,再经否定之否定形成保留旧有优点的综合。2009 年 Serdar Kadioglu 与 Meinolf Sellmann 提出的辩证算法(DA)把这套哲学搬进了约束优化:任一解视为正题,内含与其反题互动改进的潜力。 算法实现里,思辨型思考者用大步长在解空间撒网,实践型思考者贴着当前解做局部搜。群体被切成两类:推测型 k1 让质量相近但空间距离远的解互相作用;实践型 p-k1 让质量差但空间近的解互相作用。这种划分就是对立统一在寻优里的落地。 一轮搜索分三步:先理解(评估并排解),再辩证互动(给解找对立面),最后合成出新解。解一出来就引发新矛盾,逼出下一轮,形成连续改进。你在 MT5 里跑自定义优化器时,可对照这个结构检查自己的种群划分是否真的制造了对立,而不是同质扰动。

「辩证优化算法的代码骨架」

辩证算法(DA)把 50 个代理拆成三组:1 个最优思考者、k1=3 个推测型思考者、剩下 46 个实践型思考者。位置更新统一走 X(i)=X(i)+μ⊙(Xanti(i)-X(i)),μ 来自均匀分布或正态分布,逐元素乘保证两类思考者分别负责广域探索与局部细化。 与差分进化(DE)相比,DA 的解更新方程形式接近,但向量选取不是随机差分,而是按欧氏距离与解质量确定性地挑对立面。实测中两类思考者都用均匀分布时全局搜索效率更高,尽管计算距离让复杂度略升。 下面这段 MQL5 类声明给出了参数默认值与初始化逻辑。popSize=50、k1=3、k2=10 直接写死在构造函数,运行时也能通过 SetParams 改。开 MT5 新建 EA 把类挂上,先跑默认参数看收敛曲线,再调 k1 观察推测组比例对结果的影响。 代码逐行拆解:

  • class C_AO_DA : public C_AO:从基类 C_AO 继承,复用通用优化框架。
  • ao_name="DA" / ao_desc="Dialectical Algorithm":算法标识与描述,用于日志区分。
  • popSize=50:种群规模,即参与优化的代理总数。
  • k1=3:推测型思考者数量,决定自由探索的精英个数。
  • k2=10:每个推测型思考者交互的最近邻居数。
  • ArrayResize(params,3):参数数组扩到 3 项,存 popSize/k1/k2。
  • SetParams():把 params 里的值回写到成员变量,支持运行中动态调参。
  • Init() 入参:rangeMinP 最小边界、rangeMaxP 最大边界、rangeStepP 步长,供后续随机布点用。
MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">// Class implementing the dialectical optimization algorithm</span>
<span class="keyword">class</span> C_AO_DA : <span class="keyword">class="kw">public</span> C_AO
{
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span>
&nbsp;&nbsp;~C_AO_DA() { }
&nbsp;&nbsp;C_AO_DA()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ao_name = <span class="class="type">class="kw">string">"DA"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_desc = <span class="class="type">class="kw">string">"Dialectical Algorithm"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档]
&nbsp;&nbsp;&nbsp;&nbsp;popSize = <span class="number">class="num">50</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// population size</span>
&nbsp;&nbsp;&nbsp;&nbsp;k1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <span class="number">class="num">3</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// speculative thinkers</span>
&nbsp;&nbsp;&nbsp;&nbsp;k2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <span class="number">class="num">10</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// neighbours</span>
&nbsp;&nbsp;&nbsp;&nbsp;ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">3</span>);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">0</span>].name = <span class="class="type">class="kw">string">"popSize"</span>; <span class="keyword">params</span> [<span class="number">class="num">0</span>].val = popSize;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">1</span>].name = <span class="class="type">class="kw">string">"k1"</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">1</span>].val = k1;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">2</span>].name = <span class="class="type">class="kw">string">"k2"</span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">2</span>].val = k2;
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Setting algorithm parameters</span>
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetParams()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;popSize = (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">0</span>].val;
&nbsp;&nbsp;&nbsp;&nbsp;k1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">1</span>].val;
&nbsp;&nbsp;&nbsp;&nbsp;k2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">2</span>].val;
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="keyword">class="type">bool</span> Init(<span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;rangeMinP&nbsp;&nbsp;[], <span class="comment">class=class="str">"cmt">// minimum search range</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;rangeMaxP&nbsp;&nbsp;[], <span class="comment">class=class="str">"cmt">// maximum search range</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> &amp;rangeStepP [], <span class="comment">class=class="str">"cmt">// search step</span>

距离计算与初始化落点

套利者优化器的核心里,两个解向量之间的远近用欧氏距离衡量。代码里的 EuclideanDistance 接收两个 double 数组和维度 dim,逐维求差的平方并累加,最后开根号返回。 具体拆解:sum 初值为 0,diff 初值 0.0;for 循环从 i=0 跑到 dim-1,每轮 diff = vec1[i] - vec2[i],sum += diff*diff;循环结束 return MathSqrt(sum)。这一步没有任何魔法,就是标准 L2 范数。 Init 函数负责把搜索范围接进来:rangeMinP、rangeMaxP、rangeStepP 分别是最小范围、最大范围、搜索步长,epochsP 默认 0 表示迭代轮数。内部先调 StandardInit 做范围合法性校验,失败直接返回 false,通过才返回 true。 在 MT5 里把这段贴进 EA 或指标工程,改 dim 对应的参数维度,就能验证不同参数向量间的距离变化;外汇与贵金属杠杆高、滑点大,实盘前请用策略测试器跑历史数据确认稳定性。

MQL5 / C++
const class="type">int      epochsP = class="num">0);   class=class="str">"cmt">// number of epochs
class="type">void Moving();     class=class="str">"cmt">// Moving agents in the search space
class="type">void Revision();    class=class="str">"cmt">// Review and update the best solutions found
class=class="str">"cmt">//----------------------------------------------------------------------------
class="type">int k1;       class=class="str">"cmt">// number of speculative thinkers
class="type">int k2;       class=class="str">"cmt">// number of neighbors to analyze
class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
class=class="str">"cmt">// Calculate the Euclidean distance between two vectors
class="type">class="kw">double EuclideanDistance(const class="type">class="kw">double &vec1 [], const class="type">class="kw">double &vec2 [], const class="type">int dim)
{
  class="type">class="kw">double sum  = class="num">0;
  class="type">class="kw">double diff = class="num">0.0;
  for (class="type">int i = class="num">0; i < dim; i++)
  {
    diff = vec1 [i] - vec2 [i];
    sum += diff * diff;
  }
  class="kw">return MathSqrt(sum);
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">bool C_AO_DA::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="num">0)   class=class="str">"cmt">// number of epochs
{
  if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return false;
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class="kw">return true;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————

常见问题

在初始化阶段定义正反两组候选参数,循环中同时评估两者并保留对立样本,避免只选单边最优。
用距离计算把初始点撒在参数空间两端,而不是随机单点,能提升跳出局部的概率。
小布可把矛盾对立的寻优逻辑写成诊断项,打开对应品种页就能看AI给出的双侧参数倾向。
循环里只存了单极最优值,没留对立缓冲;改成分离存储正反样本即可看到辩证结果。
它衡量候选解之间的疏密,决定初始化落点分布,让对立两端都被覆盖而不是挤在一起。