您的位置 首页 外汇EA指标

外汇EA编写教程:MQL5酷客宝书:实现自己的市场深度

内容目录 介绍 第1章。MetaTrader 5中的DOM及其方法1.1。标准市场深度1.2元交易员5。市场深度操纵事件模型1.3。接收具有marketbookget函数和mqlb…

内容目录

  • 介绍
  • 第1章。MetaTrader 5中的DOM及其方法1.1。标准市场深度1.2元交易员5。市场深度操纵事件模型1.3。接收具有marketbookget函数和mqlbookinfo结构的2级报价
  • 第2章。Marketbook Class 2.1,用于简单访问和操作市场深度。设计2.2级市场信息手册。计算最常用的市场深度指数2.3级。根据该指数2.4的前值,预测出最优销售价格指数和买入价格指数。通过getdeviation byvol方法确定最大滑动点2.5。用于操作cmarketbook类的例程
  • 第3章。将自己的市场深度写为面板索引3.1。设计市场深度面板的一般原理,创建指数3.2。点击处理市场深度和事件创建3.3。市场深度单位3.4.交易量柱状图3.5显示在市场深度。快速计算市场深度最大交易量,迭代优化3.6。收盘:成交量柱状图和分界线3.7。添加有关在国内交易的金融工具的限制性订单总数的属性信息
  • 第4章。CMarketbook 4.1类文件。从市场深度获取基本信息及其操作方法刷新()infogetinteger()infogetdouble()infogetdouble()方法getdeviationbyvol方法4.2。CMarketbook类枚举的枚举和修饰符枚举枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-枚举-记数-记数-记数-记数-记数-记数-记数-记数-记使用cmarketbook类的例程
  • 结论

介绍

MQL5语言继续发展,每年提供更多的交易信息机会。交易数据的一种类型是关于市场深度的信息。这是一个特殊的表格,显示报价单的价格水平和数量。MetaTrader5有显示价格限制的内置市场深度,但这还不够。首先,您的EA必须能够轻松方便地进入市场深度。当然,mql5有一些特殊的函数来处理这些信息,但是它们有一些需要额外数学计算的低级函数。

但是,可以避免所有中间计算。你所要做的就是写一个特殊的类来操纵市场深度。所有复杂的计算都是在市场深度进行的,类本身提供了一种操纵DOM价格和级别的方便方法。这个类别可以简单地以指标的形式创建一个有效的面板,它可以立即反映市场价格状态的深度:

图1。面板显示的市场深度

本文介绍了如何用市场深度(dom)编程,介绍了cmarketbook类的工作原理,可以扩展mql5标准库的类,为使用dom提供一种方便的方法。

在阅读了本文的第一章之后,元交易者5对市场深度的监管能力更加明显。我们不会在指标中重复所有这些,因为我们的任务将完全不同。通过实现创建用户友好的深度市场交易面板的程序,我们将展示通过面向对象编程简单处理复杂数据结构的原则。我们将确保使用MQL5直接从您的EA访问市场深度并不困难,由此产生的视觉提示将帮助我们。

nbsp;

第1章。元交易员5的市场深度及其使用方法

元交易员5的标准市场深度

MetaTrader 5支持在集中交易所进行交易,并提供了操纵市场深度的标准工具。首先,它当然是一个限价订单的列表,它可以描述当前领先的模型。为了打开市场深度,不允许您连接到支持MetaTrader 5的交易所,并选择View->“Market Depth”–gt;“Financial Instrument Name”弹出窗口,其中包含分时图表和限价订单列表:

图2。元交易员5的标准市场深度

元交易者5的标准市场深度具有丰富的功能。特别是,它允许显示以下内容

  • 买卖限价单,其价格和数量(古典DOM的标准形式);
  • 当前利差水平和价格限制(主导模式);
  • 分时图表和可视化购买价格、销售价格和最终交易量;
  • 采购订单的总级别(分时图表中分别显示的上下两行)。

这份名单提供的确认比市场的深度更令人印象深刻。让我们探讨如何通过编程操作访问数据。首先,您需要了解市场深度是如何建立的,以及数据组织的关键概念。有关更多信息,请阅读“莫斯科证券交易所衍生品市场定价原则”一文第13章作为示例。买卖双方的匹配。证券交易所市场深度。我们不会花太多时间描述这个表,假设读者已经对这个概念了解得足够多。

nbsp;

1.2。市场深度操纵事件模型

市场深度有一个动态数据表。在一个快速动态的市场中,限价指令列表可能每秒发生多次变化。因此,您必须尝试只处理真正需要处理的信息,否则处理这些数据时的数据传输数量和CPU负载可能会超过合理的限制。这就是为什么MetaTrader 5需要一个特殊的事件模型来防止数据收集和处理未能充分利用它。让我们彻底检查一下这个模式。nbsp;

市场中发生的任何事件,例如新的分时价格的到达或交易的执行,都可以通过调用与之相关联的相应函数来处理。例如,当新的分时价格到达MQL5时,将调用一个特殊的ontick()事件处理程序。调整图表的大小或位置将调用onChartEvent()函数。事件模型也适用于市场深度的变化。例如,如果有人在市场深度下现货订单,其状态将发生变化,并调用一个特殊的onbookEvent()函数。

由于终端中有几十种甚至数百种不同的具有自身市场深度的产品,因此调用OnBookEvent功能的次数可能非常大,而且资源密集。为避免出现这种情况,终端应在运营指标或EA需要获得金融工具二级报价时及早关注(市场深度提供的信息也应这样调用)。为此,可以使用一个特殊的系统功能marketbookadd。例如,如果我们要获得有关金融工具SI-9.15(美元/卢布期货合约于2015年9月到期)的市场深度信息,我们需要在EA或指标的OnInit函数中编写以下代码:

void OnInit()
{
   MarketBookAdd("Si-9.15");
}

使用这个函数,我们创建了一个“订阅”,并引起了最终的注意。在此之后,EA或Metrics将收到基于SI-9.15的市场深度变化事件通知。其他金融工具市场变化的深度不会对我们造成干扰,这将大大减少这一过程所消耗的资源。

marketbook add的反函数是marketbook释放函数。相反,我们“退订”从市场深度的变化通知。程序员最好在OnDeInit部分取消订阅,以便在EA或指示器退出之前关闭数据访问。

void OnDeinit(const int reason)
{
   MarketBookRelease("Si-9.15");
}

调用marketbookadd函数基本上意味着当相关金融工具的市场深度发生变化时,将调用特殊事件处理程序onbookevent()。这种对市场深度的简单EA或指数操纵包括三个系统功能:

  • OnInit-EA或指标初始化功能用于订阅接收感兴趣金融工具的市场深度变化更新。
  • Ondeinit EA或指数的逆初始函数用于取消订阅和接收有关金融工具的市场深度更新。
  • OnBookEvent-当市场深度发生变化时调用函数。

我们的第一个EA例程将包含这三个功能。如以下程序所示,市场深度的每一次变化都会产生以下新闻:“市场深度SI-9.15已发生变化”:

//+------------------------------------------------------------------+
//|                                                       Expert.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| 初始化函数                                                        |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MarketBookAdd("Si-9.15");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 逆初函数                                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   MarketBookRelease("Si-9.15");
  }
//+------------------------------------------------------------------+
//| BookEvent 函数                                                   |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---
   printf("市场深度 " + symbol +  " 有变化"); 
  }
//+------------------------------------------------------------------+

代码中的关键说明以黄色突出显示。

nbsp;

1.3。使用marketbookget函数和mqlbookinfo结构接收二级报价

既然我们已经了解了如何接收市场深度变化的通知,现在是时候学习如何使用marketbookget函数访问DOM信息了。让我们看看它的原型和用法。

如前所述,市场深度呈现为一个特殊的表,由两部分组成,以显示销售和购买限额订单。与所有其他表一样,显示市场深度的最简单方法是使用数组,其中数组的索引是表的行数,数组值是确定一行或一系列数据,包括容量、价格和应用程序类型。我们还可以想象一个显示每行指数的市场深度表:

行索引 订单类型 交易量 价格
卖出限制 十八 五万六千八百四十四
&销售限制 1; 56843;
&销售限制 21; 56842;
&购买限额 9; 56836;
&购买限额 5; 56835;
&购买限额 15; 56834;

&表1。表中显示的市场深度

为了便于遍历整个表单,销售订单标记为粉色,采购订单标记为蓝色。市场深度表基本上是二维数组。第一个维度表示行号,第二个维度是三个表元素之一(订单类型-0、订单量-1和订单价格-2)。然而,为了避免操作多维数组,在MQL5中使用了一个特殊的mqlbookinfo结构。它包括所有必要的值。这样,每个市场深度指数都包含一个mqlbookinfo结构,它按顺序携带有关订单类型、数量和价格的信息。让我们定义这个结构:

struct MqlBookInfo
  {
   ENUM_BOOK_TYPE   type;       // 来自 ENUM_BOOK_TYPE 枚举的订单类型
   double           price;      // 订单价格
   long             volume;     // 订单交易量
  };

现在,操纵市场深度的方法应该是明确的。marketbookget函数返回一个mqlbookinfo结构数组。数组索引表示价格表的行,切线索引结构包含有关数量、价格和订单类型的信息。认识到这一点,我们将尝试访问第一个DOM订单。因此,我们在执行以下操作之前稍微修改了EA例程中的onbookEvent函数:

//+------------------------------------------------------------------+
//| BookEvent 函数                                                   |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---
   //printf("市场深度 " + symbol +  " 有变化"); 
   MqlBookInfo book[];
   MarketBookGet(symbol, book);
   if(ArraySize(book) == 0)
   {
      printf("加载市场预订价格失败。原因: " + (string)GetLastError());
      return;
   }
   string line = "价格: " + DoubleToString(book[0].price, Digits()) + "; ";
   line += "交易量: " + (string)book[0].volume + "; ";
   line += "类型: " + EnumToString(book[0].type);
   printf(line);
  }

在任何图表上运行此EA时,我们都将收到第一个DOM及其参数的报告:

2015.06.05 15:54:17.189 Expert (Si-9.15,H1)     价格: 56464; 交易量: 56; 类型: BOOK_TYPE_SELL
2015.06.05 15:54:17.078 Expert (Si-9.15,H1)     价格: 56464; 交易量: 56; 类型: BOOK_TYPE_SELL
2015.06.05 15:54:17.061 Expert (Si-9.15,H1)     价格: 56464; 交易量: 56; 类型: BOOK_TYPE_SELL
...

查看上一个表,很容易猜测dom零索引处的级别对应于最差的出价(book_type_sell)。相比之下,最低的投标价格是从结果数组的最后一个索引中获取的。最畅销和最畅销的价格处于市场深度的中间。获得的价格的第一个缺点是,当通常计算市场深度时,最佳价格通常在表的中间。最低的买卖价格是次要的。此外,当我们分析cmarketbook类时,我们将提供特殊的索引器,以便于操纵市场深度来解决这个问题。

nbsp;

第2章。具有市场深度的市场手册类的简单访问和操作

设计Cmarketinfobook类

在第一章中,我们看到了操纵市场深度的系统功能,并找到了访问二级报价的事件模型的具体特征。在本章中,我们将创建一个特殊的cmarketbook类,以便于使用标准市场深度。根据从第1章中获得的知识,我们可以讨论在操作这些数据时类应该具有的属性。

因此,在设计此类时首先要考虑的是接收数据时的资源密度。市场深度每秒可更新数十次。此外,它还包含几十个mqlbookinfo类型元素。因此,我们班最初只能操纵一种金融工具。当处理来自多个不同金融工具的深层市场时,它会为每个特定金融工具创建多个类别的副本:

CMarketBook("Si-9.15");            // 市场深度 Si-9.15
CMarketBook("ED-9.15");            // 市场深度 ED-9.15
CMarketBook("SBRF-9.15");          // 市场深度 SBRF-9.15
CMarketBook("GAZP-9.15");          // 市场深度 GAZP-9.15

第二,我们需要关注组织如何更容易地访问数据。由于限价订单生成高频价格流,因此不太可能将市场深度复制到安全的面向对象表中。因此,我们的类将通过marketbookget系统函数提供对mqlbookinfo数组的直接访问(尽管不是非常安全)。例如,使用我们的类访问dom zero索引,可以编写如下内容:

CMarketBook BookOnSi("Si-9.15");
...
//+------------------------------------------------------------------+
//| BookEvent 函数                                                   |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---
   MqlBookInfo info = BookOnSi.MarketBook[0];
  }
//+------------------------------------------------------------------+

marketbook是通过调用marketbookget函数直接获得的数组。但是,使用我们的类更方便,主要是基于我们的类在市场深度上有更多的针对性地访问最常用的价格,除了直接访问限制订单数组。例如,为了获得最佳销售价格,只需编写以下代码即可:

double best_ask = BookOnSi.InfoGetDouble(MBOOK_BEST_ASK_PRICE);

这比计算EA中的最佳投标指数和基于该指数获取价格值更方便。从上面的代码可以看出,cmarketbook和许多其他的mql5系统函数(如symbolinfould或orderhistory integer)分别使用它们自己的修饰符集和infogetinteger和infogetdouble方法访问integer和double精度值。为了获得所需的属性,我们必须为此属性指定修饰符。让我们详细描述这些属性的修饰符:

//+------------------------------------------------------------------+
//| 用于 DOM 整数属性的特别                                            |
//| 修饰符。                                                          |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_INFO_INTEGER
{
   MBOOK_BEST_ASK_INDEX,         // 最佳卖出价索引
   MBOOK_BEST_BID_INDEX,         // 最佳买入价索引
   MBOOK_LAST_ASK_INDEX,         // 最坏卖出价索引
   MBOOK_LAST_BID_INDEX,         // 最坏买入价索引
   MBOOK_DEPTH_ASK,              // 卖出级别数量
   MBOOK_DEPTH_BID,              // 买入级别数量
   MBOOK_DEPTH_TOTAL             // DOM 级别总数
};
//+------------------------------------------------------------------+
//| 用于 DOM 双精度属性的特别                                           |
//| 修饰符。                                                          |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_INFO_DOUBLE
{
   MBOOK_BEST_ASK_PRICE,         // 最佳卖出价
   MBOOK_BEST_BID_PRICE,         // 最佳买入价
   MBOOK_LAST_ASK_PRICE,         // 最坏卖出价 
   MBOOK_LAST_BID_PRICE,         // 最坏买入价
   MBOOK_AVERAGE_SPREAD          // 卖出价与买入价之间的平均点差
};

当然,除了信息获取…方法组,我们的类将包含刷新方法以触发市场深度更新。因为我们的类实际上需要调用refresh()方法,所以我们只在需要的时候用资源密集型的类来更新它。

nbsp;

2.2。市场深度水平最常用指标的计算

cmarketbook类是mqlbookinfo数组的外包。其主要目的是提供对该阵列中最频繁请求的信息的快速便捷访问。因此,类中只启用两个基本的资源密集型操作:

  • 使用marketbookget系统函数复制mqlbookinfo数组。
  • 计算最常用价格的索引。

