使用MQL5实现抛物线SAR趋势策略的自动化交易:打造高效的EA·进阶篇
📘

使用MQL5实现抛物线SAR趋势策略的自动化交易:打造高效的EA·进阶篇

第 2/3 篇

「用抛物线 SAR 给开仓定方向」

抛物线 SAR 是 MT5 里判断趋势惯性的轻量指标,核心逻辑就一句:收盘价在 SAR 上方偏多、下方偏空。把这条规则写进 EA,就能在无持仓时自动选边。 下面这段 MQL5 先取当前品种和周期的 SAR 值与上一根 K 线收盘价,再做方向判断: double sar = iSAR(_Symbol, _Period, SAR_Step, SAR_Maximum); // 计算抛物线SAR数值,步长与上限由外部参数传入 double lastPrice = iClose(_Symbol, _Period, 1); // 取前一根蜡烛的收盘价,避免用未闭合的当前线 if(!IsPositionOpen(_Symbol)) // 检查当前品种是否已有持仓,无仓才进场 if(lastPrice > sar) // 昨收在SAR之上 OpenBuyOrder(); // 开多 else if(lastPrice < sar) // 昨收在SAR之下 OpenSellOrder(); // 开空 实盘里 EURUSD 在 H1 周期用默认步长 0.02、上限 0.2 时,SAR 翻转往往滞后 1~2 根 K 线,追单容易吃假突破。外汇与贵金属杠杆高,这类裸 SAR 信号仅作方向参考,仓位要控小。 开 MT5 把 iSAR 拖到图表,对照上面逻辑改两行参数,就能看信号翻转是否和你手感一致。

MQL5 / C++
class="type">class="kw">double sar = iSAR(_Symbol, _Period, SAR_Step, SAR_Maximum); class=class="str">"cmt">// Calculate the Parabolic SAR value
class="type">class="kw">double lastPrice = iClose(_Symbol, _Period, class="num">1); class=class="str">"cmt">// Get the close price of the previous candle
class=class="str">"cmt">//--- Check if there is an existing position
  if(!IsPositionOpen(_Symbol))
class=class="str">"cmt">//--- Determine trade direction based on SAR
    if(lastPrice > sar)
      {
       OpenBuyOrder(); class=class="str">"cmt">// If the last price is above the SAR, open a buy order
      }
    else
      if(lastPrice < sar)
        {
         OpenSellOrder(); class=class="str">"cmt">// If the last price is below the SAR, open a sell order
        }
  }

SAR 翻转触发开仓的判定次序

抛物线转向指标(SAR)的核心用法之一,是把价格与 SAR 值的相对位置当作趋势开关。当最新价向上越过 SAR,系统读为上升波段开启,倾向调用买入函数;当最新价向下跌破 SAR,则读为下降波段,倾向调用卖出函数。外汇与贵金属杠杆高,这种翻转信号失效时回撤会很快,只能当作概率倾向而非确定性。 这段逻辑在代码里是顺序判断,先测买入再测卖出:只要 lastprice > sar 成立就直接开多,不往下走;只有该条件不成立,才进入 lastPrice < sar 的分支去开空。实盘里意味着同一根 K 线不会既测多又测空,避免重复发单。 你可以把下列片段直接丢进 MT5 的 EA 初始化逻辑后跑一遍,观察在 EURUSD 的 M15 上 SAR 翻转当根 K 线的成交偏向,验证优先买入是否如预期压过卖出分支。

MQL5 / C++
if(lastprice > sar)
   OpenBuyOrder();
else if(lastPrice < sar)
   OpenSellOrder();

◍ 持仓自检与双向下单的函数骨架

