mql5/Experts/Sniper.mq5
1Morty 50a3adba89
2025-09-08 21:00:32 +08:00

657 lines
No EOL
26 KiB
MQL5

//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
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();
}
}