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

12 十月 2020, 09:26
Alexander Fedosov
0
1 928

内容

概述

如今,众多交易者切换至自动交易系统,这类系统可能需要附加设置,或是能够完全自动化并准备就绪。 然而,有相当一部分交易者更喜欢以旧有方式进行手动交易。 在各种特定交易情况下制定决策时,他们更期望拥有人类专家的判断力。 有时,这种状况会快速发展,因此交易者需要迅速对其做出反应。 而且,某些交易方式(例如,剥头皮)要求准确入场时机。 在其中,附加工具也许会派上用场:它们可为所需动作提供最快可能实施,诸如入场或离场。 因此,我决定实现基本功能,从而满足快速手动交易需求。

工具箱的概念

首先,有必要确定手动交易中可能需要的一组基本操作。 有基于此,我们将开发一种工具,能够高效、快捷地执行相应的操作。 手动交易已有一套底层交易系统,这意味着以下入场入方法之一:使用市价或挂单。 因此,该工具箱的主要准则是运用这两种类型订单的能力。 我们还可以为交易者选择在交易过程中可以执行的任务 — 工具可有助于减少执行这些任务时所需的时间和数量。

图例 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 文件夹


本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/7892

附加的文件 |
MQL5.zip (5621.18 KB)
神经网络在交易中的实际应用。 是时候进行实践了 神经网络在交易中的实际应用。 是时候进行实践了

本文提供了在 Matlab 平台上实际运用神经网络模块的讲述和指南。 它还涵盖了运用神经网络模块创建交易系统的主要方面。 为了能够在一篇文章中厘清复杂内容,我必须对其进行修改,从而在一个程序中组合若干个神经网络模块函数。

神经网络在交易中的实际应用 神经网络在交易中的实际应用

在本文中,我们将研究神经网络与交易终端集成的主要方面,从而创建功能齐全的交易机器人。

计算数学表达式(第一部分)。 递归下降解析器 计算数学表达式(第一部分)。 递归下降解析器

本文研究数学表达式解析和计算的基本原理。 我们基于预建语法树,实现运行于解释器和快速计算模式的递归下降解析器。

DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区 DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区

在本文中,我将着手改进指标缓冲区对象和集合类,从而可在多周期和多品种模式下操作。 我打算在当前品种图表上的任何时间帧内接收和显示数据缓冲区对象的操作。