概论
在以前的 文章 里曾描述过利用图形界面创建通用振荡器。在本文中, 赫兹量化创建了一个非常有趣、便利和有用的指标, 可以大大简化并加快图表分析。除振荡器外, 还有其它类型的技术分析指标, 也与振荡器一样有趣。它们包括趋势、波动率和交易量指标, 以及可划分为不同类别的其它指标。在本文中, 我们将研究通用通道指标的创建。
我以前发表的有关通用振荡器的文章相当复杂, 它们是针对有经验的程序员而非初学者。由于本文的主题偏向创建通用振荡器, 所以我们在此不会赘述一般问题。赫兹量化将在通用振荡器的基础上创建一个通用通道。因此, 即便程序员新人也可以通过修改所提供的素材来创建其通用指标, 而无需分析利用图形界面创建通用指标的所有细微之处。
尽管通用指标相似, 但仍存在重大的根本差异。所有通道指标显示为三条线, 包括中心, 顶部和底部线。中心线的绘图原理与移动平均线相似, 而移动均线指标主要用于绘制通道。顶部线和底部线的位置距中心线距离相等。距离的确定可以按照点为单位, 作为价格百分比 (包络指标), 使用标准偏差值 (布林带) 或 ATR 值 (Keltner 通道)。因此, 通道指标将使用两个独立的模块构建:
-
中心线计算模块
-
通道宽度定义模块 (或绘制通道边界)
有各种不同类型的通道, 如 Donchian 通道 (价格通道)。通常它以创建边界线 (价格范围) 开始, 之后计算中心线的数值 (在范围中间)。然而, 此通道也可以通过上述方案来构建: 即首先我们绘制一条中心线, 这条中心线被定义为价格区间的中间价, 然后我们在等于价格范围一半的距离上绘制通道边界。当然, 这比通常通道需要更多的计算。然而, 由于本文的主要目的是创建一个通用指标, 赫兹量化可以允许一些例外, 而此方法将增加中心线与边界的可能组合的数量。例如, 赫兹量化将能够创建一个在价格通道中带有中心线的指标, 边界位于标准偏差的距离, 如布林带等。
中心线的类型
用于中心线的各种移动平均线。赫兹量化来定义它们的类型和参数的数量。所有将在指标中使用的中心线变体如表 1 所示。
表 1. 中心线的类型
标准 函数 | 名称 | 参数 |
---|---|---|
iAMA | 自适应移动均线 | 1. int ama_period — AMA 周期 2. int fast_ma_period — 快速移动均线周期 3. int slow_ma_period — 慢速移动均线周期 4. int ama_shift — 指标的水平偏移 5. ENUM_APPLIED_PRICE applied_price — 处理所用价格类型 |
iDEMA | 双重指数移动均线 | 1. int ma_period — 均化周期 2. int ma_shift — 指标的水平偏移 3. ENUM_APPLIED_PRICE applied_price — 价格类型 |
iFrAMA | 分形自适应移动均线 | 1. int ma_period — 均化周期 2. int ma_shift — 指标的水平偏移 3. ENUM_APPLIED_PRICE applied_price — 价格类型 |
iMA | 移动均线 | 1. int ma_period — 均化周期 2. int ma_shift — 指标的水平偏移 3. ENUM_MA_METHOD ma_method — 平滑类型 4. ENUM_APPLIED_PRICE applied_price — 价格类型 |
iTEMA | 三重指数移动均线 | 1. int ma_period — 均化周期 2. int ma_shift — 指标的水平偏移 3. ENUM_APPLIED_PRICE applied_price — 价格类型 |
iVIDyA | 变可指数动态均线 | 1. int cmo_period — Chande 动量周期 2. int ema_period — 平滑因子周期 3. int ma_shift — 指标的水平偏移 4. ENUM_APPLIED_PRICE applied_price — 价格类型 |
- | 价格通道的中心线 | 1. int period |
基于表 1 "参数" 列的分析, 赫兹量化得到所需的最小参数集合 (表 2)。
表 2. 用于计算通道中心线的通用参数集合
类型 | 名称 |
---|---|
int | period1 |
int | period2 |
int | period3 |
int | shift |
ENUM_MA_METHOD | ma_method |
ENUM_APPLIED_PRICE | price |
所有将在指标属性窗口中显示的中心线参数均带 "c_" 前缀。
边界类型
现在我们来确定通道边界的计算方法 (表 3)。
表 3. 通道宽度计算变体
标准 函数 | 名称 | 参数 |
---|---|---|
iATR | 平均真实范围 | 1. int ma_period — 均化周期 |
iStdDev | 标准偏差 | 1. int ma_period — 均化周期 2. int ma_shift — 指标的水平偏移 3. ENUM_MA_METHOD — 平滑类型 4. ENUM_APPLIED_PRICE applied_price — 价格类型 |
- | 按照点数 | int width — 宽度的点数 |
- | 百分比 (在包络线中) | double width — 宽度作为价格百分比 |
- | 就像在价格通道中 | double width — 相对于价格通道实际宽度的缩放比率 |
根据表 3 的 "参数" 列, 我们得到一组所需的参数 (表 4)。
表 4. 用于计算通道宽度的通用参数集合
类型 | 名称 |
---|---|
int | period |
int | shift |
ENUM_MA_METHOD | ma_method |
ENUM_APPLIED_PRICE | price |
double | width |
赫兹量化需要 int 变量用于计算点数, 但它未包括在表 4 中, 因为我们可以使用一个 double 变量代之。这会减少属性窗口中的变量总数。
所有将在指标属性窗口中显示的边界计算参数均带 "w_" 前缀。
中心线类
中心线基类的基本原理和方法类似于 带有图形界面的通用振荡器 中描述的 CUniOsc 类。因此我们将使用这个类, 并稍微修改它。
在 MQL5/Include 文件夹中, 赫兹量化创建 UniChannel 文件夹, 复制 CUniOsc.mqh 文件 (从 Include/UniOsc) 至此, 并将其重命名为 CUniChannel.mqh。在文件中保留基类 (COscUni), 子类 Calculate1 (其全名为 COscUni_Calculate1) 及其子类 COscUni_ATR。其它的类应予以删除。
赫兹量化将类重新命名: 将 "COscUni" 片段替换为 "CChannelUni"。为了更换, 使用编辑器功能 (主菜单 — 编辑 — 查找和替换 — 替换)。但不要使用 "全部替换" 按钮。逐一替换实例, 以确保所有替换都正确。
中心线类总是绘制一条实线, 因此我们不需要许多基类方法。删除不必要的方法后, 将会保留以下基类:
class CChannelUniWidth{ protected: int m_handle; // 指标句柄 string m_name; // 指标名称 string m_label1; // 缓存区 1 的名称 string m_help; // 指标参数帮助 double m_width; // 通道宽度 public: // 构造器 void CChannelUniWidth(){ m_handle=INVALID_HANDLE; } // 析构器 void ~CChannelUniWidth(){ if(m_handle!=INVALID_HANDLE){ IndicatorRelease(m_handle); } } // 从指标的 OnCalculate() 函数里调用的主要方法 virtual int Calculate( const int rates_total, const int prev_calculated, double & bufferCentral[], double & bufferUpper[], double & bufferLower[], ){ return(rates_total); } // 得到加载的指标句柄 int Handle(){ return(m_handle); } // 句柄检查方法, 允许检查指标是否已成功加载 bool CheckHandle(){ return(m_handle!=INVALID_HANDLE); } // 得到指标名称 string Name(){ return(m_name); } // 得到缓存区标签文本 string Label1(){ return(m_label1); } // 得到参数提示 string Help(){ return(m_help); } };
与第二个缓冲区相关的一切都可以从 Calculate 类中删除。之后只剩下一个 Calculate 方法:
class CChannelUni_Calculate1:public CChannelUni{ public: // 主要方法是从指标的 OnCalculate() 函数调用 // 前两个参数类似于 // 指标 OnCalculate() 函数的前两个参数 // 第三个参数是指标中心线的缓存区 int Calculate( const int rates_total, const int prev_calculated, double & buffer0[] ){ // 确定复制元素的数量 int cnt; if(prev_calculated==0){ cnt=rates_total; } else{ cnt=rates_total-prev_calculated+1; } // 复制数据到指标缓存区 if(CopyBuffer(m_handle,0,0,cnt,buffer0)<=0){ return(0); } return(rates_total); } };
我们来利用 iMA 指标编写子类。来自文件的 CChannelUni_ATR 类应重命名为 CChannelUni_MA。我们替换调用指标, 并删除我们不需要的部分。作为结果, 赫兹量化得到如下类:
class CChannelUni_MA:public CChannelUni_Calculate1{ public: // 构造器 // 前两个参数对于所有子类均相同 // 之后是加载指标的参数 void CChannelUni_MA( bool use_default, bool keep_previous, int & ma_period, int & ma_shift, long & ma_method, long & ma_price){ if(use_default){ // 使用选择的省缺值 if(keep_previous){ // 之前使用的参数没有修改 if(ma_period==-1)ma_period=14; if(ma_shift==-1)ma_shift=0; if(ma_method==-1)ma_method=MODE_SMA; if(ma_price==-1)ma_price=PRICE_CLOSE; } else{ ma_period=14; ma_shift=0; ma_method=MODE_SMA; ma_price=PRICE_CLOSE; } } // 加载指标 m_handle=iMA(Symbol(),Period(),ma_period,ma_shift,(ENUM_MA_METHOD)ma_method,(ENUM_APPLIED_PRICE)ma_price); // 指标名称形成一条线 m_name=StringFormat( "iMA(%i,%i,%s,%s)", ma_period, ma_shift, EnumToString((ENUM_MA_METHOD)ma_method), EnumToString((ENUM_APPLIED_PRICE)ma_price) ); // 缓冲区名称的字符串 m_label1=m_name; // 参数提示 m_help=StringFormat( "ma_period - c_Period1(%i), "+ "ma_shift - c_Shift(%i), "+ "ma_method - c_Method(%s)"+ "ma_price - c_Price(%s)", ma_period, ma_shift, EnumToString((ENUM_MA_METHOD)ma_method), EnumToString((ENUM_APPLIED_PRICE)ma_price) ); } };
赫兹量化来看一下如何在变量 m_name 和 mlabel1 中形成字符串。在子窗口中绘制的指标的名称(m_name 变量) 显示在子窗口的左上角。通道将显示在价格图表上, 其名称不可见, 因此我们将为 m_label 变量分配与 m_name 变量相同的详细名称, 以便当我们将鼠标悬浮在中央通道上时显示在弹出框里。