创建MQL5交易管理员面板(第九部分):代码组织(1)·进阶篇
📘

创建MQL5交易管理员面板(第九部分):代码组织(1)·进阶篇

第 2/3 篇

把管理面板代码拆干净

重构第一步不是加功能,而是让原有 EA 或脚本在拆分后仍能原样跑起来。我们面对的五类病灶里,整体式结构和紧密耦合最拖维护效率:前者把声明和实现全堆在主文件,后者让 UI 处理直接和交易逻辑搅在一起。 把认证抽成独立 include 文件,交易逻辑交给基于 CTrade 派生出的 TradeManager 头文件,主面板只留装配代码。下面这段就是拆分后的骨架示例,包含 AuthManager 类、两个头文件引用,以及通用的按钮创建辅助函数。 重复创建面板和按钮的代码,用 CreateButton 这种辅助函数收口后,后续十几个控件都能一行调用,冗余直接砍掉。命名上原来用小写 t 和缩写 management,协作时容易误读,改成 PascalCase 加动作导向的 HandleThemeToggle 更顺手。 硬编码密码暂时留着,但失败尝试满 3 次就强制走双因子,这一点在 AuthManager::Requires2FA 里写死。外汇与贵金属 EA 改造涉及实盘逻辑,回测前请在 MT5 策略测试器确认权限与面板资源不冲突,高风险。

MQL5 / C++
class AuthManager {
class="kw">private:
    class="type">class="kw">string m_password;
    class="type">int m_failedAttempts;
class="kw">public:
    AuthManager(class="type">class="kw">string password) : m_password(password), m_failedAttempts(class="num">0) {}
    
    class="type">bool Authenticate(class="type">class="kw">string input) {
        if(input == m_password) {
            m_failedAttempts = class="num">0;
            class="kw">return true;
        }
        m_failedAttempts++;
        class="kw">return class="kw">false;
    }
    
    class="type">bool Requires2FA() const {
        class="kw">return m_failedAttempts >= class="num">3;
    }
};
class="macro">#include <AuthManager.mqh>
class="macro">#include <TradeManager.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Generic Button Creation Helper                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CreateButton(CButton &button, CDialog &panel, const class="type">class="kw">string name, const class="type">class="kw">string text, class="type">int x1, class="type">int y1, class="type">int x2, class="type">int y2)
{
    if(!button.Create(ChartID(), name, class="num">0, x1, y1, x2, y2))
    {
        Print("Failed to create button: ", name);
        class="kw">return class="kw">false;
    }
    button.Text(text);
    panel.Add(button);
    class="kw">return true;
}
CreateButton(TradeMgmtAccessButton, AdminHomePanel, "TradeMgmtAccessButton", "Trade Management Panel", class="num">10, class="num">20, class="num">250, class="num">60)
CButton tradeMgmtAccessButton;  class=class="str">"cmt">// Inconsistent
class="type">void OnToggleThemeButtonClick(); class=class="str">"cmt">// Verbose
CButton TradeManagementAccessButton;       class=class="str">"cmt">//  PascalCase
class="type">void HandleThemeToggle();      class=class="str">"cmt">// Action-oriented

◍ 从 v1.24 到 v1.25 的面板重构落点

