找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
首页论坛EAHub 外汇论坛
查看: 12|回复: 0

布林带均值回归策略,仅供学习参考,参数和交易条件可以自行优化

[复制链接]

4万

主题

54

回帖

4万

积分

管理员

积分
45954
发表于 2026-8-19 00:46:00 | 显示全部楼层 |阅读模式
#property description "布林带均值回归策略 - Bollinger Bands Mean Reversion Strategy"






//--- 包含标准交易库

#include <Trade\Trade.mqh>


//--- 输入参数

input group "=== 基础参数 ==="

input double   InpLotSize = 0.01;          // 手数(适配股票)

input int      InpStopLoss = 500;          // 止损点数(适配股票)

input int      InpTakeProfit = 1000;       // 止盈点数(适配股票)

input int      InpMagicNumber = 12345;     // 魔术数字


input group "=== 风险管理 ==="

input int      InpMaxSpread = 200;         // 最大允许点差(适配股票)

input double   InpMaxRisk = 0.01;          // 最大风险比例(保守)

input int      InpMaxPositions = 1;        // 最大持仓数量


input group "=== 布林带参数 ==="

input int      InpBandsPeriod = 20;        // 布林带周期

input double   InpBandsDeviation = 2.0;    // 标准差倍数

input int      InpBandsShift = 0;          // 指标偏移

input ENUM_MA_METHOD InpBandsMethod = MODE_SMA; // 移动平均方法

input ENUM_APPLIED_PRICE InpBandsPrice = PRICE_CLOSE; // 应用价格


input group "=== 信号参数 ==="

input double   InpEntryThreshold = 0.1;    // 进入信号阈值(0-1)

input double   InpExitThreshold = 0.5;     // 退出信号阈值(0-1)

input bool     InpReverseSignals = false;  // 反转信号


//--- 全局变量

double         g_point_value;              // 点值

datetime       g_last_trade_time;          // 上次交易时间


//--- 交易对象

CTrade         g_trade;


//--- 布林带策略特定变量

int            g_bands_handle;             // 布林带句柄

double         g_upper_buffer[];           // 上轨缓冲区

double         g_middle_buffer[];          // 中轨缓冲区  

double         g_lower_buffer[];           // 下轨缓冲区


//+------------------------------------------------------------------+

//| Expert initialization function                                   |

//+------------------------------------------------------------------+

int OnInit()

{

    // 基础初始化

    if(!BaseInitialization())

        return INIT_FAILED;


    // 策略特定初始化

    if(!StrategyInitialization())

        return INIT_FAILED;


    Print("策略初始化成功: ", _Symbol);

    return INIT_SUCCEEDED;

}


//+------------------------------------------------------------------+

//| Expert deinitialization function                                 |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

{

    // 策略特定清理

    StrategyDeinitialization();


    // 基础清理

    BaseDeinitialization();


    Print("策略已停止运行");

}


//+------------------------------------------------------------------+

//| Expert tick function                                             |

//+------------------------------------------------------------------+

void OnTick()

{

    // 基础检查

    if(!BaseChecks())

        return;


    // 策略信号检查

    int signal = GetStrategySignal();


    if(signal != 0)

    {

        // 检查是否可以开仓

        if(CanOpenPosition(signal))

        {

            // 执行交易

            if(signal > 0)

                OpenBuyPosition();

            else

                OpenSellPosition();

        }

    }


    // 管理现有持仓

    ManagePositions();

}


//+------------------------------------------------------------------+

//| 基础初始化                                                        |

//+------------------------------------------------------------------+

bool BaseInitialization()

{

    // 设置交易对象

    g_trade.SetExpertMagicNumber(InpMagicNumber);

    g_trade.SetMarginMode();

    g_trade.SetTypeFillingBySymbol(_Symbol);

    g_trade.SetDeviationInPoints(10);


    // 获取点值

    g_point_value = _Point;

    if(_Digits == 5 || _Digits == 3)

        g_point_value = _Point * 10;


    // 初始化时间

    g_last_trade_time = 0;


    // 验证输入参数

    if(InpLotSize <= 0)

    {

        Print("错误: 手数必须大于0");

        return false;

    }


    if(InpMagicNumber <= 0)

    {

        Print("错误: 魔术数字必须大于0");

        return false;

    }


    // 显示品种基本信息

    Print("✅ 品种信息:");

    Print("  品种: ", _Symbol);

    Print("  最小手数: ", SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN));

    Print("  最大手数: ", SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX));

    Print("  手数步长: ", SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP));


    return true;

}


//+------------------------------------------------------------------+

//| 布林带策略初始化                                                    |

//+------------------------------------------------------------------+

bool StrategyInitialization()

{

    // 创建布林带指标句柄

    g_bands_handle = iBands(_Symbol, PERIOD_CURRENT,

                           InpBandsPeriod, InpBandsShift,

                           InpBandsDeviation, InpBandsPrice);


    if(g_bands_handle == INVALID_HANDLE)

    {

        Print("创建布林带指标失败");

        return false;

    }


    // 设置缓冲区为时间序列

    ArraySetAsSeries(g_upper_buffer, true);

    ArraySetAsSeries(g_middle_buffer, true);

    ArraySetAsSeries(g_lower_buffer, true);


    // 验证参数

    if(InpBandsPeriod < 2 || InpBandsPeriod > 200)

    {

        Print("错误: 布林带周期必须在2-200之间");

        return false;

    }


    if(InpBandsDeviation <= 0 || InpBandsDeviation > 5)

    {

        Print("错误: 标准差倍数必须在0-5之间");

        return false;

    }


    if(InpEntryThreshold < 0 || InpEntryThreshold > 1)

    {

        Print("错误: 进入信号阈值必须在0-1之间");

        return false;

    }


    Print("布林带策略初始化成功");

    Print("布林带周期: ", InpBandsPeriod);

    Print("标准差倍数: ", InpBandsDeviation);

    Print("进入阈值: ", InpEntryThreshold);

    Print("退出阈值: ", InpExitThreshold);


    return true;

}


//+------------------------------------------------------------------+

//| 基础清理                                                          |

//+------------------------------------------------------------------+

void BaseDeinitialization()

{

    // 基础清理工作

}


//+------------------------------------------------------------------+

//| 布林带策略清理                                                      |

//+------------------------------------------------------------------+

void StrategyDeinitialization()

{

    // 释放布林带指标句柄

    if(g_bands_handle != INVALID_HANDLE)

    {

        IndicatorRelease(g_bands_handle);

        Print("布林带指标句柄已释放");

    }

}


//+------------------------------------------------------------------+

//| 基础检查                                                          |

//+------------------------------------------------------------------+

bool BaseChecks()

{

    // 检查市场状态

    if(!IsMarketOpen())

        return false;


    // 检查点差

    if(!IsSpreadAcceptable())

        return false;


    return true;

}


//+------------------------------------------------------------------+

//| 获取布林带均值回归信号                                                |

//+------------------------------------------------------------------+

int GetStrategySignal()

