MT5专业交易面板
MT5专业交易面板
使用此功能强大的可视化面板,轻松进行紧急头寸管理。
🎯作用
专业的智能交易系统,为您的图表添加了一个时尚的控制面板,可通过高级过滤选项即时平仓。非常适合紧急情况、新闻事件或快速风险管理决策。
✨ 主要功能
- 一键即时关闭所有仓位
- 仅关闭买入头寸
- 仅关闭卖出仓位
- 仅关闭当前符号的仓位
- 仅关闭盈利仓位
- 仅关闭亏损仓位
- 实时仓位计数器和损益显示
- 彩色盈亏指标(绿色=盈利,红色=亏损)
- 平仓前显示损益的安全确认对话框
- 简易面板移除按钮
- 完全自定义颜色和面板位置
- 适用于所有符号和时间框架
- 零延迟,即时执行
🛡️ 安全第一
每个平仓操作都需要您的确认,执行前对话框会显示准确的损益金额。您绝不会意外平仓。
适用于
✓ 突发新闻时的紧急仓位退出
✓ 达到目标时快速获利了结
✓ 快速减少损失,保护您的账户
日终头寸清理
专业风险管理
剥头皮者和日间交易者
需要即时退出的新闻交易者
完全自定义
- 选择面板位置(4 个角中的任意一个)
- 自定义与角的 X 和 Y 距离
- 更改所有颜色(背景、按钮、文本)
- 包含专业配色方案
实时信息
面板持续更新显示
- 未结头寸总数
- 以账户货币计算的总损益
- 买入头寸数量和损益
- 卖出头寸数量和损益
- 所有内容均以颜色标示,一目了然
⚙️ 输入参数
所有设置都很简单,并有详细说明:
- 面板角落 - 选择显示位置
- 面板 X/Y 距离 - 微调位置
- 颜色 - 背景、按钮、文本
- 所有参数都有清晰的说明
超级易用
1.将 EA 拖到任何图表上
2.启用自动交易(工具栏中的绿色按钮)
3.面板自动出现
4.点击任何按钮关闭仓位
5.确认操作
6.完成!完成后取下面板
⚡ 高性能
- 超轻量级代码
- 无 CPU 开销或内存问题
- 即时按钮响应
- 轻松处理 100 多个位置
- 即使在剧烈波动时也不会出现延迟
经过测试,性能可靠
在多个经纪商、账户类型和市场条件下进行了全面测试。包括全面的错误处理、详细的日志记录和智能订单填写模式(FOK/IOC/RETURN),以实现最大兼容性。
完全免费
- 无隐藏费用
- 无仓位限制
- 无终止日期
- 无试用期
- 100% 永久免费
卓越的支持
我致力于帮助所有粉丝从这款工具中获得最大收益。
⭐⭐⭐⭐⭐ 如有帮助,请给予评价!⭐⭐⭐⭐⭐
记得 ,关注,转发,点赞,投币
如果此 EA 为您节省了时间或金钱,请花 10 秒钟为其评分!
您的评价有助于其他交易者发现这款免费工具帮助到更多人。
感谢您的支持!🙏
📥安装说明
1.下载文件
2.打开 MetaTrader 5
3.打开 MetaEditor(按 F4 或工具 → MetaQuotes 语言编辑器)
4.文件 → 打开数据文件夹
5.将 EA 文件复制到 MQL5 → Experts
6.重启 MT5 或刷新导航器(右键单击 → 刷新)
7.将导航器 → 专家顾问中的 "一键关闭所有仓位 - 专业交易面板 "拖到任意图表上
8.在设置窗口中点击 "OK
9.确保工具栏中的 "自动交易 "按钮已启用(绿色) 10.
10.面板出现在图表上,即可使用!
⚠️ 重要要求
- MetaTrader 5 平台
- 必须启用自动交易(绿色按钮)
- 适用于任何符号和时间框架
- 支持模拟和真实账户
- 兼容所有经纪商
🔧故障排除
如果按钮不起作用
1.检查自动交易是否打开(绿色按钮)
2.检查工具 → 选项 → 智能交易系统 → "允许自动交易 "是否选中
3.如果需要,重新编译 EA(MetaEditor 中的 F7)
4.检查 "专家 "选项卡是否有错误信息
欢迎反馈
我一直在寻求改进!如果您有功能要求或建议,请留言。
---
版本历史
v1.00 - 初次发布
- 六种仓位平仓过滤器
- 实时损益显示
- 安全确认
- 完全自定义
- 专业图形用户界面
//+------------------------------------------------------------------+ //| 一键关闭所有仓位 - 专业交易面板.mq5 | //| 最最最棒的男孩 | //| WeChat:34373430 | //+------------------------------------------------------------------+ #property copyright "最最最棒的男孩" #property link "WeChat:34373430" #property version "1.00" #property description "一键关闭所有仓位 - 专业交易面板" //--- 输入参数(可在MT5界面直接修改,无需改代码) input ENUM_BASE_CORNER PanelCorner = CORNER_LEFT_UPPER; // 面板显示的角落位置(默认左上) input int PanelX = 10; // 面板X轴偏移距离(像素) input int PanelY = 50; // 面板Y轴偏移距离(像素) input color PanelBackgroundColor = clrDarkSlateGray; // 面板背景颜色(深石板灰) input color ButtonColorClose = clrCrimson; // 总平仓按钮颜色(深红色) input color ButtonColorProfit = clrForestGreen; // 平仓盈利单按钮颜色(森林绿) input color ButtonColorLoss = clrOrangeRed; // 平仓亏损单按钮颜色(橙红) input color TextColor = clrWhite; // 面板文字颜色(白色) //--- 全局变量(面板样式配置,统一管理便于修改) string prefix = "OCA_"; // 所有图表对象的前缀(避免和其他EA/指标命名冲突) int panelWidth = 280; // 面板总宽度(像素) int panelHeight = 380; // 面板总高度(像素) int buttonHeight = 35; // 按钮高度(像素) int buttonSpacing = 5; // 按钮之间的间距(像素) //+------------------------------------------------------------------+ //| EA初始化函数(EA加载时执行一次,核心做初始化校验和面板创建) | //+------------------------------------------------------------------+ int OnInit() { // 第一步:删除已存在的同名面板对象(避免重复创建导致界面混乱) DeleteAllObjects(); // 第二步:校验1 - 检查终端是否开启算法交易(MT5全局开关) if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Alert("ERROR: 终端算法交易已禁用!请在 工具->选项->智能交易 中开启"); Print("ERROR: 终端算法交易已禁用!"); } // 第三步:校验2 - 检查当前EA是否开启自动交易(工具栏AutoTrading按钮) if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) { Alert("ERROR: 自动交易已禁用!请点击工具栏的'自动交易'按钮开启"); Print("ERROR: 当前EA的自动交易权限已禁用!"); } // 第四步:创建可视化面板(按钮、文字、背景) CreatePanel(); // 第五步:初始化持仓信息显示(首次加载时刷新数据) UpdatePositionInfo(); // 打印初始化日志(便于排查问题) Print("=== 一键平仓面板EA初始化完成 ==="); Print("账户号: ", AccountInfoInteger(ACCOUNT_LOGIN)); Print("当前图表品种: ", _Symbol); Print("初始持仓数量: ", PositionsTotal()); return(INIT_SUCCEEDED); // 返回初始化成功 } //+------------------------------------------------------------------+ //| EA卸载函数(EA移除/图表关闭/终端退出时执行) | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // 仅当不是"图表切换"原因时,删除面板对象(避免切换图表误删) if(reason != REASON_CHARTCHANGE) DeleteAllObjects(); } //+------------------------------------------------------------------+ //| 行情Tick函数(每收到一次行情数据执行一次) | //+------------------------------------------------------------------+ void OnTick() { // 实时更新持仓信息(盈亏、数量),保证面板数据和实际持仓一致 UpdatePositionInfo(); } //+------------------------------------------------------------------+ //| 图表事件处理函数(监听鼠标点击、键盘操作等交互事件) | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // 事件类型ID const long &lparam, // 长整型参数(暂未使用) const double &dparam, // 浮点型参数(暂未使用) const string &sparam) // 字符串参数(点击的对象名称) { // 仅处理"对象点击"事件(按钮点击属于此类) if(id == CHARTEVENT_OBJECT_CLICK) { // 1. 点击"平仓所有"按钮 if(sparam == prefix + "BtnCloseAll") { // 重置按钮状态(避免点击后一直高亮) ObjectSetInteger(0, sparam, OBJPROP_STATE, false); // 弹窗确认(显示总盈亏,防止误操作) if(MessageBox("确认平仓所有持仓?\n\n总盈亏: " + DoubleToString(GetTotalProfit(), 2) + " " + AccountInfoString(ACCOUNT_CURRENCY), "确认平仓", MB_YESNO | MB_ICONQUESTION) == IDYES) { // 用户点击"是",执行平仓所有 CloseAllPositions(); } } // 2. 点击"平仓所有多单"按钮 else if(sparam == prefix + "BtnCloseBuy") { ObjectSetInteger(0, sparam, OBJPROP_STATE, false); if(MessageBox("确认平仓所有多单?\n\n多单盈亏: " + DoubleToString(GetProfitByType(POSITION_TYPE_BUY), 2) + " " + AccountInfoString(ACCOUNT_CURRENCY), "确认平仓多单", MB_YESNO | MB_ICONQUESTION) == IDYES) { ClosePositionsByType(POSITION_TYPE_BUY); } } // 3. 点击"平仓所有空单"按钮 else if(sparam == prefix + "BtnCloseSell") { ObjectSetInteger(0, sparam, OBJPROP_STATE, false); if(MessageBox("确认平仓所有空单?\n\n空单盈亏: " + DoubleToString(GetProfitByType(POSITION_TYPE_SELL), 2) + " " + AccountInfoString(ACCOUNT_CURRENCY), "确认平仓空单", MB_YESNO | MB_ICONQUESTION) == IDYES) { ClosePositionsByType(POSITION_TYPE_SELL); } } // 4. 点击"平仓当前品种所有持仓"按钮 else if(sparam == prefix + "BtnCloseSymbol") { ObjectSetInteger(0, sparam, OBJPROP_STATE, false); if(MessageBox("确认平仓" + _Symbol + "的所有持仓?\n\n该品种盈亏: " + DoubleToString(GetProfitBySymbol(_Symbol), 2) + " " + AccountInfoString(ACCOUNT_CURRENCY), "确认平仓当前品种", MB_YESNO | MB_ICONQUESTION) == IDYES) { ClosePositionsBySymbol(_Symbol); } } // 5. 点击"平仓所有盈利单"按钮 else if(sparam == prefix + "BtnCloseProfit") { ObjectSetInteger(0, sparam, OBJPROP_STATE, false); if(MessageBox("确认平仓所有盈利持仓?\n\n盈利总额: " + DoubleToString(GetProfitablePositionsProfit(), 2) + " " + AccountInfoString(ACCOUNT_CURRENCY), "确认平仓盈利单", MB_YESNO | MB_ICONQUESTION) == IDYES) { ClosePositionsByProfit(true); } } // 6. 点击"平仓所有亏损单"按钮 else if(sparam == prefix + "BtnCloseLoss") { ObjectSetInteger(0, sparam, OBJPROP_STATE, false); if(MessageBox("确认平仓所有亏损持仓?\n\n亏损总额: " + DoubleToString(GetLosingPositionsProfit(), 2) + " " + AccountInfoString(ACCOUNT_CURRENCY), "确认平仓亏损单", MB_YESNO | MB_ICONQUESTION) == IDYES) { ClosePositionsByProfit(false); } } // 7. 点击"移除面板"按钮 else if(sparam == prefix + "BtnClose") { ObjectSetInteger(0, sparam, OBJPROP_STATE, false); ExpertRemove(); // 卸载当前EA(自动触发OnDeinit删除面板) } // 延迟100ms(避免高频操作导致终端卡顿) Sleep(100); // 平仓后刷新持仓信息 UpdatePositionInfo(); // 强制重绘图表(保证界面实时更新) ChartRedraw(0); } } //+------------------------------------------------------------------+ //| 创建可视化面板(背景、标题、文字、按钮) | //+------------------------------------------------------------------+ void CreatePanel() { // 1. 创建面板背景(矩形) CreateRectangle(prefix + "Background", PanelX, PanelY, panelWidth, panelHeight, PanelBackgroundColor); // 2. 创建面板标题 CreateLabel(prefix + "Title", PanelX + 10, PanelY + 10, "一键平仓面板", TextColor, 11, "Arial Bold"); // 3. 创建持仓信息标签(初始值为0,后续UpdatePositionInfo更新) CreateLabel(prefix + "InfoPositions", PanelX + 10, PanelY + 40, "持仓数量: 0", TextColor, 9, "Arial"); CreateLabel(prefix + "InfoProfit", PanelX + 10, PanelY + 60, "总盈亏: 0.00", TextColor, 9, "Arial"); CreateLabel(prefix + "InfoBuy", PanelX + 10, PanelY + 80, "多单: 0 | 盈亏: 0.00", TextColor, 9, "Arial"); CreateLabel(prefix + "InfoSell", PanelX + 10, PanelY + 100, "空单: 0 | 盈亏: 0.00", TextColor, 9, "Arial"); // 4. 创建分隔线(视觉区分信息区和按钮区) CreateRectangle(prefix + "Separator", PanelX + 10, PanelY + 125, panelWidth - 20, 1, clrGray); // 5. 创建功能按钮(按位置依次排列) int btnY = PanelY + 135; // 第一个按钮的Y轴起始位置 // 5.1 平仓所有按钮 CreateButton(prefix + "BtnCloseAll", PanelX + 10, btnY, panelWidth - 20, buttonHeight, "平仓所有持仓", ButtonColorClose); // 5.2 多单/空单平仓按钮(左右并排) btnY += buttonHeight + buttonSpacing; // 按钮Y轴位置下移(高度+间距) CreateButton(prefix + "BtnCloseBuy", PanelX + 10, btnY, (panelWidth - 25) / 2, buttonHeight, "平仓多单", clrDodgerBlue); CreateButton(prefix + "BtnCloseSell", PanelX + 15 + (panelWidth - 25) / 2, btnY, (panelWidth - 25) / 2, buttonHeight, "平仓空单", clrTomato); // 5.3 平仓当前品种按钮 btnY += buttonHeight + buttonSpacing; CreateButton(prefix + "BtnCloseSymbol", PanelX + 10, btnY, panelWidth - 20, buttonHeight, "平仓" + _Symbol, clrSlateBlue); // 5.4 平仓盈利单按钮 btnY += buttonHeight + buttonSpacing; CreateButton(prefix + "BtnCloseProfit", PanelX + 10, btnY, panelWidth - 20, buttonHeight, "平仓盈利持仓", ButtonColorProfit); // 5.5 平仓亏损单按钮 btnY += buttonHeight + buttonSpacing; CreateButton(prefix + "BtnCloseLoss", PanelX + 10, btnY, panelWidth - 20, buttonHeight, "平仓亏损持仓", ButtonColorLoss); // 5.6 移除面板按钮 btnY += buttonHeight + buttonSpacing + 10; // 额外加10像素间距,视觉更美观 CreateButton(prefix + "BtnClose", PanelX + 10, btnY, panelWidth - 20, 30, "✖ 移除面板", clrDimGray); // 强制重绘图表(确保面板立即显示) ChartRedraw(0); } //+------------------------------------------------------------------+ //| 更新持仓信息(实时计算并刷新面板文字显示) | //+------------------------------------------------------------------+ void UpdatePositionInfo() { // 1. 获取核心持仓数据 int totalPositions = PositionsTotal(); // 总持仓数量 double totalProfit = GetTotalProfit(); // 总盈亏(含手续费/隔夜利息) int buyCount = CountPositionsByType(POSITION_TYPE_BUY); // 多单数量 int sellCount = CountPositionsByType(POSITION_TYPE_SELL); // 空单数量 double buyProfit = GetProfitByType(POSITION_TYPE_BUY); // 多单盈亏 double sellProfit = GetProfitByType(POSITION_TYPE_SELL); // 空单盈亏 string currency = AccountInfoString(ACCOUNT_CURRENCY); // 账户货币(如USD、CNY) // 2. 更新"持仓数量"标签 ObjectSetString(0, prefix + "InfoPositions", OBJPROP_TEXT, "持仓数量: " + IntegerToString(totalPositions)); // 3. 更新"总盈亏"标签(盈亏为正显示绿色,负显示红色) string profitText = "总盈亏: " + DoubleToString(totalProfit, 2) + " " + currency; color profitColor = totalProfit >= 0 ? clrLime : clrRed; ObjectSetString(0, prefix + "InfoProfit", OBJPROP_TEXT, profitText); ObjectSetInteger(0, prefix + "InfoProfit", OBJPROP_COLOR, profitColor); // 4. 更新"多单信息"标签(盈亏颜色区分) ObjectSetString(0, prefix + "InfoBuy", OBJPROP_TEXT, "多单: " + IntegerToString(buyCount) + " | 盈亏: " + DoubleToString(buyProfit, 2) + " " + currency); ObjectSetInteger(0, prefix + "InfoBuy", OBJPROP_COLOR, buyProfit >= 0 ? clrLightGreen : clrLightCoral); // 5. 更新"空单信息"标签(盈亏颜色区分) ObjectSetString(0, prefix + "InfoSell", OBJPROP_TEXT, "空单: " + IntegerToString(sellCount) + " | 盈亏: " + DoubleToString(sellProfit, 2) + " " + currency); ObjectSetInteger(0, prefix + "InfoSell", OBJPROP_COLOR, sellProfit >= 0 ? clrLightGreen : clrLightCoral); } //+------------------------------------------------------------------+ //| 平仓所有持仓 | //+------------------------------------------------------------------+ void CloseAllPositions() { int total = PositionsTotal(); // 获取总持仓数 int closed = 0; // 成功平仓数量 int failed = 0; // 平仓失败数量 Print("开始尝试平仓 ", total, " 个持仓..."); // 遍历持仓(从后往前遍历,避免删除持仓后索引错乱) for(int i = total - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); // 获取持仓单号(唯一标识) if(ticket > 0) // 持仓单号有效(>0表示存在) { // 调用底层平仓函数,成功则closed+1,失败则failed+1 if(ClosePosition(ticket)) closed++; else failed++; Sleep(100); // 每平仓一个持仓延迟100ms(避免高频请求被经纪商拒绝) } } // 生成平仓结果提示(弹窗+日志) string message = "已平仓 " + IntegerToString(closed) + " / " + IntegerToString(total) + " 个持仓"; if(failed > 0) message += "(" + IntegerToString(failed) + " 个平仓失败)"; Alert(message); // 弹窗提示用户 Print(message); // 打印日志(便于排查失败原因) } //+------------------------------------------------------------------+ //| 按类型平仓(多单/空单) | //+------------------------------------------------------------------+ void ClosePositionsByType(ENUM_POSITION_TYPE type) { int total = PositionsTotal(); int closed = 0; int failed = 0; // 转换类型为文字(便于日志/提示) string typeName = type == POSITION_TYPE_BUY ? "多单" : "空单"; Print("开始尝试平仓所有", typeName, "..."); // 遍历持仓,仅平仓指定类型 for(int i = total - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); // 持仓有效 + 类型匹配 if(ticket > 0 && PositionGetInteger(POSITION_TYPE) == type) { if(ClosePosition(ticket)) closed++; else failed++; Sleep(100); } } string message = "已平仓 " + IntegerToString(closed) + " 个" + typeName; if(failed > 0) message += "(" + IntegerToString(failed) + " 个平仓失败)"; Alert(message); Print(message); } //+------------------------------------------------------------------+ //| 按品种平仓(仅平指定品种的持仓) | //+------------------------------------------------------------------+ void ClosePositionsBySymbol(string symbol) { int total = PositionsTotal(); int closed = 0; int failed = 0; Print("开始尝试平仓", symbol, "的所有持仓..."); // 遍历持仓,仅平仓指定品种 for(int i = total - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); // 持仓有效 + 品种匹配 if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == symbol) { if(ClosePosition(ticket)) closed++; else failed++; Sleep(100); } } string message = "已平仓 " + IntegerToString(closed) + " 个" + symbol + "持仓"; if(failed > 0) message += "(" + IntegerToString(failed) + " 个平仓失败)"; Alert(message); Print(message); } //+------------------------------------------------------------------+ //| 按盈亏平仓(仅平盈利单/仅平亏损单) | //+------------------------------------------------------------------+ void ClosePositionsByProfit(bool closeProfitable) { int total = PositionsTotal(); int closed = 0; int failed = 0; // 转换类型为文字 string typeName = closeProfitable ? "盈利单" : "亏损单"; Print("开始尝试平仓所有", typeName, "..."); // 遍历持仓,仅平仓指定盈亏类型 for(int i = total - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket > 0) { // 持仓总盈亏 = 平仓盈亏 + 隔夜利息(Swap) double profit = PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); // 盈利单:profit>0;亏损单:profit<0(等于0的持仓不处理) if((closeProfitable && profit > 0) || (!closeProfitable && profit < 0)) { if(ClosePosition(ticket)) closed++; else failed++; Sleep(100); } } } string message = "已平仓 " + IntegerToString(closed) + " 个" + typeName; if(failed > 0) message += "(" + IntegerToString(failed) + " 个平仓失败)"; Alert(message); Print(message); } //+------------------------------------------------------------------+ //| 底层平仓函数(单持仓平仓核心逻辑,适配不同经纪商成交规则) | //+------------------------------------------------------------------+ bool ClosePosition(ulong ticket) { Print("=== 开始平仓持仓 #", ticket, " ==="); // 第一步:校验持仓是否存在(避免平仓已关闭的持仓) if(!PositionSelectByTicket(ticket)) { Print("ERROR: 持仓单号 ", ticket, " 不存在或已平仓"); return false; } // 第二步:获取持仓详细信息(用于构造平仓请求) string symbol = PositionGetString(POSITION_SYMBOL); // 持仓品种 double volume = PositionGetDouble(POSITION_VOLUME); // 持仓手数 ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // 持仓类型(多/空) ulong magic = PositionGetInteger(POSITION_MAGIC); // 魔术号(EA标识) double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); // 开仓价格 double currentProfit = PositionGetDouble(POSITION_PROFIT); // 当前盈亏 // 打印持仓详情(便于排查问题) Print("持仓详情: 品种=", symbol, " 手数=", volume, " 类型=", EnumToString(posType), " 开仓价=", openPrice, " 当前盈亏=", currentProfit); // 第三步:校验品种是否允许交易(避免平停牌/禁止交易的品种) if(!SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE)) { Print("ERROR: 品种", symbol, "当前禁止交易"); return false; } // 第四步:构造平仓请求(MQL5标准交易请求结构体) MqlTradeRequest request; // 交易请求 MqlTradeResult result; // 交易结果 ZeroMemory(request); // 初始化请求(清空内存) ZeroMemory(result); // 初始化结果 request.action = TRADE_ACTION_DEAL; // 交易动作:直接成交(平仓) request.position = ticket; // 要平仓的持仓单号 request.symbol = symbol; // 品种 request.volume = volume; // 平仓手数(和持仓一致) request.deviation = 50; // 滑点容忍度(50点,适配波动大的品种) request.magic = magic; // 魔术号(和持仓一致) // 获取品种的成交模式(FOK/IOC/RETURN,不同经纪商支持不同) int filling = (int)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE); // 第五步:设置平仓订单类型(多单平空,空单平多) if(posType == POSITION_TYPE_BUY) { request.type = ORDER_TYPE_SELL; // 多单平仓=卖出 request.price = SymbolInfoDouble(symbol, SYMBOL_BID); // 平仓价格=买价(BID) } else { request.type = ORDER_TYPE_BUY; // 空单平仓=买入 request.price = SymbolInfoDouble(symbol, SYMBOL_ASK); // 平仓价格=卖价(ASK) } Print("平仓订单: 类型=", EnumToString(request.type), " 价格=", request.price); // 第六步:尝试不同成交模式(提高平仓成功率,适配不同经纪商) // 模式1:FOK(全部成交否则取消) if(filling && SYMBOL_FILLING_FOK) { request.type_filling = ORDER_FILLING_FOK; Print("尝试FOK成交模式..."); if(OrderSend(request, result)) { if(result.retcode == TRADE_RETCODE_DONE) { Print("SUCCESS: 持仓", ticket, "通过FOK模式平仓成功"); return true; } } Print("FOK模式失败: 错误码=", result.retcode, " - 原因=", result.comment); } // 模式2:IOC(立即成交可成交部分,剩余取消) if(filling && SYMBOL_FILLING_IOC) { request.type_filling = ORDER_FILLING_IOC; Print("尝试IOC成交模式..."); if(OrderSend(request, result)) { if(result.retcode == TRADE_RETCODE_DONE) { Print("SUCCESS: 持仓", ticket, "通过IOC模式平仓成功"); return true; } } Print("IOC模式失败: 错误码=", result.retcode, " - 原因=", result.comment); } // 模式3:RETURN(默认模式,失败则返回) request.type_filling = ORDER_FILLING_RETURN; Print("尝试RETURN成交模式..."); if(OrderSend(request, result)) { if(result.retcode == TRADE_RETCODE_DONE) { Print("SUCCESS: 持仓", ticket, "通过RETURN模式平仓成功"); return true; } } // 第七步:平仓失败,打印详细错误信息(便于排查) Print("FAILED: 持仓", ticket, "平仓失败"); Print("最终错误: 错误码=", result.retcode, " - 原因=", result.comment); Print("系统错误码: ", GetLastError()); // 错误码解析(快速定位问题) switch(result.retcode) { case TRADE_RETCODE_INVALID: Print("问题诊断: 请求参数无效"); break; case TRADE_RETCODE_INVALID_VOLUME: Print("问题诊断: 平仓手数无效(如超过品种最小/最大手数)"); break; case TRADE_RETCODE_INVALID_PRICE: Print("问题诊断: 平仓价格无效(如超出品种价格范围)"); break; case TRADE_RETCODE_INVALID_STOPS: Print("问题诊断: 止损/止盈无效(此处平仓无止损,可忽略)"); break; case TRADE_RETCODE_TRADE_DISABLED: Print("问题诊断: 终端/品种交易已禁用"); break; case TRADE_RETCODE_MARKET_CLOSED: Print("问题诊断: 品种市场已收盘(如非交易时间)"); break; case TRADE_RETCODE_NO_MONEY: Print("问题诊断: 账户资金不足(如保证金不够)"); break; case TRADE_RETCODE_PRICE_CHANGED: Print("问题诊断: 价格已变化(滑点超出容忍度,需重新请求)"); break; case TRADE_RETCODE_REJECT: Print("问题诊断: 经纪商拒绝请求(需联系经纪商)"); break; case TRADE_RETCODE_ERROR: Print("问题诊断: 通用错误(终端/网络问题)"); break; default: Print("问题诊断: 未知错误码"); } return false; // 平仓失败 } //+------------------------------------------------------------------+ //| 获取所有持仓的总盈亏(含Swap隔夜利息) | //+------------------------------------------------------------------+ double GetTotalProfit() { double profit = 0; // 遍历所有持仓,累加盈亏 for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetTicket(i) > 0) profit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); } return profit; } //+------------------------------------------------------------------+ //| 按类型获取盈亏(多单/空单) | //+------------------------------------------------------------------+ double GetProfitByType(ENUM_POSITION_TYPE type) { double profit = 0; // 遍历持仓,仅累加指定类型的盈亏 for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_TYPE) == type) profit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); } return profit; } //+------------------------------------------------------------------+ //| 按品种获取盈亏 | //+------------------------------------------------------------------+ double GetProfitBySymbol(string symbol) { double profit = 0; // 遍历持仓,仅累加指定品种的盈亏 for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetTicket(i) > 0 && PositionGetString(POSITION_SYMBOL) == symbol) profit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); } return profit; } //+------------------------------------------------------------------+ //| 按类型统计持仓数量(多单/空单) | //+------------------------------------------------------------------+ int CountPositionsByType(ENUM_POSITION_TYPE type) { int count = 0; // 遍历持仓,仅统计指定类型的数量 for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_TYPE) == type) count++; } return count; } //+------------------------------------------------------------------+ //| 获取所有盈利持仓的总盈利额 | //+------------------------------------------------------------------+ double GetProfitablePositionsProfit() { double profit = 0; // 遍历持仓,仅累加盈利单(profit>0)的盈亏 for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetTicket(i) > 0) { double posProfit = PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); if(posProfit > 0) profit += posProfit; } } return profit; } //+------------------------------------------------------------------+ //| 获取所有亏损持仓的总亏损额 | //+------------------------------------------------------------------+ double GetLosingPositionsProfit() { double profit = 0; // 遍历持仓,仅累加亏损单(profit<0)的盈亏 for(int i = 0; i < PositionsTotal(); i++) { if(PositionGetTicket(i) > 0) { double posProfit = PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); if(posProfit < 0) profit += posProfit; } } return profit; } //+------------------------------------------------------------------+ //| 创建按钮(封装重复逻辑,便于批量创建) | //+------------------------------------------------------------------+ void CreateButton(string name, int x, int y, int width, int height, string text, color clr) { // 创建按钮对象(OBJ_BUTTON为MQL5按钮类型) ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0); // 设置按钮锚点(和面板一致) ObjectSetInteger(0, name, OBJPROP_CORNER, PanelCorner); // 设置按钮位置(X/Y偏移) ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); // 设置按钮尺寸(宽/高) ObjectSetInteger(0, name, OBJPROP_XSIZE, width); ObjectSetInteger(0, name, OBJPROP_YSIZE, height); // 设置按钮文字 ObjectSetString(0, name, OBJPROP_TEXT, text); // 设置文字颜色 ObjectSetInteger(0, name, OBJPROP_COLOR, TextColor); // 设置按钮背景色 ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clr); // 设置按钮边框颜色(黑色) ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrBlack); // 设置文字大小 ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9); // 设置文字字体(粗体Arial) ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold"); } //+------------------------------------------------------------------+ //| 创建文字标签(封装重复逻辑) | //+------------------------------------------------------------------+ void CreateLabel(string name, int x, int y, string text, color clr, int size, string font) { // 创建文字标签对象(OBJ_LABEL为MQL5文字类型) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); // 设置锚点 ObjectSetInteger(0, name, OBJPROP_CORNER, PanelCorner); // 设置位置 ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); // 设置文字内容 ObjectSetString(0, name, OBJPROP_TEXT, text); // 设置文字颜色 ObjectSetInteger(0, name, OBJPROP_COLOR, clr); // 设置文字大小 ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size); // 设置字体 ObjectSetString(0, name, OBJPROP_FONT, font); } //+------------------------------------------------------------------+ //| 创建矩形(用于面板背景/分隔线) | //+------------------------------------------------------------------+ void CreateRectangle(string name, int x, int y, int width, int height, color clr) { // 创建矩形对象(OBJ_RECTANGLE_LABEL为带背景的矩形) ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0); // 设置锚点 ObjectSetInteger(0, name, OBJPROP_CORNER, PanelCorner); // 设置位置 ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); // 设置尺寸 ObjectSetInteger(0, name, OBJPROP_XSIZE, width); ObjectSetInteger(0, name, OBJPROP_YSIZE, height); // 设置背景色 ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clr); // 设置边框样式(扁平) ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT); // 设置边框颜色(黑色) ObjectSetInteger(0, name, OBJPROP_COLOR, clrBlack); } //+------------------------------------------------------------------+ //| 删除所有面板对象(避免残留) | //+------------------------------------------------------------------+ void DeleteAllObjects() { // 删除所有以prefix为前缀的对象(精准删除当前EA创建的面板) ObjectsDeleteAll(0, prefix); // 强制重绘图表(立即生效) ChartRedraw(0); } //+------------------------------------------------------------------+ // ===================== 扩展运用方式 ===================== /** * 1. 基础部署使用 * - 步骤1:将代码保存为OneClickCloseAll.mq5,放入MT5的MQL5/Experts目录; * - 步骤2:在MT5中编译代码(F7),确保无报错; * - 步骤3:将EA拖到任意品种图表,在EA设置中调整面板位置/颜色,勾选"允许自动交易"; * - 步骤4:点击面板按钮即可按条件平仓,所有操作有弹窗确认+日志记录。 * * 2. 功能扩展方向(二次开发) * - 扩展1:按魔术号平仓(新增按钮+函数,筛选PositionGetInteger(POSITION_MAGIC) == 指定魔术号); * - 扩展2:按盈亏比例平仓(如平仓盈亏>10%的持仓,修改ClosePositionsByProfit的判断条件); * - 扩展3:批量平仓指定品种(新增输入参数,支持输入多个品种,遍历平仓); * - 扩展4:添加平仓确认密码(防止误操作,在MessageBox后增加密码输入校验); * - 扩展5:自动平仓(如亏损超过X金额自动平仓,在OnTick中添加判断逻辑)。 * * 3. 注意事项 * - 务必开启MT5的"算法交易"和"自动交易"开关,否则无法平仓; * - 滑点参数(deviation=50)可根据品种调整(如外汇默认50,加密货币可设100); * - 平仓失败时查看MT5日志(终端->专家),根据错误码排查问题(如经纪商禁用FOK/IOC模式); * - 建议先在模拟账户测试,确认功能正常后再用于实盘。 */
//+------------------------------------------------------------------+ //| 智能移动止损(终极严谨版:带Magic过滤 + 无单直接退出) | //| 功能:遍历当前品种持仓,按盈利自动移动止损,支持Magic过滤 | //| 规则: | //| 1. BUY 新止损 = 旧止损 + 当前盈利 | //| 2. SELL 新止损 = 旧止损 - 当前盈利 | //| 3. 必须盈利 > 0 | //| 4. 新旧止损间距 ≥ 5倍点差 才允许移动(防频繁修改) | //| 5. 止损只向盈利方向移动,绝不回退 | //| 6. Magic 过滤:magic=0 处理所有订单;magic>0 只处理对应Magic订单 | //| 7. 优化:无任何持仓时,函数直接退出,不做任何多余计算 | //| 8. 纯原生MQL5,无DLL、无第三方依赖 | //+------------------------------------------------------------------+ void TrailSLByProfitFinal(int magic = 0) { //======================= 【提前判断:无订单直接退出】 ======================= // 先检查账户总持仓数,如果没有任何持仓,直接返回,不执行后面任何逻辑 if(PositionsTotal() == 0) return; //======================= 基础数据获取(只有存在订单时才获取) ======================= // 品种最小报价单位(1点对应的价格) double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 当前品种点差(单位:点) double spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); // 止损最小移动距离:5倍点差(换算成价格) double min_move = spread * 5.0 * point; //======================= 遍历所有持仓单 ======================= for(int i = 0; i < PositionsTotal(); i++) { // 获取当前循环到的持仓单号 ulong pos_ticket = PositionGetTicket(i); if(pos_ticket == 0) continue; //---------------------- // 过滤1:只处理当前图表品种 //---------------------- if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; //---------------------- // 过滤2:Magic 过滤(核心) //---------------------- ulong pos_magic = PositionGetInteger(POSITION_MAGIC); // 如果设置了magic(≠0),并且当前订单magic不匹配 // 则跳过这个订单,继续处理下一个订单,不修改它 if(magic != 0 && pos_magic != magic) continue; //---------------------- // 读取当前订单基础信息 //---------------------- ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double pos_open = PositionGetDouble(POSITION_PRICE_OPEN); double pos_sl_old = PositionGetDouble(POSITION_SL); double pos_tp = PositionGetDouble(POSITION_TP); double profit = 0.0; double sl_new = 0.0; bool modify = false; //---------------------- // 多单 BUY 处理 //---------------------- if(pos_type == POSITION_TYPE_BUY) { double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); profit = bid - pos_open; if(profit > 0) { sl_new = pos_sl_old + profit; // 必须同时满足3个条件才修改: // 1. 新止损比旧止损更高(只向前不后退) // 2. 移动距离 >= 5倍点差(防频繁修改) // 3. 新止损必须在当前买价下方(合法止损) if(sl_new > pos_sl_old && (sl_new - pos_sl_old) >= min_move && sl_new < bid) { modify = true; } } } //---------------------- // 空单 SELL 处理 //---------------------- else if(pos_type == POSITION_TYPE_SELL) { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); profit = pos_open - ask; if(profit > 0) { sl_new = pos_sl_old - profit; // 必须同时满足3个条件才修改: // 1. 新止损比旧止损更低(只向前不后退) // 2. 移动距离 >= 5倍点差(防频繁修改) // 3. 新止损必须在当前卖价上方(合法止损) if(sl_new < pos_sl_old && (pos_sl_old - sl_new) >= min_move && sl_new > ask) { modify = true; } } } //---------------------- // 执行修改止损 //---------------------- if(modify) { bool result = PositionModify(pos_ticket, sl_new, pos_tp); if(result) { Print("Magic:", magic, " 订单:", pos_ticket, " 止损移动成功 → 新SL:", DoubleToString(sl_new, _Digits)); } else { Print("Magic:", magic, " 订单:", pos_ticket, " 止损移动失败 错误:", GetLastError()); } } } }
1. Pinbar (针形线/Pinocchio Bar) 的识别
🧐 肉眼识别标准(K线结构)
- 长影线:影线(上影线或下影线)的长度至少是实体部分的 2 倍(有些策略要求 3 倍)。
- 小实体:实体部分很小,且通常位于 K 线的顶部或底部。
- 位置:
- 看涨 Pinbar:出现在下跌趋势中,下影线很长,像一根针一样探底回升。
- 看跌 Pinbar:出现在上涨趋势中,上影线很长,表明上方抛压重。
- 鼻子的位置:在更严格的定义中,Pinbar 的开盘价和收盘价(实体)应该位于这根 K 线总范围的 1/3 或 1/4 处,且最好处于近期高低点的极端位置。
⚙️ MT5 自动识别(工具辅助)
- Pin Bar Detector:这是 MT5 社区非常流行的指标。安装后,它会自动扫描所有时间框架,当发现符合“影线/实体比例”的 K 线时,会在图表上画出箭头或笑脸标记。
- 参数设置:你可以在指标设置中调整“影线倍数”(如设为 2 或 3),倍数越高,筛选出的信号越稀缺但通常越可靠。
2. 吞没形态 (Engulfing Pattern) 的识别
🧐 肉眼识别标准(K线组合)
- 第一根 K 线:实体较小,顺应当前的趋势(如跌势中的阴线)。
- 第二根 K 线:
- 颜色相反:必须与第一根颜色相反。
- 完全覆盖:第二根的实体部分(开盘价到收盘价)必须完全包住第一根的实体。
- 注意:严格来说,不需要包住影线,只要包住实体即可,但包住影线信号更强。
- 类型:
- 看涨吞没:下跌趋势末端,大阳线包住前一根小阴线。
- 看跌吞没:上涨趋势末端,大阴线包住前一根小阳线。
⚙️ MT5 自动识别(工具辅助)
- Candlestick Pattern Indicators:MT5 代码库中有许多名为“Engulfing Pattern”的指标。它们会在形成吞没形态的第二根 K 线上方或下方绘制图标。
- 编程逻辑:如果你懂一点 MQL5,识别逻辑很简单——判断
Close[1] > Open[0]且Open[1] < Close[0](看涨情况),即第二根的收盘价高于第一根开盘价,且第二根开盘价低于第一根收盘价。
📊 总结:MT5 识别对比表
| 特征 | Pinbar (针形线) | 吞没形态 (Engulfing) |
|---|---|---|
| K线数量 | 1 根 | 2 根 (组合) |
| 核心视觉 | 长影线,小实体,像“锤子”或“流星” | 后一根大实体完全覆盖前一根小实体 |
| 市场含义 | 拒绝:价格试探后被推回 | 吞噬:反向力量彻底压倒原趋势 |
| MT5 识别技巧 | 关注影线与实体的比例 (如 > 2:1) | 关注第二根 K 线实体是否大于第一根 |
| 最佳辅助工具 | Pin Bar Detector 指标 | Engulfing Pattern 扫描指标 |
💡 专家提示:如何过滤假信号?
- 关键位置 (Key Level):
- 形态必须出现在支撑位/阻力位、斐波那契回调位或趋势线附近。
- 例如:一个看涨吞没如果出现在半山腰(无明显支撑),往往是陷阱;如果出现在强支撑位,则是高胜率机会。
- 成交量 (Volume):
- 吞没形态的第二根 K 线,或者 Pinbar 的形成过程,最好伴随成交量的放大。这代表有大资金在参与反转。
//+------------------------------------------------------------------+ //| GoldMultiStrategyEA.mq5 | //| Multi-Strategy XAUUSD Expert Advisor | //| M1 Timeframe | Adaptive AI Scoring | //+------------------------------------------------------------------+ #property copyright "GoldMultiStrategyEA" #property link "" #property version "1.00" #property strict #property description "Multi-strategy EA for XAUUSD M1: Aggressive Scalping, SMC/ICT, Sniper Reversal" #property description "Features: AI Scoring, Adaptive Learning, Volatility Adaptation, Profit Protection" #include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include <Trade\SymbolInfo.mqh> //+------------------------------------------------------------------+ //| ENUMS | //+------------------------------------------------------------------+ enum ENUM_STRATEGY_MODE { MODE_AUTO = 0, // Auto (AI selects) MODE_SCALPING = 1, // Mode 1: Aggressive Scalping MODE_SMC = 2, // Mode 2: Smart Money (SMC/ICT) MODE_REVERSAL = 3 // Mode 3: Sniper Reversal }; enum ENUM_VOLATILITY { VOL_LOW = 0, VOL_MEDIUM = 1, VOL_HIGH = 2 }; enum ENUM_MARKET_STATE { STATE_TREND = 0, STATE_RANGE = 1 }; //+------------------------------------------------------------------+ //| INPUT PARAMETERS | //+------------------------------------------------------------------+ input group "═══════════ STRATEGY ═══════════" input ENUM_STRATEGY_MODE InpStrategyMode = MODE_AUTO; // Strategy Mode input bool InpAutoMode = true; // Enable Auto Mode Switching input group "═══════════ RISK MANAGEMENT ═══════════" input double InpRiskPerTrade = 1.0; // Risk Per Trade (% of balance) input int InpMaxTrades = 3; // Max Simultaneous Trades input double InpDailyProfitTarget = 3.0; // Daily Profit Target (%, 0=off) input double InpDailyLossLimit = 2.0; // Daily Loss Limit (%, 0=off) input group "═══════════ VOLATILITY / ATR ═══════════" input int InpATRPeriod = 14; // ATR Period input double InpATRLowThresh = 0.8; // ATR Low Threshold (multiplier of avg) input double InpATRHighThresh = 1.5; // ATR High Threshold (multiplier of avg) input group "═══════════ AI SCORING ═══════════" input double InpMinScore = 0.70; // Minimum Trade Score (0-1) input double InpWeightStructure = 0.25; // Weight: Structure input double InpWeightLiquidity = 0.25; // Weight: Liquidity input double InpWeightVolume = 0.20; // Weight: Volume input double InpWeightVolatility = 0.15; // Weight: Volatility input double InpWeightEntry = 0.15; // Weight: Entry Precision input group "═══════════ TRADE MANAGEMENT ═══════════" input bool InpUseBreakeven = true; // Enable Break-Even at 1R input bool InpUsePartialClose = true; // Enable 50% Partial Close at 1.5R input bool InpUseTrailingStop = true; // Enable Trailing Stop input double InpTrailingATRMult = 1.5; // Trailing Stop ATR Multiplier input group "═══════════ PROFIT PROTECTION ═══════════" input double InpReduceRiskAt3 = 3.0; // Reduce risk 50% at this % profit input double InpReduceRiskAt5 = 5.0; // Reduce risk 70% at this % profit input double InpDrawdownFromPeak = 2.0; // Stop trading: drawdown from peak (%) input int InpConsecWinReduce = 3; // Reduce lot after N consecutive wins input group "═══════════ EXECUTION ═══════════" input int InpMagicNumber = 77701; // Magic Number input double InpMaxSpread = 35.0; // Max Spread (points) input bool InpDebugLog = true; // Enable Debug Logs //+------------------------------------------------------------------+ //| GLOBAL OBJECTS | //+------------------------------------------------------------------+ CTrade g_trade; CPositionInfo g_position; CAccountInfo g_account; CSymbolInfo g_symbol; //+------------------------------------------------------------------+ //| TRADE HISTORY RECORD | //+------------------------------------------------------------------+ struct TradeRecord { double score; double pnl; bool isWin; ENUM_STRATEGY_MODE mode; }; //+------------------------------------------------------------------+ //| GLOBAL STATE | //+------------------------------------------------------------------+ // ATR / Volatility int g_atrHandle = INVALID_HANDLE; double g_atrBuffer[]; double g_currentATR = 0; ENUM_VOLATILITY g_volatility = VOL_MEDIUM; // Market State ENUM_MARKET_STATE g_marketState = STATE_RANGE; ENUM_STRATEGY_MODE g_activeMode = MODE_SCALPING; // Daily P&L tracking double g_dailyStartBalance = 0; double g_dailyProfit = 0; double g_peakProfit = 0; bool g_dailyStopped = false; int g_lastDay = -1; // Consecutive wins int g_consecutiveWins = 0; // Adaptive learning TradeRecord g_tradeHistory[]; int g_historyCount = 0; double g_adaptiveThreshold = 0; // added to InpMinScore double g_adaptiveLotMult = 1.0; // Signal weights (adaptive) double g_wStructure, g_wLiquidity, g_wVolume, g_wVolatility, g_wEntry; // Candle data double g_open[], g_high[], g_low[], g_close[]; long g_volume[]; // Tick volume handle int g_volHandle = INVALID_HANDLE; // Bar tracking datetime g_lastBarTime = 0; //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { // Validate symbol if(StringFind(Symbol(), "XAUUSD") < 0 && StringFind(Symbol(), "GOLD") < 0) { Print("WARNING: This EA is designed for XAUUSD. Current symbol: ", Symbol()); } // Initialize trade object g_trade.SetExpertMagicNumber(InpMagicNumber); g_trade.SetDeviationInPoints(20); g_trade.SetTypeFilling(ORDER_FILLING_IOC); // Initialize symbol info g_symbol.Name(Symbol()); g_symbol.Refresh(); // Create ATR indicator g_atrHandle = iATR(Symbol(), PERIOD_M1, InpATRPeriod); if(g_atrHandle == INVALID_HANDLE) { Print("ERROR: Failed to create ATR indicator"); return INIT_FAILED; } // Set array directions ArraySetAsSeries(g_atrBuffer, true); ArraySetAsSeries(g_open, true); ArraySetAsSeries(g_high, true); ArraySetAsSeries(g_low, true); ArraySetAsSeries(g_close, true); ArraySetAsSeries(g_volume, true); // Initialize weights g_wStructure = InpWeightStructure; g_wLiquidity = InpWeightLiquidity; g_wVolume = InpWeightVolume; g_wVolatility = InpWeightVolatility; g_wEntry = InpWeightEntry; // Initialize trade history ArrayResize(g_tradeHistory, 0); g_historyCount = 0; // Initialize daily tracking ResetDailyState(); DebugLog("EA initialized. Mode: " + EnumToString(InpStrategyMode) + " | AutoMode: " + (InpAutoMode ? "ON" : "OFF") + " | Risk: " + DoubleToString(InpRiskPerTrade, 2) + "%"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(g_atrHandle != INVALID_HANDLE) IndicatorRelease(g_atrHandle); DebugLog("EA deinitialized. Reason: " + IntegerToString(reason)); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Refresh symbol data g_symbol.Refresh(); g_symbol.RefreshRates(); // Check for new day → reset daily state MqlDateTime dt; TimeCurrent(dt); if(dt.day_of_year != g_lastDay) { ResetDailyState(); g_lastDay = dt.day_of_year; } // Update daily P&L UpdateDailyPnL(); // Check daily limits if(g_dailyStopped) return; if(CheckDailyLimits()) { g_dailyStopped = true; DebugLog("DAILY LIMIT HIT. Trading stopped for today."); return; } // Manage existing positions (every tick for responsiveness) ManageOpenPositions(); // Only process signals on new bar if(!IsNewBar()) return; // Load candle data if(!LoadCandleData()) return; // Update ATR if(!UpdateATR()) return; // Spread filter if(!CheckSpread()) return; // Max trades check if(CountOpenPositions() >= InpMaxTrades) return; // Detect volatility regime DetectVolatility(); // Detect market state DetectMarketState(); // Determine active mode DetermineActiveMode(); // Generate signal based on active mode int signal = 0; // 1=BUY, -1=SELL, 0=none double sl = 0, tp = 0; double score = 0; switch(g_activeMode) { case MODE_SCALPING: signal = SignalScalping(sl, tp, score); break; case MODE_SMC: signal = SignalSMC(sl, tp, score); break; case MODE_REVERSAL: signal = SignalReversal(sl, tp, score); break; default: signal = SignalScalping(sl, tp, score); break; } // Apply adaptive threshold double effectiveThreshold = MathMin(InpMinScore + g_adaptiveThreshold, 0.95); if(signal != 0 && score >= effectiveThreshold) { ExecuteTrade(signal, sl, tp, score); } } //+------------------------------------------------------------------+ //| UTILITY FUNCTIONS | //+------------------------------------------------------------------+ void DebugLog(string msg) { if(InpDebugLog) Print("[GMSE] ", msg); } bool IsNewBar() { datetime currentBarTime = iTime(Symbol(), PERIOD_M1, 0); if(currentBarTime == g_lastBarTime) return false; g_lastBarTime = currentBarTime; return true; } bool LoadCandleData() { if(CopyOpen(Symbol(), PERIOD_M1, 0, 100, g_open) < 100) return false; if(CopyHigh(Symbol(), PERIOD_M1, 0, 100, g_high) < 100) return false; if(CopyLow(Symbol(), PERIOD_M1, 0, 100, g_low) < 100) return false; if(CopyClose(Symbol(), PERIOD_M1, 0, 100, g_close) < 100) return false; if(CopyTickVolume(Symbol(), PERIOD_M1, 0, 100, g_volume) < 100) return false; return true; } bool UpdateATR() { if(CopyBuffer(g_atrHandle, 0, 0, 50, g_atrBuffer) < 50) return false; g_currentATR = g_atrBuffer[1]; // completed bar return (g_currentATR > 0); } bool CheckSpread() { double spread = g_symbol.Spread(); if(spread > InpMaxSpread) { return false; } return true; } int CountOpenPositions() { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(g_position.SelectByIndex(i)) { if(g_position.Symbol() == Symbol() && g_position.Magic() == InpMagicNumber) count++; } } return count; } //+------------------------------------------------------------------+ //| DAILY P&L MANAGEMENT | //+------------------------------------------------------------------+ void ResetDailyState() { g_dailyStartBalance = g_account.Balance(); g_dailyProfit = 0; g_peakProfit = 0; g_dailyStopped = false; DebugLog("Daily state reset. Start balance: " + DoubleToString(g_dailyStartBalance, 2)); } void UpdateDailyPnL() { double equity = g_account.Equity(); g_dailyProfit = ((equity - g_dailyStartBalance) / g_dailyStartBalance) * 100.0; if(g_dailyProfit > g_peakProfit) g_peakProfit = g_dailyProfit; } bool CheckDailyLimits() { // Profit target if(InpDailyProfitTarget > 0 && g_dailyProfit >= InpDailyProfitTarget) { DebugLog("Daily profit target reached: " + DoubleToString(g_dailyProfit, 2) + "%"); return true; } // Loss limit if(InpDailyLossLimit > 0 && g_dailyProfit <= -InpDailyLossLimit) { DebugLog("Daily loss limit hit: " + DoubleToString(g_dailyProfit, 2) + "%"); return true; } // Drawdown from peak (anti-giveback) if(g_peakProfit > InpDrawdownFromPeak && (g_peakProfit - g_dailyProfit) >= InpDrawdownFromPeak) { DebugLog("Drawdown from peak exceeded. Peak: " + DoubleToString(g_peakProfit, 2) + "% Current: " + DoubleToString(g_dailyProfit, 2) + "%"); return true; } return false; } //+------------------------------------------------------------------+ //| VOLATILITY DETECTION | //+------------------------------------------------------------------+ void DetectVolatility() { // Average ATR over last 50 bars double avgATR = 0; for(int i = 1; i <= 50; i++) avgATR += g_atrBuffer[i]; avgATR /= 50.0; if(avgATR <= 0) { g_volatility = VOL_MEDIUM; return; } double ratio = g_currentATR / avgATR; if(ratio < InpATRLowThresh) g_volatility = VOL_LOW; else if(ratio > InpATRHighThresh) g_volatility = VOL_HIGH; else g_volatility = VOL_MEDIUM; } //+------------------------------------------------------------------+ //| MARKET STATE DETECTION | //+------------------------------------------------------------------+ void DetectMarketState() { // Check for higher highs / higher lows (trend) vs range // Use last 20 bars, look at swing points int swingCount = 0; int hhCount = 0, hlCount = 0, lhCount = 0, llCount = 0; for(int i = 3; i < 18; i++) { // Swing high if(g_high[i] > g_high[i-1] && g_high[i] > g_high[i-2] && g_high[i] > g_high[i+1] && g_high[i] > g_high[i+2]) { swingCount++; // Compare with previous swing for(int j = i + 3; j < 20; j++) { if(g_high[j] > g_high[j-1] && g_high[j] > g_high[j-2] && g_high[j] > g_high[j+1] && g_high[j] > g_high[j+2]) { if(g_high[i] > g_high[j]) hhCount++; else lhCount++; break; } } } // Swing low if(g_low[i] < g_low[i-1] && g_low[i] < g_low[i-2] && g_low[i] < g_low[i+1] && g_low[i] < g_low[i+2]) { for(int j = i + 3; j < 20; j++) { if(g_low[j] < g_low[j-1] && g_low[j] < g_low[j-2] && g_low[j] < g_low[j+1] && g_low[j] < g_low[j+2]) { if(g_low[i] > g_low[j]) hlCount++; else llCount++; break; } } } } // ATR rising check bool atrRising = (g_atrBuffer[1] > g_atrBuffer[5] * 1.1); // Determine state if((hhCount >= 1 && hlCount >= 1) || (lhCount >= 1 && llCount >= 1)) { if(atrRising || g_volatility == VOL_HIGH) g_marketState = STATE_TREND; else g_marketState = STATE_TREND; } else { g_marketState = STATE_RANGE; } } //+------------------------------------------------------------------+ //| AUTO MODE SWITCHING | //+------------------------------------------------------------------+ void DetermineActiveMode() { if(InpStrategyMode != MODE_AUTO && !InpAutoMode) { g_activeMode = InpStrategyMode; return; } // Auto mode logic if(g_marketState == STATE_TREND && g_volatility == VOL_HIGH) { g_activeMode = MODE_SCALPING; // Aggressive breakout scalping } else if(g_marketState == STATE_TREND && (g_volatility == VOL_MEDIUM || g_volatility == VOL_LOW)) { g_activeMode = MODE_SMC; // Structural trading } else // STATE_RANGE or exhaustion { g_activeMode = MODE_REVERSAL; // Reversal / mean reversion } // Override: Low volatility → avoid aggressive modes if(g_volatility == VOL_LOW && g_activeMode == MODE_SCALPING) g_activeMode = MODE_REVERSAL; } //+------------------------------------------------------------------+ //| HELPER: Body / Wick ratios | //+------------------------------------------------------------------+ double CandleBody(int idx) { return MathAbs(g_close[idx] - g_open[idx]); } double CandleRange(int idx) { return g_high[idx] - g_low[idx]; } double UpperWick(int idx) { return g_high[idx] - MathMax(g_open[idx], g_close[idx]); } double LowerWick(int idx) { return MathMin(g_open[idx], g_close[idx]) - g_low[idx]; } bool IsBullish(int idx) { return g_close[idx] > g_open[idx]; } bool IsBearish(int idx) { return g_close[idx] < g_open[idx]; } double AverageVolume(int start, int count) { double sum = 0; for(int i = start; i < start + count && i < ArraySize(g_volume); i++) sum += (double)g_volume[i]; return sum / (double)count; } //+------------------------------------------------------------------+ //| HELPER: Support / Resistance detection | //+------------------------------------------------------------------+ double FindNearestSupport(int lookback) { double minLow = g_low[1]; for(int i = 2; i < lookback; i++) { // Swing low if(i + 2 < lookback) { if(g_low[i] < g_low[i-1] && g_low[i] < g_low[i+1]) { if(g_close[1] > g_low[i]) // price above this level { return g_low[i]; } } } } // Fallback: lowest low for(int i = 1; i < lookback; i++) if(g_low[i] < minLow) minLow = g_low[i]; return minLow; } double FindNearestResistance(int lookback) { double maxHigh = g_high[1]; for(int i = 2; i < lookback; i++) { if(i + 2 < lookback) { if(g_high[i] > g_high[i-1] && g_high[i] > g_high[i+1]) { if(g_close[1] < g_high[i]) { return g_high[i]; } } } } for(int i = 1; i < lookback; i++) if(g_high[i] > maxHigh) maxHigh = g_high[i]; return maxHigh; } //+------------------------------------------------------------------+ //| HELPER: Fibonacci levels | //+------------------------------------------------------------------+ double FibLevel(double swingHigh, double swingLow, double level) { return swingHigh - (swingHigh - swingLow) * level; } //+------------------------------------------------------------------+ //| HELPER: Find swing high/low in range | //+------------------------------------------------------------------+ double SwingHigh(int start, int count) { double val = g_high[start]; for(int i = start; i < start + count && i < ArraySize(g_high); i++) if(g_high[i] > val) val = g_high[i]; return val; } double SwingLow(int start, int count) { double val = g_low[start]; for(int i = start; i < start + count && i < ArraySize(g_low); i++) if(g_low[i] < val) val = g_low[i]; return val; } //+------------------------------------------------------------------+ //| MODE 1: AGGRESSIVE SCALPING SIGNAL | //+------------------------------------------------------------------+ int SignalScalping(double &sl, double &tp, double &score) { int signal = 0; double ask = g_symbol.Ask(); double bid = g_symbol.Bid(); double point = g_symbol.Point(); // Sub-scores double sStructure = 0, sLiquidity = 0, sVolume = 0, sVolatility = 0, sEntry = 0; // Average volume double avgVol = AverageVolume(2, 20); bool volumeSpike = ((double)g_volume[1] > avgVol * 1.5); // Momentum candle: body > 70% of range bool momentumBull = IsBullish(1) && CandleBody(1) > CandleRange(1) * 0.7; bool momentumBear = IsBearish(1) && CandleBody(1) > CandleRange(1) * 0.7; // Breakout detection: price breaks recent high/low double recentHigh = SwingHigh(2, 15); double recentLow = SwingLow(2, 15); bool breakoutUp = g_close[1] > recentHigh; bool breakoutDown = g_close[1] < recentLow; // Retest: after breakout, price pulls back slightly bool retestUp = breakoutUp && (g_low[1] <= recentHigh + g_currentATR * 0.3); bool retestDown = breakoutDown && (g_high[1] >= recentLow - g_currentATR * 0.3); // Dynamic SL range: 50-120 points based on ATR double slPoints = MathMax(50, MathMin(120, g_currentATR / point)); // Volatility adjustment if(g_volatility == VOL_LOW) slPoints *= 0.7; else if(g_volatility == VOL_HIGH) slPoints *= 1.3; // BUY SIGNAL if((breakoutUp || retestUp) && momentumBull && volumeSpike) { signal = 1; sl = ask - slPoints * point; tp = ask + slPoints * point * 2.0; // 1:2 RR // Score components sStructure = breakoutUp ? 0.8 : 0.5; sLiquidity = (g_close[1] > recentHigh) ? 0.7 : 0.4; sVolume = volumeSpike ? 0.9 : 0.4; sVolatility = (g_volatility == VOL_HIGH) ? 0.9 : (g_volatility == VOL_MEDIUM ? 0.6 : 0.3); sEntry = retestUp ? 0.9 : 0.6; } // SELL SIGNAL else if((breakoutDown || retestDown) && momentumBear && volumeSpike) { signal = -1; sl = bid + slPoints * point; tp = bid - slPoints * point * 2.0; sStructure = breakoutDown ? 0.8 : 0.5; sLiquidity = (g_close[1] < recentLow) ? 0.7 : 0.4; sVolume = volumeSpike ? 0.9 : 0.4; sVolatility = (g_volatility == VOL_HIGH) ? 0.9 : (g_volatility == VOL_MEDIUM ? 0.6 : 0.3); sEntry = retestDown ? 0.9 : 0.6; } // Compute weighted score score = sStructure * g_wStructure + sLiquidity * g_wLiquidity + sVolume * g_wVolume + sVolatility * g_wVolatility + sEntry * g_wEntry; if(signal != 0) DebugLog("SCALP Signal: " + (signal > 0 ? "BUY" : "SELL") + " | Score: " + DoubleToString(score, 3) + " [S:" + DoubleToString(sStructure,2) + " L:" + DoubleToString(sLiquidity,2) + " V:" + DoubleToString(sVolume,2) + " Vol:" + DoubleToString(sVolatility,2) + " E:" + DoubleToString(sEntry,2) + "]"); return signal; } //+------------------------------------------------------------------+ //| MODE 2: SMC / ICT SIGNAL | //+------------------------------------------------------------------+ int SignalSMC(double &sl, double &tp, double &score) { int signal = 0; double ask = g_symbol.Ask(); double bid = g_symbol.Bid(); double point = g_symbol.Point(); double sStructure = 0, sLiquidity = 0, sVolume = 0, sVolatility = 0, sEntry = 0; // ── BOS / CHOCH Detection ── // Break of Structure: price breaks previous swing double prevSwingHigh = SwingHigh(5, 20); double prevSwingLow = SwingLow(5, 20); double nearSwingHigh = SwingHigh(1, 5); double nearSwingLow = SwingLow(1, 5); bool bosUp = g_close[1] > prevSwingHigh; // Bullish BOS bool bosDown = g_close[1] < prevSwingLow; // Bearish BOS // CHOCH: Change of character - break against trend bool chochBull = false, chochBear = false; if(g_marketState == STATE_TREND) { // In downtrend, a break of recent high = CHOCH bullish if(g_close[2] < g_close[5] && bosUp) chochBull = true; if(g_close[2] > g_close[5] && bosDown) chochBear = true; } // ── Liquidity Sweep ── // Price spikes beyond a level then reverses bool sweepLow = (g_low[1] < prevSwingLow && g_close[1] > prevSwingLow); bool sweepHigh = (g_high[1] > prevSwingHigh && g_close[1] < prevSwingHigh); // ── Order Block Detection ── // Last bearish candle before bullish move (demand OB) bool demandOB = false, supplyOB = false; double obLevel = 0; for(int i = 2; i < 15; i++) { // Demand OB: bearish candle followed by strong bullish move if(IsBearish(i) && IsBullish(i-1) && CandleBody(i-1) > CandleBody(i) * 1.5) { if(g_close[1] >= g_low[i] && g_close[1] <= g_high[i]) { demandOB = true; obLevel = g_low[i]; break; } } // Supply OB if(IsBullish(i) && IsBearish(i-1) && CandleBody(i-1) > CandleBody(i) * 1.5) { if(g_close[1] >= g_low[i] && g_close[1] <= g_high[i]) { supplyOB = true; obLevel = g_high[i]; break; } } } // ── Fair Value Gap ── bool fvgBull = false, fvgBear = false; double fvgTop = 0, fvgBottom = 0; for(int i = 2; i < 15; i++) { // Bullish FVG: gap between candle[i+1] high and candle[i-1] low if(i + 1 < ArraySize(g_low)) { if(g_low[i-1] > g_high[i+1]) // gap up { fvgBull = true; fvgTop = g_low[i-1]; fvgBottom = g_high[i+1]; if(g_close[1] >= fvgBottom && g_close[1] <= fvgTop) { fvgBull = true; break; } fvgBull = false; } if(g_high[i-1] < g_low[i+1]) // gap down { fvgBear = true; fvgTop = g_low[i+1]; fvgBottom = g_high[i-1]; if(g_close[1] >= fvgBottom && g_close[1] <= fvgTop) { fvgBear = true; break; } fvgBear = false; } } } // ── Fibonacci Confluence ── double swH = SwingHigh(1, 30); double swL = SwingLow(1, 30); double fib50 = FibLevel(swH, swL, 0.50); double fib618 = FibLevel(swH, swL, 0.618); bool fibConfluence = (g_close[1] >= fib618 && g_close[1] <= fib50); // ── Volume ── double avgVol = AverageVolume(2, 20); bool volConfirm = ((double)g_volume[1] > avgVol * 1.2); // ── Signal Assembly ── // BUY: Bullish BOS/CHOCH + (sweep low or demand OB or bullish FVG) + confluence int bullPoints = 0; if(bosUp || chochBull) bullPoints += 2; if(sweepLow) bullPoints += 2; if(demandOB) bullPoints += 2; if(fvgBull) bullPoints += 1; if(fibConfluence) bullPoints += 1; if(volConfirm) bullPoints += 1; int bearPoints = 0; if(bosDown || chochBear) bearPoints += 2; if(sweepHigh) bearPoints += 2; if(supplyOB) bearPoints += 2; if(fvgBear) bearPoints += 1; if(fibConfluence) bearPoints += 1; if(volConfirm) bearPoints += 1; double slDist = g_currentATR * 2.0; if(bullPoints >= 4 && bullPoints > bearPoints) { signal = 1; // SL behind liquidity double liquidityLevel = (sweepLow) ? g_low[1] - g_currentATR * 0.5 : (demandOB) ? obLevel - g_currentATR * 0.3 : ask - slDist; sl = liquidityLevel; // TP: next liquidity zone (resistance) double nextLiq = FindNearestResistance(30); tp = (nextLiq > ask) ? nextLiq : ask + slDist * 2.0; // Ensure minimum 1:2 RR double risk = ask - sl; if(risk > 0 && (tp - ask) / risk < 2.0) tp = ask + risk * 2.0; } else if(bearPoints >= 4 && bearPoints > bullPoints) { signal = -1; double liquidityLevel = (sweepHigh) ? g_high[1] + g_currentATR * 0.5 : (supplyOB) ? obLevel + g_currentATR * 0.3 : bid + slDist; sl = liquidityLevel; double nextLiq = FindNearestSupport(30); tp = (nextLiq < bid) ? nextLiq : bid - slDist * 2.0; double risk = sl - bid; if(risk > 0 && (bid - tp) / risk < 2.0) tp = bid - risk * 2.0; } // Score sStructure = MathMin(1.0, (double)(MathMax(bullPoints, bearPoints)) / 7.0); sLiquidity = (sweepLow || sweepHigh) ? 0.9 : (demandOB || supplyOB) ? 0.7 : 0.3; sVolume = volConfirm ? 0.8 : 0.4; sVolatility = (g_volatility == VOL_MEDIUM) ? 0.8 : (g_volatility == VOL_HIGH ? 0.6 : 0.5); sEntry = fibConfluence ? 0.9 : (fvgBull || fvgBear) ? 0.7 : 0.4; score = sStructure * g_wStructure + sLiquidity * g_wLiquidity + sVolume * g_wVolume + sVolatility * g_wVolatility + sEntry * g_wEntry; if(signal != 0) DebugLog("SMC Signal: " + (signal > 0 ? "BUY" : "SELL") + " | BullPts:" + IntegerToString(bullPoints) + " BearPts:" + IntegerToString(bearPoints) + " | Score: " + DoubleToString(score, 3) + " | BOS:" + (bosUp||bosDown?"Y":"N") + " CHOCH:" + (chochBull||chochBear?"Y":"N") + " Sweep:" + (sweepLow||sweepHigh?"Y":"N") + " OB:" + (demandOB||supplyOB?"Y":"N") + " FVG:" + (fvgBull||fvgBear?"Y":"N") + " Fib:" + (fibConfluence?"Y":"N")); return signal; } //+------------------------------------------------------------------+ //| MODE 3: SNIPER REVERSAL SIGNAL | //+------------------------------------------------------------------+ int SignalReversal(double &sl, double &tp, double &score) { int signal = 0; double ask = g_symbol.Ask(); double bid = g_symbol.Bid(); double point = g_symbol.Point(); double sStructure = 0, sLiquidity = 0, sVolume = 0, sVolatility = 0, sEntry = 0; // ── Spike Detection ── // Large candle relative to ATR bool spike = CandleRange(1) > g_currentATR * 1.8; // ── Exhaustion Wick ── // Long wick relative to body → rejection double bodySize = CandleBody(1); double range = CandleRange(1); bool exhaustionWickUp = (UpperWick(1) > bodySize * 2.0 && UpperWick(1) > range * 0.5); bool exhaustionWickDown = (LowerWick(1) > bodySize * 2.0 && LowerWick(1) > range * 0.5); // ── Strong SNR Level ── // Price near a significant support/resistance double support = FindNearestSupport(40); double resistance = FindNearestResistance(40); double tolerance = g_currentATR * 0.5; bool atSupport = (g_close[1] <= support + tolerance && g_close[1] >= support - tolerance); bool atResistance = (g_close[1] >= resistance - tolerance && g_close[1] <= resistance + tolerance); // ── Volume Climax ── double avgVol = AverageVolume(2, 30); bool volumeClimax = ((double)g_volume[1] > avgVol * 2.0); // ── Mean Reversion Target ── // Simple: 20-bar moving average double sum20 = 0; for(int i = 1; i <= 20; i++) sum20 += g_close[i]; double mean20 = sum20 / 20.0; // ── Signal Assembly ── // BUY REVERSAL: at support + exhaustion wick down + volume climax int bullRev = 0; if(atSupport) bullRev += 2; if(exhaustionWickDown) bullRev += 2; if(spike) bullRev += 1; if(volumeClimax) bullRev += 2; if(g_close[1] < mean20) bullRev += 1; // below mean → room for reversion int bearRev = 0; if(atResistance) bearRev += 2; if(exhaustionWickUp) bearRev += 2; if(spike) bearRev += 1; if(volumeClimax) bearRev += 2; if(g_close[1] > mean20) bearRev += 1; if(bullRev >= 5 && bullRev > bearRev) { signal = 1; // SL beyond spike low sl = g_low[1] - g_currentATR * 0.5; // TP: mean reversion tp = mean20; // Ensure minimum 1:3 RR double risk = ask - sl; if(risk > 0) { if((tp - ask) / risk < 3.0) tp = ask + risk * 3.0; } } else if(bearRev >= 5 && bearRev > bullRev) { signal = -1; sl = g_high[1] + g_currentATR * 0.5; tp = mean20; double risk = sl - bid; if(risk > 0) { if((bid - tp) / risk < 3.0) tp = bid - risk * 3.0; } } // Score sStructure = MathMin(1.0, (double)(MathMax(bullRev, bearRev)) / 7.0); sLiquidity = (atSupport || atResistance) ? 0.9 : 0.3; sVolume = volumeClimax ? 0.95 : 0.3; sVolatility = spike ? 0.8 : 0.4; sEntry = (exhaustionWickUp || exhaustionWickDown) ? 0.9 : 0.4; score = sStructure * g_wStructure + sLiquidity * g_wLiquidity + sVolume * g_wVolume + sVolatility * g_wVolatility + sEntry * g_wEntry; if(signal != 0) DebugLog("REVERSAL Signal: " + (signal > 0 ? "BUY" : "SELL") + " | BullRev:" + IntegerToString(bullRev) + " BearRev:" + IntegerToString(bearRev) + " | Score: " + DoubleToString(score, 3) + " | Spike:" + (spike?"Y":"N") + " ExhWick:" + (exhaustionWickUp||exhaustionWickDown?"Y":"N") + " SNR:" + (atSupport||atResistance?"Y":"N") + " VolClimax:" + (volumeClimax?"Y":"N")); return signal; } //+------------------------------------------------------------------+ //| LOT SIZE CALCULATION | //+------------------------------------------------------------------+ double CalculateLotSize(double slDistance) { if(slDistance <= 0) return g_symbol.LotsMin(); double balance = g_account.Balance(); double riskPct = InpRiskPerTrade; // ── Profit Protection: reduce risk ── if(g_dailyProfit >= InpReduceRiskAt5 && InpReduceRiskAt5 > 0) riskPct *= 0.3; // 70% reduction else if(g_dailyProfit >= InpReduceRiskAt3 && InpReduceRiskAt3 > 0) riskPct *= 0.5; // 50% reduction // ── Consecutive wins reduction ── if(g_consecutiveWins >= InpConsecWinReduce && InpConsecWinReduce > 0) riskPct *= 0.8; // ── Adaptive lot multiplier ── riskPct *= g_adaptiveLotMult; double riskMoney = balance * riskPct / 100.0; double tickValue = g_symbol.TickValue(); double tickSize = g_symbol.TickSize(); if(tickValue <= 0 || tickSize <= 0) return g_symbol.LotsMin(); double lots = riskMoney / (slDistance / tickSize * tickValue); // Normalize double minLot = g_symbol.LotsMin(); double maxLot = g_symbol.LotsMax(); double lotStep = g_symbol.LotsStep(); lots = MathFloor(lots / lotStep) * lotStep; lots = MathMax(minLot, MathMin(maxLot, lots)); return lots; } //+------------------------------------------------------------------+ //| EXECUTE TRADE | //+------------------------------------------------------------------+ void ExecuteTrade(int signal, double sl, double tp, double score) { double ask = g_symbol.Ask(); double bid = g_symbol.Bid(); double point = g_symbol.Point(); // Sanitize SL/TP int digits = (int)g_symbol.Digits(); sl = NormalizeDouble(sl, digits); tp = NormalizeDouble(tp, digits); double price, slDist; string comment; if(signal == 1) { price = ask; slDist = price - sl; comment = StringFormat("GMSE|%s|S%.2f", EnumToString(g_activeMode), score); } else { price = bid; slDist = sl - price; comment = StringFormat("GMSE|%s|S%.2f", EnumToString(g_activeMode), score); } if(slDist <= 0) { DebugLog("Invalid SL distance. Skipping trade."); return; } double lots = CalculateLotSize(slDist); DebugLog("EXECUTING: " + (signal > 0 ? "BUY" : "SELL") + " | Lots: " + DoubleToString(lots, 2) + " | SL: " + DoubleToString(sl, digits) + " | TP: " + DoubleToString(tp, digits) + " | Score: " + DoubleToString(score, 3) + " | Mode: " + EnumToString(g_activeMode) + " | Vol: " + EnumToString(g_volatility) + " | State: " + EnumToString(g_marketState)); bool result; if(signal == 1) result = g_trade.Buy(lots, Symbol(), price, sl, tp, comment); else result = g_trade.Sell(lots, Symbol(), price, sl, tp, comment); if(!result) { DebugLog("ORDER FAILED: " + IntegerToString(g_trade.ResultRetcode()) + " - " + g_trade.ResultRetcodeDescription()); } else { DebugLog("ORDER PLACED: Ticket #" + IntegerToString((int)g_trade.ResultOrder())); } } //+------------------------------------------------------------------+ //| MANAGE OPEN POSITIONS | //+------------------------------------------------------------------+ void ManageOpenPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(!g_position.SelectByIndex(i)) continue; if(g_position.Symbol() != Symbol() || g_position.Magic() != InpMagicNumber) continue; double openPrice = g_position.PriceOpen(); double currentSL = g_position.StopLoss(); double currentTP = g_position.TakeProfit(); double profit = g_position.Profit(); double volume = g_position.Volume(); ulong ticket = g_position.Ticket(); // Calculate initial risk (1R) double initialRisk = MathAbs(openPrice - currentSL); if(initialRisk <= 0) continue; double currentPrice; double priceDelta; if(g_position.PositionType() == POSITION_TYPE_BUY) { currentPrice = g_symbol.Bid(); priceDelta = currentPrice - openPrice; } else { currentPrice = g_symbol.Ask(); priceDelta = openPrice - currentPrice; } double rMultiple = priceDelta / initialRisk; // ── Break-even at 1R ── if(InpUseBreakeven && rMultiple >= 1.0) { double beLevel; if(g_position.PositionType() == POSITION_TYPE_BUY) beLevel = openPrice + g_symbol.Spread() * g_symbol.Point(); else beLevel = openPrice - g_symbol.Spread() * g_symbol.Point(); beLevel = NormalizeDouble(beLevel, (int)g_symbol.Digits()); if(g_position.PositionType() == POSITION_TYPE_BUY && currentSL < beLevel) { g_trade.PositionModify(ticket, beLevel, currentTP); DebugLog("Break-even set for BUY #" + IntegerToString((int)ticket)); } else if(g_position.PositionType() == POSITION_TYPE_SELL && (currentSL > beLevel || currentSL == 0)) { g_trade.PositionModify(ticket, beLevel, currentTP); DebugLog("Break-even set for SELL #" + IntegerToString((int)ticket)); } } // ── Partial Close 50% at 1.5R ── if(InpUsePartialClose && rMultiple >= 1.5) { // Parse comment to check if already partially closed string comment = g_position.Comment(); if(StringFind(comment, "PC") < 0 && volume > g_symbol.LotsMin()) { double closeVol = MathFloor((volume * 0.5) / g_symbol.LotsStep()) * g_symbol.LotsStep(); if(closeVol >= g_symbol.LotsMin()) { g_trade.PositionClosePartial(ticket, closeVol); DebugLog("Partial close 50% for #" + IntegerToString((int)ticket) + " | Closed: " + DoubleToString(closeVol, 2)); // Note: We can't modify comment on partial, but the volume change tracks it } } } // ── Trailing Stop (ATR-based) ── if(InpUseTrailingStop && rMultiple >= 1.0 && g_currentATR > 0) { double trailDist = g_currentATR * InpTrailingATRMult; double newSL; if(g_position.PositionType() == POSITION_TYPE_BUY) { newSL = NormalizeDouble(currentPrice - trailDist, (int)g_symbol.Digits()); if(newSL > currentSL && newSL < currentPrice) { g_trade.PositionModify(ticket, newSL, currentTP); } } else { newSL = NormalizeDouble(currentPrice + trailDist, (int)g_symbol.Digits()); if((newSL < currentSL || currentSL == 0) && newSL > currentPrice) { g_trade.PositionModify(ticket, newSL, currentTP); } } } } } //+------------------------------------------------------------------+ //| ADAPTIVE LEARNING | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { // Only process deal additions (closed trades) if(trans.type != TRADE_TRANSACTION_DEAL_ADD) return; // Check if it's our trade closing if(trans.deal_type != DEAL_TYPE_BUY && trans.deal_type != DEAL_TYPE_SELL) return; // Get deal info if(!HistoryDealSelect(trans.deal)) return; long dealMagic = HistoryDealGetInteger(trans.deal, DEAL_MAGIC); if(dealMagic != InpMagicNumber) return; long entry = HistoryDealGetInteger(trans.deal, DEAL_ENTRY); if(entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_OUT_BY) return; double dealProfit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT); double dealSwap = HistoryDealGetDouble(trans.deal, DEAL_SWAP); double dealComm = HistoryDealGetDouble(trans.deal, DEAL_COMMISSION); double netPnl = dealProfit + dealSwap + dealComm; // Parse score from comment string comment = HistoryDealGetString(trans.deal, DEAL_COMMENT); double tradeScore = ParseScoreFromComment(comment); // Record trade TradeRecord rec; rec.score = tradeScore; rec.pnl = netPnl; rec.isWin = (netPnl > 0); rec.mode = g_activeMode; // Add to history (keep last 50) int size = ArraySize(g_tradeHistory); if(size >= 50) { // Shift left for(int i = 0; i < size - 1; i++) g_tradeHistory[i] = g_tradeHistory[i + 1]; g_tradeHistory[size - 1] = rec; } else { ArrayResize(g_tradeHistory, size + 1); g_tradeHistory[size] = rec; } g_historyCount = ArraySize(g_tradeHistory); // Update consecutive wins if(netPnl > 0) g_consecutiveWins++; else g_consecutiveWins = 0; // ── Adaptive Logic ── AdaptiveUpdate(); DebugLog("TRADE CLOSED: PnL=" + DoubleToString(netPnl, 2) + " | Score=" + DoubleToString(tradeScore, 3) + " | ConsecWins=" + IntegerToString(g_consecutiveWins) + " | AdaptiveThresh=" + DoubleToString(g_adaptiveThreshold, 3) + " | AdaptiveLotMult=" + DoubleToString(g_adaptiveLotMult, 3)); } double ParseScoreFromComment(string comment) { // Format: "GMSE|MODE_XXX|S0.85" int pos = StringFind(comment, "|S"); if(pos < 0) return 0.7; // default string scoreStr = StringSubstr(comment, pos + 2); return StringToDouble(scoreStr); } void AdaptiveUpdate() { if(g_historyCount < 10) return; // Count wins/losses for low-score and high-score trades int lowScoreLoss = 0, lowScoreTotal = 0; int highScoreWin = 0, highScoreTotal = 0; double medianScore = InpMinScore; for(int i = 0; i < g_historyCount; i++) { if(g_tradeHistory[i].score < medianScore + 0.1) { lowScoreTotal++; if(!g_tradeHistory[i].isWin) lowScoreLoss++; } else { highScoreTotal++; if(g_tradeHistory[i].isWin) highScoreWin++; } } // If low-score trades losing → increase threshold if(lowScoreTotal > 3) { double lossRate = (double)lowScoreLoss / (double)lowScoreTotal; if(lossRate > 0.6) g_adaptiveThreshold = MathMin(g_adaptiveThreshold + 0.02, 0.15); else if(lossRate < 0.4) g_adaptiveThreshold = MathMax(g_adaptiveThreshold - 0.01, 0.0); } // If high-score trades winning → slightly increase lot if(highScoreTotal > 3) { double winRate = (double)highScoreWin / (double)highScoreTotal; if(winRate > 0.65) g_adaptiveLotMult = MathMin(g_adaptiveLotMult + 0.05, 1.3); else if(winRate < 0.4) g_adaptiveLotMult = MathMax(g_adaptiveLotMult - 0.05, 0.7); } // ── Reduce weight of weak signals ── // Calculate win rate per factor (approximate via score correlation) UpdateSignalWeights(); } void UpdateSignalWeights() { // Simple approach: if recent trades are mostly losers, slightly shift weights // toward structure and liquidity (most reliable) and away from volume/volatility if(g_historyCount < 20) return; int recentWins = 0; int recentCount = MathMin(20, g_historyCount); for(int i = g_historyCount - recentCount; i < g_historyCount; i++) { if(g_tradeHistory[i].isWin) recentWins++; } double winRate = (double)recentWins / (double)recentCount; if(winRate < 0.4) { // Shift toward structure/liquidity (more conservative) g_wStructure = MathMin(0.35, g_wStructure + 0.01); g_wLiquidity = MathMin(0.30, g_wLiquidity + 0.01); g_wVolume = MathMax(0.10, g_wVolume - 0.01); g_wVolatility = MathMax(0.05, g_wVolatility - 0.005); g_wEntry = MathMax(0.10, g_wEntry - 0.005); } else if(winRate > 0.6) { // Revert toward default g_wStructure = g_wStructure + (InpWeightStructure - g_wStructure) * 0.1; g_wLiquidity = g_wLiquidity + (InpWeightLiquidity - g_wLiquidity) * 0.1; g_wVolume = g_wVolume + (InpWeightVolume - g_wVolume) * 0.1; g_wVolatility = g_wVolatility + (InpWeightVolatility - g_wVolatility) * 0.1; g_wEntry = g_wEntry + (InpWeightEntry - g_wEntry) * 0.1; } // Normalize weights to sum to 1 double total = g_wStructure + g_wLiquidity + g_wVolume + g_wVolatility + g_wEntry; if(total > 0) { g_wStructure /= total; g_wLiquidity /= total; g_wVolume /= total; g_wVolatility /= total; g_wEntry /= total; } } //+------------------------------------------------------------------+ //| OnTimer - for periodic operations | //+------------------------------------------------------------------+ void OnTimer() { // Reserved for future use (e.g., periodic state logging) } //+------------------------------------------------------------------+
============== End

浙公网安备 33010602011771号