外汇EA编写教程:如何在莫斯科交易所安全使用您的EA

目录

  • 介绍
  • 第1章。价格流的离散性及如何面对
    • 1.1。价格流动的离散性。价格差距
    • 1.2。价格尖峰
    • 1.3。使用限制顺序管理最大滑动点
    • 1.4。限价管理最大滑动点手动设置
    • 1.5。使用EA时设置事务执行模式的最大滑动点
    • 1.6。高限和低限销售订单代替高限和低限采购订单
    • 1.7。手工设定高限价采购和低限价销售订单而不是止损
    • 1.8。用EA中的高限额采购和低限额销售订单替换止损
  • 第2章。市场流动性分析
    • 2.1。进入竞技场前计算滑动点
    • 2.2。实时电位计算滑动点
    • 2.3。使用spreadrecord点差异索引作为入口过滤器
    • 2.4。利差大幅上升时对手工和自动交易的限制
  • 第3章。安全事务和EA测试模式
    • 3.1。将分时控制替换为“睡眠模式”
    • 3.2。基于移动平均和每周事务逻辑的EA例程
    • 3.3。用完整的列模型代替分时价格测试EA
  • 结论

介绍

所有在金融市场交易的人都有经济损失的风险。这些风险的性质是不同的,但结果是一样的——赔钱、浪费时间和持久的沮丧。为了避免这些不愉快的事情,我们应该遵循一些简单的规则:管理我们的风险(资金管理),开发可靠的交易算法,并使用有利可图的交易系统。这些规则涵盖了不同的交易领域,我们应该将它们结合起来,以期获得可靠的积极的交易结果。

目前,您可以找到大量涉及资本管理问题的书籍和文章,以及可用于日常交易活动的交易系统。不幸的是,根据市场交易安全规则,这些都是不真实的。

本文的目的是通过解释在市场交易时必须遵守的安全规则来改变这种情况。这些规则包括方法和交易执行,可以帮助您避免因价格飙升、缺乏流动性和其他不可抗力造成的巨大经济损失。本文将重点放在技术风险上,不谈交易策略的制定和风险管理。

本文以莫斯科交易所衍生品市场为例,介绍了其交易原则,并以莫斯科交易所衍生品市场定价原则为例,阐述了其交易原则。尽管本文提到了汇率定价原则,但本文描述了当一些危险的交易价格因素导致意外的金融崩溃时保护您和您的EA的机制。

第1章。价格流的离散性及如何面对

1.1。价格流动的离散性。价格差距

流动性是股票市场的主要概念之一。它是市场以最接近的价格向你购买或出售商品的能力。市场流动性越高,追随者的市场价格就越高。定价是一个离散的过程。这意味着我们使用的价格包括许多高速交易。交易流程是由报价或分时图表组成的,这些报价或分时图表随后被重新组织成蜡烛或任意时间框架的柱形图。从交易者的角度来看,这些图表是连续的。在任何给定的时间,柱子或蜡烛都有固定的价格。如下所示:

图1。价格列及其连续价格函数

不管我们在哪里估价,它都有自己的价格,比如红线。这正是在元交易者策略测试仪的“使用时间”模型中铅垂线所呈现的。在这个模型中,价格是按顺序连续地生成的。例如,如果步骤大小为1,价格从10移动到15,那么价格11、12、13和14在移动期间也会存在。在实践中,价格是离散的,变化是跳跃式的。此外,这些价格变化可能并不总是一致和规律的。有时,价格可能一次跳过几个级别。让我们看看同一列中更现实(离散)的价格变化:

图2。价格列及其离散价格函数

如我们所见,没有连续价格(如红色短线所示)。这意味着您的市场价格(尤其是止损单)可能会以意外的价格触发!这是市场订单的一个非常危险的特征。让我们看看高端提单是如何在这一行中触发的。假设价格达到64203或更高,我们发送一个市场请求(蓝色短线穿过价格列)。但是,此价格可能根本不存在于列行中。在这种情况下,我们的订单将以远高于64203的下一个价格激活:

图3。订单以离散价格激活

在我们的例子中,实际的订单执行是64220点,比我们要求的价格低13点。这些价格差异造成了不同。如果市场流动性足够,离散价格将在密集的流动中平稳有序地移动。然而,如果价格变化迅速,即使是在高流动性的市场中,仍然存在价格差距。一般价格表上的差距是不可能看到的,但我们应该意识到它们的存在。

1.2。价格尖峰

由于缺乏流动性,价格差距可能会达到很高的价值,导致价格飙升(以远离市场的价格交易)。它们对手工交易者和自动交易系统都是危险的。这样的涨价可能会以非常不利的价格触发提单的暂停。

但让我们设想一个简单的例子:假设我们交易一个卢布/美元期货合同,并将一个高采购订单定为64200。停止损耗设为64100。我们预计价格会上涨,即使没有上涨,我们的止损是64100,将损失限制在100点。我们的风险似乎得到了控制,但事实并非如此。让我们来看看价格飙升。我们的提单是以非常不同的价格激活的:

图4。使用时间图表显示了高端采购订单的峰值和执行情况。

在这个使用时间图表上,我们看到一个使用时间的价格与其他价格相差甚远,形成了一个尖锐的脊梁。这一次的使用价格触发了我们的高采购订单64 440。在下一个TOU价格中,价格返回到当前范围并触发了64100的止损。在不到一秒钟的时间内,我们的提单就会被触发并以止损关闭,从而给我们带来巨大的损失。我们计算的损失不是100分,而是340分。

事实上,峰值可能更大。所以一个巨大的尖峰足以炸毁我们,不管帐户的大小!为了避免这种灾难,您需要遵循下面描述的简单保护规则。

请注意,在Strategy Tester的“购买时间”模型中,这个峰值模拟的价格可能比实际市场中的价格要好。如果我们根据图中所示的价格区间测试我们的策略,我们的提单应该经历一个最小(如果有的话)滑动点。如我们所知,Strategy Tester中一列的价格流是相对连续的,这意味着执行我们订单的测试人员的价格非常接近我们设定的价格,并且几乎没有滑动点。事实上,这种情况也应该在战略测试人员中加以考虑。因此,您应该选择一种特殊的测试模式。我们将在第3章的特别章节中讨论它。nbsp;

