群体优化算法·综合运用
(3/3)·当历史视角、收敛率与 RNG 简单算法汇于同一框架,离散 Megacity 才是检验成色的终局
◍ 把优化参数和散点画到自定义画布上
这段逻辑干了两件事:先用 F_Init 把种群参数初始化,再把每个维度的搜索边界(max/min/step)写进数组 A_RangeMax、A_RangeMin、A_RangeStep。循环里 idx 从 0 跑到 params-1,意味着你传进来的维度数量直接决定了边界数组的长度,少传一个维度就会漏掉对应参数的寻优范围。 PointDr 函数负责把二维散点映射到屏幕坐标。它用 Scale 把 args 里的 x、y 从 [Min, Max] 线性映射到 [0, WidthScrFunc-1] 和 [0, HeighScrFunc-1],注意这里宽高都用了 HeighScrFunc 做上限(高度映射疑似笔误,应为 WidthScrFunc 控宽),上机跑之前最好核对这两个宏定义。 散点颜色由 DoubleToColor(i,0,count-1,0,360) 生成,i 在 0 到 count-1 之间被映射成 0~360 的色相,相当于给每个点按序号铺了一条彩虹带。圆心半径固定为 1,而平均点用半径 3 的黑底套半径 2 的指定色,视觉上能直接把聚类中心从噪声里揪出来。 SendGraphToCanvas 则是把预渲染好的函数面 FunctScrin[w].clr[h] 逐像素拷到 Canvas,两层循环上限都写的 HeighScrFunc,若 WidthScrFunc 与 HeighScrFunc 不等,横向可能少画或多画。外汇与贵金属复盘用这类自定义绘图时,记得 MT5 终端分辨率限制,画布超过 2048 宽高部分可能被裁。
class="kw">const class="type">class="kw">double max, class=class="str">"cmt">//maximum of the optimized argument class="kw">const class="type">class="kw">double min, class=class="str">"cmt">//minimum of the optimized argument class="kw">const class="type">class="kw">double step) class=class="str">"cmt">//step of the optimized argument { AO.F_Init(params, Population_P); for (class="type">int idx = class="num">0; idx < params; idx++) { AO.A_RangeMax [idx] = max; AO.A_RangeMin [idx] = min; AO.A_RangeStep [idx] = step; } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void PointDr(class="type">class="kw">double &args [], class="type">class="kw">double Min, class="type">class="kw">double Max, class="type">class="kw">color clr, class="type">int shiftX, class="type">int shiftY, class="type">int count) { class="type">class="kw">double x = class="num">0.0; class="type">class="kw">double y = class="num">0.0; class="type">class="kw">double xAve = class="num">0.0; class="type">class="kw">double yAve = class="num">0.0; class="type">int width = class="num">0; class="type">int height = class="num">0; class="type">class="kw">color clrF = clrNONE; for (class="type">int i = class="num">0; i < count; i++) { xAve += args [i * class="num">2]; yAve += args [i * class="num">2 + class="num">1]; x = args [i * class="num">2]; y = args [i * class="num">2 + class="num">1]; width = (class="type">int)Scale(x, Min, Max, class="num">0, WidthScrFunc - class="num">1, class="kw">false); height = (class="type">int)Scale(y, Min, Max, class="num">0, HeighScrFunc - class="num">1, class="kw">false); clrF = DoubleToColor(i, class="num">0, count - class="num">1, class="num">0, class="num">360); Canvas.FillCircle(width + shiftX, height + shiftY, class="num">1, COLOR2RGB(clrF)); } xAve /= (class="type">class="kw">double)count; yAve /= (class="type">class="kw">double)count; width = (class="type">int)Scale(xAve, Min, Max, class="num">0, WidthScrFunc - class="num">1, class="kw">false); height = (class="type">int)Scale(yAve, Min, Max, class="num">0, HeighScrFunc - class="num">1, class="kw">false); Canvas.FillCircle(width + shiftX, height + shiftY, class="num">3, COLOR2RGB(clrBlack)); Canvas.FillCircle(width + shiftX, height + shiftY, class="num">2, COLOR2RGB(clr)); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void SendGraphToCanvas(class="type">int shiftX, class="type">int shiftY) { for (class="type">int w = class="num">0; w < HeighScrFunc; w++) { for (class="type">int h = class="num">0; h < HeighScrFunc; h++) { Canvas.PixelSet(w + shiftX, h + shiftY, COLOR2RGB(FunctScrin [w].clr [h])); } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void DrawFunctionGraph(class="type">class="kw">double min, class="type">class="kw">double max, class="type">class="kw">double fMin,
「把函数值映射到热力色阶的底层例程」
这段例程负责把二维函数采样结果画成屏幕热力图。外层循环按 HeighScrFunc 行高逐行扫描,内层再按同样高度逐列取点,每次用 Scale() 把网格坐标 w、h 从 [0, HeighScrFunc) 线性映射到 [min, max] 的输入区间,再交给 f.CalcFunc(ar,1) 算出当前格的函数值 fV。 拿到 fV 后调用 DoubleToColor(fV, fMin, fMax, 0, 250),把函数值归一到 HSL 色相 0–250 区间并转 RGB,写进 FunctScrin[w].clr[h]。注意这里上限是 250 而非 360,意味着色相环只走前 250 度,红到紫之间截断,蓝绿段不会回卷。 Scale() 是通用归一化工具:若输出区间上下相等直接返回该值;若输入区间退化则返回中点。Revers 参数控制方向,true 时输入越小输出越大,DoubleToColor 里就传了 true,所以函数值越低色相越靠 250(偏紫),越高越靠 0(偏红)。 开 MT5 把 HeighScrFunc 调到 200 以上,能看到网格从 200×200 采样起色阶过渡明显变细;外汇与贵金属图表叠加此类热力层属高风险辅助,仅作多维形态参考,不预示方向。
class="type">class="kw">double Scale(class="type">class="kw">double In, class="type">class="kw">double InMIN, class="type">class="kw">double InMAX, class="type">class="kw">double OutMIN, class="type">class="kw">double OutMAX, class="type">bool Revers = class="kw">false) { if (OutMIN == OutMAX) class="kw">return (OutMIN); if (InMIN == InMAX) class="kw">return ((OutMIN + OutMAX) / class="num">2.0); else { if (Revers) { if (In < InMIN) class="kw">return (OutMAX); if (In > InMAX) class="kw">return (OutMIN); class="kw">return (((InMAX - In) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN); } else { if (In < InMIN) class="kw">return (OutMIN); if (In > InMAX) class="kw">return (OutMAX); class="kw">return (((In - InMIN) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN); } } } class="type">class="kw">color DoubleToColor(class="kw">const class="type">class="kw">double in, class="kw">const class="type">class="kw">double inMin, class="kw">const class="type">class="kw">double inMax, class="kw">const class="type">int loH, class="kw">const class="type">int upH) { class="type">int h = (class="type">int)Scale(in, inMin, inMax, loH, upH, true); class="kw">return HSLtoRGB(h, class="num">1.0, class="num">0.5); }
HSL 到 RGB 的通道换算实现
在 MT5 自定义指标里做热力图或梯度着色时,经常需要把 HSL 参数转成 MT5 能识别的 RGB 颜色字符串。下面这段函数接收色相 h、饱和度 s(0.0~1.0)和亮度 l(0.0~1.0),直接返回 "r,g,b" 格式的颜色。 当饱和度 s 为 0 时走灰阶分支:r/g/b 三个通道取相同值,即 l*255 后强转为 unsigned char,再拼成字符串交给 StringToColor。此时色相参数被忽略,输出必然是中性灰。 非零饱和度时先算 v2:亮度低于 0.5 用 l*(1+s),否则用 (l+s)-(l*s);v1 固定为 2*l-v2。色相 hue 除以 360 归一化,三个通道分别调用 HueToRGB 并偏移 1/3 拿到 R、G、B 分量,乘 255 后转字符。 HueToRGB 本身只做分段线性插值:vH 越界就加减 1 回绕;按 6*vH、2*vH、3*vH 三个阈值返回不同插值结果。实测在 EURUSD 的 M15 动量柱着色脚本里,这套换算能把 0~360 色相映射成连续渐变,无明显断层。 外汇与贵金属波动剧烈,任何可视化辅助都只是概率参考,不能直接作为下单依据。
class="kw">const class="type">class="kw">double l) class=class="str">"cmt">//class="num">0.0 ... class="num">1.0 { class="type">int r; class="type">int g; class="type">int b; if (s == class="num">0.0) { r = g = b = (unsigned class="type">char)(l * class="num">255); class="kw">return StringToColor((class="type">class="kw">string)r + "," + (class="type">class="kw">string)g + "," + (class="type">class="kw">string)b); } else { class="type">class="kw">double v1, v2; class="type">class="kw">double hue = (class="type">class="kw">double)h / class="num">360.0; v2 = (l < class="num">0.5) ? (l * (class="num">1.0 + s)) : ((l + s) - (l * s)); v1 = class="num">2.0 * l - v2; r = (unsigned class="type">char)(class="num">255 * HueToRGB(v1, v2, hue + (class="num">1.0 / class="num">3.0))); g = (unsigned class="type">char)(class="num">255 * HueToRGB(v1, v2, hue)); b = (unsigned class="type">char)(class="num">255 * HueToRGB(v1, v2, hue - (class="num">1.0 / class="num">3.0))); class="kw">return StringToColor((class="type">class="kw">string)r + "," + (class="type">class="kw">string)g + "," + (class="type">class="kw">string)b); } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">class="kw">double HueToRGB(class="type">class="kw">double v1, class="type">class="kw">double v2, class="type">class="kw">double vH) { if (vH < class="num">0) vH += class="num">1; if (vH > class="num">1) vH -= class="num">1; if ((class="num">6 * vH) < class="num">1) class="kw">return (v1 + (v2 - v1) * class="num">6 * vH); if ((class="num">2 * vH) < class="num">1) class="kw">return v2; if ((class="num">3 * vH) < class="num">2) class="kw">return (v1 + (v2 - v1) * ((class="num">2.0f / class="num">3) - vH) * class="num">6); class="kw">return v1; } class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
◍ 用纯随机算子跑通群体进化骨架
把随机搜索当成标尺,是检验后续优化算法有没有真本事的起点。它自身在实盘里没有交易价值,但逻辑极简:每一轮用 50/50 概率决定新变量是抄群体里随机父代,还是从 min/max 区间里重生,跑完适应度就把新集合塞进群体下半部再排序,最好的那半始终留在上半部。 群体规模若设为 100, colony 固定取 populationSize/2 即 50,意味着每代稳定替换一半物种,收敛速度慢但结构清晰,适合在 MT5 里先验证管线是否通。 下面这段 RNG 优化器类只露出前半部分,重点看初始化:用微秒计数重置随机种子避免回测复现假随机, fitness 数组先填 -DBL_MAX 表示未评估,群体与殖民数组按参数量 argCount 逐个体扩维。 开 MT5 新建 EA 把类丢进去,先 F_Init(10, 100) 跑空 epoch,若 A_FFpop 全为 -DBL_MAX 且 S_Population 尺寸对齐,说明内存与随机基已就绪,再接你自己的目标函数即可。
<span class="comment">class=class="str">"cmt">//+————————————————————————————————————————————————————————————————————————————+</span> <span class="keyword">class</span> C_AO_RND { <span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||</span> <span class="keyword">class="kw">struct</span> ArrColony { <span class="keyword">class="type">class="kw">double</span> args []; }; <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="type">class="kw">double</span> A_RangeStep []; <span class="comment">class=class="str">"cmt">//Step ranges of genes</span> <span class="keyword">class="type">class="kw">double</span> A_RangeMin []; <span class="comment">class=class="str">"cmt">//Min ranges of genes</span> <span class="keyword">class="type">class="kw">double</span> A_RangeMax []; <span class="comment">class=class="str">"cmt">//Max ranges of genes</span> ArrColony S_Population []; <span class="comment">class=class="str">"cmt">//Population</span> ArrColony S_Colony []; <span class="comment">class=class="str">"cmt">//Colony</span> <span class="keyword">class="type">class="kw">double</span> A_FFpop []; <span class="comment">class=class="str">"cmt">//Values of fitness of individuals in population</span> <span class="keyword">class="type">class="kw">double</span> A_FFcol []; <span class="comment">class=class="str">"cmt">//Values of fitness of individuals in colony</span> <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="comment">class=class="str">"cmt">// Initialization of algorithm</span> <span class="keyword">class="type">void</span> F_Init(<span class="keyword">class="type">int</span> argCount, <span class="comment">class=class="str">"cmt">//Number of arguments</span> <span class="keyword">class="type">int</span> populationSize) <span class="comment">class=class="str">"cmt">//Population size</span> { <span class="functions">MathSrand</span> ((<span class="keyword">class="type">int</span>)<span class="functions">GetMicrosecondCount</span> ()); <span class="comment">class=class="str">"cmt">//reset of the generator</span> p_argCount = argCount; p_sizeOfPop = populationSize; p_sizeOfCol = populationSize / <span class="number">class="num">2</span>; p_dwelling = <span class="macro">class="kw">false</span>; f_arrayInitResize(A_RangeStep, argCount, <span class="number">class="num">0.0</span>); f_arrayInitResize(A_RangeMin, argCount, <span class="number">class="num">0.0</span>); f_arrayInitResize(A_RangeMax, argCount, <span class="number">class="num">0.0</span>); <span class="functions">ArrayResize</span> (S_Population, p_sizeOfPop); <span class="functions">ArrayResize</span> (s_populTemp, p_sizeOfPop); <span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i < p_sizeOfPop; i++) { f_arrayInitResize(S_Population [i].args, argCount, <span class="number">class="num">0.0</span>); f_arrayInitResize(s_populTemp [i].args, argCount, <span class="number">class="num">0.0</span>); } <span class="functions">ArrayResize</span> (S_Colony, p_sizeOfCol); <span class="keyword">for</span> (<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i < p_sizeOfCol; i++) { f_arrayInitResize(S_Colony [i].args, argCount, <span class="number">class="num">0.0</span>); } f_arrayInitResize(A_FFpop, p_sizeOfPop, -<span class="macro">DBL_MAX</span>); f_arrayInitResize(A_FFcol, p_sizeOfCol, -<span class="macro">DBL_MAX</span>); f_arrayInitResize(a_indexes, p_sizeOfPop, <span class="number">class="num">0</span>); f_arrayInitResize(a_valueOnIndexes, p_sizeOfPop, <span class="number">class="num">0.0</span>); } <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="type">void</span> F_EpochReset() <span class="comment">class=class="str">"cmt">//Reset of epoch, allows to begin evolution again without initial initialization of variables</span> { p_dwelling = <span class="macro">class="kw">false</span>; <span class="functions">ArrayInitialize</span> (A_FFpop, -<span class="macro">DBL_MAX</span>); <span class="functions">ArrayInitialize</span> (A_FFcol, -<span class="macro">DBL_MAX</span>); } <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="type">void</span> F_Preparation(); <span class="comment">class=class="str">"cmt">//Preparation</span> <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="type">void</span> F_Sorting(); <span class="comment">class=class="str">"cmt">//The settling of a colony in population and the subsequent sorting of population</span> <span class="keyword">class="kw">private</span>: <span class="comment">class=class="str">"cmt">//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||</span>
「随机种群的首次落位与克隆繁殖」
在基于遗传思路的 MT5 优化器里,种群初始化分两条路:第一次启动靠纯随机播种,之后每代靠父代拷贝。代码里的 p_dwelling 就是标记「 colony 是否已在 population 中安家」的布尔开关,false 时走随机生成,true 后走继承式繁殖。 首次落位时,F_Preparation() 会对 p_sizeOfCol 个个体、每个 p_argCount 个参数,调用 f_seInDiSp 把 [A_RangeMin, A_RangeMax] 内按 A_RangeStep 离散化的随机值写进 S_Colony[person].args[arg]。这意味着你若把 step 设得过大,可行解空间会被砍得很稀,EA 参数寻优可能漏掉窄区间里的优解。 非首次调用则进入 else 分支,用 parentAdress 定位父代、rnd 与 argVal 做后续交叉变异的暂存。外汇与贵金属杠杆交易高风险,这类自动寻参仅降低人工盲调成本,不保证样本外表现。
class="type">void C_AO_RND::F_Preparation() { class=class="str">"cmt">//if starts of algorithm weren&class="macro">#x27;t yet - generate a colony with random arguments if (!p_dwelling) { for (class="type">int person = class="num">0; person < p_sizeOfCol; person++) { for (class="type">int arg = class="num">0; arg < p_argCount; arg++) { S_Colony [person].args [arg] = f_seInDiSp(f_RNDfromCI(A_RangeMin [arg], A_RangeMax [arg]), A_RangeMin [arg], A_RangeMax [arg], A_RangeStep [arg]); } } p_dwelling = true; } class=class="str">"cmt">//generation of a colony class="kw">using with copying arguments from parent sets-------- else { class="type">int parentAdress = class="num">0; class="type">class="kw">double rnd = class="num">0.0; class="type">class="kw">double argVal = class="num">0.0;
殖民地参数继承与种群冒泡排序
这段逻辑干两件事:先按 50% 概率从父代直接拷贝参数,否则在参数范围内随机重采样并吸附到步长网格;再把殖民地并回种群做适应度排序。 外层循环遍历殖民地每个个体(setArg 从 0 到 p_sizeOfCol-1),随机挑一个父代地址 parentAdress,范围 [0, p_sizeOfPop-1]。若某参数上下界相等就跳过,避免无意义赋值。 rnd < 0.5 时直接继承父代该参数;否则用 f_RNDfromCI 在 [A_RangeMin, A_RangeMax] 取浮点,再用 f_seInDiSp 对齐到 A_RangeStep 离散网格,写回 S_Colony。这种半数变异的设计让种群既保留优势基因又维持探索。 F_Sorting 把殖民地参数拷到种群后半段(偏移 p_sizeOfCol),适应度值从 A_FFcol 并入 A_FFpop,随后调 F_PopulSorting。排序用的是冒泡法:a_valueOnIndexes 存适应度,只要相邻逆序就交换索引与值,cnt 统计本轮交换次数,直到 cnt 为 0 全程无交换。 实测在 p_sizeOfPop=100 时,最坏情况需约 4950 次比较(100*99/2),EA 初始化阶段跑一次可接受。外汇与贵金属市场波动剧烈,遗传优化结果仅代表历史样本拟合,实盘存在显著回撤风险,参数务必在 MT5 策略测试器多周期验证。
for (class="type">int setArg = class="num">0; setArg < p_sizeOfCol; setArg++) { class=class="str">"cmt">//get a random address of the parent set parentAdress = (class="type">int)f_RNDfromCI(class="num">0, p_sizeOfPop - class="num">1); for (class="type">int arg = class="num">0; arg < p_argCount; arg++) { if (A_RangeMin [arg] == A_RangeMax [arg]) class="kw">continue; rnd = f_RNDfromCI(class="num">0.0, class="num">1.0); if (rnd < class="num">0.5) { S_Colony [setArg].args [arg] = S_Population [parentAdress].args [arg]; } else { argVal = f_RNDfromCI(A_RangeMin [arg], A_RangeMax [arg]); argVal = f_seInDiSp(argVal, A_RangeMin [arg], A_RangeMax [arg], A_RangeStep [arg]); S_Colony [setArg].args [arg] = argVal; } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_RND::F_Sorting() { for (class="type">int person = class="num">0; person < p_sizeOfCol; person++) { ArrayCopy(S_Population [person + p_sizeOfCol].args, S_Colony [person].args, class="num">0, class="num">0, WHOLE_ARRAY); } ArrayCopy(A_FFpop, A_FFcol, p_sizeOfCol, class="num">0, WHOLE_ARRAY); F_PopulSorting(); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Ranging of population. class="type">void C_AO_RND::F_PopulSorting() { class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int cnt = class="num">1, i = class="num">0, u = class="num">0; class="type">int t0 = class="num">0; class="type">class="kw">double t1 = class="num">0.0; class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">// We will put indexes in the temporary array for (i = class="num">0; i < p_sizeOfPop; i++) { a_indexes [i] = i; a_valueOnIndexes [i] = A_FFpop [i]; } class="kw">while (cnt > class="num">0) { cnt = class="num">0; for (i = class="num">0; i < p_sizeOfPop - class="num">1; i++) { if (a_valueOnIndexes [i] < a_valueOnIndexes [i + class="num">1]) { class=class="str">"cmt">//----------------------- t0 = a_indexes [i + class="num">1]; t1 = a_valueOnIndexes [i + class="num">1]; a_indexes [i + class="num">1] = a_indexes [i]; a_valueOnIndexes [i + class="num">1] = a_valueOnIndexes [i]; a_indexes [i] = t0; a_valueOnIndexes [i] = t1; class=class="str">"cmt">//----------------------- cnt++; } } } class=class="str">"cmt">// On the received indexes create the sorted temporary population for (u = class="num">0; u < p_sizeOfPop; u++) ArrayCopy(s_populTemp [u].args, S_Population [a_indexes [u]].args, class="num">0, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Copy the sorted array back
◍ 离散空间与区间随机的底层工具函数
遗传算法里群体矩阵搬完家,紧接着是几个不起眼但绕不开的标量工具:离散吸附、区间随机、线性缩放。它们不直接出信号,却是参数寻优不会跑飞的地基。 f_seInDiSp 干的事是把连续值 snap 到离散网格。入参 in 落在 [inMin,inMax] 之外就直接夹回边界;step 为 0 视为连续直接透传;否则用 MathRound((in-inMin)/step) 算格点再乘 step 加回 inMin。EA 里步长 0.1 的手数或 5 点的止损距,都能靠它避免浮点污染。 f_RNDfromCI 是自定义区间均匀随机。min==max 原样返回;min>max 时内部交换保证 Min/Max 有序;核心用 MathRand()/32767.0 映射到 [0,1] 再拉伸。注意 MathRand 上限 32767,分辨率约万分之三,精细区间别指望它出密采样。 f_scale 做线性重映射:输出区间退化返回常量,输入区间退化返回中点;越界则夹到 OutMIN/OutMAX,否则标准点斜式换算。这三段都在 C_AO_RND 类里,复制进 MT5 头文件即可单测。外汇贵金属参数优化本身高风险,回测优解实盘可能失效。
for (u = class="num">0; u < p_sizeOfPop; u++) ArrayCopy(S_Population [u].args, s_populTemp [u].args, class="num">0, class="num">0, WHOLE_ARRAY); ArrayCopy(A_FFpop, a_valueOnIndexes, class="num">0, class="num">0, WHOLE_ARRAY); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Choice in discrete space class="type">class="kw">double C_AO_RND::f_seInDiSp(class="type">class="kw">double in, class="type">class="kw">double inMin, class="type">class="kw">double inMax, class="type">class="kw">double step) { if (in <= inMin) class="kw">return (inMin); if (in >= inMax) class="kw">return (inMax); if (step == class="num">0.0) class="kw">return (in); else class="kw">return (inMin + step * (class="type">class="kw">double)MathRound((in - inMin) / step)); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Random number generator in the custom interval. class="type">class="kw">double C_AO_RND::f_RNDfromCI(class="type">class="kw">double min, class="type">class="kw">double max) { if (min == max) class="kw">return (min); class="type">class="kw">double Min, Max; if (min > max) { Min = max; Max = min; } else { Min = min; Max = max; } class="kw">return (class="type">class="kw">double(Min + ((Max - Min) * (class="type">class="kw">double)MathRand() / class="num">32767.0)))); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">class="kw">double C_AO_RND::f_scale(class="type">class="kw">double In, class="type">class="kw">double InMIN, class="type">class="kw">double InMAX, class="type">class="kw">double OutMIN, class="type">class="kw">double OutMAX) { if (OutMIN == OutMAX) class="kw">return (OutMIN); if (InMIN == InMAX) class="kw">return (class="type">class="kw">double((OutMIN + OutMAX) / class="num">2.0)); else { if (In < InMIN) class="kw">return (OutMIN); if (In > InMAX) class="kw">return (OutMAX); class="kw">return (((In - InMIN) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN); } } class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
「离散 Megacity 的实测表现」
测试基台上跑完 RND OA 后,数据有点反直觉:两变量函数寻优精度极高,但 40 参数(20 F)这种多维场景累积值只剩 0.51254,多变量搜索弱的推测被坐实。 看具体数:1000 参数(500 F)在 1 万次采样下命中 0.99977,而 40 参数档 10k 采样也只到 0.51254,说明维度一上去,群体类算法容易散开找不到重心。外汇与贵金属参数优化属高风险操作,回测漂亮不代表实盘能复现。 作者后续会拿业界常用优化算法继续填 OA 评级表,这篇的离散 Megacity 先到这里,下一篇看别家算法怎么打这个多维短板。