{

    // 复制布林带数据

    if(CopyBuffer(g_bands_handle, 0, 0, 3, g_middle_buffer) < 3 ||

       CopyBuffer(g_bands_handle, 1, 0, 3, g_upper_buffer) < 3 ||

       CopyBuffer(g_bands_handle, 2, 0, 3, g_lower_buffer) < 3)

    {

        Print("复制布林带数据失败");

        return 0;

    }


    // 获取当前价格

    double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    double ask_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);


    // 获取布林带值

    double upper_current = g_upper_buffer[0];

    double middle_current = g_middle_buffer[0];  

    double lower_current = g_lower_buffer[0];


    // 计算价格在布林带中的相对位置 (0=下轨, 0.5=中轨, 1=上轨)

    double band_width = upper_current - lower_current;

    if(band_width <= 0)

        return 0;


    double price_position = (current_price - lower_current) / band_width;


    // 均值回归信号逻辑

    int signal = 0;


    if(!InpReverseSignals)

    {

        // 正常均值回归: 价格偏离均值时反向操作

        if(price_position <= InpEntryThreshold)

        {

            // 价格接近下轨,预期回归,买入信号

            signal = 1;

        }

        else if(price_position >= (1.0 - InpEntryThreshold))

        {

            // 价格接近上轨,预期回归,卖出信号  

            signal = -1;

        }

    }

    else

    {

        // 反转信号: 用于突破策略

        if(price_position >= (1.0 - InpEntryThreshold))

        {

            // 价格突破上轨,买入信号

            signal = 1;

        }

        else if(price_position <= InpEntryThreshold)

        {

            // 价格突破下轨,卖出信号

            signal = -1;

        }

    }


    // 添加趋势过滤 - 检查中轨斜率

    double middle_prev = g_middle_buffer[1];

    double middle_prev2 = g_middle_buffer[2];


    // 计算中轨趋势

    bool uptrend = (middle_current > middle_prev) && (middle_prev > middle_prev2);

    bool downtrend = (middle_current < middle_prev) && (middle_prev < middle_prev2);


    // 趋势过滤: 在强趋势中减少反向信号

    if(!InpReverseSignals)

    {

        if(signal == -1 && uptrend)

            signal = 0;  // 上升趋势中不做空

        if(signal == 1 && downtrend)

            signal = 0;  // 下降趋势中不做多

    }


    // 调试输出

    if(signal != 0)

    {

        Print("布林带信号: ", signal > 0 ? "BUY" : "SELL");

        Print("价格位置: ", DoubleToString(price_position, 3));

        Print("上轨: ", DoubleToString(upper_current, _Digits));

        Print("中轨: ", DoubleToString(middle_current, _Digits));

        Print("下轨: ", DoubleToString(lower_current, _Digits));

        Print("当前价: ", DoubleToString(current_price, _Digits));

    }


    return signal;

}


//+------------------------------------------------------------------+

//| 检查市场是否开放                                                    |

//+------------------------------------------------------------------+

bool IsMarketOpen()

{

    MqlTick tick;

    if(!SymbolInfoTick(_Symbol, tick))

        return false;


    return tick.time > 0;

}


//+------------------------------------------------------------------+

//| 检查点差是否可接受                                                  |

//+------------------------------------------------------------------+

bool IsSpreadAcceptable()

{

    long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);

    if(spread > InpMaxSpread)

    {

        static datetime last_warning = 0;

        if(TimeCurrent() - last_warning > 300) // 每5分钟警告一次

        {

            Print("点差过大: ", spread, " > ", InpMaxSpread);

            last_warning = TimeCurrent();

        }

        return false;

    }

    return true;

}


//+------------------------------------------------------------------+

//| 检查是否可以开仓                                                    |

//+------------------------------------------------------------------+

bool CanOpenPosition(int signal)

{

    // 检查最大持仓数量

    if(CountPositions() >= InpMaxPositions)

        return false;


    // 检查时间间隔(避免频繁交易)

    if(TimeCurrent() - g_last_trade_time < 60) // 1分钟间隔

        return false;


    // 检查账户余额

    double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);

    double required_margin = 0;


    if(!OrderCalcMargin(signal > 0 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL,

                       _Symbol, InpLotSize,

                       SymbolInfoDouble(_Symbol, signal > 0 ? SYMBOL_ASK : SYMBOL_BID),

                       required_margin))

        return false;


    if(required_margin > free_margin * 0.5) // 不使用超过50%的自由保证金

        return false;


    return true;

}


//+------------------------------------------------------------------+

//| 开多头仓位                                                         |

//+------------------------------------------------------------------+

bool OpenBuyPosition()

{

    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    double sl = InpStopLoss > 0 ? ask - InpStopLoss * g_point_value : 0;

    double tp = InpTakeProfit > 0 ? ask + InpTakeProfit * g_point_value : 0;


    // 规范化交易量

    double volume = InpLotSize;

    double min_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

    double max_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);

    double vol_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);


    // 确保音量符合要求

    volume = MathMax(volume, min_vol);

    volume = MathMin(volume, max_vol);

    volume = MathRound(volume / vol_step) * vol_step;


    // 调试输出

    Print("
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|EAHub外汇论坛

GMT+8, 2026-10-9 23:30 , Processed in 0.172844 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表