【多指标共振】根据B站一个视频写的

| 发表于 昨天 17:55 | 显示全部楼层 |复制链接
最后由 吾忠生 于 2026-3-15 17:58 编辑

根据B站一个视频写的EA,具体老师们可以去看看。我自己回测了一下,还可以,需要严格的纪律。因此小弟改进了一点写了这个EA,老师们感兴趣可以回测一下,并且贴一下回测图,谢谢了,我好改进。https://www.bilibili.com/video/BV1qM4y1y7jp/?spm_id_from=333.1387.search.video_card.click&vd_source=b22b3af402b06158712d379b22aa56ec
image.png image.png image.png image.png image.png image.png image.png
image.png
image.png
filetype

TEXT13-移动止损修复版.rar

76.8 KB, 下载次数: 6, 下载积分: 活跃度 -5  [下载]

评分
  • 1
  • 2
  • 3
  • 4
  • 5
平均分:NAN    参与人数:0    我的评分:未评 下载时遇到问题?
举报

评论 使用道具

精彩评论2

吾忠生
D
 楼主 | 发表于 昨天 18:00 | 显示全部楼层
//+------------------------------------------------------------------+
//|                                           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_图表句柄);
}
//+------------------------------------------------------------------+
源码在这
举报

点赞 评论 使用道具

kekexi
DD
| 发表于 昨天 20:39 | 显示全部楼层
改实时点模式测试下就知道了,你这个数据不准
举报

点赞 评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册

广告位