我们无法加速您的marketbookget系统功能,但这是不必要的,因为在成都所有的mql5系统功能都已优化到最大。但是我们可以生成我们需要的最快的索引计算。同样,我们也提到了枚举“mbook-info”整数和枚举“mbook-info”双属性修饰符。如您所见,几乎所有可用属性都是基于四个索引计算的:

  • 最畅销价格指数;
  • 百思买价格指数;
  • 最差买入价格指数;
  • 最差的销售价格指数。

还使用三个整数数值属性:

  • 销售水平或市场深度中的销售数量(销售深度);
  • 购买数量(购买价格的深度)在购买水平或市场深度;
  • 总市场深度等于DOM中的元素总数。

显然,最差的出价指数将始终为零,因为数组由marketbookget函数生成,并以最差的出价开始。搜索最差购买价索引是不适当的-它始终是所获取的MQLinfobook数组中的最后一个索引(我们要提醒您,最后一个元素的索引小于此数组中的元素总数):

最低价指数=0

最差买入价格指数=市场深度的总要素-1

整数属性的索引也很容易计算。因此,总市场深度始终等于mqlbookinfo数组中的元素数。从售价方面看,市场深度如下:

销售深度=最佳销售价格指数-最差销售价格指数+1

我们总是添加一个,因为数组数从零开始,需要添加元素数才能更好地索引。然而,通过增加一个以获得最好的出价,我们得到了最好的出价指数。例如,如果我们在表1的第2行添加一个,我们将从最畅销限价订单56 842移动到最畅销限价订单56 836。我们还发现最差的销售价格指数总是零。因此,我们可以减少公式,找出相应的销售价格深度:

销售深度=百思买价格指数

计算购买价格的深度有些不同。显然,采购订单的数量等于订单总数减去销售订单总数或销售价格的深度。由于我们在以前的公式中已经知道,销售价格的深度等于购买价格的最佳指数,因此不难得出确定采购订单数量或购买价格深度的公式:

买入深度=总市场深度-最畅销价格指数

市场的总深度始终等于购买和销售的总深度,因此等于市场深度中的元素总数:

总市场深度=总市场深度要素

经过分析,我们找到了几乎所有的常用索引。利用数学约简,我们用直接指数代替了指数的计算。这在cmarketbook类中非常重要,因为需要尽快访问市场深度属性和索引。

除了实际指标外,还经常需要了解当前金融工具的平均点差。价差是指最佳买入价和最佳卖出价之间的差额。cmarketbook类可以使用mbook_average_spread修饰符调用infoGetDouble方法来获取此参数的平均值。cmarketbook计算刷新方法中的当前点差,并根据调用该方法时的内存次数计算平均值。

然而,我们还没有找到最佳买卖价格的主要指数,所以我们继续下一节。

nbsp;

2.3。根据这些指数的预设值预测最佳销售和购买价格指数

计算最低价和买价是一项更困难的工作,我们还没有完成。例如,表1中的最畅销价格指数应为2,而最畅销价格指数应为3。在本表中,简化的市场深度仅包含六个级别。事实上,市场可能更深入,达到64个交易水平。这是一个重要的值,考虑到DOM更新可能每秒发生多次。

最简单的解决方案是在这里使用“一分为二”的方法。实际上,如果我们取表1中的总级别数,它是6。如果我们把它分成两部分,得到的数字(3)就是最佳购买价格的指数。因此,前面的指数是最畅销价格的指数(2)。但是,此方法仅适用于等于dom表中购买级别数的销售级别数。这通常发生在流动性市场。然而,在非流动性市场中,市场深度可能只是部分饱和,而在另一方面,可能没有水平。

我们的cmarketbook类需要操纵任何市场和任何市场深度,因此一个二合一的方法不适合我们。为了描述二分法可能无法正常工作的情况,我们参考以下插图:

图3。买入价的数量并不总是等于卖出价的数量。

图3。显示两个市场深度。第一个期货合约是两年期联邦债券的国内市场(ofz2-9.15)。第二个是欧元/美元期货合同(ED-9.15)。结果表明,对于OFZ2-9.15,买入价为4,卖出价为8。在流动性更强的ED-9.15市场中,买方和卖方的数量为12。在ED-9.15的情况下,索引的二合一方法有效,但对于OFZ2无效。-

更可靠的索引搜索方法应该使用dom迭代,直到第一个订单与book_type_buy类型匹配。上一个指数将自动成为最畅销价格的指数。这是cmarketbook类具有的方法。我们将参考上述描述:

void CMarketBook::SetBestAskAndBidIndex(void)
{
   if(!FindBestBid())
   {
      //以低速全搜索来查找最佳卖出价
      int bookSize = ArraySize(MarketBook);   
      for(int i = 0; i < bookSize; i++)
      {
         if((MarketBook[i].type == BOOK_TYPE_BUY) || (MarketBook[i].type == BOOK_TYPE_BUY_MARKET))
         {
            m_best_ask_index = i-1;
            FindBestBid();
            break;
         }
      }
   }
}

此方法的主要目的是使用运算符迭代DOM。一旦在市场深度遇到与书籍购买类型匹配的第一个订单,就会设置最佳购买和销售索引,并中断迭代。完整的市场深度迭代对于每次更新都是一个非常资源密集的方案。

除了重复每次更新,还可以选择记住以前获得的最佳买入和卖出价格指数。事实上,市场深度通常包括固定数量的买入和卖出水平。因此,每次进行市场深度迭代时,不必搜索新的指数。参考之前的指数就足够了,看看它们是否仍然是买入和卖出价格的最佳指数。findbestbid私有方法用于解决此类问题。让我们看看它的内容:

//+------------------------------------------------------------------+
//| 根据最佳卖出价快速查找最佳买入价                                     |
//+------------------------------------------------------------------+
bool CMarketBook::FindBestBid(void)
{
   m_best_bid_index = -1;
   bool isBestAsk = m_best_ask_index >= 0 && m_best_ask_index < m_depth_total &&
                    (MarketBook[m_best_ask_index].type == BOOK_TYPE_SELL ||
                    MarketBook[m_best_ask_index].type == BOOK_TYPE_SELL_MARKET);
   if(!isBestAsk)return false;
   int bestBid = m_best_ask_index+1;
   bool isBestBid = bestBid >= 0 && bestBid < m_depth_total &&
                    (MarketBook[bestBid].type == BOOK_TYPE_BUY ||
                    MarketBook[bestBid].type == BOOK_TYPE_BUY_MARKET);
   if(isBestBid)
   {
      m_best_bid_index = bestBid;
      return true;
   }
   return false;
}

操作方便。首先,该方法确定了当前的最优销售价格指数仍然符合最优销售价格指数。之后,重新设置最佳投标价格的索引,并尝试再次找到它。报价要素在最佳投标指标之后:

int bestBid = m_best_ask_index+1;

如果找到的元素确实是最佳购买价格的索引,那么dom以前的状态与当前状态相同。因此,可以避免DOM迭代,因为在setBestAsk和bidIndex方法中,在迭代之前调用findbestBid方法。因此,只有在进行第一个函数调用以及销售和购买级别的数量发生变化时,才会执行DOM迭代。

虽然生成的源代码比简单的市场深度更大、更复杂,但实际上运行速度更快。对于流动性市场中的巨大国内市场而言,业绩增长尤为明显。检查条件的简单说明将很快得到满足,并且这些检查的数量远远小于使用for运算符的循环数。因此,设计用于寻找最佳价格指数的方法将比正常迭代执行得更好。

nbsp;

2.4。用体积偏差法确定最大滑动点

市场深度通常被交易员用来确定当前市场的流动性,也就是说,市场深度被用作控制风险的附加工具。如果市场流动性较低,市场价格进入可能导致高滑动点。滑动点总是意味着可能会有重大成本的额外损失。

