內容
- 概述
- 工具箱的概念
- 基本功能的實現
- 結束語
概述
如今,眾多交易者切換至自動交易系統,這類系統可能需要附加設置,或是能夠完全自動化並準備就緒。 然而,有相當一部分交易者更喜歡以舊有方式進行手動交易。 在各種特定交易情況下制定決策時,他們更期望擁有人類專家的判斷力。 有時,這種狀況會快速發展,因此交易者需要迅速對其做出反應。 而且,某些交易方式(例如,剝頭皮)要求準確入場時機。 在其中,附加工具也許會派上用場:它們可為所需動作提供最快可能實施,諸如入場或離場。 因此,我決定實現基本功能,從而滿足快速手動交易需求。
工具箱的概念
首先,有必要確定手動交易中可能需要的一組基本操作。 有基於此,我們將開發一種工具,能夠高效、快捷地執行相應的操作。 手動交易已有一套底層交易系統,這意味着以下入場入方法之一:使用市價或掛單。 因此,該工具箱的主要準則是運用這兩種類型訂單的能力。 我們還可以為交易者選擇在交易過程中可以執行的任務 — 工具可有助於減少執行這些任務時所需的時間和數量。
圖例 1 工具箱主窗口
圖例 1 顯示兩個類別:市價單和掛單。 我還選擇了三個基本任務,這些任務有時需要快速執行,但無法在一個動作里一次性完成。 許多應用程序,包括 MetaTrader 5 終端,都有一組基本的熱鍵,可快速執行某些命令或操作。 我們的應用程序理應考慮這一事實。 每個熱鍵顯示在括號中 — 按下時,將執行指定的操作,或者 — 在處理訂單時,將打開一個相應的窗口。 它也可以用鼠標執行操作。 例如,可以通過按 C 鍵或單擊 “Close all profitable” 來把所有獲利訂單平倉。
按下 M 鍵,或單擊 “Market order” 將打開 “Settings: Market order” 窗口。 該窗口包含入場買入或賣出訂單的輸入數據選項。
圖例 2 配置和創建市價訂單的窗口。
除了基本設置外,與終端中的可用設置類似,不僅可選擇手數作為數值,還可以選擇賬戶餘額的百分比。 這也涉及止盈和止損:不僅能以價位格式,還可以點數表示。 還可以通過兩種方式執行“買入”和“賣出”操作:單擊相應的按鈕,或按下括號中指定的熱鍵。
按下 P 打開掛單設置窗口。 支持四種掛單類型。
圖例 3 配置和創建掛單的窗口
與市價訂單類似,掛單手數支持數值或餘額百分比的形式,還可以選擇價格或點數的止盈和止損。
工具實現
首先,我們來創建一個初始項目結構。 打開 Experts 目錄,創建 “Simple Trading” 文件夾及一些文件,如圖例 4 所示。
圖例 4 項目文件結構
我們研究一下所創建文件的作用:
- SimpleTrading.mq5 — 智能交易系統的文件,將在其中創建 GUI,並包含初始應用程序設置。
- Program.mqh — 包含要連接到 EA 的文件,該文件將包含 CFastTrading 類,及其字段和方法。 它還包含其部分實現。
- MainWindow.mqh — 連接的包含文件 Program.mqh ,內含 GUI 元素的實現方法。
- Defines.mqh — 連接的包含文件 Program.mqh ;內含一組 GUI 元素的宏替換,用於實現英語和俄語版本。
首先,打開 Program.mqh ,連接實現界面和交易功能所需的函數庫,並創建 CFastTrading 類。
//+------------------------------------------------------------------+ //| Program.mqh | //| Alex2356 | //| https://www.mql5.com/zh/users/alex2356/seller | //+------------------------------------------------------------------+ #include <EasyAndFastGUI\WndEvents.mqh> #include <DoEasy25\Engine.mqh> #include "Defines.mqh" //+------------------------------------------------------------------+ //| 切換界面語言的枚舉 | //+------------------------------------------------------------------+ enum LANG { RUSSIAN, // 俄語 ENGLISH // 英語 }; //+------------------------------------------------------------------+ //| 創建應用程序的類 | //+------------------------------------------------------------------+ class CFastTrading : public CWndEvents { public: CFastTrading(void); ~CFastTrading(void); //--- 初始化/逆初始化 void OnInitEvent(void); void OnDeinitEvent(const int reason); //--- 計時器 void OnTimerEvent(void); //--- 圖表事件應答 virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); }; //+------------------------------------------------------------------+ //| 加入 GUI 元素 | //+------------------------------------------------------------------+ #include "MainWindow.mqh" //+------------------------------------------------------------------+ //| 構造函數 | //+------------------------------------------------------------------+ CFastTrading::CFastTrading(void) { } //+------------------------------------------------------------------+ //| 析構函數 | //+------------------------------------------------------------------+ CFastTrading::~CFastTrading(void) { } //+------------------------------------------------------------------+ //| 初始化 | //+------------------------------------------------------------------+ void CFastTrading::OnInitEvent(void) { } //+------------------------------------------------------------------+ //| 逆初始化 | //+------------------------------------------------------------------+ void CFastTrading::OnDeinitEvent(const int reason) { //--- Remove the interface CWndEvents::Destroy(); } //+------------------------------------------------------------------+ //| 計時器 | //+------------------------------------------------------------------+ void CFastTrading::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- } //+------------------------------------------------------------------+ //| 圖表事件應答 | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { } //+------------------------------------------------------------------+
現在,打開 EA 文件 SimpleTrading.mq5 ,把它連接到 Program.mqh ,若有新創建的類,則創建一個實例< /s3>。 此外,設置輸入參數,其中包括:
- Base FontSize — 應用程序的基本字體大小。
- Caption Color — 主應用程序窗口的標題顏色。
- Back color — 背景顏色。
- Interface language — 界面語言。
- Magic Number — 本智能交易系統創建訂單時的唯一識別碼。
//+------------------------------------------------------------------+ //| SimpleTrading.mq5 | //| Alex2356 | //| https://www.mql5.com/zh/users/alex2356/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, Alexander Fedosov" #property link "https://www.mql5.com/zh/users/alex2356" #property version "1.00" //--- Include application class #include "Program.mqh" //+------------------------------------------------------------------+ //| 智能交易系統輸入參數 | //+------------------------------------------------------------------+ input int Inp_BaseFont = 10; // 基本字號 input color Caption = C'0,130,225'; // 標題顏色 input color Background = clrWhite; // 背景顏色 input LANG Language = ENGLISH; // 界面語言 input ulong MagicNumber = 1111; // 魔幻數字 //--- CFastTrading program; ulong tick_counter; //+------------------------------------------------------------------+ //| 智能系統初始化函數 | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 智能系統逆初始化函數 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { program.OnDeinitEvent(reason); } //+------------------------------------------------------------------+ //| 智能系統即時報價函數 | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| 計時器函數 | //+------------------------------------------------------------------+ void OnTimer(void) { program.OnTimerEvent(); } //+------------------------------------------------------------------+ //| 圖表事件函數 | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { program.ChartEvent(id,lparam,dparam,sparam); //--- if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { Print("End in ",GetTickCount()-tick_counter," ms"); } } //+------------------------------------------------------------------+
為了令 EA 輸入參數可用於此類,必須創建變量和方法,將 EA 設置的數值分配給這些變量,以及實現的方法。 在類的私密部分中創建變量。
private: //--- color m_caption_color; color m_background_color; //--- int m_base_font_size; int m_m_edit_index; int m_p_edit_index; //--- 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);
此處是實現:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetMagicNumber(const ulong magic_number) { m_magic_number=magic_number; }
現在,於 EA 初始化部分應用它們:
//+------------------------------------------------------------------+ //| 智能系統初始化函數 | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- 初始化類變量 program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); }
添加 CreateGUI() 方法,該方法將創建整個界面。 到目前為止,它是空的。 當我們創建 UI 元素時,會進一步填充它。
//--- Create the graphical interface of the program bool CreateGUI(void);
該方法可以添加到 EA 初始化中:
//+------------------------------------------------------------------+ //| 智能系統初始化函數 | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- 初始化類變量 program.FontName("Trebuchet MS"); program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- 設置交易面板 if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); }
現在,創建主應用程序窗口。 這可在類的受保護部分里添加 CreateMainWindow() 方法來完成。
protected: //--- forms bool CreateMainWindow(void);
將其實現添加到 MainWindow.mqh 文件中,然後在 CreateGUI() 中調用它。 在此方法實現中,我使用了 CAPTION_NAME 宏替換,這就是為什麼應在特殊的 Defines.mqh 文件中創建它的原因。
//+------------------------------------------------------------------+ //| 創建訂單窗體 | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- 向窗口數組添加窗口指針 CWndContainer::AddWindow(m_main_window); //--- 屬性 m_main_window.XSize(400); m_main_window.YSize(182); //--- 坐標 int x=5; int y=20; m_main_window.CaptionHeight(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); //--- 創建窗體 if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| 創建程序的圖形界面 | //+------------------------------------------------------------------+ bool CFastTrading::CreateGUI(void) { //--- 創建著應用程序窗口 if(!CreateMainWindow()) return(false); //--- GUI 創建完成 CWndEvents::CompletedGUI(); return(true); }
現在,創建一個新的宏替換。
//+------------------------------------------------------------------+ //| 宏替換 | //+------------------------------------------------------------------+ #include "Program.mqh" #define CAPTION_NAME (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading")
編譯項目,從而得到主應用程序窗口。 現在,我們添加按鈕(圖例 1),它將執行所述操作。 為了實現它們,請在類的受保護部分中創建通用的 CreateButton() 方法。
//--- 按鈕 bool CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number);
在 MainWindow.mqh 中實現它,並於主窗口創建方法的主體中調用。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number) { //--- 存儲窗口指針 button.MainPointer(window); //--- 創建之前設置屬性 button.XSize(170); button.YSize(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); //--- 創建控制元素 if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- 向數據庫添加指向元素的指針 CWndContainer::AddToElementsArray(w_number,button); return(true); } //+------------------------------------------------------------------+ //| 創建訂單窗體 | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- 向窗口數組添加窗口指針 CWndContainer::AddWindow(m_main_window); //--- 屬性 m_main_window.XSize(400); m_main_window.YSize(182); //--- 坐標 int x=5; int y=20; m_main_window.CaptionHeight(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); //--- 創建窗體 if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- if(!CreateButton(m_main_window,m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10,0)) return(false); if(!CreateButton(m_main_window,m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10,0)) return(false); if(!CreateButton(m_main_window,m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60,0)) return(false); if(!CreateButton(m_main_window,m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110,0)) return(false); if(!CreateButton(m_main_window,m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60,0)) return(false); return(true); }
此處也用宏替換,這就是為什麼要將其添加到 Defines.mqh 當中。
//+------------------------------------------------------------------+ //| 宏替換 | //+------------------------------------------------------------------+ #include "Program.mqh" #define CAPTION_NAME (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading System") #define MARKET_ORDER_NAME (m_language==RUSSIAN ? "Рыночный ордер" : "Marker Order") #define PENDING_ORDER_NAME (m_language==RUSSIAN ? "Отложенный ордер" : "Pending Order") #define MARKET_ORDERS_PROFIT_CLOSE (m_language==RUSSIAN ? "Закрыть все прибыльные" : "Close all profitable") #define MARKET_ORDERS_LOSS_CLOSE (m_language==RUSSIAN ? "Закрыть все убыточные" : "Close all losing") #define PEND_ORDERS_ALL_CLOSE (m_language==RUSSIAN ? "Закрыть все отложенные" : "Close all pending")
這就是選擇英文版本時我們的所得。
圖例 5 主應用程序窗口。
如前所述,該應用程序還應有兩個窗口來處理市價單和掛單。 因此,創建它們並鏈接到 “Market Order” 和 “Pending Order” 按鈕:相關操作可單擊按鈕,或按熱鍵 M 和 P 來執行。 因此,在類的受保護部分中添加 CreateMarketOrdersWindow() 和 CreatePendingOrdersWindow() 這兩個方法,並在 MainWindow.mqh 中實現它們。
bool CreateMarketOrdersWindow(void); bool CreatePendingOrdersWindow(void); //+------------------------------------------------------------------+ //| 市價單創建和編輯窗口 | //+------------------------------------------------------------------+ bool CFastTrading::CreateMarketOrdersWindow(void) { //--- 向窗口數組添加窗口指針 CWndContainer::AddWindow(m_orders_windows[0]); //--- 屬性 m_orders_windows[0].XSize(450); m_orders_windows[0].YSize(242+58); //--- 坐標 int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'87,128,255'; //--- m_orders_windows[0].CaptionHeight(22); m_orders_windows[0].IsMovable(true); m_orders_windows[0].CaptionColor(clrmain); m_orders_windows[0].CaptionColorLocked(clrmain); m_orders_windows[0].CaptionColorHover(clrmain); m_orders_windows[0].BackColor(m_background_color); m_orders_windows[0].BorderColor(clrmain); m_orders_windows[0].FontSize(m_base_font_size); m_orders_windows[0].Font(m_base_font); m_orders_windows[0].WindowType(W_DIALOG); //--- 創建窗體 if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); return(true); } //+------------------------------------------------------------------+ //| 掛單創建和編輯窗口 | //+------------------------------------------------------------------+ bool CFastTrading::CreatePendingOrdersWindow(void) { //--- 向窗口數組添加窗口指針 CWndContainer::AddWindow(m_orders_windows[1]); //--- 屬性 m_orders_windows[1].XSize(600); m_orders_windows[1].YSize(580); //--- 坐標 int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'31,209,111'; //--- m_orders_windows[1].CaptionHeight(22); m_orders_windows[1].IsMovable(true); m_orders_windows[1].CaptionColor(clrmain); m_orders_windows[1].CaptionColorLocked(clrmain); m_orders_windows[1].CaptionColorHover(clrmain); m_orders_windows[1].BackColor(m_background_color); m_orders_windows[1].BorderColor(clrmain); m_orders_windows[1].FontSize(m_base_font_size); m_orders_windows[1].Font(m_base_font); m_orders_windows[1].WindowType(W_DIALOG); //--- 創建窗體 if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) return(false); return(true); }
在這兩個窗口中要用到宏替換,因此把它們添加到相應的文件之中:
#define CAPTION_M_ORD_NAME (m_language==RUSSIAN ? "Настройка: Рыночный Ордер" : "Setting: Market Order") #define CAPTION_P_ORD_NAME (m_language==RUSSIAN ? "Настройка: Отложенный Ордер" : "Setting: Pending Order")
現在,在 CreateGUI() 基本方法中調用新創建的窗口,該方法會創建應用程序界面:
//+------------------------------------------------------------------+ //| 創建程序的圖形界面 | //+------------------------------------------------------------------+ bool CFastTrading::CreateGUI(void) { //--- 創建主應用程序窗口 if(!CreateMainWindow()) return(false); if(!CreateMarketOrdersWindow()) return(false); if(!CreatePendingOrdersWindow()) return(false); //--- GUI 創建完成 CWndEvents::CompletedGUI(); return(true); }
由於這些已創建窗口是對話框,因此它們將在應用程序啟動時顯示。 我們需要創建一種機制來顯示它們。 單擊 "Market order / Pending order" 按鈕,或按下熱鍵,即會打開它們。 在基類中找到 OnEvent() 方法主體,並編寫所需條件。
//+------------------------------------------------------------------+ //| 圖表事件應答 | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- 按下按鈕事件 if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- if(lparam==m_order_button[0].Id()) m_orders_windows[0].OpenWindow(); //--- if(lparam==m_order_button[1].Id()) m_orders_windows[1].OpenWindow(); } //--- 按鍵事件 if(id==CHARTEVENT_KEYDOWN) { //--- 打市價單窗口 if(lparam==KEY_M) { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } else { if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } //--- m_orders_windows[0].OpenWindow(); } } //--- 開掛單窗口 if(lparam==KEY_P) { if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } else { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } //--- m_orders_windows[1].OpenWindow(); } } } }
現在,編譯項目,並嘗試用熱鍵打開對話框。 結果應如圖例 6 所示:
圖例 6 用熱鍵打開和切換窗口。
如您所見,在打開另一個對話框之前,無需關閉對話框。 它只是簡單地切換到另一個窗口 — 這能夠節省時間,且在市價單和掛單之間只需一鍵即可切換。 另一個方便的小技巧是使用 Esc 鍵關閉對話框的功能。 在測試如何打開窗口時,我要按好幾次鍵來關閉窗口。 因此,我們將代碼添加到 “Event” 部分:
//--- 已存在訂單窗口 if(lparam==KEY_ESC) { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } else if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } }
現在,我們繼續處理所創建的窗口,從“Market Orders” 窗口開始。 根據圖例 2,我們需要創建兩個訂單管理模塊 - 買入和賣出。 首先,我們用新的 CreateFrame() 方法創建兩個界面元素 - 框架。
bool CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number);
其實現如下:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number) { //--- 存儲指向主控件的指針 frame.MainPointer(window); //--- color clrmain=clrNONE; if(caption==BUY_ORDER) clrmain=C'88,212,210'; else if(caption==SELL_ORDER) clrmain=C'236,85,79'; //--- frame.YSize(110); frame.LabelColor(clrmain); frame.BorderColor(clrmain); frame.BackColor(m_background_color); frame.GetTextLabelPointer().BackColor(m_background_color); frame.Font(m_base_font); frame.FontSize(m_base_font_size); frame.AutoXResizeMode(true); frame.AutoXResizeRightOffset(10); //--- 創建一個控制元素 if(!frame.CreateFrame(caption,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- 將對象添加到對象組的通用數組 CWndContainer::AddToElementsArray(w_number,frame); return(true); }
在框架創建實現中,我們有兩個新的框架標頭宏替換,故將它們添加到 Defines.mqh 之中:
#define BUY_ORDER (m_language==RUSSIAN ? "Buy-ордер" : "Buy-order") #define SELL_ORDER (m_language==RUSSIAN ? "Sell-ордер" : "Sell-order")
若要應用該方法,請轉到 CreateMarketOrdersWindow() 方法的主體末尾,該方法創建市價單,並添加以下內容:
... //--- 創建窗體 if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); //--- 買入模塊 if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- 賣出模塊 if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); return(true); }
檢查結果:
圖例 7 市價單模塊。
圖例 7 中的每個模塊將包含 4 個主要的 UI 元素類別:
- 文字標題。 這些是手數、止盈、止損。
- 切換按鈕。 對於手數,這是保證金的 %。 對於止盈和止損 - 價格或點數模式。
- 輸入字段。 手數、止盈和止損。
- 動作按鈕。 賣出或買入。
我們來逐步實現每個類別。 為文本標題創建通用方法 CreateLabel()。
//+------------------------------------------------------------------+ //| 創建文本標簽 | //+------------------------------------------------------------------+ bool CFastTrading::CreateLabel(CWindow &window,CTextLabel &text_label,const int x_gap,const int y_gap,string label_text,int w_number) { //--- 存儲窗口指針 text_label.MainPointer(window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(80); text_label.BackColor(m_background_color); text_label.IsCenterText(true); //--- 創建標簽 if(!text_label.CreateTextLabel(label_text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- 向數據庫添加一個指向元素的指針 CWndContainer::AddToElementsArray(w_number,text_label); return(true); }
我們早些時候已經補充了市價單創建方法。 現在,在考慮當前實現的情況下添加以下內容。
//--- 買入模塊 if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- 賣出模塊 if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); return(true); }
此處是三個新的宏替代,針對手數、止盈和止損。 添加兩種語言的名稱:
#define LOT (m_language==RUSSIAN ? "Лот" : "Lot") #define TP (m_language==RUSSIAN ? "Тейк профит" : "Take Profit") #define SL (m_language==RUSSIAN ? "Стоп лосс" : "Stop Loss")
下一個類別包括切換按鈕。 為此專門創建了方法 CreateSwitchButton()。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateSwitchButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- 存儲窗口指針 button.MainPointer(window); color baseclr=clrSlateBlue; color pressclr=clrIndigo; //--- 在創建之前設置屬性 button.XSize(80); button.YSize(24); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); button.TwoState(true); //--- 創建一個控制元素 if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- 向數據庫添加一個指向元素的指針 CWndContainer::AddToElementsArray(w_number,button); return(true); }
在 CreateMarketWindow() 方法當中兩個模塊均可應用:
... //--- 買入模塊 if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- 切換按鈕 for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1)) return(false); //--- 賣出模塊 if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); //--- 切換按鈕 for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1)) return(false); return(true); }
應特別注意下一個類別,它與輸入字段有關的,且因為元素需要有一定的使用限制。 例如,“Lot” 輸入字段的值必須受當前交易品種規格、以及交易賬戶細則的限制。 編輯此字段時,應考慮以下參數:最小和最大手數,最小變動步長。 有基於此,創建 CreateLotEdit() ,並相應地設置其輸入字段:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateLotEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- 存儲指向主控件的指針 text_edit.MainPointer(window); //--- 屬性 text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); text_edit.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); text_edit.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); text_edit.SpinEditMode(true); text_edit.SetDigits(2); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- 創建一個控制元素 if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); //--- 將對象添加到對象組的通用數組 CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
另外,為止損和止盈創建輸入字段。 以上所有內容都應添加到創建市價單的窗口中。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateTakeProfitEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- 存儲指向主控件的指針 text_edit.MainPointer(window); //--- 屬性 text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(1); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- 創建一個控制元素 if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(150)); //--- 將對象添加到對象組的通用數組 CWndContainer::AddToElementsArray(w_number,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateStopLossEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- 存儲指向主控件的指針 text_edit.MainPointer(window); //--- 屬性 text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(1); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- 創建一個控制元素 if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(150)); //--- 將對象添加到對象組的通用數組 CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
在該窗口中,UI 元素的最後一個類別包含兩個按鈕:買入和賣出。 按鈕添加方法為 CreateBuyButton() 和 CreateSellButton()。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateBuyButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- 存儲窗口指針 button.MainPointer(window); color baseclr=C'88,212,210'; //--- 在創建之前設置屬性 button.XSize(120); button.YSize(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); //--- 創建一個控制元素 if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- 向數據庫添加一個指向元素的指針 CWndContainer::AddToElementsArray(w_number,button); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateSellButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- 存儲窗口指針 button.MainPointer(window); color baseclr=C'236,85,79'; //--- 在創建之前設置屬性 button.XSize(120); button.YSize(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); //--- 創建一個控制元素 if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- 向數據庫添加一個指向元素的指針 CWndContainer::AddToElementsArray(w_number,button); return(true); }
添加了兩個按鈕,我們即完成了 CreateMarketWindow() 方法的實現:
//+------------------------------------------------------------------+ //| 市價單創建和編輯窗口 | //+------------------------------------------------------------------+ bool CFastTrading::CreateMarketOrdersWindow(void) { //--- 向窗口數組添加窗口指針 CWndContainer::AddWindow(m_orders_windows[0]); //--- 屬性 m_orders_windows[0].XSize(450); m_orders_windows[0].YSize(242+58); //--- 坐標 int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'87,128,255'; //--- m_orders_windows[0].CaptionHeight(22); m_orders_windows[0].IsMovable(true); m_orders_windows[0].CaptionColor(clrmain); m_orders_windows[0].CaptionColorLocked(clrmain); m_orders_windows[0].CaptionColorHover(clrmain); m_orders_windows[0].BackColor(m_background_color); m_orders_windows[0].BorderColor(clrmain); m_orders_windows[0].FontSize(m_base_font_size); m_orders_windows[0].Font(m_base_font); m_orders_windows[0].WindowType(W_DIALOG); //--- 創建窗體 if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); //--- 買入模塊 if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- 切換按鈕 for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1)) return(false); //--- 編輯 if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[0],20,60+34,1)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[0],20+(80+20),60+34,1)) return(false); if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[0],20+(80+20)*2,60+34,1)) return(false); //--- 買入按鈕 if(!CreateBuyButton(m_orders_windows[0],m_buy_execute,BUY+"(B)",m_orders_windows[0].XSize()-(120+20),103,1)) return(false); //--- 賣出模塊 if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); //--- 切換按鈕 for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1)) return(false); //--- 編輯 if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[1],20,170+30+35,1)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[1],20+80+20,170+30+35,1)) return(false); if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[1],20+(80+20)*2,170+30+35,1)) return(false); //--- 賣出按鈕 if(!CreateSellButton(m_orders_windows[0],m_sell_execute,SELL+"(S)",m_orders_windows[0].XSize()-(120+20),242,1)) return(false); return(true); }
不要忘記添加兩個新的宏替換:
#define BUY (m_language==RUSSIAN ? "Купить" : "Buy") #define SELL (m_language==RUSSIAN ? "Продать" : "Sell")
編譯現階段項目,並檢查結果:
圖例 8 創建市價單窗口的填充界面。
目前,它還僅是一個模板。 為了進一步開發模板,應執行以下任務:
- 激活切換按鈕。
- 根據按鈕狀態更改輸入字段屬性。
- 根據切換模式和輸入數據值實現按市價下單。
在開始實現更改名稱的按鈕切換機制之前,若有必要,需設置其默認值。 圖例 8 現在顯示的按鈕僅帶有破折號。 為了設置名稱,請添加 SetButtonParam() 方法。 稍後將利用相同的方法來切換名稱。
//+------------------------------------------------------------------+ //| 設置按鈕文字 | //+------------------------------------------------------------------+ void CFastTrading::SetButtonParam(CButton &button,string text) { button.LabelText(text); button.Update(true); }
轉到事件應答程序,並在界面創建完成後添加 “Event” 部分。 在此利用 SetButtonParam() 方法設置切換按鈕的名稱。
//--- UI 創建完畢 if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); }
在此,我們還有一個針對按鈕名稱的宏替換,將其添加到 Defines.mqh 當中:
#define POINTS (m_language==RUSSIAN ? "Пункты" : "Points")
創建切換機制的準備已就緒。 ButtonSwitch() 函數將跟蹤按鈕狀態(已按下/未按下),並更改相應按鈕的名稱。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ButtonSwitch(CButton &button,long lparam,string state1,string state2) { if(lparam==button.Id()) { if(!button.IsPressed()) SetButtonParam(button,state1); else SetButtonParam(button,state2); } }
由於名稱是通過按鈕單擊事件切換的,因此在事件應答程序部分中調用創建的方法。
//--- 按鈕事件 if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- if(lparam==m_order_button[0].Id()) m_orders_windows[0].OpenWindow(); //--- if(lparam==m_order_button[1].Id()) m_orders_windows[1].OpenWindow(); //--- ButtonSwitch(m_switch_button[0],lparam,LOT,PERC_DEPO); ButtonSwitch(m_switch_button[1],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[2],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[3],lparam,LOT,PERC_DEPO); ButtonSwitch(m_switch_button[4],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[5],lparam,POINTS,PRICE); }
還要為兩種語言定義新的宏替換:
#define PERC_DEPO (m_language==RUSSIAN ? "% Депозит" : "% Deposit") #define PRICE (m_language==RUSSIAN ? "Цена" : "Price")
再次編譯項目:單擊按鈕後,隨之更改按鈕名稱:
圖例 9 更改切換按鈕的名稱。
我們繼續前進。 下一個任務是根據切換按鈕的相應狀態來更改輸入字段的屬性。 為此,為每個輸入字段添加三個新方法: LotMarketSwitch(),TakeMarketSwitch(),StopMarketSwitch()。 對於第一種方法,手數將被切換為帳戶餘額的百分比。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::LotMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[i*3].Id()) { if(m_switch_button[i*3].IsPressed()) { m_lot_edit[i].SetDigits(0); m_lot_edit[i].StepValue(1); m_lot_edit[i].MaxValue(100); m_lot_edit[i].MinValue(1); m_lot_edit[i].SetValue(string(2)); m_lot_edit[i].GetTextBoxPointer().Update(true); } else { m_lot_edit[i].SetDigits(2); m_lot_edit[i].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); m_lot_edit[i].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); m_lot_edit[i].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); m_lot_edit[i].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); m_lot_edit[i].GetTextBoxPointer().Update(true); } } } }
至於其他兩個方法,級別值將從點數切換為價格。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::TakeMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[3*i+1].Id()) { if(m_switch_button[3*i+1].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_tp_edit[i].SetDigits(_Digits); m_tp_edit[i].StepValue(_Point); m_tp_edit[i].SetValue(string(tick.ask)); m_tp_edit[i].GetTextBoxPointer().Update(true); } } else { m_tp_edit[i].SetDigits(0); m_tp_edit[i].StepValue(1); m_tp_edit[i].SetValue(string(150)); m_tp_edit[i].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::StopMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[3*i+2].Id()) { if(m_switch_button[3*i+2].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_sl_edit[i].SetDigits(_Digits); m_sl_edit[i].StepValue(_Point); m_sl_edit[i].SetValue(string(tick.bid)); m_sl_edit[i].GetTextBoxPointer().Update(true); } } else { m_sl_edit[i].SetDigits(0); m_sl_edit[i].StepValue(1); m_sl_edit[i].SetValue(string(150)); m_sl_edit[i].GetTextBoxPointer().Update(true); } } } }
現在,我們在事件應答程序的“按鈕單擊事件”部分中調用所有三個方法:
// --- 切換手數/餘額百分比 LotMarketSwitch(lparam); //--- 切換止盈點數/價格 TakeMarketSwitch(lparam); //--- 切換止損點數/價格 StopMarketSwitch(lparam);
檢查結果:
圖例 10 在市價單窗口中的切換模式輸入字段。
最後一個任務是使用按鈕或熱鍵,根據設置和輸入字段值來啟用按市價下單。 為此目的,創建三個新方法:
- OnInitTrading() — 定義並設置交易賬戶環境。
- SetBuyOrder() — 開立市價買單。
- SetSellOrder() — 開立市價賣單。
在我們的基類 CFastTrading 的私密部分中創建它們,並在同一個類中實現它們。
//+------------------------------------------------------------------+ //| 交易環境初始化 | //+------------------------------------------------------------------+ void CFastTrading::OnInitTrading() { string array_used_symbols[]; //--- 填寫用到的品種數組 CreateUsedSymbolsArray(SYMBOLS_MODE_CURRENT,"",array_used_symbols); //--- 在品種集合中設置使用到品種列表的類型,並填寫品種時間序列列表 m_trade.SetUsedSymbols(array_used_symbols); //--- 將所有現有集合傳遞給交易類 m_trade.TradingOnInit(); m_trade.TradingSetMagic(m_magic_number); m_trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG); //--- 為所有用到的品種設置同步傳遞訂單 m_trade.TradingSetAsyncMode(false); //--- 為所有交易對象設置正確的訂單到期和填充類型 m_trade.TradingSetCorrectTypeExpiration(); m_trade.TradingSetCorrectTypeFilling(); }
在類構造函數中調用此方法:
//+------------------------------------------------------------------+ //| 構造函數 | //+------------------------------------------------------------------+ CFastTrading::CFastTrading(void) { OnInitTrading(); }
在實現交易功能之前,請注意,它們將在兩個不同的事件上執行。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buy_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_B)) { //--- double lot; if(m_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[0].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[0].GetValue())); if(m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sell_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_S)) { //--- double lot; if(m_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[1].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[1].GetValue())); //--- if(m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } } return(false); }
盡管要在事件應答程序中調用這兩個交易方法,但由於上面出示的原因,會把它們放在標識事件的所有部分之外。 上述函數的當前實現使用 LotPercent() 方法按餘額百分比來計算手數大小。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CFastTrading::LotPercent(string symbol,ENUM_ORDER_TYPE trade_type,double price,double percent) { double margin=0.0; //--- 檢查 if(symbol=="" || price<=0.0 || percent<1 || percent>100) return(0.0); //--- 計算 1 手所需的保證金 if(!OrderCalcMargin(trade_type,symbol,1.0,price,margin) || margin<0.0) return(0.0); //--- if(margin==0.0) return(0.0); //--- 計算最大交易量 double volume=NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE)*percent/100.0/margin,2); //--- 常規化並檢查限制 double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP); if(stepvol>0.0) volume=stepvol*MathFloor(volume/stepvol); //--- double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN); if(volume<minvol) volume=0.0; //--- double maxvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX); if(volume>maxvol) volume=maxvol; //--- 返回交易量 volume return(volume); }
與市價單有關的部分已經準備就緒。 現在,我們進入創建掛單的窗口。 我們按照已完成的市價單的相同步驟創建界面。 大多數步驟是相似的。 我將只會略微深入一點細節。
//+------------------------------------------------------------------+ //| 掛單創建和編輯窗口 | //+------------------------------------------------------------------+ bool CFastTrading::CreatePendingOrdersWindow(void) { //--- 向窗口數組添加窗口指針 CWndContainer::AddWindow(m_orders_windows[1]); //--- 屬性 m_orders_windows[1].XSize(600); m_orders_windows[1].YSize(580); //--- 坐標 int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'31,209,111'; //--- m_orders_windows[1].CaptionHeight(22); m_orders_windows[1].IsMovable(true); m_orders_windows[1].CaptionColor(clrmain); m_orders_windows[1].CaptionColorLocked(clrmain); m_orders_windows[1].CaptionColorHover(clrmain); m_orders_windows[1].BackColor(m_background_color); m_orders_windows[1].BorderColor(clrmain); m_orders_windows[1].FontSize(m_base_font_size); m_orders_windows[1].Font(m_base_font); m_orders_windows[1].WindowType(W_DIALOG); //--- 創建窗體 if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) return(false); //---BUY-STOP 模塊 if(!CreateFrame(m_orders_windows[1],m_frame[2],10,20,BUYSTOP_ORDER,2)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[1],m_p_text_labels[0],20,60,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[1],20+80+20,30,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[2],20+(80+20)*2,30,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[3],20+(80+20)*3,30,SL,2)) return(false); //--- 切換 for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*i,60,2)) return(false); //--- 編輯 if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[0],20,60+35,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[2],20+(80+20),60+35,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[2],20+(80+20)*2,60+35,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[2],20+(80+20)*3,60+35,2)) return(false); //--- Buy Stop 下單按鈕 if(!CreateBuyButton(m_orders_windows[1],m_buystop_execute,"Buy Stop ( 1 )",m_orders_windows[1].XSize()-(120+20),103,2)) return(false); //---SELL-STOP 模塊 if(!CreateFrame(m_orders_windows[1],m_frame[3],10,160,SELLSTOP_ORDER,2)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[1],m_p_text_labels[4],20,170+30,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[5],20+80+20,170,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[6],20+(80+20)*2,170,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[7],20+(80+20)*3,170,SL,2)) return(false); //--- 切換 for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-3),170+30,2)) return(false); //--- 編輯 if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[1],20,170+30+35,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[3],20+(80+20),170+30+35,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[3],20+(80+20)*2,170+30+35,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[3],20+(80+20)*3,170+30+35,2)) return(false); //--- Sell Stop 下單按鈕 if(!CreateSellButton(m_orders_windows[1],m_sellstop_execute,"Sell Stop ( 2 )",m_orders_windows[1].XSize()-(120+20),242,2)) return(false); //---BUY-LIMIT 模塊 if(!CreateFrame(m_orders_windows[1],m_frame[4],10,300,BUYLIMIT_ORDER,2)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[1],m_p_text_labels[8],20,330,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[9],20+80+20,310,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[10],20+(80+20)*2,310,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[11],20+(80+20)*3,310,SL,2)) return(false); //--- 切換 for(int i=6; i<9; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-6),330,2)) return(false); //--- 編輯 if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[2],20,365,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[4],20+(80+20),365,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[4],20+(80+20)*2,365,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[4],20+(80+20)*3,365,2)) return(false); //--- Buy Limit 下單按鈕 if(!CreateBuyButton(m_orders_windows[1],m_buylimit_execute,"Buy Limit ( 3 )",m_orders_windows[1].XSize()-(120+20),382,2)) return(false); //---SELL-LIMIT 模塊 if(!CreateFrame(m_orders_windows[1],m_frame[5],10,440,SELLLIMIT_ORDER,2)) return(false); //--- 標題 if(!CreateLabel(m_orders_windows[1],m_p_text_labels[12],20,470,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[13],20+80+20,450,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[14],20+(80+20)*2,450,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[15],20+(80+20)*3,450,SL,2)) return(false); //--- 切換 for(int i=9; i<12; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-9),470,2)) return(false); //--- 編輯 if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[3],20,505,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[5],20+(80+20),505,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[5],20+(80+20)*2,505,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[5],20+(80+20)*3,505,2)) return(false); //--- Sell Limit 下單按鈕 if(!CreateSellButton(m_orders_windows[1],m_selllimit_execute,"Sell Limit ( 4 )",m_orders_windows[1].XSize()-(120+20),522,2)) return(false); //--- return(true); }
如您所見,上面的代碼用到了與市價單窗口相同的方法。 不過,它也有一個新方法。 CreatePriceEdit() 是掛單下單價格的輸入字段。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreatePriceEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- 存儲指向主控件的指針 text_edit.MainPointer(window); //--- 屬性 text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.StepValue(_Point); text_edit.SpinEditMode(true); text_edit.SetDigits(_Digits); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- 創建一個控制元素 if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); //--- MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) text_edit.SetValue(string(tick.bid)); //--- 將對象添加到對象組的通用數組 CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
方法實現之後,初始模板已準備就緒,但是該界面尚不可用。 還需要配置它。 我們將以相同的順序實現此部分:配置切換按鈕,將其狀態與輸入字段屬性連接,並創建下掛單的函數。
進入 OnEvent() 應答程序,“界面創建完成”部分,並為切換按鈕設置名稱:
//--- UI 創建完畢 if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); //--- SetButtonParam(m_p_switch_button[0],LOT); SetButtonParam(m_p_switch_button[1],POINTS); SetButtonParam(m_p_switch_button[2],POINTS); SetButtonParam(m_p_switch_button[3],LOT); SetButtonParam(m_p_switch_button[4],POINTS); SetButtonParam(m_p_switch_button[5],POINTS); SetButtonParam(m_p_switch_button[6],LOT); SetButtonParam(m_p_switch_button[7],POINTS); SetButtonParam(m_p_switch_button[8],POINTS); SetButtonParam(m_p_switch_button[9],LOT); SetButtonParam(m_p_switch_button[10],POINTS); SetButtonParam(m_p_switch_button[11],POINTS); }
轉到“按鈕單擊事件”部分,並實現切換按鈕狀態時更改其名稱的機制:
//--- ButtonSwitch(m_p_switch_button[0],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[1],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[2],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[3],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[4],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[5],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[6],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[7],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[8],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[9],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[10],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[11],lparam,POINTS,PRICE);
為了將切換按鈕與輸入字段及其屬性鏈接在一起,要創建三個新方法: LotPendingSwitch() ,TakePendingSwitch(),StopPendingSwitch() 。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::LotPendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i].Id()) { if(m_p_switch_button[3*i].IsPressed()) { m_lot_edit[i+2].SetDigits(0); m_lot_edit[i+2].StepValue(1); m_lot_edit[i+2].MaxValue(100); m_lot_edit[i+2].MinValue(1); m_lot_edit[i+2].SetValue(string(2)); m_lot_edit[i+2].GetTextBoxPointer().Update(true); } else { m_lot_edit[i+2].SetDigits(2); m_lot_edit[i+2].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); m_lot_edit[i+2].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); m_lot_edit[i+2].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); m_lot_edit[i+2].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); m_lot_edit[i+2].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::TakePendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i+1].Id()) { if(m_p_switch_button[3*i+1].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_tp_edit[i+2].SetDigits(_Digits); m_tp_edit[i+2].StepValue(_Point); m_tp_edit[i+2].SetValue(string(tick.ask)); m_tp_edit[i+2].GetTextBoxPointer().Update(true); } } else { m_tp_edit[i+2].SetDigits(0); m_tp_edit[i+2].StepValue(1); m_tp_edit[i+2].SetValue(string(150)); m_tp_edit[i+2].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::StopPendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i+2].Id()) { if(m_p_switch_button[3*i+2].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_sl_edit[i+2].SetDigits(_Digits); m_sl_edit[i+2].StepValue(_Point); m_sl_edit[i+2].SetValue(string(tick.bid)); m_sl_edit[i+2].GetTextBoxPointer().Update(true); } } else { m_sl_edit[i+2].SetDigits(0); m_sl_edit[i+2].StepValue(1); m_sl_edit[i+2].SetValue(string(150)); m_sl_edit[i+2].GetTextBoxPointer().Update(true); } } } }
現在,我們在按鈕單擊事件應答程序部分中調用它們:
// --- 切換:手數/餘額百分比 LotPendingSwitch(lparam); //--- 切換:止盈點數/價格 TakePendingSwitch(lparam); //--- 切換:止損點數/價格 StopPendingSwitch(lparam);
編譯項目,並查看結果:
圖例 11 掛單的切換模式輸入字段。
切換模式輸入字段已準備就緒;屬性也已更改。 繼續創建函數,請求下訂單。 該算法與市價單類似。 創建四個方法,每種類型一個。
bool SetBuyStopOrder(int id,long lparam); bool SetSellStopOrder(int id,long lparam); bool SetBuyLimitOrder(int id,long lparam); bool SetSellLimitOrder(int id,long lparam);
實現它們。 為每筆掛單設置熱鍵,還有單擊按鈕。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buystop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_1)) { //--- double lot; if(m_p_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[2].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[2].GetValue())); //--- double pr=double(m_pr_edit[0].GetValue()); //--- if(m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sellstop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_2)) { //--- double lot; if(m_p_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[3].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[3].GetValue())); //--- double pr=double(m_pr_edit[1].GetValue()); //--- if(m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[3].GetValue()); double sl=double(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[3].GetValue()); int sl=int(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[3].GetValue()); int sl=int(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[3].GetValue()); double sl=double(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyLimitOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buylimit_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_3)) { //--- double lot; if(m_p_switch_button[6].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[4].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[4].GetValue())); //--- double pr=double(m_pr_edit[2].GetValue()); //--- if(m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed()) { double tp=double(m_tp_edit[4].GetValue()); double sl=double(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed()) { int tp=int(m_tp_edit[4].GetValue()); int sl=int(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed()) { double tp=double(m_tp_edit[4].GetValue()); int sl=int(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed()) { int tp=int(m_tp_edit[4].GetValue()); double sl=double(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellLimitOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_selllimit_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_4)) { //--- double lot; if(m_p_switch_button[9].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[5].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[5].GetValue())); //--- double pr=double(m_pr_edit[3].GetValue()); //--- if(m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed()) { double tp=double(m_tp_edit[5].GetValue()); double sl=double(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed()) { int tp=int(m_tp_edit[5].GetValue()); int sl=int(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed()) { double tp=double(m_tp_edit[5].GetValue()); int sl=int(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed()) { int tp=int(m_tp_edit[5].GetValue()); double sl=double(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); }
現在,進入事件應答程序,並在其中添加調用新創建的方法 :
//+------------------------------------------------------------------+ //| 圖表事件應答 | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- SetBuyOrder(id,lparam); SetSellOrder(id,lparam); //--- SetBuyStopOrder(id,lparam); SetSellStopOrder(id,lparam); SetBuyLimitOrder(id,lparam); SetSellLimitOrder(id,lparam);
操控所有訂單類型的基本功能均已就緒。 由於此應用程序的最終目的是為手動交易提供最大的便利和速度,因此我決定添加一個小功能,能夠加速市價和掛單交易。 其原理如下:在不同模式下設置輸入字段值時,理應可用向上和向下箭頭按指定步驟更改該值。 不過,在設置時,例如掛單價格,按箭頭把 EURUSD 價格從 1.08500 更改為 1.85001。 這很慢,且並不方便。 因此,當選擇輸入字段進行編輯,並按上/下熱鍵時,將數值變換的步長乘以 10。 為此目的,我們需要創建 ArrowSwitch() 方法。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ArrowSwitch(long lparam) { //--- 價格 for(int i=0; i<4; i++) { if(m_pr_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- 取新值 double value=StringToDouble(m_pr_edit[i].GetValue())+m_pr_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_pr_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- 取新值 double value=StringToDouble(m_pr_edit[i].GetValue())-m_pr_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_pr_edit[i].SetValue(DoubleToString(value),false); } } } //--- 手數 for(int i=0; i<6; i++) { if(m_lot_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- 取新值 double value=StringToDouble(m_lot_edit[i].GetValue())+m_lot_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_lot_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- 取新值 double value=StringToDouble(m_lot_edit[i].GetValue())-m_lot_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_lot_edit[i].SetValue(DoubleToString(value),false); } } } //--- 止盈,止損 for(int i=0; i<6; i++) { //--- if(m_tp_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- 取新值 double value=StringToDouble(m_tp_edit[i].GetValue())+m_tp_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_tp_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- 取新值 double value=StringToDouble(m_tp_edit[i].GetValue())-m_tp_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_tp_edit[i].SetValue(DoubleToString(value),false); } } //--- if(m_sl_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- 取新值 double value=StringToDouble(m_sl_edit[i].GetValue())+m_sl_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_sl_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- 取新值 double value=StringToDouble(m_sl_edit[i].GetValue())-m_sl_edit[i].StepValue()*10; //--- 逐步遞增並檢查是否超出限制 m_sl_edit[i].SetValue(DoubleToString(value),false); } } } }
該方法應在 Keypress 部分的事件應答程序中調用。
//--- 按鍵 if(id==CHARTEVENT_KEYDOWN) { //--- ArrowSwitch(lparam); ....
圖例 12 示意數值變化速度之間的差異。
圖例 12 使用熱鍵在編輯字段中快速更改數值。
快速放置市價單和掛單的工具已準備就緒。 現在,我們轉到主應用程序窗口,並添加與存在訂單有關的操作(如本文開頭的第一張圖例所示)。 其中包括三個操作:所有盈利單平倉,所有虧損單平倉,以及刪除所有存在的掛單。 這些操作僅適用於本程序下的訂單,其訂單應擁有唯一的魔幻數字。 還應記住,在放置含當前魔幻數字的訂單時,以及在 EA 屬性中更改魔幻數字時,所有含有舊數字的存在訂單均被忽略,因為它們不再與本應用程序相關。
我們來實現這些動作。 創建三個新方法:
void CloseAllMarketProfit(int id,long lparam); void CloseAllMarketLoss(int id,long lparam); void DeleteAllPending(int id,long lparam);
實現每一個。
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketProfit(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_C)) { //--- 聲明請求和結果 MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // 持倉數量 //--- 遍曆持倉 for(int i=total-1; i>=0; i--) { //--- 訂單參數 ulong position_ticket=PositionGetTicket(i); // 單號 string position_symbol=PositionGetString(POSITION_SYMBOL); // 品種 int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // 小數點位數 ulong magic=PositionGetInteger(POSITION_MAGIC); // 魔幻數字 double volume=PositionGetDouble(POSITION_VOLUME); // 持倉量 ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // 持倉類型 double profit=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- 如果魔幻數字匹配 if(magic==m_magic_number) if(position_symbol==Symbol()) if(profit+swap>0) { //--- 將請求和結果值清零 ZeroMemory(request); ZeroMemory(result); //--- 設置操作參數 request.action =TRADE_ACTION_DEAL; // 交易操作類型 request.position =position_ticket; // 單號 request.symbol =position_symbol; // 品種 request.volume =volume; // 持倉量 request.deviation=5; // 允許的價格偏離 request.magic =m_magic_number; // 魔幻數字 //--- 根據持倉類型設置訂單價格和類型 if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- 發送請求 if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // 如果無法發出請求,則輸出錯誤代碼 } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketLoss(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[3].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_D)) { //--- 聲明請求和結果 MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); int total=PositionsTotal(); // 持倉數量 //--- 遍曆持倉 for(int i=total-1; i>=0; i--) { //--- 訂單參數 ulong position_ticket=PositionGetTicket(i); // 單號 string position_symbol=PositionGetString(POSITION_SYMBOL); // 品種 int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // 小數點位數 ulong magic=PositionGetInteger(POSITION_MAGIC); // 魔幻數字 double volume=PositionGetDouble(POSITION_VOLUME); // 持倉量 ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // 持倉類型 double profit=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- 如果魔幻數字匹配 if(magic==m_magic_number) if(position_symbol==Symbol()) if(profit+swap<0) { //--- 將請求和結果值清零 ZeroMemory(request); ZeroMemory(result); //--- 設置操作參數 request.action =TRADE_ACTION_DEAL; // 交易操作類型 request.position =position_ticket; // 單號 request.symbol =position_symbol; // 品種 request.volume =volume; // 持倉量 request.deviation=5; // 允許的價格偏離 request.magic =m_magic_number; // 魔幻數字 //--- 根據持倉類型設置訂單價格和類型 if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- 發送請求 if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // 如果無法發出請求,則輸出錯誤代碼 } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::DeleteAllPending(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_R)) { //--- 聲明並初始化交易請求和交易結果 MqlTradeRequest request; MqlTradeResult result; //--- 遍曆所有已下掛單 for(int i=OrdersTotal()-1; i>=0; i--) { ulong order_ticket=OrderGetTicket(i); // 單號 ulong magic=OrderGetInteger(ORDER_MAGIC); // 魔幻數字 //--- 如果魔幻數字匹配 if(magic==m_magic_number) { //--- 請求和結果值清零 ZeroMemory(request); ZeroMemory(result); //--- 設置操作參數 request.action= TRADE_ACTION_REMOVE; // 交易操作類型 request.order = order_ticket; // 單號 //--- 發送請求 if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // 如果無法發出請求,則輸出錯誤代碼 } } } }
請注意,所創建方法還應遵循依據兩個不同事件執行操作的規則:單擊主應用程序窗口中的按鈕,並按熱鍵。 這三個函數都將在事件應答程序中調用,位於所有部分之外。
//+------------------------------------------------------------------+ //| 圖表事件應答 | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- SetBuyOrder(id,lparam); SetSellOrder(id,lparam); //--- SetBuyStopOrder(id,lparam); SetSellStopOrder(id,lparam); SetBuyLimitOrder(id,lparam); SetSellLimitOrder(id,lparam); //--- CloseAllMarketProfit(id,lparam); CloseAllMarketLoss(id,lparam); //--- DeleteAllPending(id,lparam);
快捷手動交易工具箱基本功能的開發至此完畢。 以下視頻演示所創建的應用程序。
結束語
附件包含所有列出的文件,這些文件應置於相應的文件夾之中。 為了令其正確運行,您僅需要將 MQL5 文件夾保存到終端文件夾當中。 若要打開 MQL5 文件夾所在的終端根目錄,請在 MetaTrader 5 終端中按 Ctrl+Shift+D 組合鍵,或使用關聯菜單,如下圖例 13 所示。
圖例13 在 MetaTrader 5 終端根目錄中打開 MQL5 文件夾