admin 发表于 2025-11-16 00:27:23

【量化源码策略】布林带突破 + SAR趋势确认策略

布林带突破 + SAR趋势确认策略
这个策略的核心是等待价格突破布林带,并用SAR来确认趋势的有效性,避免假突破。

策略逻辑:
当价格突破布林带上轨,且SAR指标已经位于K线下方(确认上升趋势),则做多。
当价格突破布林带下轨,且SAR指标已经位于K线上方(确认下降趋势),则做空。

多单交易规则:

入场条件:

当前K线收盘价(或实体)上穿布林带上轨。

此时,SAR指标的点位必须已经位于当前K线(或前一根K线)的下方。

两个条件同时满足时,在下一根K线开盘时入场做多。

止损设置:

初始止损设置在最近的一个SAR点位的下方。

或者设置在入场K线最低点的下方。

止盈设置:

追踪止损: 随着行情发展,SAR点位会不断上移,当SAR点位移动到K线上方时,平仓离场。

固定目标: 当价格触及布林带中轨(20期均线)时,可平仓一半,剩余仓位继续用SAR追踪。

空单交易规则:

入场条件:

当前K线收盘价(或实体)下穿布林带下轨。

此时,SAR指标的点位必须已经位于当前K线(或前一根K线)的上方。

两个条件同时满足时,在下一根K线开盘时入场做空。

止损设置:

初始止损设置在最近的一个SAR点位的上方。

或者设置在入场K线最高点的上方。

止盈设置:

追踪止损: 随着行情发展,SAR点位会不断下移,当SAR点位移动到K线下方时,平仓离场。

固定目标: 当价格触及布林带中轨时,可平仓一半,剩余仓位继续用SAR追踪。

源码:
//+------------------------------------------------------------------+
//|                                     BB_SAR_Breakout_EA_Fixed.mq5 |
//|                                  Copyright 2023, Your Name       |
//|                                             https://www.example.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Your Name"
#property link      "https://www.example.com"
#property version   "1.1 Corrected"
#property description "布林带突破结合SAR确认的交易策略 (已修正编译错误)"

#include <Trade\Trade.mqh> // 引入官方交易库,简化交易操作

//--- 定义离场策略的枚举类型
enum ENUM_EXIT_STRATEGY
{
    EXIT_ON_SAR_FLIP,    // 当SAR反转时离场
    EXIT_ON_MIDDLE_BAND// 当价格触及布林带中轨时离场
};

//--- EA 输入参数 (可以在EA属性面板中修改)
input group             "交易参数"
input double            InpLots          = 0.01;   // 交易手数
input ulong             InpMagicNumber   = 12345;    // EA魔术号,用于区分不同EA的订单
input int               InpSlippage      = 10;       // 允许的滑点 (points)

input group             "布林带指标参数"
input int               InpBB_Period   = 20;       // 布林带周期
input double            InpBB_Deviation= 2.0;      // 布林带标准差

input group             "SAR指标参数"
input double            InpSAR_Step      = 0.02;   // SAR 步长 (Step)
input double            InpSAR_Maximum   = 0.2;      // SAR 最大值 (Maximum)

input group             "策略设置"
input ENUM_EXIT_STRATEGY InpExitStrategy = EXIT_ON_SAR_FLIP; // 选择离场策略


//--- 全局变量
CTrade trade;                     // 交易类对象
int    bb_handle;               // 布林带指标句柄
int    sar_handle;                // SAR指标句柄

//+------------------------------------------------------------------+
//| EA初始化函数                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    //--- 初始化交易对象
    trade.SetExpertMagicNumber(InpMagicNumber);
    // 修正: CTrade类中设置滑点的方法是 SetDeviationInPoints, 而不是 SetSlippage
    trade.SetDeviationInPoints(InpSlippage);
    trade.SetTypeFillingBySymbol(_Symbol); // 根据交易品种设置成交模式

    //--- 创建指标句柄
    // 创建布林带指标句柄
    bb_handle = iBands(_Symbol, _Period, InpBB_Period, 0, InpBB_Deviation, PRICE_CLOSE);
    if(bb_handle == INVALID_HANDLE)
    {
      Print("创建布林带指标失败,错误代码: ", GetLastError());
      return(INIT_FAILED);
    }

    // 创建SAR指标句柄
    sar_handle = iSAR(_Symbol, _Period, InpSAR_Step, InpSAR_Maximum);
    if(sar_handle == INVALID_HANDLE)
    {
      Print("创建SAR指标失败,错误代码: ", GetLastError());
      return(INIT_FAILED);
    }

    //--- 初始化成功
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA去初始化函数 (当EA从图表移除时调用)                     |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    //--- 释放指标句柄资源
    IndicatorRelease(bb_handle);
    IndicatorRelease(sar_handle);
}

