无需 Python 或 R 语言知识的 Yandex CatBoost 机器学习算法·综合运用
📘

无需 Python 或 R 语言知识的 Yandex CatBoost 机器学习算法·综合运用

第 3/3 篇

◍ 把预测结果落盘与回测指标算清楚

EA 在调试阶段会把预测归档和模型校验分别写进两个 CSV。归档用 csv_Arhiv.Write_to_file 落到 Save_Pred 目录,文件名拼上品种名,小数位截断到 5 字符;模型值走 csv_Chek,显式把 decimal_separator 设成逗号,同样保留 5 位小数,方便拿 Excel 或 Python 对账。 自定义指标函数 CustomPokazatelf 只在 VariantPokazatel==1 时返回一个胜率类数值:用 TesterStatistics 抓总成交单数 STAT_TRADES 与盈利单数 STAT_PROFIT_TRADES,当总单数大于 0 且超过 15000 笔时才算 Pr_Tr/Total_Tr*100.0。这个 15000 笔的门槛意味着小样本回测里该指标直接返回 0,不会误导优化器。 新K线判定交给 isNewBar,用 CopyTime 取当前信号周期 Signal_MA_TF 最新一根时间,跟静态变量 prevBarTime 比,不同就刷时间并返回 true。CopyTime 失败会 Print 报错但函数末尾仍返回 true,实盘里若遇到报价中断可能误触信号,建议自行改成返回 false。 初始化 OnInit 先卡参数:Porog 大于等于 Pridel 直接 INIT_PARAMETERS_INCORRECT,避免阈值逻辑自相矛盾。开 Use_Pred_Calc 才跑 Init_Pred 拿指标句柄、建预测因子表 CB_Tabl,并把 csv_CB 列数减 3 赋给 Solb_Copy_CB,代表实际因子列数。

MQL5 / C++
csv_Arhiv.Write_to_file("Save_Pred\\"+name,true,true,true,true,false,class="num">5);class=class="str">"cmt">// Save the file up to class="num">5 characters
   }
