概述+ Z- t, v8 j+ ^: r
我们都需要读取图表,任何可以帮助完成此任务的工具都会受到欢迎。 在有助于读取图表的工具之中,指标计算可基于价格、交易量、其它技术指标、或它们的组合,而在交易世界中存在众多新奇的思路。 我们在交易终端中内置了很多现成的指标,如果我们需要添加一些功能以便能适应我们的交易风格,我们会发现这有一些挑战,因为也许无法更改它,除此之外,我们也许无法在交易终端中的内置指标里找到期望的指标。
7 w7 i5 F% ]7 x w1 Y+ _赫兹量化交易软件6 c6 x' o, Z* d; L4 @2 k
自定义指标和 Heiken Ashi 定义( |$ m/ X9 B8 ?2 H X, E- Q
在这一部分中,我们将更详细地学习自定义指标和 Heiken Ashi 指标。 正如我在上一章节的概述中提到的,自定义指标是用户利用 MQL5 编程语言创建的技术分析工具。 它可以在 MetaTrader 5 中用来分析和明晰市场走势,并有助于做出明智的投资决策。 有许多实用的内置技术指标,但有时我们需要根据一些额外和特定的数学、统计或技术概念来分析和理解市场的行为,而这些概念在内置指标中不存在,或者没有指标可以完成任务。 那么,在这种情况下,我们必须自己创建指标 — 这是 MetaTrader 5 平台的功能之一,因为它可以帮助我们创建自己的分析或交易工具,从而满足我们的特定偏好和目标。赫兹量化交易软件
/ t# p0 n& t- G. |% n2 r简单的 Heiken Ashi 指标 G5 n& t1 D: C/ D7 L3 Z
在这一部分中,我们将创建一个用在 MetaTrader 5 上的简单 Heiken Ashi 指标。 该指标应持续检查价格(开盘价、最高价、最低价和收盘价),并执行数学计算,以从而成 haOpen、haHigh、haLow 和 haClose 等数值。 根据计算结果,指标应依据数值在图表上绘制不同颜色的烛条:如果烛条方向看涨,则为蓝色,如果为看跌,则为红色。 烛条应作为子窗口显示在传统图表下方的单独窗口之中。赫兹量化交易软件& K& v4 d7 P2 q* q- h* d+ m
我们来查看创建此自定义指标需要完成的所有步骤。
5 Q6 @1 p( @! ^* i% A通过 #property 和标识符值指定附加参数来确定指标设置,如下所示:
( E) d5 ?" T6 r/ E0 }(indicator_separate_window) 在单独的窗口中显示指标。- e. g# @, u2 o# h( k7 c
(indicator_buffers) 确定指标计算的缓冲区数量。
! S0 ^( V3 @8 D5 `' I- h$ i0 N9 R(indicator_plots) 确定指标中图形序列的数量。 图形系列是在创建自定义指标时可用的绘图样式。9 y; J, {( }+ |' H6 R2 ^
(indicator_typeN) 要根据 (ENUM_DRAW_TYPE) 的值确定图形绘图的类型,N 是我们在最后一个参数中确定的图形序列的数量,它从 1 开始。
; R/ _1 {0 \9 F( i1 o9 U+ h9 Y(indicator_colorN) 确定 N 的颜色,N 也是我们之前确定的图形序列的数量,它从 1 开始。
( `2 c! k+ f- a( r(indicator_widthN) 还要确定 N 或图形序列的宽度。& R/ [2 V) f1 x$ i# b
(indicator_labelN) 确定图形序列 N 的标签设置。; J8 m4 e7 h) i" p
#property indicator_separate_window6 B6 P# n7 y. ?9 V2 R
#property indicator_buffers 5$ A# n3 L; Q3 R
#property indicator_plots 1
; {0 N2 `5 ]6 _+ P$ L2 c#property indicator_type1 DRAW_COLOR_CANDLES w! S+ C* ~: C2 z
#property indicator_color1 clrBlue, clrRed9 m% x: P7 i: A8 ]. f% Y
#property indicator_width1 2
( v, z; h8 G: d6 X+ [+ G9 x#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"
% q8 w" _7 R" ?; c为指标的五个缓冲区(haOpen、haHigh、haLow、haClose、haColor)创建五个数组,均为双精度类型。
0 P& }# ~" d8 C0 j3 L2 Bdouble haOpen[];: b9 t* I( K# `, Z! o1 w
double haHigh[];
Y! {) k3 i' @3 y8 A, T# k% Cdouble haLow[];
/ U# B% w: U9 I- Z+ b. zdouble haClose[];: h3 t! T i! o$ M. K
double haColor[];
1 | J" {; F) {) {在 OnInit() 中,此函数初始化正在运行的指标。' H( v) e4 i: ^* R
int OnInit()
7 p4 j4 B7 n3 n) p调用(SetIndexBuffer)函数为双精度类型的一维动态数组的指标缓冲区进行排序。赫兹量化交易软件" J- |" S6 H, x+ h; ]
其参数为:
5 T g# O5 L0 P0 k# M' N1 pindex: 指标缓冲区从 0 开始的编号,此数字必须小于在参数 (indicator_buffers) 中确定的声明值。
" G: x/ ^/ ^- G. Z6 ubuffer[]: 在我们的自定义指标中声明的数组。
& L* d9 D( F ?' `. ddata_type: 我们需要在指标数组中存储的数据类型。
: J( K c% K" }3 [# mSetIndexBuffer(0,haOpen,INDICATOR_DATA);
+ n U: t6 o$ J, Y) NSetIndexBuffer(1,haHigh,INDICATOR_DATA);
$ l5 l9 D2 N8 Q/ uSetIndexBuffer(2,haLow,INDICATOR_DATA);! V' n# |- {8 G0 o
SetIndexBuffer(3,haClose,INDICATOR_DATA);
2 `% w# Y @5 X/ q, ]. [% ^4 aSetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);
4 z7 l: y* Q( ?7 }通过调用(IndicatorSetInteger)函数和调用变体,设置相应指标属性的值,其中我们指定了属性标识符。 其参数为:$ r `1 e, P* o8 r- e
prop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_INTEGER)之一的属性的标识符,我们将指定(INDICATOR_DIGITS)。
- V% g7 t* I6 F$ G! o% Rprop_value: 属性的值,我们将指定(_Digits)。
6 R( U5 } e3 y' EIndicatorSetInteger(INDICATOR_DIGITS,_Digits);8 x* E3 z; k* \8 D* o& ~
调用变体设置相应字符串类型属性的值,我们还在其中指定属性标识符。 其参数为:3 x4 r* a) i/ b& _5 u1 J4 U
prop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_STRING)之一的属性标识符,我们将指定 (INDICATOR_SHORTNAME)为指标设置短名称。
4 Y) N- D1 X! Dprop_value: 属性的值,我们将指定(“Simple Heiken Ashi”)。, Z, {6 o5 `5 W; G$ e
IndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");0 k, |! {4 j) b. e, e! `
调用(PlotIndexSetDouble)函数设置相应指标的对应双精度类型属性值。 其参数为:! R% Y& n2 S6 m" y& r) K
plot_index: 图形绘图的索引,我们将指定 0。
" c9 F; W& C8 c* [" F- {prop_id:(ENUM_PLOT_PROPERTY_DOUBLE)值之一,对于无绘图,它是(PLOT_EMPTY_VALUE)。# r7 | K9 J. P, k8 |
prop_value: 属性值。
9 }2 g+ p" h0 r5 |) ?9 GPlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);6 }6 `2 F l# E) M+ w: b
然后返回(INIT_SUCCEEDED)作为 OnInit() 函数的一部分,终止它,并返回初始化成功。
. [7 L6 j. T9 q; treturn(INIT_SUCCEEDED);
; D& w+ l# p& Y. n在指标中调用的 OnCalculate 函数内部,赫兹量化交易软件
+ r3 ?: u' s* W9 h7 X5 [7 z基于当前时间帧的时间序列进行计算,其处理的价格数据随着计算类型而变化。
' b1 o+ d% Y% j' K \& ~+ R/ wint OnCalculate(const int rates_total,/ ?9 Y. k; O% Z' k( Z
const int prev_calculated,5 E8 _. k# e1 ]$ O$ e; D9 I; Y5 e; t
const datetime &time[],2 @! i# O# ]8 y' M
const double &open[],9 T5 G$ N6 ]- w; X
const double &high[],! Q6 ?' W1 A3 U4 q2 [% S
const double &low[],. L* k& f* w# v9 m" \) F
const double &close[],! W2 r+ C+ Y' w2 v7 S4 p' R
const long &tick_volume[], }6 W% r: u0 B$ U/ d% O2 W+ R( l6 b! q
const long &volume[],
" o1 M# O4 r6 I6 |const int &spread[])$ U7 E/ W2 @" |2 I7 |0 k) o( Q3 A
创建一个整数型 “start” 变量,我们稍后将为其赋值:* \. j4 a3 M D4 X6 g7 r) t5 Y7 F
int start;9 v2 v) |, d, u1 `4 T! x* W+ \# ?
使用 'if' 语句返回索引值(最低价、最高价、开盘价和收盘价),如果 prev_calculated 等于 0,则 start 值 = 1;或把返回的值(prev_calculated-1)分配给 start:
- t+ u. B$ _8 V, J& `$ }1 yif(prev_calculated==0)
9 q9 W9 H6 R) G3 i" @4 u# [{
u+ p7 J" Q/ v! D+ k: ~haLow[0]=low[0];. T4 O0 h# b4 v1 S5 I
haHigh[0]=high[0]; M. H. B6 ]- a+ V& X& B. {
haOpen[0]=open[0]; X8 h! ]6 E o. }( V
haClose[0]=close[0];
. ^) d6 U( b% h @1 {: f* N; \start=1;9 G3 y9 \7 Y5 E# y, R- m0 B
}
# n; B) g$ i" \$ U+ [' zelse
" L8 N# Y2 ^" ^, Q7 Rstart=prev_calculated-1;) C' h2 {/ ~0 j/ I7 b% {
在主循环的 “for” 主体里进行计算,“for” 运算符由三个表达式和可执行运算符组成。; T5 U5 K( O: S
这三个表达式将是:
, p, o" q2 C' @7 g8 T0 b+ E4 E) i [i=start: 对应起始位置。
$ U5 w2 u# ]# r/ D, v! Z4 [$ W/ \$ bi<rates_total && !IsStopped(): 完成循环的条件。 IsStopped() 检查指标的强制关闭。
; b( s( O9 L; r* @# O+ Ii++: 加 1 作为新的 i。
o/ F0 E+ o- m, |% L9 c我们每次在循环过程中需要执行的操作:. p9 q8 d2 Z0 R7 b
计算四个双精度变量& ^8 S& o( ~$ P& }: W
haOpenVal: 对应 Heiken Ashi 开盘价。
6 ~! K8 G# h! k7 j. R$ K* P1 RhaCloseVal: 对应 Heiken Ashi 收盘价。( s) |' J u5 q" E: a5 h; M s) d0 G
haHighVal: 对应 Heiken Ashi 最高价。8 J2 Y( [/ p' F# a
haLowVal: 对应 Heiken Ashi 最低价。5 g; `: [0 U! H- O) j! ?. d
在上一步中分配的计算值与以下内容相同
9 E0 c! d# z% |0 U3 ^8 EhaLow=haLowVal+ \6 `3 L3 w/ i1 g2 q: p
haHigh=haHighVal
2 @0 s5 e$ w9 T) d `6 h6 A. _haOpen=haOpenVal
: j. s3 Q8 H& m2 ?* L1 whaClose=haCloseVal
* s& E$ C. K: i检查 Heiken Ashi 的开盘价是否低于收盘价,我们需要指标绘制蓝色蜡烛;如若不是,我们需要把它绘制为红色烛条。
- t r, q1 y4 \: gfor(int i=start; i<rates_total && !IsStopped(); i++)3 W) t+ v/ V! E+ Z& Z& F
{
# k: Q* f$ p# Y1 ddouble haOpenVal =(haOpen[i-1]+haClose[i-1])/2;1 ?* H' s9 W7 j) c
double haCloseVal=(open+high+low+close)/4;6 Q1 C: l( [& e) }+ H
double haHighVal =MathMax(high,MathMax(haOpenVal,haCloseVal));0 I$ p. w8 w1 p
double haLowVal =MathMin(low,MathMin(haOpenVal,haCloseVal));, Z7 }# j9 H2 Z% U. d1 j9 O
haLow=haLowVal;
( P, P- k8 b; U' Q1 jhaHigh=haHighVal;8 ~" w9 i; L8 v# {) R
haOpen=haOpenVal;
0 k$ Y, j1 |0 n/ d6 yhaClose=haCloseVal;
7 ^* T( }% l( }6 c//--- set candle color
: q) l3 g$ J! i$ Fif(haOpenVal<haCloseVal)7 e8 s& F1 j% q% O e) j
haColor=0.0;
1 Y/ K! h% @" H8 ~else
* b) O2 Z# n; @; P& |! f7 DhaColor=1.0;
' c' C$ U @6 s* P1 x6 d" C; C}5 O; f# e2 Z7 d. ^/ r( a# }/ Z
终止函数,并返回(rates_total)作为下一次调用的 prev_calculated。8 Z, q0 w' }" y* q0 l t
return(rates_total);- F$ k% l+ {3 D# \* G
然后我们编译代码,并确保没有错误。 以下是一个模块中的完整代码:' h8 Q9 m. _& h4 F0 I9 D
//+------------------------------------------------------------------+
" h1 ]1 S: }. J3 b//| simpleHeikenAshi.mq5 |$ P1 `, Q" j1 @4 t
//| Copyright 2023, MetaQuotes Ltd. |
/ \: H, f1 k1 t2 x; V: H//| https://www.mql5.com |% c" N& c; B& S
//+------------------------------------------------------------------+1 Q1 ]/ U s9 r* q6 M( _1 o
#property copyright "Copyright 2023, MetaQuotes Ltd.": C, o% j/ M+ N$ V
#property link "https://www.mql5.com"# P/ \& ]' v8 l4 J! z
#property version "1.00"
; _: e$ b, J, D8 v" k#property indicator_separate_window
5 r. J6 t4 _& h5 x0 c4 i#property indicator_buffers 56 P; _, R) Z; M. g
#property indicator_plots 1
- f+ s+ Y( ]4 G8 ]#property indicator_type1 DRAW_COLOR_CANDLES
1 X* h+ m: W* X' F3 x#property indicator_color1 clrBlue, clrRed; a( E& h$ b: c- e
#property indicator_width1 2
- h% h: b+ A0 }) j+ `( L#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"+ ^# g# @$ o6 v" W+ O
double haOpen[];
4 k5 N+ i* W# Y; K2 e4 E7 N: xdouble haHigh[];; |2 V b& u- o. x; S: J
double haLow[];
! S! b, F- O: x* U2 udouble haClose[];
. `- ^0 D8 j7 Z1 N" Rdouble haColor[];) l$ S: n& W0 ?* c( v/ F; g
int OnInit()3 v* \9 m$ q- Q" j- c
{
# Q, X& M1 S+ w ]SetIndexBuffer(0,haOpen,INDICATOR_DATA);
7 z8 ]* A, r% A9 b" f+ GSetIndexBuffer(1,haHigh,INDICATOR_DATA);
' K9 H5 c2 n! ASetIndexBuffer(2,haLow,INDICATOR_DATA);
9 W0 N$ P( B" X* @) K: CSetIndexBuffer(3,haClose,INDICATOR_DATA);+ T- v9 q7 O5 F& j8 }* j! [
SetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);
3 P! Z* q8 V |- M( H# |2 LIndicatorSetInteger(INDICATOR_DIGITS,_Digits);
I% E3 ^, G' gIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
- r# O) v- L6 i! g: D% x# jPlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
: A5 ?$ i0 c q) breturn(INIT_SUCCEEDED);
/ V0 e# T; Y% R) N$ |; E |}; D( o8 n, Y9 i- @
int OnCalculate(const int rates_total,( i ^9 I9 `) U: @5 u* R
const int prev_calculated,
& U3 r$ m5 h( Y& s( q; J) t) F7 _const datetime &time[],
6 G0 {7 c3 a% u% \) X/ Wconst double &open[],) q( _) p* x [
const double &high[],& R* @) D7 X8 o, E% d U
const double &low[],
: L. n/ J& d; q6 M1 s- |const double &close[],
* u5 }& j5 L& T& ~) yconst long &tick_volume[],
6 y8 t$ ~% s& e W4 d7 m# Sconst long &volume[], |