//+------------------------------------------------------------------+
//| EA核心逻辑函数 (每个报价到来时执行)                           |
//+------------------------------------------------------------------+
void OnTick()
{
    //--- 定义用于存储指标和价格数据的数组
    // 索引 = 当前未收盘K线, 索引 = 上一根已收盘K线, 索引 = 上上根K线
    double bb_upper, bb_lower, bb_middle;
    double sar_values;
    double close_prices, high_prices, low_prices;

    //--- 从指标句柄复制数据到数组中
    // 复制布林带数据 (0: 上轨, 1: 中轨, 2: 下轨)
    if(CopyBuffer(bb_handle, 1, 0, 3, bb_upper) < 3 ||
       CopyBuffer(bb_handle, 0, 0, 3, bb_middle) < 3 || // 注意:iBands的主缓冲区(0)是中轨
       CopyBuffer(bb_handle, 2, 0, 3, bb_lower) < 3)
    {
      Print("获取布林带数据失败");
      return;
    }

    // 复制SAR数据
    if(CopyBuffer(sar_handle, 0, 0, 3, sar_values) < 3)
    {
      Print("获取SAR数据失败");
      return;
    }

    //--- 获取K线价格数据
    if(CopyClose(_Symbol, _Period, 0, 3, close_prices) < 3 ||
       CopyHigh(_Symbol, _Period, 0, 3, high_prices) < 3 ||
       CopyLow(_Symbol, _Period, 0, 3, low_prices) < 3)
    {
      Print("获取价格数据失败");
      return;
    }

    // 修正: CopyBuffer获取的数据默认就是时序的,无需使用ArraySetAsSeries,该函数会导致编译器警告

    //--- 检查是否有新K线诞生,避免在同一根K线上重复开仓
    static datetime lastBarTime = 0;
    datetime currentBarTime = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);

    bool isNewBar = false;
    if(lastBarTime != currentBarTime)
    {
      lastBarTime = currentBarTime;
      isNewBar = true;
    }

    //--- 持仓管理逻辑 (每个tick都检查)
    ManagePositions(bb_middle, sar_values, high_prices, low_prices);

    //--- 入场逻辑 (只在新K线诞生时检查一次)
    if(isNewBar)
    {
      // 如果当前没有持仓,则检查开仓信号
      if(PositionsTotal() == 0)
      {
            CheckEntrySignal(close_prices, bb_upper, bb_lower, sar_values, high_prices, low_prices);
      }
    }
}

//+------------------------------------------------------------------+
//| 检查入场信号函数                                                 |
//+------------------------------------------------------------------+
void CheckEntrySignal(const double &close[], const double &upper[], const double &lower[],
                      const double &sar[], const double &high[], const double &low[])
{
    //--- 多单入场条件检查 (在上一根K线[索引1]上检查)
    // 1. 上一根K线收盘价上穿布林带上轨
    // 2. 上一根K线的SAR点位在K线下方
    if(close > upper && sar < low)
    {
      // 计算止损位:设置为上一根K线对应的SAR点位
      double stop_loss = sar;

      // 执行开多单操作
      trade.Buy(InpLots, _Symbol, SymbolInfoDouble(_Symbol, SYMBOL_ASK), stop_loss, 0, "BB SAR Buy");
      return; // 开仓后直接返回,避免检查空单
    }

    //--- 空单入场条件检查 (在上一根K线[索引1]上检查)
    // 1. 上一根K线收盘价下穿布林带下轨
    // 2. 上一根K线的SAR点位在K线上方
    if(close < lower && sar > high)
    {
      // 计算止损位:设置为上一根K线对应的SAR点位
      double stop_loss = sar;

      // 执行开空单操作
      trade.Sell(InpLots, _Symbol, SymbolInfoDouble(_Symbol, SYMBOL_BID), stop_loss, 0, "BB SAR Sell");
      return;
    }
}

//+------------------------------------------------------------------+
//| 持仓管理函数 (止盈/平仓逻辑)                                     |
//+------------------------------------------------------------------+
void ManagePositions(const double &middle[], const double &sar[], const double &high[], const double &low[])
{
    // 如果没有持仓,直接返回
    if(PositionsTotal() == 0)
    {
      return;
    }

    // 遍历所有持仓
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
      // 获取持仓票号
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
      {
            // 筛选由本EA、在本图表交易的订单
            if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
            {
                long position_type = PositionGetInteger(POSITION_TYPE);

                //--- 多单平仓逻辑
                if(position_type == POSITION_TYPE_BUY)
                {
                  bool close_signal = false;
                  // 检查SAR反转平仓信号 (上一根K线[索引1]的SAR点已经移动到K线上方)
                  if(InpExitStrategy == EXIT_ON_SAR_FLIP && sar > high)
                  {
                        close_signal = true;
                  }
                  // 检查触及中轨平仓信号 (当前价格的低点[索引0]触及或低于中轨)
                  if(InpExitStrategy == EXIT_ON_MIDDLE_BAND && low <= middle)
                  {
                        close_signal = true;
                  }

                  if(close_signal)
                  {
                        // 修正: PositionClose的第二个参数是滑点, 不是注释。此处省略以使用默认滑点。
                        trade.PositionClose(ticket);
                  }
                }

                //--- 空单平仓逻辑
                else if(position_type == POSITION_TYPE_SELL)
                {
                  bool close_signal = false;
                  // 检查SAR反转平仓信号 (上一根K线[索引1]的SAR点已经移动到K线下方)
                  if(InpExitStrategy == EXIT_ON_SAR_FLIP && sar < low)
                  {
                        close_signal = true;
                  }
                  // 检查触及中轨平仓信号 (当前价格的高点[索引0]触及或高于中轨)
                  if(InpExitStrategy == EXIT_ON_MIDDLE_BAND && high >= middle)
                  {
                        close_signal = true;
                  }

                  if(close_signal)
                  {
                        // 修正: PositionClose的第二个参数是滑点, 不是注释。此处省略以使用默认滑点。
                        trade.PositionClose(ticket);
                  }
                }
            }
      }
    }
}
//+------------------------------------------------------------------+


页: [1]
查看完整版本: 【量化源码策略】布林带突破 + SAR趋势确认策略

人生者,生存也。
生存所需,财(钱)官(权)也。
财自食伤(子孙福德爻)生,
印星生身荫护,官财印全,适合人间生存也。
若命缺财,缺官,则失去生存权。
一旦脱离父母养育,将天天面临,
从绝望中醒来,又从绝望中睡去。来去如烟!