概述* i5 t. y( C! o# C( I( M6 s
在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。
% i1 L9 S i$ U" U. O) I2 Y在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。
: b( o) N& x$ Z0 V我鼓励您继续阅读,因为内容承诺非常实用。
/ x& U( z( h( r6 ~' [# m* I将指标锁定在特定品种上。. [9 a' m. l" j) a1 q9 I9 T8 H' G6 O
第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:7 L) H1 \. ]- e) `7 O1 a$ ?, {
#property copyright "Daniel Jose"/ ^* N$ i5 u! a/ c4 r# f$ n
#property indicator_chart_window
% x" _6 E$ y9 n3 @#property indicator_plots 0
& f6 d. C2 X# {- @: F( G; L( k$ z7 X//+------------------------------------------------------------------+
0 s" K3 R, }- O#include <Market Replay\C_Controls.mqh>8 A: w1 \* I/ G: T2 U/ }
//+------------------------------------------------------------------+
. f1 |. }6 k" a' S3 V" VC_Controls Control;
; z8 k( H8 E' Y- ^' U% V4 T7 s3 Y//+------------------------------------------------------------------+
' V0 C+ ~8 @1 X* i% uint OnInit()
" ]1 v g, W; S; W/ S5 n- s1 ^9 `{
' F0 k7 S! ]5 y$ |( j8 k2 Qu_Interprocess Info;* l5 M6 ] }9 t
IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);& L- r5 ?5 \) o
if (_Symbol != def_SymbolReplay)6 F6 u/ n8 N) `
{
! i8 y1 V; g1 k5 I9 v! u' @* i( TChartIndicatorDelete(ChartID(), 0, def_ShortName);
' Y7 V$ H0 A( h- c( |return INIT_FAILED;8 k3 Z! g: d. ?+ B
}9 L! q3 T- J- g- G
if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
- c/ q1 z4 E) B' K% }. WControl.Init(Info.s_Infos.isPlay);
$ j8 T* f8 n8 O' B& P2 K0 }return INIT_SUCCEEDED;
) y' |6 E/ Q! J}
7 M# j$ w+ H, b# L//+------------------------------------------------------------------+
! n# E$ O0 ~1 ~5 Zint OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])- y) y- C& ?1 B; v
{
8 U. g7 E+ Q- D1 Oreturn rates_total;
9 W& [/ m9 V$ t- v5 X/ f}
1 w7 Z0 B' E$ T) D//+------------------------------------------------------------------+
0 }+ j/ v% I a! S% fvoid OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam): L4 M8 p& U4 I" O. ]
{& J9 C" h7 b4 }. H9 E! R# _; W1 C7 L, K
Control.DispatchMessage(id, lparam, dparam, sparam);
F; l2 L- R8 N( `7 c. g}3 I" `4 z' L) s- o7 m' F
//+------------------------------------------------------------------++ y5 {/ T2 p2 e4 F
void OnDeinit(const int reason)
: i' C# N3 Y% j4 h) {{3 r! ~8 z6 H7 X( t, p% D
switch (reason)
0 ?: z# z7 l1 _- T; P# r{1 p, Y& s* E' d9 B% _' h0 Y
case REASON_REMOVE:
7 h6 Z8 D3 R$ \* Ocase REASON_CHARTCLOSE:
; Z) N$ K) I3 F4 G/ n* s% Cif (_Symbol != def_SymbolReplay) break;
" d* f2 b. S Q% q7 i& L1 MGlobalVariableDel(def_GlobalVariableReplay);
9 S3 ~4 c7 s$ ?$ K3 fChartClose(ChartID());1 |. S/ B; k* O% c
break;
2 B# x, A* c; a' I# c2 g}, G# ^0 g$ L4 H/ `3 k; t+ r A8 H- t# Y
}
" m# L. w, A2 N6 P V6 Q( ^2 {//+------------------------------------------------------------------+- x' d" t& s; W
我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。- J& p/ r5 P" _( ^& s) s
现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。
% S+ K1 @( }7 v8 G. Z2 c) t S如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。- q" M; A7 A3 D7 I9 A0 C3 ^4 J
尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。' b9 l3 b7 @; F) F( g9 l2 Z" z
因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。8 p4 r2 Z E1 ^, ^' F! X% I, P
我们应该避免在同一图表上多次使用指标。; h# i: D, i( Z7 q( }5 @8 R
我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。
3 r$ b7 ]- i5 z+ m2 C我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。
% s( A" ?* G- ~ U为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:
) P X3 H9 Z, ?* I: @+ d8 n#property copyright "Daniel Jose" L% a6 g# M. q
//+------------------------------------------------------------------+
- z$ o) a& s' Y, S- b2 u0 X4 c* z# Q#define def_GlobalVariableReplay "Replay Infos"
! R/ F: `# Q( v3 l6 n+ G#define def_GlobalVariableIdGraphics "Replay ID"$ O/ s) t2 _2 P1 u/ U5 D& d
#define def_SymbolReplay "RePlay"
9 V8 j0 A( h2 N& X7 L6 x9 r#define def_MaxPosSlider 4001 S3 [1 t4 V% D0 b1 z' r
#define def_ShortName "Market Replay"
0 G0 F" @9 p9 l6 G//+------------------------------------------------------------------+
) B& S( v% C' s/ J# J: g- ^union u_Interprocess
! C" T3 b7 e3 | B/ T" V4 T2 w{
9 l* Z X' M4 G( v/ `" l/ d$ ounion u_0( c2 Z8 V8 F, H$ @
{
0 h1 @$ s6 y4 u3 Gdouble df_Value;% q9 u* F+ R1 y/ ^) z
long IdGraphic;
7 m3 X& A0 a4 ~. e! F' F, Z}u_Value;
8 |4 M/ i+ a! S/ zstruct st_0
3 L4 C$ e2 z" }, B{; Y+ V1 \# G6 {! M# z! v
bool isPlay;
0 @4 C5 y0 S; I% U( S6 Eint iPosShift;
" j, u& K7 W, |' y% E4 L/ v}s_Infos;
e+ @5 S u/ q3 I};6 G+ E6 u& X% \
//+------------------------------------------------------------------+/ S, T1 [# R# m- R
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。2 m0 V; N4 G& N: g4 G/ S
注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。. q" f0 I2 W( M0 z( ^ z
重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。
. I3 g. \3 u! X( ^ F* A7 }在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:& L% z3 {& { Y8 _* M
//+------------------------------------------------------------------+4 J4 i* b+ f( d$ y2 Q
//| Script program start function |
4 k; N' B' X4 E4 h' o: \//+------------------------------------------------------------------+7 G. l( `" F* B' ^
void OnStart()' {0 W$ M( V% o! `* K
{
k5 A( O* |7 X9 l% c9 I0 CENUM_TIMEFRAMES time = PERIOD_D1;/ k0 @3 j; ^ O* {# z8 ?) Z
string szSymbol = "EURUSD";. v" n/ z3 ^3 {) O7 y
long id = ChartOpen(szSymbol, time);! M- w, K4 Y5 `$ n$ t
ChartRedraw(id);( {0 ^+ {+ R& @" U: D% ]) Z
ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
& n1 A* V# i/ v) W. Y. N}
6 I( ?6 {" v0 \* X然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |