跨平台的EA交易: 资金管理·综合运用
(3/3)·固定手数到固定风险只是开始,真正难的是让EA在MQL4与MQL5间无缝换算法
双平台跑出来的手数差异在哪
把前一篇的订单管理例子接着改:在 OnInit 里建一个 CMoneys 容器,把固定手数和固定比例两种资金管理方法塞进去,再挂到订单管理器上。固定分数、固定风险、固定每点风险这三类因为要求非零止损,而当前 EA 进场止损还是 0,暂时只能注释掉;万一它们返回了小于 0 的无效止损,订单管理器会回退到 m_lotsize 默认的 0.1 手。 OnTick 里做了一点区分:买入仓走固定手数(0.05),卖出仓走固定比例。切换动作发生在调用 TradeOpen 之前,用 CMoneys 的 Selected 方法改一下当前选中项即可。因为资金管理只是纯算术,MT4 和 MT5 算出的手数应当一致——差异只可能来自订单管理器自身对两个平台的适配。 回测数据能印证这一点。MT4 前 10 笔里,卖出仓全是 1.00 手、买入仓全是 0.05 手;2017.01.02 至 01.06 的 EURUSD 测试,余额从 10000 走到 10401.84,第 9 笔卖出平仓亏了 170.00。MT5 对冲模式同周期前 10 笔订单,Volume 列显示卖出 1.00 / 买入 0.05 的节奏完全一样,开启价也基本重合(如 01.03 03:00 买入 1.04669 对 MT4 的 1.04679,微小滑点级偏差)。 外汇与贵金属杠杆交易风险较高,0.1 手默认仓在 170 点不利波动下可能吞掉账户 1.7% 以上,实盘前务必在策略测试器里重跑这套 OnInit 逻辑。
<span class="keyword">class="type">int</span> <span class="functions">OnInit</span>() { <span class="comment">class=class="str">"cmt">//---</span> order_manager=<span class="keyword">new</span> COrderManager(); money_manager = <span class="keyword">new</span> CMoneys(); CMoney *money_fixed= <span class="keyword">new</span> CMoneyFixedLot(<span class="number">class="num">0.05</span>); <span class="comment">class=class="str">"cmt">//CMoney *money_ff= new CMoneyFixedFractional(class="num">5);</span> CMoney *money_ratio= <span class="keyword">new</span> CMoneyFixedRatio(<span class="number">class="num">0</span>,<span class="number">class="num">0.1</span>,<span class="number">class="num">1000</span>); <span class="comment">class=class="str">"cmt">//CMoney *money_riskperpoint= new CMoneyFixedRiskPerPoint(class="num">0.1);</span> <span class="comment">class=class="str">"cmt">//CMoney *money_risk= new CMoneyFixedRisk(class="num">100);</span> money_manager.Add(money_fixed); <span class="comment">class=class="str">"cmt">//money_manager.Add(money_ff);</span> money_manager.Add(money_ratio); <span class="comment">class=class="str">"cmt">//money_manager.Add(money_riskperpoint);</span> <span class="comment">class=class="str">"cmt">//money_manager.Add(money_risk);</span> order_manager.AddMoneys(money_manager); <span class="comment">class=class="str">"cmt">//order_manager.Account(money_manager);</span> symbol_manager=<span class="keyword">new</span> CSymbolManager(); symbol_info=<span class="keyword">new</span> CSymbolInfo(); <span class="keyword">if</span>(!symbol_info.Name(<span class="functions">Symbol</span>())) <span class="functions">Print</span>(<span class="class="type">class="kw">string">"没有设置交易品种"</span>); symbol_manager.Add(<span class="functions">GetPointer</span>(symbol_info)); order_manager.Init(symbol_manager,<span class="keyword">new</span> CAccountInfo()); <span class="predefines">class="type">MqlParam</span> params[<span class="number">class="num">1</span>]; params[<span class="number">class="num">0</span>].type=<span class="macro">TYPE_STRING</span>; <span class="preprocessor">class="macro">#ifdef </span><span class="macro">__MQL5__</span> params[<span class="number">class="num">0</span>].string_value=<span class="class="type">class="kw">string">"Examples\\Heiken_Ashi"</span>; <span class="preprocessor">class="macro">#else </span>params[<span class="number">class="num">0</span>].string_value=<span class="class="type">class="kw">string">"Heiken Ashi"</span>; <span class="preprocessor">class="macro">#endif </span>SignalHA *signal_ha=<span class="keyword">new</span> SignalHA(<span class="functions">Symbol</span>(),<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,params,signal_bar);
「把 HA 与均线信号塞进同一引擎」
这段初始化把 Heikin Ashi 信号和一条自定义均线信号挂到同一个 CSignals 容器里,两个信号对象都用 GetPointer 传引用,避免 EA 在 OnTick 里反复 new 对象造成内存碎片。signals.Init 接的是 symbol_manager 指针,第二参传 NULL 表示不绑定外部时间过滤器。 OnTick 里先 RefreshRates 再 signals.Check(),顺序不能反——汇率没刷新的话 CheckOpenLong / CheckOpenShort 读到的仍是上一 tick 的卖买价,可能在 XAUUSD 这种点差跳变快的品种上误触发。 进场分支里 close_last() 在开新单前平掉旧仓,money_manager.Selected(0/1) 切换多空对应的仓位比例模板。外汇与贵金属杠杆高,这种无止损挂单逻辑在重大数据行情中可能因滑点扩大而浮亏加速,仅适合在 MT5 策略测试器里先跑历史验证。
SignalMA *signal_ma=new SignalMA(Symbol(),(ENUM_TIMEFRAMES) Period(),maperiod,class="num">0,mamethod,maapplied,signal_bar); signals=new CSignals(); signals.Add(GetPointer(signal_ha)); signals.Add(GetPointer(signal_ma)); signals.Init(GetPointer(symbol_manager),NULL); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class="type">void OnTick() { class=class="str">"cmt">//--- if(symbol_info.RefreshRates()) { signals.Check(); if(signals.CheckOpenLong()) { close_last(); class=class="str">"cmt">//Print("买入交易进场.."); money_manager.Selected(class="num">0); order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask()); } else if(signals.CheckOpenShort()) { close_last(); class=class="str">"cmt">//Print("卖出交易进场.."); money_manager.Selected(class="num">1); order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid()); } } }
◍ 别急着下结论
这套跨平台 EA 把五种资金管理方法封进统一的指针容器,运行时能动态切换,省去重写下单逻辑的麻烦。附带的 mm_ha_ma.zip 约 1.04 MB、tester_results.zip 约 0.29 MB,可直接丢进 MT5 回测验证各类方法在 EURUSD 15M 上的权益曲线差异。 手数公式里 base_volume 是底仓,balance_increase 定义资金阶梯,volume_increment 控制每跨一阶加多少量,调这三个数就能改变激进程度。外汇和贵金属杠杆高,杠杆放大盈亏,回测好看不等于实盘稳,先在策略测试器跑三个月再上真金。 读者问过按净值百分比算手数、或按连亏次数加仓的变体,原类留了接口,自己继承改几行就能接上。代码跑通之前,别假定某种方法永远优于另一种。
Volume = base_volume + (balance / balance_increase) * volume_increment