快捷手动交易工具箱:基本功能·进阶篇
(2/3)·从概念到项目文件,拆解手动交易加速工具的核心模块与热键映射
EA 骨架里的计时与事件钩子
这段 MQL5 代码给出的是一个快速交易类 EA 的最小生命周期骨架:用 MagicNumber=1111 隔离订单标识,OnInit 里记下 GetTickCount() 起点,OnChartEvent 在 GUI 构建完成后打印耗时,方便你量化界面初始化到底吃了多少毫秒。 OnTick 目前是空函数,说明报价驱动的逻辑还没接;真正的业务被委托给 CFastTrading 实例的 OnTimerEvent / ChartEvent 等方法,这种写法把底层事件和策略层解耦,改面板不用动主循环。 想验证就直接把代码贴进 MT5 的 EA 模板,编译后拖到图表,日志里出现 'End in XXX ms' 就说明自定义 GUI 事件链通了;外汇与贵金属杠杆高,这类计时只测性能、不预示任何收益。
class="kw">input class="type">class="kw">ulong MagicNumber = class="num">1111; class=class="str">"cmt">// 魔幻数字 class=class="str">"cmt">//--- CFastTrading program; class="type">class="kw">ulong tick_counter; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 智能系统初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- tick_counter=GetTickCount(); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 智能系统逆初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { program.OnDeinitEvent(reason); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 智能系统即时报价函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 计时器函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTimer(class="type">void) { program.OnTimerEvent(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 图表事件函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnChartEvent(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam) { program.ChartEvent(id,lparam,dparam,sparam); class=class="str">"cmt">//--- if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { Print("End in ",GetTickCount()-tick_counter," ms"); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">private: class=class="str">"cmt">//--- class="type">color m_caption_color; class="type">color m_background_color; class=class="str">"cmt">//--- class="type">int m_base_font_size; class="type">int m_m_edit_index; class="type">int m_p_edit_index; class=class="str">"cmt">//---
「面板外观与标识的 setter 接口」
在 MT5 自建一键交易面板时,CFastTrading 类用一组私有成员保存界面状态:m_magic_number 标记订单魔幻数,m_base_font 记录基础字体名,m_language 控制界面语言。对外暴露的 setter 方法都很薄,只做成员变量赋值,不触发重绘。 下面这段声明列出了可调控的接口:标题色、背景色、字号、字体名、语言、魔幻数,各自对应一个 void 返回的方法。 [CODE] ulong m_magic_number; string m_base_font; LANG m_language; void CaptionColor(const color clr); void BackgroundColor(const color clr); void FontSize(const int font_size); void FontName(const string font_name); void SetLanguage(const LANG lang); void SetMagicNumber(ulong magic_number); [/CODE] 实现层更直白——以 CaptionColor 为例,函数体只有一行 m_caption_color=clr;,把传入颜色写进标题色成员。BackgroundColor、FontSize、FontName、SetLanguage 同理,均为单赋值语句。 [CODE] void CFastTrading::CaptionColor(const color clr) { m_caption_color=clr; } void CFastTrading::BackgroundColor(const color clr) { m_background_color=clr; } void CFastTrading::FontSize(const int font_size) { m_base_font_size=font_size; } void CFastTrading::FontName(const string font_name) { m_base_font=font_name; } void CFastTrading::SetLanguage(const LANG lang) { m_language=lang; } [/CODE] 调面板参数时记住:改了 FontSize(14) 不会立刻生效于已绘制的 OBJ_LABEL,通常要在调用 setter 后手动触发 Redraw() 或重建控件。外汇与贵金属波动剧烈、杠杆风险高,面板只是下单辅助,参数乱改可能导致视觉错位或下单失败。
class="type">class="kw">ulong m_magic_number; class="type">class="kw">string m_base_font; LANG m_language; class="type">void CaptionColor(class="kw">const class="type">color clr); class="type">void BackgroundColor(class="kw">const class="type">color clr); class="type">void FontSize(class="kw">const class="type">int font_size); class="type">void FontName(class="kw">const class="type">class="kw">string font_name); class="type">void SetLanguage(class="kw">const LANG lang); class="type">void SetMagicNumber(class="type">class="kw">ulong magic_number); class="type">void CFastTrading::CaptionColor(class="kw">const class="type">color clr) { m_caption_color=clr; } class="type">void CFastTrading::BackgroundColor(class="kw">const class="type">color clr) { m_background_color=clr; } class="type">void CFastTrading::FontSize(class="kw">const class="type">int font_size) { m_base_font_size=font_size; } class="type">void CFastTrading::FontName(class="kw">const class="type">class="kw">string font_name) { m_base_font=font_name; } class="type">void CFastTrading::SetLanguage(class="kw">const LANG lang) { m_language=lang; }
◍ EA 初始化时把魔数塞进交易面板类
在 MT5 的 EA 框架里,OnInit 是图表加载时第一个跑的函数,用来把全局输入参数绑定到自定义界面类。上面这段把 MagicNumber 通过 program.SetMagicNumber() 写进 CFastTrading 实例,等于给后续所有下单请求打上身份标签,避免和手动单或其他 EA 的订单混淆。 注意两个版本的 OnInit 差异:第一段只做了类变量赋值就返回 INIT_SUCCEEDED,第二段补了 program.CreateGUI() 的判错,如果图形接口创建失败直接返回 INIT_FAILED 并 Print 报错。实盘加载 EA 若面板不出来,优先查这段返回码而不是去翻行情逻辑。 CreateMainWindow 里把主窗体写死成 400×182 像素、标题栏高 22、左上角坐标 (5,20),字体走 m_base_font。这种硬编码尺寸在 4K 屏或字体放大设置下可能挤版,想自适应可以改成读 Inp_BaseFont 动态算 XSize。外汇与贵金属杠杆高,EA 加载异常时切勿盲目手补单,先确认魔数隔离是否生效。
class="type">void CFastTrading::SetMagicNumber(class="kw">const class="type">class="kw">ulong magic_number) { m_magic_number=magic_number; } class="type">int OnInit() { class=class="str">"cmt">//--- tick_counter=GetTickCount(); class=class="str">"cmt">//--- 初始化类变量 program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class="type">int OnInit() { class=class="str">"cmt">//--- tick_counter=GetTickCount(); class=class="str">"cmt">//--- 初始化类变量 program.FontName("Trebuchet MS"); program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); class=class="str">"cmt">//--- 设置交易面板 if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class="type">bool CFastTrading::CreateMainWindow(class="type">void) { class=class="str">"cmt">//--- 向窗口数组添加窗口指针 CWndContainer::AddWindow(m_main_window); class=class="str">"cmt">//--- 属性 m_main_window.XSize(class="num">400); m_main_window.YSize(class="num">182); class=class="str">"cmt">//--- 坐标 class="type">int x=class="num">5; class="type">int y=class="num">20; m_main_window.CaptionHeight(class="num">22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); class=class="str">"cmt">//--- 创建窗体 if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- class="kw">return(true); }
一键交易面板的 GUI 组装逻辑
做 MT5 快速下单 EA 时,图形界面最好拆成「主窗体 + 按钮」两层来建,否则后期加功能会乱。下面这段 C++ 风格的类方法就是典型实现:先建主窗口,再把按钮逐个挂上去。 CreateGUI() 只干两件事——调 CreateMainWindow() 生成 400×182 像素的应用窗口,成功就通知事件系统界面就绪。主窗口左上角锚在 x=5、y=20,标题栏高 22,可拖动,配色走成员变量里的 m_caption_color 和 m_background_color。 CreateButton() 给每个交易按钮统一规格:宽 170、高 40,白字居中,悬停和按下时背景不变色,避免视觉闪动。它接收 x_gap / y_gap 做相对偏移,最终用 window.CaptionHeight()+y_gap 算纵向坐标,再把指针存进元素数组 w_number 槽位,方便后面按编号抓按钮发订单。 宏里 CAPTION_NAME 按 m_language 切俄文「Быстрый трейдинг」或英文「Fast Trading」,说明这套界面原生支持双语切换。外汇与贵金属波动剧烈、杠杆风险高,这类面板只解决下单速度,不替代风控。
class="type">bool CFastTrading::CreateGUI(class="type">void) { class=class="str">"cmt">//--- 创建著应用程序窗口 if(!CreateMainWindow()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- GUI 创建完成 CWndEvents::CompletedGUI(); class="kw">return(true); } class="macro">#include "Program.mqh" class="macro">#define CAPTION_NAME(m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading") class=class="str">"cmt">//--- 按钮 class="type">bool CreateButton(CWindow &window,CButton &button,class="type">class="kw">string text,class="type">color baseclr,class="type">int x_gap,class="type">int y_gap,class="type">int w_number); class="type">bool CFastTrading::CreateButton(CWindow &window,CButton &button,class="type">class="kw">string text,class="type">color baseclr,class="type">int x_gap,class="type">int y_gap,class="type">int w_number) { class=class="str">"cmt">//--- 存储窗口指针 button.MainPointer(window); class=class="str">"cmt">//--- 创建之前设置属性 button.XSize(class="num">170); button.YSize(class="num">40); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); class=class="str">"cmt">//--- 创建控制元素 if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 向数据库添加指向元素的指针 CWndContainer::AddToElementsArray(w_number,button); class="kw">return(true); } class="type">bool CFastTrading::CreateMainWindow(class="type">void) { class=class="str">"cmt">//--- 向窗口数组添加窗口指针 CWndContainer::AddWindow(m_main_window); class=class="str">"cmt">//--- 属性 m_main_window.XSize(class="num">400); m_main_window.YSize(class="num">182); class=class="str">"cmt">//--- 坐标 class="type">int x=class="num">5; class="type">int y=class="num">20; m_main_window.CaptionHeight(class="num">22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); m_main_window.TooltipsButtonIsUsed(true); class=class="str">"cmt">//--- 创建窗体
「一键交易面板的按钮布局与多语宏」
在 MT5 自定义面板里,主窗口创建失败会直接返回 false,后续所有按钮也就不会挂载。下面这段初始化逻辑一口气挂了 5 个按钮:市价单(M)、挂单(P)、平盈利仓(C)、平亏损仓(D)、清挂单(R),坐标和配色都写死在调用参数里。 按钮 0 和 1 横向分居左右:M 按钮蓝色 C'87,128,255' 放在 x=20、y=10,P 按钮绿色 C'31,209,111' 放在 x=210、y=10;C 和 D 按钮落在第二行 y=60 / y=110,R 按钮则对齐 P 的 x=210、y=60。想改键位,直接动这几个数值即可,无需碰绘制底层。 语言宏用三元表达式切换俄文 / 英文标签,例如 MARKET_ORDER_NAME 在英语下显示 "Marker Order"(原文拼写如此,非笔误)。面板标题 CAPTION_NAME 对应 "Fast Trading System"。若你的终端跑俄文版,按钮文字会自动变俄文,但坐标逻辑不变。 市价单子窗口单独建类方法 CreateMarketOrdersWindow,尺寸固定为宽 450、高 242+58=300 像素,并把自己加进窗口容器 CWndContainer。外汇与贵金属波动剧烈,这类一键平仓按钮若误触,可能瞬间了结带杠杆的头寸,实盘前务必在策略测试器里用模拟账户验证布局与逻辑。
if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- if(!CreateButton(m_main_window,m_order_button[class="num">0],MARKET_ORDER_NAME+"(M)",C&class="macro">#x27;class="num">87,class="num">128,class="num">255&class="macro">#x27;,class="num">20,class="num">10,class="num">0)) class="kw">return(class="kw">false); if(!CreateButton(m_main_window,m_order_button[class="num">1],PENDING_ORDER_NAME+"(P)",C&class="macro">#x27;class="num">31,class="num">209,class="num">111&class="macro">#x27;,class="num">210,class="num">10,class="num">0)) class="kw">return(class="kw">false); if(!CreateButton(m_main_window,m_order_button[class="num">2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C&class="macro">#x27;class="num">87,class="num">128,class="num">255&class="macro">#x27;,class="num">20,class="num">60,class="num">0)) class="kw">return(class="kw">false); if(!CreateButton(m_main_window,m_order_button[class="num">3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C&class="macro">#x27;class="num">87,class="num">128,class="num">255&class="macro">#x27;,class="num">20,class="num">110,class="num">0)) class="kw">return(class="kw">false); if(!CreateButton(m_main_window,m_order_button[class="num">4],PEND_ORDERS_ALL_CLOSE+"(R)",C&class="macro">#x27;class="num">31,class="num">209,class="num">111&class="macro">#x27;,class="num">210,class="num">60,class="num">0)) class="kw">return(class="kw">false); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 宏替换 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include "Program.mqh" class="macro">#define CAPTION_NAME(m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading System") class="macro">#define MARKET_ORDER_NAME(m_language==RUSSIAN ? "Рыночный ордер" : "Marker Order") class="macro">#define PENDING_ORDER_NAME(m_language==RUSSIAN ? "Отложенный ордер" : "Pending Order") class="macro">#define MARKET_ORDERS_PROFIT_CLOSE(m_language==RUSSIAN ? "Закрыть все прибыльные" : "Close all profitable") class="macro">#define MARKET_ORDERS_LOSS_CLOSE(m_language==RUSSIAN ? "Закрыть все убыточные" : "Close all losing") class="macro">#define PEND_ORDERS_ALL_CLOSE(m_language==RUSSIAN ? "Закрыть все отложенные" : "Close all pending") class="type">bool CreateMarketOrdersWindow(class="type">void); class="type">bool CreatePendingOrdersWindow(class="type">void); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 市价单创建和编辑窗口 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CFastTrading::CreateMarketOrdersWindow(class="type">void) { class=class="str">"cmt">//--- 向窗口数组添加窗口指针 CWndContainer::AddWindow(m_orders_windows[class="num">0]); class=class="str">"cmt">//--- 属性 m_orders_windows[class="num">0].XSize(class="num">450); m_orders_windows[class="num">0].YSize(class="num">242+class="num">58);
◍ 市价与挂单窗口的实例化落点
两个订单设置窗体在代码里共用同一套坐标基线:x 取市价按钮的 XGap(),y 在按钮 YGap() 基础上加 60 像素,避免与按钮区重叠。市价窗标题色用 C'87,128,255'(蓝),挂单窗用 C'31,209,111'(绿),尺寸则分别是默认与 600×580,视觉上能直接区分两类操作。 窗体属性里 CaptionHeight(22) 和 IsMovable(true) 决定标题栏高度与可拖拽,三种 CaptionColor* 都锁成同一主色,悬停也不变色,减少视觉噪声。BackColor 接 m_background_color、BorderColor 接主色,整体风格由这两个变量统一控。 CreateGUI() 把主窗、市价窗、挂单窗按顺序串起来,任一步返回 false 就整体中断。MT5 里若你改了 CreatePendingOrdersWindow 里的 YSize(580) 为更小值,挂单参数行可能被裁切,建议先按原值编译看布局再动。外汇与贵金属图表加载此类 EA 面板存在滑点及误触下单风险,参数调试请在模拟账户验证。
class=class="str">"cmt">//--- 坐标 class="type">int x=m_order_button[class="num">0].XGap(); class="type">int y=m_order_button[class="num">0].YGap()+class="num">60; class=class="str">"cmt">//--- class="type">color clrmain=C&class="macro">#x27;class="num">87,class="num">128,class="num">255&class="macro">#x27;; class=class="str">"cmt">//--- m_orders_windows[class="num">0].CaptionHeight(class="num">22); m_orders_windows[class="num">0].IsMovable(true); m_orders_windows[class="num">0].CaptionColor(clrmain); m_orders_windows[class="num">0].CaptionColorLocked(clrmain); m_orders_windows[class="num">0].CaptionColorHover(clrmain); m_orders_windows[class="num">0].BackColor(m_background_color); m_orders_windows[class="num">0].BorderColor(clrmain); m_orders_windows[class="num">0].FontSize(m_base_font_size); m_orders_windows[class="num">0].Font(m_base_font); m_orders_windows[class="num">0].WindowType(W_DIALOG); class=class="str">"cmt">//--- 创建窗体 if(!m_orders_windows[class="num">0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) class="kw">return(class="kw">false); class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 挂单创建和编辑窗口 |//+------------------------------------------------------------------+ class="type">bool CFastTrading::CreatePendingOrdersWindow(class="type">void) { class=class="str">"cmt">//--- 向窗口数组添加窗口指针 CWndContainer::AddWindow(m_orders_windows[class="num">1]); class=class="str">"cmt">//--- 属性 m_orders_windows[class="num">1].XSize(class="num">600); m_orders_windows[class="num">1].YSize(class="num">580); class=class="str">"cmt">//--- 坐标 class="type">int x=m_order_button[class="num">0].XGap(); class="type">int y=m_order_button[class="num">0].YGap()+class="num">60; class=class="str">"cmt">//--- class="type">color clrmain=C&class="macro">#x27;class="num">31,class="num">209,class="num">111&class="macro">#x27;; class=class="str">"cmt">//--- m_orders_windows[class="num">1].CaptionHeight(class="num">22); m_orders_windows[class="num">1].IsMovable(true); m_orders_windows[class="num">1].CaptionColor(clrmain); m_orders_windows[class="num">1].CaptionColorLocked(clrmain); m_orders_windows[class="num">1].CaptionColorHover(clrmain); m_orders_windows[class="num">1].BackColor(m_background_color); m_orders_windows[class="num">1].BorderColor(clrmain); m_orders_windows[class="num">1].FontSize(m_base_font_size); m_orders_windows[class="num">1].Font(m_base_font); m_orders_windows[class="num">1].WindowType(W_DIALOG); class=class="str">"cmt">//--- 创建窗体 if(!m_orders_windows[class="num">1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) class="kw">return(class="kw">false); class="kw">return(true); } class="macro">#define CAPTION_M_ORD_NAME(m_language==RUSSIAN ? "Настройка: Рыночный Ордер" : "Setting: Market Order") class="macro">#define CAPTION_P_ORD_NAME(m_language==RUSSIAN ? "Настройка: Отложенный Ордер" : "Setting: Pending Order") class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 创建程序的图形界面 |//+------------------------------------------------------------------+ class="type">bool CFastTrading::CreateGUI(class="type">void) { class=class="str">"cmt">//--- 创建主应用程序窗口 if(!CreateMainWindow()) class="kw">return(class="kw">false); if(!CreateMarketOrdersWindow()) class="kw">return(class="kw">false); if(!CreatePendingOrdersWindow()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- GUI 创建完成 CWndEvents::CompletedGUI(); class="kw">return(true); }