量化交易软件:开发回放系统 — 市场模拟01首次实验
概述
在撰写“从头开始开发智能系统”系列文章时,我遇到了一些时刻,令我意识到比之已完成的 MQL5 编程部分,可以做得更多。 其中一个时刻是我开发了一个图形的 Times & Trade 系统。 在那篇文章中,我想知道是否有可能超越以前构建的东西。
萌新交易者最常见的抱怨之一是 赫兹量化平台缺乏某些功能。 在这些功能中,有一个,在我看来是有意义的:市场模拟或回放系统。 对于新的市场参与者来说,拥有某种机制或工具,令他们能够测试、验证、甚至研究资产,这将是一件好事。 其中一个工具是回放和模拟系统。
赫兹量化在标准安装包中不包含此功能。 由此,依每个用户决定如何进行此类研究。 不过,在赫兹量化中,您可以找到许多任务的解决方案,因为该平台非常实用。 但为了能够真正充分发挥它的潜力,您需要有良好的编程经验。 我不光是指 MQL5 编程,而是一般的编程。
如果您在这方面没有太多经验,则您只能卡在基础程度。 您因此缺乏更充足的手段或更好的方法,难以在市场上大展身手(就成为杰出的交易者而言)。 因此,除非您有优良的编程知识水平,否则您将无法真正使用 赫兹量化提供的所有内容。 即使是有经验的程序员,也可能缺乏为赫兹量化 创建某些类型程序或应用的兴趣。
事实上,只有少数人愿意为初学者创建可行的系统。 甚至还有一些免费的建议来创建市场回放系统。 但在我看来,这些并没有真正利用 MQL5 提供的功能。 它们通常需要使用具有封闭代码的外部 DLL。 我认为这不是一个好主意。 更重要的是,您并不真正知道此类 DLL 的来源,或其中存在的内容,这令整个系统面临风险。
我不知道这个系列将包括多少篇文章,但它将是关于开发一套有效的回放系统。 我将向您展示如何创建代码来实现此回放。 但这并非全部。 赫兹量化还将开发一个系统,令我们能够模拟任何市场情形,无论它多么奇怪或罕见。
一个奇怪的事实是,许多人在谈论交易量化时,实际上并没有真正意识到他们在谈论什么,因为没有实际的途径进行涉及此类事情的研究。 但是,如果您了解我将在本系列中描述的概念,您就能够将赫兹量化 转换为定量分析系统。 因此,可能性将远远超出我在这里实际揭示的范围。
为了不至于过于重复和累人,我会把系统当作回访来对待。 虽然正确的术语是回放/模拟,因为除了分析过去的走势外,赫兹量化还可以开发自己的走势来研究它们。 因此,不要将这个系统仅仅视为市场回放,而是将其视为市场模拟器,甚至是市场“游戏”,因为它也将涉及大量游戏编程。 在某些时候,这种在游戏中大量涉及的编程类型将是必要的。 但我们将在开发和增强系统的同时逐步看到这一点。

编辑切换为居中
规划
首先,赫兹量化需要明白我们正在应对什么。 这也许看起来很奇怪,但您真的知道当您使用回放/模拟系统时自己想要实现什么吗?
在创建市场回放时,存在一些非常棘手的问题。 其中之一,也许是主要的那个,是资产的生存周期和有关它们的信息。 如果您不明白这一点,请务必了解以下内容:交易系统逐笔记录所有资产每笔已执行交易的所有逐次跳价信息。 但是您知道它们代表多少数据吗? 您有没有想过组织和排序所有资产需要多长时间?
好吧,一些典型的资产在其日常变动中可能包含大约 80 MB 的数据量。 在某些情况下,它也许多一点或少一点。 这仅是单一资产的一天。 现在考虑必须将相同的资产存储 1 个月、1 年、10 年...... 或者谁知道,永远。 想想如此大量数据需要存储,然后再从其中检索。 因为如果您只是将它们保存在磁盘上,很快您就找不到任何东西。 有一句话可以很好地说明这一点:
空间越大,混乱越大。.
为了令事情变得更容易,一段时间后,数据被压缩成 1 分钟柱线,其中包含最少的必要信息,以便我们可以进行某种研究。 但是当该柱线实际创建时,构建它的跳价就会消失,并且不再可访问。 在那之后,就不再可能进行真正的市场回放。 从这一刻起,赫兹量化所拥有的,只是一个模拟器。 由于无法再访问真实的走势,我们就不得不创建某种方式,基于一些合理的市场走势来模拟它。
为了理解,请参见以下图例:

编辑