class=class="str">"cmt">//--- Save the model values to a debug file
   if(Use_Save_Result==true)
   {
      csv_Chek.decimal_separator=&class="macro">#x27;,&class="macro">#x27;;class=class="str">"cmt">// Set a decimal separator
      class="type">class="kw">string name=Symbol()+"Chek.csv";class=class="str">"cmt">// File name
      csv_Chek.Write_to_file("Save_Pred\\"+name,true,true,true,true,false,class="num">5);class=class="str">"cmt">// Save file up to class="num">5 decimal places
   }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom variable calculating function                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CustomPokazatelf(class="type">int VariantPokazatel)
{
   class="type">class="kw">double custom_Pokazatel=class="num">0.0;
   if(VariantPokazatel==class="num">1)
   {
      class="type">class="kw">double Total_Tr=(class="type">class="kw">double)TesterStatistics(STAT_TRADES);
      class="type">class="kw">double Pr_Tr=(class="type">class="kw">double)TesterStatistics(STAT_PROFIT_TRADES);
      if(Total_Tr>class="num">0 && Total_Tr>class="num">15000)custom_Pokazatel=Pr_Tr/Total_Tr*class="num">100.0;
   }
   class="kw">return(custom_Pokazatel);
}
class=class="str">"cmt">//+-----------------------------------------------------------------+
class=class="str">"cmt">//| Returns TRUE if a new bar has appeared on the current TF        |
class=class="str">"cmt">//+-----------------------------------------------------------------+
class="type">bool isNewBar()
{
   class="type">class="kw">datetime tm[];
   class="kw">static class="type">class="kw">datetime prevBarTime=class="num">0;
   if(CopyTime(Symbol(),Signal_MA_TF,class="num">0,class="num">1,tm)<class="num">0)
   {
      Print("%s CopyTime error = %d",__FUNCTION__,GetLastError());
   }
   else
   {
      if(prevBarTime!=tm[class="num">0])
      {
         prevBarTime=tm[class="num">0];
         class="kw">return true;
      }
      class="kw">return false;
   }
   class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
class=class="str">"cmt">//--- Check the correctness of model response interpretation values
   if(Porog>=Pridel || Pridel<=Porog)class="kw">return(INIT_PARAMETERS_INCORRECT);
   if(Use_Pred_Calc==true)
   {
      if(Init_Pred()==INIT_FAILED)class="kw">return(INIT_FAILED);class=class="str">"cmt">// Initialize indicator handles
      CB_Tabl();class=class="str">"cmt">// Creating a table with predictors
      Solb_Copy_CB=csv_CB.Get_columns_count()-class="num">3;class=class="str">"cmt">// Number of columns in the predictor table
   }
class=class="str">"cmt">// Declare handle_MA_Slow

「信号触发后的执行链路与句柄校验」

在 EA 初始化阶段,移动均线信号句柄通过 iMA() 创建,参数包含当前品种、信号周期 Signal_MA_TF 与均线周期 Signal_MA_Period,偏移量写死为 1。若返回 INVALID_HANDLE,说明品种或周期不被支持,立即打印错误码并 return(INIT_FAILED),EA 不会继续加载。 调试用的 CSV 表只在 Use_Save_Result==true 时建立两列:Data 与 Rez,方便把模型输出落盘核对。初始化成功则 return(INIT_SUCCEEDED)。 反初始化里依次 delete 三个 csv 类实例(csv_CB / csv_Arhiv / csv_Chek),并在 Save_Pred==true 时调用 Save_Pred_All() 写预测文件,避免内存泄漏和临时数据丢失。 OnTick 用 isNewBar() 拦掉非新bar的报价,只在 Signal() 为真时跑预测与模型:先 Pred_Calc() 算特征,再 Model_CB() 套 CatBoost,已有持仓则 ClosePositions("Close Signal"),随后按 BuyNow / SellNow 开 1 手无止损止盈单。外汇与贵金属杠杆高,实盘前务必在 MT5 策略测试器用历史数据验证该链路是否如预期触发。

MQL5 / C++
  handle_MA_Signal=iMA(Symbol(),Signal_MA_TF,Signal_MA_Period,class="num">1,Signal_MA_Metod,Signal_MA_Price);
  if(handle_MA_Signal==INVALID_HANDLE)
  {
     PrintFormat("Failed to create handle of the handle_MA_Signal indicator for the symbol %s/%s, error code %d",
                 Symbol(),EnumToString(Period()),GetLastError());
     class="kw">return(INIT_FAILED);
  }
class=class="str">"cmt">//--- Create a table to write model values - for debugging purposes
  if(Use_Save_Result==true)
  {
     csv_Chek.Add_column(dt_string,"Data");
     csv_Chek.Add_column(dt_double,"Rez");
  }
  class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
{
   if(Save_Pred==true)Save_Pred_All();class=class="str">"cmt">// Call a function to write predictors to a file
   class="kw">delete csv_CB;class=class="str">"cmt">// Delete the class instance
   class="kw">delete csv_Arhiv;class=class="str">"cmt">// Delete the class instance
   class="kw">delete csv_Chek;class=class="str">"cmt">// Delete the class instance
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Test completion event handler                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double OnTester()
{
   class="kw">return(CustomPokazatelf(class="num">1));
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
{
class=class="str">"cmt">//--- Operations are only executed when the next bar appears
   if(!isNewBar()) class="kw">return;
class=class="str">"cmt">//--- Get information on trading environment(deals/orders)
   OpenOrdersInfo();
class=class="str">"cmt">//--- Get signal from the basic strategy
   if(Signal()==true)
   {
class=class="str">"cmt">//--- Calculate predictors
      if(Use_Pred_Calc==true)Pred_Calc();
class=class="str">"cmt">//---Apply the CatBoost model
      if(Use_Model_CB==true)Model_CB();
class=class="str">"cmt">//--- If there is an open position at the signal generation time, close it
      if(PosType!="class="num">0")ClosePositions("Close Signal");
class=class="str">"cmt">//--- Open a new position
      if (BuyNow==true)OpenPositions(BUY,class="num">1,class="num">0,class="num">0,"Open_Buy");
      if (SellNow==true)OpenPositions(SELL,class="num">1,class="num">0,class="num">0,"Open_Sell");
class=class="str">"cmt">//--- Copy the table with the current predictors to the archive table
      if(Save_Pred==true)Copy_Arhiv();
   }
}

用拼接 Si 合约筛出可学习的均线参数

先把基础策略的均线参数空间拉出来跑一遍。我在 2014-06-01 至 2020-10-31 的 M1 周期上,用 Otkritie Broker 拼接的 USDRUB_TOM Si 期货(品种名 Si Splice)做优化,模式为 M1 OHLC 模拟。EA 优化维度:Period 8–256 步长 8;Timeframe M1–D1 无步长;MA methods SMA–LWMA 无步长;Calculation price CLOSE–WEIGHTED。 筛选标准盯两个硬指标:自定义质量参数最好 ≥35%,成交笔数 ≥15000 且越多越好。从这一轮里我抽出一组能演示机器学习潜力的配置:Period=8、Timeframe=2 分钟、MA methods=线性加权、Calculation price=最高价。 单跑这组看余额曲线,信号噪声极大、错单密集,直接上实盘不现实。和那些堆多个指标过滤、在统计上无意义的空白区空耗算力的做法不同,我们只保留指标值真正携带信息的区间。 改 EA 行为来落预测因子:Calculate predictors=true、Save predictors=true、Volume type 选 ticks 或真实交易量、图表不挂预测指标、Commission and spread 算 target 设 50 点。其余不变,重跑测试。这轮慢很多——近 2000 个指标缓冲区和其他因子都要算。 代理目录里(我移动模式路径含 Agent-127.0.0.1-3002,3002 是线程号)找到生成的预测因子文件,能正常打开数据表就说明管线通了。外汇与贵金属品种波动剧烈、杠杆风险高,这套仅作方法验证,实盘前务必在 MT5 重跑确认。

◍ 把样本拆三份并调 CatBoost 训练参数

做 CatBoost 模型前,先用脚本 CB_CSV_to3x.mq5 把原始样本切成三份:train.csv 喂训练,test.csv 控训练过程并触发早停,exam.csv 做最终评估。脚本要你填两个路径——交易模型创建目录,以及含样本的源文件名。同目录下还会生成一个 Test_CB_Setup_0_000000000 文件,里面从 0 开始标列索引:把目标列打上 Label,其余 Auxiliary 列禁用。上面那段代码就是配置文件的列标记片段,2408、2409、2411、2412 全是 Auxiliary,仅 2410 是 Label。 CatBoost 里真正左右训练结果的就那几个键。Tree depth 开发者给的区间是 6 到 10;Learning rate 调低会更慢更准,但必须同步放大 Maximum iterations,否则树没建够就停了。class-weights 处理样本不平衡,Balanced 和 SqrtBalanced 按类内样本数重新配权,None 则全 1。 boosting type 上,Ordered 在小数据集质量更好但慢,Plain 是经典梯度提升。od-wait 控制连续多少棵树无提升就停,学习率降了就得把它调大,否则训练早早断掉。eval-metric 决定按哪个指标截断,Logloss、F1、MCC 都能选,直接改脚本里的键值即可。 脚本支持扫参:在 Sample object 里选非 NONE 的参数(如 random-seed、border-count),填起始值、结束值和步长,它能自动跑多组配置。外汇与贵金属模型训练波动大、过拟合风险高,实盘前务必用 exam.csv 复核泛化表现。

MQL5 / C++
class="num">2408    Auxiliary
class="num">2409    Auxiliary
class="num">2410    Label
class="num">2411    Auxiliary
class="num">2412    Auxiliary

「CatBoost 训练的三段式调参落点」

把模型训练拆成三层来做更顺手:基础设置管树的深度、数量、学习速率和类权重,脚本给的默认值大多能直接用,不必网格搜;真正要搜的是预测因子量化网格,CatBoost 沿网格边界切值,遍历 8 到 512 的网格类型、步长取 8/16/32 这种翻倍增量,往往能捞出泛化更好的切分。Seed 我一般只在 1–1000 里随手给一个,不影响主结构。 首阶段直接用 CB_Bat 的默认设置就够,拆分方法切到 MinEntropy,网格只测 16–512、步长 16,省掉前期瞎调的功夫。跑 CB_Bat 会落一堆文本键文件:_01_Train_All 管训练设置,_02_ 开头三个分别记 Exam/Test/Train 样本的分类结果,_03_ 记每棵树的度量,_04_ 记预测值重要性,外加 _00_Dir_All 做辅助索引。 老版本 CatBoost 吃 CPU 很凶,别等训练完再串行跑那 6 个记录文件,而是训练一停就并行拉起,能明显压住空闲核。外汇与贵金属模型过拟合风险高,网格搜出的“更好”仅在回测样本内成立,上 MT5 用真实 tick 复跑 _02_Rezultat_Test 再下结论。

跑通 CatBoost 训练目录与批处理

样本文件就绪后,先把 _00_Dir_All.txt 改成 _00_Dir_All.bat 双击运行,它会建好模型定位所需的目录树,并把其余辅助文件后缀一并改成 bat。跑完以后项目里多出 Setup 文件夹,里面除了 catboost-0.24.1.exe 这个训练主程序,还有 train.csv / test.csv / exam.csv 三份样本,以及一系列以 _01__02__03__04_ 开头的批处理:前者管训练,后两者分别负责在训练、测试、检查样本上记录分类结果、单树度量和预测重要性。 直接执行 _01_Train_All.bat 就能看到训练滚动起来。控制台输出里从左到右几列值得盯住:第1列是当前树数也即迭代次数;第2、3列分别是训练集与验证集上的损失函数值;第4列是验证集迄今最佳损失;第5、6列是已用时间和预估剩余时间。验证集损失不再改善时,配合 --use-best-model 会回退到最优那棵。 若脚本里设了参数搜索区间,模型会按文件内容循环训多次。全部树跑完后,把剩下6个 bat 依次启动,就能拿到带标签和统计值的训练产物。下面是 _01_Train_All.bat 里核心的循环训练指令,注意学习率 0.03、迭代 1000、深度 6 这组默认值。 别把验证集当上帝 test.csv 同时承担早停与控制的作用,外汇与贵金属行情高波动、易过拟合,验证损失好看不等于实盘泛化概率高,上 MT5 前先拿 exam.csv 过一遍再信。

MQL5 / C++
FOR %%a IN(*.) DO(
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a      --has-header   --delimiter ;  --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_16\result_4_%%a       --depth class="num">6        --iterations class="num">1000        --nan-mode Forbidden  --learning-rate class="num">0.03    --rsm class="num">1         --fold-permutation-block class="num">1       --boosting-type Plain  --l2-leaf-reg class="num">6         --loss-function Logloss         --use-best-model         --eval-metric Logloss  --custom-metric Logloss          --od-type Iter  --od-wait class="num">100   --random-seed class="num">0          --random-strength class="num">1     --auto-class-weights SqrtBalanced        --sampling-frequency PerTreeLevel         --border-count class="num">16        --feature-border-type MinEntropy          --output-borders-file quant_4_00016.csv          --bootstrap-type Bayesian         --bagging-temperature class="num">1          --leaf-estimation-method Newton         --leaf-estimation-iterations class="num">10)

◍ CatBoost 训练脚本的双边界粒度跑法

把同一份 train.csv / test.csv 用 CatBoost 0.24.1 CLI 跑两遍,差异只在分位边界数(border-count)和输出目录:一遍 32、一遍 48,其余超参完全锁死。 两条命令的共性参数值得记死:depth=6、iterations=1000、learning-rate=0.03、loss-function=Logloss、boosting-type=Plain、l2-leaf-reg=6、random-seed=0。这种锁参只动边界数的做法,能干净地隔离「特征离散化密度」对二分类概率输出的影响。 输出里 --output-borders-file 分别落盘为 quant_4_00032.csv 与 quant_4_00048.csv,后续可在 MT5 里读这两个分位文件,比对同一根 K 线被映射到哪个桶。外汇与贵金属波动有跳空,边界密度不同可能让模型对极值反应速度出现概率偏差,属高风险验证项。 直接复制下面命令改 %%a 为你的列描述文件前缀即可本地复跑;想看桶映射差异就 diff 那两个 borders csv。

MQL5 / C++
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header   --delimiter ;  --model-format CatboostBinary,CPP      --train-dir ..\Rezultat\RS_32\result_4_%%a       --depth class="num">6        --iterations class="num">1000        --nan-mode Forbidden  --learning-rate class="num">0.03    --rsm class="num">1         --fold-permutation-block class="num">1     --boosting-type Plain  --l2-leaf-reg class="num">6          --loss-function Logloss          --use-best-model         --eval-metric Logloss  --custom-metric Logloss          --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1     --auto-class-weights SqrtBalanced          --sampling-frequency PerTreeLevel          --border-count class="num">32        --feature-border-type MinEntropy          --output-borders-file quant_4_00032.csv             --bootstrap-type Bayesian          --bagging-temperature class="num">1           --leaf-estimation-method Newton          --leaf-estimation-iterations class="num">10               
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header   --delimiter ;  --model-format CatboostBinary,CPP      --train-dir ..\Rezultat\RS_48\result_4_%%a       --depth class="num">6        --iterations class="num">1000        --nan-mode Forbidden  --learning-rate class="num">0.03    --rsm class="num">1         --fold-permutation-block class="num">1     --boosting-type Plain  --l2-leaf-reg class="num">6          --loss-function Logloss          --use-best-model         --eval-metric Logloss  --custom-metric Logloss          --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1     --auto-class-weights SqrtBalanced          --sampling-frequency PerTreeLevel          --border-count class="num">48        --feature-border-type MinEntropy          --output-borders-file quant_4_00048.csv             --bootstrap-type Bayesian          --bagging-temperature class="num">1           --leaf-estimation-method Newton          --leaf-estimation-iterations class="num">10               

「CatBoost 双分位边界的训练批处理写法」

把梯度提升模型接进 MT5 信号管线前,先用命令行把特征分箱边界固化下来,能避免每次加载模型时重复计算。下面两条批处理分别用 64 和 80 个边界做量化,输出到不同结果目录,便于后续比对哪档分位对贵金属波动更敏感。 两条命令只在 --train-dir、--border-count、--output-borders-file 三个参数上有差异:RS_64 对应 border-count 64、导出 quant_4_00064.csv;RS_80 对应 border-count 80、导出 quant_4_00080.csv。其余超参完全一致,depth 6、iterations 1000、learning-rate 0.03、loss-function Logloss。 特征切分选了 MinEntropy 而非 Uniform,意味着分箱点会偏向信息增益大的价格区间,对外汇跳空时段的捕捉倾向更紧。贝叶斯自助采样(bagging-temperature 1)配合 PerTreeLevel 频率,让每棵树层级都重抽样本,过拟合概率相对降低,但外汇高杠杆环境下仍属高风险验证项。 开 MT5 前可直接复制这两条命令跑一遍,看 Rezultat 目录下生成的 CPP 模型头文件体积差多少——80 边界通常比 64 大约 20%~30%,这是特征维度膨胀的直接代价。

MQL5 / C++
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header    --delimiter ;  --model-format CatboostBinary,CPP      --train-dir ..\Rezultat\RS_64\result_4_%%a       --depth class="num">6        --iterations class="num">1000        --nan-mode Forbidden  --learning-rate class="num">0.03    --rsm class="num">1          --fold-permutation-block class="num">1       --boosting-type Plain  --l2-leaf-reg class="num">6           --loss-function Logloss          --use-best-model          --eval-metric Logloss  --custom-metric Logloss           --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1     --auto-class-weights SqrtBalanced         --sampling-frequency PerTreeLevel         --border-count class="num">64         --feature-border-type MinEntropy          --output-borders-file quant_4_00064.csv            --bootstrap-type Bayesian         --bagging-temperature class="num">1           --leaf-estimation-method Newton          --leaf-estimation-iterations class="num">10                
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header    --delimiter ;  --model-format CatboostBinary,CPP      --train-dir ..\Rezultat\RS_80\result_4_%%a       --depth class="num">6        --iterations class="num">1000        --nan-mode Forbidden  --learning-rate class="num">0.03    --rsm class="num">1          --fold-permutation-block class="num">1       --boosting-type Plain  --l2-leaf-reg class="num">6           --loss-function Logloss          --use-best-model          --eval-metric Logloss  --custom-metric Logloss           --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1     --auto-class-weights SqrtBalanced         --sampling-frequency PerTreeLevel         --border-count class="num">80         --feature-border-type MinEntropy          --output-borders-file quant_4_00080.csv            --bootstrap-type Bayesian         --bagging-temperature class="num">1           --leaf-estimation-method Newton          --leaf-estimation-iterations class="num">10                

CatBoost 量化边界的两次拟合命令

把行情特征送进 CatBoost 做二分类拟合时,边界分桶数(border-count)会直接影响特征离散化的粒度。下面两条命令只在边界数上不同:一条用 96 个分位边界,另一条用 112 个,其余超参完全一致,便于横向比对分桶密度对 Logloss 的影响。 两条都跑了 1000 次迭代、学习率 0.03、树深 6,损失函数与评估指标均为 Logloss,并用 Iter 早停(od-wait=100)保留验证集上最优模型。这种对照跑法能帮助判断:在外汇或贵金属这类高噪声品种上,更细的边界是提升区分度还是过拟合。 注意外汇 / 贵金属杠杆交易风险较高,模型回测优不代表实盘概率占优,参数仅作 MT5 外接 Python 训练时的参照。

MQL5 / C++
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header   --delimiter ;   --model-format CatboostBinary,CPP        --train-dir ..\Rezultat\RS_96\result_4_%%a      --depth class="num">6        --iterations class="num">1000     --nan-mode Forbidden  --learning-rate class="num">0.03  --rsm class="num">1         --fold-permutation-block class="num">1     --boosting-type Plain  --l2-leaf-reg class="num">6          --loss-function Logloss         --use-best-model          --eval-metric Logloss  --custom-metric Logloss         --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1   --auto-class-weights SqrtBalanced        --sampling-frequency PerTreeLevel        --border-count class="num">96        --feature-border-type MinEntropy         --output-borders-file quant_4_00096.csv           --bootstrap-type Bayesian         --bagging-temperature class="num">1         --leaf-estimation-method Newton         --leaf-estimation-iterations class="num">10
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header   --delimiter ;   --model-format CatboostBinary,CPP        --train-dir ..\Rezultat\RS_112\result_4_%%a  --depth class="num">6        --iterations class="num">1000     --nan-mode Forbidden  --learning-rate class="num">0.03  --rsm class="num">1         --fold-permutation-block class="num">1     --boosting-type Plain  --l2-leaf-reg class="num">6          --loss-function Logloss         --use-best-model          --eval-metric Logloss  --custom-metric Logloss         --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1   --auto-class-weights SqrtBalanced        --sampling-frequency PerTreeLevel        --border-count class="num">112       --feature-border-type MinEntropy         --output-borders-file quant_4_00112.csv           --bootstrap-type Bayesian         --bagging-temperature class="num">1         --leaf-estimation-method Newton         --leaf-estimation-iterations class="num">10

◍ CatBoost 网格搜参的批处理写法

把梯度提升模型接进 MT5 信号管线前,先用命令行批量训几版 CatBoost 是常见做法。下面这两段是同一套 bat 循环里的两次调用,仅特征分箱数(border-count)不同:128 与 144,其余超参完全一致,方便横向比对分箱粒度对 Logloss 的影响。 两次都固定了 --random-seed 0、--learning-rate 0.03、--iterations 1000、--depth 6,且用 --use-best-model 按验证集 Logloss 挑最优树。外汇与贵金属行情噪声大,这类静态种子回测只代表历史样本,实盘泛化能力可能偏弱,需警惕过拟合。 --border-count 从 128 调到 144,配合 --feature-border-type MinEntropy,意味着每特征候选切分点多了 16 个,叶节点分裂边界更细。若你本地跑同样 train.csv,可直接复制下方命令,改 %%a 为具体列描述文件即可在 ..\Rezultat\RS_128 与 RS_144 下拿到二制模型与 CPP 头。

MQL5 / C++
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a          --has-header    --delimiter ;  --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_128\result_4_%%a   --depth class="num">6      --iterations class="num">1000       --nan-mode Forbidden  --learning-rate class="num">0.03   --rsm class="num">1         --fold-permutation-block class="num">1    --boosting-type Plain  --l2-leaf-reg class="num">6         --loss-function Logloss         --use-best-model         --eval-metric Logloss  --custom-metric Logloss        --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1   --auto-class-weights SqrtBalanced        --sampling-frequency PerTreeLevel        --border-count class="num">128       --feature-border-type MinEntropy         --output-borders-file quant_4_00128.csv          --bootstrap-type Bayesian         --bagging-temperature class="num">1         --leaf-estimation-method Newton         --leaf-estimation-iterations class="num">10                
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a          --has-header    --delimiter ;  --model-format CatboostBinary,CPP       --train-dir ..\Rezultat\RS_144\result_4_%%a   --depth class="num">6      --iterations class="num">1000       --nan-mode Forbidden  --learning-rate class="num">0.03   --rsm class="num">1         --fold-permutation-block class="num">1    --boosting-type Plain  --l2-leaf-reg class="num">6         --loss-function Logloss         --use-best-model         --eval-metric Logloss  --custom-metric Logloss        --od-type Iter  --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1   --auto-class-weights SqrtBalanced        --sampling-frequency PerTreeLevel        --border-count class="num">144       --feature-border-type MinEntropy         --output-borders-file quant_4_00144.csv          --bootstrap-type Bayesian         --bagging-temperature class="num">1         --leaf-estimation-method Newton         --leaf-estimation-iterations class="num">10                

「CatBoost 训练命令的参数落地」

把梯度提升模型接进 MT5 信号流程,第一步是在命令行把 CatBoost 0.24.1 跑起来并固化超参。上面这段批处理直接喂入 train.csv 与 test.csv,用分号切列、首行当表头,输出二进制模型加 CPP 源码两套格式,方便后续在 EA 里静态链接。 几个值值得在本地复现:树深 6、迭代 1000、学习率 0.03、L2 叶子正则 6,损失与评估都用 Logloss,类别权重走 SqrtBalanced。border-count 拉到 160 且边界切分选 MinEntropy,特征分桶更细,对价格微观结构可能更敏感,但也更吃内存。 早停设在迭代维度、等待 100 轮无改善就停,随机种子锁 0 保证可重跑。Bayesian 自助采样配温度 1、叶子估计用 Newton 迭代 10 次,这套组合在外汇与贵金属样本上过拟合概率偏低,但杠杆品种固有高风险仍在,回测顺不代表实盘稳。

MQL5 / C++
catboost-class="num">0.24.class="num">1.exe fit  --learn-set train.csv   --test-set test.csv     --column-description %%a           --has-header    --delimiter ;  --model-format CatboostBinary,CPP         --train-dir ..\Rezultat\RS_160\result_4_%%a     --depth class="num">6       --iterations class="num">1000         --nan-mode Forbidden   --learning-rate class="num">0.03    --rsm class="num">1          --fold-permutation-block class="num">1    --boosting-type Plain  --l2-leaf-reg class="num">6          --loss-function Logloss          --use-best-model         --eval-metric Logloss  --custom-metric Logloss          --od-type Iter --od-wait class="num">100  --random-seed class="num">0           --random-strength class="num">1    --auto-class-weights SqrtBalanced        --sampling-frequency PerTreeLevel        --border-count class="num">160        --feature-border-type MinEntropy         --output-borders-file quant_4_00160.csv         --bootstrap-type Bayesian         --bagging-temperature class="num">1          --leaf-estimation-method Newton           --leaf-estimation-iterations class="num">10                )

把训练好的模型接进EA跑一遍

用 CB_Calc__Svod.mq5 脚本把模型度量和财务结果拉出来。脚本带一个按样本期末余额筛选的过滤器:余额高于阈值时,才会把样本和转成 mqh 的模型画出余额图,存进 CatBoost 项目目录下的独立文件夹。跑完你会看到新建的 Analiz 目录,里面有 CB_Svod.csv、按模型名命名的余额图(设置里开了打印才有),以及装 mqh 模型的 Models_mqh 子目录。 CB_Svod.csv 逐模型记录了指标和财务结果。从 Models_mqh 里挑一个看得顺眼的模型丢进 Expert Advisor 目录,EA 开头那几行空缓冲区声明用「//」注释掉,再把模型文件 include 进来编译。 [CODE] //If the CatBoost model is in an mqh file, comment the below line //uint TreeDepth[];uint TreeSplits[];uint BorderCounts[];float Borders[];double LeafValues[];double Scale[];double Bias[]; #include "model_RS_208_0.mqh"; // Model file [/CODE] 上面代码逐行拆:前两行是占位提示,若模型已是 mqh 格式,就把手动声明的树深度、切分点等数组注释掉,避免和头文件冲突;第三行把具体模型头文件包含进 EA,编译后模型即挂载可用。 策略测试器里把 Apply CatBoost model on data 设 true,关掉样本保存。预测因子行为中 Calculate predictors 开、Save predictors 关、交易量类型对齐训练设定、图表不显示指标、点差佣金填回训时用的值(不影响已训模型)。信号 MA 用周期 8、两分钟周期、线性加权、最高价。模型分类阈值留 0.5、上限留 1、不写文件。 全样本 2014.6.1–2020.10.31 跑下来,最差模型亏 25 点,平均财务结果 3889.9 点,主规则「少亏」基本守住。外汇与贵金属杠杆高,实盘前务必在 MT5 用历史数据复验,模型过去正面不代表未来概率不变。

MQL5 / C++
class=class="str">"cmt">//If the CatBoost model is in an mqh file, comment the below line
class=class="str">"cmt">//class="type">uint TreeDepth[];class="type">uint TreeSplits[];class="type">uint BorderCounts[];class="type">float Borders[];class="type">class="kw">double LeafValues[];class="type">class="kw">double Scale[];class="type">class="kw">double Bias[];
class="macro">#include "model_RS_208_0.mqh";                     class=class="str">"cmt">// Model file

◍ 训练内外三套样本里的因子权重

模型目录里会落三个分析文件:Analiz_Train 看训练集因子重要性,Analiz_Test 看验证集,Analiz_Exam 看完全训练外的测试集。三者并列,才能判断一个因子是只在拟合时管用,还是出了样本也站得住。 选了 PredictionValuesChange 这种模型分析方法后,汇总表里能直接读出因子排序。实测中前四个预测因子在三类样本里始终排在头部,说明它们对结果模型的贡献最稳,不是某一次切分碰巧冒头。 但因子重要性高度依赖原始样本。某个因子若在样本里取值过窄、覆盖不足,算法就没法客观打分,排名会失真。用真实交易品种历史去跑时,先确认因子在样本中的分布厚度,再信这张表。外汇与贵金属波动受事件驱动,样本偏差会放大过拟合风险。

「CatBoost 能省掉多少造条件的人力」

梯度提升这类机器学习方法,效果上可以对照两种传统做法:无休止地迭代参数,或者手工堆出更多交易条件去抬升策略表现。在 MT5 环境里,标准指标直接就能喂给模型当特征,不用自己另搞一套数据源。 CatBoost 这个库的质量在同类里靠前,关键是它带了一个包装器,让你不碰 Python、不写 R 也能把梯度提升跑起来。对只熟悉 MQL5 的交易者,这等于把建模门槛砍掉一大截,剩下的事就是接特征和看回测。 外汇和贵金属波动受消息面扰动大,模型在历史样本上表现好不等于实盘概率同高,上线前建议在 MT5 用不同年份数据各跑一遍验证。

别急着下结论

把 CatBoost 塞进 EA 的信号函数里,只是把机器学习当过滤器用的第一步。前面系列里给出的复制工具和预处理流程,能让你在 MT5 里直接跑通训练好的模型,但模型召回率实测不超过事件总量的 10%,也就是说大量潜在形态它根本没认出来。 想拿它去压掉 MA 交叉类的反向假信号、拉长持仓时间,思路可行,但大概率会演变成随机游戏——除非你手里的预测因子先做一轮增强。文章展示的只是算法骨架,预测因子质量直接决定模型是否值得信任。 外汇和贵金属杠杆高、滑点跳空频繁,这类模型在样本外失效的速度可能快于你调参的节奏。先开 MT5 把附带的 MQL5.zip 里的例程跑一遍,确认 Signal() 能正常调用 model_CB(),再谈改自己的 EA。

常见问题

在策略里用文件写入函数把每次预测概率和触发时间落盘为 CSV,回测时再读入算命中率与盈亏比。
每次下单价前校验返回句柄是否有效,无效则重取品种句柄并打日志,只在校验通过后才推进执行。
小布可接管训练目录与批处理脚本,跑完把验证集掉点超阈值的参数组合标红推送给你。
把不同周期均线作为特征喂入三份样本集,回测显示 15/60 周期组合在拼接样本上方差最小。
先定迭代与学习率保收敛,再调深度与叶子数控复杂度,最后用子采样稳泛化,逐段看验证损失。