6.2 订单池遍历与 OrderSelect
两种选择方式
- SELECT_BY_POS + MODE_TRADES:按持仓池位置选择,配合循环遍历所有未平仓订单。
- SELECT_BY_TICKET:按订单号精确选择,适合下单后复核、按 ticket 平仓。
- MODE_HISTORY:已平仓历史,配合 OrdersHistoryTotal() 使用。不要用它遍历当前持仓。
最常见的错误是“只按位置选,不检查返回值”。OrderSelect 返回 false 时,后面读到的属性可能是上一张单的残留值,导致平错单。每次选中后都要 `if(!OrderSelect(...)) continue;`。
为什么要过滤品种和魔术号
一个 MT4 账户可以同时挂多个 EA、多个品种。订单池是账户级的,不是图表级的。如果你的 EA 直接 `for(i=0;i MagicNumber 是 EA 自己写入订单的“身份证”。同一个 EA 在不同图表上运行时,建议用“基础魔术号 + 图表编号”或不同输入参数区分,避免互相干扰。 平仓会改变订单池的索引。假设池子里有 A、B、C 三条,你正序处理 i=0 平掉 A,B 会顶到索引 0,C 顶到索引 1;循环 i 自增到 1 时,实际处理的是 C,B 被跳过。倒序从 OrdersTotal()-1 开始,删除后面的元素不会影响前面未处理的索引,所以不会漏单。 同理,遍历中如果需要删除图表对象、数组元素,也优先倒序。这个习惯能避免大量“看起来随机漏一条”的 bug。 下面的代码给出两个常用函数:CloseAllByMagic 倒序平仓,CountMyOrders 正序计数。计数不需要删除元素,所以正序没问题;平仓必须倒序。 OrderClose 的第二个参数是手数,第三个是平仓价:多单用 Bid,空单用 Ask。滑点 3 表示允许 3 个点的偏差。平仓失败时一定要打印 ticket 和 GetLastError(),否则你只知道“失败了”,不知道是哪张单、为什么失败。 OrderSelect 是一个全局游标。你在函数 A 里选中了 101 号单,调用函数 B 时如果 B 又 OrderSelect 了 102 号,回到 A 后继续读 OrderLots() 读到的是 102 号的手数。解决办法:要么在同一个函数里“选中—读取—使用”一气呵成,要么先把需要的字段存进局部变量,再调用其他函数。 订单池操作的核心是“选中—校验—动作”三步:OrderSelect 选中,检查 Symbol 与 Magic,再执行平仓或修改。遍历方向跟着“是否删除元素”走:只读正序,删除倒序。记住这两条,订单池相关的 bug 会少一大半。 OrderSelect 成功之后,常用的属性有:OrderTicket() 订单号、OrderType() 方向、OrderLots() 手数、OrderOpenPrice() 开仓价、OrderStopLoss() 止损、OrderTakeProfit() 止盈、OrderMagicNumber() 魔术号、OrderSymbol() 品种、OrderProfit() 浮动盈亏、OrderComment() 注释、OrderOpenTime() 开仓时间。不要在 OrderSelect 之前读这些函数,它们返回的是“上一次选中”的残留值。 读取属性时尽量一次读完,存进局部变量。比如先 `double sl=OrderStopLoss(); double tp=OrderTakeProfit();`,再调用可能内部会 OrderSelect 的函数。这样即使后续游标被移动,你手里的值仍然是当前订单的。 OrderClose 的第二个参数可以小于 OrderLots(),这就是部分平仓。平掉一部分后,剩下的手数仍在订单池里,订单号不变。部分平仓常见于分批止盈:价格走到第一目标平一半,走到第二目标再平剩下的一半。注意每次部分平仓后重新 OrderSelect 读取剩余手数,不要用平仓前的缓存值计算下一次手数。 部分平仓也会改变订单池索引,所以循环仍要倒序。若一次循环里对同一张单多次部分平仓,建议每次重新选中并检查剩余手数,避免浮点误差导致“手数小于最小手数”的 131 错误。 OrdersTotal() 只统计当前持仓;已平仓订单要用 OrdersHistoryTotal() 配合 `OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)`。历史池的索引顺序与持仓池不同,且会随平仓增加。统计当日盈亏时,遍历历史订单,按 OrderCloseTime() 过滤日期,累加 OrderProfit()+OrderSwap()+OrderCommission()。不要用 AccountProfit() 代替,它只反映当前浮动盈亏。 历史订单的 OrderType() 仍然是开仓方向,OrderClosePrice() 是平仓价。想判断一笔交易是盈利还是亏损,用 OrderProfit() 加费用,而不是比较开仓价和平仓价——点差和佣金可能让“价格方向对了”的单子仍然亏损。 魔术号是 EA 的身份标识,不是订单的唯一标识。同一策略跑在 EURUSD 与 GBPUSD 两个图表上,如果都用 8888,订单池过滤就会跨品种;虽然还会比较 Symbol(),但同一品种的多图表、多周期仍会冲突。推荐用“基础号 + 图表序号”或“基础号 + 周期编码”,例如 20261102 + 1/2/3,既好读又能区分。 魔术号只影响你自己的过滤逻辑,不改变经纪商的订单处理。不要依赖魔术号做风控隔离,真正的隔离还要加上品种、注释、甚至订单号白名单。 订单池是动态的:你遍历的同时,另一个 EA 可能在下单或平仓,经纪商也可能因为止损触发而移除订单。遍历时不要假设“这秒看到的 6 张单,下一秒还是 6 张”。每次循环重新读 OrdersTotal(),每次动作前重新 OrderSelect,动作失败就跳过,不要用缓存的列表强行操作。 如果需要“先统计再批量处理”,先做一次只读快照,把订单号存进数组;处理时按订单号 SELECT_BY_TICKET 重新确认。这样即使订单池顺序变了,也不会平错单。快照只保存 ticket,不保存手数、价格等易变字段;真正执行时重新读取。 OrderClose 返回 false 时,按顺序检查:订单是否已经不存在(4108);平仓价是否用反(多单用 Bid、空单用 Ask);手数是否大于持仓(131);滑点是否太小导致 138;市场是否关闭(146/交易不允许)。把 ticket、方向、手数、平仓价、错误码一起打印,基本能一眼定位。 如果订单是部分平仓,剩余手数可能小于最小手数,下一次平仓会报 131。此时要么一次平完,要么在部分平仓前确认剩余手数仍满足 MODE_MINLOT。不要用“平一半”这种模糊逻辑,先算清楚再下单。 6 张单里只有 2 张属于本 EA。单步看指针从 i=5 走到 i=0,比较品种与魔术号,最后倒序平仓。 提示:动画默认暂停。点「播放」连续演示,点「单步」自己控制节奏;桌面端也可以用空格播放、方向键单步、R 重置。倒序平仓:一个必须记住的细节
完整示例:按魔术号平仓与计数
OrderSelect 之后属性会变
常见错误
小结
订单属性读取清单
部分平仓与分批处理
历史订单与统计
魔术号的设计
订单池快照与并发
平仓失败怎么排查
交互演示:订单池遍历:指针怎么走
练习题
-
6.2 订单池遍历与 OrderSelect 选择题1.遍历当前持仓订单池,OrderSelect 的正确参数组合是? (2分) [选择]A. SELECT_BY_POS, MODE_TRADESB. SELECT_BY_TICKET, MODE_TRADESC. SELECT_BY_POS, MODE_HISTORYD. SELECT_BY_TICKET, MODE_HISTORY2.平仓遍历时建议使用哪种循环方向? (2分) [选择]A. 从 0 到 OrdersTotal()-1B. 从 OrdersTotal()-1 到 0C. 从 1 到 OrdersTotal()D. 任意方向都一样3.OrderSelect 成功后,OrderMagicNumber() 返回的是最近一次选中订单的魔术号。 (2分) [判断]
-
6.2 订单池遍历与 OrderSelect 编程实战(AI 批改)1.【实战】写 CloseAllByMagic (10分) [MQL4自动批改]写函数 bool CloseAllByMagic(int magic):倒序遍历当前持仓,OrderSelect 失败则 continue;只处理 OrderSymbol()==Symbol() 且 OrderMagicNumber()==magic 的订单;多单用 Bid、空单用 Ask 平仓,滑点 3;平仓失败打印 ticket 与 GetLastError 并返回 false。必须出现 OrdersTotal、OrderSelect、OrderClose、GetLastError。
《MQL4 教程与 MT4 EA 编程》课程目录
系统讲解 MQL4 编程:从变量、循环、函数到 OrderSend 下单、iMA 指标调用与 EA 回测。36 节中文教程,覆盖 MT4 编程入门、外汇 EA 编写与外汇量化策略三类实战,每节配可运行代码和 AI 自动批改练习
- 第1章 基本语法
- 1.1 第一个EA
- 1.2 如何学习
- 1.3 变量
- 1.4 语句
- 1.5 运算符
- 1.6 常量与枚举
- 第2章 条件
- 2.1 if语句
- 2.2 关系运算符
- 2.3 逻辑运算符
- 2.4 复合条件
- 2.5 switch语句
- 第3章 循环
- 3.1 for循环
- 3.2 while循环
- 3.3 break和continue
- 3.4 嵌套循环
- 第4章 函数
- 4.1 函数的定义
- 4.2 参数和返回值
- 4.3 变量的作用域
- 4.4 常用内置函数
- 4.5 日期和时间函数
- 4.6 交易函数
- 4.7 账户和市场信息函数
- 4.8 图表和对象函数
- 4.9 文件操作函数
- 4.10 数组函数
- 4.11 技术指标函数
- 第5章 如何写EA
- 5.1 EA的基本结构
- 5.2 常用内置变量
- 5.3 下单函数
- 5.4 技术指标
- 5.5 实战:均线交叉EA
- 第6章 进阶实战:从能跑走向可靠
- 6.1 OnTimer 定时器与事件处理
- 6.2 订单池遍历与 OrderSelect
- 6.3 错误处理与 GetLastError
- 6.4 自定义指标与指标缓冲区
- 6.5 图表对象与交易面板
- 6.6 回测与参数优化