先说 IsPositionOpen 这个布尔函数,它接收一个 string 类型的 symbol 参数,用来判断当前图表品种是否已有持仓。内部先调 PositionsTotal() 拿到终端持仓总数,再用 for 循环从 0 跑到 total-1,逐个取 PositionGetSymbol(i) 比对 _Symbol。一旦匹配就 return true,循环走完没撞上就 return false,逻辑直接拦住同品种重复开仓。 比如你在 EURUSD 图表上跑 EA,函数返回 true 时策略就倾向只管已有仓位、不再新开。外汇和贵金属波动快、杠杆高,这种防重复机制能降低因逻辑漏洞导致的双向加仓风险。 OpenBuyOrder 和 OpenSellOrder 都是 void 型,不返回值只做执行。Buy 这边用 trade.Buy(LotSize, NULL, 0, 0, 0, "Buy Order"):第二参 NULL 吃当前图表品种,三、四、五参全 0 代表市价、无止损、无止盈,当前示例手数 LotSize 为 0.2。成功就 Print 成功信息,失败走 GetLastError() 把错误码打出来方便排查。 Sell 一侧结构对称,trade.Sell 参数排布完全一致,注释换成 "Sell order"。两个函数都不设初始止损止盈,实盘前你大概率要把那两个 0 改成基于 ATR 或固定点的数值,否则裸奔市价单在贵金属跳空时可能亏得超出预期。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Check if there is an open position for the given symbol              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool IsPositionOpen(class="type">class="kw">string symbol)
  {
   class="type">uint total=PositionsTotal();                                      class=class="str">"cmt">//Get the total number of open positions
   for(class="type">uint i=class="num">0; i<total; i++)
     {
      class="type">class="kw">string POSITION_SYMBOL=PositionGetSymbol(i);                    class=class="str">"cmt">//Get the symbol of the position
      if(POSITION_SYMBOL == _Symbol)                                 class=class="str">"cmt">//Check if the symbol matches the current symbol
        {
         class="kw">return true;
        }
     }
   class="kw">return class="kw">false;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Open a Buy order                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OpenBuyOrder()
  {
   if(trade.Buy(LotSize, NULL, class="num">0, class="num">0, class="num">0, "Buy Order"))
     {
      Print("Buy order placed successfully");
     }
   else
     {
      Print("Error placing buy order: ", GetLastError());
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Open a Sell order                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OpenSellOrder()
  {

「把卖出信号接进下单函数」

上面那段 if(trade.Sell(...)) 是 EA 里真正触发空单的落点:用 CTrade 类的 Sell 方法,手数走 LotSize 输入变量,品种填 NULL 表示当前图表品种,三个 0 分别对应挂单价、止损、止盈——全 0 即市价单不设防护。 成功就 Print 一条日志,失败则把 GetLastError() 的错误码打出来,这一步在 MT5 回测里能直接看到下单失败原因,比盲调参数省时间。 文件头部的输入参数给了可验证的默认值:LotSize=0.2SAR_Step=0.02SAR_Maximum=0.2Slippage=3。外汇与贵金属杠杆高,0.2 手在迷你账户可能占保证金过半,实盘前务必按自己账户校准。 OnInit 只打印初始化成功,OnDeinit 打印卸载,OnTick 开头留了获取 SAR 值的注释位——也就是说本段并没把 SAR 翻转判断写进去,只展示了下单壳,真正信号过滤得你自己补。

MQL5 / C++
if(trade.Sell(LotSize, NULL, class="num">0, class="num">0, class="num">0, "Sell Order"))
  {
    Print("Sell order placed successfully");
  }
  else
  {
    Print("Error placing sell order: ", GetLastError());
  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     ParabolicSAR_EA.mq5 |
class=class="str">"cmt">//|     Copyright class="num">2024, MetaQuotes Ltd."Duke" |
class=class="str">"cmt">//|                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." "Duke"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#include <Trade\Trade.mqh>
class=class="str">"cmt">//--- Input parameters
class="kw">input class="type">class="kw">double LotSize = class="num">0.2;           class=class="str">"cmt">// Lot size for trading
class="kw">input class="type">class="kw">double SAR_Step = class="num">0.02;         class=class="str">"cmt">// Parabolic SAR step
class="kw">input class="type">class="kw">double SAR_Maximum = class="num">0.2;       class=class="str">"cmt">// Parabolic SAR maximum
class="kw">input class="type">int Slippage = class="num">3;               class=class="str">"cmt">// Slippage for orders
class=class="str">"cmt">//--- Trade object
CTrade trade;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- Initialization code here
   Print("Parabolic SAR EA Initialized");
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
class=class="str">"cmt">//--- Cleanup code here
   Print("Parabolic SAR EA Deinitialized");
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//--- Get the Parabolic SAR value

常见问题

看SAR点位置:点在K线下方倾向多头,上方倾向空头。开仓前先确认SAR已翻转再定方向,避免逆势。
不能。判定次序应是先SAR翻转、再价格确认、最后查持仓空仓才触发,少一步都容易吃假信号。
可以。小布盯盘的AIGC会识别对应品种SAR状态,翻转时直接推送信号,你不用自己写代码也能看盘口。
先遍历订单查同品种有无持仓,无仓才按信号调用双向开仓函数;卖出信号接进下单分支即可,注意手数校验。
风险偏高。SAR在震荡中会频繁翻转,贵金属波幅大易滑点,建议先用历史数据回测再上实盘。