概述
! ?" q1 y- F$ u0 a" p3 [! d# a我们都需要读取图表,任何可以帮助完成此任务的工具都会受到欢迎。 在有助于读取图表的工具之中,指标计算可基于价格、交易量、其它技术指标、或它们的组合,而在交易世界中存在众多新奇的思路。 我们在交易终端中内置了很多现成的指标,如果我们需要添加一些功能以便能适应我们的交易风格,我们会发现这有一些挑战,因为也许无法更改它,除此之外,我们也许无法在交易终端中的内置指标里找到期望的指标。
- m' v/ _$ X6 T- d赫兹量化交易软件
6 ~& ~3 [4 Y5 @) l4 ]- a自定义指标和 Heiken Ashi 定义
/ a- D: T0 V! e0 o" E7 L在这一部分中,我们将更详细地学习自定义指标和 Heiken Ashi 指标。 正如我在上一章节的概述中提到的,自定义指标是用户利用 MQL5 编程语言创建的技术分析工具。 它可以在 MetaTrader 5 中用来分析和明晰市场走势,并有助于做出明智的投资决策。 有许多实用的内置技术指标,但有时我们需要根据一些额外和特定的数学、统计或技术概念来分析和理解市场的行为,而这些概念在内置指标中不存在,或者没有指标可以完成任务。 那么,在这种情况下,我们必须自己创建指标 — 这是 MetaTrader 5 平台的功能之一,因为它可以帮助我们创建自己的分析或交易工具,从而满足我们的特定偏好和目标。赫兹量化交易软件" Z( `7 Q/ C3 }- Z/ N
简单的 Heiken Ashi 指标- T, a7 M% O* S# d
在这一部分中,我们将创建一个用在 MetaTrader 5 上的简单 Heiken Ashi 指标。 该指标应持续检查价格(开盘价、最高价、最低价和收盘价),并执行数学计算,以从而成 haOpen、haHigh、haLow 和 haClose 等数值。 根据计算结果,指标应依据数值在图表上绘制不同颜色的烛条:如果烛条方向看涨,则为蓝色,如果为看跌,则为红色。 烛条应作为子窗口显示在传统图表下方的单独窗口之中。赫兹量化交易软件- Y- `" ]. [! i% b! d0 }
我们来查看创建此自定义指标需要完成的所有步骤。
, Q) w4 u9 T* C$ `5 @, Q/ k通过 #property 和标识符值指定附加参数来确定指标设置,如下所示:
% {1 @0 u+ @$ ^1 `% Q- m Y/ v(indicator_separate_window) 在单独的窗口中显示指标。
8 ~1 j- J" A6 \' K( `+ m(indicator_buffers) 确定指标计算的缓冲区数量。
% G0 R8 O3 h3 O; ^+ a$ ?(indicator_plots) 确定指标中图形序列的数量。 图形系列是在创建自定义指标时可用的绘图样式。1 `0 z% e- x2 C" T
(indicator_typeN) 要根据 (ENUM_DRAW_TYPE) 的值确定图形绘图的类型,N 是我们在最后一个参数中确定的图形序列的数量,它从 1 开始。* a1 k4 ]; f1 f4 L) P
(indicator_colorN) 确定 N 的颜色,N 也是我们之前确定的图形序列的数量,它从 1 开始。
# P) c5 }6 ~' ~& J8 G! x(indicator_widthN) 还要确定 N 或图形序列的宽度。
& `/ Y$ q: Z: f. S& {(indicator_labelN) 确定图形序列 N 的标签设置。0 H3 ]: U9 D g5 ]4 ~+ z1 R
#property indicator_separate_window) N& }, b( M4 L3 M$ t
#property indicator_buffers 5
3 [6 d& n' E, T#property indicator_plots 19 g$ k1 k* M6 @
#property indicator_type1 DRAW_COLOR_CANDLES% P0 i0 M- V4 C1 s; A: c
#property indicator_color1 clrBlue, clrRed
6 {/ ?" n7 r* H: z0 R#property indicator_width1 2
; ~# y& C8 K: D* H! }#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"- B" z/ l, y" H0 t* C
为指标的五个缓冲区(haOpen、haHigh、haLow、haClose、haColor)创建五个数组,均为双精度类型。
% U* Q* G5 k( W7 g% Adouble haOpen[];
& Y4 B/ L$ R( {) M3 Bdouble haHigh[];, v& {. q2 j; `6 {" j5 J
double haLow[];
7 s" }8 E% R4 ~! P o: pdouble haClose[];1 D% k; _ v1 I( H+ ~
double haColor[];
* ?, @) Z3 C6 @& i6 d在 OnInit() 中,此函数初始化正在运行的指标。1 _! M! `9 H3 L5 Q, ^4 X
int OnInit()
" R: j7 {* D1 A$ y n0 b9 D5 ^调用(SetIndexBuffer)函数为双精度类型的一维动态数组的指标缓冲区进行排序。赫兹量化交易软件
- W- ^ ]: j& B7 q! H其参数为:& o; B8 }/ L; q; x9 m
index: 指标缓冲区从 0 开始的编号,此数字必须小于在参数 (indicator_buffers) 中确定的声明值。+ r" G" T' E. R# a' C# K% W. @
buffer[]: 在我们的自定义指标中声明的数组。9 c9 W7 i) [+ U6 q
data_type: 我们需要在指标数组中存储的数据类型。
; a: O. C i& a/ b, f+ w" YSetIndexBuffer(0,haOpen,INDICATOR_DATA);
% O I4 g6 v+ G, Y# E ~SetIndexBuffer(1,haHigh,INDICATOR_DATA);8 l9 h6 k& V3 C/ h% C( n
SetIndexBuffer(2,haLow,INDICATOR_DATA);
9 f1 l1 ^- t3 [& _7 w7 CSetIndexBuffer(3,haClose,INDICATOR_DATA);
4 M0 X9 `' {9 [9 s* E& }6 hSetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);0 o' ~# g2 r5 a+ j
通过调用(IndicatorSetInteger)函数和调用变体,设置相应指标属性的值,其中我们指定了属性标识符。 其参数为:
6 T, h2 e$ A& X% ?& X6 N6 Z# u' ?% Kprop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_INTEGER)之一的属性的标识符,我们将指定(INDICATOR_DIGITS)。
8 l) u# e3 x. n* ^* oprop_value: 属性的值,我们将指定(_Digits)。
0 c# j* |* X# F$ x# {7 KIndicatorSetInteger(INDICATOR_DIGITS,_Digits); ]$ a# C% C, J2 O
调用变体设置相应字符串类型属性的值,我们还在其中指定属性标识符。 其参数为:
$ i. I* f2 Z4 E% W$ gprop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_STRING)之一的属性标识符,我们将指定 (INDICATOR_SHORTNAME)为指标设置短名称。+ I7 `' g7 L/ x# v
prop_value: 属性的值,我们将指定(“Simple Heiken Ashi”)。
; B+ t& F1 W& S- v; P9 x" KIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
+ b# i0 ?: @5 F. o. {& |调用(PlotIndexSetDouble)函数设置相应指标的对应双精度类型属性值。 其参数为:$ ~$ U: Z8 D3 e: V+ _/ \
plot_index: 图形绘图的索引,我们将指定 0。6 D* A, F% ]1 l, I4 Q
prop_id:(ENUM_PLOT_PROPERTY_DOUBLE)值之一,对于无绘图,它是(PLOT_EMPTY_VALUE)。
8 b8 B' Y8 C' u- m$ Qprop_value: 属性值。: x- C3 @1 N* T4 R0 E9 E4 B3 d$ H( j
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);) j2 p4 q! D1 F, G2 q, W
然后返回(INIT_SUCCEEDED)作为 OnInit() 函数的一部分,终止它,并返回初始化成功。1 V9 q$ j4 S1 K2 ^& g' g
return(INIT_SUCCEEDED);
! S$ Q/ x9 T4 h& E. J# z在指标中调用的 OnCalculate 函数内部,赫兹量化交易软件
" H" d! Z1 ^# F% d& w* v8 t) Q. L基于当前时间帧的时间序列进行计算,其处理的价格数据随着计算类型而变化。
. n8 C& W& N9 V# [int OnCalculate(const int rates_total,3 g) t* G( z$ ^& P- h) |4 O
const int prev_calculated,
8 p, K7 n- o5 ]" ]1 o$ N* O5 Yconst datetime &time[],
# ?# l# {' z. n3 J lconst double &open[],
! _5 w0 X! }% G5 pconst double &high[],# j" D4 H2 X5 T+ ]
const double &low[],
& |* {7 X& Q$ I! ^2 E8 A; Iconst double &close[],
) \" C9 t* F* X' y y# wconst long &tick_volume[],* b, c. `, ?$ W5 w6 [- r
const long &volume[],
" n9 b& P4 J) P. Q& i4 ?const int &spread[])* P3 ^+ R# {' E8 N0 n
创建一个整数型 “start” 变量,我们稍后将为其赋值:
) P- D$ b' C5 A# n& |) X' I eint start;
3 x. V, s% v% Q0 H4 J' _) j使用 'if' 语句返回索引值(最低价、最高价、开盘价和收盘价),如果 prev_calculated 等于 0,则 start 值 = 1;或把返回的值(prev_calculated-1)分配给 start:, R8 w/ }1 Q# C/ v* w: m: f1 p3 e
if(prev_calculated==0)
# }0 c. l& Y F. W% t, V( U{
/ N! M% r' v3 l( G3 |8 D0 x. ~haLow[0]=low[0];( U& n. x/ H4 B9 k4 u$ R
haHigh[0]=high[0];7 J4 }+ P. O6 I# W) P
haOpen[0]=open[0];5 D$ [2 i" |0 U; G2 D) t- D, {( A
haClose[0]=close[0];7 [/ H. D9 E$ ?+ y ^( s! }
start=1;/ o2 Y3 B3 d1 f0 m; a+ b* W: ^) F
}9 }$ ^0 O9 ^. H W7 k, Q$ z$ I* i
else( w. \) X: o) B7 b
start=prev_calculated-1;
: l6 h) ]% t9 C5 F在主循环的 “for” 主体里进行计算,“for” 运算符由三个表达式和可执行运算符组成。
9 g3 M( ~7 {- \( G' n3 B这三个表达式将是:1 ], ]( |0 | Q) B
i=start: 对应起始位置。
7 b2 T' v1 S' Z7 S6 H% K2 X, ~i<rates_total && !IsStopped(): 完成循环的条件。 IsStopped() 检查指标的强制关闭。& I7 W0 e& W" z+ J6 Q9 {
i++: 加 1 作为新的 i。
2 B2 x* _6 R* S* r5 O" P6 T0 ]% ?我们每次在循环过程中需要执行的操作:! y( K0 Y" @7 ?$ I1 |
计算四个双精度变量. C1 O& U2 o* M! @2 P; \9 M
haOpenVal: 对应 Heiken Ashi 开盘价。
# Q) f# T2 @' ]/ _2 a7 ]. vhaCloseVal: 对应 Heiken Ashi 收盘价。
: ~# Z' U. F, nhaHighVal: 对应 Heiken Ashi 最高价。! ?( G( X) A6 A( y
haLowVal: 对应 Heiken Ashi 最低价。. m' u' x5 i$ [! Q# y+ |6 w
在上一步中分配的计算值与以下内容相同
) z1 e- F2 p+ w' o1 [. c0 [& xhaLow=haLowVal7 \. ~* L4 G' j! \: A% X
haHigh=haHighVal1 A1 [* i8 v ^ w+ U
haOpen=haOpenVal6 w+ l7 e2 g: O5 ^* G/ `
haClose=haCloseVal
2 T) y9 m! H% m# Z! c+ `检查 Heiken Ashi 的开盘价是否低于收盘价,我们需要指标绘制蓝色蜡烛;如若不是,我们需要把它绘制为红色烛条。- Q) G2 `: {9 Z6 K, p6 P% e* I
for(int i=start; i<rates_total && !IsStopped(); i++)
) e7 T& c% p' z, K8 z{+ i1 F0 e! u9 Z \) @! _0 z# ~1 i
double haOpenVal =(haOpen[i-1]+haClose[i-1])/2;
1 W7 q# k( M6 O- pdouble haCloseVal=(open+high+low+close)/4;
8 _7 K6 Z# R) z' ~6 c; g3 J7 gdouble haHighVal =MathMax(high,MathMax(haOpenVal,haCloseVal));5 l! ?+ e# r0 q* m+ V5 p, p
double haLowVal =MathMin(low,MathMin(haOpenVal,haCloseVal));8 Z: p$ ]- c; U- b
haLow=haLowVal;& `" |: l5 l3 ^, ~) l/ ^2 p
haHigh=haHighVal;( h( T$ K5 b7 n; w6 D) A, n
haOpen=haOpenVal;
5 f5 t0 J8 P* g# PhaClose=haCloseVal;) I3 y2 `0 L. k8 N; u2 g; o
//--- set candle color/ o9 d* R* I& r( y+ p. n2 D* A
if(haOpenVal<haCloseVal)$ ~- n' Y5 j) m) v! W7 C, ~1 m! V
haColor=0.0;" Y, V5 {0 A9 z, G' n
else. Y: F; P- r' K' L: `
haColor=1.0;
) H' }' r. u# h" h0 N- I. _}
" B3 Z n4 |* n* M2 w4 E7 n# a5 g5 X终止函数,并返回(rates_total)作为下一次调用的 prev_calculated。. R# P: e7 E0 @- {
return(rates_total);# y, E4 s# X) N
然后我们编译代码,并确保没有错误。 以下是一个模块中的完整代码:% E' A$ \# b2 N3 M) h
//+------------------------------------------------------------------+$ p( l/ @& q) N
//| simpleHeikenAshi.mq5 |; i% `* b+ ]& J/ x4 d- A6 H! m
//| Copyright 2023, MetaQuotes Ltd. |
9 d* s4 c: [4 Y//| https://www.mql5.com |
8 H. W2 M3 `, c# v7 q//+------------------------------------------------------------------+' w9 |/ O5 i: x+ N; g1 a; B3 B
#property copyright "Copyright 2023, MetaQuotes Ltd."
, ~2 A0 i1 `' R. L#property link "https://www.mql5.com"/ @$ M6 d$ y8 n$ X( Y( x. N" ^" Z
#property version "1.00") y% [- a; f0 [4 d' n
#property indicator_separate_window
$ ]" @( r; [' Y5 E: k0 E3 G& {#property indicator_buffers 5
2 }7 h/ h& M& v9 M. g# m) t) r#property indicator_plots 1
. E7 a5 l2 u4 Y#property indicator_type1 DRAW_COLOR_CANDLES
/ W* |& f6 G2 ?8 M* m( P#property indicator_color1 clrBlue, clrRed$ s* H$ p6 `, U! a
#property indicator_width1 2: @/ g$ @& Z* E2 v5 |& h P
#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close", o( R' C0 J0 _0 w5 \
double haOpen[];: H/ g& P: D4 o4 @# L" `
double haHigh[];) k' R: H' g( R9 ^) }4 w
double haLow[];
# P7 T% i; _2 l$ ^double haClose[];
% i; s" e5 ] p/ P6 T& x# ldouble haColor[];& F6 }) D5 \) K3 }- C5 c
int OnInit(): ]& p0 ]5 K& T, T) d4 @
{" J; O3 Z, D. |$ L
SetIndexBuffer(0,haOpen,INDICATOR_DATA);
, o% @: b+ R% ^! O5 BSetIndexBuffer(1,haHigh,INDICATOR_DATA);
' O" H. e8 N' M, _SetIndexBuffer(2,haLow,INDICATOR_DATA);
: y8 p4 I# z' _9 `/ }" o6 p7 pSetIndexBuffer(3,haClose,INDICATOR_DATA);
0 _( X! ?( u9 Y% T$ hSetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);0 |; R8 P: Q, X( {; [' y7 @& L6 H
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
# Y5 P9 d9 z4 L. Z: h0 t; T9 HIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
! e$ [0 O) [4 I1 HPlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);# e3 y4 r) ]- T7 _0 K
return(INIT_SUCCEEDED);
6 w. E9 w5 k$ q l# K8 U) ]3 O}8 h5 I& f# |. `
int OnCalculate(const int rates_total,& z# L) F& `! n1 a; }" n, B" f l
const int prev_calculated,4 O% I/ i8 J9 |/ I$ G
const datetime &time[],
" I2 Y5 _4 q' V1 r) @const double &open[]," u% R! O2 N3 A: `
const double &high[],/ t( o/ B7 ?2 W. W$ @# T% T
const double &low[],
. D, W, `& T, U$ P: {const double &close[],
% p, s) G# M& k8 h6 U/ ^const long &tick_volume[],7 G- P+ d- s! [
const long &volume[], |