MQL5 秘籍之:OCO订单·综合运用
(3/3)· 多数平台的OCO只支持一limit一stop同方向,本文用MQL5类库打破限制写出通用订单对
很多交易者把OCO理解成平台自带的一对限价止损单,遇到跨类型或反向组合就无从下手。MQL5标准类库其实提供了足够的底层构件,只是多数人没把对象关系想清楚就直接调交易函数。本文把前兩篇的概念落进一套可复用的代码框架。
「OCO挂单对的触发前置校验」
在 MT5 面板里点「创建 OCO 对」之前,代码会先轮询 gOrdersProps 数组,逐项确认这笔订单的结构是否齐活。遍历用 for(int idx=0;idx<ArraySize(gOrdersProps);idx++),只要某一个元素的 order_type、volume、price_offset、limit_offset、sl、tp 任一还等于 WRONG_VALUE,就说明用户没填完,直接 is_to_fire_oco=false 并 break 跳出,不再往下走。 这段嵌套 if 的写法很硬:六个条件全满足才 continue,否则立刻清掉创建标识。实盘里这意味着你漏设一个止损点数,OCO 对就不会被下发,避免半残订单进场。外汇与贵金属杠杆高,这种前置拦截能降低误操作概率。 只有 is_to_fire_oco 仍为 true,才会进填充备注、改按钮文字为 "New pair"、染深蓝底浅蓝字并 Enable 的逻辑。按钮文案和颜色就是给交易者的可视反馈:能点=参数齐,灰着=还差字段。 下面这段是点击事件的入口判断,sparam 前 6 字符比对 "myFire" 才认作 OCO 创建键,再叠一层 myButton.IsEnabled() 防重复触发。
for(class="type">int idx=class="num">0;idx<ArraySize(gOrdersProps);idx++) { class=class="str">"cmt">//--- 如果订单类型已设置 if(gOrdersProps[idx].order_type!=WRONG_VALUE) class=class="str">"cmt">//--- 如果交易量已设置 if(gOrdersProps[idx].volume!=WRONG_VALUE) class=class="str">"cmt">//--- 如果市价单的入场点差已设置 if(gOrdersProps[idx].price_offset!=(class="type">uint)WRONG_VALUE) class=class="str">"cmt">//--- 如果limit单的入场点差已设置 if(gOrdersProps[idx].limit_offset!=(class="type">uint)WRONG_VALUE) class=class="str">"cmt">//--- 如果止损已设置 if(gOrdersProps[idx].sl!=(class="type">uint)WRONG_VALUE) class=class="str">"cmt">//--- 如果止赢已设置 if(gOrdersProps[idx].tp!=(class="type">uint)WRONG_VALUE) class="kw">continue; class=class="str">"cmt">//--- 清除OCO订单对的创建标识 is_to_fire_oco=false; break; } class=class="str">"cmt">//--- 创建OCO订单对? if(is_to_fire_oco) { class=class="str">"cmt">//--- 填充备注字段 for(class="type">int ord_idx=class="num">0;ord_idx<ArraySize(gOrdersProps);ord_idx++) gOrdersProps[ord_idx].comment=StringFormat("OCO Order %d",ord_idx+class="num">1); class=class="str">"cmt">//--- 改变按钮属性 myButton.Text("New pair"); myButton.Color(clrDarkBlue); myButton.ColorBackground(clrLightBlue); class=class="str">"cmt">//--- 响应用户操作 myButton.Enable(); } } } class=class="str">"cmt">//--- 点击按钮 else if(id==CHARTEVENT_OBJECT_CLICK) { class=class="str">"cmt">//--- 如果是OCO订单对创建按钮 if(!StringCompare(StringSubstr(sparam,class="num">0,class="num">6),"myFire")) class=class="str">"cmt">//--- 响应用户操作 if(myButton.IsEnabled()) { class=class="str">"cmt">//--- 触发OCO订单对创建事件
OCO 订单对的图表事件接收与列表挂载
在 EA 的 OnChartEvent 里,自定义事件 CHARTEVENT_CUSTOM+ON_OCO 承担的是「新建 OCO 订单对」的触发信号。收到该事件后,先调用 gOco.Init(),传入订单属性集合与当前列表总数加 1 作为新 ID;若初始化返回 true,说明挂单参数已就绪,可在日志里用 PrintFormat 打出形如 "Id of new OCO pair: %I32u" 的配对编号。 随后用 new CiOcoObject(gOco) 在堆上复制一份订单对对象,并用 CheckPointer 确认是 POINTER_DYNAMIC 合法动态指针。只有指针有效,才交给 gOcoList.Add() 挂入全局列表;Add 返回大于 -1 的索引即成功,此时 gOcoList.Total() 就是最新总对数,例如从 0 变 1、从 3 变 4。 任何一步失败(Init 返回 false、指针无效或 Add 返回 -1)都会走 else 分支打印 "OCO-orders placing error!" 或带 ID 的添加失败提示。无论成败,末尾必须调用 Reset() 清空 gOrdersProps 等暂存属性,否则下一轮事件会继承脏数据。外汇与贵金属 OCO 组合涉及双向挂单,滑点或流动性断裂可能导致只成交一侧,属高风险操作,实盘前务必在 MT5 策略测试器跑通这套事件链路。
EventChartCustom(class="num">0,ON_OCO,class="num">0,class="num">0.0,"OCO_fire"); Print("Command to create new bunch has been received."); } } class=class="str">"cmt">//--- 处理新订单对初始化命令 else if(id==CHARTEVENT_CUSTOM+ON_OCO) { class=class="str">"cmt">//--- OCO订单对初始化 if(gOco.Init(gOrdersProps,gOcoList.Total()+class="num">1)) { PrintFormat("Id of new OCO pair: %I32u",gOco.Id()); class=class="str">"cmt">//--- 复制 CiOcoObject *ptr_new_oco=new CiOcoObject(gOco); if(CheckPointer(ptr_new_oco)==POINTER_DYNAMIC) { class=class="str">"cmt">//--- 添加到列表 class="type">int node_idx=gOcoList.Add(ptr_new_oco); if(node_idx>-class="num">1) PrintFormat("Total number of bunch: %d",gOcoList.Total()); else PrintFormat("Error when adding OCO pair %I32u to list!",gOco.Id()); } } else Print("OCO-orders placing error!"); class=class="str">"cmt">//--- 清除相关属性 Reset(); }
◍ 把OCO面板跑起来的实操路径
前面两节把标准类库处理OCO订单的封装思路讲透了,这一节只补一个最容易被卡住的落点:代码下到本地后怎么在MT5里真正跑起来。有用户反馈按说明解压后导航器里看不到OCO文件夹,本质原因是.mq5和.mqh没有先逐个编译。 正确动作是建一个独立文件夹(比如命名OCO EA),把init_oco.mq5、control_oco_ea.mq5、panel_oco_ea.mq5、ciocoobject.mqh、crandom.mqh全部塞进MT5数据文件夹的MQL5/Experts下。按F4进IDE,把这五个文件挨个打开并编译,构建版本1981和当前新版均能做到零错误零警告。 编译完回MT5导航器,Experts里会多出OCO EA分组,把panel_oco_ea拖上图表就能调出控制面板。外汇与贵金属杠杆高,OCO只是锁风险的一层手段,仓位和止损仍要自己控。 那位2025年9月才实测跑通的用户留了句话:一步步看,别跳。代码能编译通过,差的就是路径和顺序。