//+------------------------------------------------------------------+
//| MultiSignalAlertPro.mq5|
//| Copyright 2023, MetaQuotes Software Corp.|
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "5.7" // 版本更新:修正移动止损逻辑(盈亏比<1不启动,达到各阶段后自动保护)
// === 输入参数部分 ===
// 双EMA趋势设置
input group "=== 双EMA趋势设置 ==="
input bool 启用双EMA = true; // 启用双EMA趋势系统
input int EMA_慢线周期 = 200; // 慢EMA周期
input int EMA_快线周期 = 50; // 快EMA周期
input ENUM_APPLIED_PRICE EMA_价格类型 = PRICE_CLOSE; // EMA价格类型
// MACD参数组
input group "=== MACD短期趋势设置 ==="
input int MACD_快速线 = 12; // MACD快速EMA周期
input int MACD_慢速线 = 26; // MACD慢速线周期
input int MACD_信号线 = 9; // MACD信号线周期
input ENUM_APPLIED_PRICE MACD_价格类型 = PRICE_CLOSE; // MACD价格类型
// RSI参数组
input group "=== RSI动能设置 ==="
input int RSI_周期 = 14; // RSI周期
input ENUM_APPLIED_PRICE RSI_价格类型 = PRICE_CLOSE; // RSI价格类型
input double RSI_分界线 = 50.0; // RSI多空分界线
// 止损止盈参数组(已移除K线形态止损)
input group "=== 止损止盈设置 ==="
input bool 启用ATR止损 = true; // 启用ATR动态止损
input int ATR_止损周期 = 14; // ATR周期
input double ATR_止损倍数 = 1.5; // ATR止损倍数
input double 止损缓冲点数 = 10.0; // 止损缓冲点数
input double 盈亏比例 = 3.0; // 盈亏比
input double 止盈缓冲点数 = 5.0; // 止盈缓冲点数
// 移动止损设置参数组
input group "=== 移动止损设置 ==="
input bool 启用移动止损 = true; // 启用移动止损
input int 移动止损检查间隔秒数 = 30; // 检查间隔(秒)
input bool 移动止损使用百分比 = true; // true=百分比模式,false=点数模式
// 百分比模式参数
input double 移动止损启动基准百分比 = 1.0; // 启动基准百分比(此参数在修正逻辑后已不再使用,但为兼容保留)
input double 第一阶段回撤百分比 = 10.0; // 盈亏比<1时的回撤百分比(实际用于盈亏比1-2阶段?根据原设计,当已实现盈亏比<1时使用此参数)
input double 第二阶段回撤百分比 = 50.0; // 盈亏比≥1时的回撤百分比
// 点数模式参数
input double 移动止损启动基准点数 = 10.0; // 启动基准点数(修正后不再使用)
input double 第一阶段回撤点数 = 30.0; // 盈亏比<1时的回撤点数
input double 第二阶段固定回撤点数 = 20.0; // 盈亏比≥1时的固定回撤点数
input bool 启用分段止盈 = true; // 启用分段止盈
// 交易执行参数组
input group "=== 交易执行设置 ==="
input bool 启用自动交易 = false; // 启用自动交易
input double 固定手数 = 0.1; // 固定手数
input double 最大允许点差 = 20.0; // 最大允许点差(点)
input int 最大订单数 = 1; // 最大同时持仓数
input ulong 魔术数字 = 202312; // 魔术数字
input string 订单注释 = "双EMA趋势EA"; // 订单注释
// 风险管理参数组
input group "=== 风险管理 ==="
input double 单笔风险比例 = 2.0; // 每单风险百分比(输入值)
input bool 使用资金管理 = true; // 使用资金管理
input double 最小手数 = 0.01; // 最小手数
input double 最大手数 = 10.0; // 最大手数
input bool 是美分账户 = false; // 是否为美分账户
// 预警设置参数组
input group "=== 预警设置 ==="
input bool 启用声音预警 = true; // 声音预警
input bool 启用推送通知 = false; // 推送通知
input bool 启用邮件预警 = false; // 邮件预警
input bool 图表显示信号 = true; // 图表显示信号
input bool 图表显示止损线 = true; // 显示止损线
input bool 图表显示EMA = true; // 显示EMA值
// 指标显示设置
input group "=== 指标显示设置 ==="
input bool 图表显示MACD = true; // 显示MACD
input bool 图表显示RSI = true; // 显示RSI
input bool 图表显示ATR = true; // 显示ATR
// === 全局变量声明 ===
int ema_慢线句柄, ema_快线句柄, macd_句柄, rsi_句柄, atr_句柄;
double ema_慢线缓冲区[], ema_快线缓冲区[], macd_主线缓冲区[], macd_信号线缓冲区[], rsi_缓冲区[], atr_缓冲区[];
datetime 上次预警时间 = 0;
int 上次信号类型 = 0;
// 价格数组
double 收盘价数组[], 开盘价数组[], 最高价数组[], 最低价数组[];
// 移动止损相关变量
struct 订单跟踪信息
{
ulong 订单号;
int 类型; // 1=多单, -1=空单
double 入场价;
double 初始止损价;
double 初始止盈价;
double 当前移动止损价;
double 最高价; // 多单记录最高价
double 最低价; // 空单记录最低价
int 阶段; // 0=未启动, 1=第一阶段, 2=第二阶段...
double 盈亏比;
datetime 最后检查时间;
double 已实现盈亏比; // 已实现的盈亏比
double 阶段起始价; // 当前阶段起始价格
bool 阶段激活; // 当前阶段是否已激活
};
订单跟踪信息 跟踪订单数组[]; // 动态数组
int 最大跟踪订单数 = 100; // 最大跟踪订单数
int 跟踪订单数量 = 0;
datetime 上次移动止损检查时间 = 0;
// 图表对象句柄
int ema_慢线_图表句柄, ema_快线_图表句柄, macd_图表句柄, rsi_图表句柄, atr_图表句柄;
//+------------------------------------------------------------------+
//| 前置函数声明 |
//+------------------------------------------------------------------+
void 创建图表对象();
bool 更新指标数据();
void 检查交易信号();
bool 检查MACD交叉(int 交叉类型);
bool 检查EMA交叉(int 交叉类型);
double 根据ATR寻找止损点(int 信号类型, double 入场价);
double 寻找前低点(int 回溯周期);
double 寻找前高点(int 回溯周期);
string 生成信号文本(int 信号类型, double 入场价, double 止损价, double 止盈价);
double 计算手数(double 入场价, double 止损价);
bool 执行交易(int 信号类型, double 手数, double 入场价, double 止损价, double 止盈价);
string 获取错误描述(int 错误代码);
int 统计订单数量();
void 发送预警(string 信息, int 信号类型);
void 更新图表信号(string 文本, int 信号类型);
void 更新图表显示();
void 绘制止损线(double 止损价, int 信号类型);
void 删除止损线();
void 更新EMA显示();
// 移动止损相关函数(使用引用)
void 更新移动止损();
int 查找跟踪订单(ulong 订单编号);
int 添加跟踪订单(ulong 订单编号, int 订单类型, double 入场价, double 初始止损价, double 初始止盈价);
void 更新订单价格记录(int 跟踪索引, double 当前价格, int 订单类型);
void 应用移动止损(int 跟踪索引, double 当前价格, int 订单类型);
double 计算移动止损价(订单跟踪信息 &跟踪信息, double 当前价格, double 当前利润点数);
bool 需要更新止损价(订单跟踪信息 &跟踪信息, double 新止损价, int 订单类型);
bool 修改订单止损价(ulong 订单编号, double 新止损价);
void 清理跟踪订单();
bool 检查是否达到止盈阶段(订单跟踪信息 &跟踪信息, double 当前价格);
// 指标显示函数
void 初始化指标显示();
void 创建EMA指标线();
void 创建MACD指标线();
void 创建RSI指标线();
void 创建ATR指标线();
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 初始化指标句柄
ema_慢线句柄 = iMA(_Symbol, _Period, EMA_慢线周期, 0, MODE_EMA, EMA_价格类型);
if(启用双EMA) ema_快线句柄 = iMA(_Symbol, _Period, EMA_快线周期, 0, MODE_EMA, EMA_价格类型);
macd_句柄 = iMACD(_Symbol, _Period, MACD_快速线, MACD_慢速线, MACD_信号线, MACD_价格类型);
rsi_句柄 = iRSI(_Symbol, _Period, RSI_周期, RSI_价格类型);
atr_句柄 = iATR(_Symbol, _Period, ATR_止损周期);
if(ema_慢线句柄 == INVALID_HANDLE ||
(启用双EMA && ema_快线句柄 == INVALID_HANDLE) ||
macd_句柄 == INVALID_HANDLE || rsi_句柄 == INVALID_HANDLE || atr_句柄 == INVALID_HANDLE)
{
Print("指标初始化失败!");
return INIT_FAILED;
}
ArraySetAsSeries(ema_慢线缓冲区, true);
if(启用双EMA) ArraySetAsSeries(ema_快线缓冲区, true);
ArraySetAsSeries(macd_主线缓冲区, true);
ArraySetAsSeries(macd_信号线缓冲区, true);
ArraySetAsSeries(rsi_缓冲区, true);
ArraySetAsSeries(atr_缓冲区, true);
ArraySetAsSeries(收盘价数组, true);
ArraySetAsSeries(开盘价数组, true);
ArraySetAsSeries(最高价数组, true);
ArraySetAsSeries(最低价数组, true);
ArrayResize(跟踪订单数组, 最大跟踪订单数);
if(图表显示信号) 创建图表对象();
初始化指标显示();
string 交易模式 = 启用自动交易 ? "自动交易模式" : "预警模式";
string 趋势模式 = 启用双EMA ? "双EMA趋势" : "单EMA趋势";
string 移动止损模式 = 移动止损使用百分比 ? "百分比模式" : "点数模式";
string 止盈模式 = 启用分段止盈 ? "分段止盈" : "普通止盈";
string 账户模式 = 是美分账户 ? "美分账户" : "美元账户";
Print("双EMA趋势交易系统已启动 - " + 交易模式 + " - " + 趋势模式 + " - 移动止损:" + 移动止损模式 + " - " + 止盈模式 + " - " + 账户模式);
Print("风险比例硬编码除以10生效:输入值×0.1为实际风险比例");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 创建图表对象函数 |
//+------------------------------------------------------------------+
void 创建图表对象()
{
ObjectCreate(0, "Signal_Label", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "Signal_Label", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, "Signal_Label", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "Signal_Label", OBJPROP_YDISTANCE, 20);
ObjectSetString(0, "Signal_Label", OBJPROP_TEXT, "等待信号...");
ObjectSetInteger(0, "Signal_Label", OBJPROP_FONTSIZE, 10);
ObjectCreate(0, "Trend_Label", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "Trend_Label", OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, "Trend_Label", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "Trend_Label", OBJPROP_YDISTANCE, 40);
string 趋势文本 = 启用双EMA ? "双EMA趋势" : "单EMA趋势";
ObjectSetString(0, "Trend_Label", OBJPROP_TEXT, "趋势模式: " + 趋势文本);
ObjectSetInteger(0, "Trend_Label", OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, "Trend_Label", OBJPROP_COLOR, clrSkyBlue);
if(图表显示EMA)
{
ObjectCreate(0, "EMA_Label", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "EMA_Label", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, "EMA_Label", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "EMA_Label", OBJPROP_YDISTANCE, 40);
if(启用双EMA)
ObjectSetString(0, "EMA_Label", OBJPROP_TEXT, "快EMA: 等待... | 慢EMA: 等待...");
else
ObjectSetString(0, "EMA_Label", OBJPROP_TEXT, "慢EMA: 等待...");
ObjectSetInteger(0, "EMA_Label", OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, "EMA_Label", OBJPROP_COLOR, clrYellow);
}
ObjectCreate(0, "Mode_Label", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "Mode_Label", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, "Mode_Label", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "Mode_Label", OBJPROP_YDISTANCE, 20);
string 模式文本 = 启用自动交易 ? "自动交易模式" : "预警模式";
string 移动止损文本 = 启用移动止损 ? (移动止损使用百分比 ? "(%模式)" : "(点数模式)") : "";
ObjectSetString(0, "Mode_Label", OBJPROP_TEXT, "模式: " + 模式文本 + 移动止损文本);
ObjectSetInteger(0, "Mode_Label", OBJPROP_COLOR, 启用自动交易 ? clrRed : clrGreen);
if(启用移动止损)
{
ObjectCreate(0, "Trailing_Label", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "Trailing_Label", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, "Trailing_Label", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "Trailing_Label", OBJPROP_YDISTANCE, 60);
string 跟踪状态 = "移动止损: 启用";
string 跟踪模式 = 移动止损使用百分比 ? "(百分比)" : "(点数)";
ObjectSetString(0, "Trailing_Label", OBJPROP_TEXT, 跟踪状态 + 跟踪模式);
ObjectSetInteger(0, "Trailing_Label", OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, "Trailing_Label", OBJPROP_COLOR, clrOrange);
}
}
//+------------------------------------------------------------------+
//| EA去初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ema_慢线句柄 != INVALID_HANDLE) IndicatorRelease(ema_慢线句柄);
if(启用双EMA && ema_快线句柄 != INVALID_HANDLE) IndicatorRelease(ema_快线句柄);
if(macd_句柄 != INVALID_HANDLE) IndicatorRelease(macd_句柄);
if(rsi_句柄 != INVALID_HANDLE) IndicatorRelease(rsi_句柄);
if(atr_句柄 != INVALID_HANDLE) IndicatorRelease(atr_句柄);
ObjectDelete(0, "Signal_Label");
ObjectDelete(0, "Trend_Label");
ObjectDelete(0, "EMA_Label");
ObjectDelete(0, "Mode_Label");
ObjectDelete(0, "Trailing_Label");
删除止损线();
ArrayFree(跟踪订单数组);
跟踪订单数量 = 0;
Print("交易系统已停止");
}
//+------------------------------------------------------------------+
//| 主执行函数 |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime 上次K线时间 = 0;
datetime 当前K线时间 = iTime(_Symbol, _Period, 0);
if(上次K线时间 == 当前K线时间) return;
上次K线时间 = 当前K线时间;
if(CopyClose(_Symbol, _Period, 0, 3, 收盘价数组) < 3 ||
CopyOpen(_Symbol, _Period, 0, 3, 开盘价数组) < 3 ||
CopyHigh(_Symbol, _Period, 0, 3, 最高价数组) < 3 ||
CopyLow(_Symbol, _Period, 0, 3, 最低价数组) < 3)
{
Print("错误: 无法复制价格数据");
return;
}
if(!更新指标数据()) return;
检查交易信号();
更新图表显示();
if(图表显示EMA) 更新EMA显示();
static datetime 上次移动止损时间 = 0;
datetime 当前时间 = TimeCurrent();
if(启用移动止损 && (当前时间 - 上次移动止损时间 >= 移动止损检查间隔秒数))
{
更新移动止损();
上次移动止损时间 = 当前时间;
}
}
//+------------------------------------------------------------------+
//| 更新指标数据函数 |
//+------------------------------------------------------------------+
bool 更新指标数据()
{
if(CopyBuffer(ema_慢线句柄, 0, 0, 5, ema_慢线缓冲区) < 5) return false;
if(启用双EMA && CopyBuffer(ema_快线句柄, 0, 0, 5, ema_快线缓冲区) < 5) return false;
if(CopyBuffer(macd_句柄, 0, 0, 5, macd_主线缓冲区) < 5) return false;
if(CopyBuffer(macd_句柄, 1, 0, 5, macd_信号线缓冲区) < 5) return false;
if(CopyBuffer(rsi_句柄, 0, 0, 5, rsi_缓冲区) < 5) return false;
if(CopyBuffer(atr_句柄, 0, 0, 5, atr_缓冲区) < 5) return false;
return true;
}
//+------------------------------------------------------------------+
//| 检查交易信号函数 |
//+------------------------------------------------------------------+
void 检查交易信号()
{
double 当前收盘价 = 收盘价数组[0];
double 当前最高价 = 最高价数组[0];
double 当前最低价 = 最低价数组[0];
double 当前点差 = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
if(当前点差 > 最大允许点差 * _Point)
{
Print("点差过高: ", 当前点差 / _Point, " 点,跳过交易");
return;
}
if(统计订单数量() >= 最大订单数) return;
bool ema看多 = false, ema看空 = false;
if(启用双EMA)
{
ema看多 = (当前收盘价 > ema_快线缓冲区[0]) && (ema_快线缓冲区[0] > ema_慢线缓冲区[0]);
ema看空 = (当前收盘价 < ema_快线缓冲区[0]) && (ema_快线缓冲区[0] < ema_慢线缓冲区[0]);
}
else
{
ema看多 = 当前收盘价 > ema_慢线缓冲区[0];
ema看空 = 当前收盘价 < ema_慢线缓冲区[0];
}
bool macd金叉 = 检查MACD交叉(1);
bool macd死叉 = 检查MACD交叉(-1);
bool rsi看多 = rsi_缓冲区[0] > RSI_分界线;
bool rsi看空 = rsi_缓冲区[0] < RSI_分界线;
int 信号类型 = 0;
double 止损价 = 0, 止盈价 = 0;
double 入场价 = 当前收盘价;
if(ema看多 && macd金叉 && rsi看多)
{
信号类型 = 1;
if(启用ATR止损)
{
止损价 = 根据ATR寻找止损点(信号类型, 入场价);
Print("多单ATR止损价: ", 止损价);
}
else
{
止损价 = 寻找前低点(50);
Print("多单传统止损价: ", 止损价);
}
if(止损价 <= 0 || 止损价 >= 当前收盘价)
{
Print("警告: 多单止损价无效,调整");
止损价 = 当前收盘价 - 50 * _Point;
}
止盈价 = 入场价 + (入场价 - 止损价) * 盈亏比例;
}
else if(ema看空 && macd死叉 && rsi看空)
{
信号类型 = -1;
if(启用ATR止损)
{
止损价 = 根据ATR寻找止损点(信号类型, 入场价);
Print("空单ATR止损价: ", 止损价);
}
else
{
止损价 = 寻找前高点(50);
Print("空单传统止损价: ", 止损价);
}
if(止损价 <= 0 || 止损价 <= 当前收盘价)
{
Print("警告: 空单止损价无效,调整");
止损价 = 当前收盘价 + 50 * _Point;
}
止盈价 = 入场价 - (止损价 - 入场价) * 盈亏比例;
}
if(信号类型 != 0)
{
string 信号文本 = 生成信号文本(信号类型, 入场价, 止损价, 止盈价);
发送预警(信号文本, 信号类型);
if(启用自动交易)
{
double 交易手数 = 计算手数(入场价, 止损价);
if(执行交易(信号类型, 交易手数, 入场价, 止损价, 止盈价) && 图表显示止损线)
绘制止损线(止损价, 信号类型);
}
else if(图表显示止损线)
{
绘制止损线(止损价, 信号类型);
}
更新图表信号(信号文本, 信号类型);
}
}
//+------------------------------------------------------------------+
//| 检查是否达到止盈阶段(使用引用) |
//+------------------------------------------------------------------+
bool 检查是否达到止盈阶段(订单跟踪信息 &跟踪信息, double 当前价格)
{
double 止损距离点数 = MathAbs(跟踪信息.入场价 - 跟踪信息.初始止损价) / _Point;
if(止损距离点数 == 0) return false;
double 当前利润点数 = (当前价格 - 跟踪信息.入场价) / _Point * 跟踪信息.类型;
double 当前盈亏比 = 当前利润点数 / 止损距离点数;
int 理论阶段 = (int)MathFloor(当前盈亏比);
if(理论阶段 < 1) 理论阶段 = 0;
if(理论阶段 > 跟踪信息.已实现盈亏比)
{
跟踪信息.已实现盈亏比 = 理论阶段;
跟踪信息.阶段起始价 = 当前价格;
跟踪信息.阶段激活 = true;
PrintFormat("订单 %I64u 达到止盈阶段 %d (盈亏比 %.2f)", 跟踪信息.订单号, 理论阶段, 当前盈亏比);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查MACD交叉函数 |
//+------------------------------------------------------------------+
bool 检查MACD交叉(int 交叉类型)
{
if(ArraySize(macd_主线缓冲区) < 2 || ArraySize(macd_信号线缓冲区) < 2) return false;
if(交叉类型 == 1)
return (macd_主线缓冲区[1] < macd_信号线缓冲区[1] && macd_主线缓冲区[0] > macd_信号线缓冲区[0]);
else if(交叉类型 == -1)
return (macd_主线缓冲区[1] > macd_信号线缓冲区[1] && macd_主线缓冲区[0] < macd_信号线缓冲区[0]);
return false;
}
//+------------------------------------------------------------------+
//| 检查EMA交叉函数 |
//+------------------------------------------------------------------+
bool 检查EMA交叉(int 交叉类型)
{
if(ArraySize(ema_快线缓冲区) < 2 || ArraySize(ema_慢线缓冲区) < 2) return false;
if(交叉类型 == 1)
return (ema_快线缓冲区[1] < ema_慢线缓冲区[1] && ema_快线缓冲区[0] > ema_慢线缓冲区[0]);
else if(交叉类型 == -1)
return (ema_快线缓冲区[1] > ema_慢线缓冲区[1] && ema_快线缓冲区[0] < ema_慢线缓冲区[0]);
return false;
}
//+------------------------------------------------------------------+
//| 根据ATR寻找止损点 |
//+------------------------------------------------------------------+
double 根据ATR寻找止损点(int 信号类型, double 入场价)
{
if(ArraySize(atr_缓冲区) < 1) { Print("警告: ATR数据不完整"); return 0; }
double 当前ATR = atr_缓冲区[0];
if(当前ATR <= 0) { Print("警告: ATR值无效"); return 0; }
if(信号类型 == 1) // 多单
{
double atr止损价 = 入场价 - 当前ATR * ATR_止损倍数;
double 缓冲止损价 = atr止损价 - 止损缓冲点数 * _Point;
if(缓冲止损价 >= 入场价)
{
Print("警告: ATR止损价不合理,调整");
缓冲止损价 = 入场价 - 50 * _Point;
}
return 缓冲止损价;
}
else if(信号类型 == -1) // 空单
{
double atr止损价 = 入场价 + 当前ATR * ATR_止损倍数;
double 缓冲止损价 = atr止损价 + 止损缓冲点数 * _Point;
if(缓冲止损价 <= 入场价)
{
Print("警告: ATR止损价不合理,调整");
缓冲止损价 = 入场价 + 50 * _Point;
}
return 缓冲止损价;
}
return 0;
}
//+------------------------------------------------------------------+
//| 更新EMA显示函数 |
//+------------------------------------------------------------------+
void 更新EMA显示()
{
if(ArraySize(ema_慢线缓冲区) == 0) return;
string ema文本;
if(启用双EMA && ArraySize(ema_快线缓冲区) > 0)
{
ema文本 = StringFormat("快EMA(%d): %.5f | 慢EMA(%d): %.5f", EMA_快线周期, ema_快线缓冲区[0], EMA_慢线周期, ema_慢线缓冲区[0]);
string 关系 = "";
if(ema_快线缓冲区[0] > ema_慢线缓冲区[0]) { 关系 = " (快>慢)"; ObjectSetInteger(0, "EMA_Label", OBJPROP_COLOR, clrLime); }
else if(ema_快线缓冲区[0] < ema_慢线缓冲区[0]) { 关系 = " (快<慢)"; ObjectSetInteger(0, "EMA_Label", OBJPROP_COLOR, clrRed); }
else { 关系 = " (快=慢)"; ObjectSetInteger(0, "EMA_Label", OBJPROP_COLOR, clrYellow); }
ObjectSetString(0, "EMA_Label", OBJPROP_TEXT, ema文本 + 关系);
}
else
{
ema文本 = StringFormat("慢EMA(%d): %.5f", EMA_慢线周期, ema_慢线缓冲区[0]);
ObjectSetString(0, "EMA_Label", OBJPROP_TEXT, ema文本);
ObjectSetInteger(0, "EMA_Label", OBJPROP_COLOR, clrYellow);
}
}
//+------------------------------------------------------------------+
//| 寻找前一个低点函数(备用传统方法) |
//+------------------------------------------------------------------+
double 寻找前低点(int 回溯周期)
{
if(回溯周期 <= 0) return 0;
double 低点数组[];
int 需要K线数 = MathMin(回溯周期, 100);
int 复制数量 = CopyLow(_Symbol, _Period, 1, 需要K线数, 低点数组);
if(复制数量 <= 0)
{
double 当前价格 = SymbolInfoDouble(_Symbol, SYMBOL_BID);
return 当前价格 - 100 * _Point - 止损缓冲点数 * _Point;
}
double 最低价 = 低点数组[0];
for(int i = 1; i < 复制数量; i++) if(低点数组[i] < 最低价) 最低价 = 低点数组[i];
double 最终止损价 = 最低价 - 止损缓冲点数 * _Point;
double 当前价格 = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(最终止损价 >= 当前价格)
{
Print("警告: 止损价不合理,调整");
最终止损价 = 当前价格 - 50 * _Point;
}
return 最终止损价;
}
//+------------------------------------------------------------------+
//| 寻找前一个高点函数(备用传统方法) |
//+------------------------------------------------------------------+
double 寻找前高点(int 回溯周期)
{
if(回溯周期 <= 0) return 0;
double 高点数组[];
int 需要K线数 = MathMin(回溯周期, 100);
int 复制数量 = CopyHigh(_Symbol, _Period, 1, 需要K线数, 高点数组);
if(复制数量 <= 0)
{
double 当前价格 = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
return 当前价格 + 100 * _Point + 止损缓冲点数 * _Point;
}
double 最高价 = 高点数组[0];
for(int i = 1; i < 复制数量; i++) if(高点数组[i] > 最高价) 最高价 = 高点数组[i];
double 最终止损价 = 最高价 + 止损缓冲点数 * _Point;
double 当前价格 = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(最终止损价 <= 当前价格)
{
Print("警告: 止损价不合理,调整");
最终止损价 = 当前价格 + 50 * _Point;
}
return 最终止损价;
}
//+------------------------------------------------------------------+
//| 绘制止损线函数 |
//+------------------------------------------------------------------+
void 绘制止损线(double 止损价, int 信号类型)
{
删除止损线();
string 止损线名称 = "StopLoss_Line";
ObjectCreate(0, 止损线名称, OBJ_HLINE, 0, 0, 止损价);
if(信号类型 == 1)
{
ObjectSetInteger(0, 止损线名称, OBJPROP_COLOR, clrRed);
ObjectSetString(0, 止损线名称, OBJPROP_TEXT, "多单止损: " + DoubleToString(止损价, _Digits));
}
else
{
ObjectSetInteger(0, 止损线名称, OBJPROP_COLOR, clrBlue);
ObjectSetString(0, 止损线名称, OBJPROP_TEXT, "空单止损: " + DoubleToString(止损价, _Digits));
}
ObjectSetInteger(0, 止损线名称, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, 止损线名称, OBJPROP_STYLE, STYLE_DASH);
}
//+------------------------------------------------------------------+
//| 删除止损线函数 |
//+------------------------------------------------------------------+
void 删除止损线() { ObjectDelete(0, "StopLoss_Line"); }
//+------------------------------------------------------------------+
//| 生成信号文本函数 |
//+------------------------------------------------------------------+
string 生成信号文本(int 信号类型, double 入场价, double 止损价, double 止盈价)
{
string 方向 = (信号类型 > 0) ? "做多" : "做空";
string 趋势模式 = 启用双EMA ? "(双EMA)" : "(单EMA)";
string 止损策略 = 启用ATR止损 ? "ATR动态止损" : "传统止损";
string 移动止损模式 = 启用移动止损 ? (移动止损使用百分比 ? "百分比移动止损" : "点数移动止损") : "无移动止损";
string 止盈模式 = 启用分段止盈 ? "分段止盈" : "普通止盈";
string 文本 = StringFormat("【%s信号】%s\n入场价: %.5f\n止损价: %.5f\n止盈价: %.5f\n风险回报比: 1:%.1f\n止损距离: %.1f 点\n止盈距离: %.1f 点\n止损策略: %s\n移动止损: %s\n止盈模式: %s\n\n共振条件:\n",
方向, 趋势模式, 入场价, 止损价, 止盈价, 盈亏比例,
MathAbs(入场价 - 止损价) / _Point, MathAbs(止盈价 - 入场价) / _Point,
止损策略, 移动止损模式, 止盈模式);
if(启用双EMA)
{
文本 += StringFormat("✓ 价格在快EMA(%d)%s\n", EMA_快线周期, (信号类型 > 0 ? "上方" : "下方"));
文本 += StringFormat("✓ 快EMA在慢EMA(%d)%s\n", EMA_慢线周期, (信号类型 > 0 ? "上方" : "下方"));
}
else
{
文本 += StringFormat("✓ 价格在EMA%d%s\n", EMA_慢线周期, (信号类型 > 0 ? "上方" : "下方"));
}
文本 += StringFormat("✓ MACD%s\n", (信号类型 > 0 ? "金叉" : "死叉"));
文本 += StringFormat("✓ RSI在50%s", (信号类型 > 0 ? "上方" : "下方"));
return 文本;
}
//+------------------------------------------------------------------+
//| 计算交易手数函数 |
//+------------------------------------------------------------------+
double 计算手数(double 入场价, double 止损价)
{
if(!使用资金管理) return MathMin(MathMax(固定手数, 最小手数), 最大手数);
double 账户余额 = AccountInfoDouble(ACCOUNT_BALANCE);
// 硬编码风险比例除以10:输入值 × 0.1 作为实际风险比例
double 有效风险比例 = 单笔风险比例 * 0.1;
double 风险金额 = 账户余额 * 有效风险比例 / 100.0;
if(是美分账户)
{
风险金额 /= 100.0;
Print("美分账户转换:风险金额 = ", 风险金额, " 美元");
}
double 止损点数 = MathAbs(入场价 - 止损价) / _Point;
double 每点价值 = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(止损点数 == 0 || 每点价值 == 0) return 固定手数;
double 手数 = 风险金额 / (止损点数 * 每点价值);
PrintFormat("资金管理: 余额=%.2f, 有效风险比例=%.2f%%, 风险金额=%.2f, 止损点数=%.1f, 每点价值=%.6f, 原始手数=%.4f",
账户余额, 有效风险比例, 风险金额, 止损点数, 每点价值, 手数);
手数 = NormalizeDouble(手数, 2);
手数 = MathMin(MathMax(手数, 最小手数), 最大手数);
return 手数;
}
//+------------------------------------------------------------------+
//| 执行交易函数 |
//+------------------------------------------------------------------+
bool 执行交易(int 信号类型, double 手数, double 入场价, double 止损价, double 止盈价)
{
MqlTradeRequest 交易请求;
MqlTradeResult 交易结果;
ZeroMemory(交易请求); ZeroMemory(交易结果);
交易请求.action = TRADE_ACTION_DEAL;
交易请求.symbol = _Symbol;
交易请求.volume = 手数;
交易请求.magic = 魔术数字;
交易请求.comment = 订单注释;
交易请求.type_filling = ORDER_FILLING_FOK;
if(信号类型 > 0)
{
交易请求.type = ORDER_TYPE_BUY;
交易请求.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
交易请求.sl = NormalizeDouble(止损价, _Digits);
交易请求.tp = NormalizeDouble(止盈价, _Digits);
}
else
{
交易请求.type = ORDER_TYPE_SELL;
交易请求.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
交易请求.sl = NormalizeDouble(止损价, _Digits);
交易请求.tp = NormalizeDouble(止盈价, _Digits);
}
bool 成功 = OrderSend(交易请求, 交易结果);
if(成功 && 交易结果.retcode == TRADE_RETCODE_DONE)
{
Print("交易执行成功! 订单号: ", 交易结果.order, ", 手数: ", 手数);
return true;
}
else
{
Print("交易执行失败! 错误代码: ", 交易结果.retcode, ", 描述: ", 获取错误描述(交易结果.retcode));
return false;
}
}
//+------------------------------------------------------------------+
//| 获取错误描述函数 |
//+------------------------------------------------------------------+
string 获取错误描述(int 错误代码)
{
switch(错误代码)
{
case 10004: return "交易服务器繁忙";
case 10006: return "交易请求被拒绝";
case 10007: return "交易上下文繁忙";
case 10008: return "订单超时";
case 10009: return "订单价格已变化";
case 10010: return "无效的止损或止盈";
case 10011: return "无效的手数";
case 10012: return "无效的价格";
case 10013: return "交易被禁用";
case 10014: return "保证金不足";
case 10015: return "市场已关闭";
case 10016: return "无效的交易请求";
default: return "未知错误: " + IntegerToString(错误代码);
}
}
//+------------------------------------------------------------------+
//| 统计当前订单数量函数 |
//+------------------------------------------------------------------+
int 统计订单数量()
{
int 数量 = 0;
int 持仓总数 = PositionsTotal();
for(int i = 0; i < 持仓总数; i++)
{
ulong 订单号 = PositionGetTicket(i);
if(订单号 > 0 && PositionSelectByTicket(订单号) &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
(ulong)PositionGetInteger(POSITION_MAGIC) == 魔术数字)
数量++;
}
int 挂单总数 = OrdersTotal();
for(int i = 0; i < 挂单总数; i++)
{
ulong 订单号 = OrderGetTicket(i);
if(订单号 > 0 &&
OrderGetString(ORDER_SYMBOL) == _Symbol &&
(ulong)OrderGetInteger(ORDER_MAGIC) == 魔术数字)
数量++;
}
return 数量;
}
//+------------------------------------------------------------------+
//| 发送预警函数 |
//+------------------------------------------------------------------+
void 发送预警(string 信息, int 信号类型)
{
string 预警标题 = (信号类型 > 0) ? "【多头趋势信号】" : "【空头趋势信号】";
string 趋势模式 = 启用双EMA ? "(双EMA)" : "(单EMA)";
string 完整信息 = 预警标题 + 趋势模式 + " - " + Symbol() + " " + EnumToString(_Period) + "\n" + 信息;
if(启用声音预警) { Alert(完整信息); PlaySound("alert.wav"); }
if(启用推送通知) SendNotification(预警标题 + ": " + Symbol() + " " + EnumToString(_Period));
if(启用邮件预警) SendMail(预警标题, 完整信息);
Print(TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS), " - ", 预警标题);
Print(信息);
}
//+------------------------------------------------------------------+
//| 更新图表信号显示函数 |
//+------------------------------------------------------------------+
void 更新图表信号(string 文本, int 信号类型)
{
color 文字颜色 = (信号类型 > 0) ? clrLime : clrRed;
ObjectSetString(0, "Signal_Label", OBJPROP_TEXT, 文本);
ObjectSetInteger(0, "Signal_Label", OBJPROP_COLOR, 文字颜色);
string 趋势模式 = 启用双EMA ? "双EMA趋势" : "单EMA趋势";
ObjectSetString(0, "Trend_Label", OBJPROP_TEXT, "趋势模式: " + 趋势模式);
}
//+------------------------------------------------------------------+
//| 更新图表显示函数 |
//+------------------------------------------------------------------+
void 更新图表显示()
{
double 账户净值 = AccountInfoDouble(ACCOUNT_EQUITY);
double 账户余额 = AccountInfoDouble(ACCOUNT_BALANCE);
string 模式文本 = 启用自动交易 ? "自动交易模式" : "预警模式";
string 趋势模式 = 启用双EMA ? "双EMA" : "单EMA";
string 移动止损文本 = 启用移动止损 ? (移动止损使用百分比 ? "(%止损)" : "(点数止损)") : "";
string 止盈文本 = 启用分段止盈 ? "(分段止盈)" : "";
string 账户类型 = 是美分账户 ? " | 美分账户" : "";
string 缩放提示 = " | 风险/10";
ObjectSetString(0, "Mode_Label", OBJPROP_TEXT,
StringFormat("模式: %s | 趋势: %s | 止损: %s%s%s%s | 净值: %.2f",
模式文本, 趋势模式, 移动止损文本, 止盈文本, 账户类型, 缩放提示, 账户净值));
if(启用移动止损)
{
string 跟踪状态 = "移动止损: 启用";
string 跟踪模式 = 移动止损使用百分比 ? "(百分比)" : "(点数)";
int 活跃跟踪订单 = 0;
for(int i = 0; i < 跟踪订单数量; i++) if(跟踪订单数组[i].阶段 > 0) 活跃跟踪订单++;
if(活跃跟踪订单 > 0) 跟踪状态 += StringFormat(" (%d活跃)", 活跃跟踪订单);
ObjectSetString(0, "Trailing_Label", OBJPROP_TEXT, 跟踪状态 + 跟踪模式);
}
}
//+------------------------------------------------------------------+
//| 更新移动止损函数(使用引用) |
//+------------------------------------------------------------------+
void 更新移动止损()
{
if(!启用移动止损) return;
int 持仓总数 = PositionsTotal();
for(int i = 0; i < 持仓总数; i++)
{
ulong 订单编号 = PositionGetTicket(i);
if(PositionSelectByTicket(订单编号))
{
string 订单品种 = PositionGetString(POSITION_SYMBOL);
long 订单魔术 = PositionGetInteger(POSITION_MAGIC);
if(订单品种 == _Symbol && (ulong)订单魔术 == 魔术数字)
{
int 订单类型 = ((int)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? 1 : -1;
double 当前价格 = 订单类型 > 0 ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double 入场价格 = PositionGetDouble(POSITION_PRICE_OPEN);
double 当前止损价 = PositionGetDouble(POSITION_SL);
double 当前止盈价 = PositionGetDouble(POSITION_TP);
int 跟踪索引 = 查找跟踪订单(订单编号);
if(跟踪索引 < 0)
{
跟踪索引 = 添加跟踪订单(订单编号, 订单类型, 入场价格, 当前止损价, 当前止盈价);
if(跟踪索引 < 0) continue;
}
更新订单价格记录(跟踪索引, 当前价格, 订单类型);
// 传递引用
if(启用分段止盈) 检查是否达到止盈阶段(跟踪订单数组[跟踪索引], 当前价格);
应用移动止损(跟踪索引, 当前价格, 订单类型);
}
}
}
清理跟踪订单();
}
//+------------------------------------------------------------------+
//| 查找跟踪订单函数 |
//+------------------------------------------------------------------+
int 查找跟踪订单(ulong 订单编号)
{
for(int i = 0; i < 跟踪订单数量; i++) if(跟踪订单数组[i].订单号 == 订单编号) return i;
return -1;
}
//+------------------------------------------------------------------+
//| 添加跟踪订单函数 |
//+------------------------------------------------------------------+
int 添加跟踪订单(ulong 订单编号, int 订单类型, double 入场价, double 初始止损价, double 初始止盈价)
{
if(跟踪订单数量 >= 最大跟踪订单数) return -1;
订单跟踪信息 新订单;
新订单.订单号 = 订单编号;
新订单.类型 = 订单类型;
新订单.入场价 = 入场价;
新订单.初始止损价 = 初始止损价;
新订单.初始止盈价 = 初始止盈价;
新订单.当前移动止损价 = 初始止损价;
新订单.最高价 = 订单类型 > 0 ? 入场价 : 0;
新订单.最低价 = 订单类型 < 0 ? 入场价 : 999999;
新订单.阶段 = 0;
新订单.盈亏比 = 0;
新订单.最后检查时间 = TimeCurrent();
新订单.已实现盈亏比 = 0;
新订单.阶段起始价 = 入场价;
新订单.阶段激活 = false;
if(跟踪订单数量 >= ArraySize(跟踪订单数组)) ArrayResize(跟踪订单数组, ArraySize(跟踪订单数组) + 10);
跟踪订单数组[跟踪订单数量] = 新订单;
跟踪订单数量++;
return 跟踪订单数量 - 1;
}
//+------------------------------------------------------------------+
//| 更新订单价格记录函数 |
//+------------------------------------------------------------------+
void 更新订单价格记录(int 跟踪索引, double 当前价格, int 订单类型)
{
if(跟踪索引 < 0 || 跟踪索引 >= 跟踪订单数量) return;
if(订单类型 > 0) { if(当前价格 > 跟踪订单数组[跟踪索引].最高价) 跟踪订单数组[跟踪索引].最高价 = 当前价格; }
else { if(当前价格 < 跟踪订单数组[跟踪索引].最低价) 跟踪订单数组[跟踪索引].最低价 = 当前价格; }
跟踪订单数组[跟踪索引].最后检查时间 = TimeCurrent();
}
//+------------------------------------------------------------------+
//| 应用移动止损函数(已修正启动逻辑) |
//+------------------------------------------------------------------+
void 应用移动止损(int 跟踪索引, double 当前价格, int 订单类型)
{
if(跟踪索引 < 0 || 跟踪索引 >= 跟踪订单数量) return;
double 当前利润点数 = (当前价格 - 跟踪订单数组[跟踪索引].入场价) / _Point * 跟踪订单数组[跟踪索引].类型;
double 止损距离点数 = MathAbs(跟踪订单数组[跟踪索引].入场价 - 跟踪订单数组[跟踪索引].初始止损价) / _Point;
if(止损距离点数 == 0) return;
double 当前盈亏比 = 当前利润点数 / 止损距离点数;
// 修正后的移动止损启动逻辑:
// - 如果当前盈亏比 < 1,不允许移动止损
// - 如果当前盈亏比 >= 1,则:
// - 若启用分段止盈,要求阶段已激活(即已达到新阶段)
// - 若未启用分段止盈,直接允许移动止损
bool 允许移动 = false;
if (当前盈亏比 < 1.0)
{
允许移动 = false; // 盈亏比小于1,不启动
}
else
{
if (启用分段止盈)
{
// 必须阶段已激活(由检查是否达到止盈阶段设置)
if (跟踪订单数组[跟踪索引].阶段激活)
允许移动 = true;
}
else
{
// 未启用分段止盈,一旦盈亏比达到1就允许移动
允许移动 = true;
}
}
if(!允许移动) return;
double 新移动止损价 = 计算移动止损价(跟踪订单数组[跟踪索引], 当前价格, 当前利润点数);
if(需要更新止损价(跟踪订单数组[跟踪索引], 新移动止损价, 订单类型))
{
修改订单止损价(跟踪订单数组[跟踪索引].订单号, 新移动止损价);
跟踪订单数组[跟踪索引].当前移动止损价 = 新移动止损价;
if(启用分段止盈 && 当前盈亏比 >= 1) 跟踪订单数组[跟踪索引].阶段 = (int)MathFloor(当前盈亏比);
PrintFormat("订单 %I64u 更新移动止损价: %.5f (盈亏比: %.2f, 已实现盈亏比: %.0f)",
跟踪订单数组[跟踪索引].订单号, 新移动止损价, 当前盈亏比, 跟踪订单数组[跟踪索引].已实现盈亏比);
}
}
//+------------------------------------------------------------------+
//| 计算移动止损价函数(使用引用) |
//+------------------------------------------------------------------+
double 计算移动止损价(订单跟踪信息 &跟踪信息, double 当前价格, double 当前利润点数)
{
double 止损距离点数 = MathAbs(跟踪信息.入场价 - 跟踪信息.初始止损价) / _Point;
if(止损距离点数 == 0) return 跟踪信息.初始止损价;
double 当前盈亏比 = 当前利润点数 / 止损距离点数;
if(启用分段止盈)
{
if(跟踪信息.已实现盈亏比 < 1)
{
// 第一阶段(盈亏比<1),但此时当前盈亏比可能已>=1,但已实现盈亏比仍为0?实际在检查达到止盈阶段时,当当前盈亏比>=1时,已实现盈亏比会被设为1,所以这里应不会进入此分支。但为防万一,保留原逻辑。
if(移动止损使用百分比)
{
if(跟踪信息.类型 > 0)
{
double 最高价 = 跟踪信息.最高价;
double 回撤金额 = (最高价 - 跟踪信息.入场价) * 第一阶段回撤百分比 / 100.0;
return MathMax(最高价 - 回撤金额, 跟踪信息.初始止损价);
}
else
{
double 最低价 = 跟踪信息.最低价;
double 回撤金额 = (跟踪信息.入场价 - 最低价) * 第一阶段回撤百分比 / 100.0;
return MathMin(最低价 + 回撤金额, 跟踪信息.初始止损价);
}
}
else
{
if(跟踪信息.类型 > 0)
{
double 最高价 = 跟踪信息.最高价;
return MathMax(最高价 - 第一阶段回撤点数 * _Point, 跟踪信息.初始止损价);
}
else
{
double 最低价 = 跟踪信息.最低价;
return MathMin(最低价 + 第一阶段回撤点数 * _Point, 跟踪信息.初始止损价);
}
}
}
else
{
// 第二阶段及以上(已实现盈亏比 >=1)
int 当前阶段 = (int)跟踪信息.已实现盈亏比;
double 阶段目标价 = 跟踪信息.入场价 + 跟踪信息.类型 * 止损距离点数 * _Point * 当前阶段;
if(移动止损使用百分比)
{
if(跟踪信息.类型 > 0)
{
double 阶段最高价 = MathMax(跟踪信息.最高价, 当前价格);
double 阶段利润 = 阶段最高价 - 阶段目标价;
double 允许回撤 = 阶段利润 * 第二阶段回撤百分比 / 100.0;
return MathMax(阶段最高价 - 允许回撤, 跟踪信息.初始止损价);
}
else
{
double 阶段最低价 = MathMin(跟踪信息.最低价, 当前价格);
double 阶段利润 = 阶段目标价 - 阶段最低价;
double 允许回撤 = 阶段利润 * 第二阶段回撤百分比 / 100.0;
return MathMin(阶段最低价 + 允许回撤, 跟踪信息.初始止损价);
}
}
else
{
if(跟踪信息.类型 > 0)
{
return MathMax(跟踪信息.最高价 - 第二阶段固定回撤点数 * _Point, 跟踪信息.初始止损价);
}
else
{
return MathMin(跟踪信息.最低价 + 第二阶段固定回撤点数 * _Point, 跟踪信息.初始止损价);
}
}
}
}
else
{
// 未启用分段止盈,使用普通移动止损逻辑
if(当前盈亏比 < 1.0)
{
if(移动止损使用百分比)
{
if(跟踪信息.类型 > 0)
{
double 最高价 = 跟踪信息.最高价;
double 回撤金额 = (最高价 - 跟踪信息.入场价) * 第一阶段回撤百分比 / 100.0;
return MathMax(最高价 - 回撤金额, 跟踪信息.初始止损价);
}
else
{
double 最低价 = 跟踪信息.最低价;
double 回撤金额 = (跟踪信息.入场价 - 最低价) * 第一阶段回撤百分比 / 100.0;
return MathMin(最低价 + 回撤金额, 跟踪信息.初始止损价);
}
}
else
{
if(跟踪信息.类型 > 0)
{
double 最高价 = 跟踪信息.最高价;
return MathMax(最高价 - 第一阶段回撤点数 * _Point, 跟踪信息.初始止损价);
}
else
{
double 最低价 = 跟踪信息.最低价;
return MathMin(最低价 + 第一阶段回撤点数 * _Point, 跟踪信息.初始止损价);
}
}
}
else
{
int 当前阶段 = (int)MathFloor(当前盈亏比);
if(移动止损使用百分比)
{
double 阶段目标价 = 跟踪信息.入场价 + 跟踪信息.类型 * 止损距离点数 * _Point * 当前阶段;
double 上一阶段目标价 = 跟踪信息.入场价 + 跟踪信息.类型 * 止损距离点数 * _Point * (当前阶段 - 1);
if(跟踪信息.类型 > 0)
{
double 当前阶段利润 = MathMax(0, 跟踪信息.最高价 - 上一阶段目标价);
double 允许回撤 = 当前阶段利润 * 第二阶段回撤百分比 / 100.0;
return MathMax(跟踪信息.最高价 - 允许回撤, 跟踪信息.初始止损价);
}
else
{
double 当前阶段利润 = MathMax(0, 上一阶段目标价 - 跟踪信息.最低价);
double 允许回撤 = 当前阶段利润 * 第二阶段回撤百分比 / 100.0;
return MathMin(跟踪信息.最低价 + 允许回撤, 跟踪信息.初始止损价);
}
}
else
{
if(跟踪信息.类型 > 0)
{
return MathMax(跟踪信息.最高价 - 第二阶段固定回撤点数 * _Point, 跟踪信息.初始止损价);
}
else
{
return MathMin(跟踪信息.最低价 + 第二阶段固定回撤点数 * _Point, 跟踪信息.初始止损价);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查是否需要更新止损价函数(使用引用) |
//+------------------------------------------------------------------+
bool 需要更新止损价(订单跟踪信息 &跟踪信息, double 新止损价, int 订单类型)
{
double 当前止损价 = 跟踪信息.当前移动止损价;
if(订单类型 > 0)
return (新止损价 > 当前止损价 && 新止损价 < SymbolInfoDouble(_Symbol, SYMBOL_BID));
else
return (新止损价 < 当前止损价 && 新止损价 > SymbolInfoDouble(_Symbol, SYMBOL_ASK));
}
//+------------------------------------------------------------------+
//| 修改订单止损价函数 |
//+------------------------------------------------------------------+
bool 修改订单止损价(ulong 订单编号, double 新止损价)
{
double 当前价格 = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double 最小止损距离 = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
if(!PositionSelectByTicket(订单编号)) return false;
int 订单类型 = (int)PositionGetInteger(POSITION_TYPE);
double 当前止盈价 = PositionGetDouble(POSITION_TP);
if(订单类型 == POSITION_TYPE_BUY)
{
if(新止损价 >= 当前价格 - 最小止损距离) return false;
}
else
{
if(新止损价 <= 当前价格 + 最小止损距离) return false;
}
MqlTradeRequest 请求;
MqlTradeResult 结果;
ZeroMemory(请求);
请求.action = TRADE_ACTION_SLTP;
请求.position = 订单编号;
请求.symbol = _Symbol;
请求.sl = NormalizeDouble(新止损价, _Digits);
请求.tp = 当前止盈价;
请求.magic = 魔术数字;
bool 成功 = OrderSend(请求, 结果);
if(成功 && 结果.retcode == TRADE_RETCODE_DONE)
{
PrintFormat("订单 %I64u 止损价修改成功: %.5f", 订单编号, 新止损价);
return true;
}
else
{
PrintFormat("订单 %I64u 止损价修改失败: 错误代码 %d", 订单编号, 结果.retcode);
return false;
}
}
//+------------------------------------------------------------------+
//| 清理跟踪订单函数 |
//+------------------------------------------------------------------+
void 清理跟踪订单()
{
for(int i = 跟踪订单数量 - 1; i >= 0; i--)
{
ulong 订单编号 = 跟踪订单数组[i].订单号;
bool 存在 = false;
int 持仓总数 = PositionsTotal();
for(int j = 0; j < 持仓总数; j++)
{
if(PositionGetTicket(j) == 订单编号) { 存在 = true; break; }
}
if(!存在)
{
PrintFormat("订单 %I64u 已平仓,从跟踪列表中移除", 订单编号);
if(i < 跟踪订单数量 - 1) 跟踪订单数组[i] = 跟踪订单数组[跟踪订单数量 - 1];
跟踪订单数量--;
}
}
}
//+------------------------------------------------------------------+
//| 初始化指标显示函数 |
//+------------------------------------------------------------------+
void 初始化指标显示()
{
if(图表显示EMA) 创建EMA指标线();
if(图表显示MACD) 创建MACD指标线();
if(图表显示RSI) 创建RSI指标线();
if(图表显示ATR) 创建ATR指标线();
}
void 创建EMA指标线()
{
ema_慢线_图表句柄 = iMA(_Symbol, _Period, EMA_慢线周期, 0, MODE_EMA, EMA_价格类型);
ChartIndicatorAdd(0, 0, ema_慢线_图表句柄);
if(启用双EMA)
{
ema_快线_图表句柄 = iMA(_Symbol, _Period, EMA_快线周期, 0, MODE_EMA, EMA_价格类型);
ChartIndicatorAdd(0, 1, ema_快线_图表句柄);
}
}
void 创建MACD指标线()
{
macd_图表句柄 = iMACD(_Symbol, _Period, MACD_快速线, MACD_慢速线, MACD_信号线, MACD_价格类型);
ChartIndicatorAdd(0, 2, macd_图表句柄);
}
void 创建RSI指标线()
{
rsi_图表句柄 = iRSI(_Symbol, _Period, RSI_周期, RSI_价格类型);
ChartIndicatorAdd(0, 3, rsi_图表句柄);
}
void 创建ATR指标线()
{
atr_图表句柄 = iATR(_Symbol, _Period, ATR_止损周期);
ChartIndicatorAdd(0, 4, atr_图表句柄);
}
//+------------------------------------------------------------------+
源码在这 |