为了避免这种情况,必须使用另一种方法来控制入院。更多信息,请阅读“如何使您的EA在莫斯科证券交易所交易时更安全”。因此,我们不会详细阐述这些方法,只会提到,通过深入市场,我们可以在进入市场之前评估潜在的滑动点价值。滑动点的大小取决于两个因素:

  • 买方(卖据)和卖方(买据)的流动性;
  • 体积。

参观完市场的深度后,我们可以看到我们的订单将被执行的数量和价格。如果我们知道我们的订单量,我们可以计算出入口价格的加权平均价格。这个价格和百思买或卖出价格之间的差额(根据进入的方向)是我们的滑动点。

手工计算加权平均录取价格是不可能的,因为它需要在很短的时间内进行大量的计算(我提醒您,dom的状态可能每秒更改几次)。因此,将此任务委托给EA或指标是很自然的。

cmarketbook类包含计算此功能的特殊方法getdeviationbyvol。由于交易量会影响滑动点的大小,因此有必要采用市场预期交易量的方法。由于该方法采用交易量的算术整数值,与莫斯科证券交易所期货市场一样,它将交易量作为长整数值。此外,该方法需要知道必须执行哪一侧的移动计算,因此使用了一个特殊的枚举“mbook”侧枚举:

//+------------------------------------------------------------------+
//| MarketBook 的方向                                                 |
//+------------------------------------------------------------------+
enum ENUM_MBOOK_SIDE
{
   MBOOK_ASK,                    // 卖出方
   MBOOK_BID                     // 买入方
};

现在让我们介绍getdevationbyvol方法的源代码:

//+------------------------------------------------------------------+
//| 获取交易量的偏离值。返回 -1.0 如果偏离是                              |
//| 无限 (流动性不足)                                                  |
//+------------------------------------------------------------------+
double CMarketBook::GetDeviationByVol(long vol, ENUM_MBOOK_SIDE side)
{
   int best_ask = InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   int last_ask = InfoGetInteger(MBOOK_LAST_ASK_INDEX); 
   int best_bid = InfoGetInteger(MBOOK_BEST_BID_INDEX);
   int last_bid = InfoGetInteger(MBOOK_LAST_BID_INDEX);
   double avrg_price = 0.0;
   long volume_exe = vol;
   if(side == MBOOK_ASK)
   {
      for(int i = best_ask; i >= last_ask; i--)
      {
         long currVol = MarketBook[i].volume < volume_exe ?
                        MarketBook[i].volume : volume_exe ;   
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe <= 0)break;
      }
   }
   else
   {
      for(int i = best_bid; i <= last_bid; i++)
      {
         long currVol = MarketBook[i].volume < volume_exe ?
                        MarketBook[i].volume : volume_exe ;   
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe <= 0)break;
      }
   }
   if(volume_exe > 0)
      return -1.0;
   avrg_price/= (double)vol;
   double deviation = 0.0;
   if(side == MBOOK_ASK)
      deviation = avrg_price - MarketBook[best_ask].price;
   else
      deviation = MarketBook[best_bid].price - avrg_price;
   return deviation;
}

正如您所看到的,代码的数量是非常重要的,但是计算原理实际上并不复杂。首先,市场深度迭代从最好的方向执行到最差的价格。在每个方向的相关侧执行迭代。在迭代过程中,当前事务量被添加到总事务量中。如果调用了总事务量并与所需的事务量匹配,那么它将退出循环。然后计算给定数量的平均入门价。最后,计算出平均入门价与最佳购销价格之间的差额。差异的绝对值是评估的滑动点。

这个方法需要计算dom的直接迭代。虽然只有一半的DOM迭代是部分的,而大多数时间是部分的,但是这种计算比通常的DOM索引计算花费的时间要长。因此,这种计算是直接用一种单独的方法来实现的,并在需要时进行,也就是说,只有在需要获得清晰的信息形式时才进行。

nbsp;

2.5。用于操作cmarketbook类的例程

所以,我们已经介绍了cmarketbook类的基本方法,现在是实现它们的时候了。我们的测试示例非常容易理解,即使对于编程初学者也是如此。让我们编写一个测试EA,它执行基于DOM的一次性信息输出。当然,为这个目的编写脚本更合适,但是通过脚本访问市场深度是不可能的,只使用EA或度量标准。我们的EA源代码如下:

//+------------------------------------------------------------------+
//|                                               TestMarketBook.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade/MarketBook.mqh>     //  包含 CMarketBook 类
CMarketBook Book(Symbol());         // 以当前金融工具初始化类

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   PrintMbookInfo();
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| 打印 MarketBook 信息                                              |
//+------------------------------------------------------------------+
void PrintMbookInfo()
  {
   Book.Refresh();                  // 更新市场深度状态。
   /* 获取基础统计 */
   int total=Book.InfoGetInteger(MBOOK_DEPTH_TOTAL);
   int total_ask = Book.InfoGetInteger(MBOOK_DEPTH_ASK);
   int total_bid = Book.InfoGetInteger(MBOOK_DEPTH_BID);
   int best_ask = Book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   int best_bid = Book.InfoGetInteger(MBOOK_BEST_BID_INDEX);

   printf("市场深度总数: "+(string)total);
   printf("卖出价位数量: "+(string)total_ask);
   printf("买入价位数量: "+(string)total_bid);
   printf("最佳卖出价索引: "+(string)best_ask);
   printf("最佳买入价索引: "+(string)best_bid);
   
   double best_ask_price = Book.InfoGetDouble(MBOOK_BEST_ASK_PRICE);
   double best_bid_price = Book.InfoGetDouble(MBOOK_BEST_BID_PRICE);
   double last_ask = Book.InfoGetDouble(MBOOK_LAST_ASK_PRICE);
   double last_bid = Book.InfoGetDouble(MBOOK_LAST_BID_PRICE);
   double avrg_spread = Book.InfoGetDouble(MBOOK_AVERAGE_SPREAD);
   
   printf("最佳卖出价: " + DoubleToString(best_ask_price, Digits()));
   printf("最佳买入价: " + DoubleToString(best_bid_price, Digits()));
   printf("最坏卖出价: " + DoubleToString(last_ask, Digits()));
   printf("最坏买入价: " + DoubleToString(last_bid, Digits()));
   printf("平均点差: " + DoubleToString(avrg_spread, Digits()));
  }
//+------------------------------------------------------------------+

在OFZ2图表上运行此测试EA时,我们可以得到以下报告:

2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   平均点差: 70
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   最坏买入价: 9831
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   最坏卖出价: 9999
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   最佳买入价: 9840
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   最佳卖出价: 9910
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   最佳买入价索引: 7
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   最佳卖出价索引: 6
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   买入价位数量: 2
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   卖出价位数量: 7
2015.06.16 17:13:23.482 TestMarketBook (OFZ2-9.15,D1)   市场深度总数: 9

让我们将获得的金融工具报告与DOM屏幕截图进行比较:

图4。运行测试报告时OFZ2的市场深度

我们已经确认所收到的指数和价格完全符合当前的市场深度。nbsp;

nbsp;

第3章。写下你自己的市场深度作为面板指示器

3.1。设计市场深度面板和创建指标的一般原则

通过访问cmarketbook类,您可以相对简单地创建一个特殊面板,直接在图形上显示当前市场深度。我们将根据用户指标创建面板。指标的选择基于这样一个事实:每个图表只能有一个EA,并且指标可以是无限的。如果我们使用EA作为小组的基础,就不可能再在同一个图表上与EA进行交易,这将非常不方便。

