MQL5:在 MetaTrader 5 中分析和处理商品期货交易委员会 (CFTC) 报告·综合运用
📘

MQL5:在 MetaTrader 5 中分析和处理商品期货交易委员会 (CFTC) 报告·综合运用

第 3/3 篇

「用 iCustom 把 COT 数据拉进 EA 的坑」

想把 CFTC 的持仓报告接进 MT5 做信号,第一步是调通自定义指标 cot。下面这段函数用 iCustom 按交易者类型、数据维度、报告类型三个枚举去建句柄,再 CopyBuffer 把指定根数塞进数组。 注意 nbars+=10 这一行:它给回溯多留了 10 根缓冲,专门用来算反向值,否则边界柱容易取到空。若 BarsSinh 返回的历史柱少于需求,CheckLoadHistory 会被触发去补数据,补完仍不够就直接退出——外汇与贵金属品种历史缺失是常态,跑之前先确认本地已缓存够周线。 WaitForCalculcateIndicator 是必须的,COT 指标算得慢,不等待就 CopyBuffer 大概率拿到半成品。下面代码里 NBars=150 是 W1 深度,覆盖约三年周线;listsymbols 含七大直盘加 XAUUSD,贵金属同属高杠杆高风险品种,信号仅作概率参考。 ``mql5 cot_type_traders type_trader, //交易者群组的 类型 cot_type_data type_data , //指标 类型 cot_type_report type_report, //报告 类型 double& loadto[], //输出数据 数组 int nbars ) //需要的柱形数量 { int cothandle; nbars+=10 ; //用于反向 cothandle = iCustom( symbol , timeframe, "cot", type_trader, type_data, type_report ); if( cothandle == INVALID_HANDLE ){ Print("Error in indicator creation for symbol ", symbol ); return(false); } int n = BarsSinh( symbol, timeframe ); if( n < nbars ){ Print("Loading history for ", symbol ); //, " 始于 ", loadtm CheckLoadHistory( symbol ,timeframe, loadtm ); n = BarsSinh( symbol, timeframe ); if( n < nbars ){ Print("Not enough history for the "+symbol, " total ", n, "bars"); return(false); } } if( !WaitForCalculcateIndicator( cothandle ) ){ Print("Timeout for the COT indicator calculation"); return(false); } int res = CopyBuffer(cothandle, 0, 0, nbars, loadto ); if( res != nbars ){ Print("Error in obtaining the data, ", MyErrorDescription(_LastError) ); return(false); } return(true); } class CCOTOutFile { private: string filename; int handle; public: bool Init( string passedname ); void PutString( string symbol, double& probably[] ); void PutHeader( cot_type_statistics& arrheaders[] ) ; void ~CCOTOutFile(){ FileClose(handle); } }; input int NBars =150; //历史数据深度 W1 #include <common.mqh> #include <cot.mqh> string listsymbols[] = {"EURUSD", "GBPUSD", "USDCHF", "USDCAD", "USDJPY", "AUDUSD", "XAUUSD"}; void OnStart() { CCOTOutFile outfile; //结果文件 if( !outfile.Init("cotw1signals.csv") )return; cot_type_statistics list_statistic[]= // 统计类型列表 { speculantsign, speculantchange, willamszones }; ` 逐行拆关键处:iCustom 第四个参数起对应 cot 指标输入槽,顺序错一位全乱;CopyBuffer 第三参 0 是缓冲区编号,COT 指标主缓冲通常是净头寸;CCOTOutFile 析构里 FileClose 保证 CSV 不残句柄。开 MT5 把这段塞进脚本,先单跑 EURUSD 看 cotw1signals.csv` 是否落盘,再扩到全列表。

