黄金布林带马丁EA·回本优先版
布林中轨交叉开仓,浮盈覆盖累计亏损立即平仓,否则按布林带止盈+移动止损
MT5 真机运行截图 · 服务器把编译好的 EX5 挂到黄金 H1 图表上截的,你装上去看到的就是这个样子
#property copyright "XAUOF 指标工坊"
#property version "2.00"
#property strict
#include <Trade\Trade.mqh>
// ==================== 输入参数 ====================
input double InpBaseLot = 0.01; // 初始开仓手数(推荐0.01)
input double InpLotStep = 0.01; // 每次亏损后增加的手数(推荐0.01)
input int InpBandsPeriod = 20; // 布林带计算周期(推荐20)
input double InpBandsDeviation = 2.0; // 布林带标准差倍数(推荐2.0)
input int InpTrailingStopPts = 100; // 移动止损点数,黄金1点=0.01美元(推荐100)
input ulong InpMagicNumber = 20261002;// EA魔术编号,用于区分不同EA的订单
// ==================== 全局变量 ====================
CTrade trade; // MT5交易对象
int bandsHandle; // 布林带指标句柄
double g_totalLoss; // 记录连续亏损的总金额(正数),用于判断是否回本
int g_lossCount; // 连续亏损次数,用于计算当前手数
//+------------------------------------------------------------------+
//| EA初始化 |
//+------------------------------------------------------------------+
int OnInit()
{
// 创建布林带指标句柄
bandsHandle = iBands(_Symbol, PERIOD_M5, InpBandsPeriod, 0, InpBandsDeviation, PRICE_CLOSE);
if(bandsHandle == INVALID_HANDLE)
{
Print("错误:无法创建布林带指标句柄,请检查参数!");
return INIT_FAILED;
}
// 配置交易对象
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(30);
trade.SetTypeFilling(ORDER_FILLING_IOC);
// 初始化马丁格尔状态
g_totalLoss = 0.0;
g_lossCount = 0;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| EA卸载 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(bandsHandle != INVALID_HANDLE)
IndicatorRelease(bandsHandle);
}
//+------------------------------------------------------------------+
//| 每个Tick触发 |
//+------------------------------------------------------------------+
void OnTick()
{
// 获取当前持仓数量
int posCount = CountMyPositions();
// 如果有持仓,先管理:检查回本平仓、移动止损
if(posCount > 0)
{
ManageOpenPositions();
return; // 每次只持1单
}
// 没有持仓时,检查是否需要开新仓
if(Bars(_Symbol, PERIOD_M5) < InpBandsPeriod + 5) return;
// 拷贝布林带数据:上轨、中轨、下轨
double upper[], middle[], lower[];
if(CopyBuffer(bandsHandle, 0, 0, 3, middle) < 3) return; // 中轨
if(CopyBuffer(bandsHandle, 1, 0, 3, upper) < 3) return; // 上轨
if(CopyBuffer(bandsHandle, 2, 0, 3, lower) < 3) return; // 下轨
// 拷贝收盘价
double close[];
if(CopyClose(_Symbol, PERIOD_M5, 0, 3, close) < 3) return;
// 前一根完成的K线索引是1,再前一根是0
int prev = 1;
int prev2 = 0;
// 判断K线是否上穿或下穿布林带中轨
bool crossUp = (close[prev] > middle[prev]) && (close[prev2] <= middle[prev2]);
bool crossDn = (close[prev] < middle[prev]) && (close[prev2] >= middle[prev2]);
// 计算当前应该开仓的手数
double currentLot = InpBaseLot + g_lossCount * InpLotStep;
currentLot = NormalizeLot(currentLot);
// 获取当前价格
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// 上穿中轨 -> 开多单
if(crossUp)
{
// 止盈 = 上轨与中轨的差值
double tpDistance = upper[prev] - middle[prev];
if(tpDistance <= 0) tpDistance = point * 100; // 防止差值为0
double tp = ask + tpDistance;
// 初始不设固定止损,靠移动止损管理
double sl = 0;
if(trade.Buy(currentLot, _Symbol, ask, sl, tp, "Boll_Up"))
{
Print("开多单成功,手数:", currentLot, " 止盈距离:", tpDistance, " 累计亏损:", g_totalLoss);
}
}
// 下穿中轨 -> 开空单
else if(crossDn)
{
// 止盈 = 中轨与下轨的差值
double tpDistance = middle[prev] - lower[prev];
if(tpDistance <= 0) tpDistance = point * 100;
double tp = bid - tpDistance;
double sl = 0;
if(trade.Sell(currentLot, _Symbol, bid, sl, tp, "Boll_Dn"))
{
Print("开空单成功,手数:", currentLot, " 止盈距离:", tpDistance, " 累计亏损:", g_totalLoss);
}
}
}
//+------------------------------------------------------------------+
//| 管理当前持仓:回本平仓 + 移动止损 |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double trailDist = InpTrailingStopPts * point; // 移动止损距离
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != (long)InpMagicNumber) continue;
long type = PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
double volume = PositionGetDouble(POSITION_VOLUME);
double profit = PositionGetDouble(POSITION_PROFIT);
double swap = PositionGetDouble(POSITION_SWAP);
double commission = PositionGetDouble(POSITION_COMMISSION);
// 当前净盈亏 = 浮动盈亏 + 隔夜利息 + 手续费
double netProfit = profit + swap + commission;
// 【核心逻辑1】若有累计亏损,且当前浮盈能够覆盖,立即平仓
if(g_totalLoss > 0 && netProfit >= g_totalLoss)
{
Print("浮盈(", netProfit, ")已覆盖累计亏损(", g_totalLoss, "),立即平仓重置!");
if(trade.PositionClose(ticket))
{
// 重置马丁格尔状态
g_totalLoss = 0.0;
g_lossCount = 0;
}
continue;
}
// 【核心逻辑2】如果还没达到回本条件,执行移动止损
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY)
{
// 多单移动止损:当价格涨过开仓价 + 移动止损距离时,开始跟踪
double newSL = bid - trailDist;
// 新止损必须大于原止损,且大于开仓价
if(newSL > currentSL + point && newSL > openPrice)
{
trade.PositionModify(ticket, newSL, currentTP);
Print("多单移动止损更新至:", newSL);
}
}
else if(type == POSITION_TYPE_SELL)
{
// 空单移动止损:当价格跌破开仓价 - 移动止损距离时,开始跟踪
double newSL = ask + trailDist;
if((currentSL == 0 || newSL < currentSL - point) && newSL < openPrice)
{
trade.PositionModify(ticket, newSL, currentTP);
Print("空单移动止损更新至:", newSL);
}
}
}
}
//+------------------------------------------------------------------+
//| 统计本EA当前的持仓数量 |
//+------------------------------------------------------------------+
int CountMyPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == (long)InpMagicNumber)
{
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 监听交易事件,处理订单平仓后的马丁格尔状态更新 |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
// 只关注历史订单新增(平仓)
if(trans.type != TRADE_TRANSACTION_DEAL_ADD) return;
ulong dealTicket = trans.deal;
if(dealTicket == 0) return;
// 选中历史订单才能读取属性
if(!HistoryDealSelect(dealTicket)) return;
// 过滤出本EA的平仓单
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != (long)InpMagicNumber) return;
long entry = HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
// 只处理出场单
if(entry != DEAL_ENTRY_OUT) return;
double profit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
double swap = HistoryDealGetDouble(dealTicket, DEAL_SWAP);
double commission = HistoryDealGetDouble(dealTicket, DEAL_COMMISSION);
double netProfit = profit + swap + commission;
if(netProfit < 0)
{
// 亏损:累加亏损金额,增加亏损次数,下次开仓手数变大
g_totalLoss += MathAbs(netProfit);
g_lossCount++;
Print("本单亏损:", netProfit, " 累计亏损:", g_totalLoss, " 下次手数:",
InpBaseLot + g_lossCount * InpLotStep);
}
else if(netProfit > 0)
{
// 盈利(包括回本平仓和正常止盈):重置马丁格尔状态
g_totalLoss = 0.0;
g_lossCount = 0;
Print("本单盈利:", netProfit, " 马丁格尔状态已重置,下次从0.01手开始。");
}
else
{
// 盈亏为0(极少见),也视为重置
g_totalLoss = 0.0;
g_lossCount = 0;
Print("本单盈亏为0,马丁格尔状态已重置。");
}
}
//+------------------------------------------------------------------+
//| 规范化手数 |
//+------------------------------------------------------------------+
double NormalizeLot(double lot)
{
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(stepLot <= 0) stepLot = 0.01;
lot = MathRound(lot / stepLot) * stepLot;
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
return NormalizeDouble(lot, 2);
}
//+------------------------------------------------------------------+作品说明
思路修正:你要的是"价格能够覆盖亏损时立即平仓",也就是实时监控浮盈,一旦浮盈 ≥ 累计亏损就马上平掉;否则就让订单按布林带动态止盈和移动止损自然走。无论哪种方式平仓,只要是盈利出场或回本出场,都重置手数回 0.01。
使用说明
1. 怎么安装
- 打开 MT5,菜单栏「文件」->「打开数据文件夹」,进入
MQL5\Experts\目录。 - 新建文本文件,粘贴上方完整代码,保存为
XAUUSD_Bollinger_Martingale_v2.mq5。 - 回到 MT5「导航器」面板,右键点击「智能交易系统」选择「刷新」,双击该 EA 加载到图表。
2. 参数怎么调
- 标的与周期:请将图表切换到 XAUUSD(黄金),时间周期设为 M5(5分钟)。
- 初始手数 / 递增量:默认均为 0.01。马丁格尔策略风险极高,建议从最小手数测试。
- 布林带参数:默认周期 20,偏差 2.0,经典参数,不建议随意改动。
- 移动止损点数:默认 100 点(即黄金 1 美元的价格波动)。
3. 信号与逻辑
- 开仓信号:K 线收盘上穿布林带中轨 → 开多;下穿 → 开空。
- 止盈条件:多单止盈设在"开仓价 + 上轨与中轨的差值";空单止盈设在"开仓价 - 中轨与下轨的差值"。
- 回本优先:EA 每个 Tick 检查当前持仓的浮盈(含手续费和隔夜利息),一旦 浮盈 ≥ 累计亏损总额,立即平仓并重置手数回 0.01。
- 移动止损:如果浮盈还没达到回本条件,就按 100 点移动止损锁定利润。
- 马丁格尔加仓:若订单亏损出场,下次开仓手数增加 0.01;若盈利出场(无论是回本平仓还是正常止盈),手数重置回 0.01。
4. 注意事项
- 风险警告:马丁格尔策略在连续亏损时手数会快速累积,单边行情下可能导致爆仓。强烈建议先在策略测试器中回测 1 年以上 XAUUSD 数据,确认最大回撤可接受。
- 必须允许自动交易:挂载 EA 后,确保 MT5 顶部「算法交易」按钮处于启用状态。
- 每次只持一单:代码已内置仓位检测,同一时间只会存在 1 个本 EA 的订单。
- 回测模式:建议使用「每个Tick基于真实Tick」模式回测,才能准确模拟移动止损和实时回本平仓的逻辑。
- 实盘前验证:先在模拟账户运行至少 1 周,观察实际执行情况与预期是否一致。
AI 解读根据源码逐行分析写成,供参考,以实际运行为准
这是什么
这是一个专为 MT5 平台编写的黄金(XAUUSD)自动交易程序(EA)。它用布林带中轨交叉作为进出场信号,结合马丁格尔加仓逻辑来放大仓位。它的核心特点是「回本优先」:一旦当前订单的浮盈够覆盖之前连续亏损的总额,就立刻平仓走人,不贪利润。
逻辑原理
开仓看 M5 周期。上一根 K 线收盘价上穿布林带中轨,开多单;下穿中轨,开空单。每次只持有一单。 止盈是动态的。多单止盈距离等于上轨减中轨的值,空单等于中轨减下轨的值。开仓时不设固定止损。 出场有两种方式。第一种是回本平仓:每个 Tick 都在算账,把浮动盈亏加上手续费和隔夜利息,如果这个净值大于等于之前累计亏掉的钱,马上平仓。第二种是移动止损:如果还没达到回本条件,价格顺着方向走超过设定的点数后,止损线会跟着价格移动,锁定利润。 仓位管理是马丁格尔。初始手数很小,如果这单亏了,系统记下亏了多少、连亏了几次,下一单的手数就会增加。只要任何一单是盈利出场的(不管是回本还是正常止盈),累计亏损清零,手数回到起点。
参数说明
- InpBaseLot(0.01):初始开仓手数。建议保持最小值,马丁策略加仓很快。
- InpLotStep(0.01):每次亏损后增加的手数。数值越大,回本越快,爆仓也越快。
- InpBandsPeriod(20):布林带的计算周期。经典参数,不建议随意改动。
- InpBandsDeviation(2.0):布林带的标准差倍数。决定上下轨有多宽。
- InpTrailingStopPts(100):移动止损的跟踪距离,单位是点。黄金 100 点相当于价格波动 1 美元。
- InpMagicNumber(20261002):魔术编号。用来区分这个 EA 下的单和其他 EA 或手动下的单。
怎么用
打开 MT5,点击菜单栏「文件」->「打开数据文件夹」,进入 MQL5\Experts\ 目录。新建文本文件,把源码粘贴进去,保存为 .mq5 后缀的文件。回到 MT5 的「导航器」面板,右键「智能交易系统」选「刷新」。把图表切到 XAUUSD 的 M5 周期,双击 EA 挂载上去。记得点亮 MT5 顶部的「算法交易」按钮,否则它不会下单。
适用场景
代码里布林带句柄写死了 PERIOD_M5,所以它是专门给黄金 5 分钟图设计的。适合震荡行情,价格在布林带上下轨之间来回跑的时候,中轨交叉信号比较准,止盈也容易碰到。 不适合单边大趋势行情。如果黄金走出几十美元的单向行情,中轨交叉会频繁给出反向信号导致连续亏损,马丁格尔会让手数迅速膨胀。
风险提示
马丁格尔策略没有设置最大加仓次数限制。理论上连亏 10 次,手数就会加到很大,账户资金不够时会直接爆仓。 开仓时没有设硬止损(sl = 0),全靠移动止损保护。如果遇到跳空或者极端滑点,移动止损来不及改,可能会产生巨大亏损。 代码里的容错偏差(Deviation)设了 30 点,成交模式是 IOC。在剧烈波动的新闻行情里,可能面临滑点或者无法成交的问题。 强烈建议先用 MT5 策略测试器跑至少一年的真实 Tick 数据,看看最大回撤你能不能承受,再考虑模拟盘挂一周观察。
常见问题
- 这个EA会重绘吗?
- 不会。它是实盘交易的EA,所有开仓判断都基于已经收盘的K线(前一根和前两根),不存在指标重绘问题。
- 可以用在其他品种或者其他时间周期吗?
- 代码里布林带指标句柄和K线数据读取都写死了 PERIOD_M5,如果要换周期需要修改源码。品种方面代码没有限制,但参数是针对黄金波动率设置的,用在其他品种需自行测试。
- 最多会连续加仓多少次?
- 代码里没有设置最大亏损次数或最大手数的限制。只要一直亏,它就会按 InpLotStep 一直加下去,直到触发品种的最大手数限制或账户资金不足。这是最大的风险点。
- 为什么回测要用真实Tick模式?
- 因为EA在每个Tick都会检查浮盈是否覆盖累计亏损并执行移动止损。普通回测模式的Tick是模拟生成的,无法准确反映这种高频管理的真实效果。
- MT4能用吗?
- 不能。源码使用了MQL5的CTrade类和OnTradeTransaction事件,这些在MT4的MQL4语法里不支持,只能在MT5运行。
Bollinger Bands Martingale Recovery EA for XAUUSD MT5
An MT5 Expert Advisor that trades XAUUSD using Bollinger Band middle line crossovers. It employs a martingale lot sizing approach with a recovery mechanism that closes positions immediately when floating profit covers accumulated losses, otherwise trailing stop and dynamic take profit manage the trade.
Source code is shown above. Compiled EX5 files are available to VIP members.
评论
还没有人评论,来抢个沙发。