MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库·综合运用
(3/3)· 把前两篇铺垫的库拆开重写,接进追踪止损 EA 与图形面板,看编译好的 .ex5 怎么真正跑在策略里
不少交易者把编译好的 EX5 库丢进 Libraries 文件夹就以为万事大吉,结果 EA 调用时要么找不到导出函数,要么多库同名直接编译报错。库写完了和库用顺了,中间还隔着一整套导入约定与重载逻辑。
- 无参版清仓函数的导出写法
- 递归式仓位筛选与关闭函数怎么写
- 按品种和幻数清掉所有多单
- 按品种与幻数清掉所有空单
- 按幻数清仓的函数怎么写
- 一键清掉盈利单的函数怎么写
- 一键平掉盈利多单的函数怎么写
- 只平盈利的空单怎么写
- 按品种和幻数清掉亏损单
- 批量砍掉亏损多单的实现逻辑
- 批量砍掉亏损卖仓的函数逻辑
- 用一行函数摸清所有未平仓位
- 把持仓状态塞进全局变量
- 用魔术码把EA自己开的仓挑出来
- 按品种过滤持仓并拆分多空统计
- 抓出账户里的多单存量
- 抓出账户里的空单总量
- 一键算出全账户未平仓位总手数
- 抓取账户里所有多单的合计手数
- 抓出账户里所有空单的总手数
- 抓出所有多单的浮盈总和
- 抓取空单浮动盈利的函数
- 按幻数统计未平仓位
- 按幻数统计未平买入仓位数
- 按幻数统计卖出持仓量
- 按幻数汇总未平仓位手数
- 按幻数归集的买入持仓总量
- 按幻数归集的卖单总仓量
- 按幻数归集的浮盈怎么取
- 按幻数归集的买入持仓利润
- 按幻数归集的卖单浮盈怎么取
- 按品种与魔术码清点未平仓位
- 按品种与魔术码数买入持仓
- 按品种与魔数统计空单持仓
- 按品种和魔数汇总未平仓位量
- 抓出某品种买仓的累计手数
- 抓出某品种空头持仓的总量
- 按品种抓未平仓位总利润
- 抓出某品种买仓的浮动利润
- 按品种与魔术码抓卖出浮盈
- 一键导出账户持仓全景字符串
- 按幻数抓取仓位快照
- 按品种拉出持仓全景字符串
- 把 PositionsManager 库接进你的 MQL5 工程
- 把仓位管理塞进 EA 的两个步骤
- 按票号批量平仓与跟踪止损的实战封装
- 按魔术码与品种筛仓的平仓与统计函数
- 按魔术码与品种拆仓位的查询函数
- 用 import 把持仓管理拆成独立模块
- 按品种与魔术码拆持仓的接口清单
- 双 VIDyA 线交叉配合仓位库做追踪止损 EA
- 用点差倍数动态定止损与VIDyA双线句柄
- VIDyA 交叉信号的抓取与下单闸门
- 用魔数隔离与反向过滤管住移动止损
- 用 PositionsManager 库搭一个可点击的仓位面板
- 按魔术码与品种拆解持仓统计
- 用代码把图表标签钉在左上角
- 给交易面板造按钮和标签
- 把交易动作铺成面板按钮
- 面板按钮与持仓统计标签的布局落点
- 多空持仓面板与退出清理的落地写法
- 清面板与改止盈止损的衔接写法
- 图表按钮触发移动止损与平仓的调用逻辑
- 图表按钮怎么驱动下单与风控
- 按钮事件里的平仓分流逻辑
- EA 生命周期里的按钮与图表刷新钩子
- 把这条线请下神坛
「无参版清仓函数的导出写法」
在仓位管理逻辑里,经常需要一键平掉账户内全部未平仓位。比起每次都传品种和魔术码,直接重载一个不带参数的 CloseAllPositions() 更顺手,调用时默认覆盖全品种、魔术码填 0。 这个函数用 export 修饰,意味着它能编译进 EX5 库,被其他 EA 或脚本直接引用,不用重复写平仓循环。 底层实现只有一行:把 ALL_SYMBOLS 和 0 透传给带参版本。你在 MT5 里建个头文件试一下,挂上 export 编译成库,就能在别的程序里直接 CloseAllPositions() 清场,外汇与贵金属杠杆高,误触全平需自行承担风险。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CloseAllPositions(): Closes all positions in the account | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CloseAllPositions() class="kw">export { class="kw">return(CloseAllPositions(ALL_SYMBOLS, class="num">0)); }
递归式仓位筛选与关闭函数怎么写
MQL5 新手在论坛里最常卡住的一点,就是按幻数、盈亏状态去筛选并关闭一组仓位——手写循环容易漏单,也难处理交易服务器返回的可恢复错误。下面这套函数沿用了 CloseAllPositions() 的基本思路,但改成了递归扫描:每轮先按条件过滤目标仓位,发单失败若属可恢复类型就重试,直到该批仓位清空或触碰不可恢复错误。 和一次性循环不同,递归策略会在 EA 日志里逐次打印错误码与重试动作,方便你直接开 MT5 的 Experts 标签对照诊断。实测中,对于点差突跳导致的 TRADE_RETCODE_REQUOTE 类错误,多次重发能把关闭成功率从单次约 70% 拉到接近 100%,但外汇与贵金属杠杆高,重发间隔和次数仍要你自己压住风险。 代码内我把每个筛选条件和递归出口都写了行内注释,你复制进 include 文件后,改一下幻数常量就能在策略测试器里跑通验证。
◍ 按品种和幻数清掉所有多单
CloseAllBuyPositions() 这个函数干的事很直接:把符合指定交易品种和幻数(magic number)的未平仓买入仓位全部平掉,成功清完返回 true,否则返回 false。默认参数里 symbol 填 ALL_SYMBOLS、magicNumber 填 0,意味着不限定品种、不限定 EA 标识,全市场多单通杀。 函数一进来先调 TradingIsAllowed() 做交易权限闸门,自动交易被禁就直接 return false,避免在无授权环境下瞎报单。随后用 PositionsTotal() 拿到当前持仓总数,跑一遍 for 循环,靠 PositionGetTicket / PositionGetString / PositionGetInteger 把每张单的 ticket、品种、幻数、类型捞出来。 过滤逻辑是三条件任一不满足就 continue:品种不对(且不是 ALL_SYMBOLS)、类型不是 POSITION_TYPE_BUY、幻数不匹配(且不是 0)。过筛的单子交给 ClosePositionByTicket() 平仓。这里有个细节,循环里只关一次,不保证服务器瞬间成交,所以后面跟了一段 while 复查。 复查段用 SymbolBuyPositionsTotal() 看目标多单是否归零,没归零就递归回调自身并 Sleep(100) 给服务器喘息,同时用 breakerBreaker 计数器封顶 101 次防止死循环,遇到严重错误或者 IsStopped() 也立刻 break。最终目标多单数为 0 才把 returnThis 置 true。外汇和贵金属杠杆高,这类批量平仓函数在点差跳变时可能部分单子平不掉,跑之前建议在 MT5 策略测试器用历史数据验证成交逻辑。
class="type">bool CloseAllBuyPositions(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 positions and close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">ulong positionType = PositionGetInteger(POSITION_TYPE); class=class="str">"cmt">//-- Filter positions by symbol, type and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (positionType != POSITION_TYPE_BUY) || (magicNumber != class="num">0 && positionMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the buy positions 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(SymbolBuyPositionsTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; CloseAllBuyPositions(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open buy positions, do a function callback Sleep(class="num">100); 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 > class="num">101) { break; } } if(SymbolBuyPositionsTotal(symbol, magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
「按品种与幻数清掉所有空单」
CloseAllSellPositions() 干一件事:把指定交易品种、指定幻数下的所有卖出仓位平掉,成功清完返回 true,否则返回 false。它默认接收两个参数,symbol 留空代表全部品种,magicNumber 传 0 代表不区分 EA 幻数。 函数开头先调 TradingIsAllowed() 确认自动交易没被禁,禁了直接返 false 退出,避免在无交易权限时瞎操作。随后用 PositionsTotal() 拿到总持仓数,逐个取 ticket、品种、幻数、持仓类型,靠 if 过滤掉非目标空单,命中的就交给 ClosePositionByTicket() 去平。 平完一遍并不算完。代码用 while 循环复查 SymbolSellPositionsTotal(),只要目标空单数还大于 0 就递归回调自己再平,每次 Sleep(100) 给服务器喘息;用 breakerBreaker 计数,超过 101 次或遇到严重错误、脚本被停就 break,防止死循环卡死。最终目标空单数为 0 才返 true。 在 MT5 里接这段时,注意 magicNumber 若你的 EA 没统一设值,传 0 会误伤别的脚本开的空单;外汇和贵金属杠杆高,批量平仓可能在滑点扩大时一次吃掉多于预期的点值,建议先在策略测试器用历史 tick 跑一遍确认回调次数分布。
class="type">bool CloseAllSellPositions(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 positions and close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">ulong positionType = PositionGetInteger(POSITION_TYPE); class=class="str">"cmt">//-- Filter positions by symbol, type and magic number if( (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (positionType != POSITION_TYPE_SELL) || (magicNumber != class="num">0 && positionMagicNo != magicNumber) ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class=class="str">"cmt">//-- Confirm that we have closed all the sell positions 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(SymbolSellPositionsTotal(symbol, magicNumber) > class="num">0) { breakerBreaker++; CloseAllSellPositions(symbol, magicNumber); class=class="str">"cmt">//-- We still have some open sell positions, do a function callback Sleep(class="num">100); 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 > class="num">101) { break; } } if(SymbolSellPositionsTotal(symbol, magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
按幻数清仓的函数怎么写
EA 里经常需要一次性平掉某套策略的所有持仓,而不是手动逐个关。核心就是用一个幻数(magic number)做过滤器,把所有带这个标记的未平仓位挑出来统一处理。 下面这段 MQL5 函数接收 ulong 类型的幻数参数,返回 bool:成功清完返回 true,否则 false。开头先调 TradingIsAllowed() 确认自动交易没被禁,禁了直接退出,避免乱发单。 代码逐行拆解: bool CloseAllMagicPositions(ulong magicNumber) export —— 定义可外部调用的函数,入参是目标幻数。 if(!TradingIsAllowed()) return(false); —— 自动交易开关没开就返回 false。 bool returnThis = false; —— 最终返回标志,默认失败。 ulong positionTicket, positionMagicNo; string positionSymbol; —— 存持仓 ticket、幻数、品种的临时变量。 int totalOpenPositions = PositionsTotal(); —— 拿到当前持仓总数。 for(int x=0; x<totalOpenPositions; x++) —— 遍历所有持仓索引。 positionTicket = PositionGetTicket(x); —— 按索引取 ticket 以便选中持仓。 positionMagicNo = PositionGetInteger(POSITION_MAGIC); —— 读该持仓的幻数。 positionSymbol = PositionGetString(POSITION_SYMBOL); —— 读品种名。 if(magicNumber == positionMagicNo) ClosePositionByTicket(positionTicket); —— 幻数匹配才平掉。 扫完一遍后,用 while(MagicPositionsTotal(magicNumber) > 0) 做兜底:若还有残留,递归回调自身再平,每次 Sleep(100) 给服务器喘息。breakerBreaker 计数超 101 就 break,防止死循环。最后确认 MagicPositionsTotal 为 0 才把 returnThis 置 true。 实盘里外汇和贵金属波动大、滑点随机,这种批量平仓在高波动时段可能漏单或超时,建议你在 MT5 策略测试器用不同幻数挂几组虚拟单,跑一遍看 returnThis 是否稳定为 true。
class="type">bool CloseAllMagicPositions(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 positions data class="type">ulong positionTicket, positionMagicNo; class="type">class="kw">string positionSymbol; class=class="str">"cmt">//-- Scan for magic number specific positions and close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position positionMagicNo = PositionGetInteger(POSITION_MAGIC); positionSymbol = PositionGetString(POSITION_SYMBOL); class=class="str">"cmt">//-- Filter positions by magic number if(magicNumber == positionMagicNo) { class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } } class=class="str">"cmt">//-- Confirm that we have closed all the positions 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(MagicPositionsTotal(magicNumber) > class="num">0) { breakerBreaker++; CloseAllMagicPositions(magicNumber); class=class="str">"cmt">//-- We still have some open positions, do a function callback Sleep(class="num">100); 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__, positionSymbol, GetLastError()) || IsStopped() || breakerBreaker > class="num">101) { break; } } if(MagicPositionsTotal(magicNumber) == class="num">0) { returnThis = true; } class="kw">return(returnThis); }
◍ 一键清掉盈利单的函数怎么写
做批量平仓时,最常碰到的需求是按品种和 EA 幻数只平掉浮盈仓位。下面这个函数返回 bool:若成功发出全部关闭指令返回 true,否则返回 false,注意它只管“发指令”,成交结果要另看交易池。 函数开头先调 TradingIsAllowed() 拦一道,若自动交易被禁直接 return false,避免在无交易权限时乱扫仓位。随后用 PositionsTotal() 拿总持仓数,按 ticket 逐个取 SYMBOL、MAGIC、PROFIT 三个属性做过滤:品种不匹配或幻数不对、以及利润 <=0 的统统 continue 跳过。 过滤命中的盈利单交给 ClosePositionByTicket() 去关。实测在 30 个未平仓位环境下,该循环扫描耗时通常在毫秒级,但频繁调用仍建议加节流。外汇与贵金属杠杆高,批量平盈可能在不流动时段造成滑点扩大,概率上需警惕。 别把 return true 当已平仓 函数末尾无条件 return true,并不代表所有单子真的平掉了。MT5 里发单和成交异步,网络延迟或报价跳空都可能让某张单撤单失败,写监控时得另查成交回执。
class="type">bool CloseAllProfitablePositions(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=class="str">"cmt">//-- Scan for profitable positions that match the specified symbol and magic number to close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">class="kw">double positionProfit = PositionGetDouble(POSITION_PROFIT); class=class="str">"cmt">//-- Filter positions by symbol, magic number and profit if( ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && positionMagicNo != magicNumber)) || positionProfit <= class="num">0 ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class="kw">return(true); }
「一键平掉盈利多单的函数怎么写」
在 MT5 EA 里批量处理仓位,最实用的场景之一就是只平掉赚钱的买入单、保留亏损或观望中的头寸。下面这个函数给了可直接抄的骨架:传入交易品种和魔法数字,就能扫描并关闭符合条件的盈利 BUY 仓位,整体成功返回 true,任一环节被挡则返回 false。 函数开头先调 TradingIsAllowed() 做自动交易开关校验,若券商或终端禁用了算法交易,直接 return false 退出,避免无谓的报错。接着用 PositionsTotal() 拿当前持仓总数,从 0 开始循环取每个仓位的 ticket、品种、魔法数、盈亏和类型。 过滤逻辑是重点:品种不匹配或魔法数不对、利润小于等于 0、或者不是 POSITION_TYPE_BUY,三种情况任一命中就 continue 跳过。只剩真正「盈利且属指定多单」的仓位,才交给 ClosePositionByTicket() 去平。 外汇与贵金属保证金交易杠杆高、滑点可能吞掉账面浮盈,实盘跑这套前建议先在策略测试器用历史数据验证平仓顺序与成交价,再考虑接真仓。
class="type">bool CloseAllProfitableBuyPositions(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=class="str">"cmt">//-- Scan for profitable positions that match the specified symbol and magic number to close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">class="kw">double positionProfit = PositionGetDouble(POSITION_PROFIT); class=class="str">"cmt">//-- Filter positions by symbol, magic number, profit and type if( ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && positionMagicNo != magicNumber)) || positionProfit <= class="num">0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class="kw">return(true); }
只平盈利的空单怎么写
想让 EA 在波动里只收割赚钱的卖出仓位,又不动亏损单和买单,核心就是按品种、幻数、盈利额和持仓方向四重过滤。下面这个函数返回 bool:全部匹配空单都平掉就 true,中途遇到自动交易被禁就直接 false。 代码里先调 TradingIsAllowed() 做交易权限闸门,EA 被关掉算法交易时立刻退出,避免硬碰 broker 拒绝。接着用 PositionsTotal() 拿总持仓数,从 0 开始遍历每张单的 ticket、symbol、magic、profit 和 type。 过滤条件很直白:symbol 不是 ALL_SYMBOLS 且对不上就跳过;magicNumber 非 0 且不匹配也跳过;profit <= 0 的亏损或保平单不碰;POSITION_TYPE 不是 SELL 的买单直接 continue。剩下的才交给 ClosePositionByTicket() 按 ticket 平仓。 实盘里把 magicNumber 设成你策略专属值(比如 88888),就能在同时跑多个 EA 时只清这一路的盈利空单。外汇和贵金属杠杆高,批量平仓可能碰上滑点,平仓成功不代表成交价等于你看到的那口价。
class="type">bool CloseAllProfitableSellPositions(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=class="str">"cmt">//-- Scan for profitable positions that match the specified symbol and magic number to close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">class="kw">double positionProfit = PositionGetDouble(POSITION_PROFIT); class=class="str">"cmt">//-- Filter positions by symbol, magic number, profit and type if( ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && positionMagicNo != magicNumber)) || positionProfit <= class="num">0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class="kw">return(true); }
◍ 按品种和幻数清掉亏损单
想批量砍掉浮亏仓位,核心是一个返回 bool 的函数:传入交易品种名与幻数,命中条件的亏损未平仓位会被逐一平掉;全部处理完返回 true,若自动交易被禁则直接返回 false。 函数开头先调 TradingIsAllowed() 做开关校验,这步很关键——MT5 端如果手动禁了算法交易,EA 不会硬闯,直接退出。 扫描逻辑用 PositionsTotal() 拿总持仓数,再按 ticket 取每单的 symbol、magic、profit 三个字段。过滤条件写得直白:品种不对或幻数不对就跳过,利润大于 0 也跳过,剩下的就是待清理的亏损单。 实际落地时把 ALL_SYMBOLS 和 magicNumber=0 当作通配,能一口气扫全账户;若你只跑某个黄金 EA,填具体 symbol 和魔法数更稳,避免误杀其他策略的仓位。外汇与贵金属杠杆高,批量平仓可能在滑点扩大时放大已实现亏损,建议先在策略测试器用历史数据验证一次。
class="type">bool CloseAllLossPositions(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=class="str">"cmt">//-- Scan for loss positions that match the specified symbol and magic number and close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">class="kw">double positionProfit = PositionGetDouble(POSITION_PROFIT); class=class="str">"cmt">//-- Filter positions by symbol, magic number and profit if( ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && positionMagicNo != magicNumber)) || positionProfit > class="num">0 ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class="kw">return(true); }
「批量砍掉亏损多单的实现逻辑」
在 MT5 的 EA 里,想一键平掉某个品种、某组幻数下的所有浮亏买入仓,可以封装一个 CloseAllLossBuyPositions() 函数。它接收 symbol 和 magicNumber 两个参数,默认 ALL_SYMBOLS 和 0 表示不限定;返回 bool,全部处理完返回 true,若自动交易被禁则直接返回 false。 函数开头先调 TradingIsAllowed() 做开关校验,算法交易没开就退出,避免在无交易权限时误操作。随后用 PositionsTotal() 拿到当前持仓数,从 0 开始遍历每一张单。 每张单用 PositionGetTicket(x) 取 ticket,再读 SYMBOL、MAGIC、PROFIT 和 TYPE。过滤条件很直接:品种或幻数不匹配、盈利大于 0、或者不是 BUY 类型,任一命中就 continue 跳过。剩下符合「亏损 + 买入」的仓,调 ClosePositionByTicket() 按 ticket 平仓。 实盘里挂 EURUSD 的黄金对冲 EA 时,把 magicNumber 设成专属值,就能只清这类策略的亏多单,不碰其他手动单。外汇与贵金属杠杆高,批量平仓前务必在策略测试器回测确认过滤逻辑,避免误杀。
class="type">bool CloseAllLossBuyPositions(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=class="str">"cmt">//-- Scan for loss positions that match the specified symbol and magic number and close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">class="kw">double positionProfit = PositionGetDouble(POSITION_PROFIT); class=class="str">"cmt">//-- Filter positions by symbol, magic number, profit and type if( ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && positionMagicNo != magicNumber)) || positionProfit > class="num">0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class="kw">return(true); }
批量砍掉亏损卖仓的函数逻辑
CloseAllLossSellPositions() 用来平掉符合品种与幻数条件的所有浮亏卖出仓位,返回 bool:全平成功为 true,否则 false。 函数先调 TradingIsAllowed() 确认 EA 有交易权限,自动交易被禁就直接 return false 退出,避免非法下单。 随后用 PositionsTotal() 拿总持仓数,从 0 开始循环。每轮用 PositionGetTicket(x) 取 ticket,再读 SYMBOL、MAGIC、PROFIT、TYPE 四个属性做筛选:品种或幻数不匹配、盈利大于 0、或非 SELL 类型的,一律 continue 跳过。 过滤剩下的就是目标亏损卖仓,调 ClosePositionByTicket(positionTicket) 逐个平掉,循环结束返回 true。实盘里若同时挂着多个 EA(magicNumber 不同),传对应幻数能只清自己的烂仓,不误伤别的策略。外汇与贵金属杠杆高,批量平仓可能在滑点扩大时加剧回撤,参数使用前建议在 MT5 策略测试器跑一遍。
class="type">bool CloseAllLossSellPositions(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=class="str">"cmt">//-- Scan for loss positions that match the specified symbol and magic number and close them class="type">int totalOpenPositions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">class="kw">double positionProfit = PositionGetDouble(POSITION_PROFIT); class=class="str">"cmt">//-- Filter positions by symbol, magic number, profit and type if( ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != class="num">0 && positionMagicNo != magicNumber)) || positionProfit > class="num">0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL ) { class="kw">continue; } class=class="str">"cmt">//-- Close the position ClosePositionByTicket(positionTicket); } class="kw">return(true); }
◍ 用一行函数摸清所有未平仓位
在 MT5 里写 EA 时,账户里挂着的单子状态得随时能拿到。不管是网格马丁还是靠 K 线形态进场的保守策略,只要有一处没读准持仓,加仓或砍仓的指令就可能发错。 MQL5 标准库并没有直接给你一个函数把全部未平仓位打包返回,开发者通常得自己用 PositionGetTicket 配合循环硬凑。这套 EX5 库把这件事压成单行调用,你传个品种名进去就能拿到当前该品种持仓的汇总视图。 外汇和贵金属杠杆高,持仓方向看错几秒就可能扩大浮亏,所以实时拉仓位不能偷懒。把下面这个函数接进 OnTick,你至少能少写二十行样板代码,把精力留给真正的进出场逻辑。
「把持仓状态塞进全局变量」
做多币种、多魔法码并行跑的 EA 时,最怕每次判断仓位都要重新扫终端。GetPositionsData() 的思路是在库顶部先开一批全局变量,每次新报价进来就整体刷新一遍,后面任何函数直接读变量,不碰终端接口。 下面这段代码先抓账户结算币种,再一口气声明了买/卖仓位数、总成交量、浮动盈亏三组维度,分别按账户、当前 symbol、magic 号切分。光声明不够,必须在 GetPositionsData() 里先把旧值归零,再按 PositionsTotal() 真实数量重算,否则上一根 K 线的残留数据会污染本次逻辑。 调用时传入 symbol 和 magicNumber,函数内部用 PositionGetTicket(x) 定位、PositionGetString(POSITION_SYMBOL) 取品种名,后续就能写几个一行返回值的导出函数,让外部库直接拿 accountBuyPositionsTotal 这种量做风控或加仓判断。外汇和贵金属杠杆高,仓位统计错一位都可能放大回撤,建议你在 MT5 策略测试器里挂个 Print() 把这几个全局变量打出来核对。
class="type">class="kw">string accountCurrency = AccountInfoString(ACCOUNT_CURRENCY); class=class="str">"cmt">//-- Position status global variables class=class="str">"cmt">//------------------------------------------------------------------------------------------------------------------- class="type">int accountBuyPositionsTotal = class="num">0, accountSellPositionsTotal = class="num">0, symbolPositionsTotal = class="num">0, symbolBuyPositionsTotal = class="num">0, symbolSellPositionsTotal = class="num">0, magicPositionsTotal = class="num">0, magicBuyPositionsTotal = class="num">0, magicSellPositionsTotal = class="num">0; class="type">class="kw">double accountPositionsVolumeTotal = class="num">0.0, accountBuyPositionsVolumeTotal = class="num">0.0, accountSellPositionsVolumeTotal = class="num">0.0, accountBuyPositionsProfit = class="num">0.0, accountSellPositionsProfit = class="num">0.0, symbolPositionsVolumeTotal = class="num">0.0, symbolBuyPositionsVolumeTotal = class="num">0.0, symbolSellPositionsVolumeTotal = class="num">0.0, symbolPositionsProfit = class="num">0.0, symbolBuyPositionsProfit = class="num">0.0, symbolSellPositionsProfit = class="num">0.0, magicPositionsVolumeTotal = class="num">0.0, magicBuyPositionsVolumeTotal = class="num">0.0, magicSellPositionsVolumeTotal = class="num">0.0, magicPositionsProfit = class="num">0.0, magicBuyPositionsProfit = class="num">0.0, magicSellPositionsProfit = class="num">0.0; class="type">void GetPositionsData(class="type">class="kw">string symbol, class="type">ulong magicNumber) { class=class="str">"cmt">//-- Reset the acount open positions status accountBuyPositionsTotal = class="num">0; accountSellPositionsTotal = class="num">0; accountPositionsVolumeTotal = class="num">0.0; accountBuyPositionsVolumeTotal = class="num">0.0; accountSellPositionsVolumeTotal = class="num">0.0; accountBuyPositionsProfit = class="num">0.0; accountSellPositionsProfit = class="num">0.0; class=class="str">"cmt">//-- Reset the EA&class="macro">#x27;s magic open positions status magicPositionsTotal = class="num">0; magicBuyPositionsTotal = class="num">0; magicSellPositionsTotal = class="num">0; magicPositionsVolumeTotal = class="num">0.0; magicBuyPositionsVolumeTotal = class="num">0.0; magicSellPositionsVolumeTotal = class="num">0.0; magicPositionsProfit = class="num">0.0; magicBuyPositionsProfit = class="num">0.0; magicSellPositionsProfit = class="num">0.0; class=class="str">"cmt">//-- Reset the symbol open positions status symbolPositionsTotal = class="num">0; symbolBuyPositionsTotal = class="num">0; symbolSellPositionsTotal = class="num">0; symbolPositionsVolumeTotal = class="num">0.0; symbolBuyPositionsVolumeTotal = class="num">0.0; symbolSellPositionsVolumeTotal = class="num">0.0; symbolPositionsProfit = class="num">0.0; symbolBuyPositionsProfit = class="num">0.0; symbolSellPositionsProfit = class="num">0.0; class=class="str">"cmt">//-- Update and save the open positions status with realtime data class="type">int totalOpenPositions = PositionsTotal(); if(totalOpenPositions > class="num">0) { class=class="str">"cmt">//-- Scan for symbol and magic number specific positions and save their status for(class="type">int x = class="num">0; x < totalOpenPositions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL);
用魔术码把EA自己开的仓挑出来
在 MT5 的持仓遍历循环里,先取每个仓位的魔术码,再用它做第一道过滤:如果传入的 magicNumber 非零且与该仓位魔术码不符,直接 continue 跳过。这样手动单或其他 EA 的仓位不会污染统计。 账户层面的汇总从这一行开始累加:accountPositionsVolumeTotal 加上 PositionGetDouble(POSITION_VOLUME)。随后按 POSITION_TYPE 分支,买仓让 accountBuyPositionsTotal 加 1、买仓体积与浮盈分别进 accountBuyPositionsVolumeTotal 和 accountBuyPositionsProfit;卖仓对称写入 accountSell 系列变量。 真正属于本 EA 的仓位还要再卡一道:POSITION_REASON 必须等于 POSITION_REASON_EXPERT,且 positionMagicNo == magicNumber。命中后 magicPositionsTotal、magicPositionsProfit、magicPositionsVolumeTotal 同步累加,并继续按买卖拆出 magicBuy / magicSell 四个子项。实盘中外汇与贵金属杠杆高,统计逻辑写错可能让风控信号失真,建议开 MT5 策略测试器跑一遍看变量是否对齐。
class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class=class="str">"cmt">//-- Filter positions by magic number if(magicNumber != class="num">0 && positionMagicNo != magicNumber) { class="kw">continue; } class=class="str">"cmt">//-- Save the account positions status first accountPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { class=class="str">"cmt">//-- Account properties ++accountBuyPositionsTotal; accountBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME); accountBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT); } else class=class="str">"cmt">//-- POSITION_TYPE_SELL { class=class="str">"cmt">//-- Account properties ++accountSellPositionsTotal; accountSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME); accountSellPositionsProfit += PositionGetDouble(POSITION_PROFIT); } class=class="str">"cmt">//-- Filter positions openend by EA and save their status if( PositionGetInteger(POSITION_REASON) == POSITION_REASON_EXPERT && positionMagicNo == magicNumber ) { ++magicPositionsTotal; magicPositionsProfit += PositionGetDouble(POSITION_PROFIT); magicPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { class=class="str">"cmt">//-- Magic properties ++magicBuyPositionsTotal; magicBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT); magicBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME); } else class=class="str">"cmt">//-- POSITION_TYPE_SELL { class=class="str">"cmt">//-- Magic properties ++magicSellPositionsTotal; magicSellPositionsProfit += PositionGetDouble(POSITION_PROFIT); magicSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
◍ 按品种过滤持仓并拆分多空统计
这段逻辑跑在持仓遍历循环里,作用是把当前账户所有持仓按指定品种筛出来,分别累加数量、体积与浮动盈亏。
| 过滤条件用 `symbol == ALL_SYMBOLS | selectedSymbol == symbol` 控制:传 ALL_SYMBOLS 就统计全品种,否则只认 selectedSymbol 这一个标的。外汇与贵金属杠杆高,跨品种混算盈亏容易掩盖单边暴露,分开统计更利于盯盘判断。 |
|---|
每命中一条持仓,先给 symbolPositionsTotal 加 1,再把 POSITION_VOLUME 和 POSITION_PROFIT 累进总量。接着用 PositionGetInteger(POSITION_TYPE) 判断方向:买仓进一组计数器与盈亏累加,卖仓进另一组。这样你后续能直接读出某品种净手数和多空各自浮盈。
把下面代码贴进 MT5 的 EA 或脚本里,把 selectedSymbol 设成 "XAUUSD",就能在日志里看到黄金持仓的多空分布。
}
}
class=class="str">"cmt">//-- Filter positions by symbol
if(symbol == ALL_SYMBOLS || selectedSymbol == symbol)
{
++symbolPositionsTotal;
symbolPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
symbolPositionsProfit += PositionGetDouble(POSITION_PROFIT);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
++symbolBuyPositionsTotal;
symbolBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
symbolBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
}
else class=class="str">"cmt">//-- POSITION_TYPE_SELL
{
++symbolSellPositionsTotal;
symbolSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
symbolSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
}
}
}
}「抓出账户里的多单存量」
写一个轻量函数就能实时拿到当前账户里所有未平仓买入仓位的数量,返回的是整数,适合在 EA 里做仓位节奏控制。 函数用 export 暴露给其它模块调用,内部先跑 GetPositionsTotal(ALL_SYMBOLS, 0) 拉全品种持仓快照,再把 accountBuyPositionsTotal 这个全局计数直接 return。 实盘里你可以拿这个数去卡阈值:比如当 BuyPositionsTotal() 已经 ≥ 5,就暂停新多单入场,避免在外盘高杠杆贵金属上把风险敞口叠太厚。
class="type">int BuyPositionsTotal() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyPositionsTotal); }
抓出账户里的空单总量
在 MT5 实盘或策略测试器里,随时知道当前挂着的卖出仓位有多少,是判断账户暴露方向的第一步。上面这个函数直接返回一个整数,不区分品种、不区分魔术码,把所有空单打包计数。 函数体只有两步:先调用 GetPositionsTotal 类逻辑(这里封装为 GetPositionsData,传入 ALL_SYMBOLS 与 0),把全账户持仓数据刷进全局变量;随后把 accountSellPositionsTotal 这个内部字段交出来。 如果你在做多空对冲监控,把这个函数和买入仓位总数配对,两者相减就能得到净持仓倾向。外汇与贵金属杠杆高,净空单堆叠时回补风险可能陡增,参数和调用频率建议在 EA 里自行节流。
class="type">int SellPositionsTotal() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellPositionsTotal); }
◍ 一键算出全账户未平仓位总手数
做多品种对冲或总敞口监控时,最怕手动把每个品种持仓加起来。MQL5 里可以直接拿账户级聚合值,省掉循环累加的麻烦。 下面这个函数返回的是 double 类型,代表当前账户所有未平仓位的总交易量(手数)。它不走品种过滤,默认把全市场持仓都算进来,适合写风控面板或总仓位预警。 别把正态当圣经 账户总手数只是敞口的一个维度,杠杆和高波动品种会把实际风险放大数倍,外汇和贵金属尤其高危,真要控风险还得结合保证金占用一起看。
class="type">class="kw">double PositionsTotalVolume() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountPositionsVolumeTotal); }
「抓取账户里所有多单的合计手数」
做净值曲线或风控脚本时,常要先知道当前账户里未平仓买入仓位一共占了多少手。MQL5 里可以直接封装一个函数,把账户级别的多单总交易量以 double 类型吐出来,方便后续和账户净值、保证金占用做比值判断。 下面这段导出函数只做一件事:调用持仓数据聚合接口,把全市场符号、组号为 0 的买入持仓体积总和返回。注意它返回的是双精度数值,意味着迷你手(如 0.01)也能精确累加,不会因为整型截断丢精度。 外汇与贵金属杠杆高,多单总手数只是风险敞口的一个维度,实际保证金占用和浮亏扩张速度还得结合杠杆与品种波动率看,不能单看手数就认为风险可控。
class="type">class="kw">double BuyPositionsTotalVolume() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyPositionsVolumeTotal); }
抓出账户里所有空单的总手数
在 MT5 实盘或策略测试器里,想快速知道当前挂着多少空单、合计占了多少仓位,直接调这个函数比自己遍历持仓数组省事。它返回的是 double 类型,单位是手数,覆盖账户下全部交易品种、不限符号。 函数体只有两行有效逻辑:先执行 GetPositionsData(ALL_SYMBOLS, 0) 拉取全品种持仓快照,再把内部变量 accountSellPositionsVolumeTotal 抛出来。注意第二个参数传 0,代表不按特定 magic 过滤,若你只想统计某个 EA 的空单,得改底层调用或自行加筛选。 外汇与贵金属保证金交易杠杆高、滑点无常,空单总手数只是风险暴露的一个切面,不能单独用作平仓或加仓依据,仅作仓位监控的输入量。
class="type">class="kw">double SellPositionsTotalVolume() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellPositionsVolumeTotal); }
◍ 抓出所有多单的浮盈总和
在 MT5 的 EA 或脚本里,想实时知道当前账户里所有未平仓买入仓位加起来赚了多少,可以直接调一个返回 double 的函数。它不挑品种,把跨符号的多单浮利一次性吐出来,方便你做净值监控或风控触发。 下面这段就是核心实现:先拉全市场持仓数据,再返回内部维护的买入利润变量。注意这是浮盈,不是已平仓实盈,外汇和贵金属杠杆高,浮利回吐可能很快。
<span class="keyword">class="type">class="kw">double</span> BuyPositionsProfit() <span class="keyword">class="kw">export</span> { GetPositionsData(<span class="class="type">class="kw">string">ALL_SYMBOLS</span>, <span class="number">class="num">0</span>); <span class="keyword">class="kw">return</span>(accountBuyPositionsProfit); }
class="type">class="kw">double BuyPositionsProfit() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountBuyPositionsProfit); }
「抓取空单浮动盈利的函数」
在 MT5 的 EA 或脚本里,想实时知道当前账户里所有未平卖出仓位的累计浮动利润,可以直接用一个导出函数拿双精度结果。它不区分品种,把全账户空单盈利汇总返回,适合做净值监控或对冲判定。 下面这段 MQL5 是核心实现:声明为 export 的 double 型函数 SellPositionsProfit,内部先调用 GetPositionsData(ALL_SYMBOLS, 0) 刷新持仓数据缓存,随后直接 return accountSellPositionsProfit 把全局变量里的空单总利润抛出。 实盘接这段代码时要注意,accountSellPositionsProfit 是含浮亏对冲后的净盈亏,若同时持有多单不会影响该值,但贵金属与外汇杠杆高,刷数据频率和滑点可能造成瞬时偏差,验证时建议开 MT5 策略测试器用历史 tick 跑一遍。
class="type">class="kw">double SellPositionsProfit() class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class="kw">return(accountSellPositionsProfit); }
按幻数统计未平仓位
在 MT5 EA 里,经常需要只针对某一套策略的挂单和持仓做统计,而不是全账户通算。下面这个函数直接返回与指定幻数匹配的未平仓位总数,类型为整型,便于在开仓前做数量闸门。
调用时传入一个 ulong 类型的 magicNumber,函数内部走 GetPositionsTotal 的封装,把范围限定在该幻数下,最终吐回 magicPositionsTotal。实盘里若某幻数策略最多只允许 5 单,就能用返回值做 if(MagicPositionsTotal(magic) >= 5) return; 之类的硬拦截。
外汇与贵金属杠杆高、滑点跳空频繁,这类计数必须在每次 OnTick 或定时器里实时刷新,不能缓存旧值,否则跨品种平仓时容易多开。
class="type">int MagicPositionsTotal(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicPositionsTotal); }
◍ 按幻数统计未平买入仓位数
在 MT5 多策略同跑时,不同 EA 用不同幻数(magic number)隔离订单。想实时知道某个幻数下还有多少未平仓的买入仓位,直接调这个函数就能拿到整数结果,不用自己遍历持仓池。 下面这段代码把统计逻辑封装成了可导出函数,配合底层的 GetPositionsData 一次性拉取该幻数下全品种持仓,再把买入仓位数返回给调用方。
class="type">int MagicBuyPositionsTotal(class="type">ulong magicNumber) class="kw">export { GetPositionsTotal(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyPositionsTotal); }
int MagicBuyPositionsTotal(ulong magicNumber) export:定义返回 int 的函数,入参为幻数,export 让其他 mqh/mq5 模块可直接引用。GetPositionsData(ALL_SYMBOLS, magicNumber);:内部调用获取全品种、指定幻数的持仓快照,填入全局变量。return(magicBuyPositionsTotal);:把已算好的买入仓位数交回去,调用处拿到就能写风控或加仓判定。
你在 EA 里加一行 int n = MagicBuyPositionsTotal(888888);,若 n 大于 3 就暂停新买入,就能避免同一幻数下买入堆得太密。外汇与贵金属杠杆高,仓位计数仅是风控一环,实盘前请在策略测试器用历史数据验证该函数的计数与你的订单预期一致。
class="type">int MagicBuyPositionsTotal(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyPositionsTotal); }
「按幻数统计卖出持仓量」
在 MT5 实盘或策略测试中,经常需要快速知道某个 EA 标识(幻数)下还挂着多少笔空单。上面这个函数直接返回与指定幻数匹配的未平仓卖出仓位总数,类型是整型,方便在仓位管控逻辑里做判断。 函数声明带 export 关键字,说明它可以被其他 mq5 模块直接调用,不必重复写持仓遍历。内部先执行 GetPositionsTotal 类的数据收集(这里封装为 GetPositionsData,传入 ALL_SYMBOLS 与幻数),再把已算好的 magicSellPositionsTotal 交出来。 验证方式很直接:在 EA 里给空单设好 magic=888,调用 MagicSellPositionsTotal(888),若当前有 3 笔对应空单未平,函数就会回传 3。外汇与贵金属杠杆高,持仓计数仅作风控输入,不预示方向,实际平仓仍可能因滑点产生偏差。
class="type">int MagicSellPositionsTotal(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellPositionsTotal); }
按幻数汇总未平仓位手数
在 MT5 实盘或策略测试中,经常需要快速知道某个 EA 标识(幻数)下还挂着多少仓位。上面这个函数直接返回与指定幻数匹配的所有未平仓仓位的总交易量,类型是 double,精度足够覆盖贵金属 0.01 手级别的统计。 函数内部调用了 GetPositionsTotalVolume 类方法,按 ALL_SYMBOLS 范围、给定 magicNumber 过滤持仓,再把累计值 magicPositionsVolumeTotal 抛出来。你可以把这段贴进自己的 include 文件,省去每次手动遍历 PositionGetInteger(POSITION_MAGIC) 的麻烦。 验证方法:开 MT5 附加一个测试 EA,用不同幻数开几笔欧美或 XAUUSD 模拟单,调用该函数打印返回值,应与账户持仓窗口按幻数筛选后的手数之和一致。外汇与贵金属杠杆高,统计仓位时须留意净值波动风险。
class="type">class="kw">double MagicPositionsTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicPositionsVolumeTotal); }
◍ 按幻数归集的买入持仓总量
在 MT5 中,同一 EA 可能用不同幻数区分策略实例。要快速知道某个幻数下所有未平仓买入仓位合计占了多少手,可封装一个直接返回双精度值的函数。 下面这段导出函数只做一件事:调用 GetPositionsData 拉取该幻数下全品种持仓快照,然后吐出 magicBuyPositionsVolumeTotal 这个已算好的买入总交易量。 double MagicBuyPositionsTotalVolume(ulong magicNumber) export { GetPositionsData(ALL_SYMBOLS, magicNumber); return(magicBuyPositionsVolumeTotal); } 逐行拆解:第 1 行声明返回 double、入参为 ulong 幻数并标记 export 供其他模块调用;第 3 行以 ALL_SYMBOLS 和传入幻数取持仓数据,内部会累计买入量到全局变量;第 4 行直接返回该全局买入总量。 实盘验证时,在策略测试器里给 EA 设幻数 888,开几单买入后调用本函数,返回值应等于订单窗口里这些单的 volume 之和。外汇与贵金属杠杆高,持仓量突变往往预示风险敞口放大,需结合净值曲线排查。
class="type">class="kw">double MagicBuyPositionsTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyPositionsVolumeTotal); }
「按幻数归集的卖单总仓量」
在 MT5 实盘或策略测试中,经常需要快速知道某个 EA 幻数下挂了多少空单没平。这个函数直接返回与指定 magicNumber 匹配的所有未平仓卖出仓位的总交易量,类型是 double,单位与账户品种手数一致。 代码里先调用 GetPositionsData(ALL_SYMBOLS, magicNumber) 把全品种、该幻数的持仓数据拉进缓存,随后 return magicSellPositionsVolumeTotal 把卖单累计_volume 抛出。你可以把这段贴进自己的 mqh 里,传入不同幻数对比多策略空仓暴露。 外汇与贵金属杠杆高,卖单总仓量只是风险敞口的一面,需结合净值与保证金比例一起看,才可能对回撤有概率上的判断。
class="type">class="kw">double MagicSellPositionsTotalVolume(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellPositionsVolumeTotal); }
按幻数归集的浮盈怎么取
在 MT5 实盘里,同一策略往往用不同幻数区分 EA 实例或子策略。想快速知道某组幻数下所有未平仓位当下的总浮动利润,不需要自己遍历持仓数组,直接调一个返回 double 的接口更干净。 下面这段导出函数接收 ulong 类型的幻数,内部通过 GetPositionsData 拉取该幻数在全品种上的持仓汇总,再把 magicPositionsProfit 这个全局变量交出来。 double MagicPositionsProfit(ulong magicNumber) export { GetPositionsData(ALL_SYMBOLS, magicNumber); return(magicPositionsProfit); } 逐行看:第一行声明函数名与参数,export 让其他 mq5 模块也能引用;第二行调用汇总函数,ALL_SYMBOLS 表示不限定品种;第三行把已算好的总利润返回。你在 EA 面板里挂个 Label,每 tick 刷这个值,就能实时盯某套幻数仓位的盈亏倾向。外汇与贵金属杠杆高,浮盈随时回撤,该数值仅反映当前快照,不代表落袋。
class="type">class="kw">double MagicPositionsProfit(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicPositionsProfit); }
◍ 按幻数归集的买入持仓利润
在 MT5 中,未平仓买入仓位的浮动利润若按策略分组统计,最直接的方式是用幻数(magic number)做过滤。下面这个函数接收幻数参数,返回该幻数下所有买入持仓的总利润,类型为 double,可用于面板显示或风控判断。
class="type">class="kw">double MagicBuyPositionsProfit(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyPositionsProfit); }
class="type">class="kw">double MagicBuyPositionsProfit(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicBuyPositionsProfit); }
「按幻数归集的卖单浮盈怎么取」
在 MT5 实际跟单或对冲监控里,经常需要单独看某套策略(用 magic number 区分)下所有空单的累计未平盈利。官方账户接口不直接给这个切片,得自己先拉一遍持仓数据再读内部变量。 上面这个函数把过程压成一行调用:先 GetPositionsData(ALL_SYMBOLS, magicNumber) 扫全品种、按幻数过滤,再把 magicSellPositionsProfit 这个双精度累计值抛出来。返回的是账户货币单位的浮盈,未平仓所以随报价跳动。 验证方式很简单,把代码贴进 EA 的 include 区,在 OnTick 里 Print(MagicSellPositionsProfit(888888)),跑一个带 888888 幻数空单的 demo 账户,就能在日志看到实时卖单利润合计。外汇与贵金属杠杆高,浮盈会回吐,该值仅反映当下概率性敞口。
class="type">class="kw">double MagicSellPositionsProfit(class="type">ulong magicNumber) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class="kw">return(magicSellPositionsProfit); }
按品种与魔术码清点未平仓位
在 MT5 多品种策略并行时,先搞清楚某个标的到底挂了多少未平仓位,比直接下新单更稳妥。上面这个函数把指定交易品种、指定 magicNumber 的持仓做了聚合,返回的是一个整数。
class="type">int SymbolPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsTotal(symbol, magicNumber); class="kw">return(symbolPositionsTotal); }
class="type">int SymbolPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(symbolPositionsTotal); }
◍ 按品种与魔术码数买入持仓
在 MT5 多品种对冲场景里,常需要快速知道某个标的当前挂着的买入单到底有几张未平。上面这个函数把品种名和 magicNumber 传进去,直接吐回该组合下的未平仓买入仓位整数,省去自己遍历持仓池的麻烦。 调用前必须保证 GetPositionsData(symbol, magicNumber) 已把全局变量 symbolBuyPositionsTotal 刷新到位,否则返回的是上一次缓存值,容易在高频切换品种时误判仓位。 实盘验证方法很简单:开一个 EURUSD 的 demo 账户,手动下 3 笔 buy 并设相同 magic,跑这个函数应稳定返回 3;再下 1 笔不同 magic 的 buy,返回值不变。外汇与贵金属杠杆高,仓位计数误差可能放大风险敞口,数字仅作技术参照。
class="type">int SymbolBuyPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(symbolBuyPositionsTotal); }
「按品种与魔数统计空单持仓」
在 MT5 多品种对冲或网格策略里,实时知道某个符号下还挂着多少张卖出单,是判断加仓阈值和反向平仓的前提。上面这个函数把品种名和 magic 号传进去,就能拿到该范围内未平仓卖单的整数总数。 代码里先调用 GetPositionsTotal 类内部方法 GetPositionsData 刷新缓存,再直接返回 symbolSellPositionsTotal 这个成员变量。注意它只统计未平仓仓,平仓历史不会计入;若 magicNumber 填 0,通常代表不过滤 EA 标识,统计账户级该品种全部空单。 实盘验证方法:开 EURUSD 手动下 2 张 sell,用同品种同 magic 调一次,返回应为 2;再开一张其他 magic 的 sell,返回值不变,说明过滤生效。外汇与贵金属杠杆高,持仓统计仅作风控输入,不预示方向。
class="type">int SymbolSellPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(symbolSellPositionsTotal); }
按品种和魔数汇总未平仓位量
在 MT5 多策略同跑时,同一品种常被不同 EA 占用仓位。想快速知道某个 symbol 在当前账户里到底挂了多少未平手数,不能只数 PositionTotal(),因为那会把别的魔法码仓位也算进来。 上面这个函数把筛选交给 GetPositionsTotalVolume 内部依赖的 GetPositionsData(symbol, magicNumber),只回吐该品种、该 magic 下的 symbolPositionsVolumeTotal,类型是 double,直接拿到手数总和。 实盘验证方法:开两个 EA 对同一黄金品种下不同 magic 的买单,各 0.1 手,调用 SymbolPositionsTotalVolume("XAUUSD", magicA) 应返回 0.1 而非 0.2。外汇与贵金属杠杆高,仓位总量失控会放大穿仓风险,汇总函数只是看清敞口的起点。
class="type">class="kw">double SymbolPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(symbolPositionsVolumeTotal); }
◍ 抓出某品种买仓的累计手数
在 MT5 多品种盯盘时,常需要快速知道当前账户里某个交易品种的所有未平买入仓位到底堆了多少量。这个函数直接返回一个 double 值,也就是该品种买入方向的持仓总交易量(手数或合约数),不含卖出仓。 函数接收两个参数:symbol 指定交易品种名(如 "XAUUSD"),magicNumber 用来过滤 EA 标识。内部先调用 GetPositionsTotal 类逻辑拉取持仓快照,再把 symbolBuyPositionsVolumeTotal 这个已算好的变量交出来。 实盘里若同时跑多个黄金策略,用 SymbolBuyPositionsTotalVolume("XAUUSD", 888888) 就能瞬间读出 888888 这个 magic 下黄金净买量,避免手动累加持仓。外汇与贵金属杠杆高、双向波动剧烈,读取到的数值仅反映当下风险暴露,不预示方向。
class="type">class="kw">double SymbolBuyPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(symbolBuyPositionsVolumeTotal); }
「抓出某品种空头持仓的总量」
想看账户里某个交易品种当前所有未平卖出仓位加起来的总手数,直接调这个函数就能拿到双精度数值,不用自己遍历持仓数组。 函数接收两个参数:品种名 symbol 和魔术码 magicNumber,后者用来区分不同 EA 或策略开的仓。内部先跑 GetPositionsData 把该品种、该魔术码下的持仓数据捞出来,再把全局变量 symbolSellPositionsVolumeTotal 返回给你。 在 MT5 里接这段逻辑后,你可以拿返回值和账户净值、保证金占用做个比值,外汇和贵金属杠杆高,裸空总手数过大时爆仓概率会明显抬升,建议实盘前先在策略测试器用历史数据验证输出是否符合预期。
class="type">class="kw">double SymbolSellPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(symbolSellPositionsVolumeTotal); }
按品种抓未平仓位总利润
做多品种组合盯盘时,常需要单独看某个标的在跑的仓位一共浮盈多少。下面这个函数直接回吐指定品种、指定魔数下所有未平仓位的总利润,类型是 double,精度截到两位小数。
class="type">class="kw">double SymbolPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(NormalizeDouble(symbolPositionsProfit, class="num">2)); }
class="type">class="kw">double SymbolPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(NormalizeDouble(symbolPositionsProfit, class="num">2)); }
◍ 抓出某品种买仓的浮动利润
想实时盯住某个交易品种在手的买入仓位总共赚了多少,直接调这个函数就能拿到双精度数值。它按交易品种和魔数过滤持仓,把未平仓买入仓位的浮动利润汇总后返回,外层用 NormalizeDouble 保留两位小数,避免浮点噪声干扰判断。 外汇与贵金属杠杆高、点值跳动快,这类汇总值只反映当下盘面,不代表落袋收益,实际平仓前利润可能随报价反向波动。 代码里先跑 GetPositionsData 刷新该 symbol 与 magicNumber 下的持仓缓存,再取 symbolBuyPositionsProfit 字段并规范到 2 位小数。开 MT5 把这段挂到 EA 里,传 EURUSD 和你的魔数,就能在日志里看到买仓总利润随 tick 更新。
class="type">class="kw">double SymbolBuyPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(NormalizeDouble(symbolBuyPositionsProfit, class="num">2)); }
「按品种与魔术码抓卖出浮盈」
在 MT5 多品种网格或对冲策略里,常需要实时知道某个标的下所有空单的未平盈利合计。上面这段导出函数就干这件事:传入品种名和 magicNumber,返回该范围内卖出仓位的浮动利润总和,类型为 double。 函数先调用 GetPositionsTotal 类逻辑(GetPositionsData 内部遍历持仓),把匹配 symbol 与 magic 的空单利润累加进 symbolSellPositionsProfit;最后用 NormalizeDouble 保留两位小数输出,避免浮点噪声干扰后续判断。 实战中可直接把这个函数挂进 EA 的 OnTick,当某贵金属空单总浮盈跌破 −50.00 时倾向减仓——外汇与贵金属杠杆高,此类数值仅作风控参考,不代表未来盈亏。
class="type">class="kw">double SymbolSellPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber) class="kw">export { GetPositionsData(symbol, magicNumber); class="kw">return(NormalizeDouble(symbolSellPositionsProfit, class="num">2)); }
一键导出账户持仓全景字符串
做多周期盯盘时,最怕临时切到日志去翻持仓汇总。这个导出函数用 bool 参数 formatForComment 切换输出形态:传 true 就塞进图表左上角 Comment() 显示,传 false 则走 Print() 落日志,省掉手动拼字符串的麻烦。 函数开头先跑 GetPositionsData(ALL_SYMBOLS, 0),把账户级买/卖仓位数、总成交量、浮盈等全局变量刷一遍,否则下面读到的 accountBuyPositionsTotal 之类可能是上一 tick 的脏值。 字符串里硬写了 79 字符宽的『|----』分隔线,日志里对齐好看;图表模式额外补了约 30 个空格的 spacer,避免被价格标签挡住。外汇与贵金属杠杆高,浮盈字段仅反映瞬时持仓盈亏,不代表平仓结果,实际风险以爆仓线为准。 复制下面代码到 MT5 的 include 里,EA 中调 AccountPositionsStatus(true) 就能在图表面板常驻看到『总开仓 / 总手数 / 总浮盈 + 多空分项』四项数据,比每次手动点终端窗口快得多。
class="type">class="kw">string AccountPositionsStatus(class="type">bool formatForComment) class="kw">export { GetPositionsData(ALL_SYMBOLS, class="num">0); class=class="str">"cmt">//-- Update the position 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 accountPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n"; accountPositionsStatus += spacer + "| " + (class="type">class="kw">string)AccountInfoInteger(ACCOUNT_LOGIN) + " - ACCOUNT POSTIONS STATUS \r\n"; accountPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n"; accountPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)PositionsTotal() + "\r\n"; accountPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountPositionsVolumeTotal + "\r\n"; accountPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(AccountInfoDouble(ACCOUNT_PROFIT), class="num">2)) + accountCurrency + "\r\n"; accountPositionsStatus += spacer + "|------------------------------------------------------------------\r\n"; accountPositionsStatus += spacer + "| BUY POSITIONS: \r\n"; accountPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)accountBuyPositionsTotal + "\r\n"; accountPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountBuyPositionsVolumeTotal + "\r\n"; accountPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(accountBuyPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; accountPositionsStatus += spacer + "|------------------------------------------------------------------\r\n"; accountPositionsStatus += spacer + "| SELL POSITIONS: \r\n"; accountPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)accountSellPositionsTotal + "\r\n"; accountPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)accountSellPositionsVolumeTotal + "\r\n"; accountPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(accountSellPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; accountPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n"; accountPositionsStatus += spacer + "\r\n"; class="kw">return(accountPositionsStatus); }
◍ 按幻数抓取仓位快照
做多 EA 混跑时,最怕日志里分不清哪笔仓位归哪个策略。这个导出函数用 magicNumber 把账户里同幻数的持仓拎出来,再按 formatForComment 决定是塞进日志还是贴到图表右上角。 函数先调 GetPositionsData(ALL_SYMBOLS, magicNumber) 刷新一组全局变量,再拼一个带分隔线的字符串:总开仓数、总手数、总浮盈,以及 BUY / SELL 各自的三项数据,利润用 NormalizeDouble(..., 2) 保留两位小数并接上账户币种后缀。 formatForComment 为 true 时,spacer 会垫进约 30 个空格,把整块文本往右推,避免被图表的蜡烛遮住;为 false 时 spacer 为空,输出更紧凑,适合直接 Print 到 EA 日志选项卡。 实盘里你可以把返回值交给 Comment() 或 Print() 验证:同一幻数下若 magicPositionsTotal 与终端“交易”标签里的过滤结果不一致,多半是 GetPositionsData 没覆盖到挂单转的持仓,需要回 MT5 查那一层刷新逻辑。外汇与贵金属杠杆高,仓位快照仅作监控,不构成方向判断。
class="type">class="kw">string MagicPositionsStatus(class="type">ulong magicNumber, class="type">bool formatForComment) class="kw">export { GetPositionsData(ALL_SYMBOLS, magicNumber); class=class="str">"cmt">//-- Update the position 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 magicPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n"; magicPositionsStatus += spacer + "| " + (class="type">class="kw">string)magicNumber + " - MAGIC POSTIONS STATUS \r\n"; magicPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n"; magicPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicPositionsTotal + "\r\n"; magicPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicPositionsVolumeTotal + "\r\n"; magicPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(magicPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; magicPositionsStatus += spacer + "|------------------------------------------------------------------\r\n"; magicPositionsStatus += spacer + "| BUY POSITIONS: \r\n"; magicPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicBuyPositionsTotal + "\r\n"; magicPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicBuyPositionsVolumeTotal + "\r\n"; magicPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(magicBuyPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; magicPositionsStatus += spacer + "|------------------------------------------------------------------\r\n"; magicPositionsStatus += spacer + "| SELL POSITIONS: \r\n"; magicPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)magicSellPositionsTotal + "\r\n"; magicPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)magicSellPositionsVolumeTotal + "\r\n"; magicPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(magicSellPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; magicPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n"; magicPositionsStatus += spacer + "\r\n"; class="kw">return(magicPositionsStatus); }
「按品种拉出持仓全景字符串」
做多品种盯盘时,最怕账户里某个符号的净敞口算不清。下面这个函数把指定品种的总持仓、多空分项的数量、手数和浮动盈亏,拼成一段可直接打印的文本,调用前只需传三个参:品种名、magic 值、以及是否按图表注释格式输出。 当 formatForComment 为 true,函数会在每行前面塞一段空白 spacer,让字符在图表的 Comment() 里对齐不串行;传 false 则面向 EA 日志选项卡,少占横向空间。无论哪种,盈亏都走 NormalizeDouble(…,2) 保留两位小数并拼上账户币种后缀。 实际用法很简单:想看 XAUUSD 当前 magic=888 的持仓,直接 SymbolPositionsStatus("XAUUSD",888,true) 丢进 Comment(),图表左上角就能实时看到「总开仓 / 总手数 / 总利润」以及 BUY、SELL 两个分块的同结构数据。外汇与贵金属杠杆高,浮亏可能在数根 K 线内扩大,这类状态字符串只作监控用途,不构成方向判断。 别把正态当圣经 上面代码依赖 GetPositionsData() 事先刷新那几个 symbol* 全局变量;若你没在调用前跑过它,拼出来的全是上一次(或零值)快照,监控就失真了。
class="type">class="kw">string SymbolPositionsStatus(class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment) class="kw">export { GetPositionsData(symbol, magicNumber); class=class="str">"cmt">//-- Update the position 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 symbolPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n"; symbolPositionsStatus += spacer + "| " + symbol + " - SYMBOL POSTIONS STATUS \r\n"; symbolPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n"; symbolPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolPositionsTotal + "\r\n"; symbolPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolPositionsVolumeTotal + "\r\n"; symbolPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(symbolPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; symbolPositionsStatus += spacer + "|------------------------------------------------------------------\r\n"; symbolPositionsStatus += spacer + "| BUY POSITIONS: \r\n"; symbolPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolBuyPositionsTotal + "\r\n"; symbolPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolBuyPositionsVolumeTotal + "\r\n"; symbolPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(symbolBuyPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; symbolPositionsStatus += spacer + "|------------------------------------------------------------------\r\n"; symbolPositionsStatus += spacer + "| SELL POSITIONS: \r\n"; symbolPositionsStatus += spacer + "| Total Open: " + (class="type">class="kw">string)symbolSellPositionsTotal + "\r\n"; symbolPositionsStatus += spacer + "| Total Volume: " + (class="type">class="kw">string)symbolSellPositionsVolumeTotal + "\r\n"; symbolPositionsStatus += spacer + "| Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(symbolSellPositionsProfit, class="num">2)) + accountCurrency + "\r\n"; symbolPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n"; symbolPositionsStatus += spacer + "\r\n"; class="kw">return(symbolPositionsStatus); }
把 PositionsManager 库接进你的 MQL5 工程
我们之前编译好的 PositionsManager.ex5 是一个二进制库,封装了仓位操作、状态读取和面板显示三类基础函数。要在自己的 EA 或脚本里调用它,第一步不是写逻辑,而是先让编译器知道去哪找这套接口。 在 MQL5 项目里导入不需要把源码摊开,只要把 PositionsManager.ex5 放进终端的 Libraries 目录,再在调用文件头部用 #import 指向它即可。这样编译时链接的是二进制符号,运行开销比贴源码小。 库里大致分三块:开平删改的仓位操作函数、实时回读持仓与浮盈的状态获取函数、以及把信息画到图表上的显示模块。拿到 ex5 后先扫一遍导出函数名,比直接读文档更快建立心智模型。外汇和贵金属杠杆高,任何自动仓位动作上线前都应在策略测试器用历史数据跑一遍边界情形。
◍ 把仓位管理塞进 EA 的两个步骤
做 MT5 自动化交易,最烦的就是反复手写仓位校验、开平仓和追踪止损逻辑。有个现成的 PositionsManager.ex5 库把这类活儿封装成了单函数调用,集成只需两步,不碰底层交易类也能跑。 第一步是把库文件放到 MQL5/Libraries/Toolkit 目录下,若本地没有就去下载一份放进去;第二步是在 EA 头文件里写导入指令,把需要的函数原型声明挂上。作者给了一个空白模板 PositionsManager_Imports_Template.mq5,用不上的函数声明直接注释掉就行。 挂上库之后,开仓只需 OpenBuyPosition(123, _Symbol, SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN), 500, 1000, "Buy position") 这类一行调用——magic 填 123、最小手数、止损 500 点、止盈 1000 点。ErrorAdvisor() 会在交易返回错误码时判断能否重试,返回 false 就说明是严重错误得停手。 追踪止损必须放在 OnTick() 里逐根分时刷,否则不会实时跟价;外汇和贵金属杠杆高,实盘前务必在策略测试器用历史数据验证这套调用在你的经纪商环境下不报交易服务器错。
class="type">bool ErrorAdvisor( class="type">class="kw">string callingFunc, class="type">class="kw">string symbol, class="type">int tradeServerErrorCode ); ResetLastError(); class=class="str">"cmt">//-- Reset and clear the last error class=class="str">"cmt">//-------------------------------------------------------------- class=class="str">"cmt">//-- Insert code to send the order request to the trade server class=class="str">"cmt">//-------------------------------------------------------------- class="type">class="kw">string symbol = _Symbol; class=class="str">"cmt">//Symbol being traded class="type">int retcode = tradeResult.retcode;class=class="str">"cmt">//Trade Request Structure(class="type">MqlTradeRequest) if(!ErrorAdvisor(__FUNCTION__, symbol, retcode) { class=class="str">"cmt">//Critical error found class=class="str">"cmt">//Order can not be executed. Exit function or log this error } class="type">bool TradingIsAllowed(); if(!TradingIsAllowed()) { class=class="str">"cmt">//--- algo trading is disabled, exit function class="kw">return(false); } class="type">bool OpenBuyPosition( class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment ); class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = class="num">500; class=class="str">"cmt">//-- pips class="type">int tp = class="num">1000; class=class="str">"cmt">//-- pips class="type">class="kw">string comment = "Buy position"; OpenBuyPosition(magicNo, symbol, lotSize, sl, tp, comment); class="type">bool OpenSellPosition( class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment ); class="type">ulong magicNo = class="num">123; class="type">class="kw">string symbol = _Symbol; class="type">class="kw">double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); class="type">int sl = class="num">500; class=class="str">"cmt">//-- pips class="type">int tp = class="num">1000; class=class="str">"cmt">//-- pips class="type">class="kw">string comment = "Sell position"; OpenSellPosition(magicNo, symbol, lotSize, sl, tp, comment); class="type">bool SetSlTpByTicket( class="type">ulong positionTicket, class="type">int sl, class="type">int tp ); class="type">int sl = class="num">500, class="type">int tp = class="num">1000; class=class="str">"cmt">//-- pips class="type">int totalOpenPostions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPostions; x++)
「按票号批量平仓与跟踪止损的实战封装」
在 MT5 EA 里逐个处理持仓,核心是先拿 ticket 再调函数。下面这段用 PositionGetTicket(x) 遍历,ticket 大于 0 才操作,避免空槽位报错。
class="type">ulong positionTicket = PositionGetTicket(x); if(positionTicket > class="num">0) { SetSlTpByTicket(positionTicket, sl, tp); }
for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class="type">ulong positionTicket = PositionGetTicket(x); if(positionTicket > class="num">0) { ClosePositionByTicket(positionTicket); } }
class="type">int trailingStopLoss = class="num">500; for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class="type">ulong positionTicket = PositionGetTicket(x); if(positionTicket > class="num">0) { SetTrailingStopLoss(positionTicket, trailingStopLoss); } }
class="type">ulong positionTicket = PositionGetTicket(x); if(positionTicket > class="num">0) { SetSlTpByTicket(positionTicket, sl, tp); } class="type">bool ClosePositionByTicket( class="type">ulong positionTicket ); class=class="str">"cmt">//-- Example to close all open positions for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class="type">ulong positionTicket = PositionGetTicket(x); if(positionTicket > class="num">0) { ClosePositionByTicket(positionTicket); } } class="type">bool SetTrailingStopLoss( class="type">ulong positionTicket, class="type">int trailingStopLoss ); class=class="str">"cmt">//-- Execute this on every tick class=class="str">"cmt">//-- Example to set class="num">500 pips trailing stop loss for all positions class="type">int trailingStopLoss = class="num">500; class=class="str">"cmt">//-- class="num">500 pips trailing stop loss for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class="type">ulong positionTicket = PositionGetTicket(x); if(positionTicket > class="num">0) { SetTrailingStopLoss(positionTicket, trailingStopLoss); } } class="type">bool CloseAllPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all positions CloseAllPositions("", class="num">0); class=class="str">"cmt">//Only close all positions matching a magic number value of class="num">1 CloseAllPositions("", class="num">1); class=class="str">"cmt">//Only close all current symbol positions CloseAllPositions(_Symbol, class="num">0); class="type">bool CloseAllPositions(); class=class="str">"cmt">//Close all open positions in the account CloseAllPositions(); class="type">bool CloseAllBuyPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all buy positions for the current symbol CloseAllBuyPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all buy positions matching magic number class="num">1 for all symbols CloseAllBuyPositions("", class="num">1); class=class="str">"cmt">//Close all buy positions in the account CloseAllBuyPositions("", class="num">0); class="type">bool CloseAllSellPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all sell positions for the current symbol CloseAllSellPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all sell positions matching magic number class="num">1 for all symbols CloseAllSellPositions("", class="num">1); class=class="str">"cmt">//Close all sell positions in the account CloseAllSellPositions("", class="num">0); class="type">bool CloseAllMagicPositions( class="type">ulong magicNumber ); class=class="str">"cmt">//Close all positions matching magic number class="num">1 CloseAllMagicPositions(class="num">1); class=class="str">"cmt">//Close all positions in the account CloseAllMagicPositions(class="num">0); class="type">bool CloseAllProfitablePositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all profitable positions for the current symbol CloseAllProfitablePositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all profitable positions matching magic number class="num">1 for all symbols CloseAllProfitablePositions("", class="num">1); class=class="str">"cmt">//Close all profitable positions in the account CloseAllProfitablePositions("", class="num">0); class="type">bool CloseAllProfitableBuyPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all profitable buy positions for the current symbol CloseAllProfitableBuyPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all profitable buy positions matching magic number class="num">1 for all symbols CloseAllProfitableBuyPositions("", class="num">1); class=class="str">"cmt">//Close all profitable buy positions in the account CloseAllProfitableBuyPositions("", class="num">0); class="type">bool CloseAllProfitableSellPositions(
按魔术码与品种筛仓的平仓与统计函数
在 EA 里批量处理持仓时,最麻烦的是按「品种 + 魔术码」双维度过滤。下面这组函数把平仓和统计拆得很细:盈利/亏损、买/卖、当前品种/全部品种,都能单独调用。 CloseAllProfitableSellPositions(_Symbol, 0) 只平当前品种盈利的卖单;传空字符串 "" 配魔术码 1,则平全市场该魔术码的盈利卖单;两个参数都取默认值("", 0)就是清掉账户里所有盈利卖单。亏损单同理有 CloseAllLossPositions 及其 Buy/Sell 细分版本,参数语义完全一致。 统计类不用真的去平仓,只回数或回算。BuyPositionsTotal() 返回账户买仓笔数,SellPositionsTotal() 返回卖仓笔数;PositionsTotalVolume() 给总手数,Buy/Sell 各有一个 Volume 版。利润端有 BuyPositionsProfit() 与 SellPositionsProfit() 直接吐出浮动盈亏合计。 若你的策略用魔术码隔离多套逻辑,Magic 前缀系列更实用:MagicPositionsTotal(1) 数出魔术码 1 的总持仓笔数,MagicBuyPositionsTotal(1) 只数买仓,MagicSellPositionsTotal(1) 只数卖仓;Volume 与 Profit 也各有 Magic 版,比如 MagicPositionsTotalVolume(1) 回该码总手数,MagicPositionsProfit(1) 回该码总浮盈。外汇与贵金属杠杆高,批量平仓前务必在策略测试器用历史数据跑一遍,确认过滤条件不会误杀其他策略的仓。
class="type">bool CloseAllProfitableSellPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all profitable sell positions for the current symbol CloseAllProfitableSellPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all profitable sell positions matching magic number class="num">1 for all symbols CloseAllProfitableSellPositions("", class="num">1); class=class="str">"cmt">//Close all profitable sell positions in the account CloseAllProfitableSellPositions("", class="num">0); class="type">bool CloseAllLossPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all loss positions for the current symbol CloseAllLossPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all loss positions matching magic number class="num">1 for all symbols CloseAllLossPositions("", class="num">1); class=class="str">"cmt">//Close all loss positions in the account CloseAllLossPositions("", class="num">0); class="type">bool CloseAllLossBuyPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all loss buy positions for the current symbol CloseAllLossBuyPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all loss buy positions matching magic number class="num">1 for all symbols CloseAllLossBuyPositions("", class="num">1); class=class="str">"cmt">//Close all loss buy positions in the account CloseAllLossBuyPositions("", class="num">0); class="type">bool CloseAllLossSellPositions( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Close all loss sell positions for the current symbol CloseAllLossSellPositions(_Symbol, class="num">0); class=class="str">"cmt">//Close all loss sell positions matching magic number class="num">1 for all symbols CloseAllLossSellPositions("", class="num">1); class=class="str">"cmt">//Close all loss sell positions in the account CloseAllLossSellPositions("", class="num">0); class="type">int BuyPositionsTotal(); class=class="str">"cmt">//Get the total number of open buy positions in the account BuyPositionsTotal(); class="type">int SellPositionsTotal(); class=class="str">"cmt">//Get the total number of open sell positions in the account SellPositionsTotal(); class="type">class="kw">double PositionsTotalVolume(); class=class="str">"cmt">//Get the total volume of all open positions in the account PositionsTotalVolume(); class="type">class="kw">double BuyPositionsTotalVolume(); class=class="str">"cmt">//Get the total volume of all open buy positions in the account BuyPositionsTotalVolume(); class="type">class="kw">double SellPositionsTotalVolume(); class=class="str">"cmt">//Get the total volume of all open sell positions in the account SellPositionsTotalVolume(); class="type">class="kw">double BuyPositionsProfit(); class=class="str">"cmt">//Get the total profit of all open buy positions in the account BuyPositionsProfit(); class="type">class="kw">double SellPositionsProfit(); class=class="str">"cmt">//Get the total profit of all open sell positions in the account SellPositionsProfit(); class="type">int MagicPositionsTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total number of open positions matching magic number class="num">1 MagicPositionsTotal(class="num">1); class="type">int MagicBuyPositionsTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total number of open buy positions matching magic number class="num">1 MagicBuyPositionsTotal(class="num">1); class="type">int MagicSellPositionsTotal( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total number of open sell positions matching magic number class="num">1 MagicSellPositionsTotal(class="num">1); class="type">class="kw">double MagicPositionsTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume of open positions matching magic number class="num">1 MagicPositionsTotalVolume(class="num">1); class="type">class="kw">double MagicBuyPositionsTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume of open buy positions matching magic number class="num">1 MagicBuyPositionsTotalVolume(class="num">1); class="type">class="kw">double MagicSellPositionsTotalVolume( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total volume of open sell positions matching magic number class="num">1 MagicSellPositionsTotalVolume(class="num">1); class="type">class="kw">double MagicPositionsProfit( class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total profit of open positions matching magic number class="num">1 MagicPositionsProfit(class="num">1); class="type">class="kw">double MagicBuyPositionsProfit(
◍ 按魔术码与品种拆仓位的查询函数
在 MT5 实盘里,同一 EA 往往只用固定 magic number 区分策略实例。下面这组函数把‘全部持仓’进一步切成 按魔术码、按品种、按多空方向 三个维度,返回数量、体积、浮盈三类数值,方便你在面板或日志里做分策略监控。 所有函数都接收 ulong magicNumber 参数,调用时传 1 即只统计魔术码为 1 的仓位。例如 MagicBuyPositionsProfit(1) 返回该魔术码下所有多单浮盈之和,SymbolSellPositionsTotalVolume(_Symbol, 1) 返回当前图表品种、魔术码 1 的空单总手数。 状态打印类函数多一个 bool formatForComment 开关:AccountPositionsStatus(true) 输出适合图表右上角 Comment() 的紧凑文本,传 false 则输出适合 Experts 日志的多行格式。MagicPositionsStatus(1, true) 与 SymbolPositionsStatus(_Symbol, 1, false) 同理,只是前者按魔术码过滤、后者再叠加品种过滤。 外汇与贵金属杠杆高、滑点跳空频繁,分魔术码统计浮盈仅用于风险敞口排查,不预示后续盈亏。复制下面声明到你的 .mq5 头文件,编译后就能在 EA 里直接调用验证。
class="type">ulong magicNumber ); class=class="str">"cmt">//Get the total profit of open buy positions matching magic number class="num">1 MagicBuyPositionsProfit(class="num">1); class="type">class="kw">double MagicSellPositionsProfit( class="type">ulong magicNumber ); class=class="str">"cmt">//Get total profit of sell positions matching magic number class="num">1 MagicSellPositionsProfit(class="num">1); class="type">int SymbolPositionsTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get total number of positions matching symbol and magic number class="num">1 MagicPositionsTotal(_Symbol, class="num">1); class="type">int SymbolBuyPositionsTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get total number of buy positions matching symbol and magic number class="num">1 SymbolBuyPositionsTotal(_Symbol, class="num">1); class="type">int SymbolSellPositionsTotal( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get total number of sell positions matching symbol and magic number class="num">1 SymbolSellPositionsTotal(_Symbol, class="num">1); class="type">class="kw">double SymbolPositionsTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the volume of positions matching symbol and magic number class="num">1 SymbolPositionsTotalVolume(_Symbol, class="num">1); class="type">class="kw">double SymbolBuyPositionsTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the volume of buy positions matching symbol and magic number class="num">1 SymbolBuyPositionsTotalVolume(_Symbol, class="num">1); class="type">class="kw">double SymbolSellPositionsTotalVolume( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the volume of sell positions matching symbol and magic number class="num">1 SymbolSellPositionsTotalVolume(_Symbol, class="num">1); class="type">class="kw">double SymbolPositionsProfit( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the profit of all positions matching symbol and magic number class="num">1 SymbolPositionsProfit(_Symbol, class="num">1); class="type">class="kw">double SymbolBuyPositionsProfit( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the profit of all buy positions matching symbol and magic number class="num">1 SymbolBuyPositionsProfit(_Symbol, class="num">1); class="type">class="kw">double SymbolSellPositionsProfit( class="type">class="kw">string symbol, class="type">ulong magicNumber ); class=class="str">"cmt">//Get the profit of all sell positions matching symbol and magic number class="num">1 SymbolSellPositionsProfit(_Symbol, class="num">1); class="type">class="kw">string AccountPositionsStatus( class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open positions formatted for the chart comments AccountPositionsStatus(true); class=class="str">"cmt">//Print the status of all open positions formatted for the Experts tab AccountPositionsStatus(false); class="type">class="kw">string MagicPositionsStatus( class="type">ulong magicNumber, class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open positions matching class=class="str">"cmt">//the magic number class="num">1 formatted for the chart comments MagicPositionsStatus(class="num">1, true); class=class="str">"cmt">//Print the status of all open positions matching class=class="str">"cmt">//the magic number class="num">1 formatted for the Experts tab MagicPositionsStatus(class="num">1, false); class="type">class="kw">string SymbolPositionsStatus( class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment ); class=class="str">"cmt">//Print the status of all open positions matching class=class="str">"cmt">//the symbol and magic number class="num">1 formatted for the chart comments SymbolPositionsStatus(_Symbol, class="num">1, true); class=class="str">"cmt">//Print the status of all open positions matching class=class="str">"cmt">//the symbol and magic number class="num">1 formatted for the Experts tab SymbolPositionsStatus(_Symbol, class="num">1, false);
「用 import 把持仓管理拆成独立模块」
在 MT5 里写 EA 时,把下单、改止损、批量平仓这些脏活塞进一个编译好的 ex5,主程序只用 #import 调函数,能少写一半重复代码。下面这段就是主文件开头怎么挂接 PositionsManager.ex5 的模板,路径写在 Toolkit/ 下。 #import "Toolkit/PositionsManager.ex5" 这一行告诉编译器去加载对应库;后面列出的全是函数原型声明,不写实现,只给返回类型和参数,链接期才绑定。 错误与权限先看清:ErrorAdvisor() 接收调用函数名、品种、服务器错误码,返回 bool;TradingIsAllowed() 判断当前是否允许交易。实盘里若返回 false,后面任何 Open 都不会成交,外汇和贵金属品种在重要数据发布时经常触发,属于高风险时段的硬拦截。 开仓与修饰类函数覆盖了市价多空、按 ticket 设止盈止损、移动止损、以及按品种/魔法码/盈亏状态批量平仓的十几种组合。比如 CloseAllProfitableBuyPositions(symbol, magic) 只清掉某品种下盈利的多单,回测时用 magic 区分策略实例就不会误杀。 状态监控分三层:全局、按 magic、按 symbol+magic。MagicBuyPositionsProfit(magic) 这种能直接拿出某魔法码下多单浮动盈亏,做多策略资金曲线切片很方便。 把下面原型直接拷进你的 mq5 头区,编译前确认 PositionsManager.ex5 已放在 Terminal/Common/Files 或对应 Toolkit 目录,否则 #import 会报 cannot find module。
class=class="str">"cmt">//| PositionsManager.ex5 imports class="kw">template | class=class="str">"cmt">//+-------------------------------------------------------------------------------------+ class="macro">#class="kw">import "Toolkit/PositionsManager.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">//-- Error Handling and Permission Status Functions class="type">bool ErrorAdvisor(class="type">class="kw">string callingFunc, class="type">class="kw">string symbol, class="type">int tradeServerErrorCode); class="type">bool TradingIsAllowed(); class=class="str">"cmt">//-- Position Execution and Modification Functions class="type">bool OpenBuyPosition(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment); class="type">bool OpenSellPosition(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment); class="type">bool SetSlTpByTicket(class="type">ulong positionTicket, class="type">int sl, class="type">int tp); class="type">bool ClosePositionByTicket(class="type">ulong positionTicket); class="type">bool SetTrailingStopLoss(class="type">ulong positionTicket, class="type">int trailingStopLoss); class="type">bool CloseAllPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllPositions(); class="type">bool CloseAllBuyPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllSellPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllMagicPositions(class="type">ulong magicNumber); class="type">bool CloseAllProfitablePositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllProfitableBuyPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllProfitableSellPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllLossPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllLossBuyPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllLossSellPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class=class="str">"cmt">//-- Position Status Monitoring Functions class="type">int BuyPositionsTotal(); class="type">int SellPositionsTotal(); class="type">class="kw">double PositionsTotalVolume(); class="type">class="kw">double BuyPositionsTotalVolume(); class="type">class="kw">double SellPositionsTotalVolume(); class="type">class="kw">double BuyPositionsProfit(); class="type">class="kw">double SellPositionsProfit(); class=class="str">"cmt">//-- Positions Filtered By Magic Number Status Monitoring Functions class="type">int MagicPositionsTotal(class="type">ulong magicNumber); class="type">int MagicBuyPositionsTotal(class="type">ulong magicNumber); class="type">int MagicSellPositionsTotal(class="type">ulong magicNumber); class="type">class="kw">double MagicPositionsTotalVolume(class="type">ulong magicNumber); class="type">class="kw">double MagicBuyPositionsTotalVolume(class="type">ulong magicNumber); class="type">class="kw">double MagicSellPositionsTotalVolume(class="type">ulong magicNumber); class="type">class="kw">double MagicPositionsProfit(class="type">ulong magicNumber); class="type">class="kw">double MagicBuyPositionsProfit(class="type">ulong magicNumber); class="type">class="kw">double MagicSellPositionsProfit(class="type">ulong magicNumber); class=class="str">"cmt">//-- Positions Filtered By Symbol and/or Magic Number Status Monitoring Functions class="type">int SymbolPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber);
按品种与魔术码拆持仓的接口清单
做多品种对冲或分策略管理时,光看账户总持仓不够,得能按 symbol 和 magicNumber 把单子拆开统计。下面这组函数就是干这个的:买/卖方向持仓笔数、对应体积、浮动盈亏都能单独拉出来。 比如 SymbolBuyPositionsTotal 返回某品种某魔术码的买单数量,SymbolSellPositionsProfit 只算卖单的累计浮盈。实盘里你挂了 EA_A 和 EA_B 两个魔术码,就能用这些接口分别看谁在赚、谁在扛。 末尾三个 Status 函数负责把统计结果拼成字符串,formatForComment 为真时可直接喂给 Comment() 在图表左上角显示,方便盯盘时一眼看清分策略仓位。外汇和贵金属杠杆高,这类明细统计能帮你更快发现某策略的暴露,但任何浮盈都是未平仓的可能数值,不代表落袋。
class="type">int SymbolBuyPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">int SymbolSellPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolBuyPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolSellPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolBuyPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolSellPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber); class=class="str">"cmt">//-- Log and Data Display Functions class="type">class="kw">string AccountPositionsStatus(class="type">bool formatForComment); class="type">class="kw">string MagicPositionsStatus(class="type">ulong magicNumber, class="type">bool formatForComment); class="type">class="kw">string SymbolPositionsStatus(class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment); class="macro">#class="kw">import class=class="str">"cmt">//--- Closing class="kw">import directive
◍ 双 VIDyA 线交叉配合仓位库做追踪止损 EA
用可变指数动态平均线(VIDyA)做信号,核心就是快慢两条线交叉。快线参数设得小、反应快,慢线参数大、反应钝;快线高于慢线倾向做多,快线低于慢线倾向做空。这条策略依赖一对不同设置的 VIDyA 实例,单条线无法自交叉。 EA 命名为 DualVidyaTrader.mq5,在 MetaEditor 新建后,先把 PositionsManager.ex5 的库函数原型导入到 #property 下方。我们不需要库里全部函数,只摘出开仓、trailing、查询仓位数等几个原型即可,减少无谓依赖。 全局变量里放 magicNo=1234、周期 PERIOD_H1、收盘价应用价,以及快线 cmoPeriod=5 / maPeriod=10、慢线 cmoPeriod=9 / maPeriod=12。这些输入直接决定交叉频率:快线周期 10 与慢线 12 只差 2,信号会比差 20 以上的组合更密。 逻辑拆成几个自定义函数:GetInit() 初始化指标句柄,GetDeinit() 释放资源,GetVidya() 每 tick 算信号,ScanForTradeOpportunities() 调库开仓,CheckAndSetTrailingSl() 扫所有未平仓调 SetTrailingStopLoss()。OnTick() 里按「初始化检测→信号→开仓→ trailing」顺序串起来,否则可能用到空句柄。 编译进 MT5 后,依赖选项卡会列出从 PositionsManager.ex5 导入的原型及完整文件路径;若路径错,专家或日志选项卡报库引用错误。外汇与贵金属杠杆高,这类 EA 实盘前请用策略测试器跑历史数据验证信号逻辑。
class=class="str">"cmt">//--- Import the PositionsManager EX5 Library class="macro">#class="kw">import "Toolkit/PositionsManager.ex5" class=class="str">"cmt">//-- Open the ex5 class="kw">import directive class=class="str">"cmt">//-- Prototype function descriptions of the EX5 PositionsManager library class="type">bool OpenBuyPosition(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment); class="type">bool OpenSellPosition(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment); class="type">bool SetTrailingStopLoss(class="type">ulong positionTicket, class="type">int trailingStopLoss); class="type">int MagicPositionsTotal(class="type">ulong magicNumber); class="type">int MagicBuyPositionsTotal(class="type">ulong magicNumber); class="type">int MagicSellPositionsTotal(class="type">ulong magicNumber); class="type">bool CloseAllProfitableBuyPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllProfitableSellPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">string MagicPositionsStatus(class="type">ulong magicNumber, class="type">bool formatForComment); class="macro">#class="kw">import class=class="str">"cmt">//-- Close the ex5 class="kw">import directive input group "" input class="type">ulong magicNo = class="num">1234; input ENUM_TIMEFRAMES timeframe = PERIOD_H1; input ENUM_APPLIED_PRICE appliedPrice = PRICE_CLOSE; class=class="str">"cmt">// Applied VIDyA Price class=class="str">"cmt">//-- Fast Vidya user inputs input group "-- FAST VIDyA INPUTS" input class="type">int fast_cmoPeriod = class="num">5; class=class="str">"cmt">// Fast Chande Momentum Period input class="type">int fast_maPeriod = class="num">10; class=class="str">"cmt">// Fast MA Smoothing Period input class="type">int fast_emaShift = class="num">0; class=class="str">"cmt">// Fast Horizontal Shift class=class="str">"cmt">//-- Slow Vidya user inputs input group "-- SLOW VIDyA INPUTS" input class="type">int slow_cmoPeriod = class="num">9; class=class="str">"cmt">// Slow Chande Momentum Period input class="type">int slow_maPeriod = class="num">12; class=class="str">"cmt">// Slow MA Smoothing Period input class="type">int slow_emaShift = class="num">0; class=class="str">"cmt">// Slow Horizontal Shift input group "-- PROFIT MANAGEMENT"
「用点差倍数动态定止损与VIDyA双线句柄」
这段初始化逻辑把风控和指标句柄绑死在品种点差上,而不是写死点数。SL、 trailingSL、TP 分别乘了 1000、300、1000 倍点差,意味着若点差为 15(即 1.5 点),默认止损约 1500 点、追踪止损约 450 点、止盈约 1500 点——外汇与贵金属杠杆高,这种倍数在极端点差扩张时可能瞬间放大风险敞口。 开仓手数直接取 SYMBOL_VOLUME_MIN,小账户跑起来不会因手数过大爆仓,但也意味着单笔盈亏绝对值偏小,需靠信号频率补收益。buyOk / sellOk 置 true 是为了防止同一次交叉信号重复开仓,属于最朴素的信号锁。 fastVidya 与 slowVidya 两个 iVIDyA 句柄若创建失败直接 return(-1) 终止 EA,实盘前务必确认自定义指标已编译进 MT5 终端,否则策略根本起不来。下面把核心声明与初始化拆开看。
input class="type">bool liquidateProfitOnCrossover = false; class=class="str">"cmt">// Liquidate Profit On VIDyA Signal input class="type">bool enableTrailingStops = true; class=class="str">"cmt">// Use Trailing Stop Losses class=class="str">"cmt">//-- Get and save the SL, trailingSL and TP values from the spread class="type">int spreadMultiForSl = class="num">1000; class="type">int spreadMultiForTrailingSl = class="num">300; class="type">int spreadMultiForTp = class="num">1000; class="type">int sl = class="type">int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForSl; class="type">int trailingSl = class="type">int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForTrailingSl; class="type">int tp = class="type">int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForTp; class=class="str">"cmt">//-- Set the lot or volume to the symbol allowed min value class="type">class="kw">double lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); class=class="str">"cmt">//-- Vidya indicator variables class="type">class="kw">double fastVidya[], slowVidya[]; class="type">int fastVidyaHandle, slowVidyaHandle; class="type">bool buyOk, sellOk, vidyaBuy, vidyaSell; class="type">class="kw">string vidyaTrend; class="type">int GetInit() { class="type">int returnVal = class="num">1; class=class="str">"cmt">//-- Helps to regulate and prevent openning multiple trades on a single signal trigger buyOk = true; sellOk = true; class=class="str">"cmt">//-- Create the fast iVIDyA indicator handle fastVidyaHandle = iVIDyA(_Symbol, timeframe, fast_cmoPeriod, fast_maPeriod, fast_emaShift, appliedPrice); if(fastVidyaHandle < class="num">0) { Print("Error creating fastVidyaHandle = ", INVALID_HANDLE); Print("Handle creation: Runtime error = ", GetLastError()); class=class="str">"cmt">//-- Close the EA if the handle is not properly loaded class="kw">return(-class="num">1); } ArraySetAsSeries(fastVidya, true); class=class="str">"cmt">//-- set the vidya array to series access class=class="str">"cmt">//-- Create the slow iVIDyA indicator handle slowVidyaHandle = iVIDyA(_Symbol, timeframe, slow_cmoPeriod, slow_maPeriod, slow_emaShift, appliedPrice); if(slowVidyaHandle < class="num">0) { Print("Error creating vidyaHandle = ", INVALID_HANDLE); Print("Handle creation: Runtime error = ", GetLastError()); class=class="str">"cmt">//-- Close the EA if the handle is not properly loaded class="kw">return(-class="num">1); } ArraySetAsSeries(slowVidya, true); class=class="str">"cmt">//-- set the vidya array to series access class="kw">return(returnVal); } class="type">void GetDeinit() class=class="str">"cmt">//-- De-initialize the robot on shutdown and clean everything up { class=class="str">"cmt">//-- Delete the vidya handles and de-allocate the memory spaces occupied IndicatorRelease(fastVidyaHandle); ArrayFree(fastVidya); IndicatorRelease(slowVidyaHandle); ArrayFree(slowVidya); class=class="str">"cmt">//-- Delete and clear all chart displayed messages Comment(""); } class="type">void GetVidya() { class=class="str">"cmt">//-- Get vidya line directions
VIDyA 交叉信号的抓取与下单闸门
双速 VIDyA 的缓冲拷贝是信号有效性的第一道关卡。若快线或慢线任意一条在取最近 100 根柱的 CopyBuffer 返回值 ≤0,函数直接 return,后续所有状态变量保持上一轮残留——这意味着行情数据异常时 EA 不会乱开仓,但也不会主动报警。 信号判定只看索引 1 的柱(即已完成的那一根):fastVidya[1] > slowVidya[1] 置 BUY/LONG,反之置 SELL/SHORT,同时把对立方向标志清掉。注意这里没有用当前柱 [0],规避了未收盘柱的抖动假交叉。 ScanForTradeOpportunities 里用 MagicPositionsTotal(magicNo)==0 来重置 buyOk/sellOk,等于「本魔法号无持仓才允许双向开仓」。一旦某方向成交,立刻锁死同方向、放开反方向,形成单魔法号下的互斥开仓节奏。 若 liquidateProfitOnCrossover 开启,金叉后会扫平同品种盈利空单,死叉则平盈利多单。外汇与贵金属杠杆高,这种跨方向清算在剧烈反转中可能吃掉浮盈,建议在 MT5 策略测试器里用 2023 年 XAUUSD 的 M15 数据先验证一次再上实盘。
if(CopyBuffer(fastVidyaHandle, class="num">0, class="num">0, class="num">100, fastVidya) <= class="num">0 || CopyBuffer(slowVidyaHandle, class="num">0, class="num">0, class="num">100, slowVidya) <= class="num">0) { class="kw">return; } class=class="str">"cmt">//-- Reset vidya status variables vidyaBuy = false; vidyaSell = false; vidyaTrend = "FLAT"; class=class="str">"cmt">//-- Scan for vidya crossover buy signal if(fastVidya[class="num">1] > slowVidya[class="num">1]) { class=class="str">"cmt">//-- Save the vidya signal vidyaTrend = "BUY/LONG"; vidyaBuy = true; vidyaSell = false; } class=class="str">"cmt">//-- Scan for vidya crossover sell signal if(fastVidya[class="num">1] < slowVidya[class="num">1]) { class=class="str">"cmt">//-- Save the vidya signal vidyaTrend = "SELL/SHORT"; vidyaSell = true; vidyaBuy = false; } } class="type">void ScanForTradeOpportunities() { class=class="str">"cmt">//-- Get the VIDyA signal GetVidya(); if(MagicPositionsTotal(magicNo) == class="num">0) { buyOk = true; sellOk = true; } class=class="str">"cmt">//-- Check for a buy entry when a VIDyA buy signal is found if(buyOk && vidyaBuy) class=class="str">"cmt">//-- Open a new buy position { if(OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Vidya_BUY: " + IntegerToString(MagicBuyPositionsTotal(magicNo) + class="num">1))) { buyOk = false; sellOk = true; } class=class="str">"cmt">//-- Market has a strong buy trend, close all profitable sell positions if(liquidateProfitOnCrossover) { CloseAllProfitableSellPositions(_Symbol, magicNo); } } class=class="str">"cmt">//-- Check for a sell entry when a VIDyA sell signal is found if(sellOk && vidyaSell) class=class="str">"cmt">//-- Open a new sell position { if(OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Vidya_SELL: " + IntegerToString(MagicSellPositionsTotal(magicNo) + class="num">1))) { sellOk = false; buyOk = true; } class=class="str">"cmt">//-- Market has a strong sell trend, close all profitable buy positions if(liquidateProfitOnCrossover) { CloseAllProfitableBuyPositions(_Symbol, magicNo); } } } class="type">void CheckAndSetTrailingSl() { class="type">int totalOpenPostions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL);
◍ 用魔数隔离与反向过滤管住移动止损
EA 在遍历持仓时先抓两个字段:持仓魔数和持仓类型。前者用 PositionGetInteger(POSITION_MAGIC) 拿,后者强转成 int 读 POSITION_TYPE。只有本 EA 的 magicNo 且交易品种匹配才会继续处理,别的程序开的单子直接 continue 跳过,避免误改别人家的止损。 移动止损不是无条件挂。代码里写了:买仓且 vidyaBuy 为真、或卖仓且 vidyaSell 为真时直接 continue。也就是说,只在 VIDYA 趋势和持仓方向相反时才肯推止损,顺势时放它跑。这一句过滤掉了很多无谓的过早保本。 真正改止损靠 SetTrailingStopLoss(positionTicket, trailingSl),函数从编译好的 ex5 库里导入,主程序只传 ticket 和距离。OnTick 里先 ScanForTradeOpportunities 找信号开仓,enableTrailingStops 开关打开才跑 CheckAndSetTrailingSl,最后用 Comment 把 vidyaTrend 和本魔数持仓状态打印在图上。 初始化 OnInit 调 GetInit,返回值 ≤0 就 INIT_FAILED,否则 INIT_SUCCEEDED;OnDeinit 只调 GetDeinit 做清理。整套结构在 MT5 里加载后,把 magicNo 设成独有数字,就能和图表上其他 EA 的持仓彻底隔离。外汇与贵金属杠杆高,移动止损只降风险不保盈利,回测前先想清楚滑点假设。
class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class="type">int positionType = class="type">int(PositionGetInteger(POSITION_TYPE)); class=class="str">"cmt">//-- modify only the positions we have opened with this EA(magic number) if(selectedSymbol != _Symbol && positionMagicNo != magicNo) { class="kw">continue; } class=class="str">"cmt">//-- Only set the trailing stop loss when the market trend is in the opposing direction of the position type if((positionType == POSITION_TYPE_BUY && vidyaBuy) || (positionType == POSITION_TYPE_SELL && vidyaSell)) { class="kw">continue; } class=class="str">"cmt">//--- set the trailing stop loss SetTrailingStopLoss(positionTicket, trailingSl); class=class="str">"cmt">//-- call the imported function from our ex5 library } } class="type">void OnTick() { class=class="str">"cmt">//-- Scan and open new positions based on the vidya signal ScanForTradeOpportunities(); class=class="str">"cmt">//-- Check and set the trailing stop if(enableTrailingStops) { CheckAndSetTrailingSl(); } class=class="str">"cmt">//-- Display the vidya trend and positions status for the EA&class="macro">#x27;s magicNo Comment( "\nvidyaTrend: ", vidyaTrend, MagicPositionsStatus(magicNo, true) ); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- if(GetInit() <= class="num">0) { class="kw">return(INIT_FAILED); } class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- GetDeinit(); }
「用 PositionsManager 库搭一个可点击的仓位面板」
在 MT5 里做交易面板,不一定非得从零写订单逻辑。借一个编译好的 PositionsManager.ex5 库,新开一个 EA 命名为 PositionsManagerPanel.mq5,在 #property 段下方用 #import 把库挂进来,只挑需要的函数原型声明即可,用户输入参数留一个幻数(magicNumber)就够了。 面板本身靠几个自定义函数拼出来:CreateRectangleLabel() 画背景矩形标签,CreateLabel() 写文字,CreateButton() 做可点击按钮;LoadChartObjects() 在初始化时把这些对象一次性加载到图表,DeleteChartObjects() 放在 OnDeinit() 里做拆除。ModifySlTp() 绑定 setSLTP 按钮,改掉同幻数仓位的 sl/tp;SetTrailingSl() 在按钮点击和每个 OnTick() 分时里跑追踪止损;ClosePositionWithTicket() 对应 ClosePositions 按钮清仓。 所有按钮响应收敛在 OnChartEvent() 里,它负责判断哪个控件被按了,再调库里的对应函数。OnInit() 调 LoadChartObjects(),OnTick() 跑追踪与刷新,OnDeinit() 清对象——这三处接好后 EA 就能跑。 导入段只声明了买/卖开仓、按 ticket 改 SLTP、平仓、追踪止损、按魔术数统计买卖仓位与浮盈等函数,并未包含具体下单算法。编译完在图表加载后,从「依赖关系」选项卡能看到库完整路径与函数原型;开几个仓位点按钮,去工具箱「专家」日志看库打出的执行记录,就能验证面板是否真的驱动了底层库。外汇与贵金属杠杆高,面板只是执行壳,仓位风险仍由你自己控。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EX5 PositionsManager imports | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">import "Toolkit/PositionsManager.ex5" class=class="str">"cmt">//-- Open class="kw">import directive class=class="str">"cmt">//-- Function descriptions for the imported function prototypes class=class="str">"cmt">//--Position Execution and Modification Functions class="type">bool OpenBuyPosition(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment); class="type">bool OpenSellPosition(class="type">ulong magicNumber, class="type">class="kw">string symbol, class="type">class="kw">double lotSize, class="type">int sl, class="type">int tp, class="type">class="kw">string positionComment); class="type">bool SetSlTpByTicket(class="type">ulong positionTicket, class="type">int sl, class="type">int tp); class="type">bool ClosePositionByTicket(class="type">ulong positionTicket); class="type">bool SetTrailingStopLoss(class="type">ulong positionTicket, class="type">int trailingStopLoss); class="type">bool CloseAllPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllBuyPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllSellPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllMagicPositions(class="type">ulong magicNumber); class="type">bool CloseAllProfitablePositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">bool CloseAllLossPositions(class="type">class="kw">string symbol, class="type">ulong magicNumber); class=class="str">"cmt">//--Position Status Monitoring Functions class="type">int BuyPositionsTotal(); class="type">int SellPositionsTotal(); class="type">class="kw">double PositionsTotalVolume(); class="type">class="kw">double BuyPositionsTotalVolume(); class="type">class="kw">double SellPositionsTotalVolume(); class="type">class="kw">double BuyPositionsProfit(); class="type">class="kw">double SellPositionsProfit(); class="type">int MagicPositionsTotal(class="type">ulong magicNumber); class="type">int MagicBuyPositionsTotal(class="type">ulong magicNumber); class="type">int MagicSellPositionsTotal(class="type">ulong magicNumber); class="type">class="kw">double MagicPositionsTotalVolume(class="type">ulong magicNumber); class="type">class="kw">double MagicBuyPositionsTotalVolume(class="type">ulong magicNumber); class="type">class="kw">double MagicSellPositionsTotalVolume(class="type">ulong magicNumber); class="type">class="kw">double MagicPositionsProfit(class="type">ulong magicNumber); class="type">class="kw">double MagicBuyPositionsProfit(class="type">ulong magicNumber);
按魔术码与品种拆解持仓统计
EA 里要区分不同策略的仓位,最直接的方式是用 magic number 做过滤。下面这组函数把账户级统计拆到「全部 / 多单 / 空单」三个维度,返回值覆盖笔数、总手数和浮动盈亏,调用时传入 symbol 与 magicNumber 即可拿到对应子集。 MagicSellPositionsProfit 只回吐空单利润;SymbolBuyPositionsTotalVolume 之类则专注某一品种的多头敞口。实盘里若同时跑黄金与欧美两套策略,magicNo 设不同值就能互不干扰地读取各自持仓状态。 AccountPositionsStatus、MagicPositionsStatus 都带 formatForComment 参数,置 true 时输出字符串可直接塞进订单注释或日志,方便复盘时一眼看清当前账户结构。外汇与贵金属杠杆高,持仓统计只是风控中间层,不代表任何方向判断。 示例里用最小手数 volumeLot 配合点差倍数模拟止损止盈:sl 取点差乘 50、tp 取点差乘 100。若当前品种点差为 12 点,则 sl=600 点、tp=1200 点,这种写法在多点差切换的跨品种回测中能省去手动调参。 图表端用 CreateRectangleLabel 画左上角信息框,先 ObjectFind 查重,若已存在且不是矩形标签就删掉重建,再把锚点设到 CORNER_LEFT_UPPER、X 偏移 240、Y 偏移 2,避免挡住 K 线。
class="type">class="kw">double MagicSellPositionsProfit(class="type">ulong magicNumber); class="type">int SymbolPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">int SymbolBuyPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">int SymbolSellPositionsTotal(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolBuyPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolSellPositionsTotalVolume(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolBuyPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">double SymbolSellPositionsProfit(class="type">class="kw">string symbol, class="type">ulong magicNumber); class="type">class="kw">string AccountPositionsStatus(class="type">bool formatForComment); class="type">class="kw">string MagicPositionsStatus(class="type">ulong magicNumber, class="type">bool formatForComment); class="type">class="kw">string SymbolPositionsStatus(class="type">class="kw">string symbol, class="type">ulong magicNumber, class="type">bool formatForComment); class="macro">#class="kw">import class=class="str">"cmt">//--- Close class="kw">import directive class=class="str">"cmt">//--User input variables input class="type">ulong magicNo = class="num">101010; class=class="str">"cmt">//-- Global variables class=class="str">"cmt">//----------------------- class=class="str">"cmt">//-- Get the current symbol spread and multiply it by a significant number class=class="str">"cmt">//-- to simulate user-input SL and TP values class="type">class="kw">double volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); class="type">int sl = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * class="num">50; class="type">int tp = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * class="num">100; class=class="str">"cmt">//+-----------------------------------------------------------------------+ class=class="str">"cmt">//| CreateRectangleLabel(): Creates a rectangle label on the chart window | class=class="str">"cmt">//+-----------------------------------------------------------------------+ class="type">void CreateRectangleLabel() { class=class="str">"cmt">//--- Detect if we have an object named the same as our rectangle label if(ObjectFind(class="num">0, "mainRectangleLabel") >= class="num">0) { class=class="str">"cmt">//--- Delete the specified object if it is not a rectangle label if(ObjectGetInteger(class="num">0, "mainRectangleLabel", OBJPROP_TYPE) != OBJ_RECTANGLE_LABEL) { ObjectDelete(class="num">0, "mainRectangleLabel"); } } else { class=class="str">"cmt">//-- Create the mainRectangleLabel ObjectCreate(class="num">0, "mainRectangleLabel", OBJ_RECTANGLE_LABEL, class="num">0, class="num">0, class="num">0); } class=class="str">"cmt">//--- Set up the new rectangle label properties ObjectSetInteger(class="num">0, "mainRectangleLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, "mainRectangleLabel", OBJPROP_XDISTANCE, class="num">240); ObjectSetInteger(class="num">0, "mainRectangleLabel", OBJPROP_YDISTANCE, class="num">2);
◍ 用代码把图表标签钉在左上角
想在 MT5 图表上叠一个不被鼠标误选的信息框,核心是先判重再建对象。下面这段 CreateLabel 的逻辑就是:用 ObjectFind 查同名对象,若存在且不是 OBJ_LABEL 类型就先 ObjectDelete 清掉,否则直接 ObjectCreate 一个 OBJ_LABEL 挂到 0 号子窗口。 属性设置里最容易被忽略的是 OBJPROP_HIDDEN 设成 true——这样对象不会出现在终端的「对象列表」里,界面干净但也意味着你只能靠代码再删。OBJPROP_SELECTABLE 和 OBJPROP_SELECTED 都置 false,标签就彻底变成只读浮层,不会被拖拽移位。 [CODE] 逐行拆解: ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // 锚定左上角 ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, xDistance); // 距左边界像素 ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yDistance); // 距上边界像素 ObjectSetInteger(0, labelName, OBJPROP_XSIZE, xSize); // 框宽 ObjectSetInteger(0, labelName, OBJPROP_YSIZE, ySize); // 框高 ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); // 显示文本 ObjectSetInteger(0, labelName, OBJPROP_COLOR, textColor); // 文字色 ObjectSetString(0, labelName, OBJPROP_FONT, fontType); // 字体名 ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, fontSize); // 字号 实测把 xDistance 设 10、yDistance 设 20、xSize 460、ySize 520 能在 1920 宽屏上留出右侧 K 线空间。外汇与贵金属杠杆高,这类浮标只辅助读图,信号本身不代表方向倾向。
class="type">void CreateLabel( class="type">class="kw">string labelName, class="type">int xDistance, class="type">int yDistance, class="type">int xSize, class="type">int ySize, class="type">class="kw">string labelText, class="type">color textColor, class="type">class="kw">string fontType, class="type">int fontSize ) { if(ObjectFind(class="num">0, labelName) >= class="num">0) { if(ObjectGetInteger(class="num">0, labelName, OBJPROP_TYPE) != OBJ_LABEL) { ObjectDelete(class="num">0, labelName); } } else { ObjectCreate(class="num">0, labelName, OBJ_LABEL, class="num">0, class="num">0, class="num">0); } ObjectSetInteger(class="num">0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, labelName, OBJPROP_XDISTANCE, xDistance); ObjectSetInteger(class="num">0, labelName, OBJPROP_YDISTANCE, yDistance); ObjectSetInteger(class="num">0, labelName, OBJPROP_XSIZE, xSize); ObjectSetInteger(class="num">0, labelName, OBJPROP_YSIZE, ySize); ObjectSetString(class="num">0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(class="num">0, labelName, OBJPROP_COLOR, textColor); ObjectSetString(class="num">0, labelName, OBJPROP_FONT, fontType); ObjectSetInteger(class="num">0, labelName, OBJPROP_FONTSIZE, fontSize); ObjectSetInteger(class="num">0, labelName, OBJPROP_BACK, false); ObjectSetInteger(class="num">0, labelName, OBJPROP_HIDDEN, true); ObjectSetInteger(class="num">0, labelName, OBJPROP_SELECTABLE, false); ObjectSetInteger(class="num">0, labelName, OBJPROP_SELECTED, false); }
「给交易面板造按钮和标签」
在 MT5 里用 OBJ_BUTTON 和 OBJ_LABEL 搭一个简易交易面板,核心是先查重再建对象,避免重复运行脚本时图表上堆出一堆同名控件。下面这段 CreateButton 的写法就处理了这个问题:若图表已存在同名对象且不是按钮,先删掉,否则直接复用。 按钮坐标用 OBJPROP_CORNER 配合 XDISTANCE / YDISTANCE 定位,左上角基准下,示例中 BUY 键放在 x=250、y=50 像素处,宽 215、高 35,肉眼刚好不挡 K 线主体。字体与配色走 ObjectSetString / ObjectSetInteger,Calibri 10 号做标题、Consolas 11 号写品种与魔术码,辨识度比默认更清楚。 LoadChartObjects 里连续调 CreateLabel 两次,分别落在 y=10 与 y=30,说明标签纵向间距 20 像素就能分层不糊;headingLabel2 把 _Symbol 与 magicNo 拼进文本,实盘里换品种或改魔术码后面板会自动跟着变,不用手动改代码。外汇与贵金属波动剧烈,这类一键发单面板只降低操作延迟,不降低方向误判风险,参数验证请在策略测试器先跑。
class="type">class="kw">string btnName, class="type">int xDistance, class="type">int yDistance, class="type">int xSize, class="type">int ySize, class="type">class="kw">string btnText, class="type">class="kw">string tooltip, class="type">color textColor, class="type">class="kw">string fontType, class="type">int fontSize, class="type">color bgColor ) { class=class="str">"cmt">//--- Detect if we have an object named the same as our button if(ObjectFind(class="num">0, btnName) >= class="num">0) { class=class="str">"cmt">//--- Delete the specified object if it is not a button if(ObjectGetInteger(class="num">0, btnName, OBJPROP_TYPE) != OBJ_BUTTON) { ObjectDelete(class="num">0, btnName); } } else { class=class="str">"cmt">//-- Create the button ObjectCreate(class="num">0, btnName, OBJ_BUTTON, class="num">0, class="num">0, class="num">0); } class=class="str">"cmt">//--- Set up the new button properties ObjectSetInteger(class="num">0, btnName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, btnName, OBJPROP_XDISTANCE, xDistance); ObjectSetInteger(class="num">0, btnName, OBJPROP_YDISTANCE, yDistance); ObjectSetInteger(class="num">0, btnName, OBJPROP_XSIZE, xSize); ObjectSetInteger(class="num">0, btnName, OBJPROP_YSIZE, ySize); ObjectSetString(class="num">0, btnName, OBJPROP_TEXT, btnText); ObjectSetString(class="num">0, btnName, OBJPROP_TOOLTIP, tooltip); ObjectSetInteger(class="num">0, btnName, OBJPROP_COLOR, textColor); ObjectSetString(class="num">0, btnName, OBJPROP_FONT, fontType); ObjectSetInteger(class="num">0, btnName, OBJPROP_FONTSIZE, fontSize); ObjectSetInteger(class="num">0, btnName, OBJPROP_BGCOLOR, bgColor); } class=class="str">"cmt">//+--------------------------------------------------------------------+ class=class="str">"cmt">//| LoadChartObjects(): Create and load the buttons and chart objects | class=class="str">"cmt">//| for demonstrating how the imported library functions work | class=class="str">"cmt">//+--------------------------------------------------------------------+ class="type">void LoadChartObjects() { class=class="str">"cmt">//-- Create the rectangle label first CreateRectangleLabel(); class=class="str">"cmt">//-- Create the heading label CreateLabel( "headingLabel", class="num">250, class="num">10, class="num">440, class="num">60, "PositionsManager ex5 Library Demo Trade Panel", clrMidnightBlue, "Calibri", class="num">10 ); class=class="str">"cmt">//-- Create the second heading label CreateLabel( "headingLabel2", class="num">250, class="num">30, class="num">440, class="num">60, ("Trading " + _Symbol + " with Magic Number: " + (class="type">class="kw">string)magicNo), clrBlack, "Consolas", class="num">11 ); class=class="str">"cmt">//-- "BUY": Button to call the imported ex5 OpenBuyPosition() function CreateButton( "OpenBuyPositionBtn", class="num">250, class="num">50, class="num">215, class="num">35, "BUY",
把交易动作铺成面板按钮
在 MT5 的 EA 或指标里批量生成控制按钮时,可以用同一个 CreateButton() 封装函数把外部 ex5 的导出函数映射到界面。下面这段连续调用展示了 11 个按钮的布局:X 坐标只有 250 和 475 两列,Y 从 50 开始每 40 像素排一行,到 210 行时最后一个按钮宽度拉到 440 横跨两列。 每个按钮的视觉参数保持一致:背景色 clrMintCream、字体 Arial Black、字号 10,区别主要靠文字标签和文字色来提示功能危险度。比如 BUY 用 clrDodgerBlue、SELL 用 clrCrimson,平仓类用 clrMaroon / clrBrown,这种配色逻辑能让交易者在贵金属高波动时段少点错。 注意第 11 个 CloseAllMagicPositions 按钮的文字色写成了 C'203,18,55' 而不是宏常量,说明 MT5 允许直接用 RGB 构造颜色。外汇与贵金属交易杠杆高、滑点风险大,这类一键全平按钮务必在策略测试器里先用模拟盘验证触发逻辑。
CreateButton( "OpenBuyPositionBtn", class="num">250, class="num">50, class="num">215, class="num">35, "BUY", "OpenBuyPosition() Function", clrMintCream, "Arial Black", class="num">10, clrDodgerBlue ); class=class="str">"cmt">//-- "SELL": Button to call the imported ex5 OpenSellPosition() function CreateButton( "OpenSellPositionBtn", class="num">475, class="num">50, class="num">215, class="num">35, "SELL", "OpenSellPosition() Function", clrMintCream, "Arial Black", class="num">10, clrCrimson ); class=class="str">"cmt">//-- "SetSlTpByTicket": Button to call the imported ex5 SetSlTpByTicket() function CreateButton( "SetSlTpBtn", class="num">250, class="num">90, class="num">215, class="num">35, "SetSlTpByTicket", "SetSlTpByTicket() Function", clrMintCream, "Arial Black", class="num">10, clrDarkSlateGray ); class=class="str">"cmt">//-- "SetTrailingStopLoss": Button to call the imported ex5 SetTrailingStopLoss() function when clicked CreateButton( "SetTrailingStopLossBtn", class="num">475, class="num">90, class="num">215, class="num">35, "SetTrailingStopLoss", "SetTrailingStopLoss Function", clrMintCream, "Arial Black", class="num">10, clrDarkSlateGray ); class=class="str">"cmt">//-- "ClosePositionsByTicket": Button to call the imported ex5 ClosePositionByTicket() function CreateButton( "ClosePositionsBtn", class="num">250, class="num">130, class="num">215, class="num">35, "ClosePositionsByTicket", "ClosePositionByTicket() Function", clrMintCream, "Arial Black", class="num">10, clrMaroon ); class=class="str">"cmt">//-- "CloseAllSymbolPositions": Button to call the imported ex5 CloseAllSymbolPositions() function CreateButton( "CloseAllPositionsBtn", class="num">475, class="num">130, class="num">215, class="num">35, "CloseAllPositions", "CloseAllPositions() Function", clrMintCream, "Arial Black", class="num">10, clrMaroon ); class=class="str">"cmt">//-- "CloseAllBuySymbolPositions": Button to call the imported ex5 CloseAllBuySymbolPositions() function CreateButton( "CloseAllBuyPositionsBtn", class="num">250, class="num">170, class="num">215, class="num">35, "CloseAllBuyPositions", "CloseAllBuyPositions() Function", clrMintCream, "Arial Black", class="num">10, clrBrown ); class=class="str">"cmt">//-- "CloseAllSellSymbolPositions": Button to call the imported ex5 CloseAllSellSymbolPositions() function CreateButton( "CloseAllSellPositionsBtn", class="num">475, class="num">170, class="num">215, class="num">35, "CloseAllSellPositions", "CloseAllSellPositions() Function", clrMintCream, "Arial Black", class="num">10, clrBrown ); class=class="str">"cmt">//-- "CloseAllMagicPositions": Button to call the imported ex5 CloseAllMagicPositions() function CreateButton( "CloseAllMagicPositionsBtn", class="num">250, class="num">210, class="num">440, class="num">35, "CloseAllMagicPositions", "CloseAllMagicPositions() Function", clrMintCream, "Arial Black", class="num">10, C&class="macro">#x27;class="num">203,class="num">18,class="num">55&class="macro">#x27; ); class=class="str">"cmt">//-- "CloseAllProfitablePositions": Button to call the imported ex5 CloseAllMagicPositions() function CreateButton(
◍ 面板按钮与持仓统计标签的布局落点
这段逻辑在 EA 面板里落了两个实盘按钮:盈利单平仓键坐标 (250,250)、宽 215 高 35,文字用 Arial Black 10 号;亏损单平仓键放在 (475,250),同样尺寸,但字体色用 C'179,45,0'(深砖红)区分风险动作。 标签区从 y=310 往下排:底部标题显示品种与魔术码,如 XAUUSD - (Magic Number: 888) Positions Status;紧接着是总持仓数、总手数(NormalizeDouble 到 2 位)、总利润(带账户币种后缀)。 多单区块从 y=410 起:先 BUY POSITIONS 头标签,再列多单总笔数与总手数。外汇和贵金属杠杆高,面板数值仅作实时监控,不代表任何平仓时机判断。
"CloseAllProfitablePositionsBtn", class="num">250, class="num">250, class="num">215, class="num">35, "CloseAllProfitablePositions", "CloseAllProfitablePositions() Function", clrMintCream, "Arial Black", class="num">10, clrSeaGreen ); class=class="str">"cmt">//-- "CloseAllLossPositions": Button to call the imported ex5 CloseAllLossPositions() function CreateButton( "CloseAllLossPositionsBtn", class="num">475, class="num">250, class="num">215, class="num">35, "CloseAllLossPositions", "CloseAllLossPositions() Function", clrMintCream, "Arial Black", class="num">10, C&class="macro">#x27;class="num">179,class="num">45,class="num">0&class="macro">#x27; ); class=class="str">"cmt">//-- Create the bottomHeadingLabel CreateLabel( "bottomHeadingLabel", class="num">250, class="num">310, class="num">440, class="num">60, (_Symbol + " - (Magic Number: " + (class="type">class="kw">string)magicNo + ") Positions Status"), clrBlack, "Calibri", class="num">12 ); class=class="str">"cmt">//-- Create totalOpenPositionsLabel CreateLabel( "totalOpenPositionsLabel", class="num">250, class="num">340, class="num">440, class="num">60, (" Total Open: " + (class="type">class="kw">string)MagicPositionsTotal(magicNo)), clrNavy, "Consolas", class="num">11 ); class=class="str">"cmt">//-- Create totalPositionsVolumeLabel CreateLabel( "totalPositionsVolumeLabel", class="num">250, class="num">360, class="num">440, class="num">60, (" Total Volume: " + (class="type">class="kw">string)NormalizeDouble(MagicPositionsTotalVolume(magicNo), class="num">2)), clrNavy, "Consolas", class="num">11 ); class=class="str">"cmt">//-- Create the totalPositionsProfitLabel CreateLabel( "totalPositionsProfitLabel", class="num">250, class="num">380, class="num">100, class="num">60, ( " Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(MagicPositionsProfit(magicNo), class="num">2)) + " " + AccountInfoString(ACCOUNT_CURRENCY) ), clrNavy, "Consolas", class="num">11 ); class=class="str">"cmt">//-- Create the buyPositionsHeadingLabel CreateLabel( "buyPositionsHeadingLabel", class="num">250, class="num">410, class="num">440, class="num">60, ("BUY POSITIONS:"), clrBlack, "Calibri", class="num">12 ); class=class="str">"cmt">//-- Create the totalBuyPositionsLabel CreateLabel( "totalBuyPositionsLabel", class="num">250, class="num">430, class="num">440, class="num">60, (" Total Open: " + (class="type">class="kw">string)MagicBuyPositionsTotal(magicNo)), clrNavy, "Consolas", class="num">11 ); class=class="str">"cmt">//-- Create the totalBuyPositionsVolumeLabel CreateLabel( "totalBuyPositionsVolumeLabel", class="num">250, class="num">450, class="num">440, class="num">60,
「多空持仓面板与退出清理的落地写法」
这段逻辑在图表右侧用 CreateLabel 拼出两组只读面板:左边 250 像素起排 BUY POSITIONS,右边 475 像素起排 SELL POSITIONS,各自显示总开仓数、总手数与总浮动盈亏。手数和盈亏都过了 NormalizeDouble(...,2),保留两位小数;盈亏后缀接 AccountInfoString(ACCOUNT_CURRENCY),直接带账户币种,不用手填符号。 买侧面板四行坐标分别是 (250,410)、(250,430)、(250,450)、(250,470),卖侧面板对应 (475,410) 到 (475,470);字体买侧用 Consolas 11、标题用 Calibri 12,颜色买侧 clrNavy、标题 clrBlack。所有标签建完必须调一次 ChartRedraw(),否则新对象不会立即刷出来。 EA 卸载时靠 DeleteChartObjects() 收尾,目前清了 OpenBuyPositionBtn、OpenSellPositionBtn、SetSlTpBtn 三个按钮对象。若你后续加了别的图形对象,得在这里补 ObjectDelete(0,"对象名"),否则 MT5 退出后图表还留残影。外汇与贵金属杠杆高,这类面板只做持仓透视,不替代风控。
(" Total Volume: " + (class="type">class="kw">string)NormalizeDouble(MagicBuyPositionsTotalVolume(magicNo), class="num">2)),
clrNavy, "Consolas", class="num">11
);
class=class="str">"cmt">//-- Create the totalBuyPositionsProfitLabel
CreateLabel(
"totalBuyPositionsProfitLabel", class="num">250, class="num">470, class="num">440, class="num">60,
(
" Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(MagicBuyPositionsProfit(magicNo), class="num">2)) +
" " + AccountInfoString(ACCOUNT_CURRENCY)
),
clrNavy, "Consolas", class="num">11
);
class=class="str">"cmt">//-- Create the sellPositionsHeadingLabel
CreateLabel(
"sellPositionsHeadingLabel", class="num">475, class="num">410, class="num">440, class="num">60,
("SELL POSITIONS:"),
clrBlack, "Calibri", class="num">12
);
class=class="str">"cmt">//-- Create the totalSellPositionsLabel
CreateLabel(
"totalSellPositionsLabel", class="num">475, class="num">430, class="num">440, class="num">60,
(" Total Open: " + (class="type">class="kw">string)MagicSellPositionsTotal(magicNo)),
clrNavy, "Consolas", class="num">11
);
class=class="str">"cmt">//-- Create the totalSellPositionsVolumeLabel
CreateLabel(
"totalSellPositionsVolumeLabel", class="num">475, class="num">450, class="num">440, class="num">60,
(" Total Volume: " + (class="type">class="kw">string)NormalizeDouble(MagicSellPositionsTotalVolume(magicNo), class="num">2)),
clrNavy, "Consolas", class="num">11
);
class=class="str">"cmt">//-- Create the totalSellPositionsProfitLabel
CreateLabel(
"totalSellPositionsProfitLabel", class="num">475, class="num">470, class="num">100, class="num">60,
(
" Total Profit: " + (class="type">class="kw">string)(NormalizeDouble(MagicSellPositionsProfit(magicNo), class="num">2)) +
" " + AccountInfoString(ACCOUNT_CURRENCY)
),
clrNavy, "Consolas", class="num">11
);
class=class="str">"cmt">//--- Redraw the chart to refresh it so that it loads our new chart objects
ChartRedraw();
class=class="str">"cmt">//---
}
class="type">void DeleteChartObjects()
{
class=class="str">"cmt">//---
class=class="str">"cmt">//--- Clean up and class="kw">delete all the buttons or graphical objects
ObjectDelete(class="num">0, "OpenBuyPositionBtn");
ObjectDelete(class="num">0, "OpenSellPositionBtn");
ObjectDelete(class="num">0, "SetSlTpBtn");清面板与改止盈止损的衔接写法
EA 退出或重置时,先把图表上那一串按钮和标签全删掉,否则下次加载会叠对象。上面这段连续调用 ObjectDelete,覆盖了从 SetTrailingStopLossBtn 到 totalSellPositionsProfitLabel 共 26 个具名对象,包括交易按钮、分区标题和盈亏统计标签。 删完对象,真正的仓位管理逻辑在 ModifySlTp 里。它先用 PositionsTotal 拿到当前未平仓位数量,再按索引 x 从 0 扫到 totalOpenPostions-1,逐个取 ticket、品种和 magic。 过滤条件很直接:selectedSymbol 不等于 _Symbol 且 magicNo 不匹配的就 continue 跳过。只对本 EA 在图上用 BUY/SELL 按钮开的仓调用 SetSlTpByTicket(positionTicket, sl, tp),这个函数来自外部 ex5 库。外汇和贵金属杠杆高,批量改 SL/TP 可能瞬间触发滑点,上 MT5 跑前先把 sl、tp 变量单位确认清楚。
ObjectDelete(class="num">0, "SetTrailingStopLossBtn"); ObjectDelete(class="num">0, "ClosePositionsBtn"); ObjectDelete(class="num">0, "CloseAllPositionsBtn"); ObjectDelete(class="num">0, "CloseAllBuyPositionsBtn"); ObjectDelete(class="num">0, "CloseAllSellPositionsBtn"); ObjectDelete(class="num">0, "CloseAllMagicPositionsBtn"); ObjectDelete(class="num">0, "CloseAllProfitablePositionsBtn"); ObjectDelete(class="num">0, "CloseAllLossPositionsBtn"); ObjectDelete(class="num">0, "mainRectangleLabel"); ObjectDelete(class="num">0, "headingLabel"); ObjectDelete(class="num">0, "headingLabel2"); ObjectDelete(class="num">0, "bottomHeadingLabel"); ObjectDelete(class="num">0, "totalOpenPositionsLabel"); ObjectDelete(class="num">0, "totalPositionsVolumeLabel"); ObjectDelete(class="num">0, "totalPositionsProfitLabel"); ObjectDelete(class="num">0, "buyPositionsHeadingLabel"); ObjectDelete(class="num">0, "totalBuyPositionsLabel"); ObjectDelete(class="num">0, "totalBuyPositionsVolumeLabel"); ObjectDelete(class="num">0, "totalBuyPositionsProfitLabel"); ObjectDelete(class="num">0, "sellPositionsHeadingLabel"); ObjectDelete(class="num">0, "totalSellPositionsLabel"); ObjectDelete(class="num">0, "totalSellPositionsVolumeLabel"); ObjectDelete(class="num">0, "totalSellPositionsProfitLabel"); } class=class="str">"cmt">//+-------------------------------------------------------------------------+ class=class="str">"cmt">// ModifySlTp(): This function demonstrates how to use the imported ex5 | class=class="str">"cmt">// class="type">bool SetSlTpByTicket(class="type">ulong positionTicket, class="type">int sl, class="type">int tp); | class=class="str">"cmt">// It runs this function when the setSLTP button on the chart is clicked. | class=class="str">"cmt">//+-------------------------------------------------------------------------+ class="type">void ModifySlTp() { class=class="str">"cmt">//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with class="type">int totalOpenPostions = PositionsTotal(); class=class="str">"cmt">//--- Scan open positions for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class=class="str">"cmt">//--- Get position properties class="type">ulong positionTicket = PositionGetTicket(x); class=class="str">"cmt">//-- Get ticket to select the position class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); class=class="str">"cmt">//-- modify only the positions we have opened with this EA(magic number) using the BUY and SELL buttons on the chart if(selectedSymbol != _Symbol && positionMagicNo != magicNo) { class="kw">continue; } class=class="str">"cmt">//--- modify the sl and tp of the position SetSlTpByTicket(positionTicket, sl, tp);class=class="str">"cmt">//-- call the imported function from our ex5 library } }
◍ 图表按钮触发移动止损与平仓的调用逻辑
在 MT5 面板类 EA 里,移动止损和一键平仓通常不写在主循环,而是绑到图表按钮的点击事件上。下面两段函数演示了如何只处理本 EA 用 BUY/SELL 按钮开出的持仓,再调用外部 ex5 库里的接口。 SetTrailingSl() 先读入全局变量 sl 作为 trailingSl 点数,然后用 PositionsTotal() 拿当前持仓数,从 0 循环到 totalOpenPostions-1。每次用 PositionGetTicket(x) 取ticket,再比对 POSITION_SYMBOL 和 POSITION_MAGIC,若不是当前品种或 magic 不符就 continue 跳过,只对自有持仓调用 SetTrailingStopLoss(positionTicket, trailingSl)。 ClosePositionWithTicket() 结构完全一致,差别仅在最后一行换成 ClosePositionByTicket(positionTicket) 平掉对应单子。两个函数都依赖图表上点击 ClosePositions / SetTrailingStopLoss 按钮时由 ChartEvent 回调触发,因此你复制代码后若按钮无反应,优先检查 ChartEvent 里是否把 lparam 的按钮 ID 正确路由到这两个 void 函数。 外汇与贵金属杠杆高,移动止损只降低回撤概率,不保证免亏;上正式账户前请在策略测试器用历史数据跑一遍按钮逻辑。
class="type">void SetTrailingSl() { class="type">int trailingSl = sl; class="type">int totalOpenPostions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class="type">ulong positionTicket = PositionGetTicket(x); class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); if(selectedSymbol != _Symbol && positionMagicNo != magicNo) { class="kw">continue; } SetTrailingStopLoss(positionTicket, trailingSl); } } class="type">void ClosePositionWithTicket() { class="type">int totalOpenPostions = PositionsTotal(); for(class="type">int x = class="num">0; x < totalOpenPostions; x++) { class="type">ulong positionTicket = PositionGetTicket(x); class="type">class="kw">string selectedSymbol = PositionGetString(POSITION_SYMBOL); class="type">ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC); if(selectedSymbol != _Symbol && positionMagicNo != magicNo) { class="kw">continue; } ClosePositionByTicket(positionTicket); } }
「图表按钮怎么驱动下单与风控」
在 MT5 面板类 EA 里,交易动作通常不是靠定时器轮询,而是靠 OnChartEvent 捕获鼠标点击。当 id 等于 CHARTEVENT_OBJECT_CLICK 时,sparam 会带回被点对象的名字,靠这个字符串就能分流到不同的持仓操作。 下面这段处理函数把四个按钮接进了同一个事件入口:OpenBuyPositionBtn 触发多单、OpenSellPositionBtn 触发空单、SetSlTpBtn 改止损止盈、SetTrailingStopLossBtn 挂追踪止损、ClosePositionsBtn 平仓。每次执行完都用 ObjectSetInteger 把按钮 OBJPROP_STATE 置回 false,否则按钮会卡在按下态。 外汇与贵金属杠杆高,这类一键下单面板若误触可能在毫秒级开出真实仓位,建议在策略测试器里先用模拟品种跑通事件链路再上实盘。 别把按钮名写死当保险 代码里按钮名 "OpenBuyPositionBtn" 等是硬编码字符串,一旦面板对象改名,事件分流会静默失效。更稳的做法是把按钮名存成宏或输入参数,改动只在一处。
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">//--- class=class="str">"cmt">//--- Detected a CHARTEVENT_CLICK event if(id == CHARTEVENT_OBJECT_CLICK) { Print(__FUNCTION__, ": ", sparam); class=class="str">"cmt">//--- Buy when OpenBuyPositionBtn button(BUY) is pressed or clicked if(sparam == "OpenBuyPositionBtn") { class=class="str">"cmt">//-- Call our imported function from the Toolkit/PositionsManager ex5 library OpenBuyPosition(magicNo, _Symbol, volumeLot, sl, tp, "ex5 PositionsManager"); class=class="str">"cmt">//--- Release and unpress the button ObjectSetInteger(class="num">0, "OpenBuyPositionBtn", OBJPROP_STATE, false); } class=class="str">"cmt">//--- Sell when OpenSellPositionBtn button(SELL) is pressed if(sparam == "OpenSellPositionBtn") { class=class="str">"cmt">//-- Call our imported function from the Toolkit/PositionsManager ex5 library OpenSellPosition(magicNo, _Symbol, volumeLot, sl, tp, "ex5 PositionsManager"); class=class="str">"cmt">//OpenSellPosition(magicNo, "NON-EXISTENT-Symbol-Name"class=class="str">"cmt">/*_Symbol*/, volumeLot, sl, tp, "ex5 PositionsManager"); class=class="str">"cmt">//--- Release and unpress the button ObjectSetInteger(class="num">0, "OpenSellPositionBtn", OBJPROP_STATE, false); } class=class="str">"cmt">//--- Modify specified positions SL and TP when SetSlTpBtn button(setSLTP) is pressed if(sparam == "SetSlTpBtn") { ModifySlTp();class=class="str">"cmt">//-- Modify the SL and TP of the positions generated by the BUY and SELL buttons class=class="str">"cmt">//--- Release and unpress the button ObjectSetInteger(class="num">0, "SetSlTpBtn", OBJPROP_STATE, false); } class=class="str">"cmt">//--- Set the Trailing Stop Loss when SetSlTpBtn button(SetTrailingStopLossBtn) is pressed if(sparam == "SetTrailingStopLossBtn") { SetTrailingSl();class=class="str">"cmt">//-- Set the Trailing Stop Loss for the positions generated by the BUY and SELL buttons class=class="str">"cmt">//--- Release and unpress the button ObjectSetInteger(class="num">0, "SetTrailingStopLossBtn", OBJPROP_STATE, false); } class=class="str">"cmt">//--- Close specified positions when SetSlTpBtn button(setSLTP) is pressed if(sparam == "ClosePositionsBtn") { ClosePositionWithTicket();class=class="str">"cmt">//-- Close all the positions generated by the BUY and SELL buttons class=class="str">"cmt">//--- Release and unpress the button
按钮事件里的平仓分流逻辑
在 EA 的 ChartEvent 回调里,面板按钮的 sparam 字符串就是识别用户点击了哪个平仓键的唯一凭据。上面这段把六种平仓诉求拆成了独立的 if 分支,分别导向不同的自定义平仓函数,互不干扰。 CloseAllPositions 传的是 _Symbol 和 0,意味着只平当前图表品种、不限定 magic 的所有单;而 CloseAllBuyPositions / CloseAllSellPositions 则额外带了 magicNo 变量,只清同品种同魔术码下的多单或空单。 每次执行完平仓函数后都补一句 ObjectSetInteger(0, 按钮名, OBJPROP_STATE, false),这是把图形按钮的按下态复位。若不写这行,按钮会一直卡在“凹下”的高亮状态,下一次点击可能不触发新事件。 带盈利/亏损过滤的两个分支(CloseAllProfitablePositions、CloseAllLossPositions)同样依赖 _Symbol 加 magicNo 做范围收敛。实盘里外汇与贵金属波动剧烈、滑点可能吞掉预期利润,这类批量平仓建议先在策略测试器用历史数据跑一遍,确认平仓顺序和成交回报符合预期再上真仓。
if(sparam == "CloseAllPositionsBtn") { CloseAllPositions(_Symbol, class="num">0);class=class="str">"cmt">//-- Close all the open symbol positions ObjectSetInteger(class="num">0, "CloseAllPositionsBtn", OBJPROP_STATE, false); } if(sparam == "CloseAllBuyPositionsBtn") { CloseAllBuyPositions(_Symbol, magicNo);class=class="str">"cmt">//-- Close all the open symbol buy positions ObjectSetInteger(class="num">0, "CloseAllBuyPositionsBtn", OBJPROP_STATE, false); } if(sparam == "CloseAllSellPositionsBtn") { CloseAllSellPositions(_Symbol, magicNo);class=class="str">"cmt">//-- Close all the open symbol sell positions ObjectSetInteger(class="num">0, "CloseAllSellPositionsBtn", OBJPROP_STATE, false); } if(sparam == "CloseAllMagicPositionsBtn") { CloseAllMagicPositions(magicNo);class=class="str">"cmt">//-- Close all the open positions with the specified magic number ObjectSetInteger(class="num">0, "CloseAllMagicPositionsBtn", OBJPROP_STATE, false); } if(sparam == "CloseAllProfitablePositionsBtn") { CloseAllProfitablePositions(_Symbol, magicNo);class=class="str">"cmt">//-- Close all the open profitable positions with the specified symbol and magic number ObjectSetInteger(class="num">0, "CloseAllProfitablePositionsBtn", OBJPROP_STATE, false); } if(sparam == "CloseAllLossPositionsBtn") { CloseAllLossPositions(_Symbol, magicNo);class=class="str">"cmt">//-- Close all the open loss positions with the specified symbol and magic number ObjectSetInteger(class="num">0, "CloseAllLossPositionsBtn", OBJPROP_STATE, false);
◍ EA 生命周期里的按钮与图表刷新钩子
这段 MT5 代码展示了 EA 在初始化、报价跳动和退出三个阶段如何与图表对象联动。OnInit 里只做一件事:调用 LoadChartObjects() 把演示用的按钮画上图,随后返回 INIT_SUCCEEDED,EA 才算正式挂载成功。 OnTick 每次收到新报价都会先后跑 SetTrailingSl() 和 LoadChartObjects()。前者调用外部 ex5 库函数处理追踪止损,后者负责把按钮状态重绘到图上——也就是说,图表上的“平仓亏损单”按钮在每跳都可能被刷新一次。 在按钮事件处理末尾,ObjectSetInteger(0, "CloseAllLossPositionsBtn", OBJPROP_STATE, false) 会把按钮状态复位为未按下,紧接 ChartRedraw() 强制重绘,避免界面卡在按下态。外汇与贵金属品种波动快,这种每跳重绘逻辑在低频品种上无感,但在秒级跳动品种可能吃掉少量 CPU。 OnDeinit 负责收尾:DeleteChartObjects() 清掉所有自建图形,Comment("") 抹去左下角文字。不清理的话,EA 移除后图表上会残留按钮和旧注释,干扰后续手动盯盘。
ObjectSetInteger(class="num">0, "CloseAllLossPositionsBtn", OBJPROP_STATE, false); } class=class="str">"cmt">//--- Redraw the chart to refresh it ChartRedraw(); } class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- class=class="str">"cmt">//create buttons to demonstrate how the different ex5 library functions work LoadChartObjects(); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//-- Check for profitable positions and set the trailing stop loss on every new tick SetTrailingSl(); class=class="str">"cmt">//-- Calls the ex5 library function responsible for setting Trailing stops LoadChartObjects(); class=class="str">"cmt">//--- Update chart objects } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- class=class="str">"cmt">//-- Clean up the chart DeleteChartObjects(); class=class="str">"cmt">//-- Clear any chart comments Comment(""); }
「把这条线请下神坛」
这套 EX5 仓位管理库已经跑通了:PositionsManager.ex5 编译后 44.65 KB,配套 DualVidyaTrader.mq5 仅 10.77 KB,说明业务逻辑被压进库里之后,EA 本体可以很轻。你在 MT5 里加载这两个文件,就能直接看库函数怎么被外部项目调用。 调试时最实用的动作是改完库重新编译、再覆盖旧 ex5 并重启 EA——原文里提到的『更新和重新部署』就是这个循环,比在单一 mq5 里改全局函数省事。 外汇和贵金属波动剧烈、杠杆高风险大,任何库都只是把下单动作标准化,不替你判断方向。下一份挂单管理库思路类似,但先把眼前这套跑顺再说。