MQL5 Cookbook: 交易历史和取得仓位信息的函数库·综合运用
净仓手数折算与持仓时长拆解
处理部分平仓后的可平手数时,不能简单读 pos.volume。代码先判断除首笔外加总交易量 sum_volume 是否大于 0:若大于 0,用 pos.volume 减去 sum_volume,结果为正才返回该差值,否则退回当前仓位全量。若 sum_volume 等于 0,说明没有后续加减仓,直接返回 pos.volume,最后 NormalizeDouble 到 2 位小数。 外汇与贵金属持仓时长统计建议用枚举切单位。下方 ENUM_POSITION_DURATION 给了 DAYS=0、HOURS=1、MINUTES=2、SECONDS=3 四种模式,CurrentPositionDuration 用 TimeCurrent()-pos.time 拿到秒数后做整除,例如小时数就是 seconds/(60*60),天数是 seconds/(60*60*24)。 转字符串函数 CurrentPositionDurationToString 在无持仓时返回“-”,有持仓则拆 days/hours/minutes/seconds 变量准备拼接。MT5 上把这两段塞进 EA,能直接读出某仓已扛了几个自然小时,对波段止损迁移有参考价值。贵金属隔夜跳空风险高,时长统计仅作辅助,不替代风控。
if(fabs(sum_volume)>class="num">0) { class=class="str">"cmt">//--- 当前仓位交易量减去除第一笔外的所有交易的交易量 class="type">class="kw">double result=pos.volume-sum_volume; class=class="str">"cmt">//--- 如果结果大于0,返回该结果,否则返回当前仓位交易量 deal_volume=result>class="num">0 ? result : pos.volume; } class=class="str">"cmt">//--- 如果除了进场交易外没有更多交易, if(sum_volume==class="num">0) deal_volume=pos.volume; class=class="str">"cmt">// 返回当前仓位交易量 } class=class="str">"cmt">//--- 返回初始仓位交易量 class="kw">return(NormalizeDouble(deal_volume,class="num">2)); } class=class="str">"cmt">//--- 仓位持仓时间 enum ENUM_POSITION_DURATION { DAYS = class="num">0, class=class="str">"cmt">// 天 HOURS = class="num">1, class=class="str">"cmt">// 小时 MINUTES = class="num">2, class=class="str">"cmt">// 分钟 SECONDS = class="num">3 class=class="str">"cmt">// 秒 }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 返回当前仓位的持仓时间 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">ulong CurrentPositionDuration(ENUM_POSITION_DURATION mode) { class="type">ulong result=class="num">0; class=class="str">"cmt">// 最终结果 class="type">ulong seconds=class="num">0; class=class="str">"cmt">// 秒数 class=class="str">"cmt">//--- 计算持仓时间的秒数 seconds=TimeCurrent()-pos.time; class=class="str">"cmt">//--- class="kw">switch(mode) { case DAYS : result=seconds/(class="num">60*class="num">60*class="num">24); break; class=class="str">"cmt">// 计算天数 case HOURS : result=seconds/(class="num">60*class="num">60); break; class=class="str">"cmt">// 计算小时数 case MINUTES : result=seconds/class="num">60; break; class=class="str">"cmt">// 计算分钟数 case SECONDS : result=seconds; break; class=class="str">"cmt">// 无计算 (秒数) class=class="str">"cmt">//--- class="kw">default : Print(__FUNCTION__,"(): 未知传入持仓时间模式!"); class="kw">return(class="num">0); } class=class="str">"cmt">//--- 返回结果 class="kw">return(result); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 把仓位持仓时间转换为字符串 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CurrentPositionDurationToString(class="type">ulong time) { class=class="str">"cmt">//--- 如果没有仓位则返回横线 class="type">class="kw">string result="-"; class=class="str">"cmt">//--- 如果有持仓 if(pos.exists) { class=class="str">"cmt">//--- 用于计算结果的变量 class="type">ulong days=class="num">0; class="type">ulong hours=class="num">0; class="type">ulong minutes=class="num">0; class="type">ulong seconds=class="num">0; class=class="str">"cmt">//---
◍ 把秒数拆成天时分秒的底层写法
在 MT5 里做持仓时长或指标周期可视化时,常需要把一串以秒为单位的整数 time 拆成 DD:HH:MM:SS。上面这段逻辑用连续取模和整除完成,不依赖任何时间结构体,纯整数运算,回测 1 万根 bar 的耗时通常在微秒级。 核心节奏是:先对 60 取模拿到秒,原值除以 60 去掉秒;再对 60 取模拿分,除以 60 去掉分;对 24 取模拿小时,除以 24 剩下天数。每一步的 time/= 都在压缩量级,顺序不能反。 最后用 StringFormat 按 "%02u d: %02u h : %02u m : %02u s" 拼串,%02u 保证个位补零。复制进 EA 的辅助函数,传一个秒数进去就能直接拿到可读字符串,省得在图表上盯整数犯晕。
seconds=time%class="num">60; time/=class="num">60; class=class="str">"cmt">//--- minutes=time%class="num">60; time/=class="num">60; class=class="str">"cmt">//--- hours=time%class="num">24; time/=class="num">24; class=class="str">"cmt">//--- days=time; class=class="str">"cmt">//--- 以指定的格式 DD:HH:MM:SS 生成字符串 result=StringFormat("%02u d: %02u h : %02u m : %02u s",days,hours,minutes,seconds); } class=class="str">"cmt">//--- 返回结果 class="kw">return(result); }
「拿 EURUSD 跑一轮参数优化试试」
先把这套半成品 EA 丢进 MT5 策略测试器做参数优化,目的不是立刻上线,而是看不同参数组合在历史数据上的表现差异,顺便攒一点系统开发的实感。 测试器里选好 EA,品种锁定 EURUSD,优化模式打开,外部参数留成可变量让测试器自己扫。优化跑完以后,按最大采收率(Recovery Factor)排个序,最靠前的一组数值是 4.07。 有意思的是,参数只在 EURUSD 上寻优,切到别的直盘却也没崩:EURUSD 自身结果正向,AUDUSD 出正面,NZDUSD 同样给正面。这说明这套逻辑对外汇品种有一定泛化性,但样本仅三个直盘,贵金属与交叉盘未覆盖,外汇交易本身杠杆高风险,不能直接当普适结论。 想验证的话,直接把这组 4.07 的参数原样贴回测试器,换 AUDUSD / NZDUSD 各跑一次,看净值曲线是否和原文描述一致。
别急着下结论
任何交易思路在写进 EA 之前,都该先在 MT5 策略测试器里跑足够样本。2013 年那篇俄文原帖的评论区就有人提到,默认脚本下 HistoryOrderGetDouble() 返回布尔值时会报 4755(找不到交易),这类坑只有实跑才能暴露,光看逻辑永远发现不了。
自定义和适配一套系统,真正吃功夫的是后续各种机制与规划——仓位梯度、异常订单处理、不同经纪商环境差异,这些都不是一次性写完就完事。外汇与贵金属杠杆高,回测顺滑不代表实盘能活,每一处改动都建议用小资金验证。
下一篇我们会拆具体的自适应机制,在那之前,把你手头疑似有缺陷的系统先完整测一遍,别急着删。