概述( m3 |. ^* F9 ~% v9 f
在上一篇文章我们曾做过一些修正和调整。 不过,依旧还有错误,如该文所附的视频所示。
5 m0 t2 E# Y3 d在本文中,我们将亲眼见证如何修复此错误。 虽然表面上看这似乎很简单,但我们需要遵循若干个步骤。 这个过程将是奇妙和有趣的。 我们的目标是令指标专门应用于特定的图表和品种。 即使用户尝试,他们也无法将指标应用于另外的图表,或在一个会话中多次打开它。
5 F$ ?2 t' ^9 j: e: I3 Q5 k: C我鼓励您继续阅读,因为内容承诺非常实用。
5 G D' S4 p# `, h; J将指标锁定在特定品种上。% _, X" v. V; @! D# [
第一步是将控制指标链接到进行市场回放的品种。 这一步虽然看起来很简单,但对于开发我们的主要任务是必要的。 我们来看看指标代码在上下文中会是什么样子:% m' F& w+ H7 h* E# B- G
#property copyright "Daniel Jose"
0 S1 N2 t9 N+ H& T* E# U$ i! E$ m' g8 E#property indicator_chart_window
. e, f; q# K/ t0 |#property indicator_plots 0; q+ F9 I, a) P6 k6 Q
//+------------------------------------------------------------------+
) E6 w& z! `/ B% U/ h#include <Market Replay\C_Controls.mqh> A; T% X5 M' h& s* }5 j
//+------------------------------------------------------------------+
. c4 w9 D1 ~- U3 d0 fC_Controls Control;' C4 i# p; K# ?9 W( M
//+------------------------------------------------------------------+3 T R8 \+ Q1 S8 P1 L* l0 W3 n
int OnInit()
( F& n+ i0 ~5 b. f) M{
3 y9 M# j# d a& l3 S3 g( Yu_Interprocess Info;
. v" i% `4 b+ `+ O6 zIndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
L) n6 n) M& t, r1 Nif (_Symbol != def_SymbolReplay); ^, b9 p6 c! q! ]8 V4 d% o5 `
{
% V& b4 H2 O3 C1 ^/ t/ n; e9 m: H2 QChartIndicatorDelete(ChartID(), 0, def_ShortName);- ?. C: K. M8 G/ Y* a3 U% h4 t2 M
return INIT_FAILED;" }/ `7 z4 t4 l- g+ H4 @
}' H% O( y" |) R6 Z: y+ x7 Q
if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;" @+ f7 w) c: P9 Z y. G: c
Control.Init(Info.s_Infos.isPlay);8 J" j: |: A% E) H P# `$ g
return INIT_SUCCEEDED; c# i( } o1 j/ t9 k, z) n+ ?
}0 r# G# B' z" L. D* O. j
//+------------------------------------------------------------------+) q5 O+ k" P# ?
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
* t: M; \8 b( F& ~{" T N. g8 d$ a# ?* }0 e/ P
return rates_total;
; P, s% N- {" K# }4 ^+ b}
% o1 m% Z/ A% K" ?//+------------------------------------------------------------------+ s' u+ j; J+ [0 i- P
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)9 X& a; |* k; h# A1 ^
{" t! n$ M0 @: Y
Control.DispatchMessage(id, lparam, dparam, sparam);
. ?7 o2 ? }6 D$ k}
r( B6 X% x' G//+------------------------------------------------------------------+
$ H, l9 q% m, @* J. d( rvoid OnDeinit(const int reason)
: B0 F$ W: e! X+ I& _{
0 o% D+ q) F- G: e% qswitch (reason)
- e$ V$ e0 d1 t! g9 ?- d/ t4 a5 R{
) Z7 U; F) h" kcase REASON_REMOVE:
2 Q/ ?# i* J) {! y+ ~ G; r4 Gcase REASON_CHARTCLOSE:7 n, n) J4 ?6 ] C6 x$ [1 f
if (_Symbol != def_SymbolReplay) break;
H+ v2 h: ~4 @) J7 g6 T* K' `) QGlobalVariableDel(def_GlobalVariableReplay);
+ u+ r$ h6 ~) hChartClose(ChartID());
" ]& {: r* S) {; @ J3 r$ A! Wbreak;
6 s o% R- N6 W8 M A}
* d/ S9 o y2 X# v}1 P) v& O3 F% L! Y( w0 f
//+------------------------------------------------------------------+ x5 B( n0 X+ h
我们首先检查针对的品种是否要进行市场回放。 如果不是这种情况,指标将自动关闭。 请注意,知道指标的名称很重要。 因此,初始化期间执行的第一个函数调用我们的指标,这令我们能够轻松删除它。
_/ }5 U" x: ]- p! ]现在重要的一点是:当您将其从图表中删除时,MetaTrader 5 会生成 DeInit 事件。 此事件触发 OnDeInit 函数,而标记为 REASON_REMOVE 的事件表示从图表中删除指标。 这是因为该品种与指标设计绑定的品种不同。 如果我们不重新检查并阻止代码运行,品种图表将关闭。 不过,幸亏我们的检查,它将保持开放状态。/ x2 @8 \! G- f8 L9 D9 _( S- `
如果指标代码与上一篇文章中提供的代码不同,请不要感到惊讶:上一篇文章重点在于其它改进和修复。 不过,在编写了文章和代码,并录制了本文随附的视频后,我意识到尽管其中一个问题已解决,但另一个问题仍未被发现。 这就是为什么我不得不修改代码。
7 _! k Q2 z' l* i. h! I4 y尽管进行了修改,但我们不会在此处详述所做的全部修改。 必须删除很大一部分,因为它对于此处讨论的锁定方面无效。 因此,上面的代码与以前的代码有很大差别。 不过,我相信上一篇文章中讲演的知识在某些时候可能对某些人有用。 我保存了那篇文章,以表明有时我们都会犯错误,但我们依旧应该努力把事情做好。! t, }! }% D( x4 o
因此,我们来建立第一个锁定步骤,即确保控制指标仅存在于市场回放品种的图表上。 然而,该衡量值并不会阻止向同一图表或不同图表添加多个指标,因此必须进行调整。
3 o& X6 S1 v4 _: E* @4 u/ v) K& s我们应该避免在同一图表上多次使用指标。
! C+ W+ m9 l# [& w0 S我们已经解决了一个问题,现在我们来应对另一个问题。 这里有各种解决方案,这取决于我们真正想要和愿意做什么。 就个人而言,我没有看到这个问题的理想和最终解决方案。 不过,我将尝试提出一种方式,读者能感到熟悉和理解。 最重要的是,该解决方案将完全基于 MQL5。 我甚至考虑过使用外部编码的可能性,但最终决定使用纯 MQL5。 诉诸外部编码,并利用 DLL 进行锁定的想法很诱人,但这太容易了。- ^$ h3 n* r4 s T
我认为在诉诸外部 DLL 来填补 MQL5 语言无法胜任的空白之前,我们在 MQL5 中还有很多东西需要学习。 这将提供一个在使用外部代码时看起来“更干净”的解决方案。 只是,这并无助于更好地理解 MQL5。 此外,这可能会强化 MetaTrader 5 是一个受限平台的误解。 对平台的误解和效力利用不足助长了这种误解。9 }) F' s# x% g+ N6 p
为了应用我们提议的解决方案,您不得不进行一些修改,并撤回其它更改。 第一步是更改 InterProcess.mqh 头文件,令其拥有以下结构:
& x0 {, b/ w X+ t# l$ L, b A#property copyright "Daniel Jose"
) x: B. G3 ]! ~ G4 ?' l ?//+------------------------------------------------------------------+! Z6 \* |1 d7 u8 Y6 s K
#define def_GlobalVariableReplay "Replay Infos"
8 V: e3 m9 h \. J4 [6 ^# P3 a0 u" t* P#define def_GlobalVariableIdGraphics "Replay ID"# @6 n2 t! Q3 P$ ?
#define def_SymbolReplay "RePlay"1 ^5 j% s. n; P" L* `
#define def_MaxPosSlider 400% a2 C& ^. `+ s+ J! i
#define def_ShortName "Market Replay"7 L7 p: [. D( l% K9 U
//+------------------------------------------------------------------+ o+ d4 ^3 @6 l5 H* Y2 J
union u_Interprocess
5 Y% h/ X/ _8 q) e0 h# u{
1 K8 v# G9 ^/ z. x0 s5 runion u_0+ ^4 q/ C& A, F+ B, C0 u
{
3 h; g' p1 w% x- Y) ^double df_Value;, y, S# z3 o" {! u1 e
long IdGraphic;
$ M4 m' I. m% m3 x5 }}u_Value;; Q9 Z: Z- N8 f( L
struct st_0
! U3 b/ F& v: p' [6 B6 d{% y# Z$ o, O/ W4 T1 e5 c* \9 ?6 A9 i
bool isPlay;; T Y5 S0 j" R, s) _
int iPosShift;
) ~4 V: E p: d2 T: }}s_Infos;
/ k+ @/ o2 W. F9 C; x% C% h};* |0 Q" E. F/ r$ F! A* u$ X
//+------------------------------------------------------------------+( G9 d9 @) Q6 C( S
对于许多不熟悉编程的人来说,这似乎有点奇怪,但令人惊讶的是,上述结构仅占用 8 字节的内存。 您也许已经注意到,从上一篇文章的结构中删除了一个变量。 原因是我们将不再使用这种锁定方法。 我们将采取一种不同的方式,稍微复杂一些,但对于把控制指标限定于单个图表方面则有效得多。 这将是一个非常具体和明确的回放服务。
# m W9 j- D- a+ e- S6 f. j4 d注意: 如果 MetaTrader 5 平台和 MQL5 语言的开发人员为该服务提供了向特定图表添加指标的能力,或者允许该服务在图表上调用和执行脚本,那将会很有趣。 使用脚本,我们可以将指标添加到特定图表当中,但目前服务无法做到这一点。我们可以打开图表,但不能向其内添加指标。 当尝试执行此动作时,即使我们使用 MQL5 函数,也始终显示错误消息。 在撰写本文时,MetaTrader 5 版本为 build 3280。
& [$ @, ]4 g o; b9 V7 z0 `重要提示:在撰写本章的这个阶段,这是一个更高级的阶段,由此我才能够实现这一点。 不过,当我撰写这篇文章时,我找不到任何可以帮助解决这个问题的参考资料。 因此,请关注这个回放/模拟系列,看看我是如何想出解决方案的。+ c& f8 h4 Z% b7 M% w% W2 ^
在这种关联境况下,通过运行下面的脚本,我们就能够打开指标,并将其添加到图表之中:
( l! M) B( r/ C1 P: {3 P//+------------------------------------------------------------------+( o8 u2 n( V- a9 W+ X1 z
//| Script program start function |
; J+ f% S: g- W4 ^. P5 N, r: D//+------------------------------------------------------------------+
8 f2 O7 G5 E4 xvoid OnStart()
( o3 z4 b5 S. Z O7 J: {0 S{
/ Y; A' E0 d6 S2 BENUM_TIMEFRAMES time = PERIOD_D1;! }" u, L" Z+ y$ h* X6 u
string szSymbol = "EURUSD";- v. x* f& c( X* X
long id = ChartOpen(szSymbol, time);2 n8 U9 v9 {( P1 A r$ ]& u6 p
ChartRedraw(id);
5 L7 S f) r @5 i- g3 F" \# eChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));. }3 O! [9 b% Q! M6 e
}2 ^0 b0 P: t# l
然而,如果我们将相同的脚本转换为服务,我们就不会得到相同的结果。 |