1.3。使用限制顺序管理最大滑动点

我们发现市场价格表和止损单并不能保护滑动点。流动性可能不足以满足我们的要求,或者市场在短期内失去流动性,导致价格飙升。此外,在流动性较低的市场,如Forts衍生品中,这种峰值很常见。但是,您可以通过用限价订单代替市价和停止订单来避免这种情况。

限价指令总是按规定的价格执行,不会有高的差价。交易所执行模式限制的一个有趣的特征是,它能够以当前价格执行,即使价格高于或低于指定的订单价格。

例如,如果卢布/美元期货合同的当前价格是64200,我们可以将采购订单限制在64220。这意味着我们同意以不高于64220的价格购买。由于64200的当前价格比订单中的设置好,我们的订单将在下单后立即执行。因此,我们可以管理最大滑动点值。如果由于某种原因,64220的流动性不足,我们的部分订单将无法执行。

请注意,您只能使用价格限制来管理滑动点。在交易所执行模式下,市场价格表通常不允许设置最大滑动点水平。因此,限价指令是维持低流动性市场安全的唯一途径。

合理使用限价进出市场。即使你的策略要求以当前的市场价格进入或退出,你也可以放置它们。将采购订单和销售订单分别替换为限价销售和限价销售。例如,如果您计划以当前价格购买,请以略高于当前市场价格的最高执行价格下限价订单。销售也是如此。在这种情况下,下限价订单时的执行价格略低于当前市场价格。限价订单中设置的价格与当前价格之间的差额是您可以接受的最大滑动点。

我们来看下面的例子。假设我们以1.1356的价格购买了大量的ED-3.15欧元/美元期货合约。目前,流动性非常低。故意选择这个时刻来表明使用价格限制的好处。我们带来了一个价格飙升,从M1图表可以看出:

图5。价格飙升发生在入口,ed-3.15

很明显,市场的切入点是完全不利的。让我们分析一下TOU图表上的时刻:

数字。6。流动性突破期间的分时图表和限价指令执行

我们的报价单是在一个大的白色圆圈中执行的(分时价格):。分时电价被描绘成一个蓝点。如果我们以市场价格1.1356购买,我们的市场请求将由多个交易填写,从1.1356开始,到1.1398结束。这将导致一个强大的滑动点,我们的平均录取价格将明显低于1.1356。我们需要更多的生意来满足我们的要求,而且门票价格更糟。

在我们的案例中,这种巨大的价格差距是由于流动性低造成的,当限价请求因各种原因消失,价格波动范围混乱。但是价格限制有一个内在的保护。如果当前价格超过1.1356,则完全不执行。例如,我们的限价订单由七个事务执行——它们在图表上以大的白色圆圈显示。这些交易之间还有其他价格,均低于1.1356。所以它们被直接忽略了。一段时间后,价格稳定,我方订单已完全执行。

1.4。限价管理最大滑动点手动设置

既然我们已经涵盖了价格限制的激活原则,现在是时候添加一些实践并在实际市场环境中使用我们的知识了。假设我们的账户与莫斯科交易所有关。让我们以稍微低一点的价格下限价订单。此外,我们最近选择了欧元/美元期货合约(ED-6.15)作为我们的经营品种。打电话到开盘窗口,以略高于当前售价的价格设置价格限制:

图示. 7. 在交易所执行模式手工放置一笔限价订单

图7。在执行交换模式下手动放置限价指令

正如我们在屏幕截图中看到的,目前的销售价格是1.1242,当时我们已经建立了1.1245的提单。我方报价与最优惠报价相差0.0003分(1.1245-1.1242=0.0003)。这是我们将要暴露的最大滑动点。在外汇执行模式下,这种限价指令相当于发送一个滑动点(偏差)最大的普通买卖订单:

图示. 8. 按照指定的偏离执行市价单

图8。根据规定偏差执行市场价格表

由于最大滑动点在交换执行模式中不可用,指定偏差的唯一方法是根据图7所示的方法设置限制顺序。

1.5。使用EA时设置事务执行模式的最大滑动点

现在让我们在程序中设置一个价格限制。为此,我们需要编写一个包含以下元素的简单面板:

  • 购买按钮-限价购买;
  • 销售按钮-限价销售订单;
  • 将稍微添加最大滑动点字段(单位编号)。
  • 在下一个版本的面板中还将添加买卖量。

The following screenshot is the first version of the panel:

0

图9。在偏差面板中设置最大滑动点

面板由cdevpanel类构成。其源代码如下:

//+------------------------------------------------------------------+
//|                                                       Panel.mqh  |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#include <Trade/Trade.mqh>
#define OP_BUY 0
#define OP_SELL 1
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CDevPanel
  {
private:
   CTrade            Trade;
   string            m_descr_dev;
   string            m_buy_button_name;
   string            m_sell_button_name;
   string            m_deviation_name;
   string            m_volume_name;
   string            m_bg_fon;
   int               m_deviation;
   void              OnObjClick(string sparam);
   void              OnEndEdit(string sparam);
   double            CalcCurrentPrice(int op_type);

public:
                     CDevPanel();
                    ~CDevPanel();
   void              OnChartEvent(const int id,
                                  const long &lparam,
                                  const double &dparam,
                                  const string &sparam);
  };
//+------------------------------------------------------------------+
//| CDevPanel 类                                                     |
//+------------------------------------------------------------------+
CDevPanel::CDevPanel(): m_buy_button_name("buy_button"),
                        m_sell_button_name("sell_button"),
                        m_deviation_name("deviation"),
                        m_volume_name("volume"),
                        m_bg_fon("bg_fon"),
                        m_descr_dev("descr_dev"),
                        m_deviation(3)
  {
//--- 背景
   ObjectCreate(0,m_bg_fon,OBJ_RECTANGLE_LABEL,0,0,0);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_YSIZE,80);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_XSIZE,190);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_BGCOLOR,clrWhiteSmoke);

//--- 买入按钮
   ObjectCreate(0,m_buy_button_name,OBJ_BUTTON,0,0,0);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_XDISTANCE,100);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_YDISTANCE,50);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_BGCOLOR,clrAliceBlue);
   ObjectSetString(0,m_buy_button_name,OBJPROP_TEXT,"BUY");