把主题功能从主文件里抽出去单独成头文件后,EA 管理面板在 v1.25 里不再和内置类扩展缠在一起,编译能过、加载即弹安全验证,输对密码才放你进主面板。默认口令写死在代码里是 "2024",实盘前必须改掉,外汇和贵金属杠杆高,这处疏漏可能直接把面板控制权送人。 v1.24 里 UI 初始化散落各处,ShowAuthenticationPrompt 这类函数和全局声明混排;v1.25 用「交易管理功能」「认证管理」「面板初始化功能」的明确分段把关注点切开。InitializeAuthenticationDialog 等初始化函数不再和事件处理绞在一起,复杂度降下来,组件从初始化到交互的生命周期一眼能追。 新引入的 CAnalyticsChart 和 CCustomPieChart 把画图逻辑封进类里,单一职责换来复用性。下面这段是 v1.25 头部的真实声明,注意 input 里的 Telegram 凭据留了 YOUR_CHAT_ID 占位,接通知前得填真值。 别把默认口令当临时方案 代码里 DefaultPassword 常量直接赋 "2024",很多人测完忘改就挂真账户。MT5 加载后第一道关卡形同虚设,建议本地改完再编译,或改成从加密配置文件读。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//|                     Admin Panel.mq5                              |
class=class="str">"cmt">//|         Copyright class="num">2024, Clemence Benjamin                         |
class=class="str">"cmt">//|  [MQL5官方文档]             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, Clemence Benjamin"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property description "A secure and responsive, communications, trade management and analytics Panel"
class="macro">#class="kw">property version   "class="num">1.25"
class=class="str">"cmt">//Essential header files included
class="macro">#include <Trade\Trade.mqh>
class="macro">#include <Controls\Dialog.mqh>
class="macro">#include <Controls\Button.mqh>
class="macro">#include <Controls\Edit.mqh>
class="macro">#include <Controls\Label.mqh>
class="macro">#include <Canvas\Charts\PieChart.mqh>
class="macro">#include <Canvas\Charts\ChartCanvas.mqh>
class=class="str">"cmt">// Input parameters for quick messages
input class="type">class="kw">string QuickMessage1 = "Updates";
input class="type">class="kw">string QuickMessage2 = "Close all";
input class="type">class="kw">string QuickMessage3 = "In deep profits";
input class="type">class="kw">string QuickMessage4 = "Hold position";
input class="type">class="kw">string QuickMessage5 = "Swing Entry";
input class="type">class="kw">string QuickMessage6 = "Scalp Entry";
input class="type">class="kw">string QuickMessage7 = "Book profit";
input class="type">class="kw">string QuickMessage8 = "Invalid Signal";
input class="type">class="kw">string InputChatId = "YOUR_CHAT_ID";           class=class="str">"cmt">// Telegram chat ID for notifications
input class="type">class="kw">string InputBotToken = "YOUR_BOT_TOKEN";       class=class="str">"cmt">// Telegram bot token
class=class="str">"cmt">// Security Configuration
const class="type">class="kw">string TwoFactorAuthChatId = "REPLACE_WITH_YOUR_CHAT_ID";     class=class="str">"cmt">// 2FA notification channel
const class="type">class="kw">string TwoFactorAuthBotToken = "REPLACE_WITH_YOUR_BOT_TOKEN"; class=class="str">"cmt">// 2FA bot credentials
const class="type">class="kw">string DefaultPassword = "class="num">2024";                            class=class="str">"cmt">// Default access password
class=class="str">"cmt">// Global UI Components
CDialog AdminHomePanel, TradeManagementPanel, CommunicationsPanel, AnalyticsPanel;
CButton HomeButtonComm, HomeButtonTrade, SendButton, ClearButton;
CButton ChangeFontButton, ToggleThemeButton, LoginButton, CloseAuthButton;
CButton TwoFactorAuthLoginButton, CloseTwoFactorAuthButton, MinimizeCommsButton;
CButton CloseCommsButton, TradeMgmtAccessButton, CommunicationsPanelAccessButton;

「面板对象与交易初始化的骨架」

这段声明把 EA 的界面元件和交易常量一次性铺开:分析面板、消息按钮、输入框、标签各司其职,交易执行组件则单列了买/卖/全平/分平八类按钮。安全状态用 FailedAttempts、IsTrustedUser、ActiveTwoFactorAuthCode 三个变量管登录失败计数、可信标记和动态验证码,和外汇贵金属的高频风控逻辑直接挂钩,这类品种杠杆波动大,失败计数不隔离容易锁死会话。 交易常量写死在代码里:DefaultLotSize=1.0 标准手、DefaultSlippage=3 点偏差、止损止盈默认 0。实盘跑之前你最好把 1.0 手改成 0.01 验证,贵金属 XAUUSD 一手波动可能瞬间扫掉小数账户。 OnInit 里四个 Initialize* 函数任一返回 false 就 Print('Initialization failed') 并回 INIT_FAILED,随后把 AdminHomePanel、TradeManagementPanel、CommunicationsPanel、AnalyticsPanel 全部 Hide,只留登录对话框。你开 MT5 挂这个 EA,若面板全隐只剩认证窗,说明初始化链路通了。 ExecuteMarketOrder 用 CTrade 统一发单:买按 SYMBOL_ASK、卖按 SYMBOL_BID 取价,价≤0 直接 Print 错误码并撤出。下面这段是原文核心声明与初始化代码,逐行拆完就能抄去改。

