群体优化算法·进阶篇
🧪

群体优化算法·进阶篇

(2/3)·跳过历史铺垫,直接钻进测试基台与简单 OA 的工程细节,看清收敛率背后的真实代价

案例拆解 第 2/3 篇
很多人把群体优化当成黑箱调参玩具,直接丢进 EA 跑回测,却从没验证过算法在测试函数上的收敛稳定性。一旦市场状态偏移,参数群可能集体陷入局部极值,外汇贵金属的高杠杆会把这种盲区放大成实盘亏损。

「搭一个能横向比算法的评估台」

比较不同优化算法(OA)不能拍脑袋,因为各自擅长的问题类别不同:有的收敛快但变量一多就拉胯,有的能扛上千维却不稳定。要横向比,得先把收敛、收敛率、稳定性、伸缩性拆成统一口径。 收敛性用三个测试函数,把结果线性映射到 0.0(最差)到 1.0(最佳),这样不同函数之间能直接比「保底能力」。收敛率看第 1000 次和第 10,000 次运行的最佳值,曲线越早贴顶越快。稳定性是每个函数跑 5 次取均值,波动大的算法在这一项会现原形。 伸缩性差异最直观:有些 OA 只能处理 ≤2 个变量,有些能跑 1000 个变量,后者才可能拿去当神经网络的优化器。MT5 优化器默认大约只给 10,000 步找最大值,而两个变量的 Skin 函数若用 0.00001 步长枚举,X、Y 各百万步,总计算量是 10^12 次——OA 的搜索难度可想而知。 下面这段父类把参数边界、函数极值、名称都封装好,子类只重写 Core() 即可;枚举器 SelectFunction() 用来按需切换测试函数对象。本文及后续测试统一用步长 0.0(或算法最小可能值),别手滑设成枚举步长。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class</span> C_Function
{
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//====================================================================</span>
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> CalcFunc(<span class="keyword">class="type">class="kw">double</span> &amp;args [], <span class="comment">class=class="str">"cmt">//function arguments</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp; amount)&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//amount of runs functions</span>
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> x, y;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> sum = <span class="number">class="num">0.0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i &lt; amount; i++)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x = args [i * <span class="number">class="num">2</span>];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y = args [i * <span class="number">class="num">2</span> + <span class="number">class="num">1</span>];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sum += Core(x, y);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;sum /= amount;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span> sum;
&nbsp;&nbsp;}
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> GetMinArg() { <span class="keyword">class="kw">return</span> minArg;}
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> GetMaxArg() { <span class="keyword">class="kw">return</span> maxArg;}
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> GetMinFun() { <span class="keyword">class="kw">return</span> minFun;}
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> GetMaxFun() { <span class="keyword">class="kw">return</span> maxFun;}
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> GetNamFun() { <span class="keyword">class="kw">return</span> fuName;}
&nbsp;&nbsp;<span class="keyword">class="kw">protected</span>: <span class="comment">class=class="str">"cmt">//==================================================================</span>
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetMinArg(<span class="keyword">class="type">class="kw">double</span> min) { minArg = min;}
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetMaxArg(<span class="keyword">class="type">class="kw">double</span> max) { maxArg = max;}
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetMinFun(<span class="keyword">class="type">class="kw">double</span> min) { minFun = min;}
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetMaxFun(<span class="keyword">class="type">class="kw">double</span> max) { maxFun = max;}
&nbsp;&nbsp;<span class="keyword">class="type">void</span> SetNamFun(<span class="keyword">class="type">class="kw">string</span> nam) { fuName = nam;}
&nbsp;&nbsp;<span class="keyword">class="kw">private</span>: <span class="comment">class=class="str">"cmt">//====================================================================</span>
&nbsp;&nbsp;<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">class="kw">double</span> Core(<span class="keyword">class="type">class="kw">double</span> x, <span class="keyword">class="type">class="kw">double</span> y) { <span class="keyword">class="kw">return</span> <span class="number">class="num">0.0</span>;}
&nbsp;&nbsp;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> minArg;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> maxArg;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> minFun;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> maxFun;
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> fuName;
};
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">enum</span> EFunc
{
&nbsp;&nbsp;Skin,
&nbsp;&nbsp;Forest,
&nbsp;&nbsp;Megacity,
&nbsp;&nbsp;
};
C_Function *SelectFunction(EFunc f)
{
&nbsp;&nbsp;C_Function *func;
&nbsp;&nbsp;<span class="keyword">class="kw">switch</span> (f)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span>&nbsp;&nbsp;Skin:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;func = <span class="keyword">new</span> C_Skin(); <span class="keyword">class="kw">return</span> (<span class="functions">GetPointer</span> (func));
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span>&nbsp;&nbsp;Forest:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;func = <span class="keyword">new</span> C_Forest(); <span class="keyword">class="kw">return</span> (<span class="functions">GetPointer</span> (func));
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span>&nbsp;&nbsp;Megacity:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;func = <span class="keyword">new</span> C_Megacity(); <span class="keyword">class="kw">return</span> (<span class="functions">GetPointer</span> (func));
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">default</span>:

◍ 把测试函数塞进类里跑二维曲面

上面那段工厂方法只是入口,真正定义优化地貌的是派生自 C_Function 的几个子类。每个子类在构造函数里写死名称、自变量区间和已知极值坐标,Core() 才是被采样器反复调用的目标函数。 以 C_Skin 为例:自变量限在 [-5.0, 5.0],最小函数值 -4.3182 出现在 (x=3.07021, y=3.315935),最大 14.0606 在 (x=-3.315699, y=-3.072485)。它的 Core 用 2*x*x、2*y*y 做平方放大,再套 cos/sin 偏移平方求和,曲面带多个浅坑,适合测算法是否陷局部极小。 C_Forest 的区间很怪:MinArg=-50、MaxArg=-18,也就是 x、y 都被压在负轴一段。最大值 15.95123239744 落在 (-25.1327, -32.5575),Core 用 sqrt(abs(...)) 包 sin/cos 再四次方,负值直接截断为 0,形成大片零值平原加尖峰,对全局搜索是硬骨头。 C_Megacity 把区间放宽到 [-15, 15],最小函数值 0 且注明 many points——意味着零值等高位可能连成片,容易让优化器在平原上乱走。外汇与贵金属实盘用这类测试函数验证 EA 寻优逻辑时,须注意历史拟合不代表未来,杠杆品种风险极高。

MQL5 / C++
func = new C_Skin(); class="kw">return (GetPointer(func));
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class C_Skin : class="kw">public C_Function
{
  class="kw">public: class=class="str">"cmt">//===================================================================
  C_Skin()
  {
    SetNamFun("Skin");
    SetMinArg(-class="num">5.0);
    SetMaxArg(class="num">5.0);
    SetMinFun(-class="num">4.3182);   class=class="str">"cmt">//[x=class="num">3.07021;y=class="num">3.315935] class="num">1 point
    SetMaxFun(class="num">14.0606);   class=class="str">"cmt">//[x=-class="num">3.315699;y=-class="num">3.072485] class="num">1 point
  }
  class="kw">private: class=class="str">"cmt">//===================================================================
  class="type">class="kw">double Core(class="type">class="kw">double x, class="type">class="kw">double y)
  {
    class="type">class="kw">double a1=class="num">2*x*x;
    class="type">class="kw">double a2=class="num">2*y*y;
    class="type">class="kw">double b1=MathCos(a1)-class="num">1.1;
    b1=b1*b1;
    class="type">class="kw">double c1=MathSin(class="num">0.5*x)-class="num">1.2;
    c1=c1*c1;
    class="type">class="kw">double d1=MathCos(a2)-class="num">1.1;
    d1=d1*d1;
    class="type">class="kw">double e1=MathSin(class="num">0.5*y)-class="num">1.2;
    e1=e1*e1;
    class="type">class="kw">double res=b1+c1-d1+e1;
    class="kw">return(res);
  }
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class C_Forest : class="kw">public C_Function
{
  class="kw">public: class=class="str">"cmt">//===================================================================
  C_Forest()
  {
    SetNamFun("Forest");
    SetMinArg(-class="num">50.0);
    SetMaxArg(-class="num">18.0);
    SetMinFun(class="num">0.0);                class=class="str">"cmt">//many points
    SetMaxFun(class="num">15.95123239744);  class=class="str">"cmt">//[x=-class="num">25.132741228718345;y=-class="num">32.55751918948773] class="num">1 point
  }
  class="kw">private: class=class="str">"cmt">//===================================================================
  class="type">class="kw">double Core(class="type">class="kw">double x, class="type">class="kw">double y)
  {
    class="type">class="kw">double a = MathSin(MathSqrt(MathAbs(x - class="num">1.13) + MathAbs(y - class="num">2.0)));
    class="type">class="kw">double b = MathCos(MathSqrt(MathAbs(MathSin(x))) + MathSqrt(MathAbs(MathSin(y - class="num">2.0))));
    class="type">class="kw">double f = a + b;
    class="type">class="kw">double res = MathPow(f, class="num">4);
    if (res < class="num">0.0) res = class="num">0.0;
    class="kw">return (res);
  }
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class C_Megacity : class="kw">public C_Function
{
  class="kw">public: class=class="str">"cmt">//===================================================================
  C_Megacity()
  {
    SetNamFun("Megacity");
    SetMinArg(-class="num">15.0);
    SetMaxArg(class="num">15.0);
    SetMinFun(class="num">0.0);   class=class="str">"cmt">//many points

网格扫描找极值的一段实测代码

下面这段 MQL5 把自定义函数放到一个二维网格里暴力扫一遍,目的是抓出最大值点和最小值点。输入参数里 Step 默认 0.01,意味着在 argMin 到 argMax 区间内,x、y 两个维度各要跑成百上千步,cnt 计数器会如实打印总调用次数。 核心循环先用双层 while 遍历 (x,y),每一步调 TestFunc.CalcFunc 算函数值,再和当前 maxFuncValue / minFuncValue 比大小并记录坐标。注意 cnt==1 时强行把首尾值赋给最大最小,避免初始 0 误判——这个细节直接决定打印出来的极值坐标是否可信。 外汇与贵金属品种用这类扫描做参数敏感性测试时,网格过密会让 OnStart 跑满几十秒,且 MT5 策略测试器里的高频调用有滑点失真风险,结果只能当概率参考。

MQL5 / C++
SetMaxFun(class="num">15.0);   class=class="str">"cmt">//[x=`class="num">3.16;y=class="num">1.990] class="num">1 point
  }
  class="kw">private: class=class="str">"cmt">//===================================================================
  class="type">class="kw">double Core(class="type">class="kw">double x, class="type">class="kw">double y)
  {
    class="type">class="kw">double a = MathSin(MathSqrt(MathAbs(x - class="num">1.13) + MathAbs(y - class="num">2.0)));
    class="type">class="kw">double b = MathCos(MathSqrt(MathAbs(MathSin(x))) + MathSqrt(MathAbs(MathSin(y - class="num">2.0))));
    class="type">class="kw">double f = a + b;
    class="type">class="kw">double res = floor(MathPow(f, class="num">4));
    class="kw">return (res);
  }
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
input EFunc   Function          = Skin;
input class="type">class="kw">double Step               = class="num">0.01;
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void OnStart()
{
  C_Function *TestFunc = SelectFunction(Function);
  class="type">class="kw">double argMin = TestFunc.GetMinArg();
  class="type">class="kw">double argMax = TestFunc.GetMaxArg();
  class="type">class="kw">double maxFuncValue = class="num">0;
  class="type">class="kw">double xMaxFunc      = class="num">0.0;
  class="type">class="kw">double yMaxFunc      = class="num">0.0;
  
  class="type">class="kw">double minFuncValue = class="num">0;
  class="type">class="kw">double xMinFunc      = class="num">0.0;
  class="type">class="kw">double yMinFunc      = class="num">0.0;
  
  class="type">class="kw">double fValue        = class="num">0.0;
  class="type">class="kw">double arg [class="num">2];
  arg [class="num">0] = argMin;
  arg [class="num">1] = argMin;
  class="type">long cnt = class="num">0;
  class="kw">while (arg [class="num">1] <= argMax && !IsStopped())
  {
    arg [class="num">0] = argMin;
    class="kw">while (arg [class="num">0] <= argMax && !IsStopped())
    {
      cnt++;
      fValue = TestFunc.CalcFunc(arg, class="num">1);
      if (fValue > maxFuncValue)
      {
        maxFuncValue = fValue;
        xMaxFunc = arg [class="num">0];
        yMaxFunc = arg [class="num">1];
      }
      if (fValue < minFuncValue)
      {
        minFuncValue = fValue;
        xMinFunc = arg [class="num">0];
        yMinFunc = arg [class="num">1];
      }
      arg [class="num">0] += Step;
      
      if (cnt == class="num">1)
      {
       maxFuncValue = fValue;
       minFuncValue = fValue;
      }
    }
    arg [class="num">1] += Step;
  }
  
  Print("======", TestFunc.GetNamFun(), ", launch counter: ", cnt);

「把优化结果画到画布上看个明白」

上面那段 Print 把极值的坐标和函数值用 16 位精度打出来,maxFuncValue / minFuncValue 配合 xMaxFunc、yMinFunc 等变量,能直接看到算法在这一轮搜到的峰谷位置。 真正落地要看下面这套输入参数和画布初始化。Population_P 默认 50,Test3FuncRuns_P 设到 500 次,意味着第三组测试函数会被反复跑 500 遍来统计收敛表现;NumberRepetTest_P=5 说明每组实验重复 5 次取平均,避免单次随机性误导判断。 画布部分用 CCanvas 开了 750×375 的位图标签,测试函数显示区砍掉 2 像素边距变成 373×373。FunctScrin 是个二维 color 数组,用来把每次评估的函数值映射成像素颜色。开 MT5 把这段 include 和 input 原样贴进 EA,改 Test3FuncRuns_P 到 1000,看右下角终端里 MinFuncValue 是否继续往下走——外汇与贵金属复盘用这类算法搜参数极易过拟合,高风险,仅作离线研究。

MQL5 / C++
Print("MaxFuncValue: ", DoubleToString(maxFuncValue, class="num">16), " X: ", DoubleToString(xMaxFunc, class="num">16), " Y: ", DoubleToString(yMaxFunc, class="num">16));
Print("MinFuncValue: ", DoubleToString(minFuncValue, class="num">16), " X: ", DoubleToString(xMinFunc, class="num">16), " Y: ", DoubleToString(yMinFunc, class="num">16));

 class="kw">delete TestFunc;
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="macro">#include <Canvas\Canvas.mqh>
class="macro">#include <\Math\Functions.mqh>
class="macro">#include "AO_RND.mqh"
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
input class="type">int      Population_P       = class="num">50;
input class="type">class="kw">double ArgumentStep_P    = class="num">0.0;
input class="type">int      Test1FuncRuns_P   = class="num">1;
input class="type">int      Test2FuncRuns_P   = class="num">20;
input class="type">int      Test3FuncRuns_P   = class="num">500;
input class="type">int      Measur1FuncValue_P = class="num">1000;
input class="type">int      Measur2FuncValue_P = class="num">10000;
input class="type">int      NumberRepetTest_P  = class="num">5;
input class="type">int      RenderSleepMsc_P   = class="num">0;
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">int WidthMonitor = class="num">750;  class=class="str">"cmt">//monitor screen width
class="type">int HeighMonitor = class="num">375;  class=class="str">"cmt">//monitor screen height
class="type">int WidthScrFunc = class="num">375 - class="num">2;  class=class="str">"cmt">//test function screen width
class="type">int HeighScrFunc = class="num">375 - class="num">2;  class=class="str">"cmt">//test function screen height
CCanvas Canvas;  class=class="str">"cmt">//drawing table
C_AO_RND AO;     class=class="str">"cmt">//AO object
C_Skin        SkiF;
C_Forest    ForF;
C_Megacity  ChiF;
class="kw">struct S_CLR
{
    class="type">class="kw">color clr [];
};
S_CLR FunctScrin []; class=class="str">"cmt">//two-dimensional matrix of colors
class="type">class="kw">double ScoreAll = class="num">0.0;
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void OnStart()
{
  class=class="str">"cmt">//creating a table -----------------------------------------------------------
  class="type">class="kw">string canvasName = "AO_Test_Func_Canvas";
  if (!Canvas.CreateBitmapLabel(canvasName, class="num">5, class="num">30, WidthMonitor, HeighMonitor, COLOR_FORMAT_ARGB_RAW))
  {
    Print("Error creating Canvas: ", GetLastError());
    class="kw">return;
  }
  ObjectSetInteger(class="num">0, canvasName, OBJPROP_HIDDEN, class="kw">false);
  ObjectSetInteger(class="num">0, canvasName, OBJPROP_SELECTABLE, true);
  ArrayResize(FunctScrin, HeighScrFunc);
  for (class="type">int i = class="num">0; i < HeighScrFunc; i++)
  {
    ArrayResize(FunctScrin [i].clr, HeighScrFunc);
  }

  class=class="str">"cmt">//============================================================================
  class=class="str">"cmt">//Test Skin###################################################################

◍ 三组函数族的画布比对跑法

这段调用把三个函数对象(SkiF、ForF、ChiF)各自喂给 FuncTests 做可视化验证,每组都跑 Test1~3 三套函数,并用不同颜色区分收敛轨迹:Lime 对应测试一,Aqua 对应测试二,OrangeRed 对应测试三。 SkiF 的已知最优区被锁在 x∈[-3.315699, -3.072485],ForF 落在 [-25.132741228718345, -32.55751918948773],ChiF 则是正向区间 [3.16, 1.990]——这些边界直接写死在参数里,改一处就要同步三行,容易漏。 CanvasErase 负责清黑底并填两块白矩形:左侧 1×1 到 HeighMonitor-2 的竖条,和右侧 HeighMonitor+1 起始的主图区,都是给后续 DrawFunctionGraph 留干净背景。 FuncTests 内部先用 DrawFunctionGraph 画曲线,SendGraphToCanvas 搬上画布,再把理论最优点 (xBest,yBest) 经 Scale 映射到像素坐标,画两个半径 10/11 的黑圈标记,Update 后 Sleep(1000) 停一秒让人眼能追上。 最后一行 Print 输出 ScoreAll/18.0,说明总评分被 18 次运行平摊(3 函数 × 3 测试 × 2 对象族?实际是 3×3×2=18 的计数逻辑),开 MT5 把这段塞进 OnStart 尾段就能直接看三族收敛图差异。外汇与贵金属相关回测高风险,图形结论仅作概率参考。

MQL5 / C++
  Print("=============================");
  CanvasErase();
  FuncTests(SkiF, Test1FuncRuns_P, SkiF.GetMinFun(), SkiF.GetMaxFun(), -class="num">3.315699, -class="num">3.072485, clrLime);
  FuncTests(SkiF, Test2FuncRuns_P, SkiF.GetMinFun(), SkiF.GetMaxFun(), -class="num">3.315699, -class="num">3.072485, clrAqua);
  FuncTests(SkiF, Test3FuncRuns_P, SkiF.GetMinFun(), SkiF.GetMaxFun(), -class="num">3.315699, -class="num">3.072485, clrOrangeRed);
  
  class=class="str">"cmt">//Test Forest#################################################################
  Print("=============================");
  CanvasErase();
  FuncTests(ForF, Test1FuncRuns_P, ForF.GetMinFun(), ForF.GetMaxFun(), -class="num">25.132741228718345, -class="num">32.55751918948773, clrLime);
  FuncTests(ForF, Test2FuncRuns_P, ForF.GetMinFun(), ForF.GetMaxFun(), -class="num">25.132741228718345, -class="num">32.55751918948773, clrAqua);
  FuncTests(ForF, Test3FuncRuns_P, ForF.GetMinFun(), ForF.GetMaxFun(), -class="num">25.132741228718345, -class="num">32.55751918948773, clrOrangeRed);
  
  class=class="str">"cmt">//Test Megacity#############################################################
  Print("=============================");
  CanvasErase();
  FuncTests(ChiF, Test1FuncRuns_P, ChiF.GetMinFun(), ChiF.GetMaxFun(), class="num">3.16, class="num">1.990, clrLime);
  FuncTests(ChiF, Test2FuncRuns_P, ChiF.GetMinFun(), ChiF.GetMaxFun(), class="num">3.16, class="num">1.990, clrAqua);
  FuncTests(ChiF, Test3FuncRuns_P, ChiF.GetMinFun(), ChiF.GetMaxFun(), class="num">3.16, class="num">1.990, clrOrangeRed);
  
  Print("All score for C_AO_RND: ", ScoreAll / class="num">18.0);
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void CanvasErase()
{
  Canvas.Erase(XRGB(class="num">0, class="num">0, class="num">0));
  Canvas.FillRectangle(class="num">1,               class="num">1, HeighMonitor - class="num">2, HeighMonitor - class="num">2, COLOR2RGB(clrWhite));
  Canvas.FillRectangle(HeighMonitor + class="num">1, class="num">1, WidthMonitor - class="num">2, HeighMonitor - class="num">2, COLOR2RGB(clrWhite));
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void FuncTests(C_Function &f,
                class="type">int       funcCount,
                class="type">class="kw">double    minFuncVal,
                class="type">class="kw">double    maxFuncVal,
                class="type">class="kw">double    xBest,
                class="type">class="kw">double    yBest,
                class="type">class="kw">color     clrConv)
{
  DrawFunctionGraph(f.GetMinArg(), f.GetMaxArg(), minFuncVal, maxFuncVal, f);
  SendGraphToCanvas(class="num">1, class="num">1);
  class="type">int x = (class="type">int)Scale(xBest, f.GetMinArg(), f.GetMaxArg(), class="num">0, WidthScrFunc - class="num">1, class="kw">false);
  class="type">int y = (class="type">int)Scale(yBest, f.GetMinArg(), f.GetMaxArg(), class="num">0, HeighScrFunc - class="num">1, class="kw">false);
  Canvas.Circle(x + class="num">1, y + class="num">1, class="num">10, COLOR2RGB(clrBlack));
  Canvas.Circle(x + class="num">1, y + class="num">1, class="num">11, COLOR2RGB(clrBlack));
  Canvas.Update();
  Sleep(class="num">1000);
  class="type">int xConv = class="num">0.0;

正文

&nbsp;&nbsp;<span class="keyword">int</span> yConv = <span class="number">0.0</span>; &nbsp;&nbsp;<span class="keyword">int</span> EpochCmidl = <span class="number">0</span>; &nbsp;&nbsp;<span class="keyword">int</span> EpochCount = <span class="number">0</span>; &nbsp;&nbsp;<span class="keyword">double</span> aveMid = <span class="number">0.0</span>; &nbsp;&nbsp;<span class="keyword">double</span> aveEnd = <span class="number">0.0</span>; &nbsp;&nbsp;<span class="comment">//----------------------------------------------------------------------------</span> &nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">int</span> test = <span class="number">0</span>; test &lt; NumberRepetTest_P; test++) &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;InitAO (funcCount * <span class="number">2</span>, f.GetMaxArg (), f.GetMinArg (), ArgumentStep_P); &nbsp;&nbsp;&nbsp;&nbsp;EpochCmidl = Measur1FuncValue_P / (<span class="functions">ArraySize</span> (AO.S_Colony)); &nbsp;&nbsp;&nbsp;&nbsp;EpochCount = Measur2FuncValue_P / (<span class="functions">ArraySize</span> (AO.S_Colony)); &nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// Optimization-------------------------------------------------------------</span> &nbsp;&nbsp;&nbsp;&nbsp;AO.F_EpochReset (); &nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span> (<span class="keyword">int</span> epochCNT = <span class="number">1</span>; epochCNT &lt;= EpochCount &amp;&amp; !<span class="functions">IsStopped</span> (); epochCNT++) &nbsp;&nbs

把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到算法收敛曲线和基台评估结果,你只需判断要不要换测试函数。

常见问题

通常涵盖收敛精度、收敛率、稳定性与可扩展性,复杂准则会叠加多峰函数以压力测试算法逃逸局部极值的能力。
可以,小布盯盘内置了 AIGC 诊断模块,能读取品种页的优化日志并标记收敛异常区间,省去手工翻测试报告的步骤。
RNG 引入随机性会扰动收敛轨迹,单独测能区分是算法缺陷还是随机数质量导致的早熟,避免误判策略上限。
维度升高时搜索效率可能崩坏,参数组合一多就跑不出有效极值,实盘信号延迟或钝化,贵金属波段交易尤易中招。