惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - lsgxeva

OpenSpec OPSX 完整指南 Claude Skill Creator 2.0 完整上手攻略 Auto-Memory + CLAUDE.md Conductor 完整上手攻略 GitNexus 完整上手攻略 code-review-graph 完整上手攻略 Claude Code Hooks 完整开发者指南 Openwrt switch vlan配置 llm-course Claude Code 入门教程 routeros RB750GR3 配置双WAN口 Quectel Modem Wiki sdxlemur 高通5G平台(SDX55\SDX62\SDX65):ping包异常问题排查指南 - lsgxeva - 博客园 高通SDX62平台 MBIM搜网、查询信号等功能异常 Win11数字许可证激活 BirdSat VS100K info wireshark筛选语句详解 linux基线整改方法 windows基线整改方法 NanoPi_R5C ArcBox Config win10远程桌面其他电脑出现如下错误,由于数据加密错误,这个会话讲结束,请重新连接到远程计算机 如何评价杨立昆认为大模型只是对海量文本的模式进行复杂拟合,根本不懂意义? IQ200Board default access problem Win10 输入法卡顿 adaptive_relaxed_optimized 如何下载安装App Store应用旧版本教程 小米澎湃OS 关闭广告 Scrum 模型
MT5专业交易面板
lsgxeva · 2026-04-04 · via 博客园 - lsgxeva

9.确保工具栏中的 "自动交易 "按钮已启用(绿色) 10.

//+------------------------------------------------------------------+
//|                                   一键关闭所有仓位 - 专业交易面板.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模式);
 *    - 建议先在模拟账户测试,确认功能正常后再用于实盘。
 */