//--- 卖出按钮
   ObjectCreate(0,m_sell_button_name,OBJ_BUTTON,0,0,0);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_XDISTANCE,10);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_YDISTANCE,50);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_BGCOLOR,clrPink);
   ObjectSetString(0,m_sell_button_name,OBJPROP_TEXT,"SELL");

//--- 偏离
   ObjectCreate(0,m_deviation_name,OBJ_EDIT,0,0,0);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_XDISTANCE,120);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_YDISTANCE,20);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_XSIZE,60);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_BGCOLOR,clrWhite);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_COLOR,clrBlack);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_ALIGN,ALIGN_RIGHT);
   ObjectSetString(0,m_deviation_name,OBJPROP_TEXT,(string)m_deviation);

//--- 描述
   ObjectCreate(0,m_descr_dev,OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_XDISTANCE,12);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_YDISTANCE,20);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_BGCOLOR,clrWhite);
   ObjectSetString(0,m_descr_dev,OBJPROP_TEXT,"Deviation (pips):");
   ObjectSetInteger(0,m_descr_dev,OBJPROP_COLOR,clrBlack);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDevPanel::~CDevPanel(void)
  {
   ObjectDelete(0,m_buy_button_name);
   ObjectDelete(0,m_sell_button_name);
   ObjectDelete(0,m_bg_fon);
   ObjectDelete(0,m_deviation_name);
   ObjectDelete(0,m_descr_dev);
  }
//+------------------------------------------------------------------+
//| 事件函数                                                          |
//+------------------------------------------------------------------+
void CDevPanel::OnChartEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   switch(id)
     {
      case CHARTEVENT_OBJECT_CLICK:
         OnObjClick(sparam);
         break;
      case CHARTEVENT_OBJECT_ENDEDIT:
         OnEndEdit(sparam);
     }
  }
