ALGLIB 库优化方法(第二部分)·进阶篇
🧮

ALGLIB 库优化方法(第二部分)·进阶篇

(2/3)· 接上篇基础算法,本篇实测盒式与非线性约束优化器在复杂函数上的真实表现

含代码示例 第 2/3 篇
很多人把 BC 当成 BLEIC 的平替随便用,却没注意起始点必须可行、函数须全局定义,大规模问题里踩了活跃约束的坑才意识到慢。非线性约束直接套无约束解法更是常见误操作,结果连可行域都进不去。

◍ 带约束的非线性优化怎么在MT5里跑起来

带约束的非线性优化(NLC)能在变量边界、线性/非线性等式与不等式多重限制下,去最小化一个N维目标函数 F(x)。做交易模型调参时,这相当于在「参数不能越界、成本不能超上限」这类硬约束里,逼出一个尽量优的解。 ALGLIB 给了三种求解器:SQP 适合中等规模且目标函数复杂的问题;AUL(预处理增广拉格朗日)适合大规模或目标函数算得快的场景;SLP 慢但复杂情况下更稳。对测试函数的实验里,AUL 效率明显占优,所以示例代码把另外两个求解器注释掉了。 状态变量类型是 CMinNLCState,中途想停就用 MinNLCRequestTermination 发停止指令。跑抛物面函数优化时,不限制调用次数能完全收敛到 1.0,但迭代超百万次(函数调用 1092273);限调用 28007 次时最优结果 0.8859;把步长压到 1e-16 虽不会像 BC 法那样过早停,结果却略差,只到 0.8544(调用 20005 次)。外汇/贵金属参数寻优属高风险实验,结果仅反映历史函数形态,实盘可能失效。 下面这段是可直接拷进 MT5 脚本跑的骨架,核心在 AUL 求解器的开启与边界、缩放、终止条件的设置。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class="type">void</span> OnStart()
{
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Initialization of optimization parameters---------------------------------------</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> numbTestFuncRuns = <span class="number">class="num">10000</span>;
&nbsp;&nbsp;<span class="keyword">class="type">int</span> <span class="keyword">params</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = <span class="number">class="num">1000</span>;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Create and initialize arrays for range bounds---------------------</span>
&nbsp;&nbsp;CRowDouble rangeMin, rangeMax;
&nbsp;&nbsp;rangeMin.Resize(<span class="keyword">params</span>);
&nbsp;&nbsp;rangeMax.Resize(<span class="keyword">params</span>);
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> rangeStep;
&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; <span class="keyword">params</span>; i++)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;rangeMin.Set(i, -<span class="number">class="num">10</span>);
&nbsp;&nbsp;&nbsp;&nbsp;rangeMax.Set(i,&nbsp;&nbsp;<span class="number">class="num">10</span>);
&nbsp;&nbsp;}
&nbsp;&nbsp;rangeStep = DBL_EPSILON;
&nbsp;&nbsp;CRowDouble x; x.Resize(<span class="keyword">params</span>);
&nbsp;&nbsp;CRowDouble s; s.Resize(<span class="keyword">params</span>);
&nbsp;&nbsp;s.Fill(<span class="number">class="num">1</span>);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Generate random initial parameter values in given ranges----</span>
&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; <span class="keyword">params</span>; i++)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;x.Set(i, rangeMin [i] + ((rangeMax [i] - rangeMin [i]) * rand() / <span class="number">class="num">32767.0</span>));
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Create objects for optimization------------------------------------------</span>
&nbsp;&nbsp;C_OptimizedFunction fFunc; fFunc.Init(<span class="keyword">params</span>, numbTestFuncRuns);
&nbsp;&nbsp;CObject&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj;
&nbsp;&nbsp;CNDimensional_Rep&nbsp;&nbsp; frep;
&nbsp;&nbsp;CMinNLCReport&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rep;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Setting parameters of the NLC optimization algorithm-----------------------------</span>
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> diffStep = <span class="number">class="num">0.00001</span>;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> rho&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <span class="number">class="num">1000.0</span>;
&nbsp;&nbsp;<span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;outerits = <span class="number">class="num">5</span>;
&nbsp;&nbsp;CAlglib::MinNLCCreateF&nbsp;&nbsp;&nbsp;&nbsp;(x, diffStep, fFunc.state);
&nbsp;&nbsp;CAlglib::MinNLCSetBC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(fFunc.state, rangeMin, rangeMax);
&nbsp;&nbsp;CAlglib::MinNLCSetScale&nbsp;&nbsp; (fFunc.state, s);
&nbsp;&nbsp;CAlglib::MinNLCSetCond&nbsp;&nbsp;&nbsp;&nbsp;(fFunc.state, rangeStep, numbTestFuncRuns);
&nbsp;&nbsp;<span class="comment"><span style="background-class="type">class="kw">color:rgb(class="num">216, class="num">232, class="num">194);">class=class="str">"cmt">//CAlglib::MinNLCSetAlgoSQP</span> (fFunc.state);</span>
&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">164, class="num">192, class="num">228);">CAlglib::MinNLCSetAlgoAUL</span> (fFunc.state, rho, outerits);
&nbsp;&nbsp;<span class="comment"><span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">246, class="num">200);">class=class="str">"cmt">//CAlglib::MinNLCSetAlgoSLP</span> (fFunc.state);</span>
&nbsp;&nbsp;CAlglib::MinNLCOptimize&nbsp;&nbsp; (fFunc.state, fFunc, frep, obj);
&nbsp;&nbsp;CAlglib::MinNLCResults&nbsp;&nbsp;&nbsp;&nbsp;(fFunc.state, x, rep);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Output of optimization results-----------------------------------------------</span>
&nbsp;&nbsp;Print(<span class="class="type">class="kw">string">"NLC, best result: "</span>, fFunc.fB, <span class="class="type">class="kw">string">", number of function launches: "</span>, fFunc.numberLaunches);
}
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">// Class for function optimization, inherits from CNDimensional_FVec</span>
<span class="keyword">class</span> C_OptimizedFunction : <span class="keyword">class="kw">public</span> CNDimensional_FVec
{
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span>
&nbsp;&nbsp;C_OptimizedFunction(<span class="keyword">class="type">void</span>) { }
&nbsp;&nbsp;~C_OptimizedFunction(<span class="keyword">class="type">void</span>) { }
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// A class="kw">virtual function to contain the function being optimized--------</span>
&nbsp;&nbsp;<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span> FVec(CRowDouble &amp;x, CRowDouble &amp;fi, CObject &amp;obj);

优化器的启动计数与提前终止逻辑

这段 MQL5 代码实现了非线性约束优化(MinNLC)里的初始化与回调函数骨架。Init() 负责把启动次数归零、记录允许的最大调用次数 maxNumberLaunchesAllowed,并把当前坐标数组 c 与最优坐标数组 cB 的尺寸按 coords 扩容。 fB 初始化为 -DBL_MAX,意味着在没找到任何可行解之前,最优目标函数值处于负无穷基准;只有后续 ffVal 超过它才会被刷新。 在 FVec() 回调里,每次被算法调用都会先 numberLaunches++,一旦达到上限就写回 DBL_MAX 并调用 MinNLCRequestTermination(state) 强制停止——这是防止外汇/贵金属参数寻优时陷入无限评估的硬保险。 目标函数取值通过 ObjectiveFunction(c) 算出,代码里用 fi.Set(0, -ffVal) 传回负值时,说明底层库在做最小化,而我们在逻辑层追踪的是最大化(fB 取最大)。若 ffVal > fB,则用 ArrayCopy 把当前坐标存进 cB。 开 MT5 把这段塞进你的 EA 优化类,把 maxNumberLaunchesAllowed 设成 5000 跑一轮,能在日志里看到 numberLaunches 触顶即停,避免 CPU 空转。外汇与贵金属杠杆品种波动剧烈,任何优化结果都只是历史样本上的概率倾向,实盘前务必小仓验证。

MQL5 / C++
  class=class="str">"cmt">// Initialization of optimization parameters---------------------------------------
  class="type">void Init(class="type">int coords,
             class="type">int maxNumberLaunchesAllowed)
  {
    numberLaunches        = class="num">0;
    maxNumbLaunchesAllowed = maxNumberLaunchesAllowed;
    fB = -DBL_MAX;
    ArrayResize(c,  coords);
    ArrayResize(cB, coords);
  }
  class=class="str">"cmt">//----------------------------------------------------------------------------
  CMinNLCState state;             class=class="str">"cmt">// State 
  class="type">int          numberLaunches;   class=class="str">"cmt">// Launch counter 
  class="type">class="kw">double fB;                     class=class="str">"cmt">// Best found value of the objective function(maximum)
  class="type">class="kw">double cB [];                  class=class="str">"cmt">// Coordinates of the point with the best function value
  class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
  class="type">class="kw">double c  [];                  class=class="str">"cmt">// Array for storing current coordinates
  class="type">int    maxNumbLaunchesAllowed; class=class="str">"cmt">// Maximum number of function calls allowed
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Implementation of the function to be optimized
class="type">void C_OptimizedFunction::FVec(CRowDouble &x, CRowDouble &fi, CObject &obj)
{
  class=class="str">"cmt">// Increase the function launch counter and limitation control----------------
  numberLaunches++;
  if (numberLaunches >= maxNumbLaunchesAllowed)
  {
    fi.Set(class="num">0, DBL_MAX);
    CAlglib::MinNLCRequestTermination(state);
    class="kw">return;
  }
  class=class="str">"cmt">// Copy input coordinates to internal array-------------------------
  for (class="type">int i = class="num">0; i < x.Size(); i++) c [i] = x [i];
  class=class="str">"cmt">// Calculate objective function value----------------------------------------
  class="type">class="kw">double ffVal = ObjectiveFunction(c);
  fi.Set(class="num">0, -ffVal);
  class=class="str">"cmt">// Update the best solution found--------------------------------------
  if (ffVal > fB)
  {
    fB = ffVal;
    ArrayCopy(cB, c);
  }
}

「LM优化器在MT5里的实际卡点」

莱文贝格-马夸尔特(LM)把高斯-牛顿的快和梯度下降的稳揉在一起,靠衰减比 λ 在两者之间切。地形清楚就沿斜坡猛冲,复杂了自动收成小碎步,本质是非线性最小二乘的折中解法。 状态对象用 CMinLMStateShell 承载,和前几种优化器一样设了函数调用上限,但 LM 本身没暴露停止命令——逻辑上该有 MinLMRequestTermination,实际库里没有。跑抛物面测试脚本时,默认配置在第 4003 次函数调用停下,最优值 0.6776047308612422;把迭代上限从 10000 放开,结果不变,说明它早就在局部洼地卡死了。 把差分步长压到 1e-16,算法更早就怂了:第 2001 次调用停,最优值掉到 0.6670839162547625。这说明步长太小会提前收敛到更差的局部解,不是越精细越好。 LM 对初值敏感,参数超 100 个就容易垮,实盘调参前先在脚本里用随机初值多跑几遍看散布。外汇和贵金属波动非线性强、滑点干扰大,拿这类优化做模型拟合属于高风险操作,结果只代表历史样本倾向。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class="type">void</span> <span class="functions">OnStart</span> ()
{
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Initialization of optimization parameters---------------------------------------</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> numbTestFuncRuns = <span class="number">class="num">10000</span>;
&nbsp;&nbsp;<span class="keyword">class="type">int</span> params&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = <span class="number">class="num">1000</span>;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> rangeMin [], rangeMax [], rangeStep;
&nbsp;&nbsp;<span class="functions">ArrayResize</span> (rangeMin,&nbsp;&nbsp;params);
&nbsp;&nbsp;<span class="functions">ArrayResize</span> (rangeMax,&nbsp;&nbsp;params);
&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; params; i++)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;rangeMin&nbsp;&nbsp;[i] = -<span class="number">class="num">10</span>;
&nbsp;&nbsp;&nbsp;&nbsp;rangeMax&nbsp;&nbsp;[i] =&nbsp;&nbsp;<span class="number">class="num">10</span>;
&nbsp;&nbsp;}
&nbsp;&nbsp;rangeStep = <span class="macro">DBL_EPSILON</span>;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> x [];
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> s [];
&nbsp;&nbsp;<span class="functions">ArrayResize</span> (x, params);
&nbsp;&nbsp;<span class="functions">ArrayResize</span> (s, params);
&nbsp;&nbsp;<span class="functions">ArrayInitialize</span> (s, <span class="number">class="num">1</span>);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Generate random initial parameter values in given ranges----</span>
&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; params; i++)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;x [i] = rangeMin [i] + ((rangeMax [i] - rangeMin [i]) * <span class="functions">rand</span> () / <span class="number">class="num">32767.0</span>);
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Create objects for optimization------------------------------------------</span>
&nbsp;&nbsp;C_OptimizedFunction fFunc; fFunc.Init(params, numbTestFuncRuns);
&nbsp;&nbsp;CObject&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj;
&nbsp;&nbsp;CNDimensional_Rep&nbsp;&nbsp; frep;
&nbsp;&nbsp;CMinLMReportShell&nbsp;&nbsp; rep;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Set the parameters of the LM optimization algorithm------------------------------</span>
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> diffStep = <span class="number">class="num">1</span>e-<span class="number">class="num">16</span>;<span class="comment">class=class="str">"cmt">//class="num">0.00001;</span>
&nbsp;&nbsp;CAlglib::MinLMCreateV&nbsp;&nbsp;(<span class="number">class="num">1</span>, x, diffStep, fFunc.state);
&nbsp;&nbsp;CAlglib::MinLMSetBC&nbsp;&nbsp;&nbsp;&nbsp;(fFunc.state, rangeMin, rangeMax);
&nbsp;&nbsp;CAlglib::MinLMSetScale(fFunc.state, s);
&nbsp;&nbsp;CAlglib::MinLMSetCond&nbsp;&nbsp;(fFunc.state, rangeStep, numbTestFuncRuns);
&nbsp;&nbsp;CAlglib::MinLMOptimize(fFunc.state, fFunc, frep, <span class="number">class="num">0</span>, obj);
&nbsp;&nbsp;CAlglib::MinLMResults&nbsp;&nbsp;(fFunc.state, x, rep);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Output of optimization results-----------------------------------------------</span>
&nbsp;&nbsp;<span class="functions">Print</span> (<span class="class="type">class="kw">string">"LM, best result: "</span>, fFunc.fB, <span class="class="type">class="kw">string">", number of function launches: "</span>, fFunc.numberLaunches);
}
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">// Class for function optimization, inherits from CNDimensional_FVec</span>
<span class="keyword">class</span> C_OptimizedFunction : <span class="keyword">class="kw">public</span> CNDimensional_FVec
{
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span>
&nbsp;&nbsp;C_OptimizedFunction(<span class="keyword">class="type">void</span>) { }
&nbsp;&nbsp;~C_OptimizedFunction(<span class="keyword">class="type">void</span>) { }
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// A class="kw">virtual function to contain the function being optimized--------</span>
&nbsp;&nbsp;<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">void</span> FVec(CRowDouble &amp;x, CRowDouble &amp;fi, CObject &amp;obj);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Initialization of optimization parameters---------------------------------------</span>
&nbsp;&nbsp;<span class="keyword">class="type">void</span> Init(<span class="keyword">class="type">int</span> coords,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> maxNumberLaunchesAllowed)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;numberLaunches&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = <span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;maxNumbLaunchesAllowed = maxNumberLaunchesAllowed;
&nbsp;&nbsp;&nbsp;&nbsp;fB = -<span class="macro">DBL_MAX</span>;

◍ 在目标函数里卡死调用次数与记录最优解

上面这段类声明和函数实现,干了两件硬事:一是用 maxNumbLaunchesAllowed 限制目标函数被调用的总次数,防止优化器陷入无限迭代;二是在每次计算后比较 ffVal 与 fB,把更优的坐标写进 cB。 注意 numberLaunches 每次进入 FVec 就自增 1,一旦大于等于上限,立刻把 fi[0] 设为 DBL_MAX 并 return,相当于给求解器一个“劣质解”信号让其停止。原注释里提到的 MinLMRequestTermination 并不存在,不能这么调。 实际跑 MT5 时,coords 维度决定了 c 和 cB 的大小,ArrayResize 必须在构造或初始化阶段完成;否则 c[i]=x[i] 会越界。外汇与贵金属参数优化属高风险,回测最优不代表实盘概率占优,须以样本外验证为准。

MQL5 / C++
  ArrayResize(c,  coords);
  ArrayResize(cB, coords);
  }
  class=class="str">"cmt">//----------------------------------------------------------------------------
  CMinLMStateShell state;           class=class="str">"cmt">// State 
  class="type">int              numberLaunches; class=class="str">"cmt">// Launch counter 
  class="type">class="kw">double fB;                        class=class="str">"cmt">// Best found value of the objective function(maximum)
  class="type">class="kw">double cB [];                     class=class="str">"cmt">// Coordinates of the point with the best function value
  class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
  class="type">class="kw">double c  [];                     class=class="str">"cmt">// Array for storing current coordinates
  class="type">int     maxNumbLaunchesAllowed;   class=class="str">"cmt">// Maximum number of function calls allowed
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Implementation of the function to be optimized
class="type">void C_OptimizedFunction::FVec(CRowDouble &x, CRowDouble &fi, CObject &obj)
{
  class=class="str">"cmt">// Increase the function launch counter and limitation control----------------
  numberLaunches++;
  if (numberLaunches >= maxNumbLaunchesAllowed)
  {
    fi.Set(class="num">0, DBL_MAX);
    class=class="str">"cmt">//CAlglib::MinLMRequestTermination(state); // such method does not exist
    class="kw">return;
  }
  class=class="str">"cmt">// Copy input coordinates to internal array-------------------------
  for (class="type">int i = class="num">0; i < x.Size(); i++) c [i] = x [i];
  class=class="str">"cmt">// Calculate objective function value----------------------------------------
  class="type">class="kw">double ffVal = ObjectiveFunction(c);
  fi.Set(class="num">0, -ffVal);
  class=class="str">"cmt">// Update the best solution found--------------------------------------
  if (ffVal > fB)
  {
    fB = ffVal;
    ArrayCopy(cB, c);
  }
}

ALGLIB各优化器的调用骨架对照

直接手写 ALGLIB 优化程序容易迷路:不同算法要求的变量类型、边界约束父类、内部对象集合和函数调用顺序都不一样。把 BLEIC、LBFGS、NS、BC、NLC、LM 这六个常用优化器横向拉一张表,能少走很多弯路。 BLEIC 与 LBFGS 面向 double 型变量,前者靠 MinBLEICCreateF 起手,后者用 MinLBFGSCreateF,两者都需先建 Cobject 与 CNDimensional_Rep 再做 Optimize。NS、BC、NLC 则吃 CRowDouble 向量,创建函数分别是 MinNSCreateF、MinBCCreateF、MinNLCCreateF,且都带 SetBC/SetScale/SetCond 三步前置。 LM 比较特殊,它用 double 变量但走 MinLMCreateV 起手,且官方未提供 RequestTermination 方法(表中标注 does not exist),实盘里想中途撤优化得另想办法。 这张对照的价值在于:你按表里的「对象列表 + 调用方法列表」顺序抄进 EA,编译报错的概率会明显降低;外汇与贵金属策略参数寻优属高风险操作,回测拟合不代表实盘概率占优。

把重复跑分交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同优化器在多维面上的收敛对比,你只需判断哪类约束更贴近自己的参数空间。

常见问题

BC 只处理盒式约束,约束激活策略更轻,大规模多活跃约束时可能比 BLEIC 更快;BLEIC 还能吃线性等式-不等式,通用性更高。
可以,小布内置了常见优化器的收敛与约束诊断视图,省去你手写脚本跑测试的重复劳动,你专注决策选型即可。
相对无约束方法鲁棒些,但非线性约束下初始点离可行域太远仍可能迭代失败,建议先做可行性探测再跑主优化。
LM 倾向用于残差平方和类最小二乘问题,参数不多且目标平滑时概率上收敛稳,但盒式边界需额外处理。
minbc 对应 BC 盒式约束优化,minnlc 对应基于预处理增广拉格朗日的 NLC,具体接口参数见文末函数表小节。