概述
0 U ~0 O, ]7 a* K$ X在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。9 v0 w% O, i5 K+ U* B* i. y
在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。$ e% a5 B2 O% `% ?* \. E, q# m4 x
我鼓励您继续阅读,因为内容承诺非常实用。: q! h, I8 R# `. v. T3 X
将指标锁定在特定品种上。6 W V5 H5 W" _0 {
第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:
- D7 S( K8 H; }# p% ^9 W#property copyright "Daniel Jose"0 b" h9 m# ?( Q3 k" w
#property indicator_chart_window, W# v. D3 W0 B" M: |4 {' `
#property indicator_plots 0
1 m) B. V; F) Q//+------------------------------------------------------------------+
+ E4 M0 f% G* `) G* [#include <Market Replay\C_Controls.mqh>% w/ R! ~, t3 b( V5 A8 F
//+------------------------------------------------------------------+
0 q* R( N' P6 R1 c# v0 d/ y# F- UC_Controls Control;7 X: A3 d2 }9 ^ U- C6 J- C
//+------------------------------------------------------------------+
) Z. Q# @ F1 s0 v1 ~% S% M( n% Xint OnInit()1 w: R j% q' S/ n7 W1 ?
{
* R6 `; b0 F \! H w) ]u_Interprocess Info;
3 y1 M7 L2 s8 K) z/ fIndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
& h, L" z2 A# ?1 o( N+ L6 f8 |5 Sif (_Symbol != def_SymbolReplay)4 b; M1 }% _6 s, G$ f
{
# ~; ~/ b! E# n6 Y3 _% d3 _ChartIndicatorDelete(ChartID(), 0, def_ShortName);% k* d" r2 }" O" p O
return INIT_FAILED;
; M U5 d2 G! Q7 z* u, v}5 h! G: `6 x' p+ \2 \
if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
! F# Q% {# D R/ {" T) CControl.Init(Info.s_Infos.isPlay);
' ?) ~/ f/ i( u! ?: g& Y4 freturn INIT_SUCCEEDED;% m# v5 u7 V0 I$ \) \$ Q: D
}6 c1 O& E L/ r1 E2 p- @# G) N
//+------------------------------------------------------------------+
" {. k, C; Y# zint OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) S1 @" T: m. m! O7 W- ^
{
2 r: h$ ~& M8 a8 qreturn rates_total;2 k9 {6 A6 e) B
}+ ~& }: e) s; \4 g
//+------------------------------------------------------------------+
( E1 c5 G, ]" X- Y0 I, k" g; Gvoid OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)3 l3 q, V2 d' ^2 B
{
4 a, {! c5 ]! kControl.DispatchMessage(id, lparam, dparam, sparam);
1 [6 I# G* x, y! d, Y}( a/ r( Q X' Q3 p( o1 Y
//+------------------------------------------------------------------+( s- v( n/ _: {4 P$ ~* t
void OnDeinit(const int reason)
& _* @7 z' p, W" _{: W& N- u; w' c+ e' m2 B
switch (reason)! S2 s* ^4 V* R. \; @
{
2 }5 v* r: b8 |/ X) Ucase REASON_REMOVE:
1 `, ^4 q% F0 J7 c2 bcase REASON_CHARTCLOSE:
3 N7 J1 p( E% m) Bif (_Symbol != def_SymbolReplay) break;
3 Z+ z- s0 _6 ]' `GlobalVariableDel(def_GlobalVariableReplay);
" |3 {8 U! g" S9 Z# vChartClose(ChartID());
8 ?* }0 e0 N# V* V1 a, n {. }break;
+ m# v& C' x! _}
& H) B) x( L) v}( }$ X) C6 N: I: ?
//+------------------------------------------------------------------+. K5 S _8 o C% {/ C# W
我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。
0 ?; J" O2 l# J6 I2 o% G5 T现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。
% B- u5 s" B* [, ^* t如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。8 K7 c% p$ v+ x0 U" M" s
尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。
9 g( P! h. o" T$ D9 X! L因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。/ E5 w; F, h8 g3 a" ?3 N
我们应该避免在同一图表上多次使用指标。! j' c3 y# `( X5 _( a
我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。
. ^1 J/ y- E+ b ^- f* o我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。
T$ y0 ?* j, ^. E为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:- @" Z" y( J) g
#property copyright "Daniel Jose"8 h3 d! m$ D8 x% x8 ^) T4 O% N, C
//+------------------------------------------------------------------+
% `0 E# o" Y! X: {) |, ?9 y6 h2 ^#define def_GlobalVariableReplay "Replay Infos"$ N8 Y" D- @4 _
#define def_GlobalVariableIdGraphics "Replay ID"5 {+ b t, q; P* d
#define def_SymbolReplay "RePlay"9 o7 Y" S' I+ H5 F- c- W
#define def_MaxPosSlider 400
& X+ X# O$ s( T#define def_ShortName "Market Replay"1 ~! H& p) R: n- u* D3 m V4 P N
//+------------------------------------------------------------------+2 e) r, U, N+ t2 O! E5 e2 `/ d
union u_Interprocess, E3 A) `8 o: X; y' e
{. u2 R/ j8 m$ F& ^: q1 j+ K) Z3 R
union u_0( T7 B- C' P5 H3 V6 u
{3 Z! n0 |- G2 g- J1 K0 ~
double df_Value;$ W" a4 N: N1 U1 `& C6 L; U
long IdGraphic;- u; n; d3 Z1 M2 n& a. q; m
}u_Value;
+ i' W) F% z3 j. u- K X2 e, `/ cstruct st_0- q- v8 ^$ ~$ d1 i
{
" P3 X" z! R, J% `3 o5 Jbool isPlay;* j1 @5 }+ @5 C, S* U- J G
int iPosShift;
3 E% n5 u7 |, r6 q: @}s_Infos;
; x# E4 ?) v9 G6 V8 K};7 }# }( Q ~$ A2 |. |& y% @9 w; u
//+------------------------------------------------------------------+ C9 s) \ K5 H
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。9 m' u" F$ Z$ c
注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。7 }( W- Y S- r+ A) z
重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。
3 L# T' s' @" I9 s/ f `在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:2 x; z* W/ C4 |8 i# Y4 |
//+------------------------------------------------------------------+# {7 P( x8 w2 \- W
//| Script program start function |( |, L5 {" H+ f0 s( T
//+------------------------------------------------------------------+& {6 P# M# I& R5 S8 E5 S3 U4 l# r
void OnStart()
# b: Q* o1 s% |5 b{. r: p5 X1 A" k1 Q* ?
ENUM_TIMEFRAMES time = PERIOD_D1;
\" F1 r! U3 g8 e Gstring szSymbol = "EURUSD";
% |/ a7 H3 S! l& U) p: u1 jlong id = ChartOpen(szSymbol, time);
0 T; c0 J+ a# l1 W5 t9 {0 K6 aChartRedraw(id);/ @7 l) F' ]4 i p! k
ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));: y$ a9 t# K; ?( h: Q
}/ z8 \" b. d' p) c# n5 H$ G' n* H! e
然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |