开发多币种 EA 交易(第 6 部分):自动选择实例组·综合运用
(3/3)·手工筛2到5万组参数凑10到20个好实例,三级分组已是体力极限,本篇用最简代码破局
用遗传算法跑出更优参数组
把交易策略参数集写成 CSV 丢给 EA,启动优化时用遗传算法、以「利润除以 10% 回撤归一化」为用户标准。测试期锁 2018–2022(含 2022),本地网络开 13 个测试代理,标准遗传优化跑满 1 万次以上耗时约 9 小时。 结果出乎意料地压过基线:自动遴选最优组利润 24,800 美元,人工组仅 15,200 美元,多出一倍半有余,且全程零人工干预。外汇/贵金属策略回测优异不代表实盘概率同向,杠杆品种回撤可能超预期。 细看优化表头部会发现前五组结果几乎一致,只差一两枚次要参数。根因是原 CSV 里本就有多组同结果、异次要参数的集合;优化器把它们拆成两组后仍算出相同结果,变相把「相同组合」重复算了一次,还削弱了本该靠多样性压回撤的效果。 可顺手做一处改动:把原文件中结果完全相同的参数集合并成一组再喂给优化器,省掉重复 pass,也让组别差异更干净。MT5 里直接改 CSV 再跑一轮就能对比代理耗时与顶部利润分布。
◍ 用 k-means 把参数集拆成互斥集群
把原始 CSV 里所有策略参数集按结果相似性分组,能直接砍掉冗余组合。这里取 signalPeriod_、signalDeviation_、signaAddlDeviation_、openDistance_、stopLevel_、takeLevel_ 六列做输入,用现成 k-means 聚成 64 个群集,文件随后多出一列 cluster 编号。 下面这段 Python 是实际跑聚类的核心:读表、拟合、写回。df.iloc[:, [12,13,14,15,17]] 对应上面六列在文件里的位置,n_clusters=64 即群集数,random_state=42 固定随机种子保证可复现。 import pandas as pd from sklearn.cluster import KMeans df = pd.read_csv('Params_SV_EURGBP_H1.csv') kmeans = KMeans(n_clusters=64, n_init='auto', random_state=42).fit(df.iloc[:, [12,13,14,15,17]]) df['cluster'] = kmeans.labels_ df.to_csv('Params_SV_EURGBP_H1-with_cluster.csv', index=False) EA 侧要新建 OptGroupClusterExpert.mq5。初始化时把每个参数集对应的群集号塞进 setClusters 集合,只有 setClusters.Count() == count_(所有群集互不相同)才允许启动这组运行;同时参数字符串末尾带了无关群集号,得在传给策略构造函数前用 CSVStringGet 截掉(取 0~11 列)。 这种排布有明显短板:遗传算法初始种群里若有两个以上相同参数集指数的个体,种群会快速退化、优化提前终止。再跑一次可能就顺利到终点,找到还不错的参数。 实测上,混用输入参数集或压低组内策略数量,能抬升避免退化的概率。不论怎么调,相对不聚类的优化,整体耗时降到原来的 1/1.5 到 1/2 之间。外汇与贵金属品种波动剧烈,此类优化仅降低计算成本,不预示任何盈利概率。
class="kw">import pandas as pd from sklearn.cluster class="kw">import KMeans df = pd.read_csv(&class="macro">#x27;Params_SV_EURGBP_H1.csv&class="macro">#x27;) kmeans = KMeans(n_clusters=class="num">64, n_init=&class="macro">#x27;auto&class="macro">#x27;, random_state=class="num">42).fit(df.iloc[:, [class="num">12,class="num">13,class="num">14,class="num">15,class="num">17]]) df[&class="macro">#x27;cluster&class="macro">#x27;] = kmeans.labels_ df.to_csv(&class="macro">#x27;Params_SV_EURGBP_H1-with_cluster.csv&class="macro">#x27;, index=False) class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { ... class=class="str">"cmt">// Multiplicities for parameter and cluster set indices CHashSet<class="type">class="kw">string> setIndexes; CHashSet<class="type">class="kw">string> setClusters; class=class="str">"cmt">// Add all indices and clusters to the multiplicities FOREACH(indexes, { setIndexes.Add(indexes[i]); class="type">class="kw">string cluster = CSVStringGet(params[StringToInteger(indexes[i])], class="num">11, class="num">12); setClusters.Add(cluster); }); class=class="str">"cmt">// Report an error if if(count_ < class="num">1 || count_ > class="num">8 class=class="str">"cmt">// number of instances not in the range class="num">1 .. class="num">8 || setIndexes.Count() != count_ class=class="str">"cmt">// not all indexes are unique || setClusters.Count() != count_ class=class="str">"cmt">// not all clusters are unique ) { class="kw">return INIT_PARAMETERS_INCORRECT; } ... FOREACH(indexes, { class=class="str">"cmt">// Remove the cluster number from the parameter set class="type">class="kw">string class="type">class="kw">string param = CSVStringGet(params[StringToInteger(indexes[i])], class="num">0, class="num">11); class=class="str">"cmt">// Add a strategy with a set of parameters with a given index APPEND(strategies, new CSimpleVolumesStrategy(param)) }); class=class="str">"cmt">// Form and add a group of strategies to the EA expert.Add(CVirtualStrategyGroup(strategies, scale_)); class="kw">return(INIT_SUCCEEDED); }
「只留每簇一个样本怎么玩」
另一种抑制种群退化的思路是:对每个聚类只保留一个代表集合,再喂给优化器。上面那段 Python 用 KMeans 把参数空间切成 64 簇,每簇按夏普比排个序取最后一个,导出的 CSV 就能直接丢进前面写好的任一个 EA 里跑。 若发现筛完剩的样本太少,可以加大簇数,或从一个簇里多抽几个集合。按 64 组自动筛选的优化结果和前两种办法大体接近,其中一组的成绩甚至超过了之前所有组——但这更像是运气,而非上限设定更优。 最佳小组的单次通过成绩表里能看出,各组高度重复,区别仅在于策略参数集的索引排列顺序不同。要在 EA 里硬控「索引必须递增」能去重,可种群退化太快,遗传优化基本跑不动;哪怕从 64 个集合里挑 8 个做全枚举,通过次数也爆炸,得改掉把 EA 迭代输入转成索引的方式,不过那是后话。 实盘前注意:外汇与贵金属杠杆高、回撤可能超预期。用单簇样本优化时,头几分钟就能捞出和人工筛选相近的结果(利润约 15,000 美元量级),但真要冲最佳,得熬到优化快结束。
class="kw">import pandas as pd from sklearn.cluster class="kw">import KMeans df = pd.read_csv(&class="macro">#x27;Params_SV_EURGBP_H1.csv&class="macro">#x27;) kmeans = KMeans(n_clusters=class="num">64, n_init=&class="macro">#x27;auto&class="macro">#x27;, random_state=class="num">42).fit(df.iloc[:, [class="num">12,class="num">13,class="num">14,class="num">15,class="num">17]]) df[&class="macro">#x27;cluster&class="macro">#x27;] = kmeans.labels_ df = df.sort_values([&class="macro">#x27;cluster&class="macro">#x27;, &class="macro">#x27;Sharpe Ratio&class="macro">#x27;]).groupby(&class="macro">#x27;cluster&class="macro">#x27;).agg(&class="macro">#x27;last&class="macro">#x27;).reset_index() clusters = df.cluster df = df.iloc[:, class="num">1:] df[&class="macro">#x27;cluster&class="macro">#x27;] = clusters df.to_csv(&class="macro">#x27;Params_SV_EURGBP_H1-one_cluster.csv&class="macro">#x27;, index=False
把工具请下神坛
回测数据摆在那:自动选参组相对人工挑参,盈利结果更优,代价是跑优化更耗时间,但人不用盯场,还能靠加测试代理把算力成本压下来。 下一步自然是把筛出的优组直接交给 EA 自动建组。代码层改动不大,核心只是改读取参数的方式,并支持多策略组并存;更合理的做法是把优化结果统一存进数据库,而非散落成一堆独立文件。 若能在优化测试期之外,单独拉优组做样本外观察,价值会更高——这部分留到后续文章再拆。外汇与贵金属属高风险品种,自动化仅降低人工偏差,不消除回撤可能。