//+------------------------------------------------------------------+
//| 编辑检测结束                                                      |
//+------------------------------------------------------------------+
void CDevPanel::OnEndEdit(string sparam)
  {
   if(sparam != m_deviation_name)return;
   int value = (int)ObjectGetString(0, m_deviation_name, OBJPROP_TEXT);
   if(value <= 0)
      ObjectSetString(0,m_deviation_name,OBJPROP_TEXT,(string)m_deviation);
   else
      m_deviation=value;
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| 对象点击结束                                                      |
//+------------------------------------------------------------------+
void CDevPanel::OnObjClick(string sparam)
  {
   if(sparam==m_buy_button_name)
      Trade.BuyLimit(1,CalcCurrentPrice(OP_BUY));
   if(sparam==m_sell_button_name)
      Trade.SellLimit(1,CalcCurrentPrice(OP_SELL));
   ObjectSetInteger(0,sparam,OBJPROP_STATE,false);
   Sleep(100);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| 计算价位                                                          |
//+------------------------------------------------------------------+
double CDevPanel::CalcCurrentPrice(int op_type)
  {
   if(op_type==OP_BUY)
     {
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      return ask + (m_deviation * Point());
     }
   else if(op_type==OP_SELL)
     {
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      return bid - (m_deviation * Point());
     }
   return 0.0;
  }
//+------------------------------------------------------------------+

面板可以设置市场价格执行订单的最大滑动点(单位数)。实际出入境将使用价格限制执行。

只有当经纪人提供执行订单的市场价格时,面板才起作用。否则,当输入价格限制时,代码将导致标准错误价格:

2015.04.15 14:08:39.709 Trades  '58406864': failed buy limit 0.10 EURUSD at 1.05927 [Invalid price]

1.6。高限和低限销售订单代替高限和低限采购订单

限价订单提供了一个方便和自然的防御滑动点。但是,有时需要使用票据,并在它们突破某个级别时触发它们。止损单是最明显的例子。此外,一些策略应该在价格离开既定渠道时做出反应。他们还需要停止单次进入。然而,正如我们所知,止损是为了下滑,但它并不能提供对流动性问题的保护。此外,不能为它们设置最大滑动点值。

在这种情况下,我们应该使用高限额购买和低限额销售订单。这些是元交易者5的算法命令。它们不是在市场中执行的,而是在元交易者服务器端执行的。让我们看看官方文件:

  • 高限额采购-这种类型结合了两种类型,[限额采购和高采购],是一个止损限额采购订单。只要未来销售价格达到订单(价格字段)中指定的停止水平,限价采购订单将置于停止限制字段中指定的停止水平。
  • 低限价销售-这种限价销售订单被置于停止位置。只要未来的采购价格达到“停止限制”字段中指定的停止级别(价格字段),将以“停止限制”字段中指定的价格下限价销售订单。

文档还提供图片(插图。10)在MetaTrader 5中描述订单的操作原理。两种标有黄色框的订单对我们来说是最感兴趣的:

1

图10。MetaTrader 5中的订单类型

因此,这些限价指令是在价格达到一定的止损点时投放市场的。对于高限价采购订单,停价高于当前价格,对于低限价销售订单,停价低于当前价格。在交易所的执行模式下,限价指令的价格可以高于或低于限价指令的终止价格。此功能允许我们配置滑动点可控特殊停止指令。下图显示了它的工作原理:

2

数字。11。通过下高限额采购订单设置最大滑动点

我们可以下一个高限额的采购订单,超过停止价。一旦达到停止价格,立即执行限制采购订单,因为限制价格比当前停止价格更糟。停价和限价之间的差额构成了我们在订单中确定的最大滑动点。低限价销售订单的操作类似,尽管在这种情况下,限价应低于停止价。

现在,让我们做一些练习,手工下一个高限额的采购订单。

1.7。手工设定高限价采购和低限价销售订单而不是止损

假设我们想使用停止命令来保护我们的位置。但低流动性市场是危险的,无法预测如何使用止损单或市场指令。停止顺序(例如,停止丢失)不能在无限滑动点处受到保护。因此,巨大的价格差距或尖峰可能会导致我们的仓位完全崩溃。为了避免这种情况,我们应该用止损单代替止损单。

我们来看下面的例子。假设我们有一个很长的位置si-6.15。停止损失位置为56960。我们应该设置一个最大5点的滑动点,因此停止限制为56 960-5=56 955:

3

图12。将下限指令作为长位置的停止位置。

如我们所见,可以在交换执行模式下配置这样的低限额销售订单。如果当前价格达到56960,则限价订单将置于56955。由于目前56960的价格高于价格限制中规定的价格,因此将立即在56960执行。如果该价格的流动性不足,将在随后的价格下调至56955时执行。为确保最多5个滑动点:56 960-56 955=5,将不会以更差的价格执行限制。

现在,让我们以同样的方式保护我们的空头头寸。为了通过止损来结束空头头寸,我们需要执行反向操作——我们应该使用高限额的采购订单来购买。假设我们的空头头寸止损为56920,我们应使用以下高限采购订单配置,以提供最多五个滑动点:

4

数字。13。将高限额采购订单作为空头头寸的止损头寸

这一次,与56925相比,停止限制字段超过了5个点。

1.8。用EA中的高限额采购和低限额销售订单替换止损

让我们回到1.5节中描述的面板。我们应该对其进行修改,使其能够在高限额采购和低限额销售订单中放置保护性挡块。为此,我们添加一个名为“停止丢失”的新字段。现在,我们的小组如下:

5

数字。14。将停止位置放在devaitionpanel中

这里的代码有两个明显的变化:cdevpanel类现在有了一个放置高限和低限订单的新方法。已修改用于打开新位置的OnObjClick方法。方法的源代码如下:

//+------------------------------------------------------------------+
//| 对象点击结束                                                      |
//+------------------------------------------------------------------+
void CDevPanel::OnObjClick(string sparam)
  {
   if(sparam==m_buy_button_name)
     {
      if(Trade.BuyLimit(1,CalcCurrentPrice(OP_BUY)))
         SendStopLoss(OP_BUY);
     }
   if(sparam==m_sell_button_name)
     {
      if(Trade.SellLimit(1,CalcCurrentPrice(OP_SELL)))
         SendStopLoss(OP_SELL);
     }
   ObjectSetInteger(0,sparam,OBJPROP_STATE,false);
   Sleep(100);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| 发送 SL 订单                                                      |
//+------------------------------------------------------------------+
bool CDevPanel::SendStopLoss(int op_type)
  {
   if(op_type==OP_BUY)
     {
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      if(m_sl_level>=0.0 && m_sl_level<bid)
        {
         MqlTradeRequest request={0};
         request.action = TRADE_ACTION_PENDING;
         request.symbol = Symbol();
         request.volume = 1.0;
         request.price=m_sl_level;
         request.stoplimit=m_sl_level -(m_deviation*Point());
         request.type=ORDER_TYPE_SELL_STOP_LIMIT;
         request.type_filling=ORDER_FILLING_RETURN;
         request.type_time=ORDER_TIME_DAY;
         MqlTradeResult result;
         bool res=OrderSend(request,result);
         if(!res)
            Print("设置 S/L 错误。原因: "+(string)GetLastError());
         return res;
        }
     }
   else if(op_type==OP_SELL)
     {
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      if(m_sl_level>=0.0 && m_sl_level>ask)
        {
         MqlTradeRequest request={0};
         request.action = TRADE_ACTION_PENDING;
         request.symbol = Symbol();
         request.volume = 1.0;
         request.price=m_sl_level;
         request.stoplimit=m_sl_level+(m_deviation*Point());
         request.type=ORDER_TYPE_BUY_STOP_LIMIT;
         request.type_filling=ORDER_FILLING_RETURN;
         request.type_time=ORDER_TIME_DAY;
         MqlTradeResult result;
         bool res=OrderSend(request,result);
         if(!res)
            Print("设置 S/L 错误。原因: "+(string)GetLastError());
         return res;
        }
      if(CharToStr(StringGetChar(data,strlen-1))=='.')
         StringSetChar(data,strlen-1,'');
     }
   return false;
  }

除了这些方法之外,面板类代码现在还包括用于初始化和输入停止的字段。现在,如果我们在点击买入或卖出之前填写止损字段,那么新的市场价格表会附带一个特殊的保护性高限买入或低限卖出订单(根据头寸的方向)。

第2章。市场流动性分析

进入
2.1前计算滑动点

股票市场具有集中交易的特点。因此,可以从市场深度观察所有限价购销订单。如果我们回到“以莫斯科交易所衍生品市场为例的汇率定价原则——衍生品市场的定价原则——以莫斯科交易所衍生品市场为例”一文中提到的定义,我们可以看到位于市场深度的限价指令提供了市场流动性(a能够在最终交易价格附近进行买卖,以确定交易量)。

我们希望交易量越大,市场深度触发的订单越多,滑动点就越大,因为我们必须吸收更多远离当前价格的价格流动性提供者。您可以在上面的文章“汇率定价原则”中找到关于滑动点如何工作的更多描述。让我们看看下面的简单示例,以使问题更清楚。

在任何特定时刻,我们都有市场深度描述的买卖数量。目前,我们回顾了SI-6.15美元/卢布期货合约的市场深度:

6

图15。SI-6.15期货合约的市场深度

如果我们购买两份合同,我们将以最优惠的销售价格执行交易,不存在滑动点:51931。但如果我们买四份合同,我们的平均价格将不同于51931:(2*51931+2*51932)/4=51931.5。我们买了两份51931的合同,其余两份51932的合同。51 931.5是加权平均入场价。它和最畅销的价格之间的差异形成了我们的滑动点。

现在,我们可以根据我们的营业额安排流动性表,并定义滑动点值。在1或2份合同中,我们的交易以最低价(51931)执行,没有滑动点。对于4c合同,滑动点为0.5(51931.5-51931.0)。公式非常简单:从加权平均入门价(基于交易方向)中减去最低价或最低价。

流动性表如下:

交易量 价格 交易量 加权平均门票价格 滑移点
51938; 二十五 五万一千九百三十四点五 三点五分
&51 936页 二十三 五万一千九百三十四点二 三点二
&51 935页 十四 五万一千九百三十三
&51 933页 十一 五万一千九百三十二点五 一点五
&51 932 五万一千九百三十一点五 零点五
&51 931页 五万一千九百三十一

表1。计算加权平均住院价格和相应的滑动点

这个表格应该从下而上检查,类似于市场的深度。正如我们所看到的,这两份合同的数量没有下滑。四份合同的滑动点为0.5。25个合同量有3.5个滑动点,加权平均价格为51934.5。

中央集权市场和市场深度可以得出以下结论:

了解市场的深度,我们可以在达成交易之前计算出潜在的滑点。

所以我们可以管理我们的风险。无论我们是否使用手动或交易机器人,我们都可以在进入市场前确定市场深度。在这个例子中,我们可以将交易者与潜水员进行比较。在跳入水中之前,潜水员应该知道游泳池的深度。潜水员越大,游泳池就应该越深。同样,交易量越大,我们需要的流动性就越多。当然,市场的深度可以在我们进入之前改变。但是,即使稍微过时,剩余的计算精度仍然足以执行事务。

2.2。实时电位计算滑动点

现在,是时候将理论付诸实践了。由于市场深度变化过快,无法人工计算潜在的滑动点,计算本身也很麻烦。所以我们需要自动化。为了便于计算,我们实现了一个特殊的cmarketbook类来操纵市场深度。开发这样一个类是一项非常困难的任务,需要另一篇文章。这里不需要解释它是如何工作的。相反,我们将使用它的方法之一:getdeviation byvol。让我们看看它是如何工作的:

//+------------------------------------------------------------------+
//| 通过交易量获取偏差值。返回 -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;
  }

在调用方法时,它指的是市场深度。它从最佳价格横穿市场的深度,并计算这里可用的交易量。一旦可用交易量等于或超过需求量,该方法停止搜索并计算与预览交易量相对应的加权平均价格。加权平均价格与最畅销或最畅销价格之间的计算差异构成了我们的滑动点。

如果由于某种原因,市场深度的流动性不足以满足规定的交易量,则方法返回值-1.0表示无法计算潜在的滑动点。

现在我们有了一个潜在的滑点计算方法,我们需要可视化结果。显然,滑动点价值与市场上买卖的交易量有关。体积越大,滑动点越大。因此,我们需要在面板上添加一个名为volume的新输入字段:

7

图16。交易量面板

现在,我们的小组可以买卖任何数量的交易。例如,如果我们以市场价格购买五份合同,我们只需在“数量”字段中输入5,然后单击“购买”。这不是唯一的创新。如前所述,由于getdeviation vol方法,我们可以管理入口的滑动点。

为了获得更多的可见性,让我们直接在购买和销售按钮上显示计算值。我们以点为单位指定滑动点。每次市场深度变化时都会重新计算该值。当流动性增加时,滑动点减小,反之亦然。如果我们只想买或卖一份合同,根本就没有滑动点,因为单手交易的数量不会超过最佳买/卖价格的数量。

我建议您实时检查更新的面板。以下视频显示实时计算RTS-6.15期货合约的潜在滑点:

8

从在“体积”字段中输入合同开始。如预期的那样,买卖按钮显示0。这意味着我们进入时不会造成滑动。当交易量增加到100个合同时,平均滑动点增加到10-20个点。当交易量增加到500个合同时,平均滑动点变为60-80点。Finally, when we set up 1,500 contracts, we experienced a lack of liquidity – a value of 1.0 was displayed on the BUY button (the sliding point could not be defined). 需求流动性仍然充裕,尽管出售如此大量的合同将导致100-130点的下滑。

根据市场深度操作的类和DeviationPanel最终版本的源代码附在下面。

2.3。使用spreadrecord点差异索引作为入口过滤器

在进入市场前,对当前市场流动性进行分析是一种有益的、合理的习惯。一个发达的交易机器人为你执行复杂的计算,使你在危险的滑动点安全。但这还不够。

交易者需要处理的另一个问题是可靠的价差宽度确定。价差是最低价和买入价之间的差额。点利差是主要的相关参数,因为大量交易会影响市场流动性的正常深度,甚至超过利差本身的宽度。然而,交易者往往无法获得市场历史的深度,因此很难获得交易合同的流动性。另一方面,点差异与品种流动性呈负相关。当点差缩小时,流动性更高,反之亦然。

考虑到这一特点,我们可以建立一个点差指数来显示过去的点差。这个指标在交易中非常有用,因为它可以让我们直观地评估过去的流动性和跨物种的传播。了解平均利差后,我们可以在流动性急剧变化和利差大幅扩大时限制交易。

我们来建立这个指标。它将在图形窗口的下半部分显示其值的列行。平均点差水平显示为气缸适当水平的绿点。指标计算如下:

  • 柱线开放时间点差;
  • 最大点差出现在柱线的周期内;
  • 列线周期内到达的最小点差;
  • 柱线闭合时间点差;
  • 柱期平均点差;

Indicators do not save the point difference and start drawing from the last column after the terminal restarts. 指标的源代码如下:

//+------------------------------------------------------------------+
//|                                                Spread Record.mq4 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com/ru/users/c-4"
#property version   "1.00"
#property description "记录点差并显示它。"
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   5
#property indicator_type1   DRAW_BARS
#property indicator_type2   DRAW_ARROW
#property indicator_color1   clrBlack
#property indicator_color2   clrBlack
double spread_open[];
double spread_high[];
double spread_low[];
double spread_close[];
double spread_avrg[];
int elements;
double avrg_current;
int count;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                               |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 指标缓存区映射
   SetIndexBuffer(0,spread_open,INDICATOR_DATA);
   SetIndexBuffer(1,spread_high,INDICATOR_DATA);
   SetIndexBuffer(2,spread_low,INDICATOR_DATA);
   SetIndexBuffer(3,spread_close,INDICATOR_DATA);
   SetIndexBuffer(4,spread_avrg,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,1);
   PlotIndexSetInteger(1,PLOT_ARROW,0x9f);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrRed);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrGreen);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   printf("DEINIT");
  }
