概述
5 y/ U Q z. R在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。
1 j5 f: k% Y! x2 x2 q在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。6 t2 r7 V" H- u% @& o
我鼓励您继续阅读,因为内容承诺非常实用。9 ~# \: Y, {7 ]7 M5 b0 g# Z5 T4 }
将指标锁定在特定品种上。
9 Q3 |. U: g" U: P第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:6 Z7 O! s9 U% ?. z# Q+ m' t
#property copyright "Daniel Jose"$ q1 |. g& B3 @0 c; X2 O
#property indicator_chart_window
1 e. _4 F8 j2 b1 c$ A; H#property indicator_plots 0
# B5 N8 [/ w J; F( W+ m! M//+------------------------------------------------------------------+
) W: ?" P7 A, F Q9 K& Y+ A# R#include <Market Replay\C_Controls.mqh>
, q- }8 H* \. g7 P0 T: Z//+------------------------------------------------------------------+9 Y" t; G' a% w% e" C) u% q
C_Controls Control;% j% J0 [2 m$ {5 h5 X D* _
//+------------------------------------------------------------------+
6 W7 H0 J: O B' S/ T: d, x( Yint OnInit()
; Z/ z8 F7 {- W& q9 ^- y{
3 H" @( u3 n6 m3 ?3 K2 R5 |u_Interprocess Info;
* P0 ~ E6 G: ^$ tIndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);( ]; J0 T: f8 x4 V, q
if (_Symbol != def_SymbolReplay)
( e1 h4 m8 M/ h( y' H; n) n4 }{$ ^' e% @* W: P- h/ D
ChartIndicatorDelete(ChartID(), 0, def_ShortName);
5 z- D0 g' f- F& Q+ R! Y5 {return INIT_FAILED;
1 ]6 y7 @8 T1 `, x+ S& z}" `( J5 x! j. j$ Y: R4 {- A
if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;, r8 \. v' p6 D. j
Control.Init(Info.s_Infos.isPlay);
' h- p1 x7 K: }% |; h0 a* y% T0 p& Qreturn INIT_SUCCEEDED;- [5 C) u$ u# N: |8 B
}
! @/ d$ h9 v1 t# W//+------------------------------------------------------------------+) b0 ]2 Q1 ?! Q2 T8 H8 K9 D
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
+ d0 ?. L- Y( t( ?0 L' A( ]2 U: [{* w1 C8 m8 ~) W* P& ?' D- F$ D
return rates_total; @3 u5 y& B. S5 N) f
}( S3 a& M6 \" \8 a J
//+------------------------------------------------------------------+
# B9 ^8 F8 b* |. v. @; Kvoid OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
6 A" W( a4 B' s3 M" R{
# y. a! B4 ?/ t, j. b0 NControl.DispatchMessage(id, lparam, dparam, sparam);, Y( i# K+ X! S# B8 X* a
}; P& t4 R2 A' v/ G3 d
//+------------------------------------------------------------------+
& u3 m0 k9 X6 }6 ~: z0 |, vvoid OnDeinit(const int reason)
" B7 C2 x2 x9 w7 j. p/ y% Y' s{
7 d2 p/ E! y0 C- rswitch (reason)
0 d4 f8 Y, A4 m& |+ v/ Z, O{# D4 S- i- m7 E1 C, _/ M0 H& ~
case REASON_REMOVE:! f% t8 J* V# p1 P+ _7 d1 A
case REASON_CHARTCLOSE:
; Z) z( _3 j2 Q3 w. N# z: v6 Wif (_Symbol != def_SymbolReplay) break;- K4 W' o. x) z$ O
GlobalVariableDel(def_GlobalVariableReplay);9 m9 H; S3 }+ J; I! Q' k
ChartClose(ChartID());' U# _0 _+ y, \* \( m, w. |
break;
( r9 y& G; d# Q}0 J* J# l& H/ J! a5 _
}
d& i/ [+ G' N0 p) y9 K, B9 i( \//+------------------------------------------------------------------+
/ G2 z' h3 I2 i1 q我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。8 b) f% X' ~7 S+ P. R& ]& ]) p
现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。
. _6 C4 c2 l7 k2 B" g4 }如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。
B3 n7 |- p, V; B9 G$ O尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。( j& \. [! M* ]6 N* `, g# S1 a% M) J4 @
因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。
. J2 F# O9 V9 v1 X+ V& n, x我们应该避免在同一图表上多次使用指标。
- n& w& p/ d6 X6 w4 i我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。, y0 f: J: ~) S
我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。) X6 ]& c" {4 U
为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:1 w5 ~* A% d# {+ b& _0 ]# V
#property copyright "Daniel Jose"6 X+ }. w- U0 D( ~/ t
//+------------------------------------------------------------------+% a+ E& y8 M4 b
#define def_GlobalVariableReplay "Replay Infos"9 A5 G) E2 B% {. p) c9 }, k& w) _* p% B
#define def_GlobalVariableIdGraphics "Replay ID"
) R! C: L' N0 n* k! Q#define def_SymbolReplay "RePlay"
( B) O- @* {6 L#define def_MaxPosSlider 400
2 x/ Y7 I) Q5 t/ ~& l#define def_ShortName "Market Replay"
" D3 x" c, T0 ~3 R8 p3 D//+------------------------------------------------------------------+
7 x) x7 v! ]' x' qunion u_Interprocess
; i r* P: w: f3 f0 q% I4 P3 S{& i, C* g" U0 Z; W5 B- F
union u_0
% [% X& @; `( u: L6 O* n" j U{0 W7 W' b3 |. K8 e; C, E0 N, |6 b
double df_Value;* y t# s7 h8 U7 y( Q9 h
long IdGraphic;
1 ~/ K! @- Z. M4 }}u_Value;
) E" {! u6 B$ P) K( n' E9 L; _struct st_0
" }# Y' Z6 ?1 L1 ]$ u) V. \+ z. t{& B& h5 P4 E+ m5 h
bool isPlay;
0 U* `: \( B6 b/ ^5 Cint iPosShift;
/ I! P c( I3 w8 m% w}s_Infos; h6 r2 F6 K" \- [
};% U) t8 a; y1 ^2 ^. k5 G! S7 G7 M
//+------------------------------------------------------------------+) Q/ B8 ?/ l! {1 `) k
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。
; M1 n6 r& f2 x( s3 O注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。
4 V l, ?; [1 f重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。
, R, p x( z" y! @在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:$ d2 Z0 L+ ~3 z. E, |
//+------------------------------------------------------------------+
) @$ c7 B2 p4 z, v$ A! @4 Z4 T* @- U//| Script program start function |4 C9 S% S, }0 C g* k+ \& K
//+------------------------------------------------------------------+1 \) v9 Q# l2 j, g
void OnStart(), q6 I% }' P1 B8 A
{
& l/ ]$ r( f6 ~ y3 p- c4 KENUM_TIMEFRAMES time = PERIOD_D1;5 z* }* Q( ?% r4 }
string szSymbol = "EURUSD";4 i `% H% c5 [9 f* n' M ~
long id = ChartOpen(szSymbol, time);& g" }9 _" |3 ~
ChartRedraw(id);# O3 t# G9 i1 h3 \- B8 w7 r i
ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));: R1 S% R' b. ?- ?
}
X: I$ I7 X" S- h% j3 M7 m然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |