MQL5:在 MetaTrader 5 中分析和处理商品期货交易委员会 (CFTC) 报告(基础篇)
「在 MT5 里直接读 CFTC 持仓报告」
MT5 终端内置了 CFTC(商品期货交易委员会)持仓报告的调取能力,不需要另开浏览器或导表。对做外汇和贵金属的交易者来说,这等于把机构净持仓变化直接钉在行情上下文里,但外汇和贵金属杠杆高、波动烈,报告只反映仓位倾向,不预示单边行情。 Aleksey Sergan 在 2013-11-27 10:36 发布的示例帖累计被查看 7350 次、收到 21 条评论,说明早期社区对“终端内直接解析监管持仓”的需求就很实。这个入口后来成了很多自编指标拉 COT 数据的底子。 真正有用的是把报告周期和你的图表周期错开看:周度 CFTC 是慢变量,用来判别美元指数或黄金的持仓极端;1 小时图是快变量,用来找入场节奏。两者冲突时,慢变量权重应更高。
◍ 为什么老 MQL4 玩家得重学这套
MetaTrader 5 是 MetaQuotes 开发的终端,内置支持面向对象编程的 MQL5。它和旧版 MQL4 不兼容,但带来了完整面向对象语言的大量新能力,已经熟悉 MQL4 的交易者等于要重新啃一门事实上的新语言。 本文瞄准的痛点很具体:直接拉 CFTC(商品期货交易委员会)报告里的各类数据做获取和分析,而不依赖中间处理。Meta COT 项目早先在 MT4 上做过一轮,假定你懂基础概念,这里只补原文没细说的部分。 核心思路是写一个单一指标,能从委员会发布的文件读数据、无需转换,同时兼顾多种用途——自己画图、当别的指标数据源、进脚本跑自动分析、或在写 EA 时直接调用。外汇与贵金属属高风险品类,任何基于报告数据的信号都只是概率倾向,开 MT5 前先想清楚你的样本周期。
COT 三类报告的真实分野
CFTC 的持仓报告并非只有一种口径。实战中你会碰到旧式报告(COT)、补充商品指数(CIT)和新式分类报告(DCOT)三套,底层分组逻辑差异很大。 旧式报告把市场参与者粗分为非商业、商业、非报告三类;CIT 从 2007 年起单列“指数型交易人员”,这群人既不算套保也不算纯投机,常在合约间滚动建立买入持仓;DCOT 则从 2009 年推出,把交易员拆成生产商/贸易商/加工企业/用户、互换交易商、管理基金、其他报告持仓四组,透明度明显更高。 数据文件引用上,三套报告分别对应 deacot2010.zip、dea_cit_txt_2010.zip、fut_disagg_txt_2010.zip。做跨期或跨品种相关性研究时,选错报告源会让你的持仓结构判断完全偏掉。 外汇与贵金属属高杠杆市场,CFTC 数据仅反映期货端机构倾向,不能直接推导价格方向,开 MT5 调 COT 插件前先确认自己拉的是哪一份源文件。
「用外部 CSV 给 MT5 接上 COT 指标」
在 MT5 里做 COT 指标,核心是把 CFTC 的 CSV 报告读进来再按品种画图。报告类型(COT / DCOT / CIT)、交易者分组和数据类别都做成输入参数,这样切换品种或视角不用改代码,直接在属性框里选。 数据类型目前支持四种:净长仓、未平仓合约中的净长仓比率、威廉指标(基于净长仓计算)、未平仓合约。这个列表是最小可用集,后续要加字段很容易扩,但基本覆盖多数仓位读图需求。 存储单条 COT 记录不能用 struct 而要用 class。原因是 struct 变量之间无法整体赋值,一旦里面挂了动态数组或 string 就废了;class 实例可以随意拷贝传递,配合数组就能按字段快速访问某周的持仓数据。 OnInit 里干三件事:从解压后的 CSV 拉数据、校验输入参数、给缓冲区分配内存。用 INDICATOR_DATA 初始化的缓冲区是要画到图上的,INDICATOR_CALCULATIONS 的只做中间计算,不显示。 OnCalculate 我踩过一个坑:最好对所有缓冲区数组统一用时序索引(最新一根 bar 索引为 0)。这样和函数传入的数组节奏一致,否则层层传递时间数组会让代码乱成一团——如果将来要用满 8 个数组,调用链会非常难看。外汇和贵金属受杠杆与突发事件影响大,COT 只是仓位倾向参考,不等于价格方向。
enum cot_type_report class=class="str">"cmt">// 报告类型 { COT, DCOT, CIT }; enum cot_type_traders class=class="str">"cmt">// 交易者类型 { noncommercial, class=class="str">"cmt">// 非商业 commercial, class=class="str">"cmt">// 商业 nonreportable, class=class="str">"cmt">// 无需报告 index, class=class="str">"cmt">// 指数交易者 producer, class=class="str">"cmt">// 生产商 swapdealers, class=class="str">"cmt">// 掉期交易者 managedmoney, class=class="str">"cmt">// 基金 otherreportables class=class="str">"cmt">// 其他须报告交易者 }; enum cot_type_data class=class="str">"cmt">// COT数据类型 { netlongs, class=class="str">"cmt">// 净买入 netlongsrate, class=class="str">"cmt">// 未平仓合约中的净买入比率 willams_index, class=class="str">"cmt">// Williams指数 openinterest class=class="str">"cmt">// 未平仓合约 }; class cot_record class=class="str">"cmt">// 用于COT报告数据记录的类 { class="kw">public: class="type">class="kw">datetime COT_DATE; class=class="str">"cmt">//日期 class="type">class="kw">double COT_OPENINTEREST; class=class="str">"cmt">//未平仓合约 class="type">class="kw">double COT_LONGNONCOM; class=class="str">"cmt">//非商业交易者的买入 class="type">class="kw">double COT_SHORTNONCOM; class=class="str">"cmt">//非商业交易者的卖出 class="type">class="kw">double COT_LONGCOM; class=class="str">"cmt">//商业交易者的买入 class="type">class="kw">double COT_SHORTCOM; class=class="str">"cmt">//商业交易者的卖出 class="type">class="kw">double COT_LONGNONREPORTABLE; class=class="str">"cmt">//其他非报告交易者的买入 class="type">class="kw">double COT_SHORTNONREPORTABLE; class=class="str">"cmt">//其他非报告交易者的卖出 class="type">class="kw">double COT_LONGINDEX; class=class="str">"cmt">//指数交易者的买入 class="type">class="kw">double COT_SHORTINDEX; class=class="str">"cmt">//指数交易者的卖出 class="type">class="kw">double COT_LONGPRODUCER; class=class="str">"cmt">//生产商/贸易商/加工企业/用户的买入
◍ COT 持仓结构在 MT5 里的变量映射
做贵金属或外汇的趋势判断,CFTC 的 COT 报告是绕不开的底牌。上面这段类定义把报告拆成了可落地的双精度变量:生产商卖出、掉期交易者买卖、基金管理者的买卖、以及其他需报告者的头寸,各自独立成员,方便后续直接读值。 copyvar 这个成员函数干的事很直白——把另一个 cot_record 实例里的全部字段按名赋值到当前对象。从 COT_ID、COT_NAME 到 COT_SHORTOTHERREPORTABLES,共 22 个字段逐一拷贝,没有条件判断,就是纯内存搬运,避免你在外层反复写赋值语句。 CCFTCReport 类则在外层套了一个 type 私有成员,用来标记当前报告属于哪一类(例如期货/期权分拆)。你在 MT5 里接 COT 数据时,先实例化 cot_record,调一次 copyvar 就能把拉到的周度数据塞进自己的结构体,再交给指标算基金净持仓比。 外汇和贵金属受杠杆与跨市场情绪驱动,COT 极值只代表某一类参与者的仓位倾向,反转概率高但并不确定,实盘前务必在策略测试器里跑一遍近 3 年数据。
class="type">class="kw">double COT_SHORTPRODUCER; class=class="str">"cmt">//生产商/贸易商/加工企业/用户的卖出 class="type">class="kw">double COT_LONGSWAPDEALERS; class=class="str">"cmt">//掉期交易者的买入 class="type">class="kw">double COT_SHORTSWAPDEALERS; class=class="str">"cmt">//掉期交易者的卖出 class="type">class="kw">double COT_LONGMANAGEDMONEY; class=class="str">"cmt">//基金交易者的买入 class="type">class="kw">double COT_SHORTMANAGEDMONEY; class=class="str">"cmt">//基金交易者的卖出 class="type">class="kw">double COT_LONGOTHERREPORTABLES;class=class="str">"cmt">//其他需报告者 class="type">class="kw">double COT_SHORTOTHERREPORTABLES; class="type">class="kw">string COT_ID; class=class="str">"cmt">//交易品种标识符 class="type">class="kw">string COT_NAME; class=class="str">"cmt">//交易品种名称 class="type">void copyvar(const cot_record &from) class=class="str">"cmt">// 从一个类中复制内容(变量值)到另一个类中 { COT_ID = from.COT_ID; COT_NAME = from.COT_NAME; COT_DATE = from.COT_DATE; COT_OPENINTEREST = from.COT_OPENINTEREST; COT_LONGNONCOM = from.COT_LONGNONCOM; COT_SHORTNONCOM = from.COT_SHORTNONCOM; COT_LONGCOM = from.COT_LONGCOM; COT_SHORTCOM = from.COT_SHORTCOM; COT_LONGNONREPORTABLE = from.COT_LONGNONREPORTABLE; COT_SHORTNONREPORTABLE = from.COT_SHORTNONREPORTABLE; COT_LONGINDEX = from.COT_LONGINDEX; COT_SHORTINDEX = from.COT_SHORTINDEX; COT_LONGPRODUCER = from.COT_LONGPRODUCER; COT_SHORTPRODUCER = from.COT_SHORTPRODUCER; COT_LONGSWAPDEALERS = from.COT_LONGSWAPDEALERS; COT_SHORTSWAPDEALERS = from.COT_SHORTSWAPDEALERS; COT_LONGMANAGEDMONEY = from.COT_LONGMANAGEDMONEY; COT_SHORTMANAGEDMONEY = from.COT_SHORTMANAGEDMONEY; COT_LONGOTHERREPORTABLES = from.COT_LONGOTHERREPORTABLES; COT_SHORTOTHERREPORTABLES= from.COT_SHORTOTHERREPORTABLES; }; }; class CCFTCReport class=class="str">"cmt">//COT 报告 { class="kw">private: cot_type_report type; class=class="str">"cmt">//当前报告的类型
把 COT 报告塞进自定义指标缓冲区
上面这段声明定义了一个 CCFTCReport 类骨架,专门用来把 CFTC 的 COT 持仓报告接进 MT5 指标。类里 cot_record data[] 存原始记录,sizedata 记当前条数,idsymbol 对应报告里的 Contract Market Code,terminalsymbol 则是你终端里真正的品种名,两者对不上就取不到数。 公开方法里 Init() 做实例初始化,LoadFile() 从 csv 拉数据,At() 按时间 + 交易者类型 + 数据类型(默认 netlongs 净多头)回查数值。实际写指标时,用 input 把 type_trader=noncommercial、type_data=netlongs、type_report=COT 暴露给参数面板,外汇和贵金属都属于高杠杆品种,COT 信号只反映机构倾向,不能当方向保证。 渲染层靠 SetIndexBuffer(0, BufferData, INDICATOR_DATA) 挂主缓冲,SetIndexBuffer(1, BufferCalculations, INDICATOR_CALCULATIONS) 挂计算缓冲,IndicatorSetString(INDICATOR_SHORTNAME, indname) 写短名。OnCalculate 的标准签名含 rates_total、prev_calculated、Time[]、Open[] 等,你直接照抄进 mq5 就能编译验证。
cot_record data[]; class=class="str">"cmt">//cot报告数据 class="type">int sizedata; class=class="str">"cmt">//当前报告的记录数目 class="type">class="kw">string idsymbol; class=class="str">"cmt">//持仓报告中的交易品种标识符,在字段"CFTC Contract Market Code"中 class="type">class="kw">string terminalsymbol; class=class="str">"cmt">//客户端中的交易品种名称 class="kw">public: class="type">void ~CCFTCReport(){ sizedata=class="num">0; }; class="type">bool Init( cot_type_report passedtype, class="type">class="kw">string sym="" ); class=class="str">"cmt">//类实例的初始化 class="type">bool LoadFile( class="type">class="kw">string filename ); class=class="str">"cmt">//从文件中加载数据 class="type">class="kw">string Name(); class=class="str">"cmt">//返回报告的简短名称 class="type">bool Type(cot_type_report passedtype); class=class="str">"cmt">//设置报告类型 cot_type_report Type(){class="kw">return(type);}; class=class="str">"cmt">//返回报告类型 class="type">bool TestGroup( cot_type_traders type_trader ); class=class="str">"cmt">//检查特定交易者是否出现 class="type">bool DefineIdSymbol(); class=class="str">"cmt">//持仓报告中id的定义 class="type">bool GetString( class="type">int handle, cot_record& arr ); class=class="str">"cmt">//从csv文件中获取字符串(记录) class="type">class="kw">string GetFieldCommaDelimited(class="type">class="kw">string &str); class=class="str">"cmt">//从csv字符串中获取字段 class="type">class="kw">double At(class="type">class="kw">datetime time,cot_type_traders type_trader,cot_type_data typedata=netlongs); class=class="str">"cmt">//从报告中获取数据 }; class="kw">input cot_type_traders type_trader = noncommercial; class=class="str">"cmt">//交易者类型 class="kw">input cot_type_data type_data = netlongs; class=class="str">"cmt">//指标类型 class="kw">input cot_type_report type_report = COT; class=class="str">"cmt">//报告的类型 SetIndexBuffer( class="num">0, BufferData, INDICATOR_DATA ); IndicatorSetString(INDICATOR_SHORTNAME,indname); SetIndexBuffer( class="num">1, BufferCalculations, INDICATOR_CALCULATIONS ); class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &Time[], const class="type">class="kw">double &Open[],