//+------------------------------------------------------------------+
//| 自定义指标迭代函数                                                 |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(prev_calculated==0)
     {
      printf("INITIALIZE INDICATORS "+TimeToString(TimeCurrent()));
      double init_value=EMPTY_VALUE;
      ArrayInitialize(spread_high,init_value);
      ArrayInitialize(spread_low,init_value);
      ArrayInitialize(spread_open,init_value);
      ArrayInitialize(spread_close,init_value);
      ArrayInitialize(spread_avrg,init_value);
      elements=ArraySize(spread_high);
      InitNewBar(elements-1);
     }
//--- 新柱线初始化
   for(; elements<ArraySize(spread_high); elements++)
      InitNewBar(elements);
   double d=GetSpread();
   for(int i=rates_total-1; i<rates_total; i++)
     {
      if(d>spread_high[i])
         spread_high[i]=d;
      if(d<spread_low[i])
         spread_low[i]= d;
      spread_close[i] = d;
      avrg_current+=d;
      count++;
      spread_avrg[i]=avrg_current/count;
     }
//--- 返回 prev_calculated 值用于下次调用
   return(rates_total-1);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double GetSpread()
  {
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   return NormalizeDouble((ask-bid)/Point(), 0);
  }
//+------------------------------------------------------------------+
//| 初始化新柱线                                                      |
//+------------------------------------------------------------------+
void InitNewBar(int index)
  {
   spread_open[index] = GetSpread();
   spread_high[index] = 0.0;
   spread_low[index]=DBL_MAX;
   avrg_current=0.0;
   count=0;
  }

让我们试着在si-6.15分钟图表上运行这个指示器。启动后短时间内,显示以下结果:

9

数字。17。在SI-6.15分钟图表中展开记录指示器

我们可以看到,在分析期间,Si-6.15的点差在1和21之间波动。在每分钟中,至少有一个点差对应于最小1点值。平均约3分。如上所述,它在指示器窗口中以绿点显示。

2.4。利差大幅上升时对手工和自动交易的限制

现在,我们需要了解如何使用这个指标来管理我们的风险。我们能做的最简单的事情就是在指数过高时限制我们的交易活动。在选定的时间段内,指标值主要在1-9点的范围内。这个区域可以称为“绿色”。它可以在这里交易。如果价差上升到9点,我们进入红色区域,在那里应该禁止交易。如下所示:

0

数字。18。启用和禁用指标定义的事务位置

除了手工交易限制之外,我们还需要教我们的EA接受目标值。如果当前利差超过了规定的限额,那么我们需要限制其交易行为。您可以通过使用来自EA的icustom函数调用度量来实现这一点。此函数允许您直接从EA调用任何用户度量并获取其值。以下是使用指标管理点差异的EA模板:

//+------------------------------------------------------------------+
//|                                          SpreadRecordControl.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"
#define OPEN  0
#define HIGH  1
#define LOW   2
#define CLOSE 3
#define AVRG  4

input int MaxSpread=9;

int h_spread_record=INVALID_HANDLE;       // 指标 SpreadRecord 的句柄
bool print_disable = false;
//+------------------------------------------------------------------+
//| 程序初始化函数                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
   h_spread_record=iCustom(Symbol(),Period(),"Spread Record");
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 程序逆初函数                                                      |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(h_spread_record);
  }
//+------------------------------------------------------------------+
//| 程序分时价函数                                                    |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(IsTradeDisable(MaxSpread))return;
   //
   // 交易逻辑...
   //
  }
//+------------------------------------------------------------------+
//| 如果禁止交易返回 true, 否则返回 false                              |
//+------------------------------------------------------------------+
bool IsTradeDisable(int max_spread)
  {
   if(h_spread_record==INVALID_HANDLE)
      return false;
   double close[];
   if(CopyBuffer(h_spread_record, CLOSE, 0, 1, close) < 1)return false;
   if(close[0]>MaxSpread)
     {
      if(!print_disable)
         printf("交易禁止");
      print_disable=true;
      return true;
     }
   if(print_disable)
      printf("交易允许");
   print_disable=false;
   return false;
  }

IStradeDisable函数主要负责定义是否允许事务处理。如果价差太高,它将返回真价差并禁止交易。如果点差正常,则函数返回false。此函数基于调用SpreadRecord度量并使用CopyBuffer函数复制当前值。在EA中指定maxspread参数等于阈值。如果该值超过,则EA会阻止其交易行为。如果点差再次低于指定边界,则EA将继续其操作。函数istradedisable通过相应的消息指定从一种状态到另一种状态的转换:“事务允许”和“事务禁止”:

2015.05.27 16:57:08.238 SpreadRecordControl (Si-6.15,H1)        交易允许
2015.05.27 16:57:08.218 SpreadRecordControl (Si-6.15,H1)        交易禁止
2015.05.27 16:56:49.411 SpreadRecordControl (Si-6.15,H1)        交易允许
2015.05.27 16:56:49.401 SpreadRecordControl (Si-6.15,H1)        交易禁止
2015.05.27 16:56:36.478 SpreadRecordControl (Si-6.15,H1)        交易允许
2015.05.27 16:56:36.452 SpreadRecordControl (Si-6.15,H1)        交易禁止

您可以将此EA原型用于您的交易系统,因此避免在流动性低和滑动点大的时期进入。

EA和SpreadRecord度量的源代码如下所示。

第3章。安全事务和EA测试模式

3.1。将分时控制替换为“睡眠模式”

如第1.1章“价格流的离散性”所述。正如在价格差距中提到的,市场报价可以与连续的价格流进行比较。因此,如果一只股票的价格从10美元变为15美元,这意味着价格在某个时刻将分别变为11美元、12美元、13美元和14美元。然而,我们发现情况并非总是如此。

价格通常会大幅波动,我们的交易方式通常基于这样一个假设,即报价会继续演变。当我们设置止损时,我们认为我们的仓位将在遭受重大损失之前结束。但是,许多订单都是在以超出既定水平的任何价格接受采购或销售的基础上终止的。在离散价格的情况下,该止损单成为潜在的损失制造者。如果当前价格比我们在止损单中指定的价格低几倍,那么止损将被执行,使我们遭受更高的损失。

另一方面,如果一个EA检查每个TOU的市场状况,它也有一个极其不利的价格清算风险:在流动性较低的情况下,上一笔交易产生的部分TOU可能会沿着其方向达到难以置信的价格,并产生价格飙升。

因此,不跟踪每个市场的分时价格,使用一些“脱敏”策略更为合理。这种EA事务逻辑被称为每个固定时间周期调用一次(例如,每分钟一次),而不是每个分时价格。毫无疑问,单独停止使用是一样的。相反,作为一个算法(虚拟)停止,在每个固定的时间段检查一次激活条件会更明智。

似乎这样一种对交易的逻辑去敏感会极大地扭曲交易的结果,但事实并非如此。当然,价格可能在一分钟内远离潜在的退出或进入价格,但执行远离价格反转的交易也更为有利。

让我们看看实际的市场情况,看看5月28日的SI-6.15卢布/美元期货合约。10:03(莫斯科时间)出现了相当大的峰值。假设我们在53040站有一个很长的位置,在52740站(300点)。在这种情况下,我们的停止将以低于我们指定的停止价格触发。

实践表明,在价格飙升期间,止损通常是在接近最糟糕的价格时触发的。在这种情况下,将是52493,使我们损失53 040-52493=547卢布/合同(而不是我们指定的止损300卢布)。这种情况如下图A所示。如果我们每分钟检查一次止损,我们的策略将忽略价格峰值,不会触发止损,最终我们的交易将结束盈利(图b):

1

图19。此策略的行为因使用实际或虚拟停止命令而有很大差异。

这里显示的价格上涨幅度相对较小。但有时甚至可能达到期货合约的价格上限。价格限制通常为当前价格的5%。因此,如果我们使用1:1杠杆,我们的风险是在止损执行期间本金损失5%。如果我们使用1:10杠杆,损失将是本金的50%!

3.2。基于移动平均和每周事务逻辑的EA例程

一个使用两条平均线交叉的EA例程就是一个很好的例子,它允许您创建一个每周检查一次市场状况的EA。最终平均值(ma)随最后一列的收盘价而变化。

基于这两个平均值的经典策略为许多交易者所熟知。ea买快妈穿慢妈卖快妈穿慢妈。下图描述了该策略的多个和短输入信号:

2

数字。20。平均策略的多头和短头入院信号

如前所述,最后一列的ma不断变化。在这种情况下,一个快速MA在一个列循环中多次遍历一个慢速MA,导致EA翻转并执行多次,而价格几乎是固定的。我们也知道,在期货市场中,合理的做法是在每个分时价格到达时不检查市场情况。因此,我们每分钟都要检查交易条款。在这种情况下,EA检查前一列(完整)而不是当前列,这样最后一列的平均重绘不会影响EA的行为。

平均EA的代码如下:

//+------------------------------------------------------------------+
//|                                                MovingAverage.mq5 |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade/Trade.mqh>

input int FastMAPeriod = 10;     // 快速 MA 周期
input int SlowMAPeriod = 20;     // 慢速 MA 周期
input double Volume = 1.0;       // 交易量
int FastMA = INVALID_HANDLE;     // 快速 MA 指标的句柄。
int SlowMA = INVALID_HANDLE;     // 慢速 MA 指标的句柄。
datetime TimeLastBar;
CTrade Trade;
//+------------------------------------------------------------------+
//| 程序初始化函数                                                     |
//+------------------------------------------------------------------+
int OnInit()
  {
   FastMA = iMA(Symbol(), Period(), FastMAPeriod, MODE_SMA, 1, PRICE_CLOSE);
   SlowMA = iMA(Symbol(), Period(), SlowMAPeriod, MODE_SMA, 1, PRICE_CLOSE);
   if(FastMA==POINTER_INVALID || SlowMA==POINTER_INVALID)
     {
      printf("指标句柄未能创建");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 程序逆初函数                                                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(FastMA);
   IndicatorRelease(SlowMA);
  }
//+------------------------------------------------------------------+
//| 程序分时价函数                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!NewBarDetect())return;
   if(CrossOver())
      Trade.Buy(GetVolume());
   else if(CrossUnder())
      Trade.Sell(GetVolume());
  }
//+------------------------------------------------------------------+
//| 返回 true 如果快速 ma 穿越慢速 ma 向上。否则返回                      |
//| false。                                                          |
//+------------------------------------------------------------------+
bool CrossOver()
  {
   double fast_ma[];
   double slow_ma[];
   if(CopyBuffer(FastMA, 0, 1, 2, fast_ma) < 1)return false;
   if(CopyBuffer(SlowMA, 0, 1, 2, slow_ma) < 1)return false;
   bool is_over=fast_ma[1]>slow_ma[1] && fast_ma[0]<slow_ma[0];
   return is_over;
  }
//+------------------------------------------------------------------+
//| 返回 true 如果快速 ma 穿越慢速 ma 向下。否则返回                      |
//| false。                                                          |
//+------------------------------------------------------------------+
bool CrossUnder()
  {
   double fast_ma[];
   double slow_ma[];
   if(CopyBuffer(FastMA, 0, 1, 2, fast_ma) < 1)return false;
   if(CopyBuffer(SlowMA, 0, 1, 2, slow_ma) < 1)return false;
   bool is_under=fast_ma[0]>slow_ma[0] && fast_ma[1]<slow_ma[1];
   return is_under;
  }
//+------------------------------------------------------------------+
//| 返回交易量计数。                                                   |
//+------------------------------------------------------------------+
double GetVolume()
  {
   if(PositionSelect(Symbol()))return Volume*2.0;
   return Volume;
  }
//+------------------------------------------------------------------+
//| 返回 true 如果检测到新柱线, 负责返回 false。                         |
//+------------------------------------------------------------------+
bool NewBarDetect()
  {
   datetime times[];
   if(CopyTime(Symbol(),Period(),0,1,times)<1)
      return false;
   if(times[0] == TimeLastBar)return false;
   TimeLastBar = times[0];
   return true;
  }
//+------------------------------------------------------------------+

&nbsp;主要的EA功能是检查新列的到达情况:

void OnTick()
{
   if(!NewBarDetect())return;
   ...
}

当前EA版本中没有停止丢失。但是,如果使用,则检查现有位置的停止损失仍将在新列行的检查功能之后,因此只有在新列行出现后才会触发停止损失。

这使得我们只有在新专栏开张后才能分析市场状况,从而避免潜在的价格飙升。当然,价格飙升可能发生在新专栏的到来之时,但它们比检查每次使用价格的市场状况要低数百倍。

3.3。用完整的列模型代替分时价格测试EA

最后,让我们来看一个由MetaTrader5策略测试人员提供的有趣的EA和度量测试模式。这种模式被称为“仅公开价格”。启动策略测试仪(视图-&gt;策略测试仪),然后在“测试仪”窗口的“执行”部分中选择它。

3

数字。21。选择“仅开盘价”模型

交易者经常低估这个模型,认为它太不准确了。此外,只有少数EAS可以有效地使用此模式。然而,很少有人知道,实际上,它是最准确和最快速的测试模式,尤其是相对于“使用时间价格”。

只有利用开盘价,才能达到高精度。所有新列的价格在完成之前都不可靠,因此使用历史报价中的前一列。

相反,“每笔交易分时”模式从可用的较小时间帧中以特殊方式获取数据,并使用分时生成器形成每列。由于MetaTrader 5不保留分时历史,生成器无法在一分钟内模拟价格差距。因此,有可能开发出一种“圣杯”,它在测试中显示出优异的结果,但在实际市场中却失败了。

每个TOU测试模型都是基于最危险的价格突破策略。此外,挂账单会扭曲真实结果。让我们考虑一个策略,放置一个高卖票,等待一个强大的反弹。2015年5月25日19:00(晚间清算后),SBRF-6.15期货合约在一分钟内从7473卢比升至7530卢比。如果我们在7485中有一个账单,它将在Strategy Tester中以指定的价格触发,并在若干支柱之后进行清算。

4

数字。22。挂起列表激活

然而,现实情况却大相径庭。我们对一分钟内的价格一无所知。换句话说,订单的执行价格可能非常低。以下视频显示如何在“使用时间”模式下执行订单:

如我们所见,策略测试人员在处理我们指定的价格时没有问题。但让我们看看分时蜡烛图:

5

图表。分时图表23。分蜡烛

价格在这一分钟发生了巨大的变化。分时图表存在较大的价格差距和激烈的运动。因此,在实际的市场条件下,我们很难以理想的价格执行止损指令。实际执行价格可能在7510-7520之间。

6

每次分析股票价格和使用市场价格而不是提单都没有区别。由于策略测试仪的分时生成器按顺序生成分时价格,一旦销售价格达到我们指定的价格,我们的订单就会被触发。事实上,我们的订单不可能以规定的价格完成。

因此,在使用“每笔交易的分时价格”模型时应谨慎。你应该知道你的策略是否对价格飙升特别敏感。

完整的圆柱形试验模式更安全。我们不应该在这种模式下使用账单来确保高精度。如果我们的策略要求输入7495,它应该检查每一列的开盘价,直到超过必要的价格才能以当前价格开盘。In the full column model, we will find that only one new column opening at 19:01 is higher than the expected price, because the opening price of the 19:00 column is still lower than 7495 roubles. 因此,在全列模式下,我们的事务如下所示:

7

数字。24。完整列模型中的实际交易记录

虽然最终结果仍然是否定的,但它有一个巨大的优势:

完整的列测试确保所有事务都以实际价格执行。因此,该模型可用于低流动性市场的策略测试。

如果您的策略在高于M1的时间范围内工作,并且您不能一个周期检查一次交易条件,请尝试“1分钟OHLC”测试模式。在此模式下,每列仅基于M1图表价格生成。由于M1图表的所有价格都是历史价格,因此该模型也具有绝对的准确性,可以推荐作为中线策略的合理测试模型。

对于低流动性市场,我不建议使用“使用时间”模型来测试策略。此外,此策略不应用于激活停止订单。

你可能会说,输入的准确性对交易系统至关重要,即使是几点也会对最终结果产生重大影响。然而,如果我们采用大数定律,我们会发现理论入口点和实际入口点之间的差异纯粹是噪声成分。在许多情况下,虽然有时极端价格突破了计算值并返回同一列,但入门价格比理论值差。

如果我们使用“使用时间”模型,我们将在此时遭受损失。但是,如果我们使用完整的列,我们就不在这种情况下。换言之,价格较低的录取将由其他(积极)效应补偿。通常情况下,差异会被完全消除,结果将完全取决于实施策略,而不是进入时的价格。

结论

现在是总结主要思想的时候了:

  • 市场价格是离散的。价格由多个交易组成,由此产生的市场图表掩盖了离散性。当我们分析价格栏时,在价格栏完成之前,我们无法可靠地判断价格会发生什么。在初始近似中,假设该列的流动性是无限的,并且每个列的范围是均匀分布的,并且每个价格都可以反映真实交易。
  • 在低流动性市场中,市场价格离散化可能非常高。因此,建议使用限价票进出市场。限价指令使我们能够克服市场价格的离散性,避免过多的滑动点。
  • 元交易者5的特殊功能,高限购买和低限销售订单,可以取代标准的止损价格。停止限价指令是安全的,即使终端没有运行,也可以用来管理最大点差异。
  • 市场价格除了具有离散性外,还具有一定的流动性,有时必须加以考虑。流动性影响滑动点值。了解市场的深度,就可以计算出潜在的滑动点。
  • 管理当前利差是评估潜在流动性而不使用市场深度的简单方法。范围水平通常取决于品种的流动性。如果利差太大,等待更好的交易时间是合理的。
  • 睡眠模式使您的EA能够可靠地抵御峰值。在此模式下,当新列到达时,将检查一次EA的事务逻辑。此外,在此模式下开发的EA可以与全圆柱模式测试兼容。
  • 完整的列模型是最精确的测试模型,因为它可以应用于离散和实际的历史数据时,它的工作。此外,此模式具有较高的测试速度。对于低流动性市场,完整列和“一分钟OHLC”是唯一合理的测试模型。

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

附加文件下载zip marketbook.mqh(10.83 kb)、panelsl.mqh(12.59 kb)、devationpanel.mq5(1.6 kb)移动平均值。mq5(3.99 kb)排列记录。MQ5(4.38 kb)

 

 


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

 

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

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

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投