概述
G k' A; ?# [- L7 l! J5 A' V我们都需要读取图表,任何可以帮助完成此任务的工具都会受到欢迎。 在有助于读取图表的工具之中,指标计算可基于价格、交易量、其它技术指标、或它们的组合,而在交易世界中存在众多新奇的思路。 我们在交易终端中内置了很多现成的指标,如果我们需要添加一些功能以便能适应我们的交易风格,我们会发现这有一些挑战,因为也许无法更改它,除此之外,我们也许无法在交易终端中的内置指标里找到期望的指标。' ~* `5 X6 h7 y; k9 `5 ?% J
赫兹量化交易软件6 t1 q) b4 V/ W$ W
自定义指标和 Heiken Ashi 定义
5 D2 x, f) G$ F5 C% `: d7 R$ c在这一部分中,我们将更详细地学习自定义指标和 Heiken Ashi 指标。 正如我在上一章节的概述中提到的,自定义指标是用户利用 MQL5 编程语言创建的技术分析工具。 它可以在 MetaTrader 5 中用来分析和明晰市场走势,并有助于做出明智的投资决策。 有许多实用的内置技术指标,但有时我们需要根据一些额外和特定的数学、统计或技术概念来分析和理解市场的行为,而这些概念在内置指标中不存在,或者没有指标可以完成任务。 那么,在这种情况下,我们必须自己创建指标 — 这是 MetaTrader 5 平台的功能之一,因为它可以帮助我们创建自己的分析或交易工具,从而满足我们的特定偏好和目标。赫兹量化交易软件9 x/ h3 ^) j$ w# {0 c
简单的 Heiken Ashi 指标
& c) S6 i+ n" i9 I: B在这一部分中,我们将创建一个用在 MetaTrader 5 上的简单 Heiken Ashi 指标。 该指标应持续检查价格(开盘价、最高价、最低价和收盘价),并执行数学计算,以从而成 haOpen、haHigh、haLow 和 haClose 等数值。 根据计算结果,指标应依据数值在图表上绘制不同颜色的烛条:如果烛条方向看涨,则为蓝色,如果为看跌,则为红色。 烛条应作为子窗口显示在传统图表下方的单独窗口之中。赫兹量化交易软件' D2 n# b |5 v
我们来查看创建此自定义指标需要完成的所有步骤。+ [: t3 U4 s6 y
通过 #property 和标识符值指定附加参数来确定指标设置,如下所示:! B, k3 ?2 Y! Q
(indicator_separate_window) 在单独的窗口中显示指标。
5 n, Y- O# l# P: J+ `2 D' n- Y(indicator_buffers) 确定指标计算的缓冲区数量。- ~& b$ g9 Q" m$ N& d) t
(indicator_plots) 确定指标中图形序列的数量。 图形系列是在创建自定义指标时可用的绘图样式。; n( `' @ o5 P9 t8 ^8 V' {
(indicator_typeN) 要根据 (ENUM_DRAW_TYPE) 的值确定图形绘图的类型,N 是我们在最后一个参数中确定的图形序列的数量,它从 1 开始。
0 Z# _6 Z- k" P(indicator_colorN) 确定 N 的颜色,N 也是我们之前确定的图形序列的数量,它从 1 开始。' d. E8 [' x- h
(indicator_widthN) 还要确定 N 或图形序列的宽度。5 V A- Y3 f2 I W
(indicator_labelN) 确定图形序列 N 的标签设置。+ Y' [1 @/ {( j' h2 C6 J
#property indicator_separate_window
6 m! {9 ?; E0 Y* l9 Q5 f#property indicator_buffers 5! `$ T- p- i, ?! I, c. T
#property indicator_plots 1
% s& b1 k6 @' r7 U#property indicator_type1 DRAW_COLOR_CANDLES
9 m- r& c1 J! `" ~, g- N#property indicator_color1 clrBlue, clrRed
8 q6 j" e6 f9 w#property indicator_width1 2
2 V: U7 D1 Z0 P' q8 h#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"
$ I$ N+ a9 J6 M$ I) c, i2 Q# t. V为指标的五个缓冲区(haOpen、haHigh、haLow、haClose、haColor)创建五个数组,均为双精度类型。
6 j+ x' R5 X, N! S3 X" ddouble haOpen[];
1 O, ?- C4 Z4 A# A3 }& C# Idouble haHigh[];
% i; p1 v s9 O# m: t2 Fdouble haLow[];
5 N* G( y! z# g. I* q" F7 k% Adouble haClose[]; Z( b, D# b. R5 Z9 e
double haColor[];
" j! T4 z' L+ h1 y; E) c8 @1 J在 OnInit() 中,此函数初始化正在运行的指标。
+ q) Y5 V( J& \- _( _int OnInit(), \2 \7 f, r/ V9 m& w: H+ |9 k
调用(SetIndexBuffer)函数为双精度类型的一维动态数组的指标缓冲区进行排序。赫兹量化交易软件4 u6 L- [! }9 ]
其参数为:2 p' X* d3 z/ } y5 J; r, g
index: 指标缓冲区从 0 开始的编号,此数字必须小于在参数 (indicator_buffers) 中确定的声明值。3 H* M. M9 j3 x' u
buffer[]: 在我们的自定义指标中声明的数组。
" g/ @2 k# l7 Q' Jdata_type: 我们需要在指标数组中存储的数据类型。* l( L5 o4 i0 c
SetIndexBuffer(0,haOpen,INDICATOR_DATA);! R* g1 M" Z2 b
SetIndexBuffer(1,haHigh,INDICATOR_DATA);
$ Y0 F0 s8 [& ~3 l8 L' eSetIndexBuffer(2,haLow,INDICATOR_DATA);2 H6 l) l2 Q3 w5 e3 y: I
SetIndexBuffer(3,haClose,INDICATOR_DATA);+ ]3 t f5 P% r9 d
SetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);
u$ T8 I2 l7 O通过调用(IndicatorSetInteger)函数和调用变体,设置相应指标属性的值,其中我们指定了属性标识符。 其参数为:
/ D2 V+ b% {4 L7 ?# w2 Iprop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_INTEGER)之一的属性的标识符,我们将指定(INDICATOR_DIGITS)。
' [+ a% o; E% |3 \prop_value: 属性的值,我们将指定(_Digits)。+ |# M4 q9 S( y8 O; ^
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);8 P0 j; O2 l2 H# g
调用变体设置相应字符串类型属性的值,我们还在其中指定属性标识符。 其参数为: D" o' T8 l) i
prop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_STRING)之一的属性标识符,我们将指定 (INDICATOR_SHORTNAME)为指标设置短名称。' }' ~% k0 A3 ?5 {! V! \+ I
prop_value: 属性的值,我们将指定(“Simple Heiken Ashi”)。; M- R( J* s% ~
IndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
1 d, K1 \% v8 q0 C调用(PlotIndexSetDouble)函数设置相应指标的对应双精度类型属性值。 其参数为:, V/ C7 v0 j$ t! x- l+ u4 i
plot_index: 图形绘图的索引,我们将指定 0。+ _* o5 Y5 D. E0 g3 T+ A
prop_id:(ENUM_PLOT_PROPERTY_DOUBLE)值之一,对于无绘图,它是(PLOT_EMPTY_VALUE)。
, Q& B' C: N5 h8 C: d5 Nprop_value: 属性值。, `% p6 V6 O8 W6 k
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
8 \8 [0 B1 X1 ^. n0 ?# c0 t3 k: \然后返回(INIT_SUCCEEDED)作为 OnInit() 函数的一部分,终止它,并返回初始化成功。3 g4 d5 L0 D2 ]5 y4 B0 i/ b& k8 r1 r3 ^
return(INIT_SUCCEEDED);
. B- S4 j$ J1 l4 r在指标中调用的 OnCalculate 函数内部,赫兹量化交易软件
* b# o1 B8 H4 C3 Q( B; b+ \+ Z基于当前时间帧的时间序列进行计算,其处理的价格数据随着计算类型而变化。" m! K& I$ ]/ P& [0 }/ \9 _; y
int OnCalculate(const int rates_total,
& o; M' n$ e1 U+ Iconst int prev_calculated,: b, C M; }5 u D, Q/ z G; K
const datetime &time[],
$ z, r. K/ d8 o) g0 n5 bconst double &open[],& C5 i: d1 W7 E6 Y, G. v; q- Z7 M
const double &high[],
, i: A$ u! H) I/ L. i; u( P& tconst double &low[],
r2 j3 \& F1 ?5 t' Oconst double &close[],
% x& {/ Y7 j0 Q5 w* \; n$ _const long &tick_volume[],
1 ?) l: F- E7 C1 g7 c* \; Y6 W V9 W) D0 T9 Dconst long &volume[],
/ ~7 I) Z$ N9 ] n. ]const int &spread[])
" m5 Y1 A% ?7 |; P创建一个整数型 “start” 变量,我们稍后将为其赋值:
9 H, g" L8 J+ p" V; f$ lint start;
/ W7 c. a/ o* @: B; j8 r" t使用 'if' 语句返回索引值(最低价、最高价、开盘价和收盘价),如果 prev_calculated 等于 0,则 start 值 = 1;或把返回的值(prev_calculated-1)分配给 start:, n3 t* `5 r( d: v- r0 L
if(prev_calculated==0)0 d; f* R' K, \3 \7 @
{
# p- J9 J: Q! s% e8 N, A- xhaLow[0]=low[0];
6 b$ H8 y& Z- J' ?2 _haHigh[0]=high[0];
2 C& o8 D, u. \6 _haOpen[0]=open[0];
* ~0 S0 a* R, ?: w8 |- T& bhaClose[0]=close[0];
$ F( o% B* h2 a, x+ k) H: p) estart=1;0 u5 _& X7 j6 A- {' M
}; F$ h& b# A% W( U0 S0 j* K
else- f& a6 \+ q% R1 v. ^9 ~- t- A
start=prev_calculated-1;; W5 o8 k* q% c- F/ d
在主循环的 “for” 主体里进行计算,“for” 运算符由三个表达式和可执行运算符组成。
& ?! U6 c) K1 y/ O) V, M$ k: N这三个表达式将是:' @7 A+ c8 w( G7 C$ K
i=start: 对应起始位置。
; b( g# v' z8 r9 D* O1 u" Hi<rates_total && !IsStopped(): 完成循环的条件。 IsStopped() 检查指标的强制关闭。- G$ I# @! R( ]$ L% D, Z
i++: 加 1 作为新的 i。
( w0 `+ ^! p: _$ k' Q$ L我们每次在循环过程中需要执行的操作:
- E. o# s& y- j' Y, f# ]; P计算四个双精度变量
4 P! ] K: q6 N2 ]3 C# }haOpenVal: 对应 Heiken Ashi 开盘价。
* h" x2 S* x2 ]haCloseVal: 对应 Heiken Ashi 收盘价。
1 s' P9 d% y \4 V5 M% |haHighVal: 对应 Heiken Ashi 最高价。# L6 `9 ~3 B; F2 O& [: a0 p ]
haLowVal: 对应 Heiken Ashi 最低价。4 Q! j H' p8 _! v, P
在上一步中分配的计算值与以下内容相同
& y: a1 Y1 o; H9 rhaLow=haLowVal
, [! ~' Y- z& N- r7 u% qhaHigh=haHighVal
6 W: C! h* K1 {( h+ rhaOpen=haOpenVal0 G1 C; Q& Y0 P+ _9 |; X4 r: X; q; I
haClose=haCloseVal( I) n+ I5 G, u3 }: T8 g5 _/ I
检查 Heiken Ashi 的开盘价是否低于收盘价,我们需要指标绘制蓝色蜡烛;如若不是,我们需要把它绘制为红色烛条。4 S1 y2 `4 ~0 g% z: c1 ^
for(int i=start; i<rates_total && !IsStopped(); i++)
2 [: N: N5 Y# N{2 P* U- b; ]. G5 X% Q
double haOpenVal =(haOpen[i-1]+haClose[i-1])/2;' B& q8 R+ G$ U8 a% g N
double haCloseVal=(open+high+low+close)/4;$ M# _/ Q# m% `- x4 z( R0 Q
double haHighVal =MathMax(high,MathMax(haOpenVal,haCloseVal));4 x, m: C. ]) Q
double haLowVal =MathMin(low,MathMin(haOpenVal,haCloseVal));
; r) c) F& a( |/ h9 M% Y* mhaLow=haLowVal;
% e, ^$ \: c! p; `6 e: ~haHigh=haHighVal;
$ \* C& T5 q0 w8 whaOpen=haOpenVal;
I q3 z6 u, GhaClose=haCloseVal;
* y3 E4 H2 d+ ^/ [. P' X//--- set candle color( ?9 t. b1 c/ _/ `! |3 ]/ b
if(haOpenVal<haCloseVal)& n7 q1 ?5 x3 `
haColor=0.0;" q8 W1 K+ u, r% Z
else/ y9 {# E# v; Y+ e/ ^! h& m, c
haColor=1.0;6 v, S' H: H: U/ _7 u' o
}! U# D8 n3 r8 _7 k) N& u t3 ?
终止函数,并返回(rates_total)作为下一次调用的 prev_calculated。
; L+ K0 w+ M( W, ~ m8 d7 c" qreturn(rates_total);
: E( p* n5 h" P; ^, x然后我们编译代码,并确保没有错误。 以下是一个模块中的完整代码:
4 q3 y) C; y/ c//+------------------------------------------------------------------+/ ^( g* R b; `! B/ i# b. ?% z
//| simpleHeikenAshi.mq5 |
$ H) T6 u3 h) ~0 t( b//| Copyright 2023, MetaQuotes Ltd. |
; z$ ?5 t' j* z# Z4 `/ S" w l& N//| https://www.mql5.com |
, L& {. |# v0 m9 l6 F//+------------------------------------------------------------------+3 F I. ]8 w! v+ F' B+ e
#property copyright "Copyright 2023, MetaQuotes Ltd."2 x0 L+ J, y$ f) ~7 W. r N! y
#property link "https://www.mql5.com"
8 s2 v9 E! N8 @* Y" s#property version "1.00"+ q2 b* v6 ~- o( K; T- m
#property indicator_separate_window
* ^6 w8 u# ~0 S( h ], `' K#property indicator_buffers 59 H B1 c0 ^1 A" T( U" w- ?3 v
#property indicator_plots 1! Z/ U4 h8 \, o1 {
#property indicator_type1 DRAW_COLOR_CANDLES" e8 q1 P5 Y9 W+ T
#property indicator_color1 clrBlue, clrRed
0 _0 B/ @' U& w, O4 y#property indicator_width1 2
I" J" u n/ H$ F& X: l! M) v#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"- L' K2 L, Q, s# r- Q
double haOpen[];2 Y7 @2 _6 Z) e, V
double haHigh[];9 ?/ i& d/ b7 o
double haLow[];
) a) [% D0 J; f v8 z. L( ddouble haClose[];
. p/ R( [1 g; \" U" Ndouble haColor[];
+ e; q* a6 c3 Y: q% _" Iint OnInit()" f3 K) I, X& K% j
{
6 e# y1 T8 Q9 v5 U* ?0 rSetIndexBuffer(0,haOpen,INDICATOR_DATA); q( M& c2 I9 L! y5 A" L7 p
SetIndexBuffer(1,haHigh,INDICATOR_DATA);5 U8 l& k- a+ X" \; j3 j$ {, v
SetIndexBuffer(2,haLow,INDICATOR_DATA);
7 [, r4 v" ]* u& W( f8 M- r, m( KSetIndexBuffer(3,haClose,INDICATOR_DATA);
5 a2 F! }9 ~) kSetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);6 T& K8 H$ U# d- ]
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);! u. a3 g( ^8 n, N" K% Z
IndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");. V. Y6 _& b: p+ f
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);1 I- O" m, c8 C, [
return(INIT_SUCCEEDED);
9 D: n; D! H4 T% |}
m! e$ z! B; L3 x E, D8 {3 oint OnCalculate(const int rates_total,5 [" ]4 q# T9 e* A
const int prev_calculated,
1 ^; k8 l& ?" ^' v, s2 O" Yconst datetime &time[],
6 l) D8 n. z. r) w1 aconst double &open[],
" N; F" k$ r& g% u _const double &high[],) t* W; b: O& M" p
const double &low[],
h9 ^# j- `2 B2 [7 P% ?) Wconst double &close[],' K4 v% l! d3 X( B
const long &tick_volume[],' x3 T" X1 `; {# I9 H( \& p J% {3 y
const long &volume[], |