赫兹量化软件还需要检查图表是否打开,这是间或完成的。 不过,由于我们并不经常这样做,因此造成的延迟会低得多。 好吧,所有调用总是会产生一个小的延迟。 我们还需要更新位置值,并捕获控制指标的当前状态,这也会导致轻微的执行延迟。 此外,检查图表是否打开会导致性能略有损失。 不过,严格来说这个更低得多,因为每秒钟才调用几次。
据此,我们能完结这部分了。 但首先,赫兹量化软件来看一下这个 1-分钟柱线构建系统的其它两项功能。 第一个是寻找回放/模拟应该开始的点。
void AdjustPositionToReplay(const bool bViewBuider) { u_Interprocess Info; MqlRates Rate[def_BarsDiary]; int iPos, nCount; Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return; iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1))); Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time); if (iPos < m_ReplayCount) { CustomRatesDelete(def_SymbolReplay, Rate[0].time, LONG_MAX); if ((m_dtPrevLoading == 0) && (iPos == 0)) { m_ReplayCount = 0; Rate[m_ReplayCount].close = Rate[m_ReplayCount].open = Rate[m_ReplayCount].high = Rate[m_ReplayCount].low = m_Ticks.Info[iPos].last; Rate[m_ReplayCount].tick_volume = Rate[m_ReplayCount].real_volume = 0; CustomRatesUpdate(def_SymbolReplay, Rate, 1); }else { for(Rate[0].time -= 60; (m_ReplayCount > 0) && (Rate[0].time <= macroRemoveSec(m_Ticks.Info[m_ReplayCount].time)); m_ReplayCount--); m_ReplayCount++; } }else if (iPos > m_ReplayCount) { if (bViewBuider) { Info.s_Infos.isWait = true; GlobalVariableSet(def_GlobalVariableReplay, Info.u_Value.df_Value); }else { for(; Rate[0].time > m_Ticks.Info[m_ReplayCount].time; m_ReplayCount++); for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++); CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount); } } for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(); Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); Info.s_Infos.isWait = false; GlobalVariableSet(def_GlobalVariableReplay, Info.u_Value.df_Value); }
相较于以前的版本,它并未经历重大变化,但不同于以前版本,在此版本中,赫兹量化软件不再返回任何值。 更改了柱线创建,令其更加高效。 这看似是一件小事,但这样做事实上却有很大帮助。 现在还有一个小细节:上面的函数不再公开。 它不能再从我们的对象类之外访问。 这同样适用于负责创建 1-分钟柱线的下一个函数。
inline void CreateBarInReplay(const bool bViewMetrics = false) { #define def_Rate m_MountBar.Rate[0] static ulong _mdt = 0; int i; if (m_MountBar.bNew = (m_MountBar.memDT != macroRemoveSec(m_Ticks.Info[m_ReplayCount].time))) { if (bViewMetrics) { _mdt = (_mdt > 0 ? GetTickCount64() - _mdt : _mdt); i = (int) (_mdt / 1000); Print(TimeToString(m_Ticks.Info[m_ReplayCount].time, TIME_SECONDS), " - Metrica: ", i / 60, ":", i % 60, ".", (_mdt % 1000)); _mdt = GetTickCount64(); } m_MountBar.memDT = macroRemoveSec(m_Ticks.Info[m_ReplayCount].time); def_Rate.real_volume = 0; def_Rate.tick_volume = 0; } def_Rate.close = m_Ticks.Info[m_ReplayCount].last; def_Rate.open = (m_MountBar.bNew ? def_Rate.close : def_Rate.open); def_Rate.high = (m_MountBar.bNew || (def_Rate.close > def_Rate.high) ? def_Rate.close : def_Rate.high); def_Rate.low = (m_MountBar.bNew || (def_Rate.close < def_Rate.low) ? def_Rate.close : def_Rate.low); def_Rate.real_volume += (long) m_Ticks.Info[m_ReplayCount].volume_real; def_Rate.tick_volume += (m_Ticks.Info[m_ReplayCount].volume_real > 0 ? 1 : 0); def_Rate.time = m_MountBar.memDT; m_MountBar.bNew = false; CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate, 1); m_ReplayCount++; #undef def_Rate }
它几乎不需要任何解释。 于此,我们一根接一根地创建 1-分钟柱线,然后将它们发送到图表中。 我们还未就跳价操作,也就是说,我们还不能利用一些赫兹量化软件资源进行回放/模拟。 我们会在以后实现它。 那现在就不要担心这些。 与此同时,我们还运作所有必要的检查和测量,以便判定是否值得开始构建新柱线。 如果您有意,将来可以舍弃这部分,因为它所做的只是显示一根柱线与前一根柱线之间的时差度量。 这在现在非常有用,因为它可以帮助我们优调创建循环中的延迟值。
这部分 1-分钟柱线讲义到此结束。 现在,系统能在非常合理的时间内呈现它们,至少对于测试过的交易和模拟跳价而言。 接下来,我们将应对与跳价回放相关的另一个问题。 至于跳价本身,此刻我们没有任何其它问题。
查看随机游走图形
到目前为止,这项工作一直充满乐趣,甚至令人享受。 然而,我们现在面临的一些事情或许对某些人来说非常困难,但它确实需要完成:模拟 1-分钟柱线上也许以任何方式出现的所有跳价。 我建议您专注下面介绍的解释。 为了简单起见,我不会在此展示模拟系统的最终版本。 最终版本会在稍后展示。 原因是所有这些都十分复杂,无法一次性展现。
我们真正打算、以及即将创造的是所谓的随机游走。 这种随机游走有一些规则。 与通常的编程不同,在此我们不能允许系统完全随机化。 我们需要创建一些数学规则来尝试引导走势。 不要误会我的意思。 随机游走实际上是一种完全随机性、且不可预测的走势,至少在短期内如此。 但因为我们不会创建完全不可预测的走势,而且因为我们知道它从哪里开始,在何处结束,故该系统不是完全随机的。 不过,我们仍会在柱线中加入一些随机性。
有若干个想法可以用来更容易地创建真正的随机游走。 取决于具体情况,某些方式能比其它的更佳。 经验较少的程序员也许会认为,使用随机数生成器,并执行某种转换,以便将数值限制在特定范围内就足够了。 这种方式,虽然并非完全错误,但确实有一些缺点。 如果您在图表上查看由这种移动造成的数据,您会得到类似于下图的内容:
添加图片注释,不超过 140 字(可选)
您也许会认为这张图形(是的,这就是稍后我会给展示给您的图形)看起来根本不像是随机走势。 它看起来更像是一团糟,但实际上它是一种随机走势,是经由时间点之间跳跃达成的。 为了达成这一步,我们将使用通过 MetaTrader 5 平台获得的以下数据行。 记住,每行代表一根 1-分钟的柱线。
添加图片注释,不超过 140 字(可选)
这些数据将在附件中提供,以便您可自行分析。 现在我们继续厘清为什么上面的图表与我们所预期的如此出乎意料地不同。 若要明白这一点,您需要知晓它是如何创建的。 一开始,我们就要在服务文件内定义两件事。 我们将暂时使用它们,且在将来的源代码中它们不会出现。
#define def_TEST_SIMULATION #ifdef def_TEST_SIMULATION #define def_FILE_OUT_SIMULATION "Info.csv" #endif //+------------------------------------------------------------------+ #property service #property icon "\\Images\\Market Replay\\Icon.ico" #property copyright "Daniel Jose" #property version "1.13" // ... The rest of the code...
这个定义将允许我们创建一个模拟检查,如此我们就可以分析生成的走势图。 另一方面,此文件将包含与 1-分钟柱线内的模拟走势相对应的数据。 稍后我们将看到何时及何处创建此文件。 在服务文件中声明头文件之前,一旦有了这个定义,赫兹量化软件就可以在 MQH 文件中使用此定义。 现在我们转入 C_Replay.Mqh 文件,以便了解我们将如何获取数据。
为了真正捕捉事物,从而了解模拟器如何在 1-分钟柱线内创建走势,我们将调用以下函数。