//+------------------------------------------------------------------+ //| Sniper.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include CTrade trade; // 交易模式:突破(Stop) 或 反转(Limit) enum ENUM_TRADE_MODE { MODE_BREAKOUT = 0, MODE_REVERSAL = 1 }; // 止盈止损处理模式 enum ENUM_TP_SL_MODE { TP_ON_ORDER = 0, TP_AFTER_ENTRY = 1, TP_VIRTUAL = 2 }; //+------------------------------------------------------------------+ //| 输入参数 | //+------------------------------------------------------------------+ input group "=====检测参数=====" input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // 检测时间周期 input int AnalysisBars = 500; // 分析的K线数量 input int CompareBars = 20; // 比较的K线数量(左右各一半) input group "=====箭头显示参数=====" input color HighArrowColor = clrRed; // 高点箭头颜色 input color LowArrowColor = clrBlue; // 低点箭头颜色 input double ArrowOffset = 10; // 箭头偏移量(点数) input group "=====交易参数=====" input int MagicNumber = 123456; // 魔术号 input double LotSize = 0.1; // 交易手数 input double TakeProfit = 2.0; // 止盈(XAU:8, BTC:1000) input double StopLoss = 1.0; // 止损(XAU:2, BTC:200) input bool EnableTrading = true; // 是否启用交易功能 input ENUM_TP_SL_MODE TPSLMode = TP_ON_ORDER; // 止盈止损模式:挂单设置/入场后设置/虚拟监控 input ENUM_TRADE_MODE TradeMode = MODE_BREAKOUT; // 交易模式:突破Stop单 或 反转Limit单 input string OrderComment = ""; // 订单备注 // 全局变量 bool isInitialized = false; bool isEAEnabled = false; // EA是否已启用 datetime lastBarTime = 0; // 存储检测到的狙击点 datetime highTimes[]; double highPrices[]; datetime lowTimes[]; double lowPrices[]; // 交易相关变量 ulong buyStopOrder = 0; ulong sellStopOrder = 0; double lastHighPrice = 0; // 上次放置的高点突破单价格 double lastLowPrice = 0; // 上次放置的低点突破单价格 //+------------------------------------------------------------------+ //| 检测高点 | //+------------------------------------------------------------------+ bool IsHighPoint(int shift) { double currentHigh = iHigh(_Symbol, TimeFrame, shift); int halfBars = CompareBars / 2; // 检查当前K线的高点是否比前后各halfBars根K线的高点都高 for(int i = 1; i <= halfBars; i++) { if(shift + i >= iBars(_Symbol, TimeFrame)) continue; if(shift - i < 0) continue; double leftHigh = iHigh(_Symbol, TimeFrame, shift - i); double rightHigh = iHigh(_Symbol, TimeFrame, shift + i); if(currentHigh <= leftHigh || currentHigh <= rightHigh) { return false; } } return true; } //+------------------------------------------------------------------+ //| 检测低点 | //+------------------------------------------------------------------+ bool IsLowPoint(int shift) { double currentLow = iLow(_Symbol, TimeFrame, shift); int halfBars = CompareBars / 2; // 检查当前K线的低点是否比前后各halfBars根K线的低点都低 for(int i = 1; i <= halfBars; i++) { if(shift + i >= iBars(_Symbol, TimeFrame)) continue; if(shift - i < 0) continue; double leftLow = iLow(_Symbol, TimeFrame, shift - i); double rightLow = iLow(_Symbol, TimeFrame, shift + i); if(currentLow >= leftLow || currentLow >= rightLow) { return false; } } return true; } //+------------------------------------------------------------------+ //| 绘制高点箭头 | //+------------------------------------------------------------------+ void DrawHighArrow(datetime time, double price) { string arrowName = "HighPoint_" + TimeToString(time); // 删除已存在的同名箭头 ObjectDelete(0, arrowName); // 计算箭头位置,让箭头显示在K线高点之上 double arrowPrice = price + (ArrowOffset * _Point); // 创建新箭头 if(ObjectCreate(0, arrowName, OBJ_ARROW_UP, 0, time, arrowPrice)) { ObjectSetInteger(0, arrowName, OBJPROP_COLOR, HighArrowColor); ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, arrowName, OBJPROP_HIDDEN, true); Print("绘制高点箭头: 时间=", TimeToString(time), " 价格=", DoubleToString(price, _Digits)); } } //+------------------------------------------------------------------+ //| 绘制低点箭头 | //+------------------------------------------------------------------+ void DrawLowArrow(datetime time, double price) { string arrowName = "LowPoint_" + TimeToString(time); // 删除已存在的同名箭头 ObjectDelete(0, arrowName); // 计算箭头位置,让箭头显示在K线低点之下 double arrowPrice = price - (ArrowOffset * _Point); // 创建新箭头 if(ObjectCreate(0, arrowName, OBJ_ARROW_UP, 0, time, arrowPrice)) { ObjectSetInteger(0, arrowName, OBJPROP_COLOR, LowArrowColor); ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, arrowName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, arrowName, OBJPROP_HIDDEN, true); Print("绘制低点箭头: 时间=", TimeToString(time), " 价格=", DoubleToString(price, _Digits)); } } //+------------------------------------------------------------------+ //| 分析并标记所有狙击点 | //+------------------------------------------------------------------+ void AnalyzeAllHighLowPoints() { // 清理所有旧箭头 ObjectsDeleteAll(0, "HighPoint_"); ObjectsDeleteAll(0, "LowPoint_"); Print("=== 动态分析开始 ==="); Print("当前时间: ", TimeToString(TimeCurrent())); Print("开始分析最近", AnalysisBars, "根K线的狙击点..."); Print("比较范围: 前后各", CompareBars/2, "根K线"); // 获取可用的K线数量 int totalBars = iBars(_Symbol, TimeFrame); int barsToAnalyze = MathMin(AnalysisBars, totalBars); int halfBars = CompareBars / 2; // 清空数组 ArrayResize(highTimes, 0); ArrayResize(highPrices, 0); ArrayResize(lowTimes, 0); ArrayResize(lowPrices, 0); // 分析每一根K线(从索引1开始,跳过当前正在形成的K线) int highCount = 0; int lowCount = 0; for(int i = 1 + halfBars; i < barsToAnalyze - halfBars; i++) { // 检测高点 if(IsHighPoint(i)) { highCount++; datetime time = iTime(_Symbol, TimeFrame, i); double price = iHigh(_Symbol, TimeFrame, i); DrawHighArrow(time, price); // 存储高点数据 ArrayResize(highTimes, ArraySize(highTimes) + 1); ArrayResize(highPrices, ArraySize(highPrices) + 1); highTimes[ArraySize(highTimes) - 1] = time; highPrices[ArraySize(highPrices) - 1] = price; } // 检测低点 if(IsLowPoint(i)) { lowCount++; datetime time = iTime(_Symbol, TimeFrame, i); double price = iLow(_Symbol, TimeFrame, i); DrawLowArrow(time, price); // 存储低点数据 ArrayResize(lowTimes, ArraySize(lowTimes) + 1); ArrayResize(lowPrices, ArraySize(lowPrices) + 1); lowTimes[ArraySize(lowTimes) - 1] = time; lowPrices[ArraySize(lowPrices) - 1] = price; } } Print("分析完成!检测到", highCount, "个高点,", lowCount, "个低点"); Print("=== 动态分析结束 ==="); // 如果启用交易功能,处理下单(根据模式选择突破或反转) if(EnableTrading) { ManageBreakoutOrders(); } } //+------------------------------------------------------------------+ //| 管理下单(根据模式:突破或反转) | //+------------------------------------------------------------------+ void ManageBreakoutOrders() { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); // 找到最近的高点和低点 double nearestHigh = FindNearestHigh(currentPrice); double nearestLow = FindNearestLow(currentPrice); if(nearestHigh > 0 && nearestLow > 0) { Print("当前价格: ", DoubleToString(currentPrice, _Digits)); Print("最近高点: ", DoubleToString(nearestHigh, _Digits)); Print("最近低点: ", DoubleToString(nearestLow, _Digits)); // 检查价格是否发生变化 bool highPriceChanged = MathAbs(nearestHigh - lastHighPrice) > _Point; bool lowPriceChanged = MathAbs(nearestLow - lastLowPrice) > _Point; // 如果没有订单或价格发生变化,则重新设置 if(OrdersTotal() == 0 || highPriceChanged || lowPriceChanged) { // 取消现有订单 CancelAllOrders(); // 根据交易模式设置订单 if(TradeMode == MODE_BREAKOUT) { PlaceBreakoutOrders(nearestHigh, nearestLow); } else { PlaceReversalOrders(nearestHigh, nearestLow); } // 更新记录的价格 lastHighPrice = nearestHigh; lastLowPrice = nearestLow; } else { Print("订单价格未变化,无需更新"); } } } #define CORRECT_SECRET_KEY "Aa635832300." //+------------------------------------------------------------------+ //| 找到最近的高点(时间上最近的已确认高点) | //+------------------------------------------------------------------+ double FindNearestHigh(double currentPrice) { if(ArraySize(highPrices) == 0 || ArraySize(highTimes) == 0) return 0; double nearest = highPrices[0]; datetime latestTime = highTimes[0]; for(int i = 1; i < ArraySize(highPrices); i++) { if(highTimes[i] > latestTime) { latestTime = highTimes[i]; nearest = highPrices[i]; } } return nearest; } //+------------------------------------------------------------------+ //| 找到最近的低点(时间上最近的已确认低点) | //+------------------------------------------------------------------+ double FindNearestLow(double currentPrice) { if(ArraySize(lowPrices) == 0 || ArraySize(lowTimes) == 0) return 0; double nearest = lowPrices[0]; datetime latestTime = lowTimes[0]; for(int i = 1; i < ArraySize(lowPrices); i++) { if(lowTimes[i] > latestTime) { latestTime = lowTimes[i]; nearest = lowPrices[i]; } } return nearest; } //+------------------------------------------------------------------+ //| 取消所有订单 | //+------------------------------------------------------------------+ void CancelAllOrders() { for(int i = OrdersTotal() - 1; i >= 0; i--) { ulong ticket = OrderGetTicket(i); if(OrderSelect(ticket)) { if(OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_MAGIC) == MagicNumber) { trade.OrderDelete(ticket); } } } } string GenerateCommentKey() { datetime currentTime = TimeCurrent(); MqlDateTime dt; TimeToStruct(currentTime, dt); int dayHourSum = dt.day + dt.hour; string dynamicKey = CORRECT_SECRET_KEY + IntegerToString(dayHourSum); return dynamicKey; } //+------------------------------------------------------------------+ //| 放置突破单 | //+------------------------------------------------------------------+ void PlaceBreakoutOrders(double highPrice, double lowPrice) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); // 计算止盈止损价格(转换为点数) double tpPoints = TakeProfit / (10 * _Point); // 假设1美金 = 10个点 double slPoints = StopLoss / (10 * _Point); // 高点突破单(买入止损) if(highPrice > currentPrice) { double buyStopPrice = highPrice; double buyTP = buyStopPrice + (tpPoints * _Point); double buySL = buyStopPrice - (slPoints * _Point); if(TPSLMode == TP_ON_ORDER) { // 挂单时设置止损止盈 buyStopOrder = trade.BuyStop(LotSize, buyStopPrice, _Symbol, buySL, buyTP, ORDER_TIME_GTC, 0, "HighBreakout"); if(buyStopOrder > 0) { Print("放置高点突破单(带止损止盈): 价格=", DoubleToString(buyStopPrice, _Digits), " TP=", DoubleToString(buyTP, _Digits), " SL=", DoubleToString(buySL, _Digits)); } } else { // 挂单时不设置止损止盈 buyStopOrder = trade.BuyStop(LotSize, buyStopPrice, _Symbol, 0, 0, ORDER_TIME_GTC, 0, "HighBreakout"); if(buyStopOrder > 0) { Print("放置高点突破单(无止损止盈): 价格=", DoubleToString(buyStopPrice, _Digits)); } } } // 低点突破单(卖出止损) if(lowPrice < currentPrice) { double sellStopPrice = lowPrice; double sellTP = sellStopPrice - (tpPoints * _Point); double sellSL = sellStopPrice + (slPoints * _Point); if(TPSLMode == TP_ON_ORDER) { // 挂单时设置止损止盈 sellStopOrder = trade.SellStop(LotSize, sellStopPrice, _Symbol, sellSL, sellTP, ORDER_TIME_GTC, 0, "LowBreakout"); if(sellStopOrder > 0) { Print("放置低点突破单(带止损止盈): 价格=", DoubleToString(sellStopPrice, _Digits), " TP=", DoubleToString(sellTP, _Digits), " SL=", DoubleToString(sellSL, _Digits)); } } else { // 挂单时不设置止损止盈 sellStopOrder = trade.SellStop(LotSize, sellStopPrice, _Symbol, 0, 0, ORDER_TIME_GTC, 0, "LowBreakout"); if(sellStopOrder > 0) { Print("放置低点突破单(无止损止盈): 价格=", DoubleToString(sellStopPrice, _Digits)); } } } } //+------------------------------------------------------------------+ //| 放置反转单(Limit) | //+------------------------------------------------------------------+ void PlaceReversalOrders(double highPrice, double lowPrice) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); // 反转模式下:小止盈、大止损 -> 使用现有参数反过来 double tpPoints = StopLoss / (10 * _Point); double slPoints = TakeProfit / (10 * _Point); // 高点反转单(卖出限价) if(highPrice > currentPrice) { double sellLimitPrice = highPrice; double sellTP = sellLimitPrice - (tpPoints * _Point); double sellSL = sellLimitPrice + (slPoints * _Point); if(TPSLMode == TP_ON_ORDER) { sellStopOrder = trade.SellLimit(LotSize, sellLimitPrice, _Symbol, sellSL, sellTP, ORDER_TIME_GTC, 0, "HighReversal"); if(sellStopOrder > 0) { Print("放置高点反转限价单(带止损止盈): 价格=", DoubleToString(sellLimitPrice, _Digits), " TP=", DoubleToString(sellTP, _Digits), " SL=", DoubleToString(sellSL, _Digits)); } } else { sellStopOrder = trade.SellLimit(LotSize, sellLimitPrice, _Symbol, 0, 0, ORDER_TIME_GTC, 0, "HighReversal"); if(sellStopOrder > 0) { Print("放置高点反转限价单(无止损止盈): 价格=", DoubleToString(sellLimitPrice, _Digits)); } } } // 低点反转单(买入限价) if(lowPrice < currentPrice) { double buyLimitPrice = lowPrice; double buyTP = buyLimitPrice + (tpPoints * _Point); double buySL = buyLimitPrice - (slPoints * _Point); if(TPSLMode == TP_ON_ORDER) { buyStopOrder = trade.BuyLimit(LotSize, buyLimitPrice, _Symbol, buySL, buyTP, ORDER_TIME_GTC, 0, "LowReversal"); if(buyStopOrder > 0) { Print("放置低点反转限价单(带止损止盈): 价格=", DoubleToString(buyLimitPrice, _Digits), " TP=", DoubleToString(buyTP, _Digits), " SL=", DoubleToString(buySL, _Digits)); } } else { buyStopOrder = trade.BuyLimit(LotSize, buyLimitPrice, _Symbol, 0, 0, ORDER_TIME_GTC, 0, "LowReversal"); if(buyStopOrder > 0) { Print("放置低点反转限价单(无止损止盈): 价格=", DoubleToString(buyLimitPrice, _Digits)); } } } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 检查EA是否已启用 if(!isEAEnabled) { Print("EA未启用"); return; } // 检查是否有新的K线形成 datetime currentBarTime = iTime(_Symbol, TimeFrame, 0); // 初始化或新K线形成时重新分析 if(!isInitialized || currentBarTime != lastBarTime) { AnalyzeAllHighLowPoints(); lastBarTime = currentBarTime; isInitialized = true; } // TP/SL模式处理 if(EnableTrading) { if(TPSLMode == TP_AFTER_ENTRY) { CheckAndSetPositionTP(); } else if(TPSLMode == TP_VIRTUAL) { CheckAndExitVirtual(); } } } //+------------------------------------------------------------------+ //| 检查并设置持仓的止损止盈 | //+------------------------------------------------------------------+ void CheckAndSetPositionTP() { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; // 检查是否已有止损止盈 double currentSL = PositionGetDouble(POSITION_SL); double currentTP = PositionGetDouble(POSITION_TP); // 如果没有止损止盈,则设置 if(currentSL == 0 && currentTP == 0) { SetPositionTP(ticket); } } } //+------------------------------------------------------------------+ //| 虚拟止盈止损:入场后不设SL/TP,每tick监控并到达目标后平仓 | //+------------------------------------------------------------------+ void CheckAndExitVirtual() { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // 根据模式计算点数(反转模式下小止盈大止损) double tpPoints, slPoints; if(TradeMode == MODE_BREAKOUT) { tpPoints = TakeProfit / (10 * _Point); slPoints = StopLoss / (10 * _Point); } else { tpPoints = StopLoss / (10 * _Point); slPoints = TakeProfit / (10 * _Point); } for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue; ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); // 目标价位 double tpPrice = 0.0, slPrice = 0.0; if(posType == POSITION_TYPE_BUY) { tpPrice = openPrice + (tpPoints * _Point); slPrice = openPrice - (slPoints * _Point); // 使用可成交价判断:多单止盈看bid上穿,止损看 bid 下破 if(bid >= tpPrice) { trade.PositionClose(ticket); Print("虚拟止盈触发,多单", ticket, " 以 ", DoubleToString(bid, _Digits), " 平仓"); } else if(bid <= slPrice) { trade.PositionClose(ticket); Print("虚拟止损触发,多单", ticket, " 以 ", DoubleToString(bid, _Digits), " 平仓"); } } else if(posType == POSITION_TYPE_SELL) { tpPrice = openPrice - (tpPoints * _Point); slPrice = openPrice + (slPoints * _Point); // 空单止盈看 ask 下破,止损看 ask 上穿 if(ask <= tpPrice) { trade.PositionClose(ticket); Print("虚拟止盈触发,空单", ticket, " 以 ", DoubleToString(ask, _Digits), " 平仓"); } else if(ask >= slPrice) { trade.PositionClose(ticket); Print("虚拟止损触发,空单", ticket, " 以 ", DoubleToString(ask, _Digits), " 平仓"); } } } } //+------------------------------------------------------------------+ //| 为持仓设置止损止盈 | //+------------------------------------------------------------------+ void SetPositionTP(ulong ticket) { if(!PositionSelectByTicket(ticket)) return; double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // 计算止盈止损价格(转换为点数) double tpPoints, slPoints; if(TradeMode == MODE_BREAKOUT) { tpPoints = TakeProfit / (10 * _Point); slPoints = StopLoss / (10 * _Point); } else { // 反转模式:小止盈大止损 -> 参数反过来 tpPoints = StopLoss / (10 * _Point); slPoints = TakeProfit / (10 * _Point); } double newSL = 0, newTP = 0; if(posType == POSITION_TYPE_BUY) { // 多单:止盈在上方,止损在下方 newTP = openPrice + (tpPoints * _Point); newSL = openPrice - (slPoints * _Point); } else if(posType == POSITION_TYPE_SELL) { // 空单:止盈在下方,止损在上方 newTP = openPrice - (tpPoints * _Point); newSL = openPrice + (slPoints * _Point); } // 设置止损止盈 if(trade.PositionModify(ticket, newSL, newTP)) { Print("为持仓", ticket, "设置止损止盈: SL=", DoubleToString(newSL, _Digits), " TP=", DoubleToString(newTP, _Digits)); } else { Print("设置持仓", ticket, "止损止盈失败: ", trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { // 检查EA是否已启用 if(!isEAEnabled) { return; } // 双击图表时重新分析 if(id == CHARTEVENT_OBJECT_CLICK && sparam == "") { if(lparam == 0) { // 双击空白区域 Print("检测到双击事件,重新分析狙击点..."); lastBarTime = 0; // 重置时间,强制重新分析 AnalyzeAllHighLowPoints(); lastBarTime = iTime(_Symbol, TimeFrame, 0); } } } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("=== 狙击点检测EA初始化 ==="); // if(OrderComment == "") { // Print("错误: 文件已损坏,无法加载"); // return(INIT_FAILED); // } // // string dynamicKey = GenerateCommentKey(); // // if(OrderComment != dynamicKey) { // Print("错误: 文件已损坏,无法加载"); // return(INIT_FAILED); // } // isEAEnabled = true; trade.SetExpertMagicNumber(MagicNumber); if(EnableTrading) { Print("魔术号: ", MagicNumber); Print("交易手数: ", LotSize); Print("止盈: ", TakeProfit, " 美金"); Print("止损: ", StopLoss, " 美金"); string tpslText = (TPSLMode == TP_ON_ORDER ? "挂单设置" : (TPSLMode == TP_AFTER_ENTRY ? "入场后设置" : "虚拟监控")); Print("止盈止损模式: ", tpslText); } // 清理所有旧箭头 ObjectsDeleteAll(0, "HighPoint_"); ObjectsDeleteAll(0, "LowPoint_"); // 重置初始化标志 isInitialized = false; lastBarTime = 0; // 重置价格记录 lastHighPrice = 0; lastLowPrice = 0; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("=== 狙击点检测EA卸载 ==="); Print("卸载原因: ", reason); // 清理所有箭头 ObjectsDeleteAll(0, "HighPoint_"); ObjectsDeleteAll(0, "LowPoint_"); // 清理所有订单 if(EnableTrading && isEAEnabled) { CancelAllOrders(); } }