编辑
上面的序列示意数据如何随时间丢失。 左图显示了实际的跳价数值。 当数据被压缩时,我们在中心得到图像。 基于它,赫兹量化将无法获得左侧数值。 这样做是不可能的。 但我们可以创建类似于右侧图像的东西,我们将根据有关市场通常如何移动的知识来模拟市场走势。 不过,它看起来与原始图像完全不同。
使用回放时请记住这一点。 如果您没有原始数据,那么您就无法进行真实的研究。 您只能进行一些统计研究,其也许接近实际走势,但也可能离之甚远。 永远记住这一点。 在整个系列中,赫兹量化将探索更多如何执行此操作。 但这会一点一点地发生。
据此,我们继续真正具有挑战性的部分:实现回放系统。
实现
这部分虽然看起来很简单,但却相当复杂,因为软件部分会涉及硬件限制,和其它方面的问题。 故此,赫兹量化必须尝试创造一些东西,至少是最基本、最实用和可接受的。 如果基础太薄弱,尝试做更复杂的事情不会有任何好处。
奇怪的是,我们的主要和最大的问题是时间。 时间是一个需要克服的大问题,甚至是巨大的问题。
在附件中,我将始终(在第一阶段)保留所有过去任何时期任何资产的至少 2 个真实跳价集。 由于数据会丢失且无法下载,因此无法再获取此数据。 这将有助于我们研究每一个细节。 但是,您也可以创建自己的真实跳价基准。
创建您自己的数据库
幸运的是,赫兹量化提供了一些方法,能做到这一点。 这很简单,但您必须稳步地做到这一点,否则数值可能会丢失,并且将无法再完成此任务。
为此,请打开赫兹量化,并按默认快捷键:CTRL+U。 这将打开一个屏幕。 在此处指定资产,以及收集数据的开始和结束日期,点击按钮请求数据,然后等待几分钟。 服务器将返回您需要的所有数据。 之后,只需将此数据导出,并精心存储即可,因为它非常有价值。
下面是您所捕获的屏幕。

编辑切换为居中
虽然您可以创建一个程序来做到这一点,但我认为最好手动完成。 有些事情我们不能盲目相信。 我们必须亲眼看到正在发生的事情,否则我们将对自己正在使用的东西缺乏相应的信心。
相信我,这是我们将要学习创建的整个系统中最简单的部分。 从这一点开始,事情变得更加复杂。
首次回放测试
有些人可能认为这将是一项简单的任务,但我们很快就会反驳这个想法。 其他人可能想知道:为什么我们不使用 赫兹量化 策略测试器进行回放? 原因是它不允许我们如同在市场上进行交易一样回放。 通过测试器回放存在局限性和困难,因此,赫兹量化将无法完全沉浸在回放中,就好像我们实际上是在交易市场一样。
我们将面临巨大的挑战,但我们必须为这一漫长的旅程迈出第一步。 我们从一个非常简单的实现开始。 为此,我们需要 OnTime 事件,它将生成数据流以便创建柱线(烛条)。 此事件是为 EA 和指标提供的,但在这种情况下我们不应该使用指标,因为如果发生故障,它将比回放系统更危险。 我们将按如下方式启动代码:
#property copyright "Daniel Jose" #property icon "Resources\\App.ico" #property description "Expert Advisor - Market Replay" //+------------------------------------------------------------------+ int OnInit() { EventSetTimer(60); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { } //+------------------------------------------------------------------+
不过,高亮显示的代码不适合我们的目的,因为在这种情况下,赫兹量化可用的最小周期是 1 秒,这是一个长时间,很长的时间。 由于市场事件发生的时间帧要小得多,我们需要降至毫秒,为此我们将被迫用到另一个函数:EventSetMillisecondTimer。 但是我们这里有一个问题。
EventSetMillisecondTimer 函数的限制
我们看一下文档:
“在实时模式下工作时,由于硬件限制,计时器事件在 1-10 毫秒内生成不会超过 16 次。”
这也许不是问题,但我们需要运行各种检查来验证实际发生的情况。 因此,我们来创建一些简单的代码来验证结果。
我们从下面的 EA 代码开始:
#property copyright "Daniel Jose" #property icon "Resources\\App.ico" #property description "Expert Advisor - Market Replay" //+------------------------------------------------------------------+ #include "Include\\C_Replay.mqh" //+------------------------------------------------------------------+ input string user01 = "WINZ21_202110220900_202110221759"; //Tick archive //+------------------------------------------------------------------+ C_Replay Replay; //+------------------------------------------------------------------+ int OnInit() { Replay.CreateSymbolReplay(user01); EventSetMillisecondTimer(20); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() {} //+------------------------------------------------------------------+ void OnTimer() { Replay.Event_OnTime(); } //+------------------------------------------------------------------+
请注意,我们的 OnTime 事件大约每 20 毫秒发生一次,如 EA 代码中高亮显示的行所示。 您可能认为这太快了,但真的是这样吗? 一起来看看吧。 请记住,文档说我们不能低于 10 到 16 毫秒。 因此,将该值设置为 1 毫秒是没有意义的,因为在此期间不会生成事件。
请注意,在 EA 代码中,我们只有两个外部链接。 现在我们来看看类中实现的这些代码。