657 lines
No EOL
26 KiB
MQL5
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();
|
|
}
|
|
} |