概述' U2 B4 G% y! o, j! b
在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。
- z m/ \( B: @0 a: P在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。
/ t6 M% q& N3 o6 U; {0 I5 e我鼓励您继续阅读,因为内容承诺非常实用。' e/ P" F- B$ W/ f0 Y! |
将指标锁定在特定品种上。6 ~( g' p# i: {' j P
第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:* v5 ?/ i' G' Q$ H' y
#property copyright "Daniel Jose"/ U# E1 K* E7 e9 ]! y
#property indicator_chart_window! V; t# C' ^' v6 R8 n( h: W/ `4 c6 j
#property indicator_plots 0: U: u9 q5 p" g. j# F& o
//+------------------------------------------------------------------+4 K) U, H; ^$ f5 I
#include <Market Replay\C_Controls.mqh>; O$ T* F8 ^" J6 ~% _
//+------------------------------------------------------------------++ o4 ~8 ^& N2 a" a8 O
C_Controls Control;
/ Z: L1 m$ A* o+ C3 L//+------------------------------------------------------------------+# ]- d. Q3 H4 |9 J# W3 n
int OnInit()8 h" ]1 O+ {* f( y& o3 d' p
{ j& z( }# [: g& k
u_Interprocess Info;
- X* B% a4 Y: e3 C$ VIndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);- C: g; C/ n6 X) I4 j" ?
if (_Symbol != def_SymbolReplay); w4 S' ~5 g: Q* n' s
{
6 ]1 O3 X3 s4 W0 GChartIndicatorDelete(ChartID(), 0, def_ShortName);
% F2 j$ l3 e: Y8 o# Treturn INIT_FAILED;+ l* @5 f$ i: }' Q
}
8 A! i/ T, ]+ p$ Z, [( I oif (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
/ D/ k1 Z1 ?% m3 \Control.Init(Info.s_Infos.isPlay);
8 l' C: f' \( \, g. mreturn INIT_SUCCEEDED;( ~' K! H5 `- m1 Z9 W: r( m2 _
}& W4 A% C7 K K3 L8 {5 X2 P9 p
//+------------------------------------------------------------------+
+ L! F- g6 D$ T7 I; zint OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
* l% @$ H/ I! P0 F( D7 R- I{ s1 X$ L, g3 O6 P" ^) i
return rates_total;
/ i5 w- V1 M5 S; X9 b5 s}
6 {) u! L, J: a2 e1 {//+------------------------------------------------------------------+5 x8 `7 Q; a( c {) I
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)3 z4 L* T& l9 ?2 }; e; A( ~
{
" W; S" H% F, c5 iControl.DispatchMessage(id, lparam, dparam, sparam);1 n4 j9 N2 A9 h; H! y5 l
}
4 Q& ]* E# v' D/ ?, r4 c# o) Y//+------------------------------------------------------------------+
! S1 s. Z; \; E4 u" w* _void OnDeinit(const int reason)
$ F1 d% R1 H X+ y+ t( r0 h{( U! g, W8 W& i+ }
switch (reason)
% Z% c# L- d$ z! _# g{5 }& F9 }" E: n- K. R9 S' |' W3 z
case REASON_REMOVE:
w5 E$ z" A: z8 o% Icase REASON_CHARTCLOSE:1 m6 f% N; e- m. D! V/ k1 Q% i
if (_Symbol != def_SymbolReplay) break;$ Z" p+ K( Q9 U* h' W6 ^
GlobalVariableDel(def_GlobalVariableReplay);0 e5 L8 h; b E& |
ChartClose(ChartID());
6 T7 o6 H" v1 I# D$ q* C6 ~break;
2 Q7 q/ S- a6 B j) U}
1 ]- ^( l9 c) m6 d$ g- A}
7 V0 k) {. _* q+ Q2 s3 i7 }//+------------------------------------------------------------------+- T' l- W( q) l% L/ y( j
我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。1 J1 Z( M- {! m8 d
现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。/ E( A8 q! y1 E% l8 m$ b$ n
如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。- `$ p' O2 J' o- i: r
尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。
) y* }' Z, r7 J/ ?因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。
( n$ v# G8 }( I* T我们应该避免在同一图表上多次使用指标。
/ W' Q, q: |1 ?+ Y; `; b我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。; T: |( ]" C( B
我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。
. T3 o9 s# D: h+ x- | h5 Y为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:
P: Q/ t8 N! d/ p8 v$ F#property copyright "Daniel Jose"7 A- @# o4 P7 a, e. F
//+------------------------------------------------------------------+7 `5 | [( ^) G W1 m0 h) }
#define def_GlobalVariableReplay "Replay Infos"
: [( U4 t6 E" c3 E* y#define def_GlobalVariableIdGraphics "Replay ID"
( d K* ? ^4 s' U7 l5 K3 q/ R#define def_SymbolReplay "RePlay". ]4 I) @. h9 [0 M, Q1 N
#define def_MaxPosSlider 400 B: J" K9 S' |; ?* j* ]5 Z
#define def_ShortName "Market Replay"
. k( }8 f0 l9 s3 p5 l* U- c//+------------------------------------------------------------------+2 [6 d2 n ?$ N0 s) k: Z: V
union u_Interprocess
% y" i$ B$ S- u8 g, C{) ]. E% U- [7 P4 I- e b
union u_0
8 B( r6 L2 ^1 I7 ~# k+ r{ x) b1 P4 L# d6 F) A W9 O
double df_Value;1 n) s3 ^- c [ l1 r
long IdGraphic;
3 m% u4 j. j! x0 ?6 c}u_Value;- c/ H, S4 B$ R* R$ k
struct st_0$ y+ r+ p* ?0 u6 i0 V: n
{
o* R% L5 Q1 ~% ubool isPlay;4 J [' }. B- A8 h/ ~
int iPosShift;) f4 q' D- f/ C$ r9 h) C ?. E3 w( Y
}s_Infos;2 k, p6 A$ a$ H9 G; A
};
: w4 Z) p* q( R! T7 w+ }& i//+------------------------------------------------------------------+# `( _- q: V; {0 V- e
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。# V- z. [& {+ |+ ^: x
注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。
; m+ v+ M* w9 P重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。3 P4 W' X, y$ O& U% l( C6 j1 W
在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:( {% T, \: n3 ]5 i% `7 |; j$ {
//+------------------------------------------------------------------+# b9 ]8 s8 `. R4 ]/ _
//| Script program start function |; r7 ~ W$ ?; |9 F6 i) R
//+------------------------------------------------------------------+
7 [6 {; f* ?' [5 |+ Z/ \+ Hvoid OnStart()
) L% F' e4 K9 D4 {# _{, r( f; E g" J
ENUM_TIMEFRAMES time = PERIOD_D1;
. o- x# Y9 V: K% w dstring szSymbol = "EURUSD";
' \, L1 g6 f- P9 @1 {5 llong id = ChartOpen(szSymbol, time);" s/ X( j0 x# K! I
ChartRedraw(id);% w# p, K3 M$ ?, \/ J: w
ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));& C$ b2 y: X% ^1 h" g$ @, I
}
" W' R9 X; q1 _- @# _. ~然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |