概述 B N7 a* O; O. o8 e
在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。6 o1 W2 [. y/ `9 Q
在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。- x) m$ ~/ ]7 } ?( i1 ~
我鼓励您继续阅读,因为内容承诺非常实用。3 r& O$ u( c0 Y; D; k1 X$ L
将指标锁定在特定品种上。
, I% ]# Y4 u/ P3 |$ W/ _/ u. a第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:* o4 w( s7 b' v7 r, i* L- ~
#property copyright "Daniel Jose"
" y( i" ]! q8 E) j3 F& E8 N2 E#property indicator_chart_window' F7 T; Q: j; Z
#property indicator_plots 0
S$ B% X8 W* F( S+ q ^3 v; u' [//+------------------------------------------------------------------+6 j$ F. ?1 h6 U" s! X7 _3 d
#include <Market Replay\C_Controls.mqh>7 _4 U N- I) h4 R2 [
//+------------------------------------------------------------------+
" E( S9 T' b4 s$ `C_Controls Control;
+ u: a5 n& Z8 M9 d" `2 i: c//+------------------------------------------------------------------+" ]! |' O4 c! ^; M1 e9 P
int OnInit()9 A, G! Y. c N9 O4 x- b9 d0 U) k
{
* v9 R# y3 @# u* E5 _8 Nu_Interprocess Info;
* K; T* ?3 [% S% T# EIndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);. ?& j) m% O# K) [: ~
if (_Symbol != def_SymbolReplay): p0 C4 ~4 }2 F& n; I
{# @, R1 A5 z( H& O4 ~; d
ChartIndicatorDelete(ChartID(), 0, def_ShortName);
c3 T8 _( \' z5 F' n1 `; w4 wreturn INIT_FAILED;1 l( V/ j& r) t7 T# v, |
}7 H6 r) O B8 U5 }
if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;0 O6 h; @" w" o4 b+ X
Control.Init(Info.s_Infos.isPlay);
. V+ {+ m/ p' L8 Greturn INIT_SUCCEEDED;- ]+ f. \- G+ y( r( V7 m
}
! u& q$ t f% [- _) p//+------------------------------------------------------------------+9 w9 ]1 @6 F6 K5 W' v! m
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])- u2 [% I& G& z! q% z: ]7 M
{
- x' ]4 |, @ B8 a, |4 i' R7 @: s/ oreturn rates_total;& q: U6 @- w- ~) F1 S' m7 G# l, t& ?
}
; `& P& i/ x, B- g) @//+------------------------------------------------------------------+0 p! G' u- _/ f/ w* S$ W0 V
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
. C0 {, e6 q+ \+ }, ~4 D4 `{( d- a! p, g6 `" A* `% K; i) Y
Control.DispatchMessage(id, lparam, dparam, sparam);
' l' Q; y, b6 r6 s# X- P}
1 S$ J- q7 _" N/ r# W3 _//+------------------------------------------------------------------+( U7 \, q3 P- Y% N: m. S1 Y
void OnDeinit(const int reason)% ~( o: e# X/ U! O! k* T: n
{ R# N4 Q0 `1 `- W% d* [! O: q. G
switch (reason)
0 Q% g% m4 D9 b- P5 h{' A6 \7 F8 ^0 K7 s2 k* d) }2 X
case REASON_REMOVE:- f! i4 V$ b# t$ w
case REASON_CHARTCLOSE:
' ]# y& s5 Q2 a) o# I, m3 M( J* Mif (_Symbol != def_SymbolReplay) break;
: u+ Y# z) A" M; S ~3 DGlobalVariableDel(def_GlobalVariableReplay);$ k5 z8 s! n+ O1 ? [0 O9 ]) k
ChartClose(ChartID());
: V7 Z$ y2 I6 T- ybreak;
* b; h2 S6 G7 N3 R8 ]}- W4 Q3 j3 R% Y+ {6 U" @
}
; g4 [6 T J1 b4 o* A//+------------------------------------------------------------------+
+ S3 A( V L( T( c; s我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。
1 p* q6 [ r3 U现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。! m8 F& u, m9 E5 G; t% ?, c
如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。
6 E* `9 T6 V4 D h尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。
3 Z2 { F3 @; H6 w! O+ J" C7 s* [因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。- C$ `9 G. T/ o! j8 U8 m1 s; Y
我们应该避免在同一图表上多次使用指标。0 m: J& L7 Z4 i9 O3 Y
我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。
}) r" l5 d/ u4 {6 _我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。) |3 p# q$ u9 q+ v% H
为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构: k3 ]& Y' |3 }2 H1 A* @
#property copyright "Daniel Jose"6 P3 }" Z! u$ v' a; \7 I
//+------------------------------------------------------------------+/ W. a# u/ ~3 g1 i9 w
#define def_GlobalVariableReplay "Replay Infos"
/ U* m; ~: Q4 F6 n S' B2 N) a#define def_GlobalVariableIdGraphics "Replay ID"2 l4 s# G, K7 s
#define def_SymbolReplay "RePlay": ?; q8 k3 R: i I. n
#define def_MaxPosSlider 400
- }& A) w* Y7 k i#define def_ShortName "Market Replay"; u- _6 @6 |6 ?
//+------------------------------------------------------------------+
9 O9 u. f" Q* u: I( u3 T; lunion u_Interprocess: b! u& ?3 n# k) S- M' ]
{
N) Q$ M w# W* T# ~union u_0 I% \! Q; M9 A5 R
{
+ J& n% E0 ^& r4 A: v% ~. T5 Fdouble df_Value;
/ I0 j* L$ A8 N5 clong IdGraphic;: L% r8 m9 H+ \* v( X+ T: j
}u_Value;/ f, G* V. g4 h8 f# H
struct st_0
& v: @+ L: G; R5 K, W{' }9 x* h7 |9 W: `; b
bool isPlay;3 b j3 }; B" q1 e3 ?
int iPosShift;- R% M1 y$ V) B/ B; h Z$ Z
}s_Infos;
9 l" g' K. W, a& u* d5 Q};
3 [0 M9 x3 ?0 M- E; G! D5 ]7 R9 K- K//+------------------------------------------------------------------+
3 o* g5 ~( u( c+ }- f% n对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。* p0 t8 f% h8 u- V ^. M
注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。& z1 ~/ s2 d" U. e& C
重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。
7 l) V, z. X5 `6 y, g, X在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:; M% e# |1 g/ Q2 ^, b
//+------------------------------------------------------------------+
4 }8 k N4 u4 C& Z! W8 U1 R//| Script program start function |
' f3 h0 j3 R& t2 A( V# q9 Z; P3 c//+------------------------------------------------------------------+/ ]9 T& p. U# F. l
void OnStart()" i. L6 @' ~& t) O. X F% K9 ?
{$ f: B3 n x( r. U
ENUM_TIMEFRAMES time = PERIOD_D1;$ U4 A- X8 |$ W0 l% a+ r+ |
string szSymbol = "EURUSD";1 n. R# N6 H- W8 n1 e
long id = ChartOpen(szSymbol, time);# _( R' i/ N; x- w2 x) S& ~
ChartRedraw(id);
7 D/ x8 ?5 F+ C4 N. CChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));- A% h* q9 E3 \
}
; E1 E0 R# e. b% ^( V5 O1 w c7 ]然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |