概述
$ O; N3 P+ V% I0 s/ ]5 e3 E/ l8 t# E我们都需要读取图表,任何可以帮助完成此任务的工具都会受到欢迎。 在有助于读取图表的工具之中,指标计算可基于价格、交易量、其它技术指标、或它们的组合,而在交易世界中存在众多新奇的思路。 我们在交易终端中内置了很多现成的指标,如果我们需要添加一些功能以便能适应我们的交易风格,我们会发现这有一些挑战,因为也许无法更改它,除此之外,我们也许无法在交易终端中的内置指标里找到期望的指标。
9 W6 Y6 c( A& N3 P赫兹量化交易软件
1 |0 I' ?& N9 c' L2 `& V3 ^自定义指标和 Heiken Ashi 定义
7 z/ f4 @& h' q0 P在这一部分中,我们将更详细地学习自定义指标和 Heiken Ashi 指标。 正如我在上一章节的概述中提到的,自定义指标是用户利用 MQL5 编程语言创建的技术分析工具。 它可以在 MetaTrader 5 中用来分析和明晰市场走势,并有助于做出明智的投资决策。 有许多实用的内置技术指标,但有时我们需要根据一些额外和特定的数学、统计或技术概念来分析和理解市场的行为,而这些概念在内置指标中不存在,或者没有指标可以完成任务。 那么,在这种情况下,我们必须自己创建指标 — 这是 MetaTrader 5 平台的功能之一,因为它可以帮助我们创建自己的分析或交易工具,从而满足我们的特定偏好和目标。赫兹量化交易软件
& ^: ~5 \% c0 [1 A简单的 Heiken Ashi 指标8 d. e$ c! w1 @, A: b! j& e
在这一部分中,我们将创建一个用在 MetaTrader 5 上的简单 Heiken Ashi 指标。 该指标应持续检查价格(开盘价、最高价、最低价和收盘价),并执行数学计算,以从而成 haOpen、haHigh、haLow 和 haClose 等数值。 根据计算结果,指标应依据数值在图表上绘制不同颜色的烛条:如果烛条方向看涨,则为蓝色,如果为看跌,则为红色。 烛条应作为子窗口显示在传统图表下方的单独窗口之中。赫兹量化交易软件
* r" i5 x4 ]; C, u+ U我们来查看创建此自定义指标需要完成的所有步骤。0 q# s/ a9 @- r7 J+ p
通过 #property 和标识符值指定附加参数来确定指标设置,如下所示:3 K* @, ~ J- E: v j# i
(indicator_separate_window) 在单独的窗口中显示指标。; y* q# h1 m& e: G
(indicator_buffers) 确定指标计算的缓冲区数量。5 t4 B- G* p+ r) f
(indicator_plots) 确定指标中图形序列的数量。 图形系列是在创建自定义指标时可用的绘图样式。' i2 R* Q$ }; B: z0 \7 _9 U
(indicator_typeN) 要根据 (ENUM_DRAW_TYPE) 的值确定图形绘图的类型,N 是我们在最后一个参数中确定的图形序列的数量,它从 1 开始。
& \& t3 l6 k% {) _(indicator_colorN) 确定 N 的颜色,N 也是我们之前确定的图形序列的数量,它从 1 开始。
) I* X( w" I; [(indicator_widthN) 还要确定 N 或图形序列的宽度。
* o/ H' v" ?$ ~7 {$ Q(indicator_labelN) 确定图形序列 N 的标签设置。1 w: Y% M+ S* E `# G' t% b( n* w& w
#property indicator_separate_window- u1 u! g4 R5 T
#property indicator_buffers 5
! k: c2 {, ~/ Y$ B, E#property indicator_plots 1
2 M. q$ `+ b2 m' f' H5 E4 t9 e$ K#property indicator_type1 DRAW_COLOR_CANDLES
w% E& Y2 f5 q, [8 U* f#property indicator_color1 clrBlue, clrRed1 D$ C; b- l9 y
#property indicator_width1 2( A9 C* ]: b' j
#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close"( S- D" w) c2 V9 d7 n
为指标的五个缓冲区(haOpen、haHigh、haLow、haClose、haColor)创建五个数组,均为双精度类型。1 I' x2 @. Q- y7 |
double haOpen[];/ K8 G7 N8 q; A5 q$ @! H+ O6 r
double haHigh[];
5 t% n0 k( b3 k% Hdouble haLow[];2 w9 Y/ @ E- G E
double haClose[];
0 I& `4 e0 d) Vdouble haColor[];# a# w, }% B$ p, E: n
在 OnInit() 中,此函数初始化正在运行的指标。5 y* y0 J9 ^! M {6 f7 u( k& @. m
int OnInit()( O+ s/ P# Q( e3 [
调用(SetIndexBuffer)函数为双精度类型的一维动态数组的指标缓冲区进行排序。赫兹量化交易软件
% H* V' L- E- _% y3 c+ Z其参数为:6 A* o& ]. q; Q( y1 ` }
index: 指标缓冲区从 0 开始的编号,此数字必须小于在参数 (indicator_buffers) 中确定的声明值。
7 C( Y* W6 H2 u' V% i, B# P8 J! |buffer[]: 在我们的自定义指标中声明的数组。% V" M+ |" t$ E
data_type: 我们需要在指标数组中存储的数据类型。% Z7 k7 b0 M9 I0 K
SetIndexBuffer(0,haOpen,INDICATOR_DATA);
/ m* Q D- D( s- D! T, JSetIndexBuffer(1,haHigh,INDICATOR_DATA);
- `4 V0 W8 f3 Q) W$ {. WSetIndexBuffer(2,haLow,INDICATOR_DATA);* H6 O4 l7 E9 L* C9 v% S
SetIndexBuffer(3,haClose,INDICATOR_DATA);
, Z. i4 x9 H/ x; ^+ j; }6 C) |9 C% z) MSetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);4 F A3 {' Z3 J& z) Q
通过调用(IndicatorSetInteger)函数和调用变体,设置相应指标属性的值,其中我们指定了属性标识符。 其参数为:
% \" L8 P3 Z$ w4 n5 L6 A, vprop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_INTEGER)之一的属性的标识符,我们将指定(INDICATOR_DIGITS)。
! Q! E$ V7 \# p' c* L5 k/ K sprop_value: 属性的值,我们将指定(_Digits)。- t+ ~9 t5 c% Q- b
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);* V+ C" \# h- F9 D y
调用变体设置相应字符串类型属性的值,我们还在其中指定属性标识符。 其参数为:
$ c9 p, P7 Y* Uprop_id: 可以是(ENUM_CUSTOMIND_PROPERTY_STRING)之一的属性标识符,我们将指定 (INDICATOR_SHORTNAME)为指标设置短名称。
- O8 x5 p' V, L" {/ ^! n3 Bprop_value: 属性的值,我们将指定(“Simple Heiken Ashi”)。
2 W$ g" Y( ^) E$ k5 p$ I7 v* y( BIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
) n' i& m4 f6 ? r4 u调用(PlotIndexSetDouble)函数设置相应指标的对应双精度类型属性值。 其参数为:
0 w$ R. ^% o% j q# y. k0 ]plot_index: 图形绘图的索引,我们将指定 0。" H/ X* j( @$ u( M2 ]& X+ z0 |
prop_id:(ENUM_PLOT_PROPERTY_DOUBLE)值之一,对于无绘图,它是(PLOT_EMPTY_VALUE)。7 |" H+ L: K7 f* H+ D! D" j
prop_value: 属性值。1 c# x! p8 C/ t
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
( s0 n- I1 T0 f' I8 b/ J0 G" k然后返回(INIT_SUCCEEDED)作为 OnInit() 函数的一部分,终止它,并返回初始化成功。
) ^3 d0 _- X0 S; E* j: W1 Mreturn(INIT_SUCCEEDED);0 ^! w. {& j; X- |& I/ ~
在指标中调用的 OnCalculate 函数内部,赫兹量化交易软件
; T- e# K _: G) i& l基于当前时间帧的时间序列进行计算,其处理的价格数据随着计算类型而变化。7 x l" x' s0 w) c, `2 z0 F* j/ F+ j
int OnCalculate(const int rates_total,
5 v: m# F, E6 I' b6 y/ ?7 R3 iconst int prev_calculated," |/ M8 W3 ~+ t# i; R' y
const datetime &time[],9 o/ l7 V# [3 v, y! m
const double &open[],
6 z8 A. o; @9 s% Mconst double &high[],
8 D) F& b( \: }* h/ R6 b7 zconst double &low[],, s, }2 g2 x/ A; v! W
const double &close[],0 O$ m9 ?( S7 w- }) j3 e7 T( j' ^
const long &tick_volume[],
% S, e8 t! C/ m5 B$ P% xconst long &volume[],
- q* f; ?: j# Y8 I9 d4 Lconst int &spread[]), i: Q/ F! U( d
创建一个整数型 “start” 变量,我们稍后将为其赋值:) e) J* }3 n5 N* D: d9 V
int start;
8 M; K+ R- S7 H/ C! k. K使用 'if' 语句返回索引值(最低价、最高价、开盘价和收盘价),如果 prev_calculated 等于 0,则 start 值 = 1;或把返回的值(prev_calculated-1)分配给 start:
9 e' r; q7 i7 Rif(prev_calculated==0)9 h N1 f, N, u4 i- V0 I
{
) w$ H" b9 d# vhaLow[0]=low[0];
$ E& a2 W; L1 ?7 i- L2 H/ h! lhaHigh[0]=high[0];+ F1 @2 J9 Q3 _& T s. d
haOpen[0]=open[0];9 G& x9 T* A) Q8 n3 X
haClose[0]=close[0];
g" v- n8 S! a) cstart=1;
9 V$ U+ u. z0 K E}: Z' d" C# `5 i4 t. T
else+ ], h, U3 l4 K4 T8 `
start=prev_calculated-1;9 z# z8 S3 W! d" x! N [
在主循环的 “for” 主体里进行计算,“for” 运算符由三个表达式和可执行运算符组成。
" ?( R& K+ G6 j w这三个表达式将是:+ J; } D; g& Q& X. d9 m
i=start: 对应起始位置。
7 l+ ?% H7 d: i0 t+ F& U( Di<rates_total && !IsStopped(): 完成循环的条件。 IsStopped() 检查指标的强制关闭。; c+ q: w2 a6 }6 o8 V3 K2 l, p
i++: 加 1 作为新的 i。
0 h$ E3 E, G/ M, F' s: k我们每次在循环过程中需要执行的操作:& r$ p" H# k+ H& l. b: S
计算四个双精度变量+ `2 T1 \- h" K' s
haOpenVal: 对应 Heiken Ashi 开盘价。+ v2 Y6 V$ M6 X5 v
haCloseVal: 对应 Heiken Ashi 收盘价。4 U9 |/ r" Z, n7 h( @7 F
haHighVal: 对应 Heiken Ashi 最高价。+ E# ^ [+ }( O, b, K4 l
haLowVal: 对应 Heiken Ashi 最低价。: N [- t8 v) W0 K9 Z, Z! n
在上一步中分配的计算值与以下内容相同
+ D3 [" J* y2 @: \haLow=haLowVal
& u6 P* H4 v( A3 KhaHigh=haHighVal
) ?( G. D K: V5 S$ yhaOpen=haOpenVal
. L3 B+ A) @3 r6 z6 g9 C. ChaClose=haCloseVal
- [3 ]9 ?3 t7 [3 j' _检查 Heiken Ashi 的开盘价是否低于收盘价,我们需要指标绘制蓝色蜡烛;如若不是,我们需要把它绘制为红色烛条。" u [" |; Q5 Z6 z
for(int i=start; i<rates_total && !IsStopped(); i++)
; Z! d, C9 s! D1 t( m; I{
% a8 b8 V9 T! V# R: Y* b# f# udouble haOpenVal =(haOpen[i-1]+haClose[i-1])/2;! T% n" x) z. x/ a- O
double haCloseVal=(open+high+low+close)/4;# ]/ ~5 v/ y! _5 W6 [4 F3 v1 A: t2 h
double haHighVal =MathMax(high,MathMax(haOpenVal,haCloseVal));
6 I0 N3 y# S+ q5 _ y; e0 B* M \double haLowVal =MathMin(low,MathMin(haOpenVal,haCloseVal));# e& l: s3 W) m# ]' c- I% j+ \* u
haLow=haLowVal;7 ^1 _' ]* }, F0 E
haHigh=haHighVal;
& i2 U: o" n7 A% R4 l8 h6 D0 JhaOpen=haOpenVal;
$ j6 m8 S; c1 O2 W3 zhaClose=haCloseVal;
, ~1 z, c( [2 I) [" \' X//--- set candle color* d4 t# M/ Q; p: i3 E% u
if(haOpenVal<haCloseVal)& @/ B5 M7 w/ K7 W% n( ?5 [
haColor=0.0;7 H6 n ~3 _. f. Z
else# b; c# X& O; {7 t8 T# k
haColor=1.0;
3 z7 g' K) ?; ]. p6 |8 i: ^$ r}- b2 ?6 n" V! {3 n: q0 m
终止函数,并返回(rates_total)作为下一次调用的 prev_calculated。
- c. p$ \1 r6 S; X' Y0 Ereturn(rates_total);' P1 p) r! B5 O9 j
然后我们编译代码,并确保没有错误。 以下是一个模块中的完整代码:' V9 s, k- I; ~5 @& d8 r: O
//+------------------------------------------------------------------+
# s1 h9 F+ s$ q# b" ^4 q& I' J//| simpleHeikenAshi.mq5 |
6 O. z" |6 \0 y; A! [ J( p2 m' `//| Copyright 2023, MetaQuotes Ltd. |
# @: ?& W- E! {//| https://www.mql5.com |
! v, [# p( p+ i. n1 u1 N9 h//+------------------------------------------------------------------+ D) n0 B4 a3 M6 G
#property copyright "Copyright 2023, MetaQuotes Ltd."; R0 i" Z' D: n6 m0 T0 ?. w
#property link "https://www.mql5.com"
" o. d8 H1 ~1 @5 ~0 w2 N7 I#property version "1.00"; u8 | ? w- D8 p. a: T4 f0 t' I: N
#property indicator_separate_window
3 j1 s g) e- H/ f8 p% _& }" y#property indicator_buffers 5, Y& x' O( `5 L* j. w# A- ~$ p
#property indicator_plots 1- h0 g6 }. `# ~
#property indicator_type1 DRAW_COLOR_CANDLES
5 A0 L- W# I- k! w/ W! X' F1 o3 t7 V#property indicator_color1 clrBlue, clrRed
. s- g3 O: Z) w#property indicator_width1 2
. w1 l% A6 y% U# Y/ P( Q! X3 y#property indicator_label1 "Heiken Ashi Open;Heiken Ashi High;Heiken Ashi Low;Heiken Ashi Close" O- F c$ N" Q9 G) X
double haOpen[];% t% M7 | T3 w+ {7 N/ e
double haHigh[];
u2 Q5 I7 R# A0 B2 W$ G# }# z5 S( Ddouble haLow[];
$ O0 T5 [6 q. V% p5 [double haClose[];
p# Q9 u* t0 Kdouble haColor[];
4 K( R5 G4 P2 e1 P7 wint OnInit(). D/ M( O: |5 C% M( }% u
{
, Y% ?3 `2 a) ?; }1 o! b6 |' @2 FSetIndexBuffer(0,haOpen,INDICATOR_DATA);& ~) ^; y. B2 i; F2 Y
SetIndexBuffer(1,haHigh,INDICATOR_DATA);
# u2 r( C. o" HSetIndexBuffer(2,haLow,INDICATOR_DATA);; w- L; ~8 h, X$ K& O, `
SetIndexBuffer(3,haClose,INDICATOR_DATA);$ d7 U9 e. H- C+ b, v
SetIndexBuffer(4,haColor,INDICATOR_COLOR_INDEX);5 A( H+ S d3 \$ n$ ~5 C
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
+ _( n! ? L- E; ~) D$ L# VIndicatorSetString(INDICATOR_SHORTNAME,"Simple Heiken Ashi");
% g9 x+ r# W- x# b: `: K8 h; JPlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);! P* Z: R) B) X) {! s6 a( S
return(INIT_SUCCEEDED);
. o2 _7 p' e, T. e% O}* p* I& g5 X: ] ?2 P
int OnCalculate(const int rates_total,8 d6 h3 i% V; o% ^4 p& U3 @& e
const int prev_calculated,
" J9 C5 `/ `" W$ ?0 F% K$ W0 uconst datetime &time[],! E0 h4 W9 [, _1 C" t
const double &open[],8 B% n7 ` y0 u+ H# [
const double &high[],
1 w# |; u$ }3 L5 w. t1 L: t0 a0 {const double &low[],8 E& j/ C$ Q/ M# S0 z# |# K: e
const double &close[],
8 i; ~) m2 L- C) Vconst long &tick_volume[],
8 L) T- j1 K8 s( }+ @const long &volume[], |