概述
5 S9 X: [6 j) F! @0 ^5 A在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。
0 Q( B2 o% {5 I1 w5 i9 t5 D在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。' m8 z6 U- z' |$ H3 l7 {
我鼓励您继续阅读,因为内容承诺非常实用。& ^# y- K7 K) W7 ~$ A1 ]3 \8 a2 d; L
将指标锁定在特定品种上。
. Y0 u$ s8 o) v W第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:
4 y# U( h6 z. l8 [ l @ R#property copyright "Daniel Jose"
. j& z! P- U8 Z: d" n. C* o) W$ N#property indicator_chart_window
, J2 R$ m7 a) u% w. C( [6 C+ v6 }#property indicator_plots 0
" L) A: U4 X3 O/ y3 w& F$ @//+------------------------------------------------------------------+) A8 h* l- y( e6 z
#include <Market Replay\C_Controls.mqh>
6 I' i6 }$ ]; L1 D( v" y//+------------------------------------------------------------------+
4 a5 v& D4 r5 DC_Controls Control;; I7 }. H, O! b* i' Z0 ~8 W
//+------------------------------------------------------------------+
y+ W6 V7 F8 s2 l8 Fint OnInit(). S) O. l# ]* A1 v. l0 X' m! k) \
{, \* E5 m- [3 S2 ~& I( N
u_Interprocess Info;7 j7 g, K: Z- H8 J* W0 y. I4 v7 u
IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);4 _5 N: o/ P0 F
if (_Symbol != def_SymbolReplay)+ [1 b& O7 S; {% Q4 ~+ P, `
{6 f: o5 I* \- O* O. t$ Z
ChartIndicatorDelete(ChartID(), 0, def_ShortName);' `! A3 F1 A) t6 I
return INIT_FAILED;
# x7 R* {- w' m# F1 \1 d7 q0 ?}
$ p% Q. a5 t9 K* |0 W Z: l3 v I8 ~if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;! c4 w' l7 R0 ^' v; g1 j
Control.Init(Info.s_Infos.isPlay);
, d% D+ M' c9 Jreturn INIT_SUCCEEDED;
2 d: n/ F2 X7 v( z4 h}8 U1 \2 z. i) C% X, ^
//+------------------------------------------------------------------+
8 [( h4 ]2 P1 |/ Cint OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
: T" H4 n7 T9 a/ r: b7 I2 G) i{
- U. V1 F: C: q% Kreturn rates_total; T' D: `2 @7 ]
}
7 g h# a; e$ ^9 e8 K//+------------------------------------------------------------------+
4 Z! ~4 }6 W# [. [void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); p5 K+ |7 v* Q; }3 q8 `
{
6 `8 @7 |/ R, Q, R1 RControl.DispatchMessage(id, lparam, dparam, sparam);
+ R8 v8 H& u, R( b0 q}
! c" Z6 M8 S1 E3 d! c! r0 {0 r//+------------------------------------------------------------------+. D0 N& R2 c8 j1 ]% K* }6 f
void OnDeinit(const int reason)
8 x) q, U; t, I+ N; ~{, ]% m5 n; f7 ?+ E2 J, x
switch (reason)
8 V: X/ J* [6 g [{$ z/ O, Y1 i- L3 C( S: L9 Q t' M
case REASON_REMOVE:: s% f; U9 f, w, C
case REASON_CHARTCLOSE:
1 q% B/ U* ?) L4 J C* Z: qif (_Symbol != def_SymbolReplay) break;
7 h3 c+ s, k; p1 I" |! D$ G8 zGlobalVariableDel(def_GlobalVariableReplay);. S% I) j( i! v v, Z8 ]4 t4 c8 J& m
ChartClose(ChartID());$ J) O9 ^ Q/ S0 p& E4 b& ~' s
break;$ k- n6 S( j" b+ T1 f
}
8 o( D" K! }: P7 w' u4 t6 L}
e3 H8 S P0 b" }/ W6 K5 `5 U3 n2 ~//+------------------------------------------------------------------+1 N" U% n( W7 |) {6 e0 t
我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。% y: T; u8 P& x1 \+ [, o
现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。
& Y% m0 [8 @# A+ g) T如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。$ q/ M, A! U% Z/ q" W( Y- g+ S
尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。+ D. \2 D) x y- R3 v$ I: K R
因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。" b) }1 m# q5 B4 V& I w7 b3 F
我们应该避免在同一图表上多次使用指标。/ D; }" f3 T5 u+ L2 Q) }; a& ~
我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。% j N* [7 L: n. j# ~# a) [
我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。; T1 Z" V$ s. U0 E
为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:2 X v* S4 J5 d1 X
#property copyright "Daniel Jose" f1 b+ S& z: M' E3 J, \
//+------------------------------------------------------------------+
K5 _% ?) H0 `% B" ]$ M) y" @& q" ^3 V, S#define def_GlobalVariableReplay "Replay Infos"/ J# ~- I7 h+ ^4 I+ a2 S0 Y
#define def_GlobalVariableIdGraphics "Replay ID"
' p6 ]- y5 O, ?* j b( n& R#define def_SymbolReplay "RePlay"* J- {7 N6 L0 F: g9 k
#define def_MaxPosSlider 400
4 j: O4 Z0 K) U9 J J( j! |#define def_ShortName "Market Replay"% e' w# J& s. s) s. Q
//+------------------------------------------------------------------+
2 h8 h5 h1 B8 |8 H; ]union u_Interprocess
9 c2 C7 X4 ~) t{
. @1 c' r0 Y$ R# }* nunion u_0, P4 c( W8 R7 w5 Q+ e& A
{
6 t; j! j" ]& j9 C4 F) }double df_Value;
+ h& N. B2 K( r" U. ]2 e; y& Nlong IdGraphic;
4 _$ H! z7 @; U" B' m}u_Value;7 t7 y( O3 {9 Y0 H, D
struct st_0! @2 L: _0 m6 F7 Z. ~
{
0 C" P k) D$ s# T! V2 B- `bool isPlay;' `* s7 s0 r' D4 s9 v% M3 u* r
int iPosShift;8 H: ?; \- K4 d1 r
}s_Infos;+ K3 U6 V7 p. e3 z* |$ `' Q# }: @
};
0 d# Z+ K9 M3 C8 P0 |1 ~//+------------------------------------------------------------------+# y' M+ W& U2 `8 z% y
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。- ^4 q% ~, H+ P
注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。
* z1 w" ]. H2 t! Z ~+ g5 K+ m重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。
0 U7 e0 }+ t* r; ?在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:
$ l. f# n4 s- O& o//+------------------------------------------------------------------+. N. W5 S8 F, w# ?1 d
//| Script program start function |5 A3 c* f0 p2 \( a0 v/ Q# ~
//+------------------------------------------------------------------+2 Z5 f o( t/ g9 x
void OnStart()
! j0 u; N* c J+ S0 m{3 D- A) m P V- d3 t4 C+ D0 t
ENUM_TIMEFRAMES time = PERIOD_D1;
6 e2 o; G! [5 w8 B) L n6 e1 rstring szSymbol = "EURUSD";) n( E: |$ K6 H* r1 U- X
long id = ChartOpen(szSymbol, time);
" p) Z: z8 _3 L+ C) v8 n/ oChartRedraw(id);
" h' u1 R" W$ w2 [ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));9 X2 X' G* p% v, d2 O1 g7 }2 u
}
& J; M! U: L+ ]然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |