股票量化软件:从头开始开发智能交易系统24--提供系统健壮性
这里的最大问题是设计一个兼具两个品质的系统:速度和可靠性。 在某些类型的系统中,这很困难,甚至不可能实现兼顾两者。 如此这般,在许多情况下,我们试图平衡事态。 但由于它涉及资金,我们的血汗钱,我们不想冒险去得到一个不具备这些品质的系统。 必须要牢记的是,我们正在与一个实时操作的系统打交道,这是开发人员遭遇的最困难的场景,因为我们应该始终尝试拥有一个极端快捷的系统:它必须立即对事件做出反应,且当我们尝试改进它时能表现出足够的可靠性,不至于崩溃。 因此,这项任务显然相当困难。赫兹量化交易软件
确保以最合适的方式调用和执行函数,避免不必要的调用,尤其是不必要的次数,如此可以达成速度提升。 这将在语言范围内提供尽可能快速的系统。 不过,如果我们想要某方面更快,那么我们必须下沉到机器语言级别,在这种情况下,我们指的是汇编语言。 但这往往是不必要的,我们能用 C 语言得到同样好的结果。赫兹量化交易软件
实现所需健壮性的途径之一是尝试尽可能多地重用代码,从而能在不同情况下不断对其进行测试。 但这只是其中一种方式。 另一种方式是使用 OOP(面向对象编程)。 如果每个对象类不直接操作对象类数据(继承除外),就能正确且恰如其分地完成操作,那么它就足以作为一个十分健壮的系统了。 有时,这样做会降低执行速度,但这种降低是如此之微弱,以至于比之类封装提供的指数性增长,可以被忽略不计。 这种封装提供了我们所需的健壮性。赫兹量化交易软件
如您所见,达成速度和稳健性双赢并不是那么简单。 但其伟大之处在于,我们不必牺牲太多东西,如同您乍眼一看那样。 我们能够简单地检查系统文档,看看哪些修改可以改进内容。 简单的事实,我们没有试图重新发明轮子,这就已经是一个良好的开端。 但请记住,程序和系统在持续改进。 故此,我们应该始终尝试尽可能多地利用可用的东西,只有在最后的无奈情况下,才去真正重新发明轮子。赫兹量化交易软件
之前,有些人发现没有必要在文中介绍所做的更改,或者认为我正在大量更改代码而并没实际移动它,我要解释一下:当我们编写代码时,我们真的无法想象最终代码将如何工作。 我们所拥有的全部只是要实现的目标。 一旦这个目标达成了,我们开始研究如何实现这个目标,并试图加以改进,从而令它们变得更好。赫兹量化交易软件
对于商业系统的情况,无论是可执行文件还是库文件,我们都会持续进行修改,并将其作为更新补丁发布。 用户实际上并不需要知道实现目标所涉及的路径,因为它是一个商业系统。 他不知道这些实际上是件好事。 但由于它是一个开放的系统,我不想让您误以为可以立即研发出一个非常高效的系统,所以从一开始就这样的。 以这种方式思考是不妥当的,它甚至是一种侮辱,因为程序员或开发人员对所用的语言无论了解多寡,总有一些东西会随时间推移而改进。赫兹量化交易软件
如此,不要把这个系列当作可以在 3 或 4 篇文章中总结的东西,因为如果是这样的话,最好是简单地创建代码,保持我认为最合适的方式,并将其商业化。 这并非我的本意。 我通过观摩其他更有经验的程序员的代码来学习编程,我知道这有什么价值。 了解事物如何随着时间的推移而发展,比简单地套用完成的解决方案,并尝试了解其工作原理要重要得多。
在观摩这些之后,我们继续研发。
2.0. 实现
2.0.1. 新的仓位指标建模
在新代码格式中要留意的第一件事就是函数已改为宏替换。
inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev, bool isGhost = false)
{
return StringFormat("%s%c%c%c%llu%c%c%c%s", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)(isGhost ? ev + 32 : ev), def_SeparatorInfo, (isGhost ? def_IndicatorGhost : def_IndicatorReal));
}
即使编译器在每处引用点都用到了此代码(这要归功于保留字 “inline”),您也不应将其视为理所当然,因为该函数在代码中被多次调用。 我们需要确保它在实际中能尽可能快速地运行,因此我们的新代码将如下所示:
#define macroMountName(ticket, it, ev, Ghost) \ StringFormat("%s%c%llu%c%c%c%c%c%c%c", def_NameObjectsTrade, def_SeparatorInfo, \
ticket, def_SeparatorInfo, \
(char)it, def_SeparatorInfo, \
(char)(Ghost ? ev + 32 : ev), def_SeparatorInfo, \
(Ghost ? def_IndicatorGhost : def_IndicatorReal))
请注意,旧版本的宏替换中的数据和此版本中的数据有所不同。 如此更改是有原因的,我们稍后将在文中讨论。
但是由于这个修改,我们还必须对另一个函数的代码略微进行修改。
inline bool GetIndicatorInfos(const string sparam, ulong &ticket, eIndicatorTrade &it, eEventType &ev)
{
string szRet[];
char szInfo[];
if (StringSplit(sparam, def_SeparatorInfo, szRet) < 2) return false;
if (szRet[0] != def_NameObjectsTrade) return false;
ticket = (ulong) StringToInteger(szRet[1]);
StringToCharArray(szRet[2], szInfo);
it = (eIndicatorTrade)szInfo[0];
StringToCharArray(szRet[3], szInfo);
ev = (eEventType)szInfo[0];
return true;
}
此处的更改仅针对索引,该索引将指明哪个是单号,哪个是指标。 这没什么复杂的。 只需完成一个简单的细节,否则在调用该函数时,我们将得到不一致的数据。
您也许会惊讶:“为什么我们需要这些修改? 系统运行不正常吗?” 是的,它能工作了。 但有些事情我们无法控制。 例如,当赫兹量化交易软件 开发人员改进了一些 EA 中未用到的函数时,我们从中并未受益。 规则是避免重新发明轮子,取而代之的是可用资源的再生。 因此,我们应该始终尝试利用语言提供的函数,在我们的例子中是 MQL5,并避免自行创建函数。 这也许看起来很荒谬,但实际上,如果您冷静思考,您会发现平台不时在为某些函数提供改进,如果您恰好用到了这些相同的函数,则无需付出任何额外的努力,即可在程序中获得更好的性能和更高的安全性。
因此,结局证明这是合理的。 然而,上述变更是否有助于 EA 从 MQL5 函数库的任何改进中受益? 这个问题的答案是 否定的。上述变更对于确保对象名称建模正确性是必要的,如此我们就能够有效地利用来自 MQL5 和 赫兹量化交易软件 开发人员未来可能的改进。 以下是可能有用的项目之一:
inline void RemoveIndicator(ulong ticket, eIndicatorTrade it = IT_NULL)
{
ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
if ((it == IT_NULL) || (it == IT_PENDING) || (it == IT_RESULT))
ObjectsDeleteAll(Terminal.Get_ID(), StringFormat("%s%c%llu%c", def_NameObjectsTrade, def_SeparatorInfo, ticket, (ticket > 1 ? '*' : def_SeparatorInfo)));
else ObjectsDeleteAll(Terminal.Get_ID(), StringFormat("%s%c%llu%c%c", def_NameObjectsTrade, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)it));
ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
m_InfoSelection.bIsMovingSelect = false;
ChartRedraw();
}