MQL5 / C++
CButton AnalyticsPanelAccessButton, ShowAllButton, QuickMessageButtons[class="num">8];
CEdit InputBox, PasswordInputBox, TwoFactorAuthCodeInput;
CLabel CharCounter, PasswordPromptLabel, FeedbackLabel;
CLabel TwoFactorAuthPromptLabel, TwoFactorAuthFeedbackLabel;
class=class="str">"cmt">// Trade Execution Components
CButton BuyButton, SellButton, CloseAllButton, CloseProfitButton;
CButton CloseLossButton, CloseBuyButton, CloseSellButton;
CButton DeleteAllOrdersButton, DeleteLimitOrdersButton;
CButton DeleteStopOrdersButton, DeleteStopLimitOrdersButton;
class=class="str">"cmt">// Security State Management
class="type">int FailedAttempts = class="num">0;                    class=class="str">"cmt">// Track consecutive failed login attempts
class="type">bool IsTrustedUser = class="kw">false;                class=class="str">"cmt">// Flag for verified users
class="type">class="kw">string ActiveTwoFactorAuthCode = ""; class=class="str">"cmt">// Generated 2FA verification code
class=class="str">"cmt">// Trade Execution Constants
const class="type">class="kw">double DefaultLotSize = class="num">1.0;   class=class="str">"cmt">// Standard trade volume
const class="type">class="kw">double DefaultSlippage = class="num">3;     class=class="str">"cmt">// Allowed price deviation
const class="type">class="kw">double DefaultStopLoss = class="num">0;     class=class="str">"cmt">// Default risk management
const class="type">class="kw">double DefaultTakeProfit = class="num">0; class=class="str">"cmt">// Default profit target
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Program Initialization                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
   if(!InitializeAuthenticationDialog() ||
      !InitializeAdminHomePanel() ||
      !InitializeTradeManagementPanel() ||
      !InitializeCommunicationsPanel())
      
   {
      Print("Initialization failed");
      class="kw">return INIT_FAILED;
   }
   
   AdminHomePanel.Hide();
   TradeManagementPanel.Hide();
   CommunicationsPanel.Hide();
   AnalyticsPanel.Hide();
   
   class="kw">return INIT_SUCCEEDED;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Trade Management Functions                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CTrade TradeExecutor;   class=class="str">"cmt">// Centralized trade execution handler
class=class="str">"cmt">// Executes a market order with predefined parameters
class=class="str">"cmt">// name="orderType">Type of order(ORDER_TYPE_BUY/ORDER_TYPE_SELL)
class=class="str">"cmt">// <returns>True if order execution succeeded</returns>
class="type">bool ExecuteMarketOrder(class="type">int orderType)
{
   class="type">class="kw">double executionPrice = (orderType == ORDER_TYPE_BUY) ?
         SymbolInfoDouble(Symbol(), SYMBOL_ASK) :
         SymbolInfoDouble(Symbol(), SYMBOL_BID);
   if(executionPrice <= class="num">0)
   {
      Print("Price retrieval failed. Error: ", GetLastError());
      class="kw">return class="kw">false;
   }
   class="type">bool orderResult = (orderType == ORDER_TYPE_BUY) ?
         TradeExecutor.Buy(DefaultLotSize, Symbol(), executionPrice, DefaultSlippage, DefaultStopLoss, DefaultTakeProfit) :

平仓筛选与登录弹窗的落地写法

这段逻辑把「按条件平仓」和「鉴权弹窗初始化」拆成了两个独立函数,实战里可以直接抄去管仓位。ClosePositions 用 closureCondition 参数控制范围:0 平所有、1 只平盈利、−1 只平亏损、2 平多单、3 平空单,回测时切到 1 就能快速锁利。 循环从 PositionsTotal()-1 倒序取到 0,避免平仓后索引错位漏单;positionInfo.SelectByIndex(i) 命中后再用 Profit() 和 Type() 做二次过滤,最后拿 Ticket() 交给 PositionClose。外汇与贵金属杠杆高,批量平亏损单前先确认保证金占用,否则可能触发强平。 鉴权部分 InitializeAuthenticationDialog 用 CDialog 挂出 500×300 的窗口,密码框、提示标签、反馈标签和两个按钮的坐标都是写死的(如密码框 20,70 到 260,95)。若组件任一 Create 返回 false 就 Print 报错并退出,MT5 上跑起来若弹窗不出来,先去日志看是不是坐标超出图表可视区。 别把正态当圣经:这种硬编码坐标在 4K 屏或缩放比异常时可能错位,建议改成按 ChartGetInteger(0,CHART_WIDTH_IN_PIXELS) 动态算。

MQL5 / C++
TradeExecutor.Sell(DefaultLotSize, Symbol(), executionPrice, DefaultSlippage, DefaultStopLoss, DefaultTakeProfit);
if(orderResult)
{
   Print(orderType == ORDER_TYPE_BUY ? "Buy" : "Sell", " order executed successfully");
}
else
{
   Print("Order execution failed. Error: ", GetLastError());
}
class="kw">return orderResult;
}
class=class="str">"cmt">// Closes positions based on specified criteria
class=class="str">"cmt">//  name="closureCondition"
class=class="str">"cmt">// class="num">0=All, class="num">1=Profitable, -class="num">1=Losing, class="num">2=Buy, class="num">3=Sell
class="type">bool ClosePositions(class="type">int closureCondition)
{
   CPositionInfo positionInfo;
   for(class="type">int i = PositionsTotal()-class="num">1; i >= class="num">0; i--)
   {
      if(positionInfo.SelectByIndex(i) &&
         (closureCondition == class="num">0 ||
         (closureCondition == class="num">1 && positionInfo.Profit() > class="num">0) ||
         (closureCondition == -class="num">1 && positionInfo.Profit() < class="num">0) ||
         (closureCondition == class="num">2 && positionInfo.Type() == POSITION_TYPE_BUY) ||
         (closureCondition == class="num">3 && positionInfo.Type() == POSITION_TYPE_SELL)))
      {
         TradeExecutor.PositionClose(positionInfo.Ticket());
      }
   }
   class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Authentication Management                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
CDialog AuthenticationDialog, TwoFactorAuthDialog;
class=class="str">"cmt">/// Initializes the primary authentication dialog
class="type">bool InitializeAuthenticationDialog()
{
   if(!AuthenticationDialog.Create(ChartID(), "Authentication", class="num">0, class="num">100, class="num">100, class="num">500, class="num">300))
      class="kw">return class="kw">false;
   class=class="str">"cmt">// Create dialog components
   if(!PasswordInputBox.Create(ChartID(), "PasswordInput", class="num">0, class="num">20, class="num">70, class="num">260, class="num">95) ||
      !PasswordPromptLabel.Create(ChartID(), "PasswordPrompt", class="num">0, class="num">20, class="num">20, class="num">260, class="num">40) ||
      !FeedbackLabel.Create(ChartID(), "AuthFeedback", class="num">0, class="num">20, class="num">140, class="num">380, class="num">160) ||
      !LoginButton.Create(ChartID(), "LoginButton", class="num">0, class="num">20, class="num">120, class="num">100, class="num">140) ||
      !CloseAuthButton.Create(ChartID(), "CloseAuthButton", class="num">0, class="num">120, class="num">120, class="num">200, class="num">140))
   {
      Print("Authentication component creation failed");
      class="kw">return class="kw">false;

◍ 面板与双因素认证的代码拼装细节

这段逻辑展示了管理后台对话框与 Telegram 二次验证的落地方式。先配置密码弹窗的标签、反馈文字与按钮文案,再把五个控件塞进 AuthenticationDialog 并调 Show() 弹出,返回 true 表示初始化成功。 双因素认证用 MathRand() % 1000000 生成 0–999999 的随机数,再用 StringFormat("%06d") 补齐为 6 位字符串,通过 SendMessageToTelegram 推到指定 chat_id。实盘里若 BotToken 或 ChatId 填错,验证码发不出去,登录流程会卡死。 管理主面板用 Create(ChartID(), "Admin Home Panel", 0, 30, 80, 335, 350) 定位在图表左上,宽 335 高 350;四个入口按钮的 y 坐标从 20 递增到 170,间距 50,按钮宽 250。交易管理面板则放在 x=500、y=30,尺寸 1280×170,Buy 与 Sell 按钮的 x 相差 90 像素,方便手指点。 外汇与贵金属杠杆高,这类后台若用在实盘 EA,任何面板创建失败都应直接 return false 阻断启动,避免盲操下单。

MQL5 / C++
   }
   class=class="str">"cmt">// Configure component properties
   PasswordPromptLabel.Text("Enter Administrator Password:");
   FeedbackLabel.Text("");
   FeedbackLabel.Color(clrRed);
   LoginButton.Text("Login");
   CloseAuthButton.Text("Cancel");
   class=class="str">"cmt">// Assemble dialog
   AuthenticationDialog.Add(PasswordInputBox);
   AuthenticationDialog.Add(PasswordPromptLabel);
   AuthenticationDialog.Add(FeedbackLabel);
   AuthenticationDialog.Add(LoginButton);
   AuthenticationDialog.Add(CloseAuthButton);
   
