noonoon 发表于 2024-3-23 07:50:46

期货量化交易软件:开发回放系统

实现目录系统此处的问题不在于赫兹量化是否真的需要实现这个系统,而是我们为什么要实现它。在当前开发阶段,我们可以使用目录系统。不过,我们将不得不做更多的工作来实现回放/模拟服务。我的意思是,不仅简单地将新变量添加到配置文件当中,还有更多工作。为了明白我在说什么,请看下面的图片:

添加图片注释,不超过 140 字(可选)
图例 01 – 访问当前系统中目录的途径。

添加图片注释,不超过 140 字(可选)
图例 02 – 访问目录的替代途径。
尽管从回放/模拟系统的角度来看,图例 01 的行为与图例 02 相同,但您很快就会注意到,使用图例 02 所示来配置要实用得多。这是因为赫兹量化只需要一次性指定数据所在的目录,回放/建模系统会关照剩下的一切。而使用图例 02 所示的系统时,如果我们使用一个非常大的数据库,就可避免忘记或错误地指定从哪里查找数据,例如添加移动平均线。如果出现这样的编写错误,可能会发生两种状况:
在第一种情况下,系统只会发出无法访问数据的警告。
在第二种情况下,情况更为严重,将会使用不正确的数据。
不过,由于能够把目录设置在一处,这些类型的错误就能少得多。这并不意味它们根本不会发生,只是更少见。请记住,赫兹量化可以将对象组织到更具体的目录当中,从而将图例 01 和图例 02 结合到一起。不过,在此,我将把所有内容都保留在一个更简单的水平上。您的实现方式可随意符合您的数据处理和组织风格。
添加图片注释,不超过 140 字(可选)我们已经见识过理论,现在是时候看看如何在实践中做到这一点了。这个过程相对简单明了,至少与我们尚未做的事情相比是这样。首先,我们为该类创建一个新的私密变量,如下代码所示:private :    enum eTranscriptionDefine {Transcription_INFO, Transcription_DEFINE};    string m_szPath;当我们将该变量添加到此位置时,它对于该类内部的所有过程均可见。不过,无法从类的外部访问它。这样可以防止它被覆盖修改。这是因为在类的某些内部过程当中,赫兹量化能在不知不觉中更改变量的值。我们也许很难明白为什么代码没有按预期工作。一旦这些完成后,我们需要告诉我们的类开始识别配置文件中的新命令。此过程是在非常具体的点上完成的,但可能会因我们所添加的内容而异。在我们的例子中,我们将按如下所示的顺序来做:inline bool Configs(const string szInfo)    {      const string szList[] = {                              "POINTSPERTICK",                              "PATH"                              };      stringszRet[];      char    cWho;                        if (StringSplit(szInfo, '=', szRet) == 2)      {            StringTrimRight(szRet);            StringTrimLeft(szRet);            for (cWho = 0; cWho < ArraySize(szList); cWho++) if (szList == szRet) break;            switch (cWho)            {                case 0:                  m_PointsPerTick = StringToDouble(szRet);                  return true;                case 1:                  m_szPath = szRet;                  return true;                                                }            Print("Variable >>", szRet, "<< undefined.");      }else            Print("Definition of configuratoin >>", szInfo, "<< invalid.");                                        return false;    }注意,当所有代码的结构都得以改进时,这就容易得多。不过,赫兹量化必须小心。如果我们采取预先措施,我们将毫无问题地把我们需要的所有一切添加到代码之中。我们要做的第一件事是把配置文件中要用到的命令名称或标签添加到顺序数据数组之内。注意,所有这些都必须用大写字母编写。我们可以令其大小写敏感,但这会让用户更难键入,以及将其放置在配置文件当中。如果您是唯一使用该系统,并打算用同一标签但具有不同值的人,那么区分大小写的系统大概是一个好主意。否则,这个想法会令整个工作复杂化。个人而言,我认为使用相同的标签来表达不同的含义只会让我们的生活更加困难。这就是为什么我不会这样做。将标签添加到命令矩阵后,赫兹量化需要实现其功能。此刻完成恰到好处。就这么简单。由于它是链条中的第二环,并且链条从零开始,因此我们以数字 1 来表示我们正在实现该特定功能。该思路是仅指定目录名称,所以命令十分简单。最后,我们将返回 true 给调用方,指示该命令已被识别,并成功实现。往系统里附加任何东西的顺序与所示完全相同。一旦开始这样做,我们就能使用配置文件中提供的数据。不过,有一点我忘了提,它很简单,但值得关注。在某些情况下,添加的新资源也许会导致问题出现,而实际上或许只是因为它未正确初始化。在这种情况下,每当我们添加私密全局变量时,我们都需要确保它在类构造函数中被正确初始化。您可以在下面的代码中看到这一点,其中我们正在初始化一个新变量。C_ConfigService():m_szPath(NULL){}按这样做,赫兹量化确保为尚未赋值的变量会有一个已知值。在某些状况下,这个细节可能看起来微不足道,但在其它情况下,它可以避免严重的问题,并节省时间,且被认为是良好的编程实践。完成这项工作之后,变量已在类构造函数中初始化,并且我们已明确如何基于配置文件中指定的内容为其赋值,到了使用该值的时候了。该值将仅在一个负责控制数据库加载的函数中用到。我们看看如何实现这一点:bool SetSymbolReplay(const string szFileConfig)    {      #define macroFileName ((m_szPath != NULL ? m_szPath + "\\" : "") + szInfo)      int      file,                iLine;      char    cError,                cStage;      stringszInfo;      bool    bBarsPrev;      C_FileBars *pFileBars;                if ((file = FileOpen("Market Replay\\" + szFileConfig, FILE_CSV | FILE_READ | FILE_ANSI)) == INVALID_HANDLE)      {            Print("Failed to open the configuration file [", szFileConfig, "]. Closing the service...");            return false;      }      Print("Loading ticks for replay. Please wait....");      ArrayResize(m_Ticks.Rate, def_BarsDiary);      m_Ticks.nRate = -1;      m_Ticks.Rate.time = 0;      iLine = 1;      cError = cStage = 0;      bBarsPrev = false;      while ((!FileIsEnding(file)) && (!_StopFlag) && (cError == 0))      {            switch (GetDefinition(FileReadString(file), szInfo))            {                case Transcription_DEFINE:                  cError = (WhatDefine(szInfo, cStage) ? 0 : 1);                  break;                case Transcription_INFO:                  if (szInfo != "") switch (cStage)                  {                        case 0:                            cError = 2;                            break;                        case 1:                            pFileBars = new C_FileBars(macroFileName);                            if ((m_dtPrevLoading = (*pFileBars).LoadPreView()) == 0) cError = 3; else bBarsPrev = true;                            delete pFileBars;                            break;                        case 2:                            if (LoadTicks(macroFileName) == 0) cError = 4;                            break;                        case 3:                            if ((m_dtPrevLoading = LoadTicks(macroFileName, false)) == 0) cError = 5; else bBarsPrev = true;                            break;                        case 4:                            if (!BarsToTicks(macroFileName)) cError = 6;                            break;                        case 5:                            if (!Configs(szInfo)) cError = 7;                            break;                  }a                break;            };            iLine += (cError > 0 ? 0 : 1);      }      FileClose(file);      switch(cError)      {            case 0:                if (m_Ticks.nTicks <= 0)                {                  Print("No ticks to use. Closing the service...");                  cError = -1;                }else if (!bBarsPrev) FirstBarNULL();                break;            case 1: Print("Command in line ", iLine, " cannot be recognized by the system...");    break;            case 2: Print("The system did not expect the content of the line ", iLine);                  break;            default : Print("Error in line ", iLine);      }                                                                return (cError == 0 ? !_StopFlag : false);#undef macroFileName    }鉴于我们将以独特的方式同时在几个不同的地方使用它,故我选用宏定义来简化编码。所有标记为黄色之处都将完整接收宏定义中包含的代码。这大大简化了任务,因为没有必要多次编写相同的内容。这也避免了维护时在多个不同位置修改所用代码可能发生的错误。现在我们来仔细看看宏定义的作用。
页: [1]
查看完整版本: 期货量化交易软件:开发回放系统