hanniball 发表于 2024-4-21 07:51:34

期货量化交易软件——开发回放系统的调整2.0

调整自定义品种数据为了实现我们的订单系统,赫兹量化交易软件最初需要三个基本值:最小交易量、最小跳价值和最小跳价大小。这些值类型中目前只实现了一种,且其实现并不完全符合要求,因为也许会发生未在配置文件中设置该值的情况。这令我们创建合成品种的工作复杂化,其仅涉及模拟可能的市场走势。如果没有所需的调整,当我们以后操控该订单系统时,其中的数据也许会不一致。赫兹量化交易软件需要确保正确配置此数据。这将令我们尝试在系统中实现某些东西时避免出现问题,毕竟代码已经相当长了。因此,我们将开始纠正错误之处,从而避免下一阶段工作中的问题。如果我们有问题,就让它们具有不同的性质。订单系统实际上不会与创建市场回放/模拟服务的服务进行交互。我们会遇到的唯一信息是图表和品种名称,仅此而已。至少这是我现在的意图。我不知道我们是否真的会成功。对于这种场景,我们要做的第一件事就是初始化我们绝对需要的三个值。不过,它们都会设置为零。我们按部就班地研究这个问题。首先,我们需要修复我们的问题。这是在以下代码中完成的:C_Replay(const string szFileConfig)    {      m_ReplayCount = 0;      m_dtPrevLoading = 0;      m_Ticks.nTicks = 0;      Print("************** Market Replay Service **************");      srand(GetTickCount());      GlobalVariableDel(def_GlobalVariableReplay);      SymbolSelect(def_SymbolReplay, false);      CustomSymbolDelete(def_SymbolReplay);      CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);      CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);      CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);      SymbolSelect(def_SymbolReplay, true);      CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0.0);      CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");      m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);    }在此,我们将初始值设置为零,但作为福利,赫兹量化交易软件还将提供自定义品种的描述。这不是必需的,但如果您打开一个包含品种列表的窗口,并看到一个独有名称的品种,这可能会很有趣。您大概已经注意到,我们将不再使用以前存在的变量。变量会在特定位置声明,如下面的代码所示:class C_FileTicks{    protected:      struct st00      {            MqlTickInfo[];            MqlRates Rate[];            int      nTicks,                     nRate;            bool   bTickReal;      }m_Ticks;      double       m_PointsPerTick;    private :      int          m_File;现在,出现此变量的所有点都应引用品种中包含和定义的值。现在我们有了新代码,但基本上这个值在贯穿回放/模拟系统中的两处被提及。第一处如下所示:inline long RandomWalk(long pIn, long pOut, const MqlRates &rate, MqlTick &tick[], int iMode)    {      double vStep, vNext, price, vHigh, vLow, PpT;      char i0 = 0;      PpT = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE);      vNext = vStep = (pOut - pIn) / ((rate.high - rate.low) / PpT);      vHigh = rate.high;      vLow = rate.low;      for (long c0 = pIn, c1 = 0, c2 = 0; c0 < pOut; c0++, c1++)      {            price = tick.last + (PpT * ((rand() & 1) == 1 ? -1 : 1));            price = tick.last = (price > vHigh ? price - PpT : (price < vLow ? price + PpT : price));            switch (iMode)            {                case 0:                  if (price == rate.close)                        return c0;                        break;                case 1:                  i0 |= (price == rate.high ? 0x01 : 0);                        i0 |= (price == rate.low ? 0x02 : 0);                        vHigh = (i0 == 3 ? rate.high : vHigh);                        vLow = (i0 ==3 ? rate.low : vLow);                        break;                case 2:                  break;            }            if ((int)floor(vNext) < c1)            {                if ((++c2) <= 3) continue;                vNext += vStep;                if (iMode == 2)                {                  if ((c2 & 1) == 1)                  {                        if (rate.close > vLow) vLow += PpT; else vHigh -= PpT;                  }else                  {                        if (rate.close < vHigh) vHigh -= PpT; else vLow += PpT;                  }                } else                {                  if (rate.close > vLow) vLow = (i0 == 3 ? vLow : vLow + PpT); else vHigh = (i0 == 3 ? vHigh : vHigh - PpT);                }            }      }      return pOut;    }由于我们不想在多处重复相同的代码,因此我们使用局部变量来帮助我们。不过,原理是相同的:我们指的是品种内定义的值。该值引用的第二处位于 C_Replay 类之中。不过,出于实际原因,赫兹量化交易软件要做的事情与上面所示的内容略有不同,与我们创建随机游走时相反。在图表中显示和使用信息往往会降低性能,因为有太多不必要的调用。这是因为在创建随机游走期间,每根柱线将产生三次访问。但是一旦它被创建,它就可以包含数千个跳价,所有这些都是在三次调用中创建的。这往往会在呈现和绘图过程中略微降低性能,但我们来看看在实践中这样做是如何发挥作用的。当我们使用真正的跳价文件时,即我们回放,这样的降速不会出现。这是因为当使用真实数据时,系统不需要任何额外的信息来绘制 1-分钟柱线,以及将信息传输到市场观察窗口中的跳价图表。我们在之前的两篇文章中对此进行了研究。但是,赫兹量化交易软件要使用 1-分钟柱线来生成跳价时,即执行模拟时,我们需要知道跳价大小,如此这些信息才可以帮助服务创建合适的走势模型。这种走势将在“市场观察”窗口中可见。但是此信息并非创建柱线所需,因为转换是在 C_FileTicks 类中执行的。
添加图片注释,不超过 140 字(可选)知道了这个细节,我们必须研究生成指定图表的函数,且还要检查在执行过程中将收到多少次调用。以下是模拟期间所用的函数:inline void CreateBarInReplay(const bool bViewMetrics, const bool bViewTicks)      {#define def_Rate m_MountBar.Rate                        bool bNew;      MqlTick tick;      static double PointsPerTick = 0.0;                if (m_MountBar.memDT != macroRemoveSec(m_Ticks.Info.time))      {                        PointsPerTick = (PointsPerTick == 0.0 ? SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) : PointsPerTick);                                          if (bViewMetrics) Metrics();                        m_MountBar.memDT = (datetime) macroRemoveSec(m_Ticks.Info.time);                        def_Rate.real_volume = 0;                        def_Rate.tick_volume = 0;      }      bNew = (def_Rate.tick_volume == 0);      def_Rate.close = (m_Ticks.Info.volume_real > 0.0 ? m_Ticks.Info.last : def_Rate.close);      def_Rate.open = (bNew ? def_Rate.close : def_Rate.open);      def_Rate.high = (bNew || (def_Rate.close > def_Rate.high) ? def_Rate.close : def_Rate.high);      def_Rate.low = (bNew || (def_Rate.close < def_Rate.low) ? def_Rate.close : def_Rate.low);      def_Rate.real_volume += (long) m_Ticks.Info.volume_real;      def_Rate.tick_volume += (m_Ticks.Info.volume_real > 0 ? 1 : 0);      def_Rate.time = m_MountBar.memDT;      CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);      if (bViewTicks)      {                        tick = m_Ticks.Info;                        if (!m_Ticks.bTickReal)                        {                              static double BID, ASK;                              doubledSpread;                              int   iRand = rand();                                                      dSpread = PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? PointsPerTick : 0 ) : 0 );                              if (tick.last > ASK)                              {                                        ASK = tick.ask = tick.last;                                        BID = tick.bid = tick.last - dSpread;                              }                              if (tick.last < BID)                              {                                        ASK = tick.ask = tick.last + dSpread;                                        BID = tick.bid = tick.last;                              }                        }                        CustomTicksAdd(def_SymbolReplay, tick);         }                m_ReplayCount++;#undef def_Rate      }于此我们声明了一个静态局部变量。这是为了避免不必要的调用修复跳价大小的函数。在服务生存周期和运行时,这样的捕获只会发生一次,而变量仅在指定位置使用。故此,将其扩展到此函数之外是没有意义的。但要注意,实际使用变量的这处只在我们使用模拟模式时才可用。在回放模式下,该变量没有实际用途。这也解决了跳价大小的问题。还有两个问题亟待解决。不过,跳价的问题尚未彻底解决。初始化存在问题。我们将在解决其它两个问题的同时解决它,因为方式将是相同的。
最后要创建的细节问题是我们实际上应该调整什么。的确,我们可以在自定义品种中调整若干事项。但其中大多并非我们的目的所需。我们只需关注我们真正需要的东西。我们还需要并对其进行设置,如此在我们需要这些信息时,我们就能以简单而通用的方式获得它。我之所以这样说,是因为我们很快就会开始创建一个订单系统,但我不确定它是否真的会那么快发生。任何情况下,我希望我们的 EA 与回放/模拟兼容,并且适合在真实市场中使用,既可是模拟账户,亦或真实账户。为此,我们需要一些含有所需信息的组件,且与真实市场中存在的级别相同。在这种情况下,我们需要用零值初始化它们。这可确保自定义品种的这些值与实际交易品种中的值一致。此外,将值初始化为零意味着我们可以稍后测试它们,并且它令实现和测试品种配置中可能的错误的工作变得更加容易。C_Replay(const string szFileConfig)    {      m_ReplayCount = 0;      m_dtPrevLoading = 0;      m_Ticks.nTicks = 0;      Print("************** Market Replay Service **************");      srand(GetTickCount());      GlobalVariableDel(def_GlobalVariableReplay);      SymbolSelect(def_SymbolReplay, false);      CustomSymbolDelete(def_SymbolReplay);      CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);      CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);      CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);      SymbolSelect(def_SymbolReplay, true);      CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);      CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);      CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);      CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");      m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);    }在此,我们设置未来真正需要的那些值,就在不久。这些值是跳价大小、跳价值、和交易量(在本例中为步数)。但由于该步数通常对应于应使用的最小交易量,故在我看来设置它替代最小交易量没有任何问题。还因为在下一阶段,这个步数对我们来说更加重要。还有另一个原因:我尝试调整最小交易量,但由于某种原因我无法这样做。MetaTrader 5 简单地忽略了我们需要设置最小交易量的事实。一旦完成之后,我们还需要做些事情,并检查这些值是否已真的被初始化。这是在下一段代码中完成的:bool ViewReplay(ENUM_TIMEFRAMES arg1)   {#define macroError(A) { Print(A); return false; }   u_Interprocess info;      if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)      macroError("Configurao do ativo no esta completa, falta declarar o tamanho do ticket.");   if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)      macroError("Configurao do ativo no esta completa, falta declarar o valor do ticket.");   if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)      macroError("Configurao do ativo no esta completa, falta declarar o volume mínimo.");   if (m_IdReplay == -1) return false;   if ((m_IdReplay = ChartFirst()) > 0) do   {      if (ChartSymbol(m_IdReplay) == def_SymbolReplay)      {            ChartClose(m_IdReplay);            ChartRedraw();      }   }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);   Print("Aguardando permisso do indicador para iniciar replay ...");   info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);   ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");   ChartRedraw(m_IdReplay);   GlobalVariableDel(def_GlobalVariableIdGraphics);   GlobalVariableTemp(def_GlobalVariableIdGraphics);   GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);   while ((!GlobalVariableCheck(def_GlobalVariableReplay)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);      return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));#undef macroError}为了避免不必要的重复,我们将使用宏定义。它将显示一条错误消息,并以系统故障消息结束。在此,我们逐一检查必须在配置文件中声明和初始化的值。如果其中任何一个尚未初始化,系统将通知用户正确配置自定义品种。不这样的话,回放/模拟服务将无法继续起作用。从这一刻起,可以认为它能起作用,并能为订单系统提供必要的数据,在本例中是正在创建的 EA,以便正确建模或回放行情。这将令我们能够模拟发送订单。期货量化交易软件:开发回放系统必要的调整
页: [1]
查看完整版本: 期货量化交易软件——开发回放系统的调整2.0