   AuthenticationDialog.Show();
   class="kw">return true;
}
class=class="str">"cmt">/// Generates a class="num">6-digit 2FA code and sends via Telegram
class="type">void HandleTwoFactorAuthentication()
{
   ActiveTwoFactorAuthCode = StringFormat("%06d", MathRand() % class="num">1000000);
   SendMessageToTelegram("Your verification code: " + ActiveTwoFactorAuthCode,
                         TwoFactorAuthChatId, TwoFactorAuthBotToken);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Panel Initialization Functions                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool InitializeAdminHomePanel()
{
   if(!AdminHomePanel.Create(ChartID(), "Admin Home Panel", class="num">0, class="num">30, class="num">80, class="num">335, class="num">350))
      class="kw">return class="kw">false;
   class="kw">return CreateButton(TradeMgmtAccessButton, AdminHomePanel, "TradeMgmtAccessButton", "Trade Management Panel", class="num">10, class="num">20, class="num">250, class="num">60) &&
          CreateButton(CommunicationsPanelAccessButton, AdminHomePanel, "CommunicationsPanelAccessButton", "Communications Panel", class="num">10, class="num">70, class="num">250, class="num">110) &&
          CreateButton(AnalyticsPanelAccessButton, AdminHomePanel, "AnalyticsPanelAccessButton", "Analytics Panel", class="num">10, class="num">120, class="num">250, class="num">160) &&
          CreateButton(ShowAllButton, AdminHomePanel, "ShowAllButton", "Show All 💥", class="num">10, class="num">170, class="num">250, class="num">210);
}
class="type">bool InitializeTradeManagementPanel() {
   if (!TradeManagementPanel.Create(ChartID(), "Trade Management Panel", class="num">0, class="num">500, class="num">30, class="num">1280, class="num">170)) {
      Print("Failed to create Trade Management Panel.");
      class="kw">return class="kw">false;
   }
   
