精控呗441极端信号EA
双K线突破+ATR过滤,完整风控体系(移动止盈、动态止损、日盈亏限制)
效果示意图 · AI 根据源码绘制,仅示意画了哪些元素,细节以实际运行为准
#property copyright "XAUOF指标工坊"
#property version "1.00"
#include <Trade\Trade.mqh>
//--- 输入参数
input group "========== 信号参数 =========="
input int InpMagicNumber = 260813; // 魔术号码
input bool InpUseDoubleCandle = true; // 启用双K线确认模式
input int InpMinCandlePoints = 225; // 做多最小K线幅度(点)
input int InpMultiMinHighReq = 225; // 做多最小涨幅要求(点)
input int InpMultiMinLowReq = 225; // 做多最小跌幅要求(点)
input bool InpUseATRFilter = true; // 启用ATR过滤器
input int InpATRPeriod = 12; // ATR计算周期
input double InpMinATRPoints = 330.0; // 最小ATR点数(只交易大于此值)
input bool InpAutoAdjustTime = true; // 启用自动周期适应
input int InpTradeStartHour = 0; // 开始交易的时间(服务器时间),0=全天交易
input int InpTradeEndHour = 0; // 结束交易的时间(服务器时间),0=全天交易
input group "========== 止损止盈 =========="
input double InpFixedStopLoss = 0.0; // 固定止损(0=自动计算)
input double InpRiskPercent = 1.0; // 风险百分比(0.1-20.0)
input double InpNormalStopPoints = 750; // 普通止损(点)
input double InpNormalTakeProfitPoints = 5650; // 普通止盈(点)
input bool InpUseTimeBasedParams = false; // 启用时段特定参数
input double InpPeakStopLoss = 850; // 高峰柔时段止损(08:00-16:00)
input double InpPeakTakeProfit = 4250; // 高峰柔时段止盈
input bool InpUseMovingStop = true; // 启用移动止损
input int InpMovingStopActivate = 1350; // 移动止盈盘启动点数(0=禁用,浮盈达此点数后启动)
input int InpMovingStopDistance = 200; // 移动止盈回撤点数(0=禁用,从最高/最低回撤此点数平仓)
input bool InpUseTrailStop = true; // 启用动态追踪止损
input int InpTrailActivateProfit = 1600; // 动态追踪启动盈利(点) - 盈利达到此点数后启动动态...
input int InpTrailStopDistance = 300; // 动态追踪距离(点) - 止损距离当前价格的点数
input group "========== 日盈亏控制 =========="
input double InpDailyProfitLimit = 12.0; // 日盈利限制(%)
input double InpDailyLossLimit = 8.0; // 日亏损限制(%)
input double InpDailyProfitAmount = 0.0; // 日盈利金额限制($) (0=禁用)
input double InpDailyLossAmount = 0.0; // 日亏损金额限制($) (0=禁用)
input int InpMaxDailyTrades = 20; // 最大日交易数
input bool InpUseLossContinuousControl = true; // 启用连续亏损控制
//--- 全局变量
CTrade trade;
int atrHandle = INVALID_HANDLE;
double atrBuffer[];
datetime lastBarTime = 0;
int dailyTradeCount = 0;
double dailyProfit = 0.0;
double dailyLoss = 0.0;
datetime lastResetDate = 0;
double consecutiveLoss = 0.0;
bool buyAllowed = true;
bool sellAllowed = true;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// 设置魔术号
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFilling(ORDER_FILLING_FOK);
if(trade.ResultRetcode() == TRADE_RETCODE_INVALID_FILL)
trade.SetTypeFilling(ORDER_FILLING_IOC);
// 创建ATR指标句柄
if(InpUseATRFilter)
{
atrHandle = iATR(_Symbol, PERIOD_CURRENT, InpATRPeriod);
if(atrHandle == INVALID_HANDLE)
{
Print("[初始化] ATR指标创建失败");
return INIT_FAILED;
}
}
ArraySetAsSeries(atrBuffer, true);
Print("========== EA初始化成功 ==========");
Print("[参数] 魔术号: ", InpMagicNumber);
Print("[参数] 双K线确认: ", InpUseDoubleCandle ? "启用" : "禁用");
Print("[参数] 最小K线幅度: ", InpMinCandlePoints, " 点");
Print("[参数] ATR过滤: ", InpUseATRFilter ? "启用" : "禁用", ", 最小ATR: ", InpMinATRPoints, " 点");
Print("[参数] 风险百分比: ", InpRiskPercent, "%");
Print("[参数] 普通止损/止盈: ", InpNormalStopPoints, "/", InpNormalTakeProfitPoints, " 点");
Print("[参数] 移动止损: ", InpUseMovingStop ? "启用" : "禁用", ", 启动点数: ", InpMovingStopActivate);
Print("[参数] 动态追踪: ", InpUseTrailStop ? "启用" : "禁用", ", 启动盈利: ", InpTrailActivateProfit);
Print("[参数] 日盈亏限制: +", InpDailyProfitLimit, "% / -", InpDailyLossLimit, "%");
Print("[参数] 最大日交易数: ", InpMaxDailyTrades);
Print("=================================");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(atrHandle != INVALID_HANDLE)
IndicatorRelease(atrHandle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 检查是否是新K线
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;
// 重置日统计
ResetDailyStats();
// 检查交易权限
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
{
Print("[跳过] 终端不允许交易");
return;
}
if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
{
Print("[跳过] EA不允许交易");
return;
}
// 检查日盈亏限制
if(!CheckDailyLimits())
return;
// 检查交易时间
if(!CheckTradingTime())
return;
// 更新移动止损和动态追踪
ManageOpenPositions();
// 检查是否达到最大日交易数
if(dailyTradeCount >= InpMaxDailyTrades)
{
Print("[风控] 今日交易数已达上限 ", InpMaxDailyTrades);
return;
}
// 检查开仓信号
int signal = CheckSignal();
if(signal == 1 && buyAllowed)
{
if(!HasPosition(POSITION_TYPE_BUY))
OpenPosition(ORDER_TYPE_BUY);
}
else if(signal == -1 && sellAllowed)
{
if(!HasPosition(POSITION_TYPE_SELL))
OpenPosition(ORDER_TYPE_SELL);
}
}
//+------------------------------------------------------------------+
//| 检查交易信号 |
//+------------------------------------------------------------------+
int CheckSignal()
{
// 获取最近3根K线数据
double close[], open[], high[], low[];
ArraySetAsSeries(close, true);
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
if(CopyClose(_Symbol, PERIOD_CURRENT, 0, 3, close) < 3) return 0;
if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, 3, open) < 3) return 0;
if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, 3, high) < 3) return 0;
if(CopyLow(_Symbol, PERIOD_CURRENT, 0, 3, low) < 3) return 0;
// 获取ATR值
double atrValue = 0;
if(InpUseATRFilter)
{
if(BarsCalculated(atrHandle) < 2)
return 0;
if(CopyBuffer(atrHandle, 0, 0, 2, atrBuffer) < 2)
return 0;
atrValue = atrBuffer[0] / _Point;
// ATR过滤
if(atrValue < InpMinATRPoints)
{
return 0;
}
}
// 计算第1根K线的涨跌幅(索引1是前一根已完成的K线)
double candle1Range = MathAbs(close[1] - open[1]) / _Point;
bool candle1Bullish = close[1] > open[1];
bool candle1Bearish = close[1] < open[1];
// 第1根K线必须满足最小幅度要求
if(candle1Range < InpMinCandlePoints)
return 0;
// 双K线确认模式
if(InpUseDoubleCandle)
{
// 做多信号:第1根看涨K线(收>开)且涨幅≥225点,第2根(当前K线)收盘突破第1根最高点
if(candle1Bullish && candle1Range >= InpMultiMinHighReq)
{
if(close[0] > high[1])
{
Print("[信号] 做多信号触发 - 双K线突破,第1根涨幅:", (int)candle1Range, "点,第2根突破前高,ATR:", (int)atrValue, "点");
return 1;
}
}
// 做空信号:第1根看跌K线(收<开)且跌幅≥225点,第2根(当前K线)收盘突破第1根最低点
if(candle1Bearish && candle1Range >= InpMultiMinLowReq)
{
if(close[0] < low[1])
{
Print("[信号] 做空信号触发 - 双K线突破,第1根跌幅:", (int)candle1Range, "点,第2根突破前低,ATR:", (int)atrValue, "点");
return -1;
}
}
}
else
{
// 单K线模式(不需要第二根确认)
if(candle1Bullish && candle1Range >= InpMultiMinHighReq)
{
Print("[信号] 做多信号触发 - 单K线模式,涨幅:", (int)candle1Range, "点,ATR:", (int)atrValue, "点");
return 1;
}
if(candle1Bearish && candle1Range >= InpMultiMinLowReq)
{
Print("[信号] 做空信号触发 - 单K线模式,跌幅:", (int)candle1Range, "点,ATR:", (int)atrValue, "点");
return -1;
}
}
return 0;
}
//+------------------------------------------------------------------+
//| 开仓 |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType)
{
double price = (orderType == ORDER_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 计算止损止盈
double sl = 0, tp = 0;
CalculateStopLossTakeProfit(orderType, price, sl, tp);
// 计算手数
double lotSize = CalculateLotSize(sl, price);
// 检查手数范围
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathFloor(lotSize / lotStep) * lotStep;
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
// 执行开仓
bool result = false;
if(orderType == ORDER_TYPE_BUY)
result = trade.Buy(lotSize, _Symbol, 0, sl, tp, "精控呗441");
else
result = trade.Sell(lotSize, _Symbol, 0, sl, tp, "精控呗441");
if(result)
{
dailyTradeCount++;
Print("[开仓] ", (orderType == ORDER_TYPE_BUY ? "做多" : "做空"),
" ", lotSize, " 手,价格:", price, ",止损:", sl, ",止盈:", tp);
}
else
{
Print("[开仓失败] 错误代码:", trade.ResultRetcode(), ",描述:", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| 计算止损止盈 |
//+------------------------------------------------------------------+
void CalculateStopLossTakeProfit(ENUM_ORDER_TYPE orderType, double price, double &sl, double &tp)
{
double stopLossPoints = InpNormalStopPoints;
double takeProfitPoints = InpNormalTakeProfitPoints;
// 如果启用时段特定参数
if(InpUseTimeBasedParams)
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(dt.hour >= 8 && dt.hour < 16)
{
stopLossPoints = InpPeakStopLoss;
takeProfitPoints = InpPeakTakeProfit;
}
}
// 检查最小止损距离
int stopsLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
if(stopsLevel > 0)
{
stopLossPoints = MathMax(stopLossPoints, stopsLevel + 10);
takeProfitPoints = MathMax(takeProfitPoints, stopsLevel + 10);
}
double point = _Point;
if(orderType == ORDER_TYPE_BUY)
{
sl = price - stopLossPoints * point;
tp = price + takeProfitPoints * point;
}
else
{
sl = price + stopLossPoints * point;
tp = price - takeProfitPoints * point;
}
// 规范化价格
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
sl = NormalizeDouble(sl, digits);
tp = NormalizeDouble(tp, digits);
}
//+------------------------------------------------------------------+
//| 计算手数(基于风险百分比) |
//+------------------------------------------------------------------+
double CalculateLotSize(double sl, double entryPrice)
{
if(InpRiskPercent <= 0 || sl == 0)
return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * InpRiskPercent / 100.0;
double slDistance = MathAbs(entryPrice - sl);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickSize == 0 || slDistance == 0)
return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double lotSize = riskAmount / (slDistance / tickSize * tickValue);
return lotSize;
}
//+------------------------------------------------------------------+
//| 管理已开仓位(移动止损和动态追踪) |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
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) != InpMagicNumber) continue;
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
double profit = PositionGetDouble(POSITION_PROFIT);
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double profitPoints = 0;
if(posType == POSITION_TYPE_BUY)
profitPoints = (currentPrice - openPrice) / _Point;
else
profitPoints = (openPrice - currentPrice) / _Point;
bool needModify = false;
double newSL = currentSL;
double newTP = currentTP;
// 移动止盈(回撤平仓)
if(InpUseMovingStop && InpMovingStopActivate > 0 && InpMovingStopDistance > 0)
{
if(profitPoints >= InpMovingStopActivate)
{
// 计算从最高/最低价回撤的距离
if(posType == POSITION_TYPE_BUY)
{
double newStopPrice = currentPrice - InpMovingStopDistance * _Point;
if(newStopPrice > currentSL)
{
newSL = NormalizeDouble(newStopPrice, _Digits);
needModify = true;
}
}
else
{
double newStopPrice = currentPrice + InpMovingStopDistance * _Point;
if(currentSL == 0 || newStopPrice < currentSL)
{
newSL = NormalizeDouble(newStopPrice, _Digits);
needModify = true;
}
}
}
}
// 动态追踪止损
if(InpUseTrailStop && InpTrailActivateProfit > 0 && InpTrailStopDistance > 0)
{
if(profitPoints >= InpTrailActivateProfit)
{
if(posType == POSITION_TYPE_BUY)
{
double trailStop = currentPrice - InpTrailStopDistance * _Point;
if(trailStop > currentSL)
{
newSL = NormalizeDouble(trailStop, _Digits);
needModify = true;
}
}
else
{
double trailStop = currentPrice + InpTrailStopDistance * _Point;
if(currentSL == 0 || trailStop < currentSL)
{
newSL = NormalizeDouble(trailStop, _Digits);
needModify = true;
}
}
}
}
// 执行修改
if(needModify)
{
if(trade.PositionModify(ticket, newSL, currentTP))
{
Print("[止损调整] 订单:", ticket, ",新止损:", newSL, ",当前盈利:", (int)profitPoints, "点");
}
}
}
}
//+------------------------------------------------------------------+
//| 检查是否有指定类型的持仓 |
//+------------------------------------------------------------------+
bool HasPosition(ENUM_POSITION_TYPE posType)
{
for(int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == posType)
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查交易时间 |
//+------------------------------------------------------------------+
bool CheckTradingTime()
{
if(InpTradeStartHour == 0 && InpTradeEndHour == 0)
return true;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
// 周末不交易
if(dt.day_of_week == 0 || dt.day_of_week == 6)
return false;
int currentHour = dt.hour;
if(InpTradeStartHour < InpTradeEndHour)
{
if(currentHour < InpTradeStartHour || currentHour >= InpTradeEndHour)
return false;
}
else if(InpTradeStartHour > InpTradeEndHour)
{
if(currentHour < InpTradeStartHour && currentHour >= InpTradeEndHour)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| 重置日统计 |
//+------------------------------------------------------------------+
void ResetDailyStats()
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
datetime currentDate = StringToTime(StringFormat("%04d.%02d.%02d", dt.year, dt.mon, dt.day));
if(currentDate != lastResetDate)
{
dailyTradeCount = 0;
dailyProfit = 0.0;
dailyLoss = 0.0;
buyAllowed = true;
sellAllowed = true;
consecutiveLoss = 0.0;
lastResetDate = currentDate;
Print("[日统计] 新交易日开始: ", TimeToString(currentDate, TIME_DATE));
}
}
//+------------------------------------------------------------------+
//| 检查日盈亏限制 |
//+------------------------------------------------------------------+
bool CheckDailyLimits()
{
// 计算今日盈亏
CalculateDailyProfitLoss();
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
// 检查日盈利限制(百分比)
if(InpDailyProfitLimit > 0)
{
double profitPercent = (dailyProfit / balance) * 100.0;
if(profitPercent >= InpDailyProfitLimit)
{
Print("[风控] 今日盈利已达 ", DoubleToString(profitPercent, 2), "%,停止开仓");
return false;
}
}
// 检查日盈利限制(金额)
if(InpDailyProfitAmount > 0 && dailyProfit >= InpDailyProfitAmount)
{
Print("[风控] 今日盈利已达 $", DoubleToString(dailyProfit, 2), ",停止开仓");
return false;
}
// 检查日亏损限制(百分比)
if(InpDailyLossLimit > 0)
{
double lossPercent = (dailyLoss / balance) * 100.0;
if(lossPercent >= InpDailyLossLimit)
{
Print("[风控] 今日亏损已达 ", DoubleToString(lossPercent, 2), "%,停止开仓");
return false;
}
}
// 检查日亏损限制(金额)
if(InpDailyLossAmount > 0 && dailyLoss >= InpDailyLossAmount)
{
Print("[风控] 今日亏损已达 $", DoubleToString(dailyLoss, 2), ",停止开仓");
return false;
}
// 检查连续亏损控制
if(InpUseLossContinuousControl && consecutiveLoss >= 200)
{
Print("[风控] 连续亏损已达 ", (int)consecutiveLoss, " 点,暂停开仓");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| 计算今日盈亏 |
//+------------------------------------------------------------------+
void CalculateDailyProfitLoss()
{
dailyProfit = 0.0;
dailyLoss = 0.0;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
datetime todayStart = StringToTime(StringFormat("%04d.%02d.%02d 00:00", dt.year, dt.mon, dt.day));
// 统计当前持仓的盈亏
for(int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
double profit = PositionGetDouble(POSITION_PROFIT);
if(profit > 0)
dailyProfit += profit;
else
dailyLoss += MathAbs(profit);
}
// 统计今日已平仓订单
HistorySelect(todayStart, TimeCurrent());
for(int i = 0; i < HistoryDealsTotal(); i++)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0) continue;
if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue;
if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != InpMagicNumber) continue;
if(HistoryDealGetInteger(ticket, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
if(profit > 0)
{
dailyProfit += profit;
consecutiveLoss = 0.0; // 重置连续亏损
}
else
{
dailyLoss += MathAbs(profit);
consecutiveLoss += MathAbs(profit / _Point); // 累加连续亏损点数
}
}
}作品说明
这是一个基于双K线突破确认的EA,配合ATR波动率过滤和完整的风控体系(移动止盈、动态追踪止损、日盈亏限制、连续亏损控制)。核心逻辑:第一根K线单边涨跌≥225点,第二根K线突破前高/前低,且ATR≥330点时开仓。
使用说明
- 安装方法:将代码保存为
PrecisionControl441_ExtremeSignal.mq5,放入MQL5\Experts目录,重启 MT5 或刷新导航器,拖到图表上。
- 核心参数:
InpMinCandlePoints(225):第一根K线的最小涨跌幅度,低于此值不开仓InpMinATRPoints(330):ATR波动率过滤,市场波动小于此值不交易InpMovingStopActivate(1350):浮盈达到此点数后启动移动止盈InpTrailActivateProfit(1600):浮盈达到此点数后启动动态追踪止损
- 风控设置:
InpDailyProfitLimit(12%)和InpDailyLossLimit(8%):日盈亏达限后自动停止开仓InpMaxDailyTrades(20):每日最多开仓次数,防止过度交易InpRiskPercent(1.0%):每单风险占账户权益的百分比,自动计算手数
- 交易时间:默认全天交易(参数为0),如需限制交易时段,将
InpTradeStartHour设为 8、InpTradeEndHour设为 16(服务器时间08:00-16:00)
- 信号逻辑:前一根K线单边涨跌≥225点,当前K线收盘突破前一根的高/低点,且ATR≥330点时开仓;同方向已有持仓时跳过信号
- 止损止盈:默认普通止损750点、止盈5650点;启用时段参数后,08:00-16:00 使用高峰柔时段的850/4250点
- 日志查看:在"工具箱-专家"标签页查看详细日志,所有开仓、平仓、风控决策都有记录,便于回溯和调试
- 注意事项:首次运行建议先在模拟盘测试,观察信号触发频率和盈亏分布;实盘前务必确认服务器时间与交易时段设置一致;ATR过滤值需根据品种特性调整(黄金波动大,外汇可能需降低阈值)
AI 解读根据源码逐行分析写成,供参考,以实际运行为准
这是什么
这是一个 MetaTrader 5 自动交易 EA,核心逻辑是双 K 线突破确认:第一根 K 线单边涨跌幅度达到设定点数(默认 225 点),第二根 K 线收盘价突破第一根的高点或低点时开仓,同时用 ATR 指标过滤低波动行情。内置完整的风控体系,包括移动止盈、动态追踪止损、日盈亏限制、最大日交易数和连续亏损控制,适合追踪趋势突破行情。
逻辑原理
入场信号生成:EA 在每根新 K 线形成时检查信号。启用双 K 线模式时(默认启用),做多信号需要满足:前一根已完成的 K 线是阳线(收盘价 > 开盘价)且涨幅 ≥ 225 点,当前 K 线收盘价突破前一根的最高点;做空信号则相反,前一根是阴线且跌幅 ≥ 225 点,当前 K 线收盘价跌破前一根的最低点。如果关闭双 K 线模式,只要单根 K 线涨跌幅达标即可触发信号,不需要第二根确认。
ATR 波动率过滤:启用后(默认启用,周期 12),EA 会计算当前 ATR 值并转换为点数,只有当 ATR ≥ 330 点时才允许开仓。这个设计用来避免在低波动、盘整行情中频繁交易。
止损止盈计算:默认使用普通止损 750 点、止盈 5650 点。如果启用时段特定参数(InpUseTimeBasedParams),在服务器时间 08:00-16:00 期间会切换为高峰柔时段的止损 850 点、止盈 4250 点。止损会自动检查交易商的最小止损距离(SYMBOL_TRADE_STOPS_LEVEL),如果设定值小于要求,会自动加 10 点补偿。
手数计算:根据风险百分比(默认 1.0%)和止损距离自动计算手数。公式是:风险金额 = 账户余额 × 风险百分比,手数 = 风险金额 ÷ (止损点数 × 每点价值)。计算后的手数会按交易商的手数步长规范化,并限制在最小手数和最大手数之间。
移动止盈:当浮盈达到 1350 点后启动,止损会跟随价格移动,始终保持距离当前价 200 点。例如做多单,价格涨到浮盈 1350 点后,止损会设在当前卖价 - 200 点的位置,价格继续上涨时止损继续上移,价格回撤 200 点即平仓锁定利润。
动态追踪止损:当浮盈达到 1600 点后启动,止损跟随价格保持 300 点距离。和移动止盈的区别是距离更宽松,适合让利润继续奔跑。两种机制可同时启用,EA 会在每根新 K 线时检查并更新止损。
日盈亏限制:每天开始时重置统计。EA 会累计今日所有已平仓订单和当前持仓的盈亏,如果盈利达到账户余额的 12%(或设定的固定金额),或亏损达到 8%(或固定金额),会停止开新仓但不影响已有持仓。同时限制每日最多开仓 20 次。
连续亏损控制:启用后(默认启用),EA 会累计连续亏损的点数。每次平仓盈利时重置为 0,亏损时累加亏损点数。当连续亏损达到 200 点时暂停开仓,直到下一个交易日重置。
持仓管理:同方向已有持仓时不会重复开仓。EA 通过魔术号(默认 260813)识别自己的订单,不会影响同账户其他 EA 或手动单。
参数说明
信号参数
InpMagicNumber(260813):魔术号,用于识别 EA 的订单,多个 EA 同时运行时需设不同值InpUseDoubleCandle(true):是否启用双 K 线确认,true = 需要第二根 K 线突破确认,false = 单根 K 线达标即开仓InpMinCandlePoints(225):第一根 K 线的最小涨跌幅度,低于此值不产生信号。减小会增加信号频率但可能增加假信号InpMultiMinHighReq(225):做多时第一根 K 线的最小涨幅要求,通常和InpMinCandlePoints保持一致InpMultiMinLowReq(225):做空时第一根 K 线的最小跌幅要求InpUseATRFilter(true):是否启用 ATR 波动率过滤,建议启用以避免低波动行情InpATRPeriod(12):ATR 计算周期,越大越平滑但反应越慢InpMinATRPoints(330.0):最小 ATR 点数,低于此值不交易。黄金可用默认值,外汇品种波动小可能需降到 100-200InpAutoAdjustTime(true):自动周期适应开关,代码中未实现具体逻辑,可忽略InpTradeStartHour(0):开始交易的小时(服务器时间),0 = 全天交易。例如设 8 则只在 08:00 后交易InpTradeEndHour(0):结束交易的小时,0 = 全天。配合开始时间使用,例如 8-16 限制在 08:00-16:00 交易
止损止盈
InpFixedStopLoss(0.0):固定止损,代码中未使用此参数,实际止损由后面参数决定InpRiskPercent(1.0):每单风险占账户余额的百分比,范围 0.1-20.0。1.0 表示每单风险 1% 余额InpNormalStopPoints(750):普通止损点数,非时段参数模式下使用InpNormalTakeProfitPoints(5650):普通止盈点数InpUseTimeBasedParams(false):是否启用时段特定参数,true 时在 08:00-16:00 使用下面两个值InpPeakStopLoss(850):高峰柔时段止损点数InpPeakTakeProfit(4250):高峰柔时段止盈点数InpUseMovingStop(true):是否启用移动止盈,建议启用以保护利润InpMovingStopActivate(1350):移动止盈启动点数,浮盈达此值后开始跟随价格移动止损InpMovingStopDistance(200):移动止盈回撤距离,止损始终保持距离当前价此点数InpUseTrailStop(true):是否启用动态追踪止损InpTrailActivateProfit(1600):动态追踪启动盈利点数,浮盈达此值后启动InpTrailStopDistance(300):动态追踪距离,止损距离当前价的点数
日盈亏控制
InpDailyProfitLimit(12.0):日盈利限制百分比,达到账户余额的此百分比后停止开仓InpDailyLossLimit(8.0):日亏损限制百分比InpDailyProfitAmount(0.0):日盈利金额限制(美元),0 = 禁用。优先级和百分比并列,任一达标即停止InpDailyLossAmount(0.0):日亏损金额限制InpMaxDailyTrades(20):最大日交易数,达到后不再开新仓InpUseLossContinuousControl(true):是否启用连续亏损控制,连续亏损 200 点后暂停开仓
怎么用
- 将代码保存为
.mq5文件,放入 MT5 数据文件夹的MQL5\Experts目录 - 重启 MT5 或在导航器中右键"智能交易系统"选择刷新
- 将 EA 从导航器拖到图表上,在弹出的参数窗口调整设置
- 确保勾选"允许自动交易"和"允许 DLL 导入"(虽然此 EA 未用 DLL,但部分经纪商要求勾选)
- 图表右上角显示笑脸图标表示 EA 运行正常
- 在"工具箱-交易"标签页查看持仓和订单,"专家"标签页查看详细日志
信号查看:EA 在每根新 K 线形成时检查信号,满足条件会在日志中输出"[信号] 做多/做空信号触发"并显示具体数据。开仓成功会输出"[开仓]"日志,包含手数、价格、止损止盈。止损调整会输出"[止损调整]"日志。
建议设置:首次使用先在模拟盘测试至少一周,观察信号频率和盈亏分布。实盘时确认服务器时间与交易时段设置一致(可在 MT5 菜单"工具-选项-服务器"查看)。黄金(XAUUSD)可用默认参数,外汇对建议降低 ATR 阈值到 100-200 点,同时调整 K 线幅度要求到 50-100 点。
适用场景
适合的品种和周期:设计用于波动较大的品种,默认参数适合黄金(XAUUSD)的 H1 或 H4 周期。外汇主要货币对(EURUSD、GBPUSD 等)波动较小,需降低 ATR 和 K 线幅度阈值。指数(US30、NAS100)也可用,但需根据点值调整参数。不建议用于加密货币,因其极端波动可能触发过宽的止损。
适合的行情:趋势突破行情,尤其是盘整后的大幅单边突破。ATR 过滤确保只在高波动时交易,双 K 线确认机制减少假突破。不适合低波动盘整行情(ATR 自动过滤)和剧烈震荡行情(可能频繁触发信号但立即回撤)。
时段选择:默认全天交易。如果发现夜间或亚洲盘信号质量差,可设置 InpTradeStartHour = 8 和 InpTradeEndHour = 16 限制在欧美盘交易(具体时段需根据经纪商服务器时间调整)。启用时段特定参数后,高峰时段会使用更紧的止损止盈(850/4250 点),适合快进快出。
资金管理:1% 风险百分比适合保守交易者,激进者可提高到 2-3%,但需配合更严格的日亏损限制。日盈亏限制(12% 盈利 / 8% 亏损)可根据个人风险承受能力调整,建议盈利限制 > 亏损限制,以"让利润奔跑、尽早止损"为原则。
风险提示
信号滞后:双 K 线确认机制要等第二根 K 线收盘才开仓,可能错过突破初期的最佳入场点。如果追求更快入场,可关闭双 K 线模式(InpUseDoubleCandle = false),但会增加假信号风险。
假突破风险:单纯的价格突破容易遇到假突破,尤其在新闻发布或开盘时段。虽然 ATR 过滤和双 K 线确认能减少假信号,但无法完全避免。建议配合更大的时间周期使用(H4 比 M15 更可靠)。
止损距离:默认止损 750 点对黄金来说相对较宽(约 7.5 美元),如果行情剧烈波动可能造成较大单笔亏损。风险百分比和日亏损限制能控制总风险,但单笔亏损仍需心理承受。止损过窄(如改为 300 点)则容易被正常波动扫掉。
滑点和点差:EA 使用市价单开仓(trade.Buy 和 trade.Sell 不指定价格),在波动剧烈时可能遇到滑点。止损止盈也可能因为点差或流动性不足无法精确成交。建议选择点差稳定、执行速度快的经纪商。
移动止盈和追踪止损的平衡:两种机制同时启用时,移动止盈(1350 点启动,200 点回撤)比追踪止损(1600 点启动,300 点距离)更激进,可能在利润刚达标时就被提前平仓。如果希望让利润充分增长,可以只启用追踪止损,或调高移动止盈的启动点数。
连续亏损控制:200 点连续亏损后暂停开仓,相当于约 2-3 次止损(每次 750 点止损 / 风险百分比)。如果设置过严(如 100 点),可能在正常回撤中就被限制交易,错过后续盈利机会。
重绘问题:此 EA 不存在重绘,因为所有信号判断都基于已完成的 K 线(close[1]、high[1]、low[1]),不会随着当前 K 线的变化而改变历史信号。
优化和过拟合:默认参数(225 点 K 线幅度、330 点 ATR)可能是基于历史数据优化的结果。直接用于实盘前需在样本外数据测试,避免过拟合导致未来表现差。建议在至少 3-6 个月的历史数据上回测,并在模拟盘验证。
常见问题
- 这个 EA 会重绘吗?信号会变化吗?
- 不会重绘。所有信号判断都基于前一根已完成的 K 线,不会因为当前 K 线的波动而改变历史信号或已开仓位的依据。
- 默认参数适合什么周期和品种?
- 默认参数(225 点 K 线幅度、330 点 ATR、750 点止损)主要针对黄金(XAUUSD)的 H1 或 H4 周期。外汇对波动较小,建议降低 ATR 到 100-200 点,K 线幅度到 50-100 点。
- 可以只用移动止盈或追踪止损吗?
- 可以。在参数中将 `InpUseMovingStop` 或 `InpUseTrailStop` 设为 false 即可单独禁用。两者同时启用时,移动止盈更激进(启动点 1350、回撤 200),追踪止损更宽松(启动点 1600、距离 300)。
- 日亏损限制达到后会自动平仓吗?
- 不会。日盈亏限制只控制是否开新仓,不会主动平掉已有持仓。已开仓位仍按止损止盈或移动止损规则管理。如需自动平仓需自行修改代码。
- ATR 过滤值 330 点是怎么来的?需要调整吗?
- 330 点是代码中设定的默认值,用于过滤低波动行情。黄金日均波动通常在 300-800 点,330 是中等阈值。如果信号过少可降到 200-250,如果假信号多可提高到 400-500,需根据回测和实盘表现调整。
Precision Control 441 Extreme Signal EA for MT5
An automated trading EA for MetaTrader 5 using double-candle breakout confirmation with ATR volatility filter. Features comprehensive risk management including trailing stop, dynamic stop-loss adjustment, daily profit/loss limits, and consecutive loss control.
Source code is shown above. Compiled EX5 files are available to VIP members.
评论
还没有人评论,来抢个沙发。