概述
) d" y7 k5 O/ B0 z我们都需要读取图表,任何可以帮助完成此任务的工具都会受到欢迎。 在有助于读取图表的工具之中,指标计算可基于价格、交易量、其它技术指标、或它们的组合,而在交易世界中存在众多新奇的思路。 我们在交易终端中内置了很多现成的指标,如果我们需要添加一些功能以便能适应我们的交易风格,我们会发现这有一些挑战,因为也许无法更改它,除此之外,我们也许无法在交易终端中的内置指标里找到期望的指标。. ]+ c) f9 W( e! E( G! ~+ B+ ?
赫兹量化交易软件1 J4 r5 F9 J4 }
自定义指标和 Heiken Ashi 定义* d( }9 D K4 S* \2 ]
在这一部分中,我们将更详细地学习自定义指标和 Heiken Ashi 指标。 正如我在上一章节的概述中提到的,自定义指标是用户利用 MQL5 编程语言创建的技术分析工具。 它可以在 MetaTrader 5 中用来分析和明晰市场走势,并有助于做出明智的投资决策。 有许多实用的内置技术指标,但有时我们需要根据一些额外和特定的数学、统计或技术概念来分析和理解市场的行为,而这些概念在内置指标中不存在,或者没有指标可以完成任务。 那么,在这种情况下,我们必须自己创建指标 — 这是 MetaTrader 5 平台的功能之一,因为它可以帮助我们创建自己的分析或交易工具,从而满足我们的特定偏好和目标。赫兹量化交易软件
+ e& m ^7 X1 O简单的 Heiken Ashi 指标( b3 |* ], j/ B7 {& B2 V/ w) Y2 L8 x& d; U
在这一部分中,我们将创建一个用在 MetaTrader 5 上的简单 Heiken Ashi 指标。 该指标应持续检查价格(开盘价、最高价、最低价和收盘价),并执行数学计算,以从而成 haOpen、haHigh、haLow 和 haClose 等数值。 根据计算结果,指标应依据数值在图表上绘制不同颜色的烛条:如果烛条方向看涨,则为蓝色,如果为看跌,则为红色。 烛条应作为子窗口显示在传统图表下方的单独窗口之中。赫兹量化交易软件
0 _7 C- E9 \ D- d5 U* e0 [- `我们来查看创建此自定义指标需要完成的所有步骤。
0 G. `8 `: n. X2 W1 r: L! _通过 #property 和标识符值指定附加参数来确定指标设置,如下所示:
$ s; H# `1 M* B" Q3 p Z& l( H. \(indicator_separate_window) 在单独的窗口中显示指标。
* x: L1 P9 P6 J Y/ A- F4 ?(indicator_buffers) 确定指标计算的缓冲区数量。
+ _' q) g& r3 q$ B(indicator_plots) 确定指标中图形序列的数量。 图形系列是在创建自定义指标时可用的绘图样式。
# u# |+ K i a# F" m(indicator_typeN) 要根据 (ENUM_DRAW_TYPE) 的值确定图形绘图的类型,N 是我们在最后一个参数中确定的图形序列的数量,它从 1 开始。
+ O9 U! O9 W) v- ?' I6 h* H(indicator_colorN) 确定 N 的颜色,N 也是我们之前确定的图形序列的数量,它从 1 开始。
" Y+ X- L/ ?& b9 W(indicator_widthN) 还要确定 N 或图形序列的宽度。2 E. i/ r0 K: {% t. w7 j3 M4 z3 M
(indicator_labelN) 确定图形序列 N 的标签设置。
* p& t {* S6 _" W#property indicator_separate_window
: y$ [3 J1 p, }0 k2 R H" O#property indicator_buffers 5' H( Y2 N, Q$ X; u* `( P) _ l
#property indicator_plots 1$ P( i! h" l) F6 _2 t5 w- i
#property indicator_type1 DRAW_COLOR_CANDLES, s' X, |; B+ K9 t* U. q9 M
#property indicator_color1 clrBlue, clrRed$ V ^- V: A; j' K" o* P1 s
#property indicator_width1 2; G/ ^% Y) B- a" g
#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"
$ ], Z1 p ~/ }* ^为指标的五个缓冲区(haOpen、haHigh、haLow、haClose、haColor)创建五个数组,均为双精度类型。# v5 ~3 j0 p7 o, R4 C7 z
double haOpen[];5 U. t: y H+ ~ l1 _( p8 I
double haHigh[];
. D. R1 W! _2 @* M: F7 Edouble haLow[];) Y* ?& C" s+ y3 `9 r2 i. w
double haClose[];
2 @! X2 x: L, v: w u3 wdouble haColor[];8 o- q/ m6 b+ l0 e1 R" B4 p
在 OnInit() 中,此函数初始化正在运行的指标。0 Y: `/ |; F9 n. O% n
int OnInit()1 E6 L! J2 O! O5 F' U
调用(SetIndexBuffer)函数为双精度类型的一维动态数组的指标缓冲区进行排序。赫兹量化交易软件
7 C5 m7 A7 x7 W: r其参数为:$ M" v9 Q' v( F6 k- \# `
index: 指标缓冲区从 0 开始的编号,此数字必须小于在参数 (indicator_buffers) 中确定的声明值。 X, S B/ X! x: ~5 b+ J& z
buffer[]: 在我们的自定义指标中声明的数组。
) Q! w; y$ y% j' _ v# {6 sdata_type: 我们需要在指标数组中存储的数据类型。: W% w7 v2 F& `, E
SetIndexBuffer(0,haOpen,INDICATOR_DATA);
& @" ~, X/ ?, P( ]1 k! WSetIndexBuffer(1,haHigh,INDICATOR_DATA);9 {! y2 @. U6 \
SetIndexBuffer(2,haLow,INDICATOR_DATA);% Y: H- d6 X* m% |; p# T# x1 I) z
SetIndexBuffer(3,haClose,INDICATOR_DATA); q) E$ \/ G+ _2 B
SetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);8 y; W6 q6 W$ @
通过调用(IndicatorSetInteger)函数和调用变体,设置相应指标属性的值,其中我们指定了属性标识符。 其参数为:$ l' w+ K2 @2 ^7 x8 U/ V
prop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_INTEGER)之一的属性的标识符,我们将指定(INDICATOR_DIGITS)。
/ @" m; S7 y$ j% I7 iprop_value: 属性的值,我们将指定(_Digits)。
. g, b% g) y! f1 s3 f; T6 \IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
. |9 h. I# f. R& j$ s调用变体设置相应字符串类型属性的值,我们还在其中指定属性标识符。 其参数为:/ ]- {' s' c | L3 ]9 S- Z* N
prop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_STRING)之一的属性标识符,我们将指定 (INDICATOR_SHORTNAME)为指标设置短名称。+ l! {& H$ J; A" q- d" x
prop_value: 属性的值,我们将指定(“Simple Heiken Ashi”)。
* {! j9 X% W* n0 O4 f/ kIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");, i4 }$ t& W$ f% j
调用(PlotIndexSetDouble)函数设置相应指标的对应双精度类型属性值。 其参数为:
6 r) w+ d- f6 T1 j9 M" {plot_index: 图形绘图的索引,我们将指定 0。- o& D6 A6 Q, h2 r# i/ u- m. I# e2 A. K
prop_id:(ENUM_PLOT_PROPERTY_DOUBLE)值之一,对于无绘图,它是(PLOT_EMPTY_VALUE)。
1 h) ?* `, c, f0 s0 bprop_value: 属性值。. i. {% @" M! \+ ?; ?# C6 U2 {
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
3 }) U) B) M2 D+ X5 `1 s然后返回(INIT_SUCCEEDED)作为 OnInit() 函数的一部分,终止它,并返回初始化成功。
% H& r* J' W* A0 H* f J+ Z, Preturn(INIT_SUCCEEDED);
4 X' Y- h9 ~2 e: n6 O2 U) j在指标中调用的 OnCalculate 函数内部,赫兹量化交易软件
+ h+ D* }6 K# ]+ e2 O基于当前时间帧的时间序列进行计算,其处理的价格数据随着计算类型而变化。
! q9 Q; k t+ j3 C4 P# U8 x; N& O( H* Jint OnCalculate(const int rates_total," K2 l' b1 u' j0 |7 [" m
const int prev_calculated,
5 D( e" X; i2 A8 o8 S( iconst datetime &time[],
9 j" d# S" R5 xconst double &open[],. R! T1 b/ P" l
const double &high[],
& O8 u. v' U0 l! [3 M9 Xconst double &low[],
' v& F7 f- l9 T8 a9 n7 sconst double &close[],& n8 w' q! G, O* B' j& O
const long &tick_volume[],
! ~6 @8 U/ R( s5 Fconst long &volume[],% Q; h& b4 w+ g
const int &spread[])& }% K8 k1 c2 t+ z2 Q; n1 t
创建一个整数型 “start” 变量,我们稍后将为其赋值:
. ~ h; G) g7 L- G! y1 B& \int start;6 v6 Q5 Y4 w; F3 q' R+ V
使用 'if' 语句返回索引值(最低价、最高价、开盘价和收盘价),如果 prev_calculated 等于 0,则 start 值 = 1;或把返回的值(prev_calculated-1)分配给 start:
/ X4 L: m% Y l p" aif(prev_calculated==0)
% R6 ^+ g; D& j, r! v7 U2 G{1 |/ j5 C) ~- x6 R
haLow[0]=low[0];5 o1 v; B- F9 j( \
haHigh[0]=high[0];
3 C+ Q( Y& U& K( S4 yhaOpen[0]=open[0];
! I& O$ h* o5 g* Y2 m+ KhaClose[0]=close[0];
% B3 w2 F2 ~4 a) c; Istart=1;
9 u& Z5 S2 v0 L) p Z}
- c+ { {: p" y' s$ S yelse
; Q [) T3 y8 d0 a/ g5 tstart=prev_calculated-1;
1 g3 e* r- X3 s$ j0 D7 w' z在主循环的 “for” 主体里进行计算,“for” 运算符由三个表达式和可执行运算符组成。
4 M m7 T/ z6 n3 P; T% T' A0 _这三个表达式将是:
+ ]' N5 k3 i& V" _3 i' T! b/ L" Li=start: 对应起始位置。; V, @6 Q/ U3 @/ @+ G% l2 {( u6 n
i<rates_total && !IsStopped(): 完成循环的条件。 IsStopped() 检查指标的强制关闭。
: \# u1 x- o! j- n% @i++: 加 1 作为新的 i。
) n& Y1 V3 y+ v我们每次在循环过程中需要执行的操作:2 m5 M8 l' [" ]+ J8 u. X
计算四个双精度变量4 a: {% ?' @$ t1 ^8 K) F
haOpenVal: 对应 Heiken Ashi 开盘价。% t' S% P9 o6 s8 W
haCloseVal: 对应 Heiken Ashi 收盘价。
& ^* h; [4 X0 o- X) E, qhaHighVal: 对应 Heiken Ashi 最高价。3 e$ K( l( {& X1 K2 q7 b7 R! e' w
haLowVal: 对应 Heiken Ashi 最低价。
0 a- p# d- L$ P6 X+ D. N t' ~$ P在上一步中分配的计算值与以下内容相同" Z J; ]* e/ ?, q2 S
haLow=haLowVal
8 o) d7 G# }5 e) e# ChaHigh=haHighVal
, s# ~- M6 \4 Z/ h0 NhaOpen=haOpenVal |/ Z+ n% e% c: o( ^
haClose=haCloseVal' q" f& C: ]7 V7 @+ N/ k0 f m
检查 Heiken Ashi 的开盘价是否低于收盘价,我们需要指标绘制蓝色蜡烛;如若不是,我们需要把它绘制为红色烛条。; ?) R6 u7 z5 N' C; ^
for(int i=start; i<rates_total && !IsStopped(); i++)( V3 n, w, J. a5 ]4 P! R) _* ]- M! L
{
. Z% |& K9 i; r) Jdouble haOpenVal =(haOpen[i-1]+haClose[i-1])/2;
! ^5 Q# Z" b4 n5 Z( a3 ~double haCloseVal=(open+high+low+close)/4;: c. U2 J/ J* a3 ]
double haHighVal =MathMax(high,MathMax(haOpenVal,haCloseVal));" s/ h9 R9 A/ ?* e1 J% s
double haLowVal =MathMin(low,MathMin(haOpenVal,haCloseVal));
9 W1 S+ K3 T7 bhaLow=haLowVal;) F b; a: A9 |2 R2 A
haHigh=haHighVal;# ~+ F6 K* N% N$ O
haOpen=haOpenVal;
4 v& H. A" a# S- o B2 ]haClose=haCloseVal;% x0 W) T( {5 m; C. H
//--- set candle color
' Y- |" m5 [) `; S& P5 lif(haOpenVal<haCloseVal)( `1 w! k, z" @* M7 a! l- W# a3 R, o
haColor=0.0;! E" q' G$ ~ L; u: ?0 {
else
* h3 B- u6 p: U8 o% S" IhaColor=1.0;
. c& f' e% R7 Q& a0 S}6 ]$ C) F, `/ ^1 ?+ d( ?) `+ z
终止函数,并返回(rates_total)作为下一次调用的 prev_calculated。
/ ?2 e- U+ R! ereturn(rates_total);* x+ \9 D' G/ M
然后我们编译代码,并确保没有错误。 以下是一个模块中的完整代码:
' _8 `& [) O/ ]: }6 E1 m2 e//+------------------------------------------------------------------+, J) ^" z% z+ k' r$ E; t1 z9 |) C& _6 \
//| simpleHeikenAshi.mq5 |
_2 z N: B0 l" z0 S8 A# r//| Copyright 2023, MetaQuotes Ltd. |2 h5 ] t& N! ^# B3 `
//| https://www.mql5.com |! {9 K e: D5 F2 D, x( A l
//+------------------------------------------------------------------+
" S T7 N1 H5 S# ^0 X& Z#property copyright "Copyright 2023, MetaQuotes Ltd."7 u( q) l$ E! o
#property link "https://www.mql5.com" |: N. t8 K* f8 j4 a7 ^
#property version "1.00"
! c5 G; Z* Z4 X# b$ U! e#property indicator_separate_window7 u: H' Z* }1 w6 Q$ g
#property indicator_buffers 5
' p7 I) x2 W. R( y2 k#property indicator_plots 1# C1 O/ L# t7 ]2 z3 |
#property indicator_type1 DRAW_COLOR_CANDLES7 v; B8 E% O1 Q: Z5 h$ C
#property indicator_color1 clrBlue, clrRed1 c$ U0 Y4 f$ Z( \/ T- p" V
#property indicator_width1 2
6 `! e U: D: d9 H$ ^#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"
+ @" b8 p8 F- E' e2 _double haOpen[];
* Q3 e. N4 u" D' Ydouble haHigh[];
/ V9 ]* I7 y0 vdouble haLow[];" ?4 |7 W; n1 W6 I I
double haClose[];' X0 U, y ^/ a5 @# Y, Y M- F- I
double haColor[];
3 K& N9 X6 W! _int OnInit()
1 y. }, t" H4 v1 H* v9 N{
8 b. ~3 K2 p0 d' |7 x& [& l2 xSetIndexBuffer(0,haOpen,INDICATOR_DATA);
: J! e' x3 t. T+ pSetIndexBuffer(1,haHigh,INDICATOR_DATA);6 G$ a9 u: `3 h# @) {7 r' Q
SetIndexBuffer(2,haLow,INDICATOR_DATA);' G8 ]2 E' E& b) o( W" x
SetIndexBuffer(3,haClose,INDICATOR_DATA);
- V% |6 X- v4 Z! V- ^1 [& BSetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);2 \2 U2 i4 k8 X: U
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
+ }# y a/ }0 S( N! \: Q6 I% P( I' \1 bIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
$ H' V: O% @. r# \/ R: V6 Y# [1 O" oPlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
# x* W/ ?; j5 Creturn(INIT_SUCCEEDED);& Z$ X( l) `/ I5 b4 g' t* I- j
}
% u0 G4 c2 c! V5 vint OnCalculate(const int rates_total,+ y9 z, E8 J" C/ _
const int prev_calculated,
7 x, @! b' w, b* h0 b. b, Aconst datetime &time[],- e2 D2 x0 D5 p
const double &open[]," ? B; D* m+ F+ n# P8 B
const double &high[],; [! e" Q: {& t `4 x( W
const double &low[],
2 y/ q9 j) @0 n0 m5 {* h& P' }const double &close[],
, M. |8 l4 h% y7 j" Q% ?0 Yconst long &tick_volume[],# A- V7 m9 R2 Z, o1 ?! ^! ?
const long &volume[], |