通用EA交易: 访问交易品种的属性 (第8部分)(基础篇)
「在EA里读取品种属性到底能拿到什么」
写通用EA时,第一道坎不是信号逻辑,而是搞清楚当前品种到底有哪些可交易属性。MT5 把这些都封装在 SymbolInfo 系列函数里,但很多新手只会硬写点差常量,结果换到 XAUUSD 或交叉盘就爆掉。 一个冷数据:在 2017-08-31 那篇示例里,作者用标准五位数账户测过,EURUSD 的 SYMBOL_TRADE_TICK_VALUE 约为 1 美元/点,而 XAUUSD 的每跳价值随报价小数位浮动,硬编码必然错。读取属性要用动态方式。 下面这段是从原文抽出的核心取属性写法,先贴原码再拆: [CODE] double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); [/CODE] 第一行拿当前品种的每跳金额,注意是 double 不是 int,黄金和外汇差十倍量级。第二行取报价小数位,强转 int 只为后续计算方便,原函数是 long。第三行取最小价格变动单位 point,算止损距离时直接用 point 乘倍数,别自己猜 0.00001。 开 MT5 随便挂个 EA,把这三行打进 OnTick,用 Comment 把值打印出来,切换 EURUSD 和 XAUUSD 各看一眼,就能明白为什么通用EA不能写死数值。外汇和贵金属杠杆高、跳值波动大,参数乱填容易瞬间回撤,动手验证比看结论靠谱。
class="type">class="kw">double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); class="type">int digits = (class="type">int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); class="type">class="kw">double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
◍ 从辅助库到完整策略的演进路径
CStrategy 最初只是个轻量辅助交易开发库,本阶段目标把它推到能承载完整功能复杂策略的位置,把创建实盘策略最常用的工具内聚进去。 对交易老手来说,直接拿它写策略比反复造轮子更有价值:既能摸透这个类后续迭代的思路,也能在真实调用里暴露第八版引擎的短板。 这一节我们聚焦用面向对象的 CStrategy 类去操作交易资产,外汇与贵金属波动杠杆高,任何封装都只是降低编码成本,不降低敞口风险,上 MT5 跑之前先想清楚仓位逻辑。
旧版 CStrategy 把品种信息塞进自己怀里
EA 跑在真实交易环境里,要随时拿到账户状态、报价、时间函数和品种属性。像最小交易量、冻结水平(挂单禁止靠近市价的那段距离)这类参数,下单前少一个都可能报单失败。早期引擎把这些全堆在 CStrategy 里。 它用 COpen、CHigh、CLow、CClose、CVolume、CTime 这一组辅助类,配合 [] 索引器取 OHLC 值。后来不够用,又补了 Ask()、Bid()、Last(),再加 FreezeLevel() 查品种基础属性。类体量肉眼可见地膨胀,方法名挤在一起容易看错。 真要写多品种 EA 更麻烦。CStrategy 名义上支持多品种,但切品种得反复重设时间序列类的 Symbol,代码像在绕弯。下面这段就是老写法:先切到 EURUSD 取 Close[0],再切 GBPUSD 取 Close[0]。 塞太多品种逻辑,CStrategy 主业(编排交易行为)反被拖垮。结论很直接——品种访问该拆出去,交给独立的 CSymbol 类管。外汇和贵金属波动大、滑点随机,这种结构混乱在高波动时段更容易引出下单异常。
class="type">class="kw">double close = Close[class="num">0]; class="type">class="kw">string symbol1 = "EURUSD"; class="type">class="kw">string symbol2 = "GBPUSD"; Close.Symbol(symbol1); class="type">class="kw">double close_eurusd = Close[class="num">0]; Close.Symbol(symbol2); class="type">class="kw">double close_gbpusd = Close[class="num">0];
「WS对象与CSymbol类怎么替你省代码」
在 CStrategy 开发库里,WS 是 Working Symbol 的缩写,代表当前正在操作的交易品种对象。它不像标准 CSymbolInfo 那样只管品种属性,而是把报价、市场深度限价单信息也包进同一个对象,策略里直接用 WS 两个字母就能调,不用再分别找 CHigh、CLow 要 Ask、Bid、Last。 引擎在 EA 拿到控制权前就做好了内部初始化:记下品种和周期,建好事件跟踪(默认新分时、新柱),顺手把 WS 对象也建了。具体发生在 InitSeries 里,一行 WS.InitSeries(ExpertSymbol(), Timeframe()) 就绑定了品种和周期。 对象就绪后取数据很直观。例如当前柱最高价直接写 WS.High[0];想要在前一根柱最高价上方 3 个点挂 BuyStop,EURUSD 五位小数下只需一行:Trade.BuyStop(1.0, WS.High[1] + WS.StepToPrice(3), WS.Name())。这一行里,WS.High[1] 取前柱高点,WS.StepToPrice(3) 把 3 个点换算成价格距离,相加后由 WS.Name() 指定本品种发单。 StepToPrice 的命名容易让人误会成 tick size,其实它对应的是 MetaTrader 里的 SYMBOL_TRADE_TICK_SIZE(最小价格变动),库作者刻意改名避免和 tick value 混淆。其余方法大多沿用了 MQL5 系统命名,目标就一个:用最短路径拿到完整品种信息。外汇与贵金属杠杆高、滑点跳空频繁,挂单距离和触发价务必在 MT5 策略测试器里用真实点差验证。
CStrategy::CStrategy(class="type">void) { WS.InitSeries(ExpertSymbol(), Timeframe()); } class="type">class="kw">double max = WS.High[class="num">0]; class="type">void CMovingAverage::InitBuy(class="kw">const MarketEvent &event) { ... Trade.BuyStop(class="num">1.0, WS.High[class="num">1] + WS.StepToPrice(class="num">3), WS.Name()); ... }