繁簡切換您正在訪問的是FX168財經網,本網站所提供的內容及信息均遵守中華人民共和國香港特別行政區當地法律法規。

FX168財經網>人物頻道>帖子

快捷手動交易工具箱:基本功能

作者/量化客 2020-10-12 16:00 0 來源: FX168財經網人物頻道

內容

  • 概述
  • 工具箱的概念
  • 基本功能的實現
  • 結束語

概述

如今,眾多交易者切換至自動交易系統,這類系統可能需要附加設置,或是能夠完全自動化並準備就緒。 然而,有相當一部分交易者更喜歡以舊有方式進行手動交易。 在各種特定交易情況下制定決策時,他們更期望擁有人類專家的判斷力。 有時,這種狀況會快速發展,因此交易者需要迅速對其做出反應。 而且,某些交易方式(例如,剝頭皮)要求準確入場時機。 在其中,附加工具也許會派上用場:它們可為所需動作提供最快可能實施,諸如入場或離場。 因此,我決定實現基本功能,從而滿足快速手動交易需求。

工具箱的概念

首先,有必要確定手動交易中可能需要的一組基本操作。 有基於此,我們將開發一種工具,能夠高效、快捷地執行相應的操作。 手動交易已有一套底層交易系統,這意味着以下入場入方法之一:使用市價或掛單。 因此,該工具箱的主要準則是運用這兩種類型訂單的能力。 我們還可以為交易者選擇在交易過程中可以執行的任務 — 工具可有助於減少執行這些任務時所需的時間和數量。

圖例 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 文件夾


分享到:
舉報財經168客戶端下載

全部回複

0/140

投稿 您想發表你的觀點和看法?

更多人氣分析師

  • 張亦巧

    人氣2208文章4145粉絲45

    暫無個人簡介信息

  • 張迎妤

    人氣1904文章3305粉絲34

    個人專注於行情技術分析,消息面解讀剖析,給予您第一時間方向...

  • 指導老師

    人氣1864文章4423粉絲52

    暫無個人簡介信息

  • 李冉晴

    人氣2320文章3821粉絲34

    李冉晴,專業現貸實盤分析師。

  • 梁孟梵

    人氣2184文章3177粉絲39

    qq:2294906466 了解群指導添加微信mfmacd

  • 王啟蒙現貨黃金

    人氣312文章3406粉絲8

    本人做分析師以來,並專注於貴金屬投資市場,尤其是在現貨黃金...

  • 金泰鉻J

    人氣2328文章3925粉絲51

    投資問答解咨詢金泰鉻V/信tgtg67即可獲取每日的實時資訊、行情...

  • 金算盤

    人氣2696文章7761粉絲125

    高級分析師,混過名校,廝殺於股市和期貨、證券市場多年,專注...

  • 金帝財神

    人氣4760文章8329粉絲119

    本文由資深分析師金帝財神微信:934295330,指導黃金,白銀,...

FX168財經

FX168財經學院

FX168財經

FX168北美