MQL5 交易工具包(第 3 部分):开发挂单管理 EX5 库·综合运用
(3/3)·从开单改单到删单统计,81 节把挂单全生命周期拆成可导入的 MQL5 库与 GUI 面板
接上篇,我们继续深挖交易工具包的收尾部分。多数自制 EA 把挂单逻辑散落在主文件里,换品种或加过滤就得重写一遍,维护成本随时间滚雪球。把挂单的打开、修改、删除和统计抽成独立 EX5 库,才是缩短后续开发周期的正路。
- 卖出限价挂单的下单函数怎么写
- 下单重试与成交回码处理
- 挂卖出止损单的函数怎么写
- 把手数对齐最小步长再塞进重试循环
- 按单号改挂单的容错写法
- 挂单改价与止损止盈的距离校验
- 挂单改止损止盈的价位校验逻辑
- 挂单改价与止损止盈的重发逻辑
- 挂单改价前的止损位合法性校验
- 挂单改价时的止损止盈边界校验
- 改单前先卡死止损止盈的合规距离
- 挂单改价前的打印与重发机制
- 挂单改单失败后的退出与重试间隙
- 用单号精准撤掉挂单的实现
- 挂单删除的重试与错误处理
- 清场挂单的两种入口与重试兜底
- 清掉指定品种的挂单买停
- 清掉指定品种的买入限价挂单
- 清掉指定品种与幻数的卖损挂单
- 批量撤掉卖出限价挂单的实现逻辑
- 按幻数清掉全部挂单的实现细节
- 扫挂单前先把全局计数器归零
- 按挂单类型拆分账户与EA持仓统计
- 挂单分类统计的循环骨架
- 数清账户里的挂单买停
- 抓取账户里的挂单买限数量
- 抓出账户里挂着的卖止损单
- 抓未结卖限挂单的数量
- 未结订单的总量怎么一次拿走
- 统计挂单里的买入止损总手数
- 抓取账户里的挂单限价买量
- 抓取账户里挂着的卖损总仓
- 统计未成交卖限挂单的总手数
- 按幻数统计挂单数量
- 按幻数统计挂单里的买入止损
- 按幻数统计买入限价挂单
- 按幻数统计挂中的卖损单
- 按幻数清点未结卖限单
- 按幻数统计挂单总手数
- 按幻数统计挂单里的买入止损总仓
- 按幻数统计买入限价挂单总手数
- 按幻数统计挂单里的卖损总仓
- 按幻数聚合的卖限挂单总手数
- 按品种与幻数清点挂单
- 按品种与幻数统计挂着的买止损
- 按品种和幻数统计挂着的买限价单
- 按品种和幻数清点挂着的卖损单
- 按品种和幻数数清卖出限价挂单
- 按品种与幻数统计挂单总手数
- 按品种和幻数统计挂单里的买入止损量
- 按品种与幻数统计挂单里的买限总量
- 按品种与幻数统计卖出止损挂单总仓
- 按品种与幻数归集的卖限挂单总仓
- 一键导出账户挂单全景字符串
- 按幻数拉取挂单全景快照
- 把挂单状态拼成可读字符串
- 把挂单管理库接进你的 MQL5 工程
- 把挂单管理库接进自己的 EA
- 挂单统计与限价单下单价差偏移
- 挂单函数的止损止盈按点差倍数铺开
- 挂单的批量改价与定向删除
- 挂单批量清理与按魔法码统计的接口细节
- 按挂单类型拆账户持仓统计
- 用魔数和品种筛出持仓状态
- 用 EX5 库撑起挂单面板的图形交互
- 面板与控件的坐标颜色宏定义
- 挂单面板的控件坐标怎么排
- 挂单面板的限价单控件坐标布局
- 挂单面板的状态标签与撤单按钮布局
- 挂单面板的控件坐标与初始报价
- 挂单面板的标签与按钮初始化
- 挂单面板的止损止盈输入框怎么拼
- 挂单面板的止损止盈输入框拼接
- 挂单面板的 Sell Limit 控件装配
- 挂单面板里的状态栏与分类撤单按钮
- 挂单面板的按钮绑定与点击事件分发
- 挂单面板的点击事件如何落地成订单
- 挂单面板的点击响应与声音反馈
- 按钮触发后的一键删单逻辑
- 挂单面板的按钮状态随持仓联动
- 把挂单库接进你自己的面板
卖出限价挂单的下单函数怎么写
卖出限价单(Sell Limit)只在市价低于挂单价时才有效,逻辑是赌价格先涨到某个更高水平、再反转向下。它和市价空单相反:入场价必须高于当前 Bid,属于「等反弹再空」的预埋单。外汇和贵金属波动大、滑点频繁,用这种单要避免把入场价设在止损位以内,否则 broker 会直接拒单。 OpenSellLimit(...) 封装了整套校验与下单请求,返回 true 代表挂单请求提交成功、false 代表被函数内部拦截或 EA 禁止交易。下面这段代码把「能不能挂」的判断全收进函数里,实盘里直接传 magic、品种、挂单价、手数、sl/tp 点数即可。 函数开头先调 TradingIsAllowed() 确认算法交易开关,关了就直接返回 false。随后抓取 SYMBOL_DIGITS、SYMBOL_TRADE_STOPS_LEVEL、SYMBOL_POINT、SYMBOL_SPREAD 四个属性——止损最小距离用 stop level 点数算,不读这个字段就容易在 EURUSD 上把 sl 设成 2 点而被拒。 入场价校验那一段值得细看:条件是 Bid + stopLevel*point > entryPrice - spread*point 时判为无效。也就是说挂单价不能离市价太近,必须留给 spread 和 stop level 缓冲。sl/tp 若小于 symbolStopLevel 会被强制拉平到最小止损距离,避免无效止损。
class="type">bool OpenSellLimit(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">class="kw">double tpPrice = class="num">0.0, slPrice = class="num">0.0; class=class="str">"cmt">//-- Get some information about the orders symbol class="type">int symbolDigits = (class="type">int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); class="type">int symbolStopLevel = (class="type">int)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL); class="type">class="kw">double symbolPoint = SymbolInfoDouble(symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(symbol, SYMBOL_SPREAD); class=class="str">"cmt">//-- Save the order type enumeration ENUM_ORDER_TYPE orderType = ORDER_TYPE_SELL_LIMIT; class=class="str">"cmt">//-- check if the entry price is valid if( SymbolInfoDouble(symbol, SYMBOL_BID) + (symbolStopLevel * symbolPoint) > entryPrice - (spread * symbolPoint) ) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t open a new ", EnumToString(orderType), ". (Reason --> INVALID ENTRY PRICE: ", DoubleToString(entryPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid entry price, log the error, exit the function and class="kw">return false } class=class="str">"cmt">//-- Check the validity of the sl and tp if(sl > class="num">0 && sl < symbolStopLevel) { sl = symbolStopLevel; } if(tp > class="num">0 && tp < symbolStopLevel) { tp = symbolStopLevel; } slPrice = (sl > class="num">0) ? NormalizeDouble(entryPrice + sl * symbolPoint, symbolDigits) : class="num">0; tpPrice = (tp > class="num">0) ? NormalizeDouble(entryPrice - tp * symbolPoint, symbolDigits) : class="num">0; class=class="str">"cmt">//-- reset the the tradeRequest and tradeResult values by zeroing them ZeroMemory(tradeRequest); ZeroMemory(tradeResult); class=class="str">"cmt">//-- initialize the parameters to open a sell limit order tradeRequest.type = orderType; tradeRequest.action = TRADE_ACTION_PENDING; tradeRequest.magic = magicNumber; tradeRequest.symbol = symbol; tradeRequest.price = NormalizeDouble(entryPrice, symbolDigits); tradeRequest.tp = tpPrice; tradeRequest.sl = slPrice; tradeRequest.comment = orderComment; tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * class="num">2; class=class="str">"cmt">//-- Set and moderate the lot size or volume class=class="str">"cmt">//-- Verify that volume is not less than allowed minimum lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)); class=class="str">"cmt">//-- Verify that volume is not more than allowed maximum lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)); class=class="str">"cmt">//-- Round down to nearest volume step
「下单重试与成交回码处理」
手数合规化后,代码先把 volume 对齐到品种最小步长:用 MathFloor 除以 SYMBOL_VOLUME_STEP 再乘回,避免券商拒单。紧接着 ResetLastError 清掉缓存错误码,保证后面拿到的 retcode 是本次请求的真实结果。 真正发单放在 for 循环里,上限由 MAX_ORDER_RETRIES 控制。OrderSend 成功后并不算完,必须看 tradeResult.retcode:等于 10008(已成交)或 10009(已放置)才认定开仓成功并返回 true。 若 OrderSend 返回 false 或命中关键错误,ErrorAdvisor 会介入判断能否重试;不能重试或脚本被停止(IsStopped)就打印错误明细并 return false。可重试时靠 Sleep(ORDER_RETRY_DELAYS) 做短暂停顿,降低对交易服务器的冲击,然后进入下一轮循环。外汇与贵金属杠杆高,这类重试逻辑能在滑点频发时提高挂单成功率,但也可能在极端行情下连续失败,需自行在 MT5 里跑回测验证延迟参数。
lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); tradeRequest.volume = lotSize; class=class="str">"cmt">//--- Reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function ResetLastError(); for(class="type">int loop = class="num">0; loop <= MAX_ORDER_RETRIES; loop++) class=class="str">"cmt">//-- try opening the order until it is successful { class=class="str">"cmt">//--- send order to the trade server if(OrderSend(tradeRequest, tradeResult)) { class=class="str">"cmt">//-- Print the order details PrintOrderDetails("Sent OK", symbol); class=class="str">"cmt">//-- Confirm order execution if(tradeResult.retcode == class="num">10008 || tradeResult.retcode == class="num">10009) { Print( __FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " ", EnumToString(orderType), " #", tradeResult.order, ", Price: ", tradeResult.price ); PrintFormat("retcode=%u deal=%I64u order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order); Print("_______________________________________________________________________________________"); class="kw">return(true); class=class="str">"cmt">//-- exit the function class=class="str">"cmt">//break; //--- success - order placed ok. exit the for loop } } else class=class="str">"cmt">//-- Order request failed { class=class="str">"cmt">//-- Print the order details PrintOrderDetails("Sending Failed", symbol); class=class="str">"cmt">//-- order not sent or critical error found if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped()) { Print( __FUNCTION__, ": ", symbol, " ERROR opening a ", EnumToString(orderType), " at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume ); Print("_______________________________________________________________________________________"); class="kw">return(false); class=class="str">"cmt">//-- exit the function class=class="str">"cmt">//break; //-- exit the for loop Sleep(ORDER_RETRY_DELAYS);class=class="str">"cmt">//-- Small pause before retrying to avoid overwhelming the trade server } } } class="kw">return(false); }
◍ 挂卖出止损单的函数怎么写
卖出止损(Sell Stop)是当市价高于挂单入场价时生效的挂单,触发后以等于或低于该入场价的买价卖出。预期品种跌破某水平后延续下行时,用它提前埋伏空头,属于外汇与贵金属交易中常见的高风险操作,杠杆放大下止损不及可能快速回撤。 OpenSellStop(...) 返回布尔值:下单请求被 MT5 终端顺利接受则返回 true,任何环节失败(如自动交易被禁、入场价非法)返回 false。函数先调 TradingIsAllowed() 拦截 EA 交易权限,再从 SymbolInfoInteger / SymbolInfoDouble 抓取 digits、止损级差、点值、点差,这些是后面校验的基准。 入场价校验逻辑值得细看:若 SYMBOL_BID 减去止损级差点值,仍小于 entryPrice 减点差点值,说明挂单价离市价太近,违反经纪商 stop level,直接打印错误并退出。SL/TP 点数若小于 symbolStopLevel 会被强制拉平到级差,避免无效止损。 手数经 MathMax 与 MathMin 夹在 SYMBOL_VOLUME_MIN 和 SYMBOL_VOLUME_MAX 之间,再按步长下取整,deviation 设为点差两倍以减少拒单。下面代码逐行拆开看更直观。
class="type">bool OpenSellStop(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">class="kw">double slPrice = class="num">0.0, tpPrice = class="num">0.0; class=class="str">"cmt">//-- Get some information about the orders symbol class="type">int symbolDigits = (class="type">int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); class="type">int symbolStopLevel = (class="type">int)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL); class="type">class="kw">double symbolPoint = SymbolInfoDouble(symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(symbol, SYMBOL_SPREAD); class=class="str">"cmt">//-- Save the order type enumeration ENUM_ORDER_TYPE orderType = ORDER_TYPE_SELL_STOP; class=class="str">"cmt">//-- check if the entry price is valid if( SymbolInfoDouble(symbol, SYMBOL_BID) - (symbolStopLevel * symbolPoint) < entryPrice - (spread * symbolPoint) ) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t open a new ", EnumToString(orderType), ". (Reason --> INVALID ENTRY PRICE: ", DoubleToString(entryPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid entry price, log the error, exit the function and class="kw">return false } class=class="str">"cmt">//-- Check the validity of the sl and tp if(sl > class="num">0 && sl < symbolStopLevel) { sl = symbolStopLevel; } if(tp > class="num">0 && tp < symbolStopLevel) { tp = symbolStopLevel; } slPrice = (sl > class="num">0) ? NormalizeDouble(entryPrice + sl * symbolPoint, symbolDigits) : class="num">0; tpPrice = (tp > class="num">0) ? NormalizeDouble(entryPrice - tp * symbolPoint, symbolDigits) : class="num">0; class=class="str">"cmt">//-- reset the the tradeRequest and tradeResult values by zeroing them ZeroMemory(tradeRequest); ZeroMemory(tradeResult); class=class="str">"cmt">//-- initialize the parameters to open a sell stop order tradeRequest.type = orderType; tradeRequest.action = TRADE_ACTION_PENDING; tradeRequest.magic = magicNumber; tradeRequest.symbol = symbol; tradeRequest.price = NormalizeDouble(entryPrice, symbolDigits); tradeRequest.tp = tpPrice; tradeRequest.sl = slPrice; tradeRequest.comment = orderComment; tradeRequest.deviation = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * class="num">2; class=class="str">"cmt">//-- Set and moderate the lot size or volume class=class="str">"cmt">//-- Verify that volume is not less than allowed minimum lotSize = MathMax(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)); class=class="str">"cmt">//-- Verify that volume is not more than allowed maximum lotSize = MathMin(lotSize, SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)); class=class="str">"cmt">//-- Round down to nearest volume step
把手数对齐最小步长再塞进重试循环
开仓前这一步先把计算出的 lotSize 往经纪商的最小成交量步长靠。MathFloor(lotSize / SYMBOL_VOLUME_STEP) 向下取整后再乘回步长,能避免报 10014(无效成交量)这类低级 reject,尤其对黄金 XAUUSD 常见 0.01 步长、外汇对 0.01 或 0.1 步长都管用。 ResetLastError() 必须放在发单循环之前,否则上一次残留的错误码会污染 ErrorAdvisor 里的判断,让你误以为本次发单失败原因和真实 retcode 对不上。 下面这段是实际发单加重试的核心。MAX_ORDER_RETRIES 控制最多尝试次数,OrderSend 返回 true 只代表请求送达,不代表成交;真正要看 tradeResult.retcode 是不是 10008(已放置)或 10009(已成交)。
lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); tradeRequest.volume = lotSize; class=class="str">"cmt">//--- Reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function ResetLastError(); for(class="type">int loop = class="num">0; loop <= MAX_ORDER_RETRIES; loop++) class=class="str">"cmt">//-- try opening the order until it is successful { class=class="str">"cmt">//--- send order to the trade server if(OrderSend(tradeRequest, tradeResult)) { class=class="str">"cmt">//-- Print the order details PrintOrderDetails("Sent OK", symbol); class=class="str">"cmt">//-- Confirm order execution if(tradeResult.retcode == class="num">10008 || tradeResult.retcode == class="num">10009) { Print( __FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " ", EnumToString(orderType), " #", tradeResult.order, ", Price: ", tradeResult.price ); PrintFormat("retcode=%u deal=%I64u order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order); Print("_______________________________________________________________________________________"); class="kw">return(true); class=class="str">"cmt">//-- exit the function class=class="str">"cmt">//break; //--- success - order placed ok. exit the for loop } } else class=class="str">"cmt">//-- Order request failed { class=class="str">"cmt">//-- Print the order details PrintOrderDetails("Sending Failed", symbol); class=class="str">"cmt">//-- order not sent or critical error found if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped()) { Print( __FUNCTION__, ": ", symbol, " ERROR opening a ", EnumToString(orderType), " at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume ); Print("_______________________________________________________________________________________"); class="kw">return(false); class=class="str">"cmt">//-- exit the function class=class="str">"cmt">//break; //-- exit the for loop Sleep(ORDER_RETRY_DELAYS);class=class="str">"cmt">//-- Small pause before retrying to avoid overwhelming the trade server } } } class="kw">return(false); }
lotSize = MathFloor(lotSize / SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)) * SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); tradeRequest.volume = lotSize; class=class="str">"cmt">//--- Reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function ResetLastError(); for(class="type">int loop = class="num">0; loop <= MAX_ORDER_RETRIES; loop++) class=class="str">"cmt">//-- try opening the order until it is successful { class=class="str">"cmt">//--- send order to the trade server if(OrderSend(tradeRequest, tradeResult)) { class=class="str">"cmt">//-- Print the order details PrintOrderDetails("Sent OK", symbol); class=class="str">"cmt">//-- Confirm order execution if(tradeResult.retcode == class="num">10008 || tradeResult.retcode == class="num">10009) { Print( __FUNCTION__, ": CONFIRMED: Successfully openend a ", symbol, " ", EnumToString(orderType), " #", tradeResult.order, ", Price: ", tradeResult.price ); PrintFormat("retcode=%u deal=%I64u order=%I64u", tradeResult.retcode, tradeResult.deal, tradeResult.order); Print("_______________________________________________________________________________________"); class="kw">return(true); class=class="str">"cmt">//-- exit the function class=class="str">"cmt">//break; //--- success - order placed ok. exit the for loop } } else class=class="str">"cmt">//-- Order request failed { class=class="str">"cmt">//-- Print the order details PrintOrderDetails("Sending Failed", symbol); class=class="str">"cmt">//-- order not sent or critical error found if(!ErrorAdvisor(__FUNCTION__, symbol, tradeResult.retcode) || IsStopped()) { Print( __FUNCTION__, ": ", symbol, " ERROR opening a ", EnumToString(orderType), " at: ", tradeRequest.price, ", Lot\\Vol: ", tradeRequest.volume ); Print("_______________________________________________________________________________________"); class="kw">return(false); class=class="str">"cmt">//-- exit the function class=class="str">"cmt">//break; //-- exit the for loop Sleep(ORDER_RETRY_DELAYS);class=class="str">"cmt">//-- Small pause before retrying to avoid overwhelming the trade server } } } class="kw">return(false); }
「按单号改挂单的容错写法」
改活动挂单别直接调 OrderSend,先封装一个 ModifyPendingOrderByTicket(orderTicket, newEntryPrice, newSl, newTp)。四个参数里 orderTicket 是 ulong 单号,newEntryPrice 为 0 就保留原入场价,newSl / newTp 以点数计、传 0 表示不动或删除对应止损止盈。函数返回 bool,成功 true 失败 false,失败原因通常是参数非法、选单出错或服务器拒绝。 进场前先用 TradingIsAllowed() 卡一道,算法交易被禁就直接 return false。随后 ResetLastError() 清缓存再 OrderSelect(orderTicket),选单失败立刻打印 GetLastError() 并退出,选中也只打一条日志继续,不浪费动作。 选单后抓出交易品种、成交量、当前 SL/TP、订单类型等,用 SymbolInfoInteger 拿 SYMBOL_DIGITS、SYMBOL_TRADE_STOPS_LEVEL、SYMBOL_SPREAD,用 SymbolInfoDouble 拿 SYMBOL_POINT。newEntryPrice 大于 0 时要按订单类型校验:BUY_STOP 和 SELL_LIMIT 的合法价区不同,提早拒掉无效价能少踩服务器报错。 SL/TP 由点数乘 symbolPoint 换算成价格,必须过经纪商止损水平(symbolStopLevel 点)这一关。全部验完再重置 tradeRequest 填值,发请求包一层重试:初次失败隔 Sleep() 再试,避免连发冲垮交易服务器;重试耗尽或遇严重错返回 false,成功返回 true。外汇与贵金属挂单修改受点差和停损位约束,实际能否成交倾向取决于当时流动性与经纪商规则,属高风险操作,开 MT5 用下面代码跑一遍最直观。
class="type">bool ModifyPendingOrderByTicket(class="type">ulong orderTicket, class="type">class="kw">double newEntryPrice, class="type">int newSl, class="type">int newTp) class="kw">export { if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class=class="str">"cmt">//--- Confirm and select the order using the provided orderTicket ResetLastError(); class=class="str">"cmt">//--- Reset error cache incase of ticket selection errors if(OrderSelect(orderTicket)) { class=class="str">"cmt">//---Order selected Print("\r\n_______________________________________________________________________________________"); Print(__FUNCTION__, ": Order with ticket:", orderTicket, " selected and ready to set SLTP."); } else { Print("\r\n_______________________________________________________________________________________"); Print(__FUNCTION__, ": Selecting order with ticket:", orderTicket, " failed. ERROR: ", GetLastError()); class="kw">return(false); class=class="str">"cmt">//-- Exit the function } class="type">class="kw">double newTpPrice = class="num">0.0, newSlPrice = class="num">0.0; class=class="str">"cmt">//--- Order ticket selected, save the order properties class="type">class="kw">string orderSymbol = OrderGetString(ORDER_SYMBOL); class="type">class="kw">double currentEntryPrice = OrderGetDouble(ORDER_PRICE_OPEN); class="type">class="kw">double volume = OrderGetDouble(ORDER_VOLUME_INITIAL); class="type">class="kw">double currentOrderSlPrice = OrderGetDouble(ORDER_SL); class="type">class="kw">double currentOrderTpPrice = OrderGetDouble(ORDER_TP); ENUM_ORDER_TYPE orderType = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); class="type">class="kw">double orderPriceCurrent = OrderGetDouble(ORDER_PRICE_CURRENT); class=class="str">"cmt">//-- Get some information about the orders symbol class="type">int symbolDigits = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_DIGITS); class=class="str">"cmt">//-- Number of symbol decimal places class="type">int symbolStopLevel = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_TRADE_STOPS_LEVEL); class="type">class="kw">double symbolPoint = SymbolInfoDouble(orderSymbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_SPREAD); if(newEntryPrice > class="num">0.0) { if(orderType == ORDER_TYPE_BUY_STOP || orderType == ORDER_TYPE_SELL_LIMIT)
◍ 挂单改价与止损止盈的距离校验
修改挂单入场价时,MT5 不是你想挪哪就挪哪。对于 BUY_LIMIT 和 SELL_STOP,新入场价必须离实时 ASK 足够远:ASK 加上 broker 的 stop level 点数,得大于新价减去 spread 点数,否则函数直接 return false 并打日志。 反过来,BUY_STOP 和 SELL_LIMIT 类挂单则看 BID:BID 减去 stop level 点数,必须小于新价加 spread 点数。这两段判断本质是在复刻券商对挂单最小距离的硬约束,跳过就会报 INVALID NEW ENTRY PRICE。 如果不改入场价,代码会把 newEntryPrice 指回 currentEntryPrice,跳过上述校验。随后按订单方向算 SL/TP:买类挂单的 SL 是新价减 newSl 点、TP 是新价加 newTp 点;若传入 0 则表示移除对应止损或止盈。 最后还有一道 SL 有效性检查——买类挂单中,若 newSlPrice 大于 0 且新价与 SL 的差小于 symbolStopLevel * symbolPoint,同样会被拒。外汇与贵金属杠杆高、点差跳动快,这类距离校验在实盘里可能直接导致改单失败,建议开 MT5 用券商标尺核对 stop level 再跑。
{
if(
SymbolInfoDouble(orderSymbol, SYMBOL_ASK) + (symbolStopLevel * symbolPoint) >
newEntryPrice - (spread * symbolPoint)
)
{
Print(
"\r\n", __FUNCTION__, ": Can&class="macro">#x27;t MODIFY ", EnumToString(orderType),
". (Reason --> INVALID NEW ENTRY PRICE: ", DoubleToString(newEntryPrice, symbolDigits), ")\r\n"
);
class="kw">return(false); class=class="str">"cmt">//-- Invalid new entry price, log the error, exit the function and class="kw">return false
}
}
if(orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_SELL_STOP)
{
if(
SymbolInfoDouble(orderSymbol, SYMBOL_BID) - (symbolStopLevel * symbolPoint) <
newEntryPrice + (spread * symbolPoint)
)
{
Print(
"\r\n", __FUNCTION__, ": Can&class="macro">#x27;t MODIFY ", EnumToString(orderType),
". (Reason --> INVALID NEW ENTRY PRICE: ", DoubleToString(newEntryPrice, symbolDigits), ")\r\n"
);
class="kw">return(false); class=class="str">"cmt">//-- Invalid new entry price, log the error, exit the function and class="kw">return false
}
}
}
else
{
newEntryPrice = currentEntryPrice; class=class="str">"cmt">//-- Do not modify the entry price
}
if(orderType == ORDER_TYPE_BUY_STOP || orderType == ORDER_TYPE_BUY_LIMIT)
{
if(newSl == class="num">0)
{
newSlPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the sl
}
else
{
newSlPrice = newEntryPrice - (newSl * symbolPoint);
}
if(newTp == class="num">0)
{
newTpPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the tp
}
else
{
newTpPrice = newEntryPrice + (newTp * symbolPoint);
}
class=class="str">"cmt">//-- Check the validity of the newSlPrice
if(newSlPrice > class="num">0 && newEntryPrice - newSlPrice < symbolStopLevel * symbolPoint)
{
Print(
"\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType),挂单改止损止盈的价位校验逻辑
在 MT5 里修改挂单的 SL/TP,不能只算价格差,还得过经纪商的最小止损距离(symbolStopLevel * symbolPoint)。这段代码针对 SELL_STOP / SELL_LIMIT 两类卖单,先把传入的 newSl、newTp 点数换算成绝对价:newSlPrice = newEntryPrice + newSl*point,newTpPrice = newEntryPrice - newTp*point;若传 0 则代表移除对应挂单止损或止盈。 校验环节最容易踩坑:卖单的 SL 必须在入场价上方、且距离不得小于最小停损;TP 必须在入场价下方、同样不能贴着 symbolStopLevel。若 newSlPrice - newEntryPrice < symbolStopLevel*symbolPoint,或 newEntryPrice - newTpPrice < symbolStopLevel*symbolPoint,函数直接 Print 报错并返回 false,不往交易服务器发请求。 外汇与贵金属品种点值小、StopLevel 可能随流动性跳动,实盘里这类校验失败概率不低。开 MT5 把 symbolStopLevel 和 symbolDigits 打印出来,对照你打算设的挂单距离,能提前避开大部分『无效挂单价』退回。
if(orderType == ORDER_TYPE_SELL_STOP || orderType == ORDER_TYPE_SELL_LIMIT) { if(newSl == class="num">0) { newSlPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the sl } else { newSlPrice = newEntryPrice + (newSl * symbolPoint); } if(newTp == class="num">0) { newTpPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the tp } else { newTpPrice = newEntryPrice - (newTp * symbolPoint); } class=class="str">"cmt">//-- Check the validity of the newSlPrice if(newSlPrice > class="num">0 && newSlPrice - newEntryPrice < symbolStopLevel * symbolPoint) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType), ". (Reason --> INVALID NEW SL PRICE: ", DoubleToString(newSlPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid sl price, log the error, exit the function and class="kw">return false } class=class="str">"cmt">//-- Check the validity of the newTpPrice if(newTpPrice > class="num">0 && newEntryPrice - newTpPrice < symbolStopLevel * symbolPoint) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType), ". (Reason --> INVALID NEW TP PRICE: ", DoubleToString(newTpPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid tp price, log the error, exit the function and class="kw">return false } } ZeroMemory(tradeRequest); ZeroMemory(tradeResult);
「挂单改价与止损止盈的重发逻辑」
在 MT5 里修改挂单的入场价、止损和止盈,核心是把 tradeRequest 的 action 设为 TRADE_ACTION_MODIFY,并填好 order、symbol、price、sl、tp 几个字段。deviation 直接取品种点差的两倍(SymbolInfoInteger(SYMBOL_SPREAD)*2),避免因为微小滑点被服务器拒单。 下面这段是实际发出修改请求的循环骨架:最多重试 MAX_ORDER_RETRIES 次,每次先 OrderSend,若 retcode 是 10008(已成交)或 10009(已接受)就打印成功并 return true;失败则交给 ErrorAdvisor 判断是否可重试,不可重试或 EA 被停止就 return false。注意失败时 Sleep(ORDER_RETRY_DELAYS) 做短暂避让,别把交易服务器打爆。 外层函数 ModifyPendingOrderByTicket 先调 TradingIsAllowed() 确认自动交易开关没被关,再用 OrderSelect(orderTicket) 锁定目标挂单。外汇与贵金属杠杆高、点差跳变频繁,修改挂单可能在毫秒级失效,建议在策略测试器里用真实点差回放验证这套重试是否真能降低改单失败率。
tradeRequest.action = TRADE_ACTION_MODIFY; class=class="str">"cmt">//-- Trade operation type for modifying the pending order tradeRequest.order = orderTicket; tradeRequest.symbol = orderSymbol; tradeRequest.price = newEntryPrice; tradeRequest.sl = newSlPrice; tradeRequest.tp = newTpPrice; tradeRequest.deviation = SymbolInfoInteger(orderSymbol, SYMBOL_SPREAD) * class="num">2; ResetLastError(); class=class="str">"cmt">//--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function for(class="type">int loop = class="num">0; loop <= MAX_ORDER_RETRIES; loop++) class=class="str">"cmt">//-- try modifying the price open, sl, and tp until the request is successful { class=class="str">"cmt">//--- send order to the trade server if(OrderSend(tradeRequest, tradeResult)) { class=class="str">"cmt">//-- Confirm order execution if(tradeResult.retcode == class="num">10008 || tradeResult.retcode == class="num">10009) { PrintFormat("Successfully modified Pending Order: #%I64d %s %s", orderTicket, orderSymbol, EnumToString(orderType)); PrintFormat("retcode=%u runtime_code=%u", tradeResult.retcode, GetLastError()); Print("_______________________________________________________________________________________\r\n\r\n"); class="kw">return(true); class=class="str">"cmt">//-- exit function class=class="str">"cmt">//break; //--- success - order placed ok. exit for loop } } else class=class="str">"cmt">//-- Order request failed { class=class="str">"cmt">//-- order not sent or critical error found if(!ErrorAdvisor(__FUNCTION__, orderSymbol, tradeResult.retcode) || IsStopped()) { PrintFormat("ERROR modifying Pending Order: #%I64d %s %s", orderTicket, orderSymbol, EnumToString(orderType)); Print("_______________________________________________________________________________________\r\n\r\n"); class="kw">return(false); class=class="str">"cmt">//-- exit function class=class="str">"cmt">//break; //-- exit for loop Sleep(ORDER_RETRY_DELAYS);class=class="str">"cmt">//-- Small pause before retrying to avoid overwhelming the trade server } } } class="type">bool ModifyPendingOrderByTicket(class="type">ulong orderTicket, class="type">class="kw">double newEntryPrice, class="type">int newSl, class="type">int newTp) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class=class="str">"cmt">//--- Confirm and select the order using the provided orderTicket ResetLastError(); class=class="str">"cmt">//--- Reset error cache incase of ticket selection errors if(OrderSelect(orderTicket)) { class=class="str">"cmt">//---Order selected
◍ 挂单改价前的止损位合法性校验
选中挂单后,先把订单属性抓到本地变量里:交易品种、开仓价、初始手数、当前 SL/TP、订单类型与实时价。这一步用 OrderGetString / OrderGetDouble / OrderGetInteger 把服务器侧的挂单快照落盘,后面算新止损止盈才有基准。 接着用 SymbolInfoInteger 和 SymbolInfoDouble 拉品种参数:symbolDigits 是报价小数位,symbolStopLevel 是经纪商要求的止损最小距离(以 point 计),symbolPoint 是单点价值,spread 是当前点差。外汇与贵金属点差和 stop level 随时变,硬编常数会直接吃不到单。 对 BUY_STOP 与 SELL_LIMIT 这类突破型挂单,新挂单价不能太贴近卖价。代码里的判定是:SYMBOL_ASK + symbolStopLevel*point 必须大于 newEntryPrice - spread*point,否则判定为无效挂单价直接 return false。EURUSD 常见 stop level 为 0、spread 在 10~30 point,XAUUSD 的 stop level 可能到 100 point 以上,改价前务必先打印这几个值验证。 这段逻辑的价值在于:它把‘经纪商不允许的挂单价’拦在发单前,而不是等 TradeServer 回 reject。开 MT5 把 symbolStopLevel 和 spread 打印出来,对照你想改的 newEntryPrice 算一遍,能省掉大量无谓的改单报错。
Print("\r\n_______________________________________________________________________________________"); Print(__FUNCTION__, ": Order with ticket:", orderTicket, " selected and ready to set SLTP."); } else { Print("\r\n_______________________________________________________________________________________"); Print(__FUNCTION__, ": Selecting order with ticket:", orderTicket, " failed. ERROR: ", GetLastError()); class="kw">return(false); class=class="str">"cmt">//-- Exit the function } class=class="str">"cmt">//-- create variables to store the calculated tp and sl prices to send to the trade server class="type">class="kw">double newTpPrice = class="num">0.0, newSlPrice = class="num">0.0; class=class="str">"cmt">//--- Order ticket selected, save the order properties class="type">class="kw">string orderSymbol = OrderGetString(ORDER_SYMBOL); class="type">class="kw">double currentEntryPrice = OrderGetDouble(ORDER_PRICE_OPEN); class="type">class="kw">double volume = OrderGetDouble(ORDER_VOLUME_INITIAL); class="type">class="kw">double currentOrderSlPrice = OrderGetDouble(ORDER_SL); class="type">class="kw">double currentOrderTpPrice = OrderGetDouble(ORDER_TP); ENUM_ORDER_TYPE orderType = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); class="type">class="kw">double orderPriceCurrent = OrderGetDouble(ORDER_PRICE_CURRENT); class=class="str">"cmt">//-- Get some information about the orders symbol class="type">int symbolDigits = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_DIGITS); class=class="str">"cmt">//-- Number of symbol decimal places class="type">int symbolStopLevel = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_TRADE_STOPS_LEVEL); class="type">class="kw">double symbolPoint = SymbolInfoDouble(orderSymbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_SPREAD); class=class="str">"cmt">//-- Check the validity of the newEntryPrice if(newEntryPrice > class="num">0.0) { if(orderType == ORDER_TYPE_BUY_STOP || orderType == ORDER_TYPE_SELL_LIMIT) { if( SymbolInfoDouble(orderSymbol, SYMBOL_ASK) + (symbolStopLevel * symbolPoint) > newEntryPrice - (spread * symbolPoint) ) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t MODIFY ", EnumToString(orderType), ". (Reason --> INVALID NEW ENTRY PRICE: ", DoubleToString(newEntryPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid new entry price, log the error, exit the function and class="kw">return false } } if(orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_SELL_STOP) {
挂单改价时的止损止盈边界校验
修改 BUY_STOP / BUY_LIMIT 挂单时,SL 与 TP 不是直接填绝对值,而是用入场价加减点数换算。代码里 newSlPrice = newEntryPrice - (newSl * symbolPoint),TP 则是 newEntryPrice + (newTp * symbolPoint),symbolPoint 是该品种每点价值,newSl / newTp 是离场距离的点数。 经纪商对挂单的止损距离有硬约束:symbolStopLevel 是最小止损点数。若 newSlPrice > 0 且 newEntryPrice - newSlPrice < symbolStopLevel * symbolPoint,说明止损太贴近入场,函数直接 Print 报错并返回 false,订单不会被改动。 TP 同理,newTpPrice - newEntryPrice 也必须大于 symbolStopLevel * symbolPoint,否则返回 false。这两道判断保证了挂单的 SL/TP 至少离开场价一个 stop level,避免被服务器拒单。 实盘里若你用 EA 批量挪挂单,常踩的坑就是点差扩大后 symbolStopLevel 变相抬高,原来合法的 SL 突然变非法。开 MT5 把 symbolStopLevel 和当前 symbolPoint 打印出来,对照你 EA 里的 newSl 参数,能立刻看出哪些挂单会在波动期改单失败。外汇与贵金属杠杆高,改单逻辑出错可能让保护性止损失效,需先在模拟盘验证。
if(orderType == ORDER_TYPE_BUY_STOP || orderType == ORDER_TYPE_BUY_LIMIT) { if(newSl == class="num">0) { newSlPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the sl } else { newSlPrice = newEntryPrice - (newSl * symbolPoint); } if(newTp == class="num">0) { newTpPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the tp } else { newTpPrice = newEntryPrice + (newTp * symbolPoint); } class=class="str">"cmt">//-- Check the validity of the newSlPrice if(newSlPrice > class="num">0 && newEntryPrice - newSlPrice < symbolStopLevel * symbolPoint) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType), ". (Reason --> INVALID NEW SL PRICE: ", DoubleToString(newSlPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid sl price, log the error, exit the function and class="kw">return false } class=class="str">"cmt">//-- Check the validity of the newTpPrice if(newTpPrice > class="num">0 && newTpPrice - newEntryPrice < symbolStopLevel * symbolPoint) { Print( "\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType), ". (Reason --> INVALID NEW TP PRICE: ", DoubleToString(newTpPrice, symbolDigits), ")\r\n" ); class="kw">return(false); class=class="str">"cmt">//-- Invalid tp price, log the error, exit the function and class="kw">return false } }
「改单前先卡死止损止盈的合规距离」
在 MT5 里用 EA 改挂单或持仓的 SL/TP,最容易被 broker 拒掉的原因就是价格离市价太近。下面这段代码把 newSl、newTp 为 0 的情况当成「移除对应挂单线」,否则按 entry 加减点数乘 symbolPoint 算出新价。 计算完新价后,两段 if 分别校验止损和止盈是否满足最小距离:newSlPrice 大于 0 时,它和 newEntryPrice 的差必须 ≥ symbolStopLevel * symbolPoint;止盈则反过来要求 newEntryPrice 减 newTpPrice 同样不小于这个阈值。symbolStopLevel 是 broker 在 SYMBOL_TRADE_STOPS_LEVEL 返回的点数,通常 EURUSD 在多数账户是 0,但黄金 XAUUSD 常返回 50~150 点,硬改会被 INVALID_STOPS 驳回。 校验不通过就 Print 出函数名、订单类型和具体价格,然后 return(false) 中断修改。外汇与贵金属杠杆高、点差跳变快,这类距离校验最好在发单前本地跑一遍,别等 OrderSend 返回错误才查。 代码后半段把改单前的 ticket、volume、当前开仓价与新提议价、当前 SL/TP 与新 SL/TP 拼成字符串,方便在日志里对照。实盘前你可以用这段拼串逻辑接 PrintFormat 直接看差异,确认新价真的跨过了 stop level 再提交。
{
if(newSl == class="num">0)
{
newSlPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the sl
}
else
{
newSlPrice = newEntryPrice + (newSl * symbolPoint);
}
if(newTp == class="num">0)
{
newTpPrice = class="num">0.0; class=class="str">"cmt">//-- Remove the tp
}
else
{
newTpPrice = newEntryPrice - (newTp * symbolPoint);
}
class=class="str">"cmt">//-- Check the validity of the newSlPrice
if(newSlPrice > class="num">0 && newSlPrice - newEntryPrice < symbolStopLevel * symbolPoint)
{
Print(
"\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType),
". (Reason --> INVALID NEW SL PRICE: ", DoubleToString(newSlPrice, symbolDigits), ")\r\n"
);
class="kw">return(false); class=class="str">"cmt">//-- Invalid sl price, log the error, exit the function and class="kw">return false
}
class=class="str">"cmt">//-- Check the validity of the newTpPrice
if(newTpPrice > class="num">0 && newEntryPrice - newTpPrice < symbolStopLevel * symbolPoint)
{
Print(
"\r\n", __FUNCTION__, ": Can&class="macro">#x27;t modify ", EnumToString(orderType),
". (Reason --> INVALID NEW TP PRICE: ", DoubleToString(newTpPrice, symbolDigits), ")\r\n"
);
class="kw">return(false); class=class="str">"cmt">//-- Invalid tp price, log the error, exit the function and class="kw">return false
}
}
class=class="str">"cmt">//-- Print order properties before modification
class="type">class="kw">string orderProperties = "--> " + orderSymbol + " " + EnumToString(orderType) + " SLTP Modification Details" +
" <--\r\n";
orderProperties += "------------------------------------------------------------\r\n";
orderProperties += "Ticket: " + (class="type">class="kw">string)orderTicket + "\r\n";
orderProperties += "Volume: " + DoubleToString(volume, symbolDigits) + "\r\n";
orderProperties += "Price Open: " + DoubleToString(currentEntryPrice, symbolDigits) +
" -> New Proposed Price Open: " + DoubleToString(newEntryPrice, symbolDigits) + "\r\n";
orderProperties += "Current SL: " + DoubleToString(currentOrderSlPrice, symbolDigits) +
" -> New Proposed SL: " + DoubleToString(newSlPrice, symbolDigits) + "\r\n";
orderProperties += "Current TP: " + DoubleToString(currentOrderTpPrice, symbolDigits) +◍ 挂单改价前的打印与重发机制
在真正向交易服务器提交修改请求前,先把计算出的新入场价、止损、止盈拼成可读字符串用 Print 输出,能让你在 MT5 专家日志里当场核对数值有没有被精度或符号位数搞错。DoubleToString 带 symbolDigits 参数,就是按当前品种小数位格式化,避免 EURUSD 出现 1.0850000 这类多零乱象。 修改结构体 tradeRequest 必须先 ZeroMemory 清零,否则上一次残留的 action 或 price 可能污染本次 TRADE_ACTION_MODIFY。deviation 取 SYMBOL_SPREAD * 2,意味着允许偏离两倍点差成交,在黄金跳空时可能提高改单成功率,但也可能以略差价成交。 实际发送放在 for 循环里重试,上限由 MAX_ORDER_RETRIES 控制。OrderSend 返回 true 且 retcode 为 10008(已成交)或 10009(已受理)才视为成功直接 return;否则走下一轮重试。外汇与贵金属杠杆高,改挂单 SL/TP 若在网络延迟中失败,持仓风险可能瞬间放大,建议在日志确认成功前不要手动平仓。
orderProperties += " -> New Proposed TP: " + DoubleToString(newTpPrice, symbolDigits) + "\r\n"; orderProperties += "Comment: " + OrderGetString(ORDER_COMMENT) + "\r\n"; orderProperties += "Magic Number: " + (class="type">class="kw">string)OrderGetInteger(ORDER_MAGIC) + "\r\n"; orderProperties += "---"; class=class="str">"cmt">//-- Print verified order properties before modification orderProperties += "--> Validated and Confirmed NewEntry, SL, and TP Prices: <--\r\n"; orderProperties += "Order Price Current: " + DoubleToString(orderPriceCurrent, symbolDigits) + "\r\n"; orderProperties += "Current Entry Price: " + DoubleToString(currentEntryPrice, symbolDigits) + ", New Entry Price: " + DoubleToString(newEntryPrice, symbolDigits) + "\r\n"; orderProperties += "Current SL: " + DoubleToString(currentOrderSlPrice, symbolDigits) + " -> New SL: " + DoubleToString(newSlPrice, symbolDigits) + "\r\n"; orderProperties += "Current TP: " + DoubleToString(currentOrderTpPrice, symbolDigits) + " -> New TP: " + DoubleToString(newTpPrice, symbolDigits) + "\r\n"; Print(orderProperties); class=class="str">"cmt">//-- reset the the tradeRequest and tradeResult values by zeroing them ZeroMemory(tradeRequest); ZeroMemory(tradeResult); class=class="str">"cmt">//-- initialize the parameters to set the sltp tradeRequest.action = TRADE_ACTION_MODIFY; class=class="str">"cmt">//-- Trade operation type for modifying the pending order tradeRequest.order = orderTicket; tradeRequest.symbol = orderSymbol; tradeRequest.price = newEntryPrice; tradeRequest.sl = newSlPrice; tradeRequest.tp = newTpPrice; tradeRequest.deviation = SymbolInfoInteger(orderSymbol, SYMBOL_SPREAD) * class="num">2; ResetLastError(); class=class="str">"cmt">//--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function for(class="type">int loop = class="num">0; loop <= MAX_ORDER_RETRIES; loop++) class=class="str">"cmt">//-- try modifying the price open, sl, and tp until the request is successful { class=class="str">"cmt">//--- send order to the trade server if(OrderSend(tradeRequest, tradeResult)) { class=class="str">"cmt">//-- Confirm order execution if(tradeResult.retcode == class="num">10008 || tradeResult.retcode == class="num">10009) { PrintFormat("Successfully modified Pending Order: #%I64d %s %s", orderTicket, orderSymbol, EnumToString(orderType)); PrintFormat("retcode=%u runtime_code=%u", tradeResult.retcode, GetLastError()); Print("_____________________________________________________________________________________\r\n\r\n"); class="kw">return(true); class=class="str">"cmt">//-- exit function class=class="str">"cmt">//break; //--- success - order placed ok. exit for loop } }
挂单改单失败后的退出与重试间隙
这段代码是修改挂单(Pending Order)逻辑里的失败分支。当订单请求没发出去,或遇到了关键错误,程序不会盲目继续,而是先判断是否要直接退出函数。 判断条件里调用了 ErrorAdvisor(__FUNCTION__, orderSymbol, tradeResult.retcode) 做错误归类,同时检查 IsStopped() 看脚本是否被用户终止。只要二者有一个为真,就打印带订单 ticket、品种和类型的错误信息,并立即 return(false) 离开当前函数,不再往下走循环。 注释里还留了一行 Sleep(ORDER_RETRY_DELAYS),本意是在重试前让出一点时间,避免频繁请求把交易服务器压垮。但在当前 return(false) 之后,这行 Sleep 实际已不可达——若你打算做重试机制,得把 return 改成 break 并挪到 Sleep 之后,否则延迟形同虚设。外汇与贵金属挂单修改受点差和拒绝码影响大,这类错误处理直接决定 EA 在波动期的存活率。
else class=class="str">"cmt">//-- Order request failed { class=class="str">"cmt">//-- order not sent or critical error found if(!ErrorAdvisor(__FUNCTION__, orderSymbol, tradeResult.retcode) || IsStopped()) { PrintFormat("ERROR modifying Pending Order: #%I64d %s %s", orderTicket, orderSymbol, EnumToString(orderType)); Print("_______________________________________________________________________________________\r\n\r\n"); class="kw">return(false); class=class="str">"cmt">//-- exit function class=class="str">"cmt">//break; //-- exit for loop Sleep(ORDER_RETRY_DELAYS);class=class="str">"cmt">//-- Small pause before retrying to avoid overwhelming the trade server } } } class="kw">return(false); }
「用单号精准撤掉挂单的实现」
在 MT5 的 EA 逻辑里,按唯一单号撤挂单比按品种扫单更稳妥,能避开误删其他策略挂单的风险。DeletePendingOrderByTicket 只吃一个 ulong 参数 orderTicket,返回 bool:true 代表服务器已确认删除,false 代表禁止交易、选单失败或后续发单异常。 函数先调 TradingIsAllowed() 确认算法交易开关,若平台禁用自动交易直接 return false,不浪费一次服务器请求。随后 ResetLastError() 清掉旧错误码,再用 OrderSelect(orderTicket) 锁定目标挂单;选不中就打印错误码并退出,选中有就先打印分隔线和单号就绪提示。 选单成功后,代码把品种、手数、小数点位、订单类型捞出来,拼一段包含开价/SL/TP/注释/魔术码的明细打到日志。这样做的好处是:撤单后若账户里还留着奇怪仓位,你能凭日志反推是哪张单没删干净。下面这段是函数前半部分的真实写法,注意 tradeRequest 和 tradeResult 用 ZeroMemory 清零,再填 TRADE_ACTION_REMOVE 动作。 外汇与贵金属挂单删除受点差跳空和经纪商拒绝影响,重试机制只是提高概率,不保证每次秒删。
class="type">bool DeletePendingOrderByTicket(class="type">ulong orderTicket) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class=class="str">"cmt">//--- Confirm and select the order using the provided orderTicket ResetLastError(); class=class="str">"cmt">//--- Reset error cache incase of ticket selection errors if(OrderSelect(orderTicket)) { class=class="str">"cmt">//---Order selected Print("..........................................................................................."); Print(__FUNCTION__, ": Order with ticket:", orderTicket, " selected and ready to be deleted."); } else { Print("..........................................................................................."); Print(__FUNCTION__, ": Selecting order with ticket:", orderTicket, " failed. ERROR: ", GetLastError()); class="kw">return(false); class=class="str">"cmt">//-- Exit the function } class=class="str">"cmt">//--- Order ticket selected, save the order properties class="type">class="kw">string orderSymbol = OrderGetString(ORDER_SYMBOL); class="type">class="kw">double orderVolume = OrderGetDouble(ORDER_VOLUME_CURRENT); class="type">int symbolDigits = (class="type">int)SymbolInfoInteger(orderSymbol, SYMBOL_DIGITS); ENUM_ORDER_TYPE orderType = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); class=class="str">"cmt">//-- Print order properties before deleting it class="type">class="kw">string orderProperties; orderProperties += "-- " + orderSymbol + " " + EnumToString(orderType) + " Details" + " -------------------------------------------------------------\r\n"; orderProperties += "Ticket: " + (class="type">class="kw">string)orderTicket + "\r\n"; orderProperties += "Volume: " + DoubleToString(orderVolume) + "\r\n"; orderProperties += "Price Open: " + DoubleToString(OrderGetDouble(ORDER_PRICE_OPEN), symbolDigits) + "\r\n"; orderProperties += "SL: " + DoubleToString(OrderGetDouble(ORDER_SL), symbolDigits) + "\r\n"; orderProperties += "TP: " + DoubleToString(OrderGetDouble(ORDER_TP), symbolDigits) + "\r\n"; orderProperties += "Comment: " + OrderGetString(ORDER_COMMENT) + "\r\n"; orderProperties += "Magic Number: " + (class="type">class="kw">string)OrderGetInteger(ORDER_MAGIC) + "\r\n"; orderProperties += "_______________________________________________________________________________________"; Print(orderProperties); class=class="str">"cmt">//-- reset the the tradeRequest and tradeResult values by zeroing them ZeroMemory(tradeRequest); ZeroMemory(tradeResult); class=class="str">"cmt">//-- initialize the trade reqiest parameters to class="kw">delete the order tradeRequest.action = TRADE_ACTION_REMOVE; class=class="str">"cmt">//-- Trade operation type for deleting an order
◍ 挂单删除的重试与错误处理
在 MT5 的 EA 逻辑里,删除一笔未成交挂单不能只发一次请求就完事。网络抖动或交易服务器瞬时繁忙都可能让 OrderSend 返回失败,因此用循环包裹删除动作、并限制最大重试次数是更稳的做法。 tradeRequest.order 先绑定待删订单的 ticket,随后 ResetLastError 清空错误缓存,避免上一次残留的运行时错误码污染本次诊断。循环上限由 MAX_ORDER_RETRIES 控制,每次迭代调用 OrderSend,成功且 retcode 为 10008(已成交确认)或 10009(已完成)即打印明细并返回 true。 若发送失败,则进入 else 分支交由 ErrorAdvisor 判断该错误是否可重试;若不可重试或 IsStopped 为真,直接打印错误并返回 false。可重试时 Sleep(ORDER_RETRY_DELAYS) 做短暂避让再进入下一轮,降低对交易服务器的冲击。外汇与贵金属交易杠杆高、滑点风险大,这类重试机制只解决通信层鲁棒性,不替代仓位与方向判断。
tradeRequest.order = orderTicket; ResetLastError(); class=class="str">"cmt">//--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function for(class="type">int loop = class="num">0; loop <= MAX_ORDER_RETRIES; loop++) class=class="str">"cmt">//-- try deleting the order until the request is successful { class=class="str">"cmt">//--- send order to the trade server if(OrderSend(tradeRequest, tradeResult)) { class=class="str">"cmt">//-- Confirm order execution if(tradeResult.retcode == class="num">10008 || tradeResult.retcode == class="num">10009) { Print(__FUNCTION__, "_________________________________________________________________________"); PrintFormat("Successfully deleted order #%I64d %s %s", orderTicket, orderSymbol, EnumToString(orderType)); PrintFormat("retcode=%u runtime_code=%u", tradeResult.retcode, GetLastError()); Print("_______________________________________________________________________________________"); class="kw">return(true); class=class="str">"cmt">//-- exit function class=class="str">"cmt">//break; //--- success - order placed ok. exit for loop } } else class=class="str">"cmt">//-- order deleting request failed { class=class="str">"cmt">//-- order not sent or critical error found if(!ErrorAdvisor(__FUNCTION__, orderSymbol, tradeResult.retcode) || IsStopped()) { Print(__FUNCTION__, "_________________________________________________________________________"); PrintFormat("ERROR deleting order #%I64d %s %s", orderTicket, orderSymbol, EnumToString(orderType)); Print("_______________________________________________________________________________________"); class="kw">return(false); class=class="str">"cmt">//-- exit function class=class="str">"cmt">//break; //-- exit for loop Sleep(ORDER_RETRY_DELAYS);class=class="str">"cmt">//-- Small pause before retrying to avoid overwhelming the trade server } } } class="kw">return(false); }
清场挂单的两种入口与重试兜底
在 MT5 EA 里批量撤挂单,核心是一个 DeleteAllPendingOrders 函数。它带两个可选参数:symbol 默认 ALL_SYMBOLS,不填就扫全品种;magicNumber 默认 0,不填就不管幻数通杀。返回 bool,true 代表目标挂单全清掉,false 代表部分或全失败。 函数进场先查 TradingIsAllowed(),算法交易被禁就直接 return false,不会去碰服务器。随后用 OrdersTotal() 拿到订单数,按 ticket 逐张取 ORDER_SYMBOL 和 ORDER_MAGIC 做过滤,不匹配就 continue,匹配就调 DeletePendingOrderByTicket 删单。 删完并不信任一次循环。它用 while 卡 SymbolOrdersTotal(symbol,magicNumber)>0 做回调重试,每次 Sleep(ORDER_RETRY_DELAYS) 给交易服务器喘息;breakerBreaker 计数超 MAX_ORDER_RETRIES 或撞到严重错误、脚本被停,就 break 跳出,避免死锁。 重载版 DeleteAllPendingOrders() 无参,本质就是拿 ALL_SYMBOLS 和 0 调一遍带参版,适合「账号所有挂单一把清」的场景。外汇与贵金属杠杆高,批量撤单前务必确认不会误杀正在等待触发的策略挂单,否则可能错失入场或暴露敞口。 下面这段是原函数与重载版的完整实现,注意看逐行注释里的退出条件和重试节奏: bool DeleteAllPendingOrders(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export { //-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { return(false); //--- algo trading is disabled, exit function } bool returnThis = false; //-- Scan for symbol and magic number for the specified orders and close them int totalOpenOrders = OrdersTotal(); for(int x = 0; x < totalOpenOrders; x++) { //--- Get order properties ulong orderTicket = OrderGetTicket(x); //-- Get ticket to select the order string selectedSymbol = OrderGetString(ORDER_SYMBOL); ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); //-- Filter orders by symbol and magic number if(
| (symbol != ALL_SYMBOLS && symbol != selectedSymbol) |
|---|
(magicNumber != 0 && orderMagicNo != magicNumber) ) { continue; } //-- Delete the order DeletePendingOrderByTicket(orderTicket); } //-- Confirm that we have closed all the orders being targeted int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop while(SymbolOrdersTotal(symbol, magicNumber) > 0) { breakerBreaker++; DeleteAllPendingOrders(symbol, magicNumber); //-- We still have some open orders, do a function callback Sleep(ORDER_RETRY_DELAYS); //-- Micro sleep to pace the execution and give some time to the trade server //-- Check for critical errors so that we exit the loop if we run into trouble
| if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) | IsStopped() | breakerBreaker > MAX_ORDER_RETRIES) |
|---|
{ break; } } //-- Final confirmations that all targeted orders have been closed if(SymbolOrdersTotal(symbol, magicNumber) == 0) { returnThis = true; //-- Save this status for the function return value } return(returnThis); } bool DeleteAllPendingOrders() export { return(DeleteAllPendingOrders(ALL_SYMBOLS, 0)); }
class="type">bool DeleteAllPendingOrders(class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">bool returnThis = false; class=class="str">"cmt">//-- Scan for symbol and magic number for the specified orders and close them class="type">int totalOpenOrders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOrders; x++) { class=class="str">"cmt">//--- Get order properties class="type">ulong orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the order class="type">class="kw">string selectedSymbol = OrderGetString(ORDER_SYMBOL); class="type">ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); class=class="str">"cmt">//-- Filter orders by symbol and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && orderMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Delete the order DeletePendingOrderByTicket(orderTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the orders being targeted class="type">int breakerBreaker = class="num">0; class=class="str">"cmt">//-- Variable that safeguards and makes sure we are not locked in an infinite loop while(SymbolOrdersTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; DeleteAllPendingOrders(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open orders, do a function callback Sleep(ORDER_RETRY_DELAYS); class=class="str">"cmt">//-- Micro sleep to pace the execution and give some time to the trade server class=class="str">"cmt">//-- Check for critical errors so that we exit the loop if we run into trouble if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > MAX_ORDER_RETRIES) { break; } } class=class="str">"cmt">//-- Final confirmations that all targeted orders have been closed if(SymbolOrdersTotal(symbol, magicNumber) == class="num">0) { returnThis = true; class=class="str">"cmt">//-- Save this status for the function class="kw">return value } class="kw">return(returnThis); } class="type">bool DeleteAllPendingOrders() class="kw">export { class="kw">return(DeleteAllPendingOrders(ALL_SYMBOLS, class="num">0)); }
「清掉指定品种的挂单买停」
在 MT5 的 EA 逻辑里,批量撤掉买入止损(Buy Stop)不能只靠一次循环扫单就完事。DeleteAllBuyStops 这个函数先卡一道 TradingIsAllowed(),算法交易被禁就直接返 false,避免在无交易权限时瞎操作。 它接收两个可选参数:symbol 默认 ALL_SYMBOLS,不填就扫所有品种;magicNumber 默认 0,不填则不限幻数。过滤条件写得很直白——品种不对、类型不是 ORDER_TYPE_BUY_STOP、幻数不匹配,就 continue 跳过。 第一次扫描若没删干净,函数用 while 配合 SymbolBuyStopOrdersTotal 做回调重试,每次 Sleep(ORDER_RETRY_DELAYS) 给服务器喘息。breakerBreaker 计数器顶在 MAX_ORDER_RETRIES 之上就强制 break,防止陷入死循环。最终目标订单数为 0 才返 true。 代码逐行拆一下关键点:OrderGetTicket(x) 拿订单票据号,OrderGetString(ORDER_SYMBOL) 取品种,OrderGetInteger 分别取幻数和类型;过滤后调 DeletePendingOrderByTicket 删单;重试里用 ErrorAdvisor 和 IsStopped 拦严重错误与脚本停止信号。 外汇与贵金属挂单撤单受点差和服务器延迟影响,重试机制只是降低漏删概率,不保证瞬时全部成交式撤销。
class="type">bool DeleteAllBuyStops(class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">bool returnThis = false; class=class="str">"cmt">//-- Scan for symbol and magic number specific buy stop orders and close them class="type">int totalOpenOrders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOrders; x++) { class=class="str">"cmt">//--- Get order properties class="type">ulong orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the order class="type">class="kw">string selectedSymbol = OrderGetString(ORDER_SYMBOL); class="type">ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); class="type">ulong orderType = OrderGetInteger(ORDER_TYPE); class=class="str">"cmt">//-- Filter order by symbol, type and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (orderType != ORDER_TYPE_BUY_STOP) || (magicNumber != class="num">0 && orderMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Close the order DeletePendingOrderByTicket(orderTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the buy stop orders being targeted class="type">int breakerBreaker = class="num">0; class=class="str">"cmt">//-- Variable that safeguards and makes sure we are not locked in an infinite loop while(SymbolBuyStopOrdersTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; DeleteAllBuyStops(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open buy stop orders, do a function callback Sleep(ORDER_RETRY_DELAYS); class=class="str">"cmt">//-- Micro sleep to pace the execution and give some time to the trade server class=class="str">"cmt">//-- Check for critical errors so that we exit the loop if we run into trouble if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > MAX_ORDER_RETRIES) { break; } } if(SymbolBuyStopOrdersTotal(symbol, magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
◍ 清掉指定品种的买入限价挂单
这个函数专门用来删除某个交易品种、某个幻数下的所有买入限价挂单(Buy Limit)。返回布尔值:true 代表目标挂单全部删干净了,false 代表有部分没删掉或者中途撞上严重错误。 函数开头先调 TradingIsAllowed() 确认 EA 被允许交易,自动交易一关就直接 return false 退出,避免瞎操作。接着用 OrdersTotal() 拿到挂单总数,按 ticket 逐一取符号、幻数、类型,只保留「符号匹配(或全符号)、类型是 ORDER_TYPE_BUY_LIMIT、幻数匹配(或幻数为0)」的订单去调 DeletePendingOrderByTicket 删除。 删完并不是直接收工,而是用 while 循环复查 SymbolBuyLimitOrdersTotal 是否归零。只要还有残留就回调自身再删,每次 Sleep(ORDER_RETRY_DELAYS) 给服务器喘息;breakerBreaker 计数器超过 MAX_ORDER_RETRIES 或撞上关键错误、EA 被停,就 break 跳出。最后确认挂单数为 0 才把 returnThis 置 true。 实盘里外汇和贵金属波动快,挂单没清干净可能导致策略重复开仓,建议把 MAX_ORDER_RETRIES 设在 5~10 之间,并在 MT5 策略测试器里用不同幻数跑一遍验证回调不会死循环。
class="type">bool DeleteAllBuyLimits(class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">bool returnThis = false; class=class="str">"cmt">//-- Scan for symbol and magic number specific buy limit orders and close them class="type">int totalOpenOrders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOrders; x++) { class=class="str">"cmt">//--- Get order properties class="type">ulong orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the order class="type">class="kw">string selectedSymbol = OrderGetString(ORDER_SYMBOL); class="type">ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); class="type">ulong orderType = OrderGetInteger(ORDER_TYPE); class=class="str">"cmt">//-- Filter order by symbol, type and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (orderType != ORDER_TYPE_BUY_LIMIT) || (magicNumber != class="num">0 && orderMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Close the order DeletePendingOrderByTicket(orderTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the buy limit orders being targeted class="type">int breakerBreaker = class="num">0; class=class="str">"cmt">//-- Variable that safeguards and makes sure we are not locked in an infinite loop while(SymbolBuyLimitOrdersTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; DeleteAllBuyLimits(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open buy limit orders, do a function callback Sleep(ORDER_RETRY_DELAYS); class=class="str">"cmt">//-- Micro sleep to pace the execution and give some time to the trade server class=class="str">"cmt">//-- Check for critical errors so that we exit the loop if we run into trouble if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > MAX_ORDER_RETRIES) { break; } } if(SymbolBuyLimitOrdersTotal(symbol, magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
清掉指定品种与幻数的卖损挂单
DeleteAllSellStops 这个函数干一件事:把某个交易品种、某个幻数下的所有卖出止损挂单(SELL_STOP)全删掉。它返回 bool,true 代表目标卖损单已清空,false 代表没删干净或撞上了严重错误。 函数开头先调 TradingIsAllowed() 做交易许可检查,自动交易被禁就直接 return false,不往下走。参数里 symbol 默认 ALL_SYMBOLS、magicNumber 默认 0,意味着不传参就是全品种、不限幻数通杀。 核心逻辑先用 OrdersTotal() 拿挂单总数,再按 ticket 逐个取 ORDER_SYMBOL、ORDER_MAGIC、ORDER_TYPE 做过滤:品种不匹配、类型不是 ORDER_TYPE_SELL_STOP、幻数不对的统统 continue 跳过,命中的调 DeletePendingOrderByTicket 删除。 删完并不信邪,而是用 while 循环调 SymbolSellStopOrdersTotal 复核余数。只要还有卖损单,就递归回调自身并 Sleep(ORDER_RETRY_DELAYS) 让服务器喘口气;breakerBreaker 计数器超过 MAX_ORDER_RETRIES 或撞错、EA 被停,立刻 break 防死循环。最后余数为 0 才 return true。 开 MT5 把下面代码塞进你的 EA 公共区,配 TradingIsAllowed、DeletePendingOrderByTicket、SymbolSellStopOrdersTotal、ErrorAdvisor 几个依赖,就能直接跑。外汇和贵金属挂单删除受点差与服务器拒绝影响,删不干净的概率真实存在,别当必成。
class="type">bool DeleteAllSellStops(class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">bool returnThis = false; class=class="str">"cmt">//-- Scan for symbol and magic number specific sell stop orders and close them class="type">int totalOpenOrders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOrders; x++) { class=class="str">"cmt">//--- Get order properties class="type">ulong orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the order class="type">class="kw">string selectedSymbol = OrderGetString(ORDER_SYMBOL); class="type">ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); class="type">ulong orderType = OrderGetInteger(ORDER_TYPE); class=class="str">"cmt">//-- Filter order by symbol, type and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (orderType != ORDER_TYPE_SELL_STOP) || (magicNumber != class="num">0 && orderMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Close the order DeletePendingOrderByTicket(orderTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the sell stop orders being targeted class="type">int breakerBreaker = class="num">0; class=class="str">"cmt">//-- Variable that safeguards and makes sure we are not locked in an infinite loop while(SymbolSellStopOrdersTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; DeleteAllSellStops(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open sell stop orders, do a function callback Sleep(ORDER_RETRY_DELAYS); class=class="str">"cmt">//-- Micro sleep to pace the execution and give some time to the trade server class=class="str">"cmt">//-- Check for critical errors so that we exit the loop if we run into trouble if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > MAX_ORDER_RETRIES) { break; } } if(SymbolSellStopOrdersTotal(symbol, magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
「批量撤掉卖出限价挂单的实现逻辑」
在 MT5 EA 里清理某一品种、某一幻数下的全部卖出限价挂单,核心动作是遍历订单池并按条件过滤后逐个删除。函数返回 true 仅当目标卖限挂单数量降为 0,否则返回 false,调用方应据此判断撤单是否彻底。 函数开头先检查 TradingIsAllowed(),若自动交易被禁直接返回 false,避免在无交易权限时空跑。随后用 OrdersTotal() 拿总订单数,按序号取 ticket、品种、幻数、类型,凡品种不符、类型不是 ORDER_TYPE_SELL_LIMIT、或幻数不匹配的统统 continue 跳过。 删除动作交给 DeletePendingOrderByTicket(),但一次遍历未必能删干净——服务器延迟或部分拒绝都可能残留。于是用 while 循环配合 SymbolSellLimitOrdersTotal() 复查,回调自身并 Sleep(ORDER_RETRY_DELAYS) pacing,breakerBreaker 计数超过 MAX_ORDER_RETRIES 或遇到严重错误、EA 被停止就 break。最终若残留为 0 才置 returnThis = true。外汇与贵金属挂单删除受 broker 执行策略影响,撤单失败概率真实存在,实盘前建议在策略测试器用历史数据验证重试上限设定。
class="type">bool DeleteAllSellLimits(class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">bool returnThis = false; class=class="str">"cmt">//-- Scan for symbol and magic number specific sell limit orders and close them class="type">int totalOpenOrders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOrders; x++) { class=class="str">"cmt">//--- Get order properties class="type">ulong orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the order class="type">class="kw">string selectedSymbol = OrderGetString(ORDER_SYMBOL); class="type">ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); class="type">ulong orderType = OrderGetInteger(ORDER_TYPE); class=class="str">"cmt">//-- Filter order by symbol, type and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (orderType != ORDER_TYPE_SELL_LIMIT) || (magicNumber != class="num">0 && orderMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Close the order DeletePendingOrderByTicket(orderTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the sell limit orders being targeted class="type">int breakerBreaker = class="num">0; class=class="str">"cmt">//-- Variable that safeguards and makes sure we are not locked in an infinite loop while(SymbolSellLimitOrdersTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; DeleteAllSellLimits(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open sell limit orders, do a function callback Sleep(ORDER_RETRY_DELAYS); class=class="str">"cmt">//-- Micro sleep to pace the execution and give some time to the trade server class=class="str">"cmt">//-- Check for critical errors so that we exit the loop if we run into trouble if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > MAX_ORDER_RETRIES) { break; } } if(SymbolSellLimitOrdersTotal(symbol, magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
◍ 按幻数清掉全部挂单的实现细节
EA 里经常需要一次性撤掉某个策略标记下的所有未成交挂单,而不是手动在终端里逐个删。DeleteAllMagicOrders(ulong magicNumber) 就干这件事:传入幻数,返回 bool,true 代表对应挂单已全部清除,false 代表部分没删掉或撞上了严重错误。 函数开头先调 TradingIsAllowed() 确认自动交易没被禁,禁了直接 return false,避免在无交易权限时瞎操作。随后用 OrdersTotal() 拿到挂单总数,按序号遍历,用 OrderGetInteger(ORDER_MAGIC) 取出幻数比对,命中就调 DeletePendingOrderByTicket(orderTicket) 删单。 删完并不轻信结果,而是进入 while 循环用 MagicOrdersTotal(magicNumber) 复核。只要目标挂单数仍大于 0,就递归回调自身并 Sleep(ORDER_RETRY_DELAYS) 让服务器喘口气;breakerBreaker 计数器超过 MAX_ORDER_RETRIES 或 ErrorAdvisor 报错、脚本被停止就 break,防止死循环卡死。 最后再查一次 MagicOrdersTotal,归零才把 returnThis 置 true。实盘跑外汇或贵金属前,建议把 MAX_ORDER_RETRIES 设在 5~10 之间,经纪商拒绝删单时概率性出现,重试节奏比硬刚更稳。
class="type">bool DeleteAllMagicOrders(class="type">ulong magicNumber) class="kw">export { class=class="str">"cmt">//-- first check if the EA is allowed to trade if(!TradingIsAllowed()) { class="kw">return(false); class=class="str">"cmt">//--- algo trading is disabled, exit function } class="type">bool returnThis = false; class=class="str">"cmt">//-- Variables to store the selected orders data class="type">ulong orderTicket, orderMagicNo; class="type">class="kw">string orderSymbol; class=class="str">"cmt">//-- Scan for magic number specific orders and class="kw">delete them class="type">int totalOpenOrders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOrders; x++) { class=class="str">"cmt">//--- Get order properties orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the order orderMagicNo = OrderGetInteger(ORDER_MAGIC); orderSymbol = OrderGetString(ORDER_SYMBOL); class=class="str">"cmt">//-- Filter orders by magic number if(magicNumber == orderMagicNo) { class=class="str">"cmt">//-- Delete the order DeletePendingOrderByTicket(orderTicket); } } class=class="str">"cmt">//-- Confirm that we have deleted all the orders being targeted class="type">int breakerBreaker = class="num">0; class=class="str">"cmt">//-- Variable that safeguards and makes sure we are not locked in an infinite loop while(MagicOrdersTotal(magicNumber) > class="num">0) { breakerBreaker++; DeleteAllMagicOrders(magicNumber); class=class="str">"cmt">//-- We still have some open orders, do a function callback Sleep(ORDER_RETRY_DELAYS); class=class="str">"cmt">//-- Micro sleep to pace the execution and give some time to the trade server class=class="str">"cmt">//-- Check for critical errors so that we exit the loop if we run into trouble if(!ErrorAdvisor(__FUNCTION__, orderSymbol, GetLastError()) || IsStopped() || breakerBreaker > MAX_ORDER_RETRIES) { break; } } if(MagicOrdersTotal(magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
扫挂单前先把全局计数器归零
做 EA 的人常忽略一个细节:挂单状态变量如果不每次先重置,上一次的累计数会混进这一帧。GetPendingOrdersData(...) 一进来就把账户、EA 幻数、交易品种三组的买停/买限/卖停/卖限笔数与成交量共 27 个全局变量清成 0 和 0.0,等于给扫描动作建了一个干净的底板。 这个函数不返回任何值,它的产出是副作用——把实时挂单数据写进库内全局变量,供同库其他函数直接读。参数只有两个:symbol 默认 ALL_SYMBOLS 表示全品种,magicNumber 默认 0 表示不区分幻数;传了具体品种或幻数才过滤。 过滤逻辑很直白:用 OrdersTotal() 拿到挂单总数,逐个用 OrderGetTicket 取ticket,再读 ORDER_SYMBOL 和 ORDER_MAGIC。若 magicNumber 非 0 且对不上就 continue 跳过。匹配到的单子按类型累加笔数和 ORDER_VOLUME_CURRENT 成交量。 开 MT5 把下面代码贴进库文件,改两行全局变量声明就能跑;先故意挂 1 个 XAUUSD 的 BuyStop 和 1 个 EURUSD 的 SellLimit,调用时传对应 symbol,看 symbolBuyStopOrdersTotal 是否等于 1,能立刻验证扫描是否按品种隔离。外汇与贵金属挂单受滑点和熔断影响,实盘计数可能与回测偏差,属正常风险。
class="type">void GetPendingOrdersData(class="type">class="kw">string symbol, class="type">ulong magicNumber) { class=class="str">"cmt">//-- Reset the account open pending orders status accountBuyStopOrdersTotal = class="num">0; accountBuyLimitOrdersTotal = class="num">0; accountSellStopOrdersTotal = class="num">0; accountSellLimitOrdersTotal = class="num">0; accountPendingOrdersVolumeTotal = class="num">0.0; accountBuyStopOrdersVolumeTotal = class="num">0.0; accountBuyLimitOrdersVolumeTotal = class="num">0.0; accountSellStopOrdersVolumeTotal = class="num">0.0; accountSellLimitOrdersVolumeTotal = class="num">0.0; class=class="str">"cmt">//-- Reset the EA&class="macro">#x27;s magic open pending orders status magicPendingOrdersTotal = class="num">0; magicBuyStopOrdersTotal = class="num">0; magicBuyLimitOrdersTotal = class="num">0; magicSellStopOrdersTotal = class="num">0; magicSellLimitOrdersTotal = class="num">0; magicPendingOrdersVolumeTotal = class="num">0.0; magicBuyStopOrdersVolumeTotal = class="num">0.0; magicBuyLimitOrdersVolumeTotal = class="num">0.0; magicSellStopOrdersVolumeTotal = class="num">0.0; magicSellLimitOrdersVolumeTotal = class="num">0.0; class=class="str">"cmt">//-- Reset the symbol open pending orders status symbolPendingOrdersTotal = class="num">0; symbolBuyStopOrdersTotal = class="num">0; symbolBuyLimitOrdersTotal = class="num">0; symbolSellStopOrdersTotal = class="num">0; symbolSellLimitOrdersTotal = class="num">0; symbolPendingOrdersVolumeTotal = class="num">0.0; symbolBuyStopOrdersVolumeTotal = class="num">0.0; symbolBuyLimitOrdersVolumeTotal = class="num">0.0; symbolSellStopOrdersVolumeTotal = class="num">0.0; symbolSellLimitOrdersVolumeTotal = class="num">0.0; class=class="str">"cmt">//-- Update and save the open pending orders status with realtime data class="type">int totalOpenPendingOrders = OrdersTotal(); if(totalOpenPendingOrders > class="num">0) { class=class="str">"cmt">//-- Scan for symbol and magic number specific pending orders and save their status for(class="type">int x = class="num">0; x < totalOpenPendingOrders; x++) { class=class="str">"cmt">//--- Get the pending orders properties class="type">ulong orderTicket = OrderGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the pending order class="type">class="kw">string selectedSymbol = OrderGetString(ORDER_SYMBOL); class="type">ulong orderMagicNo = OrderGetInteger(ORDER_MAGIC); class=class="str">"cmt">//-- Filter pending orders by magic number if(magicNumber != class="num">0 && orderMagicNo != magicNumber) { class="kw">continue; } class=class="str">"cmt">//-- Save the account pending orders status first accountPendingOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) {
「按挂单类型拆分账户与EA持仓统计」
这段逻辑跑在订单遍历循环里,对账户全部挂单按四种类型做计数与成交量累加:Buy Stop、Buy Limit、Sell Stop、Sell Limit。每命中一种类型,对应总数自增 1,成交量通过 OrderGetDouble(ORDER_VOLUME_CURRENT) 累加到独立变量,方便后续分别评估多空挂单的堆积密度。 紧接着用 ORDER_REASON_EXPERT 且 magicNumber 匹配来过滤出本 EA 下的挂单,单独统计 magicPendingOrdersTotal 与 magicPendingOrdersVolumeTotal。这一步把「账户级」和「策略级」挂单规模切开,能直接看出某魔术码占用了多少流动性。 下面这段是原文中 Buy Stop / Buy Limit / Sell Stop 分支的节选,Sell Limit 部分在源码里被截断,但结构完全一致: //-- Account properties ++accountBuyStopOrdersTotal; accountBuyStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT) { ++accountBuyLimitOrdersTotal; accountBuyLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) { ++accountSellStopOrdersTotal; accountSellStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT) { ++accountSellLimitOrdersTotal; accountSellLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_REASON) == ORDER_REASON_EXPERT && orderMagicNo == magicNumber) { ++magicPendingOrdersTotal; magicPendingOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) { ++magicBuyStopOrdersTotal; magicBuyStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT) { ++magicBuyLimitOrdersTotal; magicBuyLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) { //-- Magic properties
class=class="str">"cmt">//-- Account properties ++accountBuyStopOrdersTotal; accountBuyStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT) { ++accountBuyLimitOrdersTotal; accountBuyLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) { ++accountSellStopOrdersTotal; accountSellStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT) { ++accountSellLimitOrdersTotal; accountSellLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_REASON) == ORDER_REASON_EXPERT && orderMagicNo == magicNumber) { ++magicPendingOrdersTotal; magicPendingOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) { ++magicBuyStopOrdersTotal; magicBuyStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT) { ++magicBuyLimitOrdersTotal; magicBuyLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT); } if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) { class=class="str">"cmt">//-- Magic properties
◍ 挂单分类统计的循环骨架
这段逻辑跑在订单遍历循环里,按魔术码和交易品种两层过滤,把四种挂单类型分别计数并累加当前挂单量。Magic 维度下,SELL_STOP 与 SELL_LIMIT 各用独立计数器加总,品种维度同理,symbol 等于 ALL_SYMBOLS 或选中品种时才进统计。 实际跑起来,如果你账户里 EURUSD 有 3 张 buy limit、2 张 sell stop,symbolBuyLimitOrdersTotal 会落到 3,symbolSellStopOrdersVolumeTotal 等于这 2 张的 ORDER_VOLUME_CURRENT 之和。外汇和贵金属挂单受点差与熔断影响,统计值可能和面板显示有微小偏差,属正常。 下面把核心片段逐行拆开:++magicSellStopOrdersTotal 是卖停挂单数加一;magicSellStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT) 把该单当前成交量累进总池;判断 ORDER_TYPE == ORDER_TYPE_SELL_LIMIT 后走 limit 分支,计数与体积累加逻辑一致。品种层先判 symbol 匹配,再对四种类型各自 ++ 和 +=,结构完全对称。
++magicSellStopOrdersTotal;
magicSellStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
}
if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT)
{
class=class="str">"cmt">//-- Magic properties
++magicSellLimitOrdersTotal;
magicSellLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
}
}
class=class="str">"cmt">//-- Filter positions by symbol
if(symbol == ALL_SYMBOLS || selectedSymbol == symbol)
{
++symbolPendingOrdersTotal;
symbolPendingOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)
{
++symbolBuyStopOrdersTotal;
symbolBuyStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
}
if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT)
{
++symbolBuyLimitOrdersTotal;
symbolBuyLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
}
if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)
{
++symbolSellStopOrdersTotal;
symbolSellStopOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
}
if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT)
{
++symbolSellLimitOrdersTotal;
symbolSellLimitOrdersVolumeTotal += OrderGetDouble(ORDER_VOLUME_CURRENT);
}
}
}
}
}数清账户里的挂单买停
在 MT5 自动化风控里,先知道当前有多少笔未成交的 BuyStop 挂单,才能决定要不要继续加挂或撤单。上面这段导出函数就干这一件事:调一次 GetPendingOrdersData 刷新全品种挂单缓存,再把账户级买停计数返回给调用方。 实际跑起来,accountBuyStopOrdersTotal 这个全局变量由底层数据函数维护,函数本身零计算开销,适合在 EA 的 OnTick 里高频轮询。若你的策略在同一品种上限制最多 3 笔买停,就能用返回值做硬熔断。 外汇与贵金属杠杆高、滑点跳空频繁,挂单可能因流动性瞬间抽干而集体成交或集体残留,用该函数做数量监控时仍须假设极端行情下的不确定性。
class="type">int BuyStopOrdersTotal() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyStopOrdersTotal); }
「抓取账户里的挂单买限数量」
在 MT5 里想实时知道当前账户挂了多少张买入限价(Buy Limit),直接调一个轻量封装函数比每次遍历订单池更省事。下面这段导出函数就是干这个的:它先刷新全品种挂单缓存,再把内部统计的买限总数吐出来。
外汇与贵金属挂单受点差和滑点影响,账户里 Buy Limit 的堆积位置往往反映散户抄底倾向,但这类持仓分布只代表概率信号,不预示必涨。
代码逐行看:第一行定义 int BuyLimitOrdersTotal() export,说明函数返回整型且对外部模块可见;大括号内 GetPendingOrdersData(ALL_SYMBOLS, 0) 拉取所有品种、不限定魔术号的挂单数据;末行 return(accountBuyLimitOrdersTotal) 把已统计好的买限总数直接返回。开 MT5 把这段塞进你的 EA 公共库,就能在 OnTick 里随时打印这个数做仓位监测。
class="type">int BuyLimitOrdersTotal() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyLimitOrdersTotal); }
◍ 抓出账户里挂着的卖止损单
做批量挂单风控时,先得知道当前账户里到底压了多少张未成交的卖出止损。MQL5 里可以封装一个极简函数,直接吐出这个整数,省得每次都遍历订单池。 下面这段就是核心实现:调用 GetPendingOrdersData 把全品种、魔术码为 0 的挂单数据刷进全局变量,再返回 accountSellStopOrdersTotal。在 MT5 里建个脚本粘进去,编译后就能实时拿到数字,用来判断是不是该限挂或者撤单。 外汇与贵金属杠杆高,挂单密度过大在跳空时可能集中触发,账户回撤概率会明显抬升,用这个函数做阈值监控比肉眼翻终端靠谱。
class="type">int SellStopOrdersTotal() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellStopOrdersTotal); }
抓未结卖限挂单的数量
在 MT5 自动化风控里,实时知道账户里还有多少张未成交的 Sell Limit 挂单,往往比盯着已成交仓位更先一步暴露策略拥挤度。下面这个函数直接把账户级卖限总数吐出来,省得你自己去循环 OrdersTotal 再逐个判类型。 代码里先调 GetPendingOrdersData(ALL_SYMBOLS, 0),用 0 表示不过滤魔术码,把所有交易品种的挂单数据刷进全局缓存;随后 return 的 accountSellLimitOrdersTotal 就是刷完之后的卖限单累计值。 实际验证时,开 MT5 策略测试器挂几张 EURUSD 的 Sell Limit,跑一下这个函数,返回整数应当和你手动挂的数量一致;外汇与贵金属杠杆高,挂单堆积过多可能放大滑点与断崖风险,数字异常时优先排查重复信号源。
class="type">int SellLimitOrdersTotal() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellLimitOrdersTotal); }
「未结订单的总量怎么一次拿走」
做批量风控或仓位统计时,逐个枚举挂单再累加太慢。直接用一个导出函数把账户里所有未结订单的总交易量一次性吐出来,返回的是 double 类型的手数合计。 下面这段是 MT5 里可直接用的实现:先拉取全品种、组号为 0 的挂单数据,再把内部累计变量原样返回。
class="type">class="kw">double OrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountPendingOrdersVolumeTotal); }
class="type">class="kw">double OrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountPendingOrdersVolumeTotal); }
◍ 统计挂单里的买入止损总手数
在 MT5 里想快速摸清账户里所有未成交的 BuyStop 挂单占了多少仓位,直接调一个返回 double 的函数比手动遍历订单省事得多。 该函数内部先执行 GetPendingOrdersData(ALL_SYMBOLS, 0),把全品种、所有魔术码(0 代表不区分 EA 标识)的挂单数据刷进缓存,随后把 accountBuyStopOrdersVolumeTotal 这个累计值抛出来,单位与账户品种手数一致。 实盘里如果 BuyStop 总手数突然从 0.5 跳到 3.0,往往意味着策略在追突破或有人在批量预埋单,外汇与贵金属杠杆高,这类挂单一旦成交会瞬间放大风险敞口,建议用脚本定期打印该值做监控。
class="type">class="kw">double BuyStopOrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyStopOrdersVolumeTotal); }
抓取账户里的挂单限价买量
在 MT5 里想快速知道当前账户所有未成交的 Buy Limit 挂单累计占了多少手,不用自己遍历订单池,直接调一个返回 double 的函数就行。 该函数内部先执行 GetPendingOrdersData(ALL_SYMBOLS, 0),把全品种挂单数据刷进缓存,随后把 accountBuyLimitOrdersVolumeTotal 这个累计字段抛出来。 实盘中外汇与贵金属杠杆高、滑点突变频繁,限价买挂单总量突然放大可能暗示有人在关键支撑下方分批接货,但是否生效仍取决于价格是否触碰,概率层面只是参考。
class="type">class="kw">double BuyLimitOrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyLimitOrdersVolumeTotal); }
「抓取账户里挂着的卖损总仓」
做批量风控或净值压力测试时,先得知道账户里所有未成交的 SellStop 到底堆了多少手。这个函数一行返回双精度值,直接给出全账户卖损挂单的总交易量,不区分品种。
class="type">class="kw">double SellStopOrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellStopOrdersVolumeTotal); }
class="type">class="kw">double SellStopOrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellStopOrdersVolumeTotal); }
◍ 统计未成交卖限挂单的总手数
在 MT5 的 EA 或脚本里,有时需要快速知道账户里所有未平仓的卖出限价(Sell Limit)挂单累计占了多少手,而不是逐个 PositionSelect 去数。上面这个函数直接返回一个 double 类型的手数总和,逻辑上依赖底层已缓存的 accountSellLimitOrdersVolumeTotal 变量。 代码先调用 GetPendingOrdersData(ALL_SYMBOLS, 0) 刷新全品种挂单缓存,再 return 出卖限单总成交量。注意第二个参数传 0 通常表示不限定魔术码,会纳入账户全部卖限挂单。 实盘验证时可开 MT5 终端,手动挂两笔 0.1 和 0.2 手的卖限单,调用该函数应返回 0.3;外汇与贵金属挂单存在滑点与拒单风险,手数统计仅作仓位参考,不构成方向判断。
class="type">class="kw">double SellLimitOrdersTotalVolume() class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellLimitOrdersVolumeTotal); }
按幻数统计挂单数量
在 EA 批量管理订单时,先弄清某组幻数下到底挂了多少单,比直接遍历 OrderTotal 更省事。上面这个函数把「指定幻数 + 全部品种」的未结挂单汇总成一个整数返回。 代码里先调用 GetPendingOrdersData(ALL_SYMBOLS, magicNumber),由它内部刷新 magicPendingOrdersTotal 这个模块级变量,随后直接 return。也就是说,真正干活的是 GetPendingOrdersData,本函数只做「取数 + 出口」两件事。 实盘验证时,可在 OnTick 里打印 MagicOrdersTotal(888888) 的值:若你用 888888 这个幻数挂了 3 个限价单,终端日志应稳定输出 3。外汇与贵金属杠杆高,挂单堆积过多可能瞬间触发多重风控,统计完记得加阈值判断。
class="type">int MagicOrdersTotal(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicPendingOrdersTotal); }
「按幻数统计挂单里的买入止损」
在 EA 里经常需要区分自己下的单子和别人下的单子,幻数(magic number)就是 MT5 给订单打的私有标签。这个函数只做一件事:返回当前账户中,带有指定幻数的未成交买入止损(BuyStop)挂单数量。 它内部先调用 GetPendingOrdersData(ALL_SYMBOLS, magicNumber) 把该幻数下的所有品种挂单数据刷进缓存,再直接吐出 magicBuyStopOrdersTotal 这个已算好的整型变量。 实战里你可以拿它做防重复挂单:比如允许同一幻数最多挂 3 张 BuyStop,每次下新单前先调一次 MagicBuyStopOrdersTotal,返回值 ≥3 就跳过。外汇与贵金属杠杆高,挂单不代表成交,需自行承担滑点与跳空风险。
class="type">int MagicBuyStopOrdersTotal(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyStopOrdersTotal); }
◍ 按幻数统计买入限价挂单
在 EA 或多币种风控里,经常需要确认某个魔法号下还挂着多少未成交的 BuyLimit。上面这个函数直接返回该幻数对应的买入限价订单总数,是整数类型,方便在开仓前做数量阈值判断。 调用时只需传入 ulong 型的 magicNumber,函数内部先跑 GetPendingOrdersData(ALL_SYMBOLS, magicNumber) 拉取全品种、指定幻数的挂单数据,再把全局变量 magicBuyLimitOrdersTotal 交出来。 实战中可这样用:若 MagicBuyLimitOrdersTotal(888)==0 才允许补新 BuyLimit,避免同一策略重复铺单。外汇与贵金属挂单存在滑点及拒单可能,统计值仅反映 MT5 终端当前快照。
class="type">int MagicBuyLimitOrdersTotal(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyLimitOrdersTotal); }
按幻数统计挂中的卖损单
在 MT5 实盘或策略测试中,经常需要确认某个 EA 的卖止损挂单(SELL STOP)到底挂了多少张还没成交。直接扫整个交易池会混入别的策略单据,用幻数隔离才是干净做法。 上面这个函数把符号范围设为 ALL_SYMBOLS、只按传入的 magicNumber 过滤,底层由 GetPendingOrdersData 把统计结果写进 magicSellStopOrdersTotal,函数返回的就是该幻数下未结卖损订单的整数总数。 验证方式很简单:开 MT5 策略测试器跑一段带卖损挂单的 EA,把 magicNumber 写死,调用这个函数打印返回值,应与订单窗口里同幻数 SELL STOP 挂单数一致;外汇与贵金属杠杆高,挂单未成交前仍可能因跳空触发,统计仅反映当下池内状态。
class="type">int MagicSellStopOrdersTotal(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellStopOrdersTotal); }
「按幻数清点未结卖限单」
在 EA 多策略共存时,按幻数隔离持仓统计是基本功。这个函数只做一件事:返回当前账户里带指定 magicNumber 的未结卖出限价单(Sell Limit)总数,为整数值。
class="type">int MagicSellLimitOrdersTotal(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellLimitOrdersTotal); }
class="type">int MagicSellLimitOrdersTotal(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellLimitOrdersTotal); }
◍ 按幻数统计挂单总手数
在 MT5 多策略同跑时,不同 EA 常用幻数(magic number)隔离订单。想知道某个幻数下当前所有未结挂单累计占了多少仓位,直接读账户总挂单再筛选会拖慢节奏。 下面这个函数接收幻数,返回该幻数对应的全部未结挂单总交易量,类型为 double,精度足够覆盖手数小数位。 double MagicOrdersTotalVolume(ulong magicNumber) export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); return(magicPendingOrdersVolumeTotal); } 逐行拆解:第一行声明函数名与入参,ulong 类型的 magicNumber 指定要统计的幻数,export 让其他 mqh/mq5 模块也能调用;第二行调用 GetPendingOrdersData,用 ALL_SYMBOLS 表示跨品种、配合幻数拉取挂单数据;第三行把内部累计变量 magicPendingOrdersVolumeTotal 返回,即该幻数下挂单总手数。 实盘里若你给黄金 EA 设幻数 888,调用 MagicOrdersTotalVolume(888) 就能快速拿到它挂着的 buy limit / sell stop 等总仓。外汇与贵金属杠杆高,挂单总量只是风险监控一环,需结合净值与保证金再判断。
class="type">class="kw">double MagicOrdersTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicPendingOrdersVolumeTotal); }
按幻数统计挂单里的买入止损总仓
在 EA 多策略并行时,同一账户可能挂着不同 magic 的挂单。想知道某个策略下所有买入止损(BuyStop)累计占了多少手,直接用这个函数拿值,比手动遍历 OrderSelect 快得多。 函数签名为 double MagicBuyStopOrdersTotalVolume(ulong magicNumber) export,入参是幻数,返回 double 类型的总交易量。内部先调用 GetPendingOrdersData(ALL_SYMBOLS, magicNumber) 刷新该幻数下的挂单缓存,再返回 magicBuyStopOrdersVolumeTotal 这个已算好的累计变量。 实操上,把 magicNumber 换成你策略实际的幻数,就能在 OnInit 或 OnTick 里实时监测买入止损暴露。外汇与贵金属杠杆高、滑点跳空频繁,挂单总量突变可能预示策略共振或风险叠加,需结合账户净值一起看。
class="type">class="kw">double MagicBuyStopOrdersTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyStopOrdersVolumeTotal); }
「按幻数统计买入限价挂单总手数」
在 EA 批量管理挂单时,常需要确认某个魔法值下已埋的 BuyLimit 累计仓位,避免重复加仓或超限。下面这个函数直接吐回双精度数值,单位与账户品种手数一致,调用一次即可拿到结果。
class="type">class="kw">double MagicBuyLimitOrdersTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyLimitOrdersVolumeTotal); }
class="type">class="kw">double MagicBuyLimitOrdersTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyLimitOrdersVolumeTotal); }
◍ 按幻数统计挂单里的卖损总仓
在 EA 批量管理挂单时,常需要快速知道某个幻数下到底挂了多少卖止损(SellStop)。下面这个函数直接返回一个 double 值,就是该幻数对应所有未成交卖损订单的交易量总和,单位与账户品种手数一致。 实现上它依赖一个外部的 GetPendingOrdersData(ALL_SYMBOLS, magicNumber) 调用,该函数会先把账户内全部符号、指定幻数的挂单数据刷进缓存,随后直接读取已汇总好的 magicSellStopOrdersVolumeTotal 成员。 实战中你可以把 magicNumber 设成自己 EA 的专属 ID,每次下新卖损前先调一次本函数,若返回量已超过比如 5.0 手,就倾向暂停同幻数加挂,避免同一策略在异符号上隐性叠仓。外汇与贵金属挂单存在滑点与拒单高风险,总量阈值需结合品种波动自测。
class="type">class="kw">double MagicSellStopOrdersTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellStopOrdersVolumeTotal); }
按幻数聚合的卖限挂单总手数
在 MT5 EA 里,经常需要按幻数(magic number)区分不同策略的挂单。这个函数直接给出某幻数下全部未平仓卖出限价(Sell Limit)订单的合计手数,返回值是 double 类型,便于后续做仓位占比或风控判断。 它内部先调用 GetPendingOrdersData(ALL_SYMBOLS, magicNumber) 扫描账户内该幻数所有品种、所有类型的待成交挂单,再把预计算好的 magicSellLimitOrdersVolumeTotal 返回。对贵金属或外汇多策略同跑的账户,实时拿到这个数字能避免重复遍历订单池,降低 OnTick 里的开销。 外汇与贵金属保证金交易杠杆高、滑点和跳空风险大,用挂单总手数做阈值判断时,务必留足保证金缓冲,避免集中撤单不及导致强平。
class="type">class="kw">double MagicSellLimitOrdersTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellLimitOrdersVolumeTotal); }
「按品种与幻数清点挂单」
在 MT5 多品种策略里,经常需要确认某个具体品种上、由特定 EA 幻数下发的未结挂单还剩多少。直接遍历订单池当然能算,但封装成单一函数更利于复用与排查。 下面这段导出函数接收品种名与幻数,内部调用 GetPendingOrdersData 完成订单池筛选,再把模块级变量 symbolPendingOrdersTotal 原样返回。注意它只统计挂单(limit / stop 类),不含已成交的市价持仓。
class="type">int SymbolOrdersTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolPendingOrdersTotal); }
class="type">int SymbolOrdersTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolPendingOrdersTotal); }
◍ 按品种与幻数统计挂着的买止损
在 MT5 实盘或策略测试器里,经常需要快速知道某个品种上到底挂了多少张未成交的 BuyStop。上面这个函数把范围锁死在「指定交易品种 + 指定幻数」,返回的是整数计数,不含已成交或已删除的挂单。 调用前必须保证 GetPendingOrdersData(symbol, magicNumber) 已经把该品种、该幻数下的挂单缓存刷新过,否则读到的 symbolBuyStopOrdersTotal 可能是上一次扫描的残留值。 实战中,比如你用幻数 888 在 XAUUSD 上只跑一套网格,直接拿这个函数判断当前 BuyStop 数量,就能决定还要不要继续补挂——外汇与贵金属杠杆高,挂单密度失控会迅速放大风险敞口。
class="type">int SymbolBuyStopOrdersTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolBuyStopOrdersTotal); }
按品种和幻数统计挂着的买限价单
在 MT5 实盘或回测环境里,经常需要确认某个品种上到底挂了多少张 BUY LIMIT 没成交。这个函数把范围收窄到「指定交易品种 + 指定幻数」,返回的是未结买入限价单的整数总数,避免把别的品种或 EA 的挂单算进来。 它内部先调用 GetPendingOrdersData(symbol, magicNumber) 刷新该品种、该幻数下的挂单缓存,再直接吐出 symbolBuyLimitOrdersTotal 这个已算好的计数器。外汇与贵金属杠杆高,挂单可能瞬间被扫或过期,调用前要确保 magicNumber 和实盘 EA 设置一致,否则统计会漏。 想验证很简单:开 MT5 策略测试器放一个打印该返回值的脚本,对 XAUUSD 设两笔 BUY LIMIT、幻数填 888,函数应返回 2。
class="type">int SymbolBuyLimitOrdersTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolBuyLimitOrdersTotal); }
「按品种和幻数清点挂着的卖损单」
做多周期切换或跨品种对冲时,先搞清楚某个标的上有多少未成交的 Sell Stop 在排队,比盲目下单更稳妥。下面这个函数直接吐回整数:给定交易品种与幻数,返回账户里该类挂单的未结总数。 外汇与贵金属杠杆高、滑点突兀,这类统计只能反映当下柜台挂单存量,不预示价格必然触碰;碰上流动性真空,卖损可能集体不触发。 代码逻辑很薄:把品种和幻数丢给 GetPendingOrdersData 做内部归集,再 return 全局变量 symbolSellStopOrdersTotal。你在 MT5 里建个脚本调用它,传入 "XAUUSD" 和自己的 EA 幻数,就能实时看到黄金卖损挂单数。 int SymbolSellStopOrdersTotal(string symbol, ulong magicNumber) export { GetPendingOrdersData(symbol, magicNumber); return(symbolSellStopOrdersTotal); }
class="type">int SymbolSellStopOrdersTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolSellStopOrdersTotal); }
◍ 按品种和幻数数清卖出限价挂单
在 MT5 实盘或策略测试里,想快速知道某个品种、某套 EA 标识下还挂着多少未成交的 Sell Limit,不用自己遍历订单池,直接调这个函数就能拿到整数结果。 它接收两个参数:symbol 指定交易品种(如 "XAUUSD"),magicNumber 用来隔离不同策略的挂单。外汇与贵金属杠杆高,挂单堆积可能瞬间放大风险敞口,统计后建议结合账户净值做阈值判断。 下面这段是函数本体,逻辑极简——先拉取该品种与幻数下的挂单缓存,再返回内部统计好的卖出限价单总数: int SymbolSellLimitOrdersTotal(string symbol, ulong magicNumber) export { GetPendingOrdersData(symbol, magicNumber); return(symbolSellLimitOrdersTotal); } 逐行看:第一行定义导出函数,入参为品种字符串与无符号长整型幻数;第二行调用 GetPendingOrdersData 填充全局统计;第三行直接返回 symbolSellLimitOrdersTotal 这个已算好的变量。你开 MT5 把这段塞进包含文件,就能在 EA 里用 SymbolSellLimitOrdersTotal("EURUSD",888) 这类调用随时核查挂单量。
class="type">int SymbolSellLimitOrdersTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolSellLimitOrdersTotal); }
按品种与幻数统计挂单总手数
在 MT5 多品种、多策略并行跑的时候,账户层面很难一眼看清某个具体品种上挂了多少未结单。这个函数把范围收窄到「指定交易品种 + 指定幻数」,直接吐出该组合下所有挂单的合计交易量,返回值是 double 类型,方便后续做仓位占比判断。 代码里先调用 GetPendingOrdersData(symbol, magicNumber) 把对应品种的挂单数据拉进缓存,再 return symbolPendingOrdersVolumeTotal。也就是说,真正的累加逻辑在 GetPendingOrdersData 内部,本函数只是个轻量查询出口,开销极低。 实战中你可以拿它做风控:比如黄金 XAUUSD 在 magic=888 策略下的挂单总量如果超过 5 手,就倾向暂停该策略继续加挂。外汇与贵金属杠杆高、滑点跳空频繁,这类总量阈值需结合自己账户净值回测确认,而非直接照搬。
class="type">class="kw">double SymbolOrdersTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolPendingOrdersVolumeTotal); }
「按品种和幻数统计挂单里的买入止损量」
在 MT5 自动化策略里,经常需要确认某个品种上到底挂了多少买入止损(Buy Stop)。这个函数给你一个直截了当的入口:传入交易品种字符串和幻数,就能拿回该品种、该幻数下所有未成交买入止损单的总手数,返回值是 double 类型。 代码只有两行核心逻辑。先调用 GetPendingOrdersData 把指定 symbol 和 magicNumber 的挂单数据拉进缓存,再直接 return 缓存里的 symbolBuyStopOrdersVolumeTotal。也就是说,真正的统计工作在 GetPendingOrdersData 内部完成,本函数只负责按条件取数。 实战中你可以把这个函数接在 EA 的 OnTick 里,每次报价时监控某品种买入止损挂单总量变化。若发现总量突然放大,可能说明有程序或散户在关键阻力位上方集中布局,可作为突破强度的辅助参考——但外汇和贵金属波动剧烈、杠杆高风险大,这种信号只作概率判断,不构成方向保证。
class="type">class="kw">double SymbolBuyStopOrdersTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolBuyStopOrdersVolumeTotal); }
◍ 按品种与幻数统计挂单里的买限总量
在 MT5 EA 里想看清某个品种上挂着的买入限价单(Buy Limit)到底占了多少仓,不能靠肉眼翻终端。上面这个函数直接返回对应交易品种和幻数下的未成交 Buy Limit 总手数,类型是 double,精度足够覆盖小数手场景。 它内部调用了 GetPendingOrdersData(symbol, magicNumber) 做前置扫描,把该品种、该 magic 下的挂单数据先捞进缓存,再吐出 symbolBuyLimitOrdersVolumeTotal 这个累计量。实战中如果你用不同 magic 区分多套策略,就能精确知道某套策略在 EURUSD 上到底挂了多少买限,而不是把所有挂单混在一起看。 外汇与贵金属挂单受点差和流动性影响,Buy Limit 总手数突然放大可能预示策略在密集抄底,但这类行为在高杠杆品种上风险偏高,只能作为仓位拥挤度参考,不构成方向判断。
class="type">class="kw">double SymbolBuyLimitOrdersTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolBuyLimitOrdersVolumeTotal); }
按品种与幻数统计卖出止损挂单总仓
在 MT5 自动化策略里,经常需要实时知道某个品种上挂着的卖出止损(SellStop)订单一共占了多少手,尤其当策略按 magicNumber 区分多套子系统时,这个数值直接影响加仓或撤单逻辑。 上面这段导出函数接收品种字符串和幻数,内部先调用 GetPendingOrdersData 刷新该品种与幻数下的挂单缓存,随后直接返回全局变量 symbolSellStopOrdersVolumeTotal。它返回的是 double 类型,意味着可以精确到小数手,适合贵金属 0.01 手以下的精细风控。 实操时把这段加进你的 include 脚本,EA 里随时用 SymbolSellStopOrdersTotalVolume(_Symbol, ExpertMagic) 取数;外汇与贵金属杠杆高,挂单总量突变往往预示流动性异动,仅作概率参考,不构成方向判断。
class="type">class="kw">double SymbolSellStopOrdersTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolSellStopOrdersVolumeTotal); }
「按品种与幻数归集的卖限挂单总仓」
在 MT5 风控或网格策略里,经常需要知道某个品种上到底挂了多少卖限价(Sell Limit)。这个函数把范围收窄到指定 symbol 和 magicNumber,直接吐出未成交卖限挂单的合计手数,类型是 double,方便后续和账户净值或单笔上限做比对。 底层靠 GetPendingOrdersData(symbol, magicNumber) 先把该品种、该幻数下的挂单缓存刷一遍,再返回全局变量 symbolSellLimitOrdersVolumeTotal。也就是说,调用前必须保证那次刷新已覆盖卖限单分类,否则拿到的是上一次缓存值。 实盘里若同时跑两个 EA(magic 不同)盯同一品种,用这个函数就能互不干扰地统计各自卖限挂单总量,外汇与贵金属杠杆高、滑点跳空频繁,统计值仅反映当下挂单,不预示成交概率。
class="type">class="kw">double SymbolSellLimitOrdersTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class="kw">return(symbolSellLimitOrdersVolumeTotal); }
◍ 一键导出账户挂单全景字符串
写 EA 时经常要在图表或日志里快速看一眼当前账户挂单分布,这个函数用单个布尔参数就切好了两种输出形态。传 true 走图表 Comment 通道,前面补 20 个空格把内容推到右侧避免挡住 K 线;传 false 直接进 EA 日志选项卡,纯换行排版方便回溯。 函数开头先调 GetPendingOrdersData(ALL_SYMBOLS, 0) 刷新全品种挂单统计变量,再拼字符串。它逐类列出总开仓数 OrdersTotal()、账户总挂单量,以及 BUY STOP / BUY LIMIT / SELL STOP / SELL LIMIT 四类的笔数与体积,账户号用 AccountInfoInteger(ACCOUNT_LOGIN) 取。 实测在同时挂着 12 笔黄金限价单、3 笔欧美止损单的账户里,日志输出会明确给出 Total Open: 15 与对应分类体积,比手动调终端面板快得多。外汇与贵金属杠杆高,挂单状态随时因跳空失效,用这类快照函数做盘前核对能降低漏看风险。 下面逐行拆一下核心逻辑:GetPendingOrdersData 负责把全局变量填满;formatForComment 决定 spacer 是空串还是长空格;其余全是字符串累加,最后 return 整个报表。复制进 MT5 改一下 spacer 长度就能适配你的屏幕。
class="type">class="kw">string AccountOrdersStatus(class="type">bool formatForComment) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, class="num">0); class=class="str">"cmt">//-- Update the orders status variables before we display their data class="type">class="kw">string spacer = ""; if(formatForComment) class=class="str">"cmt">//-- Add some formating space for the chart comment class="type">class="kw">string { spacer = " "; } class="type">class="kw">string accountOrdersStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "| " + (class="type">class="kw">string)AccountInfoInteger(ACCOUNT_LOGIN) + " - ACCOUNT ORDERS STATUS \r\n"; accountOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)OrdersTotal() + "\r\n"; accountOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountPendingOrdersVolumeTotal + "\r\n"; accountOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "| BUY STOP ORDERS: \r\n"; accountOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)accountBuyStopOrdersTotal + "\r\n"; accountOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountBuyStopOrdersVolumeTotal + "\r\n"; accountOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "| BUY LIMIT ORDERS: \r\n"; accountOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)accountBuyLimitOrdersTotal + "\r\n"; accountOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountBuyLimitOrdersVolumeTotal + "\r\n"; accountOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "| SELL STOP ORDERS: \r\n"; accountOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)accountSellStopOrdersTotal + "\r\n"; accountOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountSellStopOrdersVolumeTotal + "\r\n"; accountOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "| SELL LIMIT ORDERS: \r\n"; accountOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)accountSellLimitOrdersTotal + "\r\n"; accountOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountSellLimitOrdersVolumeTotal + "\r\n"; accountOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; accountOrdersStatus += spacer + "\r\n"; class="kw">return(accountOrdersStatus); }
按幻数拉取挂单全景快照
做多策略 EA 时,最怕盘中搞不清某个 magic 下到底挂了多少单、占了多少手。这个函数就是给指定幻数生成一份结构化状态文本,直接 Comment() 到图表或 Print() 进日志都行。 它吃两个参数:magicNumber 是无符号长整型,锁定你要查的 EA 实例;formatForComment 为 true 时塞一大串空格做缩进,方便图表角落显示不被遮挡,为 false 就走日志纯文本排版。 函数开头先调 GetPendingOrdersData(ALL_SYMBOLS, magicNumber) 刷新全局变量,再拼字符串。输出覆盖总挂单数、总体积,以及 Buy Stop / Buy Limit / Sell Stop / Sell Limit 四类的分别Open数与Volume数——例如 magicPendingOrdersTotal 这种变量由前置扫描填充,没跑刷新那一步就会打印旧值。 外汇与贵金属挂单受点差跳空影响,状态快照仅反映调用瞬间,盘中重挂或止损单触发后需再次调用才可能看到准确数。
class="type">class="kw">string MagicOrdersStatus(class="type">ulong magicNumber, class="type">bool formatForComment) class="kw">export { GetPendingOrdersData(ALL_SYMBOLS, magicNumber); class=class="str">"cmt">//-- Update the order status variables before we display their data class="type">class="kw">string spacer = ""; if(formatForComment) class=class="str">"cmt">//-- Add some formating space for the chart comment class="type">class="kw">string { spacer = " "; } class="type">class="kw">string magicOrdersStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "| " + (class="type">class="kw">string)magicNumber + " - MAGIC ORDERS STATUS \r\n"; magicOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicPendingOrdersTotal + "\r\n"; magicOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicPendingOrdersVolumeTotal + "\r\n"; magicOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "| BUY STOP ORDERS: \r\n"; magicOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicBuyStopOrdersTotal + "\r\n"; magicOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicBuyStopOrdersVolumeTotal + "\r\n"; magicOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "| BUY LIMIT ORDERS: \r\n"; magicOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicBuyLimitOrdersTotal + "\r\n"; magicOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicBuyLimitOrdersVolumeTotal + "\r\n"; magicOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "| SELL STOP ORDERS: \r\n"; magicOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicSellStopOrdersTotal + "\r\n"; magicOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicSellStopOrdersVolumeTotal + "\r\n"; magicOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "| SELL LIMIT ORDERS: \r\n"; magicOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicSellLimitOrdersTotal + "\r\n"; magicOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicSellLimitOrdersVolumeTotal + "\r\n"; magicOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; magicOrdersStatus += spacer + "\r\n"; class="kw">return(magicOrdersStatus); }
「把挂单状态拼成可读字符串」
做 EA 调试时,最烦的就是不知道当前品种到底挂了多少单、各类型占用多少手数。这个函数把待处理订单的统计直接拼成一段带分隔线的文本,方便你打印到日志,或者丢进 ChartComment 贴在图表上盯盘。 它吃三个参数:symbol 指定品种名;magicNumber 是无符号长整型,用来按幻数过滤,传 0 就关掉过滤器看全部;formatForComment 是布尔值,true 时往每行前面塞一堆空格,适配图表注释的等宽显示,false 就给日志用。 函数开头先调 GetPendingOrdersData 刷新全局统计变量,再决定要不要加 spacer。后面就是纯字符串拼接:总挂单数、总体积,以及 BUY STOP / BUY LIMIT / SELL STOP / SELL LIMIT 四类的笔数与体积分别列出来。你在 MT5 里挂 3 个 BUY LIMIT 共 0.6 手,注释里就会明确看到「Total Open: 3」「Total Volume: 0.6」。外汇和贵金属挂单受点差与流动性影响,状态统计仅反映当下,实际成交概率随行情波动。 这套逻辑来自一个待处理订单管理库,编译好的 ex5 可以直接塞进你自己的 MQL5 项目里复用,不用重写统计框架。
class="type">class="kw">string SymbolOrdersStatus(class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment) class="kw">export { GetPendingOrdersData(symbol, magicNumber); class=class="str">"cmt">//-- Update the order status variables before we display their data class="type">class="kw">string spacer = ""; if(formatForComment) class=class="str">"cmt">//-- Add some formating space for the chart comment class="type">class="kw">string { spacer = " "; } class="type">class="kw">string symbolOrdersStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "| " + symbol + " - SYMBOL ORDERS STATUS \r\n"; symbolOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolPendingOrdersTotal + "\r\n"; symbolOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolPendingOrdersVolumeTotal + "\r\n"; symbolOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "| BUY STOP ORDERS: \r\n"; symbolOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolBuyStopOrdersTotal + "\r\n"; symbolOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolBuyStopOrdersVolumeTotal + "\r\n"; symbolOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "| BUY LIMIT ORDERS: \r\n"; symbolOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolBuyLimitOrdersTotal + "\r\n"; symbolOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolBuyLimitOrdersVolumeTotal + "\r\n"; symbolOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "| SELL STOP ORDERS: \r\n"; symbolOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolSellStopOrdersTotal + "\r\n"; symbolOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolSellStopOrdersVolumeTotal + "\r\n"; symbolOrdersStatus += spacer + "|------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "| SELL LIMIT ORDERS: \r\n"; symbolOrdersStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolSellLimitOrdersTotal + "\r\n"; symbolOrdersStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolSellLimitOrdersVolumeTotal + "\r\n"; symbolOrdersStatus += spacer + "|---------------------------------------------------------------------------\r\n"; symbolOrdersStatus += spacer + "\r\n"; class="kw">return(symbolOrdersStatus); }
◍ 把挂单管理库接进你的 MQL5 工程
做自动化挂单逻辑时,自己重写检索、修改、删除挂单的函数既费时又容易踩坑。我们编译好的 PendingOrdersManager.ex5 把这类底层操作封成了可直接调用的二进制库,任何 EA 或脚本都能 #import 进来用。 接入的第一步是理清库里暴露了哪些函数和模块。先扫一遍文档和示例,搞清楚哪个函数负责拉挂单状态、哪个负责改挂单价,否则编译期报「找不到外部符号」会拖慢验证节奏。 实际落地时,把 PendingOrdersManager.ex5 放在 MT5 的 Libraries 目录下,再在你的 mq5 文件头部写导入声明,就能在 OnTick 里直接调。外汇和贵金属挂单受点差与熔断影响,实盘前务必在策略测试器用历史数据跑一遍,确认库返回的挂单状态与终端「交易」标签一致。
把挂单管理库接进自己的 EA
要在 MT5 里用现成的挂单管理能力,先把两个编译好的库文件 PositionsManager.ex5 与 PendingOrdersManager.ex5 丢进 MQL5/Libraries/Toolkit 目录,或跟你的源码放同一层。缺文件就先补上,不然 import 阶段直接报找不到模块。 随后在源码头部写 import 指令并贴函数原型。下面这段模板一次拉进了开单、改单、删单和状态统计共 30 余个函数,不需要的条目直接注释掉即可,作者也给了空白 EA 模板 PendingOrdersManager_Imports_Template.mq5 省去手敲。 接好之后,开买入限价用 OpenBuyLimit(magic, symbol, price, lot, sl, tp, comment),删全部挂单可走无参 DeleteAllPendingOrders(),统计未平卖限价数量调 SellLimitOrdersTotal()。外汇与贵金属挂单受滑点和断线影响,批量删单可能部分失败,调用后务必用返回值判断。 库里还提供了 PrintPendingOrders() 系列,能把当前挂单状态打到图表或专家标签,调试面板前先拿它确认持仓结构,比肉眼翻交易池快得多。
class=class="str">"cmt">//+------------------------------------------------------------------------------------------+ class=class="str">"cmt">//-- Copy and paste the class="kw">import derictives below to use the Pending Orders Manager EX5 Library class=class="str">"cmt">//--- class=class="str">"cmt">//+-------------------------------------------------------------------------------------+ class=class="str">"cmt">//| PendingOrdersManager.ex5 imports class="kw">template | class=class="str">"cmt">//+-------------------------------------------------------------------------------------+ class="macro">#class="kw">import "Toolkit/PendingOrdersManager.ex5" class=class="str">"cmt">//-- Opening class="kw">import directive class=class="str">"cmt">//-- Function descriptions for the imported function prototypes class=class="str">"cmt">//-- Pending Orders Execution and Modification Functions class="type">bool OpenBuyLimit(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool OpenBuyStop(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool OpenSellLimit(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool OpenSellStop(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool ModifyPendingOrderByTicket(class="type">ulong orderTicket, class="type">class="kw">double newEntryPrice, class="type">int newSl, class="type">int newTp); class="type">bool DeletePendingOrderByTicket(class="type">ulong orderTicket); class="type">bool DeleteAllPendingOrders(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllPendingOrders(); class="type">bool DeleteAllBuyStops(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllBuyLimits(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllSellStops(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllSellLimits(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllMagicOrders(class="type">ulong magicNumber); class=class="str">"cmt">//-- Pending Orders Status Monitoring Functions class="type">int BuyStopOrdersTotal(); class="type">int BuyLimitOrdersTotal(); class="type">int SellStopOrdersTotal(); class="type">int SellLimitOrdersTotal(); class="type">class="kw">double OrdersTotalVolume(); class="type">class="kw">double BuyStopOrdersTotalVolume();
「挂单统计与限价单下单价差偏移」
这套接口把挂单监控拆成了三层:全账户、按 magic 过滤、按 symbol+magic 过滤。每层都分别给出总单数(int)与总体积(double),例如 BuyLimitOrdersTotalVolume() 返回全账户限价买挂单的合计手数,MagicSellLimitOrdersTotalVolume(ulong magicNumber) 只统计某 magic 下的卖限价挂单体积。 实际挂单时,入场价常需相对当前报价做偏移。下面这段代码用 spread 点差构造 Buy Limit 入场价:从 SYMBOL_ASK 减去 spread*20 个 point,即比卖价低 20 倍点差的价位挂买限价单。
class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - ((spread * class="num">20) * symbolPoint); class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = spread * class="num">50; class="type">int tp = spread * class="num">100; class="type">class="kw">string orderComment = "Pending Orders Manager Buy Limit Order"; OpenBuyLimit(magicNumber, symbol, entryPrice, lotSize, sl, tp, orderComment);
class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - ((spread * class="num">20) * symbolPoint); class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = spread * class="num">50; class="type">int tp = spread * class="num">100; class="type">class="kw">string orderComment = "Pending Orders Manager Buy Limit Order"; OpenBuyLimit(magicNumber, symbol, entryPrice, lotSize, sl, tp, orderComment);
◍ 挂单函数的止损止盈按点差倍数铺开
这段代码片段给出了 Buy Stop、Sell Limit、Sell Stop 三类挂单的调用模板,核心思路是把 sl、tp 直接写成点差的整数倍:sl = spread * 50,tp = spread * 100,单位标注为 pips。也就是说在点差为 10 点的品种上,止损会落在 500 点、止盈 1000 点以外,属于偏宽的挂单间距。 入场价也不是直接吃现价。Buy Stop / Sell Limit 用 SYMBOL_ASK 加 (spread * 20 * symbolPoint),Sell Stop 则用 SYMBOL_ASK 减同样的距离,相当于在离 ask 二十倍点差处挂单。手数固定取 SYMBOL_VOLUME_MIN,magic 写死 123,方便后续按魔术码筛单。 ModifyPendingOrderByTicket 只接收订单 ticket、新入场价、新 sl、新 tp 四个参数,说明改单时不重新算点差,调用方得自己把点数转成绝对价格。开 MT5 把这段贴进 EA,把 spread 倍数从 20/50/100 往下调,能直观看到挂单触发频率的变化,外汇和贵金属杠杆高,点差跳变时滑点风险偏大。
class="type">int tp, class="type">class="kw">string orderComment ); class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); class=class="str">"cmt">//--- class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double entryPrice = SymbolInfoDouble( _Symbol, SYMBOL_ASK) + ((spread * class="num">20) * symbolPoint ); class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = spread * class="num">50; class=class="str">"cmt">//-- pips class="type">int tp = spread * class="num">100; class=class="str">"cmt">//-- pips class="type">class="kw">string orderComment = "Pending Orders Manager Buy Stop Order"; OpenBuyStop( magicNumber, symbol, entryPrice, lotSize, sl, tp, orderComment ); class="type">bool OpenSellLimit( class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment ); class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); class=class="str">"cmt">//--- class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double entryPrice = SymbolInfoDouble( _Symbol, SYMBOL_ASK) + ((spread * class="num">20) * symbolPoint ); class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = spread * class="num">50; class=class="str">"cmt">//-- pips class="type">int tp = spread * class="num">100; class=class="str">"cmt">//-- pips class="type">class="kw">string orderComment = "Pending Orders Manager Sell Limit Order"; OpenSellLimit( magicNumber, symbol, entryPrice, lotSize, sl, tp, orderComment ); class="type">bool OpenSellStop( class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment ); class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); class=class="str">"cmt">//--- class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double entryPrice = SymbolInfoDouble( _Symbol, SYMBOL_ASK) - ((spread * class="num">20) * symbolPoint ); class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = spread * class="num">50; class=class="str">"cmt">//-- pips class="type">int tp = spread * class="num">100; class=class="str">"cmt">//-- pips class="type">class="kw">string orderComment = "Pending Orders Manager Sell Stop Order"; OpenSellStop( magicNumber, symbol, entryPrice, lotSize, sl, tp, orderComment ); class="type">bool ModifyPendingOrderByTicket( class="type">ulong orderTicket, class="type">class="kw">double newEntryPrice, class="type">int newSl, class="type">int newTp ); class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
挂单的批量改价与定向删除
遍历当前账户全部挂单时,用 OrdersTotal() 拿到总数,再靠 OrderGetTicket(x) 逐个取 ticket。下面这段把第一个抓到的 BUY_STOP 挂单入场价往上推了 40 倍点差的距离,止损止盈保持不动,改完就 break 跳出循环。 int totalOpenOders = OrdersTotal(); for(int x = 0; x < totalOpenOders; x++) { ulong orderTicket = OrderGetTicket(x); if(orderTicket > 0) { //-- Modify a buy stop order if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) { double newEntryPrice = OrderGetDouble( ORDER_PRICE_OPEN) + ((spread * 40) * symbolPoint ); int newSl = 0; //-- Do not modify the stop loss level int newTp = 0; //-- Don not modify the take profit level ModifyPendingOrderByTicket( orderTicket, newEntryPrice, newSl, newTp ); break; } } } 逐行看:第1行取挂单总数;for 从 0 扫到总数-1;OrderGetTicket 拿第 x 个单的 ticket;ticket>0 才有效;OrderGetInteger(ORDER_TYPE) 判断是不是 BUY_STOP;newEntryPrice 在原开盘价上加 spread*40*symbolPoint,即推远 40 个点差;newSl/newTp 传 0 表示不改;ModifyPendingOrderByTicket 执行改单;break 只改一张就停。外汇和贵金属点差跳动大,40 倍点差偏移可能让挂单远离现价,触发概率会明显下降,属高风险操作。 删除逻辑分单张和批量两套。DeletePendingOrderByTicket(ulong orderTicket) 按 ticket 删;下面循环取到第一张就删并 break。 bool DeletePendingOrderByTicket( ulong orderTicket ); for(int x = 0; x < totalOpenOders; x++) { ulong orderTicket = OrderGetTicket(x); if(orderTicket > 0) { DeletePendingOrderByTicket(orderTicket); break; } } 批量删用 DeleteAllPendingOrders,可传 symbol 和 magicNumber。空串+0 删全部;_Symbol+0 删当前品种;空串+101 删魔数 101;"EURUSD"+101 只删 EURUSD 且魔数 101 的。DeleteAllPendingOrders("", 0) 实测会清掉账户里所有挂单,回测时若误调可能把策略预埋单全清了。 bool DeleteAllPendingOrders( string symbol = ALL_SYMBOLS, ulong magicNumber = 0 ); //Deletes all orders in the account DeleteAllPendingOrders("", 0); //Deletes all orders belonging to the symbol DeleteAllPendingOrders(_Symbol, 0); //Deletes all orders that have a magic number 101 DeleteAllPendingOrders("", 101); //Deletes all EURUSD orders that have a magic number 101 DeleteAllPendingOrders("EURUSD", 101); 按类型删还有细分:DeleteAllBuyStops、DeleteAllBuyLimits、DeleteAllSellStops 签名都类似,symbol 默认 ALL_SYMBOLS、magicNumber 默认 0。比如 DeleteAllBuyStops("EURUSD", 101) 只清 EURUSD 上魔数 101 的 Buy Stop,不影响 Sell Stop 和其他魔数单。 bool DeleteAllBuyStops( string symbol = ALL_SYMBOLS, ulong magicNumber = 0 ); DeleteAllBuyStops("", 0); DeleteAllBuyStops(_Symbol, 0); DeleteAllBuyStops("", 101); DeleteAllBuyStops("EURUSD", 101); bool DeleteAllBuyLimits( string symbol = ALL_SYMBOLS, ulong magicNumber = 0 ); DeleteAllBuyLimits("", 0); DeleteAllBuyLimits(_Symbol, 0); DeleteAllBuyLimits("", 101); DeleteAllBuyLimits("GBPUSD", 101); bool DeleteAllSellStops( string symbol = ALL_SYMBOLS, ulong magicNumber = 0 ); DeleteAllSellStops("", 0); DeleteAllSellStops(_Symbol, 0); DeleteAllSellStops("", 101); DeleteAllSellStops("JPYUSD", 101); 开 MT5 新建 EA 把上面片段贴进 OnTick,先以 demo 账户挂几张不同品种和魔数的单,调 symbol 与 magicNumber 参数验证删除范围是否如预期,避免实盘误删。
class="type">int totalOpenOders = OrdersTotal(); for(class="type">int x = class="num">0; x < totalOpenOders; x++) { class="type">ulong orderTicket = OrderGetTicket(x); if(orderTicket > class="num">0) { class=class="str">"cmt">//-- Modify a buy stop order if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) { class="type">class="kw">double newEntryPrice = OrderGetDouble( ORDER_PRICE_OPEN) + ((spread * class="num">40) * symbolPoint ); class="type">int newSl = class="num">0; class=class="str">"cmt">//-- Do not modify the stop loss level class="type">int newTp = class="num">0; class=class="str">"cmt">//-- Don not modify the take profit level ModifyPendingOrderByTicket( orderTicket, newEntryPrice, newSl, newTp ); break; } } } class="type">bool DeletePendingOrderByTicket( class="type">ulong orderTicket ); for(class="type">int x = class="num">0; x < totalOpenOders; x++) { class="type">ulong orderTicket = OrderGetTicket(x); if(orderTicket > class="num">0) { DeletePendingOrderByTicket(orderTicket); break; } } class="type">bool DeleteAllPendingOrders( class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0 ); class=class="str">"cmt">//Deletes all orders in the account DeleteAllPendingOrders("", class="num">0); class=class="str">"cmt">//Deletes all orders belonging to the symbol DeleteAllPendingOrders(_Symbol, class="num">0); class=class="str">"cmt">//Deletes all orders that have a magic number class="num">101 DeleteAllPendingOrders("", class="num">101); class=class="str">"cmt">//Deletes all EURUSD orders that have a magic number class="num">101 DeleteAllPendingOrders("EURUSD", class="num">101); class="type">bool DeleteAllPendingOrders() class=class="str">"cmt">//Deletes all orders in the account DeleteAllPendingOrders(); class="type">bool DeleteAllBuyStops( class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0 ); class=class="str">"cmt">//Deletes all buy stops in the account DeleteAllBuyStops("", class="num">0); class=class="str">"cmt">//Deletes all buy stops belonging to the symbol DeleteAllBuyStops(_Symbol, class="num">0); class=class="str">"cmt">//Deletes all buy stops that have a magic number class="num">101 DeleteAllBuyStops("", class="num">101); class=class="str">"cmt">//Deletes all EURUSD buy stops that have a magic number class="num">101 DeleteAllBuyStops("EURUSD", class="num">101); class="type">bool DeleteAllBuyLimits( class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0 ); class=class="str">"cmt">//Deletes all buy limits in the account DeleteAllBuyLimits("", class="num">0); class=class="str">"cmt">//Deletes all buy limits belonging to the symbol DeleteAllBuyLimits(_Symbol, class="num">0); class=class="str">"cmt">//Deletes all buy limits that have a magic number class="num">101 DeleteAllBuyLimits("", class="num">101); class=class="str">"cmt">//Deletes all GBPUSD buy limits that have a magic number class="num">101 DeleteAllBuyLimits("GBPUSD", class="num">101); class="type">bool DeleteAllSellStops( class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0 ); class=class="str">"cmt">//Deletes all sell stops in the account DeleteAllSellStops("", class="num">0); class=class="str">"cmt">//Deletes all sell stops belonging to the symbol DeleteAllSellStops(_Symbol, class="num">0); class=class="str">"cmt">//Deletes all sell stops that have a magic number class="num">101 DeleteAllSellStops("", class="num">101); class=class="str">"cmt">//Deletes all JPYUSD sell stops that have a magic number class="num">101
「挂单批量清理与按魔法码统计的接口细节」
在 EA 维护挂单时,按品种和魔法码批量撤单比逐单遍历更省事。DeleteAllSellLimits 支持两个入参:symbol 传空串 "" 表示全品种,magicNumber 传 0 表示不区分魔法码;例如 DeleteAllSellLimits("AUDJPY", 101) 只清掉 AUDJPY 上魔法码为 101 的卖限价单。 DeleteAllMagicOrders 只收一个 ulong 参数,调用 DeleteAllMagicOrders(101) 会移除账户内所有魔法码 101 的挂单与成交外的待处理单。注意原文示例里 DeleteAllMagicOrders("", 0) 带了两个参数,与函数声明不一致,实际编译可能报错,开 MT5 验证时以声明为准。 统计类接口分三层:全账户(如 SellStopOrdersTotal 返回卖止损挂单数)、按魔法码(MagicBuyLimitOrdersTotal(101) 返回该魔法码的买限价单数)、按品种+魔法码(SymbolOrdersTotal(_Symbol, 0) 返回当前图品种全部挂单数)。 成交量统计对称提供:OrdersTotalVolume 给全账户挂单总手数,MagicSellStopOrdersTotalVolume(101) 给魔法码 101 的卖止损总手数。外汇与贵金属杠杆高,批量撤单前先用量化接口核对总手数,避免误删造成滑点风险。
DeleteAllSellStops("JPYUSD", class="num">101); class="type">bool DeleteAllSellLimits( class="type">class="kw">string symbol = ALL_SYMBOLS, class="type">ulong magicNumber = class="num">0 ); class=class="str">"cmt">//Deletes all sell limits in the account DeleteAllSellLimits("", class="num">0); class=class="str">"cmt">//Deletes all sell limits belonging to the symbol DeleteAllSellLimits(_Symbol, class="num">0); class=class="str">"cmt">//Deletes all sell limits that have a magic number class="num">101 DeleteAllSellLimits("", class="num">101); class=class="str">"cmt">//Deletes all AUDJPY sell limits that have a magic number class="num">101 DeleteAllSellLimits("AUDJPY", class="num">101); class="type">bool DeleteAllMagicOrders( class="type">ulong magicNumber ); class=class="str">"cmt">//-- Deletes all orders open in the account DeleteAllMagicOrders("", class="num">0); class=class="str">"cmt">//-- Deletes all orders that have a magic number class="num">101 DeleteAllMagicOrders(class="num">101); class="type">int BuyStopOrdersTotal(); class=class="str">"cmt">//Get the total number of open buy stops in the account BuyStopOrdersTotal(); class="type">int BuyLimitOrdersTotal(); class=class="str">"cmt">//Get the total number of open buy limits in the account BuyLimitOrdersTotal(); class="type">int SellStopOrdersTotal(); class=class="str">"cmt">//Get the total number of open sell stops in the account SellStopOrdersTotal(); class="type">int SellLimitOrdersTotal(); class=class="str">"cmt">//Get the total number of open sell limits in the account SellLimitOrdersTotal(); class="type">class="kw">double OrdersTotalVolume(); class=class="str">"cmt">//Get the total volume/lot of open orders in the account OrdersTotalVolume(); class="type">class="kw">double BuyStopOrdersTotalVolume(); class=class="str">"cmt">//Get the total volume/lot of open buy stops in the account BuyStopOrdersTotalVolume(); class="type">class="kw">double BuyLimitOrdersTotalVolume(); class=class="str">"cmt">//Get the total volume/lot of open buy limits in the account BuyLimitOrdersTotalVolume(); class="type">class="kw">double SellStopOrdersTotalVolume(); class=class="str">"cmt">//Get the total volume/lot of open sell stops in the account SellStopOrdersTotalVolume(); class="type">class="kw">double SellLimitOrdersTotalVolume(); class=class="str">"cmt">//Get the total volume/lot of open sell limits in the account SellLimitOrdersTotalVolume(); class="type">int MagicOrdersTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total open pending orders for magic number class="num">101 MagicOrdersTotal(class="num">101); class="type">int MagicBuyStopOrdersTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total open buy stop orders for magic number class="num">101 MagicBuyStopOrdersTotal(class="num">101); class="type">int MagicBuyLimitOrdersTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total open buy limit orders for magic number class="num">101 MagicBuyLimitOrdersTotal(class="num">101); class="type">int MagicSellStopOrdersTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total open sell stop orders for magic number class="num">101 MagicSellStopOrdersTotal(class="num">101); class="type">int MagicSellLimitOrdersTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total open sell limit orders for magic number class="num">101 MagicSellLimitOrdersTotal(class="num">101); class="type">class="kw">double MagicOrdersTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume/lot of all open orders for magic class="num">101 MagicOrdersTotalVolume(class="num">101); class="type">class="kw">double MagicBuyStopOrdersTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume/lot of all buy stop orders for magic class="num">101 MagicBuyStopOrdersTotalVolume(class="num">101); class="type">class="kw">double MagicBuyLimitOrdersTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume/lot of all buy limit orders for magic class="num">101 MagicBuyLimitOrdersTotalVolume(class="num">101); class="type">class="kw">double MagicSellStopOrdersTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume/lot of all sell stop orders for magic class="num">101 MagicSellStopOrdersTotalVolume(class="num">101); class="type">class="kw">double MagicSellLimitOrdersTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume/lot of all sell limit orders for magic class="num">101 MagicSellLimitOrdersTotalVolume(class="num">101); class="type">int SymbolOrdersTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total open orders for the symbol SymbolOrdersTotal(_Symbol, class="num">0);
◍ 按挂单类型拆账户持仓统计
在 MT5 里做挂单风控,最怕把所有 pending order 混在一起数。下面这组函数把统计维度拆到 BuyStop / BuyLimit / SellStop / SellLimit 四个类型,再叠加 magic number 过滤,能直接看出某套策略在某品种上挂了什么、挂了多少手。 SymbolBuyStopOrdersTotal 这类函数签名都是 (string symbol, ulong magicNumber),magic 传 0 表示不区分策略、统计该品种全部同类型挂单;传 101 则只数 magic=101 的 EA 挂的单。实盘验证时,先开一个 magic=101 的挂单 EA,再用 SymbolBuyStopOrdersTotal(_Symbol,101) 返回值和订单池比对,不一致就说明有遗漏或重复下单。 Volume 后缀的版本返回 double 手数而非笔数,比如 SymbolSellLimitOrdersTotalVolume(_Symbol,0) 给出当前品种全部卖限价的合计手数。外汇与贵金属杠杆高,挂单手数累加可能瞬间放大敞口,统计后须人工核对保证金占用。 AccountOrdersStatus 与 MagicOrdersStatus 用来把订单状态格式化成字符串:传 true 配合 Comment() 贴到图表右上角,传 false 配合 Print() 进 Experts 日志。小布盯盘时可让 EA 每 tick 调一次 Comment(AccountOrdersStatus(true)),省得手动翻终端。
class=class="str">"cmt">//Get the total open orders for the symbol and magic class="num">101 SymbolOrdersTotal(_Symbol, class="num">101); class="type">int SymbolBuyStopOrdersTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total buy stop orders for the symbol SymbolBuyStopOrdersTotal(_Symbol, class="num">0); class=class="str">"cmt">//Get the total buy stop orders for the symbol and magic number class="num">101 SymbolBuyStopOrdersTotal(_Symbol, class="num">101); class="type">int SymbolBuyLimitOrdersTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total buy limit orders for the symbol SymbolBuyLimitOrdersTotal(_Symbol, class="num">0); class=class="str">"cmt">//Get the total buy limit orders for the symbol and magic number class="num">101 SymbolBuyLimitOrdersTotal(_Symbol, class="num">101); class="type">int SymbolSellStopOrdersTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total sell stop orders for the symbol SymbolSellStopOrdersTotal(_Symbol, class="num">0); class=class="str">"cmt">//Get the total sell stop orders for the symbol and magic class="num">101 SymbolSellStopOrdersTotal(_Symbol, class="num">101); class="type">int SymbolSellLimitOrdersTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total sell limit orders for the symbol SymbolSellLimitOrdersTotal(_Symbol, class="num">0); class=class="str">"cmt">//Get the total sell limit orders for the symbol and magic class="num">101 SymbolSellLimitOrdersTotal(_Symbol, class="num">101); class="type">class="kw">double SymbolOrdersTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total orders volume/lot for the symbol SymbolOrdersTotalVolume(_Symbol, class="num">0); class=class="str">"cmt">//Get the total orders volume/lot for the symbol and magic class="num">101 SymbolOrdersTotalVolume(_Symbol, class="num">101); class="type">class="kw">double SymbolBuyStopOrdersTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total buy stops volume/lot for the symbol SymbolBuyStopOrdersTotalVolume(_Symbol, class="num">0); class=class="str">"cmt">//Get the total buy stops volume/lot for the symbol and magic class="num">101 SymbolBuyStopOrdersTotalVolume(_Symbol, class="num">101); class="type">class="kw">double SymbolBuyLimitOrdersTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total buy limits volume/lot for the symbol SymbolBuyLimitOrdersTotalVolume(_Symbol, class="num">0); class=class="str">"cmt">//Get the total buy limits volume/lot for symbol and magic class="num">101 SymbolBuyLimitOrdersTotalVolume(_Symbol, class="num">101); class="type">class="kw">double SymbolSellStopOrdersTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total sell stops volume/lot for symbol SymbolSellStopOrdersTotalVolume(_Symbol, class="num">0); class=class="str">"cmt">//Get the total sell stops volume/lot for symbol and magic class="num">101 SymbolSellStopOrdersTotalVolume(_Symbol, class="num">101); class="type">class="kw">double SymbolSellLimitOrdersTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total sell limits volume/lot for symbol SymbolSellLimitOrdersTotalVolume(_Symbol, class="num">0); class=class="str">"cmt">//Get the total sell limits volume/lot for symbol and magic class="num">101 SymbolSellLimitOrdersTotalVolume(_Symbol, class="num">101); class="type">class="kw">string AccountOrdersStatus( class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open orders class=class="str">"cmt">//formatted for the chart comments Comment(AccountOrdersStatus(true)); class=class="str">"cmt">//Print the status of all open orders class=class="str">"cmt">//formatted for the Experts tab Print(AccountOrdersStatus(false)); class=class="str">"cmt">//Activate an alert with the status of all class=class="str">"cmt">//open orders formatted for printing Print(AccountOrdersStatus(false)); class="type">class="kw">string MagicOrdersStatus( class="type">ulong magicNumber,
用魔数和品种筛出持仓状态
在 MT5 实盘里,同时盯多个 EA 的挂单容易乱。把订单状态按 magic number 或交易品种抽出来打印,是排查重复开仓、漏平仓的直接办法。 下面两段函数声明给出了两个维度:MagicOrdersStatus 只认 magic=101,SymbolOrdersStatus 再叠加当前品种 _Symbol。第二个布尔参数决定输出是给图表右下角 Comment 看,还是给 EA 日志 Print 看。 调用时若传 true,状态文本带换行适合图表悬浮;传 false 则纯文本进 Experts 标签。外汇与贵金属杠杆高,多 EA 同魔数可能触发同向加仓,用这段先看清底仓再动手。
class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//magic number class="num">101 formatted for the chart comments Comment(MagicOrdersStatus(class="num">101, true)); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//magic number class="num">101 formatted for the Experts tab Print(MagicOrdersStatus(class="num">101, false)); class=class="str">"cmt">//Activate an alert with the status of all open orders class=class="str">"cmt">//matching magic number class="num">101 formatted for printing Print(MagicOrdersStatus(class="num">101, false)); class="type">class="kw">string SymbolOrdersStatus( class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//the symbol and magic number class="num">101 formatted for the chart comments Comment(SymbolOrdersStatus(_Symbol, class="num">101, true)); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//the symbol and magic number class="num">101 formatted for the Experts tab Print(SymbolOrdersStatus(_Symbol, class="num">101, false)); class=class="str">"cmt">//Activate an alert with the status of all open orders class=class="str">"cmt">//matching the symbol and magic number class="num">101 formatted for printing Print(SymbolOrdersStatus(_Symbol, class="num">101, false));
class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//magic number class="num">101 formatted for the chart comments Comment(MagicOrdersStatus(class="num">101, true)); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//magic number class="num">101 formatted for the Experts tab Print(MagicOrdersStatus(class="num">101, false)); class=class="str">"cmt">//Activate an alert with the status of all open orders class=class="str">"cmt">//matching magic number class="num">101 formatted for printing Print(MagicOrdersStatus(class="num">101, false)); class="type">class="kw">string SymbolOrdersStatus( class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//the symbol and magic number class="num">101 formatted for the chart comments Comment(SymbolOrdersStatus(_Symbol, class="num">101, true)); class=class="str">"cmt">//Print the status of all open orders matching class=class="str">"cmt">//the symbol and magic number class="num">101 formatted for the Experts tab Print(SymbolOrdersStatus(_Symbol, class="num">101, false)); class=class="str">"cmt">//Activate an alert with the status of all open orders class=class="str">"cmt">//matching the symbol and magic number class="num">101 formatted for printing Print(SymbolOrdersStatus(_Symbol, class="num">101, false));
「用 EX5 库撑起挂单面板的图形交互」
在 MT5 里做挂单面板,核心不是画按钮,而是把界面操作和订单管理库接起来。这里用 MQL5 标准库的面板和对话框类做壳,背后调用 PendingOrdersManager.ex5 里的函数原型,负责按指定幻数开挂单、删挂单和统计订单数,代码量比从头写 GUI 逻辑小得多。 导入库时别全量拷函数声明,只挑用得上的原型。下面这段代码里,OpenBuyLimit / OpenSellStop 等四个函数管挂单下发,Magic*OrdersTotal 系列回传某幻数下的各类挂单计数,DeleteAll* 系列负责按类型或整组清理。 全局变量里有个细节值得抄:sl 和 tp 直接用点差推算,sl = spread * 50、tp = spread * 100。假设 EURUSD 点差是 8 点(8 * SYMBOL_POINT 计),初始止损自动填 400 点、止盈 800 点,加载 EA 后面板字段就有了起点值,不用手敲。 面板交互靠 OnChartEvent 抓按钮事件,再调库函数执行;同时播不同提示音区分成功或报错。订单删除按钮在未检测到同幻数挂单时置灰,一旦 OnTick 里轮询到 MagicOrdersTotal > 0 就激活变色的状态——这部分要在 OnTick 持续调库函数同步,EA 退出时 OnDeinit 释放资源。 开 MT5 用向导建 PendingOrdersPanel.mq5,把下面导入段和全局变量贴到 #property 下方,就能跑通一个最小可验证骨架。外汇和贵金属杠杆高,挂单未成交也可能因跳空失效,实盘前先在策略测试器用模拟品种验交互。
class="macro">#class="kw">import "Toolkit/PendingOrdersManager.ex5" class=class="str">"cmt">//-- Opening class="kw">import directive class=class="str">"cmt">//-- Function descriptions for the imported function prototypes class=class="str">"cmt">//-- Pending Orders Execution and Modification Functions class="type">bool OpenBuyLimit(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool OpenBuyStop(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool OpenSellLimit(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class="type">bool OpenSellStop(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double entryPrice, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string orderComment); class=class="str">"cmt">//-- class="type">int MagicOrdersTotal(class="type">ulong magicNumber); class="type">int MagicBuyStopOrdersTotal(class="type">ulong magicNumber); class="type">int MagicBuyLimitOrdersTotal(class="type">ulong magicNumber); class="type">int MagicSellStopOrdersTotal(class="type">ulong magicNumber); class="type">int MagicSellLimitOrdersTotal(class="type">ulong magicNumber); class=class="str">"cmt">//-- class="type">bool DeleteAllBuyStops(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllBuyLimits(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllSellStops(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllSellLimits(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool DeleteAllMagicOrders(class="type">ulong magicNumber); class="macro">#class="kw">import class=class="str">"cmt">//--- Closing class="kw">import directive class=class="str">"cmt">//-- Global variables class=class="str">"cmt">//----------------------- class="type">ulong magicNo = class="num">10101010; class="type">class="kw">double symbolPoint = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="type">long symbolDigits = SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); class="type">int spread = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); class="type">class="kw">double volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); class="type">int sl = (class="type">int)spread * class="num">50; class="type">int tp = (class="type">int)spread * class="num">100; class=class="str">"cmt">//-- Define some values for the main panel
◍ 面板与控件的坐标颜色宏定义
在 MT5 里手搓交易面板,第一步是把所有界面元素的几何与配色固化成宏,避免运行时反复算坐标。下面这段预处理指令把主面板锚在 X=350、Y=10 的子窗口 0,宽度拉到 1150(800+350),高度按 10 行控件推算约 410 像素,足够塞下买卖与挂单区。 控件统一高 40、边距 5,单行宽度直接用总宽除以 7 得到约 164,字体 9 号;买按钮蓝底白字、卖按钮深红底白字,编辑框分别用 AliceBlue 和 MistyRose 做背景色,肉眼区分多空输入区。 表头标签用 Navy 色,VOLUME/LOT 从 X=169 起排,OPENING PRICE 从 2 倍控件宽起排,SL 与 TP 分别落在 3 倍和 3.75 倍宽度位置,Y 统一为 10。外汇与贵金属波动快,这种硬布局在 1080p 屏上可能偏挤,调 GUI_OBJECTS_WIDTH 分母就能改密度。 代码里只列到买停按钮的宏名,后续挂单控件照此套路补即可,复制去 MQ5 头部编译能立刻看到骨架。
class="macro">#define MAIN_PANEL_NAME class="type">class="kw">string("Orders Panel - Trading: " + _Symbol + " - Magic No: " + IntegerToString(magicNo)) class="macro">#define MAIN_PANEL_SUBWINDOW class="num">0 class="macro">#define MAIN_PANEL_X1 class="num">350 class="macro">#define MAIN_PANEL_Y1 class="num">10 class="macro">#define MAIN_PANEL_WIDTH class="type">int(class="num">800 + MAIN_PANEL_X1) class="macro">#define MAIN_PANEL_X2 MAIN_PANEL_WIDTH class=class="str">"cmt">//-- Define the GUI objects general properties class="macro">#define GUI_OBJECTS_MARGIN class="num">5 class="macro">#define GUI_OBJECTS_HEIGHT class="num">40class=class="str">"cmt">//class="num">40 class="macro">#define GUI_OBJECTS_WIDTH class="type">int((MAIN_PANEL_WIDTH) / class="num">7) class="macro">#define GUI_OBJECTS_FONT_SIZE class="num">9class=class="str">"cmt">//class="num">10 class="macro">#define GUI_OBJECTS_HEADER_FONT_SIZE GUI_OBJECTS_FONT_SIZE class=class="str">"cmt">//----- class="macro">#define MAIN_PANEL_HEIGHT class="type">int(((GUI_OBJECTS_HEIGHT + (GUI_OBJECTS_MARGIN * class="num">2)) * class="num">10) + MAIN_PANEL_Y1) class="macro">#define MAIN_PANEL_Y2 MAIN_PANEL_HEIGHT class=class="str">"cmt">//-- Define the GUI objects colors class="macro">#define GUI_OBJECTS_HEADING_COLOR clrNavy class="macro">#define GUI_OBJECTS_BUY_BTN_COLOR clrWhite class="macro">#define GUI_OBJECTS_BUY_BTN_BG_COLOR clrBlue class="macro">#define GUI_OBJECTS_BUY_EDIT_COLOR clrBlue class="macro">#define GUI_OBJECTS_BUY_EDIT_BG_COLOR clrAliceBlue class="macro">#define GUI_OBJECTS_SELL_BTN_COLOR clrWhite class="macro">#define GUI_OBJECTS_SELL_BTN_BG_COLOR clrCrimson class="macro">#define GUI_OBJECTS_SELL_EDIT_COLOR clrMaroon class="macro">#define GUI_OBJECTS_SELL_EDIT_BG_COLOR clrMistyRose class=class="str">"cmt">/*------------------------------------------------------ * Define GUI components for the heading labels **** *-----------------------------------------------------*/ class=class="str">"cmt">//-- Define values for the lotVolHeaderLabel class="macro">#define VOLUME_LOT_LABEL_NAME "Volume Lot Header Label" class="macro">#define VOLUME_LOT_LABEL_SUBWINDOW class="num">0 class="macro">#define VOLUME_LOT_LABEL_X1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_WIDTH) class="macro">#define VOLUME_LOT_LABEL_Y1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define VOLUME_LOT_LABEL_TEXT "VOLUME/LOT" class=class="str">"cmt">//-- Define values for the openPriceHeaderLabel class="macro">#define OPEN_PRICE_LABEL_NAME "Open Price Header Label" class="macro">#define OPEN_PRICE_LABEL_SUBWINDOW class="num">0 class="macro">#define OPEN_PRICE_LABEL_X1(class="type">int(GUI_OBJECTS_WIDTH) * class="num">2) class="macro">#define OPEN_PRICE_LABEL_Y1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define OPEN_PRICE_LABEL_TEXT "OPENING PRICE" class=class="str">"cmt">//-- Define values for the slHeaderLabel class="macro">#define SL_LABEL_NAME "Sl Header Label" class="macro">#define SL_LABEL_SUBWINDOW class="num">0 class="macro">#define SL_LABEL_X1(class="type">int(GUI_OBJECTS_WIDTH) * class="num">3) class="macro">#define SL_LABEL_Y1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define SL_LABEL_TEXT "SL(Pips)" class=class="str">"cmt">//-- Define values for the tpHeaderLabel class="macro">#define TP_LABEL_NAME "Tp Header Label" class="macro">#define TP_LABEL_SUBWINDOW class="num">0 class="macro">#define TP_LABEL_X1(class="type">int(GUI_OBJECTS_WIDTH) * class="num">3.75) class="macro">#define TP_LABEL_Y1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define TP_LABEL_TEXT "TP(Pips)" class=class="str">"cmt">/*------------------------------------------------------ * Define Buy Stop Order GUI components **** *-----------------------------------------------------*/ class=class="str">"cmt">//-- Define values for the buyStopBtn class="macro">#define BUY_STOP_BTN_NAME "Buy Stop Button"
挂单面板的控件坐标怎么排
在 MT5 自定义面板里做 Buy Stop / Sell Stop 下单区,最省事的办法是用宏把每个控件的名字和左上角坐标一次性定死,后面创建对象时直接引用,不用在代码里到处算像素。 Buy Stop 这一行所有控件都贴在子窗口 0,Y 坐标统一取 GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT,也就是第一行按钮和编辑框的底沿;X 方向从 BUY_STOP_BTN_X1 的左边距开始,手数框偏移一个 GUI_OBJECTS_WIDTH,开价框偏移两个宽度,SL 框三个,TP 框 3.7 个——用 3.7 而不是 4,是为了给 TP 框右侧留点空隙,避免和下一控件黏边。 Sell Stop 整行照搬这个逻辑,只是 Y 坐标乘了 2,落在第二行:(GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * 2。这样两行挂单控件在视觉上等距对齐,改 GUI_OBJECTS_MARGIN 或 GUI_OBJECTS_WIDTH 时整片跟着动。 外汇和贵金属挂单受点差与滑点影响,这类面板只是把下单参数集中化,实际成交价可能偏离编辑框里的 Open Price,属于高风险操作,参数填错容易吃意外单。
class="macro">#define BUY_STOP_BTN_SUBWINDOW class="num">0 class="macro">#define BUY_STOP_BTN_X1 GUI_OBJECTS_MARGIN class="macro">#define BUY_STOP_BTN_Y1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) class="macro">#define BUY_STOP_BTN_TEXT "BUY STOP" class=class="str">"cmt">//-- Define values for the buyStopVolumeLotEdit class="macro">#define BUY_STOP_VOLUME_LOT_EDIT_NAME "Buy Stop Volume Lot Edit" class="macro">#define BUY_STOP_VOLUME_LOT_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_STOP_VOLUME_LOT_EDIT_X1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_WIDTH) class="macro">#define BUY_STOP_VOLUME_LOT_EDIT_Y1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) class=class="str">"cmt">//-- Define values for the buyStopOpenPriceEdit class="macro">#define BUY_STOP_OPEN_PRICE_EDIT_NAME "Buy Stop Open Price Edit" class="macro">#define BUY_STOP_OPEN_PRICE_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_STOP_OPEN_PRICE_EDIT_X1 class="type">int((GUI_OBJECTS_WIDTH) * class="num">2) class="macro">#define BUY_STOP_OPEN_PRICE_EDIT_Y1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) class=class="str">"cmt">//-- Define values for the buyStopSlEdit class="macro">#define BUY_STOP_SL_EDIT_NAME "Buy Stop SL Edit" class="macro">#define BUY_STOP_SL_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_STOP_SL_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3) class="macro">#define BUY_STOP_SL_EDIT_Y1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) class=class="str">"cmt">//-- Define values for the buyStopTpEdit class="macro">#define BUY_STOP_TP_EDIT_NAME "Buy Stop TP Edit" class="macro">#define BUY_STOP_TP_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_STOP_TP_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3.7) class="macro">#define BUY_STOP_TP_EDIT_Y1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) class=class="str">"cmt">/*------------------------------------------------------ * Define Sell Stop Order GUI components **** *-----------------------------------------------------*/ class=class="str">"cmt">//-- Define values for the sellStopBtn class="macro">#define SELL_STOP_BTN_NAME "Sell Stop Button" class="macro">#define SELL_STOP_BTN_SUBWINDOW class="num">0 class="macro">#define SELL_STOP_BTN_X1 GUI_OBJECTS_MARGIN class="macro">#define SELL_STOP_BTN_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">2) class="macro">#define SELL_STOP_BTN_TEXT "SELL STOP" class=class="str">"cmt">//-- Define values for the sellStopVolumeLotEdit class="macro">#define SELL_STOP_VOLUME_LOT_EDIT_NAME "Sell Stop Volume Lot Edit" class="macro">#define SELL_STOP_VOLUME_LOT_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_STOP_VOLUME_LOT_EDIT_X1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_WIDTH) class="macro">#define SELL_STOP_VOLUME_LOT_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">2) class=class="str">"cmt">//-- Define values for the sellStopOpenPriceEdit class="macro">#define SELL_STOP_OPEN_PRICE_EDIT_NAME "Sell Stop Open Price Edit" class="macro">#define SELL_STOP_OPEN_PRICE_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_STOP_OPEN_PRICE_EDIT_X1 class="type">int((GUI_OBJECTS_WIDTH) * class="num">2) class="macro">#define SELL_STOP_OPEN_PRICE_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">2) class=class="str">"cmt">//-- Define values for the sellStopSlEdit class="macro">#define SELL_STOP_SL_EDIT_NAME "Sell Stop SL Edit" class="macro">#define SELL_STOP_SL_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_STOP_SL_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3) class="macro">#define SELL_STOP_SL_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">2) class=class="str">"cmt">//-- Define values for the sellStopTpEdit class="macro">#define SELL_STOP_TP_EDIT_NAME "Sell Stop TP Edit" class="macro">#define SELL_STOP_TP_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_STOP_TP_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3.7) class="macro">#define SELL_STOP_TP_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">2)
「挂单面板的限价单控件坐标布局」
在 MT5 自建交易面板时,Buy Limit 与 Sell Limit 的 GUI 组件通常用预编译宏集中定义,避免在主逻辑里散落硬编码坐标。上面这段把两类挂单按钮、手数框、挂单价框、止损框、止盈框的命名与位置一次性铺开,主图子窗口(subwindow 0)里纵向排了第 3、第 4 行。 注意 Y 坐标的算法:Buy Limit 行用 (GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * 3,Sell Limit 行乘 4,说明每行高度就是 margin 加控件高度,行号直接当倍数。X 方向则按 GUI_OBJECTS_WIDTH 的 1 倍、2 倍、3 倍、3.7 倍错开,TP 框故意留了 0.7 个宽度的间隙,可能是为了和 SL 框拉开点击盲区。 外汇与贵金属挂单涉及杠杆与滑点,限价单未成交前不改变敞口,但误触面板发单会带来真实高风险,坐标定义阶段就该把控件间距调得手感清晰。 把这段宏直接塞进你的 EA 头文件,改 GUI_OBJECTS_MARGIN 和 GUI_OBJECTS_WIDTH 两个常量,就能在 MT5 里直观看到挂单区重排,比运行时算坐标省事。
class=class="str">"cmt">/*------------------------------------------------------ * Define Buy Limit Order GUI components **** *-----------------------------------------------------*/ class=class="str">"cmt">//-- Define values for the buyLimitBtn class="macro">#define BUY_LIMIT_BTN_NAME "Buy Limit Button" class="macro">#define BUY_LIMIT_BTN_SUBWINDOW class="num">0 class="macro">#define BUY_LIMIT_BTN_X1 GUI_OBJECTS_MARGIN class="macro">#define BUY_LIMIT_BTN_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">3) class="macro">#define BUY_LIMIT_BTN_TEXT "BUY LIMIT" class=class="str">"cmt">//-- Define values for the buyLimitVolumeLotEdit class="macro">#define BUY_LIMIT_VOLUME_LOT_EDIT_NAME "Buy Limit Volume Lot Edit" class="macro">#define BUY_LIMIT_VOLUME_LOT_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_LIMIT_VOLUME_LOT_EDIT_X1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_WIDTH) class="macro">#define BUY_LIMIT_VOLUME_LOT_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">3) class=class="str">"cmt">//-- Define values for the buySLimitOpenPriceEdit class="macro">#define BUY_LIMIT_OPEN_PRICE_EDIT_NAME "Buy Limit Open Price Edit" class="macro">#define BUY_LIMIT_OPEN_PRICE_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_LIMIT_OPEN_PRICE_EDIT_X1 class="type">int((GUI_OBJECTS_WIDTH) * class="num">2) class="macro">#define BUY_LIMIT_OPEN_PRICE_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">3) class=class="str">"cmt">//-- Define values for the buyLimitSlEdit class="macro">#define BUY_LIMIT_SL_EDIT_NAME "Buy Limit SL Edit" class="macro">#define BUY_LIMIT_SL_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_LIMIT_SL_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3) class="macro">#define BUY_LIMIT_SL_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">3) class=class="str">"cmt">//-- Define values for the buyLimitTpEdit class="macro">#define BUY_LIMIT_TP_EDIT_NAME "Buy Limit TP Edit" class="macro">#define BUY_LIMIT_TP_EDIT_SUBWINDOW class="num">0 class="macro">#define BUY_LIMIT_TP_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3.7) class="macro">#define BUY_LIMIT_TP_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">3) class=class="str">"cmt">/*------------------------------------------------------ * Define Sell Limit Order GUI components **** *-----------------------------------------------------*/ class=class="str">"cmt">//-- Define values for the sellLimitBtn class="macro">#define SELL_LIMIT_BTN_NAME "Sell Limit Button" class="macro">#define SELL_LIMIT_BTN_SUBWINDOW class="num">0 class="macro">#define SELL_LIMIT_BTN_X1 GUI_OBJECTS_MARGIN class="macro">#define SELL_LIMIT_BTN_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">4) class="macro">#define SELL_LIMIT_BTN_TEXT "SELL LIMIT" class=class="str">"cmt">//-- Define values for the sellLimitVolumeLotEdit class="macro">#define SELL_LIMIT_VOLUME_LOT_EDIT_NAME "Sell Limit Volume Lot Edit" class="macro">#define SELL_LIMIT_VOLUME_LOT_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_LIMIT_VOLUME_LOT_EDIT_X1 class="type">int(GUI_OBJECTS_MARGIN + GUI_OBJECTS_WIDTH) class="macro">#define SELL_LIMIT_VOLUME_LOT_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">4) class=class="str">"cmt">//-- Define values for the sellLimitOpenPriceEdit class="macro">#define SELL_LIMIT_OPEN_PRICE_EDIT_NAME "Sell Limit Open Price Edit" class="macro">#define SELL_LIMIT_OPEN_PRICE_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_LIMIT_OPEN_PRICE_EDIT_X1 class="type">int((GUI_OBJECTS_WIDTH) * class="num">2) class="macro">#define SELL_LIMIT_OPEN_PRICE_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">4) class=class="str">"cmt">//-- Define values for the sellLimitSlEdit class="macro">#define SELL_LIMIT_SL_EDIT_NAME "Sell Limit SL Edit" class="macro">#define SELL_LIMIT_SL_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_LIMIT_SL_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3) class="macro">#define SELL_LIMIT_SL_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">4)
◍ 挂单面板的状态标签与撤单按钮布局
这段宏定义把 EA 图形界面里的挂单状态区拆得很细:先给「Sell Limit TP Edit」定位,X 坐标取 GUI_OBJECTS_WIDTH 的 3.7 倍强转 int,Y 坐标按 (GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * 4 算,说明它排在面板第四行高度附近。
订单状态区用一组颜色与尺寸常量控制观感:STATUS_EDIT_WIDTH 等于 (MAIN_PANEL_WIDTH / 1.485) 再减两倍边距,背景刷 LemonChiffon、边框 MidnightBlue;三个撤单按钮的背景色分别用 RoyalBlue、Crimson、MediumVioletRed 区分买停、卖停、全删,按钮宽度是 STATUS_EDIT_WIDTH / 1.93 减三倍边距,肉眼能直接靠颜色分辨危险操作。
标签文字在编译期拼好:MAGIC_ORDER_STATUS_LABEL_TEXT 把 magicNo 转成字符串并接上「TOTAL OPEN ORDERS: 」,运行时只需填数字。两个删除按钮的 Y 坐标都挂在 MAGIC_ORDER_STATUS_EDIT_Y1 之下再加一行高度与边距,X 方向第二个按钮靠 DELETE_ORDERS_BTN_WIDTH 错开,避免重叠。
开 MT5 把这套宏丢进你的面板工程,改 MAIN_PANEL_WIDTH 或 GUI_OBJECTS_MARGIN 就能看到挂单位置和按钮间距随之重排;外汇与贵金属杠杆高,面板只是辅助,撤单动作仍可能因滑点不按预期成交。
class=class="str">"cmt">//-- Define values for the sellLimitTpEdit class="macro">#define SELL_LIMIT_TP_EDIT_NAME "Sell Limit TP Edit" class="macro">#define SELL_LIMIT_TP_EDIT_SUBWINDOW class="num">0 class="macro">#define SELL_LIMIT_TP_EDIT_X1 class="type">int(GUI_OBJECTS_WIDTH * class="num">3.7) class="macro">#define SELL_LIMIT_TP_EDIT_Y1 class="type">int((GUI_OBJECTS_MARGIN + GUI_OBJECTS_HEIGHT) * class="num">4) class=class="str">"cmt">/*------------------------------------------------------ * Define Order Status GUI components **** *-----------------------------------------------------*/ class=class="str">"cmt">//-- Define values for the orders status class="macro">#define STATUS_HEADER_FONT_SIZE class="type">int(GUI_OBJECTS_FONT_SIZE)class=class="str">"cmt">// / class="num">1.1) class="macro">#define STATUS_EDIT_FONT_SIZE class="type">int(GUI_OBJECTS_FONT_SIZE)class=class="str">"cmt">// / class="num">1.1) class="macro">#define STATUS_EDIT_WIDTH class="type">int((MAIN_PANEL_WIDTH / class="num">1.485) - (GUI_OBJECTS_MARGIN * class="num">2)) class="macro">#define STATUS_EDIT_COLOR clrBlack class="macro">#define STATUS_EDIT_BG_COLOR clrLemonChiffon class="macro">#define STATUS_EDIT_BORDER_COLOR clrMidnightBlue class="macro">#define DELETE_ORDERS_BTN_COLOR clrLightYellow class="macro">#define DELETE_BUY_ORDERS_BTN_BG_COLOR clrRoyalBlue class="macro">#define DELETE_SELL_ORDERS_BTN_BG_COLOR clrCrimson class="macro">#define DELETE_ALL_ORDERS_BTN_BG_COLOR clrMediumVioletRed class="macro">#define DELETE_ORDERS_BTN_BORDER_COLOR clrBlack class="macro">#define DELETE_ORDERS_BTN_WIDTH class="type">int((STATUS_EDIT_WIDTH / class="num">1.93) - (GUI_OBJECTS_MARGIN * class="num">3)) class="macro">#define DELETE_ORDERS_BTN_FONT_SIZE class="type">int((GUI_OBJECTS_FONT_SIZE))class=class="str">"cmt">// / class="num">1.05) class=class="str">"cmt">//-- Define values for the magicOrderStatusLabel class="macro">#define MAGIC_ORDER_STATUS_LABEL_NAME "Magic Order Status Label" class="macro">#define MAGIC_ORDER_STATUS_LABEL_SUBWINDOW class="num">0 class="macro">#define MAGIC_ORDER_STATUS_LABEL_X1 class="type">int(GUI_OBJECTS_MARGIN * class="num">3) class="macro">#define MAGIC_ORDER_STATUS_LABEL_Y1 class="type">int((GUI_OBJECTS_HEIGHT * class="num">6) + (GUI_OBJECTS_MARGIN * class="num">2)) class="macro">#define MAGIC_ORDER_STATUS_LABEL_TEXT class="type">class="kw">string("MAGIC No: " + IntegerToString(magicNo) + " - TOTAL OPEN ORDERS: ") class=class="str">"cmt">//-- Define values for the magicOrdersStatusEdit class="macro">#define MAGIC_ORDER_STATUS_EDIT_NAME "Magic Order Status Edit" class="macro">#define MAGIC_ORDER_STATUS_EDIT_SUBWINDOW class="num">0 class="macro">#define MAGIC_ORDER_STATUS_EDIT_X1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define MAGIC_ORDER_STATUS_EDIT_Y1 class="type">int((MAGIC_ORDER_STATUS_LABEL_Y1) + (GUI_OBJECTS_HEIGHT / class="num">1.7)) class=class="str">"cmt">//-- Define values for the deleteAllMagicBuyStopsBtn class="macro">#define DELETE_ALL_MAGIC_BUY_STOPS_BTN_NAME "Delete All Magic Buy Stops Btn" class="macro">#define DELETE_ALL_MAGIC_BUY_STOPS_BTN_SUBWINDOW class="num">0 class="macro">#define DELETE_ALL_MAGIC_BUY_STOPS_BTN_X1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define DELETE_ALL_MAGIC_BUY_STOPS_BTN_Y1 class="type">int((MAGIC_ORDER_STATUS_EDIT_Y1) + (GUI_OBJECTS_HEIGHT + GUI_OBJECTS_MARGIN)) class="macro">#define DELETE_ALL_MAGIC_BUY_STOPS_BTN_TEXT "DELETE ALL MAGIC BUY STOPS" class=class="str">"cmt">//-- Define values for the deleteAllMagicSellStopsBtn class="macro">#define DELETE_ALL_MAGIC_SELL_STOPS_BTN_NAME "Delete All Magic Sell Stops Btn" class="macro">#define DELETE_ALL_MAGIC_SELL_STOPS_BTN_SUBWINDOW class="num">0 class="macro">#define DELETE_ALL_MAGIC_SELL_STOPS_BTN_X1 class="type">int((GUI_OBJECTS_MARGIN * class="num">3) + DELETE_ORDERS_BTN_WIDTH) class="macro">#define DELETE_ALL_MAGIC_SELL_STOPS_BTN_Y1 class="type">int((MAGIC_ORDER_STATUS_EDIT_Y1) + (GUI_OBJECTS_HEIGHT + GUI_OBJECTS_MARGIN)) class="macro">#define DELETE_ALL_MAGIC_SELL_STOPS_BTN_TEXT "DELETE ALL MAGIC SELL STOPS" class=class="str">"cmt">//-- Define values for the deleteAllMagicBuyLimitsBtn
挂单面板的控件坐标与初始报价
这段定义把 MT5 自定义挂单面板的几个关键删除按钮和初始挂单价算了出来。删除 Magic Buy Limits 的按钮 X 坐标取 GUI_OBJECTS_MARGIN*2 再强转 int,Y 坐标则叠在删除 Buy Stops 按钮之下再加一个控件高度与间距,说明界面是纵向流式排布,改 GUI_OBJECTS_MARGIN 会整体平移。 初始挂单价全部以 SYMBOL_ASK 为锚:BuyStop 和 SellLimit 在 Ask 上加 spread*20*symbolPoint,BuyLimit 和 SellStop 在 Ask 下减同样数值。以 EURUSD 点值 0.00001、spread 1.2 点计,偏移约 0.00024,开面板前先核对 symbolPoint 与 spread 变量已正确赋值,否则挂单价会偏离预期。 面板主体用标准库 CAppDialog 承载,买卖停损限价各配 4 个 CEdit(手数、开价、SL、TP)加 1 个 CButton,另有 5 个删除按钮对象。外汇与贵金属杠杆高,这类一键删 Magic 单的按钮误触可能瞬间平掉批量挂单,实盘前应在策略测试器用演示账户验一遍坐标与点击事件。
class="macro">#define DELETE_ALL_MAGIC_BUY_LIMITS_BTN_NAME "Delete All Magic Buy Limits Btn" class="macro">#define DELETE_ALL_MAGIC_BUY_LIMITS_BTN_SUBWINDOW class="num">0 class="macro">#define DELETE_ALL_MAGIC_BUY_LIMITS_BTN_X1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define DELETE_ALL_MAGIC_BUY_LIMITS_BTN_Y1 class="type">int((DELETE_ALL_MAGIC_BUY_STOPS_BTN_Y1) + (GUI_OBJECTS_HEIGHT + GUI_OBJECTS_MARGIN)) class="macro">#define DELETE_ALL_MAGIC_BUY_LIMITS_BTN_TEXT "DELETE ALL MAGIC BUY LIMITS" class=class="str">"cmt">//-- Define values for the deleteAllMagicSellLimitsBtn class="macro">#define DELETE_ALL_MAGIC_SELL_LIMITS_BTN_NAME "Delete All Magic Sell Limits Btn" class="macro">#define DELETE_ALL_MAGIC_SELL_LIMITS_BTN_SUBWINDOW class="num">0 class="macro">#define DELETE_ALL_MAGIC_SELL_LIMITS_BTN_X1 class="type">int((GUI_OBJECTS_MARGIN * class="num">3) + DELETE_ORDERS_BTN_WIDTH) class="macro">#define DELETE_ALL_MAGIC_SELL_LIMITS_BTN_Y1 DELETE_ALL_MAGIC_BUY_LIMITS_BTN_Y1class=class="str">"cmt">//class="type">int((MAGIC_ORDER_STATUS_EDIT_Y1) + (GUI_OBJECTS_HEIGHT + GUI_OBJECTS_MARGIN)) class="macro">#define DELETE_ALL_MAGIC_SELL_LIMITS_BTN_TEXT "DELETE ALL MAGIC SELL LIMITS" class=class="str">"cmt">//-- Define values for the deleteAllMagicOrdersBtn class="macro">#define DELETE_ALL_MAGIC_ORDERS_BTN_NAME "Delete All Magic Orders Btn" class="macro">#define DELETE_ALL_MAGIC_ORDERS_BTN_SUBWINDOW class="num">0 class="macro">#define DELETE_ALL_MAGIC_ORDERS_BTN_X1 class="type">int(GUI_OBJECTS_MARGIN * class="num">2) class="macro">#define DELETE_ALL_MAGIC_ORDERS_BTN_Y1 class="type">int((DELETE_ALL_MAGIC_BUY_LIMITS_BTN_Y1) + (GUI_OBJECTS_HEIGHT + GUI_OBJECTS_MARGIN)) class="macro">#define DELETE_ALL_MAGIC_ORDERS_BTN_TEXT "DELETE ALL MAGIC PENDING ORDERS" class=class="str">"cmt">//-- Include the MQL5 standard library for panels and dialogs class="macro">#include <Controls\Dialog.mqh> class="macro">#include <Controls\Button.mqh> class="macro">#include <Controls\Label.mqh> class="macro">#include <Controls\Edit.mqh> class=class="str">"cmt">//-- Create objects for the included standard classes CAppDialog mainPanelWindow; class=class="str">"cmt">//-- Create the header label components CLabel lotVolHeaderLabel; CLabel openPriceHeaderLabel; CLabel slHeaderLabel; CLabel tpHeaderLabel; class=class="str">"cmt">//-- Create the buy stop GUI components class=class="str">"cmt">//--BuyStopBtn CButton buyStopBtn; class=class="str">"cmt">//--BuyStopEdits CEdit buyStopVolumeLotEdit; CEdit buyStopOpenPriceEdit; CEdit buyStopSlEdit; CEdit buyStopTpEdit; class=class="str">"cmt">//-- Create the sell stop GUI components class=class="str">"cmt">//--SellStopBtn CButton sellStopBtn; class=class="str">"cmt">//--sellStopEdits CEdit sellStopVolumeLotEdit; CEdit sellStopOpenPriceEdit; CEdit sellStopSlEdit; CEdit sellStopTpEdit; class=class="str">"cmt">//-- Create the buy limit GUI components class=class="str">"cmt">//--BuyLimitBtn CButton buyLimitBtn; class=class="str">"cmt">//--BuyLimitEdits CEdit buyLimitVolumeLotEdit; CEdit buyLimitOpenPriceEdit; CEdit buyLimitSlEdit; CEdit buyLimitTpEdit; class=class="str">"cmt">//-- Create the sell limit GUI components class=class="str">"cmt">//--sellLimitBtn CButton sellLimitBtn; class=class="str">"cmt">//--sellLimitEdits CEdit sellLimitVolumeLotEdit; CEdit sellLimitOpenPriceEdit; CEdit sellLimitSlEdit; CEdit sellLimitTpEdit; class=class="str">"cmt">//-- Create the order status GUI components class=class="str">"cmt">//--magic order status CLabel magicOrderStatusLabel; CEdit magicOrdersStatusEdit; class=class="str">"cmt">//--Magic orders class="kw">delete buttons CButton deleteAllMagicBuyStopsBtn; CButton deleteAllMagicSellStopsBtn; CButton deleteAllMagicBuyLimitsBtn; CButton deleteAllMagicSellLimitsBtn; CButton deleteAllMagicOrdersBtn; class=class="str">"cmt">//-- Default starting entry prices for different pending orders class="type">class="kw">double buyStopEntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) + ((spread * class="num">20) * symbolPoint); class="type">class="kw">double buyLimitEntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - ((spread * class="num">20) * symbolPoint); class="type">class="kw">double sellStopEntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - ((spread * class="num">20) * symbolPoint); class="type">class="kw">double sellLimitEntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) + ((spread * class="num">20) * symbolPoint); class=class="str">"cmt">//-- String values for the orders status class="type">class="kw">string magicOrderStatus; class="type">void CreateGui() { class=class="str">"cmt">//-- Create the orders panel mainPanelWindow.Create(
「挂单面板的标签与按钮初始化」
这段逻辑在主面板窗口里逐个创建表头标签和 Buy Stop 挂单控件,坐标全部由宏常量传入,宽度高度则复用 GUI_OBJECTS_WIDTH / HEIGHT 统一规范。 先看表头:lotVolHeaderLabel、openPriceHeaderLabel 用完整 GUI_OBJECTS_WIDTH,而 slHeaderLabel 与 tpHeaderLabel 的宽度被切成 int(GUI_OBJECTS_WIDTH / 1.4),说明 SL/TP 栏在视觉上比手数、开价栏窄约 29%,布局上留了紧凑空间。 Buy Stop 按钮与编辑框的 Create 调用里宽高先传 0,再靠 Width()/Height() 二次赋值,这种写法方便后续用同一套 GUI_OBJECTS 尺寸批量刷新。buyStopVolumeLotEdit 直接把 volumeLot 用 DoubleToString 塞进文本,buyStopOpenPriceEdit 则按 symbolDigits 精度显示 buyStopEntryPrice,用户输入和预填报价由此分离。 开 MT5 把这段接进你的面板类,改一下 GUI_OBJECTS_WIDTH 看 SL/TP 标签是否如预期收窄,能立刻验证布局假设。外汇与贵金属挂单界面涉及高杠杆风险,参数调整仅用于功能验证,不代表任何收益可能。
class="num">0, MAIN_PANEL_NAME, MAIN_PANEL_SUBWINDOW, MAIN_PANEL_X1, MAIN_PANEL_Y1, MAIN_PANEL_X2, MAIN_PANEL_Y2 ); class=class="str">"cmt">/*------------------------------------------------------ * Header Labels GUI components creation **** *-----------------------------------------------------*/ class=class="str">"cmt">//--Create the lot volume header label lotVolHeaderLabel.Create( class="num">0, VOLUME_LOT_LABEL_NAME, VOLUME_LOT_LABEL_SUBWINDOW, VOLUME_LOT_LABEL_X1, VOLUME_LOT_LABEL_Y1, GUI_OBJECTS_WIDTH, GUI_OBJECTS_HEIGHT ); lotVolHeaderLabel.Text(VOLUME_LOT_LABEL_TEXT); lotVolHeaderLabel.Color(GUI_OBJECTS_HEADING_COLOR); lotVolHeaderLabel.FontSize(GUI_OBJECTS_HEADER_FONT_SIZE); mainPanelWindow.Add(lotVolHeaderLabel); class=class="str">"cmt">//--Create the open price header label openPriceHeaderLabel.Create( class="num">0, OPEN_PRICE_LABEL_NAME, OPEN_PRICE_LABEL_SUBWINDOW, OPEN_PRICE_LABEL_X1, OPEN_PRICE_LABEL_Y1, GUI_OBJECTS_WIDTH, GUI_OBJECTS_HEIGHT ); openPriceHeaderLabel.Text(OPEN_PRICE_LABEL_TEXT); openPriceHeaderLabel.Color(GUI_OBJECTS_HEADING_COLOR); openPriceHeaderLabel.FontSize(GUI_OBJECTS_HEADER_FONT_SIZE); mainPanelWindow.Add(openPriceHeaderLabel); class=class="str">"cmt">//--Create the sl header label slHeaderLabel.Create( class="num">0, SL_LABEL_NAME, SL_LABEL_SUBWINDOW, SL_LABEL_X1, SL_LABEL_Y1, class="type">int(GUI_OBJECTS_WIDTH / class="num">1.4), GUI_OBJECTS_HEIGHT ); slHeaderLabel.Text(SL_LABEL_TEXT); slHeaderLabel.Color(GUI_OBJECTS_HEADING_COLOR); slHeaderLabel.FontSize(GUI_OBJECTS_HEADER_FONT_SIZE); mainPanelWindow.Add(slHeaderLabel); class=class="str">"cmt">//--Create the tp header label tpHeaderLabel.Create( class="num">0, TP_LABEL_NAME, TP_LABEL_SUBWINDOW, TP_LABEL_X1, TP_LABEL_Y1, class="type">int(GUI_OBJECTS_WIDTH / class="num">1.4), GUI_OBJECTS_HEIGHT ); tpHeaderLabel.Text(TP_LABEL_TEXT); tpHeaderLabel.Color(GUI_OBJECTS_HEADING_COLOR); tpHeaderLabel.FontSize(GUI_OBJECTS_HEADER_FONT_SIZE); mainPanelWindow.Add(tpHeaderLabel); class=class="str">"cmt">/*------------------------------------------------------ * Buy Stop Order GUI components creation **** *-----------------------------------------------------*/ class=class="str">"cmt">//--Create the open buy stop button buyStopBtn.Create( class="num">0, BUY_STOP_BTN_NAME, BUY_STOP_BTN_SUBWINDOW, BUY_STOP_BTN_X1, BUY_STOP_BTN_Y1, class="num">0, class="num">0 ); buyStopBtn.Text(BUY_STOP_BTN_TEXT); buyStopBtn.Width(GUI_OBJECTS_WIDTH); buyStopBtn.Height(GUI_OBJECTS_HEIGHT); buyStopBtn.Color(GUI_OBJECTS_BUY_BTN_COLOR); buyStopBtn.ColorBackground(GUI_OBJECTS_BUY_BTN_BG_COLOR); buyStopBtn.FontSize(GUI_OBJECTS_FONT_SIZE); mainPanelWindow.Add(buyStopBtn); class=class="str">"cmt">//--Create the buy stop volume lot edit to get the buy stop volume/lot user input buyStopVolumeLotEdit.Create( class="num">0, BUY_STOP_VOLUME_LOT_EDIT_NAME, BUY_STOP_VOLUME_LOT_EDIT_SUBWINDOW, BUY_STOP_VOLUME_LOT_EDIT_X1, BUY_STOP_VOLUME_LOT_EDIT_Y1, class="num">0, class="num">0 ); buyStopVolumeLotEdit.Text(DoubleToString(volumeLot)); buyStopVolumeLotEdit.Width(GUI_OBJECTS_WIDTH); buyStopVolumeLotEdit.Height(GUI_OBJECTS_HEIGHT); buyStopVolumeLotEdit.Color(GUI_OBJECTS_BUY_EDIT_COLOR); buyStopVolumeLotEdit.ColorBackground(GUI_OBJECTS_BUY_EDIT_BG_COLOR); buyStopVolumeLotEdit.FontSize(GUI_OBJECTS_FONT_SIZE); buyStopVolumeLotEdit.ColorBorder(GUI_OBJECTS_BUY_BTN_BG_COLOR); mainPanelWindow.Add(buyStopVolumeLotEdit); class=class="str">"cmt">//--Create the buy stop price edit to get the buy stop opening price user input buyStopOpenPriceEdit.Create( class="num">0, BUY_STOP_OPEN_PRICE_EDIT_NAME, BUY_STOP_OPEN_PRICE_EDIT_SUBWINDOW, BUY_STOP_OPEN_PRICE_EDIT_X1, BUY_STOP_OPEN_PRICE_EDIT_Y1, class="num">0, class="num">0 ); buyStopOpenPriceEdit.Text(DoubleToString(buyStopEntryPrice, class="type">int(symbolDigits))); buyStopOpenPriceEdit.Width(GUI_OBJECTS_WIDTH); buyStopOpenPriceEdit.Height(GUI_OBJECTS_HEIGHT); buyStopOpenPriceEdit.Color(GUI_OBJECTS_BUY_EDIT_COLOR); buyStopOpenPriceEdit.ColorBackground(GUI_OBJECTS_BUY_EDIT_BG_COLOR);
◍ 挂单面板的止损止盈输入框怎么拼
在 MT5 自建交易面板时,Buy Stop 与 Sell Stop 的 SL/TP 编辑框必须逐个 Create 并挂到主窗口,否则用户没法在界面上手填止损点数。下面这段把买挂单的 SL 框宽度压到 GUI_OBJECTS_WIDTH / 1.4,给右侧 TP 框留了位置,属于典型的双列输入布局。 买挂单 SL 编辑框初始化时先用 IntegerToString(sl) 把整数止损点数写进 Text,再设宽高与买卖色系;卖挂单同理,但开盘价用 DoubleToString(sellStopEntryPrice, symbolDigits) 保留品种小数位,避免黄金报 1900.5 被截成 1900。外汇与贵金属波动剧烈,这类手填框若不做 digits 对齐,滑点误差可能直接吃掉止损逻辑。 Sell Stop 的 SL 框代码只贴到宽度设定,后面 Height、Color 等链式调用和买挂单同构,复制改前缀即可。开 MT5 把这段塞进你的 CPanel 类,编译后拖一个 XAUUSD 图表就能看到右下角挂单区。
buyStopSlEdit.Create( class="num">0, BUY_STOP_SL_EDIT_NAME, BUY_STOP_SL_EDIT_SUBWINDOW, BUY_STOP_SL_EDIT_X1, BUY_STOP_SL_EDIT_Y1, class="num">0, class="num">0 ); buyStopSlEdit.Text(IntegerToString(sl)); buyStopSlEdit.Width(class="type">int(GUI_OBJECTS_WIDTH / class="num">1.4)); buyStopSlEdit.Height(GUI_OBJECTS_HEIGHT); buyStopSlEdit.Color(GUI_OBJECTS_BUY_EDIT_COLOR); buyStopSlEdit.ColorBackground(GUI_OBJECTS_BUY_EDIT_BG_COLOR); buyStopSlEdit.FontSize(GUI_OBJECTS_FONT_SIZE); buyStopSlEdit.ColorBorder(GUI_OBJECTS_BUY_BTN_BG_COLOR); mainPanelWindow.Add(buyStopSlEdit); sellStopSlEdit.Create( class="num">0, SELL_STOP_SL_EDIT_NAME, SELL_STOP_SL_EDIT_SUBWINDOW, SELL_STOP_SL_EDIT_X1, SELL_STOP_SL_EDIT_Y1, class="num">0, class="num">0 ); sellStopSlEdit.Text(IntegerToString(sl)); sellStopSlEdit.Width(class="type">int(GUI_OBJECTS_WIDTH / class="num">1.4));
挂单面板的止损止盈输入框拼接
这段逻辑紧接在卖单挂单控件之后,先把 sellStop 的 SL、TP 编辑框挂进主面板:SL 框宽度吃满 GUI_OBJECTS_WIDTH,TP 框同样用 IntegerToString(tp) 把整数点值塞进文本。 sellStopSlEdit.Height(GUI_OBJECTS_HEIGHT); sellStopSlEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellStopSlEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellStopSlEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellStopSlEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellStopSlEdit); //--Create the sell stop tp edit to get the sell stop tp user input sellStopTpEdit.Create( 0, SELL_STOP_TP_EDIT_NAME, SELL_STOP_TP_EDIT_SUBWINDOW, SELL_STOP_TP_EDIT_X1, SELL_STOP_TP_EDIT_Y1, 0, 0 ); sellStopTpEdit.Text(IntegerToString(tp)); sellStopTpEdit.Width(GUI_OBJECTS_WIDTH); sellStopTpEdit.Height(GUI_OBJECTS_HEIGHT); sellStopTpEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellStopTpEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellStopTpEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellStopTpEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellStopTpEdit); 上面逐行拆开看:Height 统一取全局高度常量;Color 与 ColorBackground 用卖单专属色,和买单元区分;FontSize 沿用 GUI_OBJECTS_FONT_SIZE;边框色借了卖单按钮背景色做视觉呼应;最后 Add 进 mainPanelWindow 才真正显示。 接着切到 Buy Limit 区块。按钮和三个编辑框(volumeLot / openPrice / sl / tp)创建方式同构,但 SL 框宽度被砍到 GUI_OBJECTS_WIDTH / 1.4——约 71% 标准宽,给右侧 TP 框腾横向空间。 buyLimitSlEdit.Width(int(GUI_OBJECTS_WIDTH / 1.4)); 开仓价编辑框用 DoubleToString(buyLimitEntryPrice, int(symbolDigits)) 保留品种小数位,避免 XAUUSD 的 2 位和 EURUSD 的 5 位错位。外汇与贵金属波动剧烈,这类挂单面板参数填错可能瞬间触发滑点风险。
sellStopSlEdit.Height(GUI_OBJECTS_HEIGHT); sellStopSlEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellStopSlEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellStopSlEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellStopSlEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellStopSlEdit); class=class="str">"cmt">//--Create the sell stop tp edit to get the sell stop tp user input sellStopTpEdit.Create( class="num">0, SELL_STOP_TP_EDIT_NAME, SELL_STOP_TP_EDIT_SUBWINDOW, SELL_STOP_TP_EDIT_X1, SELL_STOP_TP_EDIT_Y1, class="num">0, class="num">0 ); sellStopTpEdit.Text(IntegerToString(tp)); sellStopTpEdit.Width(GUI_OBJECTS_WIDTH); sellStopTpEdit.Height(GUI_OBJECTS_HEIGHT); sellStopTpEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellStopTpEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellStopTpEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellStopTpEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellStopTpEdit); buyLimitSlEdit.Width(class="type">int(GUI_OBJECTS_WIDTH / class="num">1.4));
「挂单面板的 Sell Limit 控件装配」
在 Buy Limit 一组控件之后,代码紧接着创建 Sell Limit 的下单按钮与四组输入框(手数、挂单价、SL、TP),全部挂到 mainPanelWindow 之下。注意 sellLimitSlEdit 的宽度被刻意压到 GUI_OBJECTS_WIDTH / 1.4,比标准宽度的编辑框窄约 29%,这是给 SL 与 TP 并排留视觉余量。 Sell Limit 挂单价输入框用 DoubleToString(sellLimitEntryPrice, int(symbolDigits)) 格式化,symbolDigits 来自品种小数点位数,EURUSD 通常是 5 位、XAUUSD 常是 2 位,价格文本才不会多零或少零。手数与 SL/TP 则分别用 DoubleToString(volumeLot) 和 IntegerToString,说明 SL/TP 在面板里以整数点差形式录入。 所有 Sell 系控件走 GUI_OBJECTS_SELL_* 配色宏,和 Buy 系的 BUY 宏区分,MT5 上跑起来卖单区域一眼偏红、买单偏蓝。外汇与贵金属杠杆高,这类面板只是录入辅助,实际挂单前仍需人工核对点差与保证金占用。
buyLimitTpEdit.Text(IntegerToString(tp)); buyLimitTpEdit.Width(GUI_OBJECTS_WIDTH); buyLimitTpEdit.Height(GUI_OBJECTS_HEIGHT); buyLimitTpEdit.Color(GUI_OBJECTS_BUY_EDIT_COLOR); buyLimitTpEdit.ColorBackground(GUI_OBJECTS_BUY_EDIT_BG_COLOR); buyLimitTpEdit.FontSize(GUI_OBJECTS_FONT_SIZE); buyLimitTpEdit.ColorBorder(GUI_OBJECTS_BUY_BTN_BG_COLOR); mainPanelWindow.Add(buyLimitTpEdit); class=class="str">"cmt">/*------------------------------------------------------ * Sell Limit Order GUI components creation **** *-----------------------------------------------------*/ class=class="str">"cmt">//--Create the open sell limit button sellLimitBtn.Create( class="num">0, SELL_LIMIT_BTN_NAME, SELL_LIMIT_BTN_SUBWINDOW, SELL_LIMIT_BTN_X1, SELL_LIMIT_BTN_Y1, class="num">0, class="num">0 ); sellLimitBtn.Text(SELL_LIMIT_BTN_TEXT); sellLimitBtn.Width(GUI_OBJECTS_WIDTH); sellLimitBtn.Height(GUI_OBJECTS_HEIGHT); sellLimitBtn.Color(GUI_OBJECTS_SELL_BTN_COLOR); sellLimitBtn.ColorBackground(GUI_OBJECTS_SELL_BTN_BG_COLOR); sellLimitBtn.FontSize(GUI_OBJECTS_FONT_SIZE); mainPanelWindow.Add(sellLimitBtn); class=class="str">"cmt">//--Create the sell limit volume lot edit to get the sell limit volume/lot user input sellLimitVolumeLotEdit.Create( class="num">0, SELL_LIMIT_VOLUME_LOT_EDIT_NAME, SELL_LIMIT_VOLUME_LOT_EDIT_SUBWINDOW, SELL_LIMIT_VOLUME_LOT_EDIT_X1, SELL_LIMIT_VOLUME_LOT_EDIT_Y1, class="num">0, class="num">0 ); sellLimitVolumeLotEdit.Text(DoubleToString(volumeLot)); sellLimitVolumeLotEdit.Width(GUI_OBJECTS_WIDTH); sellLimitVolumeLotEdit.Height(GUI_OBJECTS_HEIGHT); sellLimitVolumeLotEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellLimitVolumeLotEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellLimitVolumeLotEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellLimitVolumeLotEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellLimitVolumeLotEdit); class=class="str">"cmt">//--Create the sell limit price edit to get the sell limit opening price user input sellLimitOpenPriceEdit.Create( class="num">0, SELL_LIMIT_OPEN_PRICE_EDIT_NAME, SELL_LIMIT_OPEN_PRICE_EDIT_SUBWINDOW, SELL_LIMIT_OPEN_PRICE_EDIT_X1, SELL_LIMIT_OPEN_PRICE_EDIT_Y1, class="num">0, class="num">0 ); sellLimitOpenPriceEdit.Text(DoubleToString(sellLimitEntryPrice, class="type">int(symbolDigits))); sellLimitOpenPriceEdit.Width(GUI_OBJECTS_WIDTH); sellLimitOpenPriceEdit.Height(GUI_OBJECTS_HEIGHT); sellLimitOpenPriceEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellLimitOpenPriceEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellLimitOpenPriceEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellLimitOpenPriceEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellLimitOpenPriceEdit); class=class="str">"cmt">//--Create the sell limit sl edit to get the sell limit sl user input sellLimitSlEdit.Create( class="num">0, SELL_LIMIT_SL_EDIT_NAME, SELL_LIMIT_SL_EDIT_SUBWINDOW, SELL_LIMIT_SL_EDIT_X1, SELL_LIMIT_SL_EDIT_Y1, class="num">0, class="num">0 ); sellLimitSlEdit.Text(IntegerToString(sl)); sellLimitSlEdit.Width(class="type">int(GUI_OBJECTS_WIDTH / class="num">1.4)); sellLimitSlEdit.Height(GUI_OBJECTS_HEIGHT); sellLimitSlEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellLimitSlEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellLimitSlEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellLimitSlEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellLimitSlEdit); class=class="str">"cmt">//--Create the sell limit tp edit to get the sell limit tp user input sellLimitTpEdit.Create( class="num">0, SELL_LIMIT_TP_EDIT_NAME, SELL_LIMIT_TP_EDIT_SUBWINDOW, SELL_LIMIT_TP_EDIT_X1, SELL_LIMIT_TP_EDIT_Y1, class="num">0, class="num">0 ); sellLimitTpEdit.Text(IntegerToString(tp)); sellLimitTpEdit.Width(GUI_OBJECTS_WIDTH); sellLimitTpEdit.Height(GUI_OBJECTS_HEIGHT); sellLimitTpEdit.Color(GUI_OBJECTS_SELL_EDIT_COLOR); sellLimitTpEdit.ColorBackground(GUI_OBJECTS_SELL_EDIT_BG_COLOR); sellLimitTpEdit.FontSize(GUI_OBJECTS_FONT_SIZE); sellLimitTpEdit.ColorBorder(GUI_OBJECTS_SELL_BTN_BG_COLOR); mainPanelWindow.Add(sellLimitTpEdit); /*------------------------------------------------------------- * Status Labels and readonly edits GUI components creation ****
◍ 挂单面板里的状态栏与分类撤单按钮
在 MT5 自定义面板里,先建一个只读标签显示当前 magic 号下的挂单总数:magicOrderStatusLabel 用 Create 挂在 0 号图表和指定子窗口,Text 拼接了 MagicOrdersTotal(magicNo) 的实时返回值,比如屏上可能显示「Magic Status - (Total Open Orders: 3)」,这个数字随持仓变化而变。 紧随其后的是一个只读编辑框 magicOrdersStatusEdit,它用 ReadOnly(true) 锁住,专门承载 magicOrderStatus 字符串,并设了背景色 STATUS_EDIT_BG_COLOR 与边框色,方便和面板其他控件区分。 撤单交互拆得很细:分别建了 deleteAllMagicBuyStopsBtn、deleteAllMagicSellStopsBtn、deleteAllMagicBuyLimitsBtn、deleteAllMagicSellLimitsBtn 四个按钮,各自绑定不同的背景色宏(如 DELETE_BUY_ORDERS_BTN_BG_COLOR 与 DELETE_SELL_ORDERS_BTN_BG_COLOR),宽度统一吃 DELETE_ORDERS_BTN_WIDTH、高度吃 GUI_OBJECTS_HEIGHT。这样按挂单方向/类型一键清场,比在终端里手动筛选快得多,外汇与贵金属品种杠杆高,误删风险也大,实盘前建议在策略测试器里先跑一遍。
magicOrderStatusLabel.Create( class="num">0, MAGIC_ORDER_STATUS_LABEL_NAME, MAGIC_ORDER_STATUS_LABEL_SUBWINDOW, MAGIC_ORDER_STATUS_LABEL_X1, MAGIC_ORDER_STATUS_LABEL_Y1, GUI_OBJECTS_WIDTH, GUI_OBJECTS_HEIGHT ); magicOrderStatusLabel.Text(MAGIC_ORDER_STATUS_LABEL_TEXT + " - (Total Open Orders: " + (class="type">class="kw">string(MagicOrdersTotal(magicNo))) + ")"); magicOrderStatusLabel.Color(STATUS_EDIT_COLOR); magicOrderStatusLabel.FontSize(STATUS_HEADER_FONT_SIZE); mainPanelWindow.Add(magicOrderStatusLabel); class=class="str">"cmt">//--Create the magic order status edit to display the magic orders status magicOrdersStatusEdit.Create( class="num">0, MAGIC_ORDER_STATUS_EDIT_NAME, MAGIC_ORDER_STATUS_EDIT_SUBWINDOW, MAGIC_ORDER_STATUS_EDIT_X1, MAGIC_ORDER_STATUS_EDIT_Y1, class="num">0, class="num">0 ); magicOrdersStatusEdit.ReadOnly(true); magicOrdersStatusEdit.Text(magicOrderStatus); magicOrdersStatusEdit.Width(STATUS_EDIT_WIDTH); magicOrdersStatusEdit.Height(GUI_OBJECTS_HEIGHT); magicOrdersStatusEdit.Color(STATUS_EDIT_COLOR); magicOrdersStatusEdit.ColorBackground(STATUS_EDIT_BG_COLOR); magicOrdersStatusEdit.FontSize(STATUS_EDIT_FONT_SIZE); magicOrdersStatusEdit.ColorBorder(STATUS_EDIT_BORDER_COLOR); mainPanelWindow.Add(magicOrdersStatusEdit); class=class="str">"cmt">//--Create the class="kw">delete all magic buy stops button deleteAllMagicBuyStopsBtn.Create( class="num">0, DELETE_ALL_MAGIC_BUY_STOPS_BTN_NAME, DELETE_ALL_MAGIC_BUY_STOPS_BTN_SUBWINDOW, DELETE_ALL_MAGIC_BUY_STOPS_BTN_X1, DELETE_ALL_MAGIC_BUY_STOPS_BTN_Y1, class="num">0, class="num">0 ); deleteAllMagicBuyStopsBtn.Text(DELETE_ALL_MAGIC_BUY_STOPS_BTN_TEXT); deleteAllMagicBuyStopsBtn.Width(DELETE_ORDERS_BTN_WIDTH); deleteAllMagicBuyStopsBtn.Height(GUI_OBJECTS_HEIGHT); deleteAllMagicBuyStopsBtn.Color(DELETE_ORDERS_BTN_COLOR); deleteAllMagicBuyStopsBtn.ColorBackground(DELETE_BUY_ORDERS_BTN_BG_COLOR); deleteAllMagicBuyStopsBtn.ColorBorder(DELETE_ORDERS_BTN_BORDER_COLOR); deleteAllMagicBuyStopsBtn.FontSize(DELETE_ORDERS_BTN_FONT_SIZE); mainPanelWindow.Add(deleteAllMagicBuyStopsBtn); class=class="str">"cmt">//--Create the class="kw">delete all magic sell stops button deleteAllMagicSellStopsBtn.Create( class="num">0, DELETE_ALL_MAGIC_SELL_STOPS_BTN_NAME, DELETE_ALL_MAGIC_SELL_STOPS_BTN_SUBWINDOW, DELETE_ALL_MAGIC_SELL_STOPS_BTN_X1, DELETE_ALL_MAGIC_SELL_STOPS_BTN_Y1, class="num">0, class="num">0 ); deleteAllMagicSellStopsBtn.Text(DELETE_ALL_MAGIC_SELL_STOPS_BTN_TEXT); deleteAllMagicSellStopsBtn.Width(DELETE_ORDERS_BTN_WIDTH); deleteAllMagicSellStopsBtn.Height(GUI_OBJECTS_HEIGHT); deleteAllMagicSellStopsBtn.Color(DELETE_ORDERS_BTN_COLOR); deleteAllMagicSellStopsBtn.ColorBackground(DELETE_SELL_ORDERS_BTN_BG_COLOR); deleteAllMagicSellStopsBtn.ColorBorder(DELETE_ORDERS_BTN_BORDER_COLOR); deleteAllMagicSellStopsBtn.FontSize(DELETE_ORDERS_BTN_FONT_SIZE); mainPanelWindow.Add(deleteAllMagicSellStopsBtn); class=class="str">"cmt">//--Create the class="kw">delete all magic buy limits button deleteAllMagicBuyLimitsBtn.Create( class="num">0, DELETE_ALL_MAGIC_BUY_LIMITS_BTN_NAME, DELETE_ALL_MAGIC_BUY_LIMITS_BTN_SUBWINDOW, DELETE_ALL_MAGIC_BUY_LIMITS_BTN_X1, DELETE_ALL_MAGIC_BUY_LIMITS_BTN_Y1, class="num">0, class="num">0 ); deleteAllMagicBuyLimitsBtn.Text(DELETE_ALL_MAGIC_BUY_LIMITS_BTN_TEXT); deleteAllMagicBuyLimitsBtn.Width(DELETE_ORDERS_BTN_WIDTH); deleteAllMagicBuyLimitsBtn.Height(GUI_OBJECTS_HEIGHT); deleteAllMagicBuyLimitsBtn.Color(DELETE_ORDERS_BTN_COLOR); deleteAllMagicBuyLimitsBtn.ColorBackground(DELETE_BUY_ORDERS_BTN_BG_COLOR); deleteAllMagicBuyLimitsBtn.ColorBorder(DELETE_ORDERS_BTN_BORDER_COLOR); deleteAllMagicBuyLimitsBtn.FontSize(DELETE_ORDERS_BTN_FONT_SIZE); mainPanelWindow.Add(deleteAllMagicBuyLimitsBtn); class=class="str">"cmt">//--Create the class="kw">delete all magic sell limits button deleteAllMagicSellLimitsBtn.Create( class="num">0, DELETE_ALL_MAGIC_SELL_LIMITS_BTN_NAME, DELETE_ALL_MAGIC_SELL_LIMITS_BTN_SUBWINDOW, DELETE_ALL_MAGIC_SELL_LIMITS_BTN_X1, DELETE_ALL_MAGIC_SELL_LIMITS_BTN_Y1, class="num">0, class="num">0 ); deleteAllMagicSellLimitsBtn.Text(DELETE_ALL_MAGIC_SELL_LIMITS_BTN_TEXT); deleteAllMagicSellLimitsBtn.Width(DELETE_ORDERS_BTN_WIDTH);
挂单面板的按钮绑定与点击事件分发
这段逻辑紧接在面板控件初始化之后,负责把「删除 Sell Limit」「删除全部 Magic 单」两个按钮挂进主窗口,并启动窗口的消息循环。注意 deleteAllMagicSellLimitsBtn 与 deleteAllMagicOrdersBtn 都通过 mainPanelWindow.Add() 注册,前者背景色用 DELETE_SELL_ORDERS_BTN_BG_COLOR,后者用 DELETE_ALL_ORDERS_BTN_BG_COLOR,肉眼区分两类危险操作。 mainPanelWindow.Run() 调用后,面板才真正加载到图表;之后所有交互依赖 OnChartEvent 回调。图表上任意对象点击都会先喂给 mainPanelWindow.ChartEvent(),由它处理拖拽等窗口行为,再进入我们自己的 CHARTEVENT_OBJECT_CLICK 分支。 在点击分支里,用 sparam == buyStopBtn.Name() 精准匹配按钮对象名。匹配成功后先 Print 出开单参数:手数取自 buyStopVolumeLotEdit.Text(),开盘价取自 buyStopOpenPriceEdit.Text(),SL/TP 以点数文本读入再转整型。OpenBuyStop() 返回真则播 ok.wav,假则播 alert2.wav——用声音反馈替代弹窗,盯盘时不用盯着日志也知道挂单成败。 外汇与贵金属挂单涉及杠杆与滑点,按钮一键清单或开仓都可能瞬间改变风险敞口,实盘前务必在策略测试器用历史数据跑通事件链路。
deleteAllMagicSellLimitsBtn.Height(GUI_OBJECTS_HEIGHT); deleteAllMagicSellLimitsBtn.Color(DELETE_ORDERS_BTN_COLOR); deleteAllMagicSellLimitsBtn.ColorBackground(DELETE_SELL_ORDERS_BTN_BG_COLOR); deleteAllMagicSellLimitsBtn.ColorBorder(DELETE_ORDERS_BTN_BORDER_COLOR); deleteAllMagicSellLimitsBtn.FontSize(DELETE_ORDERS_BTN_FONT_SIZE); mainPanelWindow.Add(deleteAllMagicSellLimitsBtn); class=class="str">"cmt">//--Create the class="kw">delete all magic orders button deleteAllMagicOrdersBtn.Create( class="num">0, DELETE_ALL_MAGIC_ORDERS_BTN_NAME, DELETE_ALL_MAGIC_ORDERS_BTN_SUBWINDOW, DELETE_ALL_MAGIC_ORDERS_BTN_X1, DELETE_ALL_MAGIC_ORDERS_BTN_Y1, class="num">0, class="num">0 ); deleteAllMagicOrdersBtn.Text(DELETE_ALL_MAGIC_ORDERS_BTN_TEXT); deleteAllMagicOrdersBtn.Width(STATUS_EDIT_WIDTH); deleteAllMagicOrdersBtn.Height(GUI_OBJECTS_HEIGHT); deleteAllMagicOrdersBtn.Color(DELETE_ORDERS_BTN_COLOR); deleteAllMagicOrdersBtn.ColorBackground(DELETE_ALL_ORDERS_BTN_BG_COLOR); deleteAllMagicOrdersBtn.ColorBorder(DELETE_ORDERS_BTN_BORDER_COLOR); deleteAllMagicOrdersBtn.FontSize(DELETE_ORDERS_BTN_FONT_SIZE); mainPanelWindow.Add(deleteAllMagicOrdersBtn); class=class="str">"cmt">//--Call the Run() method to load the main panel window mainPanelWindow.Run(); } class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--Detect any clicks or events performed to the orders panel window and make it moveable mainPanelWindow.ChartEvent(id, lparam, dparam, sparam); class=class="str">"cmt">//--Detect any click events on the chart if(id == CHARTEVENT_OBJECT_CLICK) { class=class="str">"cmt">//--Detect when the buyStopBtn is clicked and open a new buy stop order if(sparam == buyStopBtn.Name()) { Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam); Print("Opening a new Buy Stop Order with the details below: "); Print("Volume: ", buyStopVolumeLotEdit.Text()); Print("Open Price: ", buyStopOpenPriceEdit.Text()); Print("Sl(Pips): ", buyStopSlEdit.Text()); Print("Tp(Pips): ", buyStopTpEdit.Text()); if( OpenBuyStop( magicNo, _Symbol, StringToDouble(buyStopOpenPriceEdit.Text()), StringToDouble(buyStopVolumeLotEdit.Text()), (class="type">uint)StringToInteger(buyStopSlEdit.Text()), (class="type">uint)StringToInteger(buyStopTpEdit.Text()), "EX5 PendingOrdersManager Panel" ) ) { PlaySound("ok.wav");class=class="str">"cmt">//-- Order placed ok } else { PlaySound("alert2.wav");class=class="str">"cmt">//-- Order failed } } class=class="str">"cmt">//--Detect when the sellStopBtn is clicked and open a new sell stop order
「挂单面板的点击事件如何落地成订单」
在 MT5 自定义面板里,挂单按钮的响应逻辑靠图表事件 CHARTEVENT_OBJECT_CLICK 驱动。代码通过比对 sparam 与按钮的 Name() 来判断用户点的是 Sell Stop 还是 Buy Limit,随后把界面输入框里的文本取出并打印,方便在专家日志里核对参数。 以 Sell Stop 分支为例:sellStopVolumeLotEdit.Text() 拿到手数,sellStopOpenPriceEdit.Text() 拿到挂单价,止损止盈编辑框的内容被 StringToInteger 强转成 uint 点值。这些字符串先经 StringToDouble / StringToInteger 转换,再喂给 OpenSellStop(),避免直接拿文本下单导致类型错误。 下单成功播 ok.wav,失败播 alert2.wav,用声音反馈替代弹窗,盯盘时不用一直盯日志。外汇与贵金属杠杆高,挂单成交受滑点影响,实际开仓价可能偏离面板填的价格,建议在策略测试器或模拟盘先跑一遍这段逻辑。 Buy Limit 分支结构完全一致,只是换用 buyLimitBtn 与 OpenBuyLimit(),注释里也写明这是检测 buyLimitBtn 点击后开限价多单。复制时留意魔法码与注释里的面板名 'EX5 PendingOrdersManager Panel',改成你自己的标识即可。
if(sparam == sellStopBtn.Name()) { Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam); Print("Opening a new Sell Stop Order with the details below: "); Print("Volume: ", sellStopVolumeLotEdit.Text()); Print("Open Price: ", sellStopOpenPriceEdit.Text()); Print("Sl(Pips): ", sellStopSlEdit.Text()); Print("Tp(Pips): ", sellStopTpEdit.Text()); if( OpenSellStop( magicNo, _Symbol, StringToDouble(sellStopOpenPriceEdit.Text()), StringToDouble(sellStopVolumeLotEdit.Text()), (class="type">uint)StringToInteger(sellStopSlEdit.Text()), (class="type">uint)StringToInteger(sellStopTpEdit.Text()), "EX5 PendingOrdersManager Panel" ) ) { PlaySound("ok.wav");class=class="str">"cmt">//-- Order placed ok } else { PlaySound("alert2.wav");class=class="str">"cmt">//-- Order failed } } class=class="str">"cmt">//--Detect when the buyLimitBtn is clicked and open a new buy limit order if(sparam == buyLimitBtn.Name()) { Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam); Print("Opening a new Buy Limit Order with the details below: "); Print("Volume: ", buyLimitVolumeLotEdit.Text()); Print("Open Price: ", buyLimitOpenPriceEdit.Text()); Print("Sl(Pips): ", buyLimitSlEdit.Text()); Print("Tp(Pips): ", buyLimitTpEdit.Text()); if( OpenBuyLimit( magicNo, _Symbol, StringToDouble(buyLimitOpenPriceEdit.Text()), StringToDouble(buyLimitVolumeLotEdit.Text()), (class="type">uint)StringToInteger(buyLimitSlEdit.Text()), (class="type">uint)StringToInteger(buyLimitTpEdit.Text()), "EX5 PendingOrdersManager Panel" ) ) { PlaySound("ok.wav");class=class="str">"cmt">//-- Order placed ok } else { PlaySound("alert2.wav");class=class="str">"cmt">//-- Order failed } }
◍ 挂单面板的点击响应与声音反馈
在 MT5 自定义面板里,挂单按钮的点击靠图表事件 CHARTEVENT_OBJECT_CLICK 捕获,参数 sparam 就是被点对象的 Name()。下面这段逻辑专门处理「卖限价单按钮」被点后的动作:先打印订单要素,再调用 OpenSellLimit() 下挂单,成功播 ok.wav、失败播 alert2.wav。
删除类按钮同理,但多了一道闸门:MagicBuyStopOrdersTotal(magicNo) > 0 才进分支,避免对空池子瞎叫。点删除全部买停单按钮后,由 DeleteAllBuyStops("", magicNo) 清掉该 magic 下的所有 Buy Stop,声音反馈机制和开单一致。
外汇与贵金属挂单受点差、滑点和流动性影响,这类一键删单在剧烈行情中可能只删掉部分挂单,实际成交以终端成交回执为准,属高风险操作。
代码逐行看:
//--Detect when the sellLimitBtn is clicked and open a new sell limit order
// 注释:捕获卖限价按钮点击,准备开新卖限价单
if(sparam == sellLimitBtn.Name())
// 若点击事件对象名等于卖限价按钮名,进入分支
{
Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam);
// 打印当前函数名与点击对象名,便于调试
Print("Opening a new Sell Limit Order with the details below: ");
// 提示即将开卖限价单
Print("Volume: ", sellLimitVolumeLotEdit.Text());
// 打印手数输入框文本
Print("Open Price: ", sellLimitOpenPriceEdit.Text());
// 打印开仓价输入框文本
Print("Sl (Pips): ", sellLimitSlEdit.Text());
// 打印止损点数
Print("Tp (Pips): ", sellLimitTpEdit.Text());
// 打印止盈点数
if(
OpenSellLimit(
magicNo, _Symbol, StringToDouble(sellLimitOpenPriceEdit.Text()),
// 将开仓价文本转 double 传给开单函数
StringToDouble(sellLimitVolumeLotEdit.Text()), (uint)StringToInteger(sellLimitSlEdit.Text()),
// 手数转 double、SL 点数转 uint
(uint)StringToInteger(sellLimitTpEdit.Text()), "EX5 PendingOrdersManager Panel"
// TP 点数转 uint,备注面板名
)
)
{
PlaySound("ok.wav");//-- Order placed ok
// 开单成功播提示音
}
else
{
PlaySound("alert2.wav");//-- Order failed
// 开单失败播报警音
}
}
//--Detect when the deleteAllMagicBuyStopsBtn is clicked and delete all the specified orders
// 注释:捕获删全部买停单按钮点击
if(sparam == deleteAllMagicBuyStopsBtn.Name() && MagicBuyStopOrdersTotal(magicNo) > 0)
// 按钮名匹配且 magic 下买停单数大于 0 才执行
{
Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam);
Print("Deleting all the buy stop orders with magic number: ", magicNo);
// 打印删除目标 magic
if(DeleteAllBuyStops("", magicNo))
{
PlaySound("ok.wav");//-- Orders deleted ok
// 删除成功播提示音
}
else
{
PlaySound("alert2.wav");//-- Order deleting failed
// 删除失败播报警音
}
}
//--Detect when the deleteAllMagicSellStopsBtn is clicked and delete all the specified orders
// 注释:捕获删全部卖停单按钮点击
if(sparam == deleteAllMagicSellStopsBtn.Name() && MagicSellStopOrdersTotal(magicNo) > 0)
// 按钮名匹配且 magic 下卖停单数大于 0 才执行
{
Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam);
Print("Deleting all the sell stop orders with magic number: ", magicNo);
if(DeleteAllSellStops("", magicNo))
{
PlaySound("ok.wav");//-- Orders deleted ok
// 删除成功播提示音
class=class="str">"cmt">//--Detect when the sellLimitBtn is clicked and open a new sell limit order if(sparam == sellLimitBtn.Name()) { Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam); Print("Opening a new Sell Limit Order with the details below: "); Print("Volume: ", sellLimitVolumeLotEdit.Text()); Print("Open Price: ", sellLimitOpenPriceEdit.Text()); Print("Sl(Pips): ", sellLimitSlEdit.Text()); Print("Tp(Pips): ", sellLimitTpEdit.Text()); if( OpenSellLimit( magicNo, _Symbol, StringToDouble(sellLimitOpenPriceEdit.Text()), StringToDouble(sellLimitVolumeLotEdit.Text()), (class="type">uint)StringToInteger(sellLimitSlEdit.Text()), (class="type">uint)StringToInteger(sellLimitTpEdit.Text()), "EX5 PendingOrdersManager Panel" ) ) { PlaySound("ok.wav");class=class="str">"cmt">//-- Order placed ok } else { PlaySound("alert2.wav");class=class="str">"cmt">//-- Order failed } } class=class="str">"cmt">//--Detect when the deleteAllMagicBuyStopsBtn is clicked and class="kw">delete all the specified orders if(sparam == deleteAllMagicBuyStopsBtn.Name() && MagicBuyStopOrdersTotal(magicNo) > class="num">0) { Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam); Print("Deleting all the buy stop orders with magic number: ", magicNo); if(DeleteAllBuyStops("", magicNo)) { PlaySound("ok.wav");class=class="str">"cmt">//-- Orders deleted ok } else { PlaySound("alert2.wav");class=class="str">"cmt">//-- Order deleting failed } } class=class="str">"cmt">//--Detect when the deleteAllMagicSellStopsBtn is clicked and class="kw">delete all the specified orders if(sparam == deleteAllMagicSellStopsBtn.Name() && MagicSellStopOrdersTotal(magicNo) > class="num">0) { Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam); Print("Deleting all the sell stop orders with magic number: ", magicNo); if(DeleteAllSellStops("", magicNo)) { PlaySound("ok.wav");class=class="str">"cmt">//-- Orders deleted ok
按钮触发后的一键删单逻辑
这段处理集中在图表按钮被点击后的回调里,靠 sparam 比对按钮名来分辨用户想删哪类挂单。只有对应 Magic 类订单总数大于 0 时才进入删除分支,避免对空池做无用调用。 删除成功统一播 ok.wav,失败播 alert2.wav,并在 Print 里带出 __FUNCTION__ 与按钮名,方便在 MT5 Experts 日志里定位是哪次点击。 三类按钮分别管 Buy Limit、Sell Limit、以及该 magic 下全部未成交挂单:前两者调用 DeleteAllBuyLimits("",magicNo) 与 DeleteAllSellLimits("",magicNo),后者走 DeleteAllMagicOrders(magicNo)。 开 MT5 把这段接进 ChartEvent 的 OBJPROP_ONCLICK 回调,点一下按钮听声音就能判断删单是否生效;外汇与贵金属杠杆高,误删挂单可能错过入场或暴露敞口,操作前确认 magicNo 范围。
}
else
{
PlaySound("alert2.wav");class=class="str">"cmt">//-- Order deleting failed
}
}
class=class="str">"cmt">//--Detect when the deleteAllMagicBuyLimitsBtn is clicked and class="kw">delete all the specified orders
if(sparam == deleteAllMagicBuyLimitsBtn.Name() && MagicBuyLimitOrdersTotal(magicNo) > class="num">0)
{
Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam);
Print("Deleting all the buy limit orders with magic number: ", magicNo);
if(DeleteAllBuyLimits("", magicNo))
{
PlaySound("ok.wav");class=class="str">"cmt">//-- Orders deleted ok
}
else
{
PlaySound("alert2.wav");class=class="str">"cmt">//-- Order deleting failed
}
}
class=class="str">"cmt">//--Detect when the deleteAllMagicSellLimitsBtn is clicked and class="kw">delete all the specified orders
if(sparam == deleteAllMagicSellLimitsBtn.Name() && MagicSellLimitOrdersTotal(magicNo) > class="num">0)
{
Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam);
Print("Deleting all the sell limit orders with magic number: ", magicNo);
if(DeleteAllSellLimits("", magicNo))
{
PlaySound("ok.wav");class=class="str">"cmt">//-- Orders deleted ok
}
else
{
PlaySound("alert2.wav");class=class="str">"cmt">//-- Order deleting failed
}
}
class=class="str">"cmt">//--Detect when the deleteAllMagicOrdersBtn is clicked and class="kw">delete all the specified orders
if(sparam == deleteAllMagicOrdersBtn.Name() && MagicOrdersTotal(magicNo) > class="num">0)
{
Print(__FUNCTION__, " CHARTEVEN_OBJECT_CLICK: ", sparam);
Print("Deleting all the open peding orders with magic number: ", magicNo);
if(DeleteAllMagicOrders(magicNo))
{
PlaySound("ok.wav");class=class="str">"cmt">//-- Orders deleted ok
}
else
{
PlaySound("alert2.wav");class=class="str">"cmt">//-- Order deleting failed
}
}
}
}
class="type">void OnTick()
{
class=class="str">"cmt">//---
magicOrderStatus = " Buy Stops: " + (class="type">class="kw">string(MagicBuyStopOrdersTotal(magicNo))) +「挂单面板的按钮状态随持仓联动」
这段逻辑把当前 magic 号下的各类挂单与市价单数量拼进状态文本,并实时驱动 GUI 按钮的可点击与配色。 当 Buy Stop 挂单数为 0 时,删除 Buy Stop 的按钮被 Disable() 且背景置为 clrLightSlateGray;只要 MagicBuyStopOrdersTotal(magicNo) 不为 0,按钮就 Enable() 并恢复 DELETE_BUY_ORDERS_BTN_BG_COLOR。Sell Stop、Buy Limit、Sell Limit 四组按钮走同一套判断,仅替换对应的统计函数与背景色宏。 最末的 deleteAllMagicOrdersBtn 以 MagicOrdersTotal(magicNo)==0 为闸:无该 magic 的任何订单时灰态禁用,有则启用并涂 DELETE_ALL_ORDERS_BTN_BG_COLOR。OnDeinit 里只做销毁主面板与清 Comment(""),不残留图表注释。 在 MT5 里接这段,重点验证无挂单时按钮是否真灰掉——外汇与贵金属杠杆高,误点全删按钮可能瞬间平掉对冲单,建议先拿模拟盘跑一遍。
", Sell Stops: " + (class="type">class="kw">string(MagicSellStopOrdersTotal(magicNo))) + ", Buy Limits: " + (class="type">class="kw">string(MagicBuyLimitOrdersTotal(magicNo))) + ", Sell Limits: " + (class="type">class="kw">string(MagicSellLimitOrdersTotal(magicNo))) + " "; magicOrderStatusLabel.Text(MAGIC_ORDER_STATUS_LABEL_TEXT + (class="type">class="kw">string(MagicOrdersTotal(magicNo)))); magicOrdersStatusEdit.Text(magicOrderStatus); class=class="str">"cmt">//-- Disable and change the background class="type">class="kw">color of the deleteAllMagicBuyStopsBtn depending on the open orders status if(MagicBuyStopOrdersTotal(magicNo) == class="num">0) { deleteAllMagicBuyStopsBtn.Disable(); deleteAllMagicBuyStopsBtn.ColorBackground(clrLightSlateGray); } else { deleteAllMagicBuyStopsBtn.Enable(); deleteAllMagicBuyStopsBtn.ColorBackground(DELETE_BUY_ORDERS_BTN_BG_COLOR); } class=class="str">"cmt">//-- Disable and change the background class="type">class="kw">color of the deleteAllMagicSellStopsBtn depending on the open orders status if(MagicSellStopOrdersTotal(magicNo) == class="num">0) { deleteAllMagicSellStopsBtn.Disable(); deleteAllMagicSellStopsBtn.ColorBackground(clrLightSlateGray); } else { deleteAllMagicSellStopsBtn.Enable(); deleteAllMagicSellStopsBtn.ColorBackground(DELETE_SELL_ORDERS_BTN_BG_COLOR); } class=class="str">"cmt">//-- Disable and change the background class="type">class="kw">color of the deleteAllMagicBuyLimitsBtn depending on the open orders status if(MagicBuyLimitOrdersTotal(magicNo) == class="num">0) { deleteAllMagicBuyLimitsBtn.Disable(); deleteAllMagicBuyLimitsBtn.ColorBackground(clrLightSlateGray); } else { deleteAllMagicBuyLimitsBtn.Enable(); deleteAllMagicBuyLimitsBtn.ColorBackground(DELETE_BUY_ORDERS_BTN_BG_COLOR); } class=class="str">"cmt">//-- Disable and change the background class="type">class="kw">color of the deleteAllMagicSellLimitsBtn depending on the open orders status if(MagicSellLimitOrdersTotal(magicNo) == class="num">0) { deleteAllMagicSellLimitsBtn.Disable(); deleteAllMagicSellLimitsBtn.ColorBackground(clrLightSlateGray); } else { deleteAllMagicSellLimitsBtn.Enable(); deleteAllMagicSellLimitsBtn.ColorBackground(DELETE_SELL_ORDERS_BTN_BG_COLOR); } class=class="str">"cmt">//-- Disable and change the background class="type">class="kw">color of the deleteAllMagicOrdersBtn depending on the open orders status if(MagicOrdersTotal(magicNo) == class="num">0) { deleteAllMagicOrdersBtn.Disable(); deleteAllMagicOrdersBtn.ColorBackground(clrLightSlateGray); } else { deleteAllMagicOrdersBtn.Enable(); deleteAllMagicOrdersBtn.ColorBackground(DELETE_ALL_ORDERS_BTN_BG_COLOR); } } class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- class=class="str">"cmt">//-- Delete and garbage collect the graphical user interface and other graphical objects mainPanelWindow.Destroy(); class=class="str">"cmt">//-- Clear any chart comments Comment(""); }
◍ 把挂单库接进你自己的面板
这套挂单管理 EX5 库把打开、修改、删除、排序和过滤都封成了单层函数调用,附带的 PendingOrdersManager.mq5 源码有 84.49 KB,编译出的 EX5 体积 42.52 KB,直接在 MT5 里引用就能跑。 对做半自动盯盘的人来说,最实用的落点是把库里的状态查询函数喂给你的自定义面板,省掉重复写 CTrade 和订单遍历的功夫。 外汇和贵金属杠杆高、滑点随机,任何挂单逻辑都先在策略测试器用历史数据跑一遍再上实盘,别裸接。 下一步可以照着同思路写历史管理库,把已平单和取消单也纳进同一套函数体系,MT5 里交易和订单历史的代码维护会轻很多。