我们将提供的市场深度能够在图表上显示和隐藏,因为对于每个图表,它仅作为标准交易面板实施。我们将使用相同的按钮来显示或隐藏它:

和NBSP;

图5。MetaTrader 5中的标准交易面板

我们的市场深度将放在图表的左上角,与交易面板的位置相同。这是因为,事实上,市场深度指数所在的图表上已经启动了交易EA。为了不遮挡右上角的图标,我们将面板移到了左侧。

创建指标时,需要使用OnCalculate,这是两个系统功能之一。因为我们的面板不使用从这些函数接收到的信息,所以我们将保持这些函数为空。此外,指示器不会使用任何图形系列,因此在这种情况下,“指示器绘图”属性将为零。

OnBookEvent系统功能将是我们的指标使用的主要功能,因此我们需要签署当前的图表品种,以便接收相关的市场深度变化信息。我们将使用著名的marketbookadd功能订阅。

市场深度小组将以一个特殊的cbookpanel类的形式实施。现在,如果没有关于此类的进一步详细信息,我们将提供原始度量文件代码:

//+------------------------------------------------------------------+
//|                                                   MarketBook.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0
#include <Trade/MarketBook.mqh>
#include "MBookPanel.mqh"

CBookPanel Panel;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 指标缓存区映射
   MarketBookAdd(Symbol());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| MarketBook 变化事件                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
   Panel.Refresh();
  }
//+------------------------------------------------------------------+
//| 图表事件                                                          |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // 事件表示符
                  const long& lparam,   // 长整形事件参数
                  const double& dparam, // 双精度形事件参数
                  const string& sparam) // 字符串形事件参数
  {
   Panel.Event(id,lparam,dparam,sparam);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| 自定义指标迭代函数                                                 |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---

//--- 返回 prev_calculated 值用于下次调用
   return(rates_total);
  }
//+------------------------------------------------------------------+

现在cbookpanel类只包含其操作的基本元素:您单击市场深度中出现的箭头,以及“marketbook”标记来签署我们未来的dom。运行度量时,图表如下所示:

和NBSP;

图6。市场手册面板在图表上的位置

此类中的每个元素都是从基类cnode派生的单独类。这个类包含一些基本方法,如show和hide,这些方法可以在子类中被重写。cnode类还为每个实例生成一个唯一的名称,这使得使用标准函数创建图形对象和设置其属性更加容易。

nbsp;

3.2。点击事件处理,创建市场深度表

目前,我们的指标无法响应点击箭头,所以我们将继续这项工作。对于这个面板,我们需要做的第一件事是输入onchartevent事件处理程序。我们将此方法称为事件。它将获取onchartevent中的参数。此外,我们将扩展cnode基类,为它提供一个carrayobj数组,该数组将包含其他cnode类型的图形元素。稍后它将帮助我们创建更多相同类型的元素-市场深度单位。

现在,我们将为cBookpanel类及其父cnode提供源代码:

//+------------------------------------------------------------------+
//|                                                   MBookPanel.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Trade/MarketBook.mqh>
#include "Node.mqh"
#include "MBookText.mqh"
#include "MBookFon.mqh"
//+------------------------------------------------------------------+
//| CBookPanel 类                                                    |
//+------------------------------------------------------------------+
class CBookPanel : CNode
  {
private:
   CMarketBook       m_book;
   bool              m_showed;
   CBookText         m_text;
public:
   CBookPanel();
   ~CBookPanel();
   void              Refresh();
   virtual void Event(int id, long lparam, double dparam, string sparam);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CBookPanel::CBookPanel()
{
   m_elements.Add(new CBookFon(GetPointer(m_book)));
   ObjectCreate(ChartID(), m_name, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XDISTANCE, 70);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, -3);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_COLOR, clrBlack);
   ObjectSetString(ChartID(), m_name, OBJPROP_FONT, "Webdings");
   ObjectSetString(ChartID(), m_name, OBJPROP_TEXT, CharToString(0x36));
}
CBookPanel::~CBookPanel(void)
{
   OnHide();
   m_text.Hide();
   ObjectDelete(ChartID(), m_name);
}

CBookPanel::Refresh(void)
{

}

CBookPanel::Event(int id, long lparam, double dparam, string sparam)
{
   switch(id)
   {
      case CHARTEVENT_OBJECT_CLICK:
      {
         if(sparam != m_name)return;
         if(!m_showed)OnShow();        
         else OnHide();
         m_showed = !m_showed;
      }
   }
}
//+------------------------------------------------------------------+

更新DOM状态的刷新方法不完整。我们稍后再创建。目前的功能已经被证明是我们市场深度的第一个原型。到目前为止,单击箭头时,仅显示标准灰色画布。再次单击时,它将消失:

和NBSP;

图7。市场深度

市场的深度似乎不令人信服,但我们将继续改善。

nbsp;

3.3。市场深度单位。

单元格创建市场深度的基础是每个单元格都是一个包含有关数量或价格信息的表元素。此外,还可以通过颜色来区分单元:采购限额为蓝色,销售限额为粉色。每个市场深度的单位数量各不相同,因此需要根据需要动态创建所有单位,并存储在一个特殊的数据容器carrayobj中。因为所有单元,不管它们显示什么,都有相同的大小和类型,所以类中实现的单元类型是相同的。

一个特殊的cbookceil类将用于显示数量和价格的单位。创建此类的对象时,将指定单元类型,因此每个类实例都将从市场的深度知道它应该显示哪些必要的信息,以及应该在哪些颜色上绘制背景。cbookceil将使用两个原语:文本标签obj_-text_标签和矩形标签obj_-rectanble_标签。第一个显示文本,第二个显示实际市场深度单位。

这是cBookceil类的源代码:

//+------------------------------------------------------------------+
//|                                                   MBookPanel.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include "Node.mqh"
#include <Trade/MarketBook.mqh>
#include "Node.mqh"
#include "MBookText.mqh"

#define BOOK_PRICE 0
#define BOOK_VOLUME 1

class CBookCeil : public CNode
{
private:
   long  m_ydist;
   long  m_xdist;
   int   m_index;
   int m_ceil_type;
   CBookText m_text;
   CMarketBook* m_book;
public:
   CBookCeil(int type, long x_dist, long y_dist, int index_mbook, CMarketBook* book);
   virtual void Show();
   virtual void Hide();
   virtual void Refresh();
   
};

CBookCeil::CBookCeil(int type, long x_dist, long y_dist, int index_mbook, CMarketBook* book)
{
   m_ydist = y_dist;
   m_xdist = x_dist;
   m_index = index_mbook;
   m_book = book;
   m_ceil_type = type;
}

void CBookCeil::Show()
{
   ObjectCreate(ChartID(), m_name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XDISTANCE, m_xdist);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_FONTSIZE, 9);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   m_text.Show();
   m_text.SetXDist(m_xdist+10);
   m_text.SetYDist(m_ydist+2);
   Refresh();
}

void CBookCeil::Refresh(void)
{
   ENUM_BOOK_TYPE type = m_book.MarketBook[m_index].type;
   if(type == BOOK_TYPE_BUY || type == BOOK_TYPE_BUY_MARKET)
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrCornflowerBlue);
   else if(type == BOOK_TYPE_SELL || type == BOOK_TYPE_SELL_MARKET)
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrPink);
   else
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrWhite);
   MqlBookInfo info = m_book.MarketBook[m_index];
   if(m_ceil_type == BOOK_PRICE)
      m_text.SetText(DoubleToString(info.price, Digits()));
   else if(m_ceil_type == BOOK_VOLUME)
      m_text.SetText((string)info.volume);
}

void CBookCeil::Hide(void)
{
   OnHide();
   m_text.Hide();
   ObjectDelete(ChartID(),m_name);
}

此类的主要操作是使用show和refresh方法执行的。后者根据发送的单位类型,根据其相应的颜色进行着色,并显示其中交易的数量或价格。要创建单元格,必须指定其类型、X轴上的位置、Y轴上的位置、与单元格相对应的DOM索引以及单位市场深度的来源信息。

一个特殊的私有方法createCeils将在类中创建单元来实现一个dom基板。以下是源代码:

void CBookFon::CreateCeils()
{
   int total = m_book.InfoGetInteger(MBOOK_DEPTH_TOTAL);
   for(int i = 0; i < total; i++)
   {
      CBookCeil* Ceil = new CBookCeil(0, 12, i*15+20, i, m_book);
      CBookCeil* CeilVol = new CBookCeil(1, 63, i*15+20, i, m_book);
      m_elements.Add(Ceil);
      m_elements.Add(CeilVol);
      Ceil.Show();
      CeilVol.Show();
   }
}

点击箭头可以调用它来扩大市场的深度。

现在一切都准备好创造我们新版本的市场深度。在修改和编译项目之后,我们的度量标准获得了一种新形式:

和NBSP;

图式。8。第一版市场深度指数

3.4。交易量柱状图显示市场深度

获得的市场深度执行了基本功能——它显示了交易价格、交易量以及买卖限额的价格。因此,市场深度的每一次变化也可以改变相应的单位价值。但是,通过视觉跟踪所获得表中的事务量并不容易。例如,metatrader 5中的标准dom交易量显示在柱状图的背景中,即当前市场深度中最大交易量的相对大小。此外,这不应妨碍我们在市场深度上实现类似的功能。

有不同的方法来解决这个问题。最简单的解决方案是直接在cbookceil类中进行所有必要的计算。因此,它需要在其刷新方法中写入以下内容:

void CBookCeil::Refresh(void)
{
   ...
   MqlBookInfo info = m_book.MarketBook[m_index];
   ...
   //更新市场深度直方条
   int begin = m_book.InfoGetInteger(MBOOK_LAST_ASK_INDEX);
   int end = m_book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   long max_volume = 0;
   if(m_ceil_type != BOOK_VOLUME)return;
   for(int i = begin; i < end; i++)
   {
      if(m_book.MarketBook[i].volume > max_volume)
         max_volume = m_book.MarketBook[i].volume;
   }
   double delta = 1.0;
   if(max_volume > 0)
      delta = (info.volume/(double)max_volume);
   long size = (long)(delta * 50.0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XSIZE, size);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
}

在完整的dom迭代方法中,有一个最大的dom事务量,然后将当前事务量除以最大分段。所得股份乘以交易表单位的最大宽度(以50像素的常量表示)。画布获得的宽度是所需的柱状图:

和NBSP;

图9。市场深度与成交量柱状图

但是,此代码的问题是,每次调用刷新时,每个单元格中的DOM迭代都会完成。对于具有40个元素的市场深度,这意味着每个市场深度更新将执行800次迭代。每个单元只在自己的市场上迭代,因此每个单元中的迭代由20个迭代组成(市场深度除以一半)。现代计算机处理这类任务的效率非常低,尤其是考虑到需要用最快和最有效的算法来操纵市场的深度。

nbsp;

3.5。市场深度最大交易量的快速计算与迭代优化

不幸的是,它不可能完全消除市场深度的迭代。每次市场深度刷新后,最大交易量和价格都可能发生剧烈变化。但是,我们可以最小化迭代次数。为此,您应该理解,在两个刷新调用之间,DOM迭代不会超过一次。您需要做的第二件事是最小化完成迭代的调用次数。为此,您必须使用延迟计算,或者换句话说,只有在明确需要时才使用。我们将所有的计算结果直接转移到cmarketbook market depth类,并在cmarketbook中编写一个特殊的计算子类cbookcalculation。请参见以下源代码:

class CMarketBook;

class CBookCalculation
{
private:
   int m_max_ask_index;         // 最大卖出交易量索引
   long m_max_ask_volume;       // 最大卖出交易量
   
   int m_max_bid_index;         // 最大买入交易量索引
   long m_max_bid_volume;       // 最大买入交易量
   
   long m_sum_ask_volume;       // DOM 中卖出交易量总数
   long m_sum_bid_volume;       // DOM 中买入交易量总数
   
   bool m_calculation;          // 所有计算执行完毕的指示标志
   CMarketBook* m_book;         // 市场深度指标
   
   void Calculation(void)
   {
      // 对于卖方
      int begin = (int)m_book.InfoGetInteger(MBOOK_LAST_ASK_INDEX);
      int end = (int)m_book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);
      for(int i = begin; i < end; i++)
      {
         if(m_book.MarketBook[i].volume > m_max_ask_volume)
         {
            m_max_ask_index = i;
            m_max_ask_volume = m_book.MarketBook[i].volume;
         }
         m_sum_ask_volume += m_book.MarketBook[i].volume;
      }
      // 对于买方
      begin = (int)m_book.InfoGetInteger(MBOOK_BEST_BID_INDEX);
      end = (int)m_book.InfoGetInteger(MBOOK_LAST_BID_INDEX);
      for(int i = begin; i < end; i++)
      {
         if(m_book.MarketBook[i].volume > m_max_bid_volume)
         {
            m_max_bid_index = i;
            m_max_bid_volume = m_book.MarketBook[i].volume;
         }
         m_sum_bid_volume += m_book.MarketBook[i].volume;
      }
      m_calculation = true;
   }
   
public:
   CBookCalculation(CMarketBook* book)
   {
      Reset();
      m_book = book;
   }
   
   void Reset()
   {
      m_max_ask_volume = 0.0;
      m_max_bid_volume = 0.0;
      m_max_ask_index = -1;
      m_max_bid_index = -1;
      m_sum_ask_volume = 0;
      m_sum_bid_volume = 0;
      m_calculation = false;
   }
   int GetMaxVolAskIndex()
   {
      if(!m_calculation)
         Calculation();
      return m_max_ask_index;
   }
   
   long GetMaxVolAsk()
   {
      if(!m_calculation)
         Calculation();
      return m_max_ask_volume;
   }
   int GetMaxVolBidIndex()
   {
      if(!m_calculation)
         Calculation();
      return m_max_bid_index;
   }
   
   long GetMaxVolBid()
   {
      if(!m_calculation)
         Calculation();
      return m_max_bid_volume;
   }
   long GetAskVolTotal()
   {
      if(!m_calculation)
         Calculation();
      return m_sum_ask_volume;
   }
   long GetBidVolTotal()
   {
      if(!m_calculation)
         Calculation();
      return m_sum_bid_volume;
   }
};

所有市场深度迭代和资源密集型计算都隐藏在Calculate的私有方法中。只有当评估标志m_calculate重置为false条件时,才会调用它。重置标志只出现在重置方法中。因为这个类是为操作cmarketbook类而设计的,所以只有这个类可以访问它。

市场深度刷新方法刷新后,cmarketbook类调用reese方法重置计算模块的条件。因为在两次刷新之间,市场深度的完整迭代不会超过一次。还使用预订执行。换句话说,只有当从六个公共可用方法之一显式调用cBookCalCultae类时,才调用它的calculate方法。

除了寻找交易量之外,执行市场深度、完整迭代的类还添加了一个包含买卖配额总数的字段。不需要额外的时间来计算这些参数,因为已经计算了数组的总周期。

现在,传统的市场深度迭代被智能按需迭代所取代。这大大减少了资源的使用,使市场运作的深度非常快和有效。

nbsp;

收盘:成交量柱状图和分界线

我们几乎完成了创建指标的任务。查询最大交易量的实际需要帮助我们创建了一种计算经济指标的有效方法。如果将来,我们需要增加新的计算参数到我们的市场深度,这将很容易做到。为此,只需扩展我们的cBookCalculate类,添加相关的方法,并输入相应的修饰符enum mbook_info_integer和enum_mbook_info_double enumeration。

现在,我们必须利用我们所做的,为每个单元重写刷新方法:

void CBookCeil::Refresh(void)
{
   ENUM_BOOK_TYPE type = m_book.MarketBook[m_index].type;
   long max_volume = 0;
   if(type == BOOK_TYPE_BUY || type == BOOK_TYPE_BUY_MARKET)
   {
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrCornflowerBlue);
      max_volume = m_book.InfoGetInteger(MBOOK_MAX_BID_VOLUME);
   }
   else if(type == BOOK_TYPE_SELL || type == BOOK_TYPE_SELL_MARKET)
   {
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrPink);
      max_volume = m_book.InfoGetInteger(MBOOK_MAX_ASK_VOLUME); //交易量之前已计算, 不会发生循环重复出现
   }
   else
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrWhite);
   MqlBookInfo info = m_book.MarketBook[m_index];
   if(m_ceil_type == BOOK_PRICE)
      m_text.SetText(DoubleToString(info.price, Digits()));
   else if(m_ceil_type == BOOK_VOLUME)
      m_text.SetText((string)info.volume);
   if(m_ceil_type != BOOK_VOLUME)return;
   double delta = 1.0;
   if(max_volume > 0)
      delta = (info.volume/(double)max_volume);
   long size = (long)(delta * 50.0);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_XSIZE, size);
   ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
}

显然,我们的面板指示器的工作方式与以前的版本相同,但实际上,柱状图的计算速度显著增加。编程的艺术是什么?它是关于创建高效且易于使用的算法,隐藏在相应模块(类)的私有方法中实现这些算法的复杂逻辑。

随着交易量柱状图的出现,买卖量的界限变得非常模糊。因此,我们希望在cbookpanel类中创建这一行,该类由cbookline的特殊子类实现:

class CBookLine : public CNode
{
private:
   long m_ydist;
public:
   CBookLine(long y){m_ydist = y;}
   virtual void Show()
   {
      ObjectCreate(ChartID(),     m_name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_YDISTANCE, m_ydist);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_XDISTANCE, 13);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_YSIZE, 3);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_XSIZE, 108);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_COLOR, clrBlack);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BGCOLOR, clrBlack);
      ObjectSetInteger(ChartID(), m_name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   }
};

这是一个非常简单的类,基本上只是为了确定它的位置。在显示方法中创建该线时,必须计算该线的Y轴位置。了解最畅销价格的指数相对容易做到这一点:

long best_bid = m_book.InfoGetInteger(MBOOK_BEST_BID_INDEX);
long y = best_bid*15+19;

在这种情况下,最佳单元格的索引乘以每个单元格的宽度(15像素),再加上19像素的附加常量。

我们的市场深度终于有了最小的外观和功能,获得了愉快的体验。当然,还有很多事情要做。如有必要,我们的指标可以更接近标准的metatrader 5市场深度函数。

然而,这并不是本文的主要目的。创建市场深度面板的唯一目的是演示cmarketbook类的可行性。它有助于使这个类更快、更好、更强大,从而完全实现其目标。我们会给你看一个短片,展示我们迄今为止所做的所有工作。下面是我们的DOM动态面板:

0

3.7。添加有关在DOM中交易的金融工具的价格限制订单总数的属性信息

莫斯科交易所的一个显著特点是传输限价订单总数的实时信息。本文的重点是对市场的深度操纵,而不是任何特定的市场。但是,尽管这些信息是特定的(特定于特定的交易平台),但在终端的系统级别上仍然可用。此外,市场深度还提供了扩展数据。这取决于属性修饰符枚举的扩展,包括直接在市场深度cmarketbook类中支持这些属性。

莫斯科证券交易所提供以下实时信息:

  • 当前对金融工具下达的限售指令数量;
  • 当前对金融工具设定的购买价格限制数;
  • 金融工具当前下达的销售配额交易总数;
  • 当前对金融工具下达的限购订单总额;
  • 不公平合同的开立和数量(仅适用于期货市场)。

不均衡合约与市场中的限价指令数量(如当前流动性)没有直接关系。然而,这些信息通常需要与相关的限价单信息结合在一起,因此通过cmarketbook类访问它似乎是合适的。要访问此信息,必须使用symbolNFointeger和symbolNFouple函数。但是,为了从单个位置访问数据,我们将在infogetinteger和infogetdouble函数中引入更多的枚举和更改,以扩展我们的市场深度类:

long CMarketBook::InfoGetInteger(ENUM_MBOOK_INFO_INTEGER property)
{
   switch(property)
   {
      ...
      case MBOOK_BUY_ORDERS:
         return SymbolInfoInteger(m_symbol, SYMBOL_SESSION_BUY_ORDERS);
      case MBOOK_SELL_ORDERS:
         return SymbolInfoInteger(m_symbol, SYMBOL_SESSION_SELL_ORDERS);
      ...
   }
   return 0;
}

nbsp;

double CMarketBook::InfoGetDouble(ENUM_MBOOK_INFO_DOUBLE property)
{
   switch(property)
   {
      ...
      case MBOOK_BUY_ORDERS_VOLUME:
         return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_BUY_ORDERS_VOLUME);
      case MBOOK_SELL_ORDERS_VOLUME:
         return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_SELL_ORDERS_VOLUME);
      case MBOOK_OPEN_INTEREST:
         return SymbolInfoDouble(m_symbol, SYMBOL_SESSION_INTEREST);
   }
   return 0.0;  
}

如您所见,代码非常简单。实际上,它重复了MQL的标准功能。但是,将其添加到cmarketbook类的目的是为用户提供方便和集中的模块来访问限价信息及其价格。

第4章。cmarketbook类的文档

我们已经完成了描述并创建了一个控制市场深度的cmarketbook类。第4章包含了其常用方法的文档。使用这些文档,类操作变得简单,即使对于初学者也是如此。此外,本章还可以很容易地用作类操作的小指南。

4.1。从市场深度获取基本信息及其操作方法

刷新()方法

它刷新了市场深度条件。它还需要为调用的每个OnBookEvent系统事件调用此方法(市场深度已更改)。

void        Refresh(void);

使用

在第四章的相关章节中查找用法。

nbsp;

infoGetInteger()方法

返回与枚举“mbook”信息“integer”修饰符对应的market depth属性。支持函数的完整列表可以在枚举“mbook”信息“integer”列表中找到。

long        InfoGetInteger(ENUM_MBOOK_INFO_INTEGER property);

返回值

长形市场深度属性的整数值。如果失败,返回-1。

使用

在第四章的相关章节中查找使用例程。

nbsp;

InfoGetDouble()方法

返回与枚举“mbook”信息“double”修饰符对应的market depth属性。支持功能的完整列表可以在Enum-mbook-info-double列表中找到。

double      InfoGetDouble(ENUM_MBOOK_INFO_DOUBLE property);

返回值

市场深度属性的双精度值。失败时返回-1.0。

使用

在第四章的相关章节中查找使用例程。

nbsp;

Isavailable()方法

如果市场深度信息可用,则返回true,否则返回false。在操纵Market Depth类以检查相应的信息类型之前,必须调用此方法。

bool        IsAvailable(void);

返回值

如果市场深度可以用于进一步的运营,则为真,否则为假。

nbsp;

setmarketbook符号()方法

建立品种,市场深度即将经营品种。您还可以在创建cmarketbook类实例时设置市场深度的多样性,并指定在构造函数中使用的产品的名称。

bool        SetMarketBookSymbol(string symbol);

返回值

如果品种可以交易,则为真,否则为假。

nbsp;

GetMarketBook符号()方法

返回金融工具的名称、类的当前实例以及各种市场深度操作。nbsp;

string      GetMarketBookSymbol(void);

返回值

类的当前实例显示了市场深度操作的多样性。如果未选择或不可用金融工具,则为空。

nbsp;

GetDeviationByVol()方法

回到市场时市场价格可能出现的滑动点。此值只是一个评估。如果市场深度在进入时发生了变化,则所获得的滑动点可能与之前的函数计算不同。但是,此函数提供了一个相当精确的评估滑动点,可以在输入时用作附加信息的来源。

该方法有两个参数:意向交易金额和完成交易所用流动性类型的迭代指示。例如,销售限额的流动性将用于购买,在这种情况下,mbook_ask类型将是指定方。出售时,将使用mbook_bid作为指示。有关详细信息,请阅读枚举薄边枚举。

double     GetDeviationByVol(long vol, ENUM_MBOOK_SIDE side);

参数:

  • [输入]卷-有意交易量;
  • [输入]一方-进行交易的市场深度。nbsp;

返回值

金融工具的潜在滑动点。

nbsp;

4.2。CmarketBook类的枚举和修饰符

枚举枚举枚举方

枚举薄边枚举包含流动性类型的指示器修饰符。枚举和字段描述列表如下:

领域 描述
MBONKY问答 &指示性流动性由卖出限额提供。
MBooKo.BID &指示性流动性由购买限额提供。

注意事项;

每个市场价格表都可以执行一个价格限制。根据订单的指示,将使用购买或销售限制。在买入交易下方将有一个或多个销售配额。在销售交易下方将有一个或多个采购限额订单。通过这种方式,修改者可以表示市场深度的两个部分之一:买方或卖方。修改器由getdevationbyvol函数使用,您在操作时需要知道的只是希望市场交易使用哪一方的流动性。

nbsp;

枚举枚举枚举

枚举枚举枚举“”mbook“”info“”integer包含必须使用infogetinteger方法获取的属性修饰符。枚举和字段描述列表如下:

领域 描述
mbook_最佳问答索引 &最佳销售价格指数
mbook_Best_Bid_指数 &最佳购买价格指数
mbook_last_ask_索引 &上次最差销售指数
mbook-last-bid-u指数 &上次最差投标指数
MBONKY-DEPTHASK问答 &卖方市场深度或总交易水平
MBONKY-DEP &买方市场深度或总交易水平
mbook_深度总计 &市场总深度或买卖数量
mbook_max_ask_音量 &最大销售量
mbook_max_ask_volume_索引 &最大销售量指数
mbook_最大投标量 &最大购买量
mbook_max_bid_volume_索引 &最大买入交易量指数
mbook-ask-volume-总量 &市场深度目前可获得的销售配额总数
mbook出价总量 &市场深度当前可用的采购限额订单总数
mbook_购买订单 &目前股票市场上可用的采购限额订单总数
mbook_销售订单 &目前股票市场上可用的限价指令总数

nbsp;

枚举枚举枚举

枚举枚举枚举包含必须使用InfoGetDouble方法获取的属性修饰符。枚举和字段描述列表如下:

领域 描述
mbook_最佳询问价 &最佳销售价格
mbook_最佳出价 &最佳购买价格
最后一次问价 &最低价或最低价
最后出价 &最差或最后一次购买价格
mbook_平均值 &最佳买入价和最佳卖出价之间的平均差或点差。
mbook-open-利息 &不平衡合同
mbook_购买订单量 &采购数量
mbook_销售订单量 &销售订单数量

nbsp;

4.3。使用cmarketbook类的例程

这个例程包含组成EA的源代码部分。它显示了市场深度的基本信息:

//+------------------------------------------------------------------+
//|                                               TestMarketBook.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade/MarketBook.mqh>     //  包含 CMarketBook 类
CMarketBook Book(Symbol());         // 以当前金融工具初始化类

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   PrintMbookInfo();
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| 打印 MarketBook 信息                                              |
//+------------------------------------------------------------------+
void PrintMbookInfo()
  {
   Book.Refresh();                                                   // 更新市场深度状态。
//--- 获取主要的整数型统计值
   int total=(int)Book.InfoGetInteger(MBOOK_DEPTH_TOTAL);            // 获取市场深度总数
   int total_ask = (int)Book.InfoGetInteger(MBOOK_DEPTH_ASK);        // 获取卖出级别数量
   int total_bid = (int)Book.InfoGetInteger(MBOOK_DEPTH_BID);        // 获取买入级别数量
   int best_ask = (int)Book.InfoGetInteger(MBOOK_BEST_ASK_INDEX);    // 获取最佳卖出价索引
   int best_bid = (int)Book.InfoGetInteger(MBOOK_BEST_BID_INDEX);    // 获取最佳买入价索引

//--- 显示基本统计
   printf("市场深度总数: "+(string)total);
   printf("卖出价位数量: "+(string)total_ask);
   printf("买入价位数量: "+(string)total_bid);
   printf("最佳卖出价索引: "+(string)best_ask);
   printf("最佳买入价索引: "+(string)best_bid);
   
//--- 获取主要的双精度型统计值
   double best_ask_price = Book.InfoGetDouble(MBOOK_BEST_ASK_PRICE); // 获取最佳卖出价
   double best_bid_price = Book.InfoGetDouble(MBOOK_BEST_BID_PRICE); // 获取最佳买入价
   double last_ask = Book.InfoGetDouble(MBOOK_LAST_ASK_PRICE);       // 获取最坏卖出价
   double last_bid = Book.InfoGetDouble(MBOOK_LAST_BID_PRICE);       // 获取最坏买入价
   double avrg_spread = Book.InfoGetDouble(MBOOK_AVERAGE_SPREAD);    // 获取操纵市场深度期间的平均点差
   
//--- 显示价格和点差
   printf("最佳卖出价: " + DoubleToString(best_ask_price, Digits()));
   printf("最佳买入价: " + DoubleToString(best_bid_price, Digits()));
   printf("最坏卖出价: " + DoubleToString(last_ask, Digits()));
   printf("最坏买入价: " + DoubleToString(last_bid, Digits()));
   printf("平均点差: " + DoubleToString(avrg_spread, Digits()));
  }
//+------------------------------------------------------------------+

nbsp;

结论

本文的发展是动态的。我们从技术角度分析了市场的深度,提出了一种高性能的类容器来操纵它。作为一个例子,我们已经创建了一个基于这种容器的市场深度指数,它可以紧凑地显示在金融工具的价格表上。

我们的市场深度指数是非常基本的。它仍然缺少一些东西。然而,我们的主要目标已经实现——我们相信,通过我们创建的市场账簿类别,我们可以相对快速地构建复杂的EA和指标,以分析金融工具的当前流动性。在设计cmarketbook类时,人们非常关注性能,因为市场深度有一个非常动态的表,每分钟变化数百次。

本文所述的类别可以为您的头皮剥离或高频系统奠定坚实的基础。您可以随意向系统添加特定功能。为此,只需从cmarketbook派生市场深度类,并编写所需的扩展方法。希望市场深度的基本属性能使您的工作更容易、更可靠。

本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/1793。

附加文件下载zip mql5.zip(202.33 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自网络,不代表迈投财经立场,转载请注明出处:https://www.myfxtop.cn/ea/5897.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: myfxtop@hotmail.com

9:30 - 18:00/Mon-Fri
返回顶部