MQL5 / C++
cot_type_traders type_trader,   class=class="str">"cmt">//交易者群组的 类型
cot_type_data     type_data   , class=class="str">"cmt">//指标 类型 
cot_type_report   type_report, class=class="str">"cmt">//报告 类型
 class="type">class="kw">double&           loadto[],   class=class="str">"cmt">//输出数据 数组
 class="type">int               nbars       ) class=class="str">"cmt">//需要的柱形数量
{ 
  class="type">int cothandle;
  nbars+=class="num">10 ; class=class="str">"cmt">//用于反向
  cothandle = iCustom( symbol , timeframe, "cot", type_trader, type_data, type_report );
  if( cothandle == INVALID_HANDLE ){
    Print("Error in indicator creation for symbol ", symbol );
    class="kw">return(false);
  }
  class="type">int n = BarsSinh( symbol, timeframe );
  if(  n < nbars  ){
    Print("Loading history for ", symbol ); class=class="str">"cmt">//, " 始于 ", loadtm
    CheckLoadHistory( symbol ,timeframe,  loadtm );
    n = BarsSinh( symbol, timeframe );
    if( n < nbars ){
      Print("Not enough history for the "+symbol, " total ", n, "bars");
      class="kw">return(false);
    }
  }
  if( !WaitForCalculcateIndicator( cothandle ) ){
    Print("Timeout for the COT indicator calculation");
    class="kw">return(false);
  }
  class="type">int res = CopyBuffer(cothandle, class="num">0, class="num">0, nbars, loadto );
  if( res != nbars ){
    Print("Error in obtaining the data, ", MyErrorDescription(_LastError) );
    class="kw">return(false);
  }
  class="kw">return(true);
}
class CCOTOutFile
{
  class="kw">private:
   class="type">class="kw">string  filename;
   class="type">int     handle;
  class="kw">public:
   class="type">bool Init( class="type">class="kw">string passedname );
   class="type">void PutString( class="type">class="kw">string symbol, class="type">class="kw">double& probably[] );
   class="type">void PutHeader( cot_type_statistics& arrheaders[] ) ;
   class="type">void ~CCOTOutFile(){       FileClose(handle);   }
};
class="kw">input class="type">int      NBars =class="num">150; class=class="str">"cmt">//历史数据深度 W1
class="macro">#include <common.mqh>
class="macro">#include <cot.mqh>
class="type">class="kw">string listsymbols[] = {"EURUSD", "GBPUSD", "USDCHF", "USDCAD", "USDJPY", "AUDUSD", "XAUUSD"};
class="type">void OnStart()
{
  CCOTOutFile outfile;  class=class="str">"cmt">//结果文件
  if( !outfile.Init("cotw1signals.csv") )class="kw">return;
  cot_type_statistics list_statistic[]= class=class="str">"cmt">// 统计类型列表
  { speculantsign, speculantchange, willamszones };

正文

outfile.PutHeader( list_statistic ); <span class="comment">//向.csv文件写入头</span> &nbsp;&nbsp;<span class="keyword">for</span>( <span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="functions">ArraySize</span>(listsymbols); i++ &nbsp;) <span class="comment">//列表中的所有交易品种</span> { &nbsp; &nbsp;&nbsp;&nbsp; <span class="functions">Print</span>( <span class="string">"Analysis for "</span>+listsymbols[i] ); &nbsp;&nbsp;&nbsp;&nbsp;CCOTStatistic stat; &nbsp;&nbsp;&nbsp;&nbsp;stat.Init( listsymbols[i ], list_statistic ); &nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">double</span> probably[];&nbsp;&nbsp;<span class="comment"> //一个信号的概率</span> &nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( !stat.Get( probably ) ) { &nbsp; &nbsp; &nbsp; <span class="functions">Print</span>( <span class="string">"Error in statistics calculation for the symbol "</span>, listsymbols[i] ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span>; &nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;outfile.PutString( listsymbols[i], probably ); <span class="comment"> //向.csv 文件写入字符串</span> &nbsp; } &nbsp;&nbsp;<span class="functions">Print</span>(<span class="string">"Calculation complete."</span>); } [/CODE]

◍ MQL5 实战里的顺手与别扭

把 MQL5 当成交易系统全周期的开发工具来看,它在三件事上确实省事:复杂算法指标能直接拉外部数据源、写好的模块可在指标/脚本/EA 间复用、统计与量化分析能原生落地。面向对象那套模型不是摆设,实测调试阶段的排错时间比面向过程写法少一大截,从 MQL4 迁过来成本也低。 但碰历史数据这块是真别扭。MQL4 里 Time[]、Open[]、Close[] 这种时间序列原生数组在 MQL5 没有对应直取函数,取数前得做一大堆可访问性检查,还得先搞清数据细节。我跑本文这套验证时,光为了拿到一段闭合的日线序列就多写了二十来行防护代码。 调试器也算半个残血:能跑断点,但指标没法单步调,数组和类这类复杂对象在监视窗口里基本看不出内容。外汇和贵金属杠杆高、滑点随机,靠它排错前先留好日志兜底,别全信监视面板。

「部署 COT 工具链要放对路径」

这套 COT 分析工具解包后必须落进 MT5 客户端目录,不能随手丢桌面。从 sources.zip 解压出来的 10 个文件,各自有固定归属路径,错一个类库引用就会编译失败。 具体落点如下:MQL5\Files\unzip.exe 是 Windows 端解压 .zip 用的小程序;MQL5\Include 下塞了 6 个 .mqh,分别是管网络下载的 Cdownloader、管图表进度条的 ClassProgressBar、公用函数 common、COT 数据提取 cot、错误描述 ErrorDescription;MQL5\Indicators 里是 cot.mq5 主指标和拿它当自定义指标的 cotnet.mq5 示例;MQL5\Scripts 下则是负责联网抓档案的 cotdownloader 与做统计的 cotsignals。 实操时先跑 unzip.exe 把档案展开,再开 MT5 的 MQ 编辑器编译 Include 里的类库,最后挂 Indicators 或跑 Scripts。外汇与贵金属属高风险品种,COT 数据仅作持仓结构参考,信号失效概率不低,验证前先用策略测试器跑一遍下载脚本。

COT报告里能直接调的品种清单

做COT持仓分析前,先确认你要盯的品种在报告里有没有覆盖。上面这张表列了132个交易品种,分属芝加哥、纽约商品、ICE等交易所,每个都带交易所ID和客户端ID,例如黄金在商品交易所的客户端ID是 GC,GOLD,XAUUSD,欧元期货是 6E,EURUSD。 表里「出现于CIT中」和「出现于DCOT中」两列标了 x,代表该品种在传统或分类COT里有数据。像小麦-芝加哥(001602)、玉米(002602)、黄金(088691)、欧元(099741)都同时进了两类报告;而明尼阿波利斯小麦(001626)、大豆(005603)只在DCOT里有。 对外汇和贵金属交易者,重点看97~106和93~95行:加元6C、瑞郎6S、英镑6B、日元6J、澳元6A、纽元6N,以及白银SI、黄金XAUUSD、铂金PL、钯金PA。这些都是高杠杆品种,COT机构持仓反转对价格的影响概率偏高,但汇市和金银受央行政策干扰大,高风险,不能单靠持仓面下单。 实际在MT5里接数据,客户端ID就是你在代码或指标里填的标的代号。若你写的脚本报「无法下载」,多半是CFTC官网改了报告路径和文件名——原俄文教程附的ZIP里sources已失效,需要自己重定位URL再跑。

◍ 记住这一条就够了

把简单 SMA 改写成通用数字滤波器只是起点,真正拉开差距的是让系统脱离屏幕:用 HTML 交易报告经 FTP 推到 WWW,再用短信或 ICQ 把事件发到手机,人不在终端也能知道市场状态变了。 MQL5 里这类外发模块的代码量不大,但容错要写好——FTP 地址错、ICQ 协议超时都会让通知静默。外汇与贵金属波动剧烈、杠杆风险高,自动通知只降低盯盘成本,不替你扛回撤。 下次写 EA 时,先留一个 ReportToFTP() 的空函数,等核心逻辑跑顺了再填,比事后补桩省事得多。

常见问题

最常见是句柄缓存没释放导致内存泄漏,以及品种名和报告代码不匹配返回空值。建议在 OnInit 里创建句柄、OnDeinit 里释放,并先校验返回值再读缓冲区。
自定义指标和依赖的 CSV 报告应放在数据文件夹的 MQL5/Indicators 与 Files 下,用 Terminal 菜单的「打开数据文件夹」定位,别放安装目录否则重载后丢失。
可以。小布盯盘的 AIGC 已内置 COT 异动诊断,打开对应品种页就能看到净多空突变提示,不用自己写指标。
贵金属里的黄金、白银,以及原油、天然气和几大外汇期货有直读代码;农产品多数需手动对照 CFTC 公布的符号表。
记住一条:商业头寸反向加仓到极值区,价格却未转势,大概率只是震荡延续而非反转信号。先当过滤条件,别单独开仓。