股票量化交易软件:开发回放系统

文章讲述了作者如何将1分钟柱线的创建与交易系统分离,使用服务模式来优化回放和研究。通过服务实现,可以专注于其他问题,同时减少因EA系统导致的柱线构建延迟。作者强调了这种方法的实用性,并分享了相关代码实现细节。
摘要由CSDN通过智能技术生成

规划

为了验证这一点,我们必须对方法进行一些修改。 如果这个过程成功了,我们就不必再困惑于回放创建系统了。 我们就能专注于其它问题,并帮助我们使用真实跳价数值或模拟值进行研究或训练。 拼装 1 分钟柱线的方式保持不变。 这将是本文的主要焦点。

我们将最大程度使用一种通用方式,而我发现的最好的方式就是使用类似客户端-服务器的系统。 我已经在之前的文章“从头开始开发智能系统(第 16 部分):访问 Web 上的数据(II)”中解释过相关技术。在那篇文章中,我展示了在 赫兹量化中传输信息的三种途径。 此处,我们将采用这些途径之一,即服务。 因此,市场回放将成为 赫兹量化的一个服务。

您也许会认为我将从头开始创建所有内容。 但我为什么要做这样的事情呢? 基本上,系统已经在运行,然并未达到期望的 1 分钟时间。 您也许会问:“您认为将系统更改为服务可以解决此问题吗?“ 事实上,简单地用服务替换系统并不能解决我们的问题。 但是,如果我们从一开始就将 1 分钟柱线的创建与 EA 系统的其余部分隔离开来,那么我们以后的工作就会减少,因为 EA 本身会导致柱线构建的执行略有延迟。 我稍后会解释其中的原因。

您现在明白我们为什么要使用服务了吗? 它比上面讨论的其它方法更实用。 我们能够如我在有关如何在 EA 和服务之间交换消息的文章中解释的方式来控制它:从头开始开发交易 EA(第 17 部分):访问网络上的数据(III)。 但在这里我们不在意如何生成此控制,我们只希望服务能生成放置在图表上的柱线。 为了令事情更有趣,我们将以更具创造性的方式使用该平台,而不仅仅是使用 EA 和服务。

提醒一下,在最后一次尝试减少时间时,我们得到了以下结果:

这是我们得到的最佳时间。 在此,我们马上就要打碎这个时间。然而,我不希望您完全依附于这些值或此处显示的测试。 这一系列与创建回放/模拟器系统相关的文章已经处于更高级的阶段,我多次更改了一些概念,以便系统能真实地按预期工作。 即使此时一切似乎都足够了,但在内心深处,我犯了一些与计时测试相关的错误。 这种错误或误解,在一个早期的系统中,并不容易被注意到。 随着本系列文章的发展,您会注意到这个与时间相关的问题要复杂得多,它涉及的不仅仅是让 CPU 和 MetaTrader 5 平台在图表上提供一定数量的数据,如此您就可以沉浸在回放/模拟器系统中。

所以您不要从字面上理解在这里看到的一切。 追随本系列文章,因为我们在这里要做的事情并不简单或容易做到。

实现

我们从创建系统的基础开始。 这些包括:

  1. 创建 1 分钟柱线的服务
  2. 用于启动服务的脚本
  3. 用于模拟的 EA(这将在后面讨论)

定义行情回放服务

为了正确操控该服务,我们需要更新我们的 C_Replay 类。 但是这些变化非常小,所以我们不会深入到细节。 基本上,这些是返回代码。 不过,有一点值得单独注意,因为它实现了其他一些东西。 代码如下:

#define macroGetMin(A)  (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
                int Event_OnTime(void)
                        {
                                bool isNew;
                                int mili;
                                static datetime _dt = 0;
                                
                                if (m_ReplayCount >= m_ArrayCount) return -1;
                                if (m_dt == 0)
                                {
                                        m_Rate[0].close = m_Rate[0].open =  m_Rate[0].high = m_Rate[0].low = m_ArrayInfoTicks[m_ReplayCount].Last;
                                        m_Rate[0].tick_volume = 0;
                                        m_Rate[0].time = m_ArrayInfoTicks[m_ReplayCount].dt - 60;
                                        CustomRatesUpdate(def_SymbolReplay, m_Rate, 1);
                                        _dt = TimeLocal();
                                }
                                isNew = m_dt != m_ArrayInfoTicks[m_ReplayCount].dt;
                                m_dt = (isNew ? m_ArrayInfoTicks[m_ReplayCount].dt : m_dt);
                                mili = m_ArrayInfoTicks[m_ReplayCount].milisec;
                                while (mili == m_ArrayInfoTicks[m_ReplayCount].milisec)
                                {
                                        m_Rate[0].close = m_ArrayInfoTicks[m_ReplayCount].Last;
                                        m_Rate[0].open = (isNew ? m_Rate[0].close : m_Rate[0].open);
                                        m_Rate[0].high = (isNew || (m_Rate[0].close > m_Rate[0].high) ? m_Rate[0].close : m_Rate[0].high);
                                        m_Rate[0].low = (isNew || (m_Rate[0].close < m_Rate[0].low) ? m_Rate[0].close : m_Rate[0].low);
                                        m_Rate[0].tick_volume = (isNew ? m_ArrayInfoTicks[m_ReplayCount].Vol : m_Rate[0].tick_volume + m_ArrayInfoTicks[m_ReplayCount].Vol);
                                        isNew = false;
                                        m_ReplayCount++;
                                }
                                m_Rate[0].time = m_dt;
                                CustomRatesUpdate(def_SymbolReplay, m_Rate, 1);
                                mili = (m_ArrayInfoTicks[m_ReplayCount].milisec < mili ? m_ArrayInfoTicks[m_ReplayCount].milisec + (1000 - mili) : m_ArrayInfoTicks[m_ReplayCount].milisec - mili);
                                if ((macroGetMin(m_dt) == 1) && (_dt > 0))
                                {
                                        Print("Elapsed time: ", TimeToString(TimeLocal() - _dt, TIME_SECONDS));
                                        _dt = 0;
                                }                               
                                return (mili < 0 ? 0 : mili);
                        };
#undef macroGetMin

高亮显示的部分已添加到 C_Replay 类的源代码之中。 我们要做的是定义延迟时间,也就是说,我们将明确地采用在该行中获得的值,但以毫秒为单位。 不要忘记,这个时间并非准确,因为它还取决于一些变量。 不过,我们将尝试将其维持在尽可能接近 1 毫秒。

考虑到这些更改,我们来查看下面的服务代码:

#property service
#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Market Replay\C_Replay.mqh>
//+------------------------------------------------------------------+
input string    user01 = "WINZ21_202110220900_202110221759"; //File with ticks
//+------------------------------------------------------------------+
C_Replay Replay;
//+------------------------------------------------------------------+
void OnStart()
{
        ulong t1;
        int delay = 3;
        
        if (!Replay.CreateSymbolReplay(user01)) return;
        Print("Waiting for permission to start replay ...");
        GlobalVariableTemp(def_GlobalVariable01);
        while (!GlobalVariableCheck(def_SymbolReplay)) Sleep(750);
        Print("Replay service started ...");
        t1 = GetTickCount64();
        while (GlobalVariableCheck(def_SymbolReplay))
        {
                if ((GetTickCount64() - t1) >= (uint)(delay))
                {
                        if ((delay = Replay.Event_OnTime()) < 0) break;
                        t1 = GetTickCount64();
                }
        }
        GlobalVariableDel(def_GlobalVariable01);
        Print("Replay service finished ...");
}
//+------------------------------------------------------------------+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值