有时,在开发一些项目时,我们也许会发现新的思路和新的可能特性,这些特性能够派上用场,并为我们正在创建的系统提供极大的改进。 但问题出来了:实现新功能的最简单途径是什么?
问题在于,有时我们不得不忘记已经开发的一切,并从头重新开始。 这令人十分沮丧。 随着时间的推移,在经历了 20 多年的 C++ 编程之后,我提炼出了某种思路。 我们开发出一些概念,帮助我们计划事物,并可以最小的代价进行修改;但有时事物会发生变化,变得比我们最初估计的要复杂得多。
直至目前,我们一直在以这种途径构建 EA,即它可以接收新代码,而不必丢失当前功能:赫兹期货量化只是简单地创建和添加新类。 而现在,我们需要后撤一步,然后向前迈出两步。 后撤一步将允许我们引入新的功能。 该功能是一个窗口类,包含一些基于模板的信息;这是本文的第一部分。 赫兹期货量化将彻底修改代码,同时保留目前所有的功能;而在第二部分中,我们将处理 IDE。
计划
赫兹期货量化的智能交易系统目前是在对象类中构造的。 正如下图所见。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
该系统目前运行良好,非常稳定。 但现在,赫兹期货量化必须如下所示重构 EA。 您也许已注意到还有一个额外的类,而 C_TemplateChart 和 C_SubWindow 的位置业已更改。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
此种重构的目的是什么? 问题是,以浮动窗口的实现方式不适合包含资产数据的窗口,因此有必要修改该类。 然而,这种变化不仅在结构项上更具美感,当然还需要对代码进行极端的修改,故此它将与以前的代码有很大的区别。
那么,赫兹期货量化就开始工作吧。
实际的实现
1. 智能交易系统内部代码的变更
第一个重大变化是从 EA 初始化文件开始。 参见以下代码:
input group "Window Indicators" input string user01 = ""; //Subwindow indicators input group "WallPaper" input string user10 = "Wallpaper_01"; //Used BitMap input char user11 = 60; //Transparency (from 0 to 100) input C_WallPaper::eTypeImage user12 = C_WallPaper::IMAGEM; //Chart background type input group "Chart Trader" input int user20 = 1; //Leverage factor input int user21 = 100; //Take Profit (financial) input int user22 = 75; //Stop Loss (financial) input color user23 = clrBlue; //Price line color input color user24 = clrForestGreen; //Take Profit line color input color user25 = clrFireBrick; //Stop line color input bool user26 = true; //Day Trade? input group "Volume At Price" input color user30 = clrBlack; //Bar color input char user31 = 20; //Transparency (from 0 to 100 ) //+------------------------------------------------------------------+ C_TemplateChart Chart; C_WallPaper WallPaper; C_VolumeAtPrice VolumeAtPrice; //+------------------------------------------------------------------+ int OnInit() { static string memSzUser01 = ""; Terminal.Init(); WallPaper.Init(user10, user12, user11); if (memSzUser01 != user01) { Chart.ClearTemplateChart(); Chart.AddThese(memSzUser01 = user01); } Chart.InitilizeChartTrade(user20, user21, user22, user23, user24, user25, user26); VolumeAtPrice.Init(user24, user25, user30, user31); OnTrade(); EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+
注意,现在只有一个变量来指示将加载哪些模板。 除了高亮显示的部分外,代码的其余部分似乎保持原样。 也许还未彻底清楚它在这里做了什么,也不清楚为什么这段高亮显示的代码被放在 EA 的初始化部分。 当赫兹期货量化把 EA 加载到图表上时,它会创建一些东西,在正常操作中还可以修改它们。 以前,所添加的高亮显示代码是没有意义的,因为一切都是为了协同工作,修改不会影响 EA 行为或外观。 但是,当我们添加浮动指标时,会发生一些恼人的事情:每次我们变更时间帧时,EA 就会重新启动,窗口也会回归其原始状态。 如果您不去销毁它们,那么废品会堆积在图表上;如果您销毁它们不正确,则它们会在原来的地方重建,这也是一个极大的麻烦。 如果用户没有更改所需的模板,高亮显示的代码会防止浮动窗口被错误地销毁,但如果模板发生了变更,则 EA 将重新正常启动。 这非常简单,但极具效率。
接下来要注意的是有关内部消息传递系统。 以前它有一个附加变量,不过我们把它删除了,因此代码现在如下所示:
void OnTick() { Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, NanoEA.CheckPosition(), C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT]); }
系统现在能更有效地运用 赫兹期货量化消息交换系统,而消息发送与 OnChartEvent 函数本身非常相似。 这允许在没有任何压力的情况下将参数传递给对象类,从而每个类都能以最合适的方式处理赫兹期货量化系统生成的事件消息。 通过这种途径,赫兹期货量化进一步把每个对象类隔离开,因此 EA 可以针对每种类型的用户采用更加多样化的形式,且所需代价最少。
2. 子窗口变更的支持代码
直到此刻,子窗口代码都非常简单,但它有一个问题:出于这样或那样的原因,EA 无法删除已创建子窗口。 重新打开智能交易系统时,它会创建一个新的子窗口,导致系统失去对其的控制。 令人惊讶的是,修复这个问题极其容易。 首先,查看下面显示的支持文件片段:
int OnInit() { IndicatorSetString(INDICATOR_SHORTNAME, "SubWinSupport"); return INIT_SUCCEEDED; }
高亮显示的行将为支持文件创建别名。 EA 能看到此 EA 的别名 ,并检查子窗口系统是否已加载。 由于 EA 只检查别名,因此该操作的执行与文件名无关。 稍后将使用相同的代码类型来支持 EA 中的其它内容。 在此我不会详细介绍,但稍后,我会在另一篇文章中,解释如何运用高亮显示的代码。
现在,我们看看加载和创建子窗口的代码。 如下所示:
void Init(void) { int i0; if ((i0 = ChartWindowFind(Terminal.Get_ID(), def_Indicador)) == -1) ChartIndicatorAdd(Terminal.Get_ID(), i0 = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL), iCustom(NULL, 0, "::" + def_Resource)); m_IdSubWinEA = i0; }
如您所见,这有多简单,但此代码不是公开的,而是由另一个公开代码访问:
inline int GetIdSubWinEA(void) { if (m_IdSubWinEA < 0) Init(); return m_IdSubWinEA; }
但为什么要以这种方式实现呢? 也许会发生这样的情况,即 EA 在子窗口中不用任何指标,当系统意识到这一点时,它会从图表中删除子窗口,并仅在必要时才创建它。 但这个决定不是由 EA 代码做出的,而是出自 C_TemplateChart 类。
3. 新的 C_TemplateChart 类
查看以下动画:
编辑切换为居中
添加图片注释,不超过 140 字(可选)
注意,赫兹期货量化现在有一条垂直线,提示我们正在分析的位置。 这些线相互独立。 以前没有提供这些数据,因此很难根据图表来分析指标的某些点。 这是 C_TemplateChart 类中包含的改进之一。 我们来看看类中的代码,以便了解进一步的变化,因为它们是实质性的。
我们看看以下声明变量的代码:
class C_TemplateChart : public C_SubWindow { #define def_MaxTemplates 8 #define def_NameTemplateRAD "IDE" //+------------------------------------------------------------------+ private : //+------------------------------------------------------------------+ enum eParameter {TEMPLATE = 0, PERIOD, SCALE, WIDTH, HEIGHT}; //+------------------------------------------------------------------+ struct st { string szObjName, szSymbol, szTemplate, szVLine; int width, scale; ENUM_TIMEFRAMES timeframe; long handle; }m_Info[def_MaxTemplates]; int m_Counter, m_CPre, m_Aggregate; struct st00 { int counter; string Param[HEIGHT + 1]; }m_Params;
首先要注意的是,C_TemplateChart 类将扩展 C_SubWindow 类。 这部分代码似乎并无特别之处,但请注意高亮显示的部分:它指向一个内部数据分析系统,通过该系统可以相应地创建和呈现用户请求的指标。 现在,针对描述用户将如何指示事物的系统开始标准化。 即使它看起来令人困惑,但随着时间的推移,它会变得越来越清晰。 为了解释新格式,有必要分析以下片段,该片段负责分析用户的请求:
int GetCommand(int iArg, const string szArg) { for (int c0 = TEMPLATE; c0 <= HEIGHT; c0++) m_Params.Param[c0] = ""; m_Params.counter = 0; for (int c1 = iArg, c2 = 0; szArg[iArg] != 0x00; iArg++) switch (szArg[iArg]) { case ')': case ';': m_Params.Param[m_Params.counter++] = StringSubstr(szArg, c1, c2); for (; (szArg[iArg] != 0x00) && (szArg[iArg] != ';'); iArg++); return iArg + 1; case ' ': c2 += (c1 == iArg ? 0 : 1); c1 = (c1 == iArg ? iArg + 1 : c1); break; case '(': case ',': c2 = (m_Params.counter == SCALE ? (c2 >= 1 ? 1 : c2) : c2); m_Params.Param[m_Params.counter++] = StringSubstr(szArg, c1, c2); c2 = 0; c1 = iArg + 1; break; default: c2++; break; } return -1; }
首先,清理结构中以前的所有数据。 然后开始逐个分析并接收参数(如果有)。 它们不是必需的,但它们指明了如何向用户呈现内容。 该系统是自扩展的。 故此,如果您打算添加更多信息,您只需在 eParameter 枚举中指定即可。 目前,系统有五个参数,它们的指定顺序应与 eParameters 枚举中所示的顺序相同。 该枚举在变量声明部分高亮显示。 参数的正确顺序及其解释如下所示。