         CreateButton(HomeButtonTrade, TradeManagementPanel, "HomeButtonTrade", "Home 🏠", class="num">20, class="num">10, class="num">120, class="num">30) &&
         CreateButton(BuyButton, TradeManagementPanel, "BuyButton", "Buy", class="num">130, class="num">5, class="num">210, class="num">40) &&
         CreateButton(SellButton, TradeManagementPanel, "SellButton", "Sell", class="num">220, class="num">5, class="num">320, class="num">40) &&

「交易面板按钮与双因子验证窗体的初始化落点」

上面这段连续调用的 CreateButton,把平仓与删单的控制入口一次性铺到了 TradeManagementPanel 上。注意坐标参数:CloseAllButton 占 x=130~230、y=50~70,DeleteAllOrdersButton 则从 x=40 起,两者水平错开 90 像素,按钮高度统一 20,说明面板按 50 像素步进横向排布,避免 MT5 图表对象重叠遮挡。 双因子验证窗体走的是另一条路:TwoFactorAuthDialog 以 ChartID() 为父容器,在图表坐标 (100,100) 处生成 500×300 的对话框。其内部输入框 TwoFactorAuthCodeInput 宽 260、高 95,反馈标签 TwoFactorAuthFeedbackLabel 延伸到 y=140~160,留给错误提示的空间只有 20 像素高,实盘里验证码输错时文字可能被裁切,建议把 160 改成 180 再编译。 外汇与贵金属杠杆高,这类一键平仓面板若被误触,可能瞬间清空对冲仓位。把 Delete Stops 和 Close Losing 分开布局,就是给手动确认留缓冲——上机前先改两处坐标验证渲染,比盲信面板安全。

MQL5 / C++
CreateButton(CloseAllButton, TradeManagementPanel, "CloseAllButton", "Close All", class="num">130, class="num">50, class="num">230, class="num">70) &&
CreateButton(CloseProfitButton, TradeManagementPanel, "CloseProfitButton", "Close Profitable", class="num">240, class="num">50, class="num">380, class="num">70) &&
CreateButton(CloseLossButton, TradeManagementPanel, "CloseLossButton", "Close Losing", class="num">390, class="num">50, class="num">510, class="num">70) &&
CreateButton(CloseBuyButton, TradeManagementPanel, "CloseBuyButton", "Close Buys", class="num">520, class="num">50, class="num">620, class="num">70) &&
CreateButton(CloseSellButton, TradeManagementPanel, "CloseSellButton", "Close Sells", class="num">630, class="num">50, class="num">730, class="num">70) &&
CreateButton(DeleteAllOrdersButton, TradeManagementPanel, "DeleteAllOrdersButton", "Delete All Orders", class="num">40, class="num">50, class="num">180, class="num">70) &&
CreateButton(DeleteLimitOrdersButton, TradeManagementPanel, "DeleteLimitOrdersButton", "Delete Limits", class="num">190, class="num">50, class="num">300, class="num">70) &&
CreateButton(DeleteStopOrdersButton, TradeManagementPanel, "DeleteStopOrdersButton", "Delete Stops", class="num">310, class="num">50, class="num">435, class="num">70) &&
CreateButton(DeleteStopLimitOrdersButton, TradeManagementPanel, "DeleteStopLimitOrdersButton", "Delete Stop Limits", class="num">440, class="num">50, class="num">580, class="num">70);
class="kw">return true;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Two-Factor Authentication Dialog                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool InitializeTwoFactorAuthDialog()
{
   if(!TwoFactorAuthDialog.Create(ChartID(), "Two-Factor Authentication", class="num">0, class="num">100, class="num">100, class="num">500, class="num">300))
      class="kw">return class="kw">false;
   if(!TwoFactorAuthCodeInput.Create(ChartID(), "TwoFACodeInput", class="num">0, class="num">20, class="num">70, class="num">260, class="num">95) ||
      !TwoFactorAuthPromptLabel.Create(ChartID(), "TwoFAPromptLabel", class="num">0, class="num">20, class="num">20, class="num">380, class="num">40) ||
      !TwoFactorAuthFeedbackLabel.Create(ChartID(), "TwoFAFeedbackLabel", class="num">0, class="num">20, class="num">140, class="num">380, class="num">160) ||
      !TwoFactorAuthLoginButton.Create(ChartID(), "TwoFALoginButton", class="num">0, class="num">20, class="num">120, class="num">100, class="num">140) ||
      !CloseTwoFactorAuthButton.Create(ChartID(), "Close2FAButton", class="num">0, class="num">120, class="num">120, class="num">200, class="num">140))
   {
      class="kw">return class="kw">false;
   }

常见问题

按功能切分:面板对象、交易初始化、平仓筛选、弹窗各自独立成文件或类,v1.25 相比 v1.24 就是把耦合逻辑拆到独立模块,便于维护。
先建面板对象容器,再在初始化阶段只做交易环境校验与句柄绑定,不把下单逻辑写进构造里,避免启动卡死。
可以,小布能读取你的面板结构描述并标出耦合点,提示哪些模块该拆开,省去手动梳理的时间。
登录弹窗单独做窗体类,平仓筛选只调用其返回状态;两者通过事件回调通信,不要直接互嵌代码。
按钮初始化放在面板加载末尾,双因子窗体仅在登录事件触发时构造,二者生命周期分开,避免重复实例化。