概述
8 \. A! Y+ [- o9 [在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。 i( x4 V- L; t; J& ^* \
在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。
1 I- @6 ^' M" l8 x) Q我鼓励您继续阅读,因为内容承诺非常实用。
; [% ~& [) t2 H将指标锁定在特定品种上。
' K9 J/ M. Z( m! @9 b6 O. H3 j第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:* O9 a8 C% B; u
#property copyright "Daniel Jose". C, W' j) i) z/ D3 \9 t, Q3 O9 C* N. j
#property indicator_chart_window; s. l" Y4 l6 |! T( [4 I5 o. _
#property indicator_plots 02 B% e5 _$ q5 e# s% O
//+------------------------------------------------------------------+, P! I' x9 r* D4 Y. g
#include <Market Replay\C_Controls.mqh> l# D$ D0 y( k+ H) T
//+------------------------------------------------------------------+
* X1 ?& ]" n _, dC_Controls Control;4 `. m; k4 }, ~2 J, P4 L3 ]% Q
//+------------------------------------------------------------------+
, k# ?' k& o0 e; j& Y( uint OnInit()
: m. p4 C* ]' k, y{
4 G/ t7 g @7 U9 lu_Interprocess Info;3 ~8 `( F0 [/ ] W: u
IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
, Z, P% ? g+ y# J a c# kif (_Symbol != def_SymbolReplay)
4 c# k- N$ w, s3 D f% |2 ~* J9 o{3 [9 x. K& N' B4 E
ChartIndicatorDelete(ChartID(), 0, def_ShortName);
8 t% E! U/ }8 y: ]return INIT_FAILED;
& F3 m) c3 A5 q A: n& ~% x}) j4 a- h) s/ w; @$ ?2 |
if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
e# l' E. t$ ~Control.Init(Info.s_Infos.isPlay);
( |0 B0 k* Y! N0 _return INIT_SUCCEEDED;
$ B- l+ g i ^/ I f% N}
7 t3 e( [5 M6 { \6 K' H) w//+------------------------------------------------------------------+! m9 _! j6 J9 E# ]: s3 ?$ Y
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])- @) `7 w& i1 Z9 Q9 k
{! n' E# R1 Q" o( _+ ]1 [ b0 e% P
return rates_total;
' w5 W- o# K9 R9 Z2 k) \}
; g. n$ {$ z# W. f; C1 z: q5 E//+------------------------------------------------------------------+
! y" P% x- Y; u% |' f7 lvoid OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)" z) a0 S+ E7 J+ w9 e$ |* Y5 O0 n) \
{- s, f! Q6 `9 A8 \" N; C5 W
Control.DispatchMessage(id, lparam, dparam, sparam);
" ?7 ?" O$ i' B2 U9 R0 q5 K& T {}$ t* F$ V! i% }+ l- O7 X
//+------------------------------------------------------------------+
* g, X6 _* N1 F, @9 t& B1 Xvoid OnDeinit(const int reason)- n0 J- L! y5 [6 ?
{
. z# b* W( W& `switch (reason)
* y g/ R5 j+ b5 z{3 K, ^, t0 `& y4 k+ D4 W, B( N
case REASON_REMOVE:% ]/ G2 j' I# g: z5 I- a! D; _
case REASON_CHARTCLOSE:
, ^0 V: j. q+ Dif (_Symbol != def_SymbolReplay) break;3 s2 i! c8 }& j! l/ f/ _
GlobalVariableDel(def_GlobalVariableReplay);
+ w. u7 G$ C& D4 `2 PChartClose(ChartID());
- ~+ R7 l; u# A3 g: T) p/ jbreak;
8 n/ W3 f8 x( ^1 @* H& i) b}
+ g3 g2 i8 N: j7 k' G}
1 G0 [3 X! V9 l; Y8 A% ]//+------------------------------------------------------------------+
5 s6 E+ U# A: r& H我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。
# A# {' q! y/ ^5 {" z1 G现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。 ?: v2 d* U0 f+ a0 n
如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。
* s8 v: M1 x/ J尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。: ]7 a/ `. \" E2 d3 U' I
因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。
3 j$ L4 O; W( q3 E P6 d: i# o) [我们应该避免在同一图表上多次使用指标。3 y0 F# `& O* v7 ]
我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。
, \+ D6 G8 x* H0 r9 ~ q我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。
$ v( n6 a5 @' _1 D为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:
2 Y8 M% U. [ }: ?#property copyright "Daniel Jose"* T8 {3 [) ?1 G' m
//+------------------------------------------------------------------+
1 z' ~6 n/ G8 T- q/ k! ~ n7 _% y0 p#define def_GlobalVariableReplay "Replay Infos"
: Y3 h. Z# Q% H' `5 \+ ]7 }5 Y#define def_GlobalVariableIdGraphics "Replay ID"
' j* k0 l; T3 {! | e" g8 E! M8 N#define def_SymbolReplay "RePlay"- p$ w5 ?; r/ J
#define def_MaxPosSlider 400
. R- O5 a7 ~) W9 U. V8 t#define def_ShortName "Market Replay"9 A) i; t# o1 z4 T. d, q
//+------------------------------------------------------------------+; j6 R# P8 X' m. w" I5 @
union u_Interprocess
/ b. s; V' s6 b9 W( v9 G{
' V1 ~# |$ S% N7 O( }4 H; lunion u_0! [1 x$ [. O0 K* \) ~% X( P4 d7 ~
{
w( x5 ^& B+ v$ {double df_Value;$ L9 W9 n% A+ k8 H+ x) o
long IdGraphic;
9 l' b5 y6 v. H9 b" V: O}u_Value;% \* n) d3 X2 a9 T7 r2 i& h# B
struct st_0, n6 e8 L! q+ w3 ?& T) y( p& \
{1 O- p) Y) o5 T2 ~. Q- w
bool isPlay;- o2 R% i: o5 O( ]& q% S7 C2 y
int iPosShift;8 ~, E; b7 }" w* O2 ]8 `" y0 Y7 E
}s_Infos;' ]1 L: ~7 d; J5 S! U' n+ g* Y
};" A5 b1 \" D' ^0 w' H$ d
//+------------------------------------------------------------------+0 o" N& h1 I. T: ~
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。
- ?5 j! w; A) U! I注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。
$ F- ?- e, {' \2 s j重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。
5 C4 t7 ^/ r: ~7 G1 j- O在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:
! R! i/ d7 E3 H. Z' r//+------------------------------------------------------------------+
8 c+ K& a; ], h% i3 ~+ U2 n2 U' j0 j//| Script program start function |
0 v5 c5 x% W+ p//+------------------------------------------------------------------+. t5 ]/ z9 u. [+ S5 i: `5 L
void OnStart()
1 x( s k8 r; r9 R! D{
! \) t1 c. i- TENUM_TIMEFRAMES time = PERIOD_D1;
% l" y- h) S6 R3 Ystring szSymbol = "EURUSD";
, y. w- }, g5 D) j9 c- Xlong id = ChartOpen(szSymbol, time);
* E, \1 ^0 p, v+ K' p3 GChartRedraw(id);3 L# m! b& j' V% I
ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
) z# a5 s M8 u' E}
+ I4 w! q/ d) \6 o" a- j4 I然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |