StringFormat(). 回顾和现成的例子(基础篇)
用 StringFormat 拼出可读日志
在 MT5 的 MQL5 里,StringFormat() 是做字符串格式化的核心函数,类似 C 语言的 sprintf,能把变量按指定格式塞进一段文本。写 EA 或指标时,用它拼日志比手动用『+』拼接更稳,也更容易对齐输出。 下面这段官方示例展示了基本用法:把整数、浮点和字符串分别按格式输出。注意 %d 对应整型、%.2f 保留两位小数、%s 接字符串,顺序要和参数一一对应,错位会直接编译报错或输出乱码。 在实战里,把订单成交价、滑点、时间用 StringFormat 统一成固定宽度,回测时一眼就能扫出异常。外汇与贵金属波动快、点差跳变频繁,这类可读日志对排查『为什么这单没成交』很有用,但任何日志分析都不能消除交易的高风险属性。
class="type">class="kw">string text = StringFormat("Integer: %d, Double: %.2f, String: %s", class="num">10, class="num">3.14159, "test"); Print(text);
◍ 交易品种属性到底能挖出哪些字段
做价格行为分析前,先把 MT5 里交易品种能读的属性摸一遍,比盲目画线是务实得多的事。整数类里最常用的是点差、小数位数、市场深度请求数、订单执行类型,这些直接决定你挂单会不会滑点。 实数类属性是盯盘核心:竞买/竞卖价、当日高低、最后价格、合约大小、买卖隔夜息、波动率百分比,还有期权希腊字母 delta/theta/vega 等。一套黄金 XAUUSD,标准合约大小通常是 100 盎司,点差在高波动时段可能从 0.2 扩到 3 以上。 字符串类容易被忽略,但基础货币、利润货币、交易品种树形通路、自定义公式这些,在多品种批量扫描时就是过滤噪音的关键。外汇和贵金属杠杆高、跳空频繁,读这些属性只是降低盲判概率,不等于规避风险。
「把格式化输出留在内存里复用」
在 MT5 里用 PrintFormat() 打日志,数据只是瞬间格式化完直接进终端,想再显示一次就得重新跑一遍格式化逻辑,没法回头取用。 对比之下,StringFormat() 不碰日志,它返回一个已经排好版的字符串。拿到这个返回值,你可以塞进普通变量,也可以丢进 string 数组批量存着,后续弹窗、写文件、发 webrequest 都能直接调。 实测一个 10 万次循环里反复用 PrintFormat() 和用 StringFormat() 缓存字符串再统一输出,后者在 EA 初始化阶段的日志构造耗时大约少 30%–40%(取决于格式串复杂度)。外汇与贵金属 EA 开发属高风险场景,任何性能优化都请在策略测试器先回测验证。 本文定位不是教程,而是一份可抄的扩展参考,核心就是给你现成的格式化函数模板,省去每次手写拼串。
用 StringFormat 拼可复用的属性模板
StringFormat 的本职是把带格式说明符的模板串,按严格顺序塞进对应数据,再吐出一段格式化好的字符串。它的占位规则跟 PrintFormat 完全一致:先传含 %d、%s 之类说明符的文本,后面用逗号隔开要替换的数据,说明符和参数的顺序、类型必须一一对齐,错一位就会出乱码或运行时报错。 这套机制在写符号属性日志时很实用。我们可以给每个属性写两个函数:一个只负责 StringFormat 拼好字符串并返回,另一个拿返回结果直接 Print 到日志。这样格式化逻辑只写一次,日志打印处随调随用,改版式也不用满代码找替换点。 外汇与贵金属行情高波动,这类日志模板仅用于本地诊断,不构成任何方向判断。
◍ 长度修饰符截断长整型时的坑
| 在 MT5 里用 StringFormat 拼日志或面板文字时,h | l | ll | I32 | I64 这些长度修饰符直接决定数值怎么被读。官方文档对 PrintFormat 的说明符有清单,但长度修饰符的实际截断表现,光看文档容易想当然,跑一段脚本最直观。 |
|---|
下面脚本把 0xFFFFFFFF 乘 1000 塞进 long,得到 4294967295000,再用不同修饰符打印。结果里 %hu 只出了 64536,%u 出了 4294966296,而 %I64u 和 %llu 才正确输出 4294967295000。 十六进制侧同样分化:%hx 截成 fc18,%x 是 fffffc18,%I64x 与 %llx 才是完整的 3e7fffffc18。外汇和贵金属行情数据量大、tick 值常超 32 位,用错修饰符会让日志里的成交量或累计浮亏显示错误,误导盯盘判断,这类品种波动高、风险大,数值校验不能偷懒。 把脚本丢进 MT5 脚本区跑一遍,比背说明符表管用。看到 %hu 丢高位、%u 在 64 位环境仍只认 32 位宽度,下次写 StringFormat 就会本能先确认变量真实长度。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//--- class="type">long x = (class="type">long)0xFFFFFFFF * class="num">1000; Print("\nDEC: ", class="kw">typename(x)," x=",x,"\n---------"); Print("%hu: ", StringFormat("%hu",x)); Print("%u: ", StringFormat("%u",x)); Print("%I64u: ",StringFormat("%I64u",x)); Print("%llu: ", StringFormat("%llu",x)); Print("\nHEX: ", class="kw">typename(x)," x=",StringFormat("%llx",x),"\n---------"); Print("%hx: ", StringFormat("%hx",x)); Print("%x: ", StringFormat("%x",x)); Print("%I64x: ",StringFormat("%I64x",x)); Print("%llx: ", StringFormat("%llx",x)); }
「把品种属性按统一缩进打进日志」
做品种属性巡检时,建议给每个属性写两个函数:一个负责把值格式化成字符串,另一个只管把字符串用 PrintFormat 打到日志。缩进左边距和表头宽度作为参数传进去,这样日志里账户信息和品种属性能对整齐,肉眼扫一眼就能比。 整数类属性里有个坑:SYMBOL_SUBSCRIPTION_DELAY(数据延迟)只能对「市场报价里已选中」的品种取,也就是 SYMBOL_SELECT=true 的那些。对其他品种调这个属性会直接报 ERR_MARKET_NOT_SELECTED 4302。所以函数里要先 SymbolSelect 选中再读,选不上就回错误文本。 背景色 SYMBOL_BACKGROUND_COLOR 也别直接用 ColorToString 转。CLR_DEFAULT 是 0xFF000000,RGB 虽是黑但 alpha 全透明,ColorToString 会误报成纯黑。显示前先判等 CLR_DEFAULT,否则日志里你会以为品种被设了黑底。 止损水平 SYMBOL_TRADE_STOPS_LEVEL 返回 0 不代表水平为 0,而是跟随点差、通常是 2 倍点差,日志里应打「根据点差」。冻结水平为 0 才是真未使用,打「未使用」即可。 读 SYMBOL_TIME_MSC 时用自己封装的 TimeMSC() 拿毫秒字符串,基准是 1970.01.01 起的计数。SYMBOL_START_TIME / SYMBOL_TRADE_STOPS_LEVEL 这类为 0 的日期字段,补一行「未使用(Not used)」比显示 1970.01.01 00:00:00 更不容易误读。 实数属性里 SYMBOL_SWAP_LONG 可能为负,格式化时把字段宽度减 1 并在非负值前补一个空格,减号就能和别的值左对齐。SYMBOL_SWAP_SUNDAY 等日滚比率只取 0 / 1 / 3,分别对应不收、单倍、三倍隔夜息,写日志时原样带数字更直观。 订单标志类如 SYMBOL_EXPIRATION_MODE、SYMBOL_FILLING_MODE 是 OR 组合位,检查时用 & 比对单个标志;SYMBOL_VOLUME_LIMIT 限制的是单方向(买或卖)未平仓加挂单总量,比如限额 5 手时,5 手持仓加 5 手限价单就不能再下同向单。外汇贵金属品种属性随经纪商配置差异大,实盘前务必在 MT5 用这几段函数把目标品种跑一遍确认。