带有图形界面的通用趋势

Dmitry Fedoseev | 2 五月, 2017


内容

概论

之前的两篇文章包含了如何创建一款 带有图形界面的通用振荡器 的说明, 以及如何在其基础上开发一款 通用通道。  

也可以在通用振荡器的基础上创建通用趋势指标。为了避免重复, 我们将采用稍微不同的方法。有些人可能会喜欢新的途径。

在通用振荡器和通用通道中, 在表单类中创建的控件的所有对象都使用自动 指针, 其可见性和位置均使用附加类进行管理。现在我们将使用 "定制图形控件" 系列 (文章 1, 文章 2, 文章 3)。不过, 仅创建此特定文章所需要的方法。 

指标类型

我们不会局限于传统上认定的趋势指标。任何依赖价格方向 (移动平均线, RSI 指标及其它) 的指标都可用来判断入场和确定趋势。例如, 如果价格高于移动平均线, 我们可推断此为上升趋势, 反之亦然 (图例. 1)。


图例. 1. 使用移动平均线和价格来判断趋势。蓝色向上箭头标示的间隔
我们可以买入 (价格高于均线), 红色向下箭头示意的间隔, 可卖出 (价格低于均线)。

均线坡度可类似运用: 坡度向上 — 上行趋势, 向下坡度 — 下行趋势 (图例. 2)。

 
图例. 2. 使用移动均线坡度来判断趋势。蓝色向上箭头 标示的间隔 
我们可以买入 (均线指向向上), 红色向下箭头
示意的间隔, 可卖出 (均线指向向下)

如果确定一根柱线, 价格穿越均线 (一根柱线的收盘价低于均线, 下一根则高于均线), 在这种情况下, 移动均线可用来判断入场点。在此情况下, 一根柱线中存在交易信号 (图例. 3)。 

 
图例. 3. 使用移动平均线和价格来判断入场点。蓝色向上箭头 示意此柱线 
买入 (一根柱线的收盘价低于均线, 下一根则高于均线), 红色向下箭头 
示意
柱线卖出 
(一根柱线的收盘价高于均线, 下一根则低于均线)

均线坡度可类似运用: 均线方向改变的柱线为入场点 (图例. 4)。 

 
图例. 4. 使用移动平均坡度来判断入场点。蓝色向上箭头 示意此柱线 
买入 (均线的下端极值), 红色向下箭头 示意 柱线卖出 (上端极值)

在本文中, 我们感兴趣的是趋势判断选项 (如同图例. 1 和图例 2)。在此给出判断入场柱线的选项, 以便进行比较和完整的理解。   

振荡器正常情况下用于判断入场点, 通常是级别穿越, 例如 RSI (图例. 5)。至少有两种方式可使用 随机振荡器 来判断入场点: 级别穿越, 以及主线与信号线交汇。  

 
图例. 5. 根据 RSI 指标判断入场。蓝色箭头表示柱线买入 (级别穿越
70 向上), 红色箭头表示柱线卖出 (如果穿越级别 30 向下)

我们来研究如何使用振荡器识别趋势。对于随机振荡器答案是显而易见的: 基于主线和信号线的位置, 当主线高于信号线时, 我们可以买入; 当主线低于信号线时 — 卖出 (图例. 6)。


图例. 6. 随机振荡器主信号线和信号线的位置用于确定趋势。蓝色箭头示意
上行趋势 (主线高于信号线), 红色箭头意味着下行趋势 (主线低于信号线)

类似地, 我们可以使用级别: 如果振荡器高于某个级别, 我们可以买入, 如果低于 — 我们卖出 (图例. 7)。

 
图例. 7. 使用 RSI 振荡器和级别来判断趋势。蓝色箭头示意 上行趋势 
(RSI 高于 60), 红色箭头示意下行趋势 (RSI 低于 40)
 

趋势显示

我们来寻找最便利的方式来显示趋势。最初的想法是绘制一条线, 其颜色依据趋势而变化。我们来研究使用 RSI 的 60 和 40 级别检验趋势。如果 RSI 线高于 60, 我们可以买入, 如果低于 40, 我们可以卖出 (图例. 7)。在此情况下, 当 RSI 线在 40 到 60 之间时, 我们既不买也不卖。现在让我们交换级别: 买入级别等于 40 (如果数值大于40, 我们可以买入), 卖出级别为 60 (卖出时可能低于 60)。在此情况下, 卖出和买入都可能在 40 到 60 之间 (图例. 8)。

 
图例. 8. 使用 RSI 振荡器来判断趋势。当买入级别设置为 40, 以及卖出级别设为 60 时
有些区域, 我们即可以买入亦可以卖出 (蓝色和红色箭头共存)。

因此, 趋势应显示为两个独立的数据集。我们将使用两个指标缓冲区: 一个缓冲区稍微偏近顶部, 另一个缓冲区偏近底部 (在此情况下, 两个缓冲区可以并行显示)。由于缓冲区不会连续显示, 最好使用直方图或图标。我们选择用图标。在此情况下, 除了不同的缓冲区颜色有区别, , 我们可以使用不同的图标。图例. 9 显示指标的外观。

 
图例. 9. 一种显示趋势的便利方式

这种趋势显示方法允许扩充可用指标的数量。例如, 您可以添加不依赖于方向的指标, 但仅允许/禁止在不同的间段进行交易。例如, 我们可用 ATRSTD。如果其数值超过某个阈值, 则将显示两个箭头 (上与下)。因此, 一款通用指标变得更加万能。   

指标运用

在我们开始创建通用趋势指标之前, 定义一套完整的用来判断趋势的指标清单。这也会让我们找到必要的外部参数集 (变量的数量和类型)。当然, 所有可能的选项都不可能轻松地塞进一个指标 — 它们太多了。即使移动平均线也可以按照两种不同的方式使用。此外, 可以使用两条移动平均线。因此, 有必要首先提供简单的可能性来修改指标。

一些指标使用的选项可以是相同的, 虽然看起来不是很明显。例如, 使用两条均线 (快速和慢速) 来判断趋势, 使得基于均线和价格的趋势检测选项没有必要。如果我们将快速均线的周期设置为 1, 则该均线将与该价格相对应。因此, 每个指标应分开考虑。此外, 有必要考虑运用每个指标的每个变体。

iAC (加速器振荡器)

指标没用参数, 它与附带某些参数的 OsMA 指标相同, 因此不会用到它。  

iAD (累积/分布)

一款非常特别的指标。指标值的范围没有定义, 它断线严重, 在没有适当处理 (例如平滑) 的情况下不适合用来判断趋势。将不会用到它。 

iADX (平均方向指数)

变体 1. PDI 和 MDI 线的位置。PDI 高于 MDI — 上行趋势, PDI 低于 MDI — 下行趋势。

参数

  1. int adx_period. 

变体 2. 不依赖方向。ADX 线相对于某些级别的位置。ADX 高于级别 — 允许买入和卖出。除了指标参数外, 我们还需要一个额外的级别参数。 

 参数:

  1. int adx_period — 周期;
  2. double level — 级别。 

iADXWilder (由 Welles Wilder 的平均方向指数)

类似于 ADX。

iAlligator (鳄鱼)

指标由三个具有不同周期和不同位移值的移动平均线组成。这个指标有很多使用方式。我们只会使用它们之一。

变体 1. 快速线 (唇) 高于中间线 (齿), 中间线高于慢速线 (颚) — 上行趋势, 如果线性排列顺序相反 — 下行趋势。   

参数

    1. int jaw_period — 颚线周期; 
    2. int jaw_shift — 颚线位移; 
    3. int teeth_period — 齿线周期;
    4. int teeth_shift — 齿线位移; 
    5. int lips_period — 唇线周期; 
    6. int lips_shift — 唇线位移;
    7. ENUM_MA_METHOD ma_method — 平滑类型; 
    8. ENUM_APPLIED_PRICE applied_price — 价格类型。

iAMA (自适应移动均线)

指标是一款移动平均线。我们将使用所有均线的两种应用变体。

变体 1. 线坡度。除了标准指标参数之外, 我们还需要一个参数来判断第二个指标点相对于所检查坡度的位移。 

参数

    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 — 价格类型;
    6. int shift2 — 相对于所检查坡度点的位移。

变体 2. 两条线: 快速和慢速。快速线高于慢速线 — 上行趋势, 快速低于慢速 — 下行趋势。需要两套相同的标准参数。 

参数

    1. int ama_period1 — AMA 周期; 
    2. int fast_ma_period1 — 快速均线周期;
    3. int slow_ma_period1 — 慢速均线周期; 
    4. int ama_shift1 — 指标的水平位移; 
    5. ENUM_APPLIED_PRICE  applied_price1 — 价格类型;
    6. int ama_period2 — AMA 周期; 
    7. int fast_ma_period2 — 快速均线周期;
    8. int slow_ma_period2 — 慢速均线周期; 
    9. int ama_shift2 — 指标的水平位移; 
    10. ENUM_APPLIED_PRICE  applied_price2 — 价格类型。
注意: 末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。
iAO (动量震荡器)


指标没有参数, 与附带某些参数的 MACD 指标相同, 因此不会用到。   

iATR (平均真实范围)

指示器不依赖于方向, 将会使用一个变体。

变体 1: 不依赖于方向。ATR 线相对于级别的位置。ATR 高于级别 — 允许买入和卖出。除了指标参数外, 我们还需要一个额外的级别参数。 

 参数:

  1. int ma_period — 周期;
  2. double level — 级别。  

iBearsPower (熊市动力)iBullsPower (牛市动力)

BearsPower 和 BullsPower 指标并不对称, 即它们应该配合使用: 一个用于买入信号, 另一个用于卖出信号。我们将使用 BullsPower 来判断上行趋势, 而 BearsPower 则判断下行趋势。我们将使用两个变体: 指标相对于级别的位置, 以及坡度。

变体 1. 如果 BullsPower 高于设定的级别 — 允许买入, 如果 BearsPower 低于同样的级别 — 允许卖出。除标准参数之外, 我们还需要一个额外的参数。

参数:

    1. int ma_period — 周期;
    2. double level — 级别。   

变体 2. 指标方向。对于此变体, 我们需要一个额外的参数来判断第二个点相对于所定义坡度的位移。

参数:

    1. int ma_period — 周期;
    2. int shift2 — 第二个点的位移   

iBands (布林带)

指标是一个通道。

变体 1. 如果价格上穿上边界, 上行趋势开始; 当价格下穿中心线时, 趋势取消。指标对于下边界和下行趋势等 其它方向 的操作类同。  

参数:

    1. int bands_period — 中心线计算周期;
    2. int bands_shift — 指标的水平线位移; 
    3. double — 标准偏差的数量; 
    4. ENUM_APPLIED_PRICE  applied_price — 价格类型。

iCCI (商品通道指数)

指标是一款子窗口中的振荡器。指标线即使有较大周期的强烈断裂, 因此坡度版本将不会用到。

变体 1. 相对于级别的位置。如果指标高于级别 — 上行趋势, 如果低于级别的负值 — 下行趋势。 

参数:

    1. int ma_period — 均化周期; 
    2. ENUM_APPLIED_PRICE  applied_price — 价格类型或句柄;  
    3. double level — 级别。   

iChaikin (Chaikin 振荡器)

类似于 CCI 的指标应用。

变体 1. 相对于级别的位置。如果指标高于级别 — 上行趋势, 如果低于级别的负值 — 下行趋势。 

参数:

    1. int fast_ma_period — 快速周期;  
    2. int ow_ma_period — 慢速周期; 
    3. ENUM_MA_METHOD ma_method — 平滑类型; 
    4. ENUM_APPLIED_VOLUME  applied_volume — 使用交易量; 
    5. double level — 级别。 

iDEMA (双重指数移动均线)

指标是一种移动平均线, 且其应用类似于先前讨论的 AMA。

变体 1. 线坡度。 

参数

    1. int ma_period — 均化周期;
    2. ENUM_APPLIED_PRICE  applied_price — 价格类型;
    3. int ma_shift — 指标的水平位移; 
    4. int shift2 — 相对于所检查坡度点的位移。

变体 2. 两条线: 快速和慢速。

参数

    1. int ma_period1 — 均化周期;
    2. ENUM_APPLIED_PRICE  applied_price1 — 价格类型;
    3. int ma_shift1 — 指标的水平位移; 
    4. int ma_period2 — 均化周期;
    5. ENUM_APPLIED_PRICE  applied_price2 — 价格类型;
    6. int ma_shift2 — 指标的水平位移。
注意: 末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。 

iDeMarker (DeMarker)

指标是一款在子窗口中的振荡器, 其范围在 0 到 1 之间。指标的中性级别为 0.5。指标线非常不均匀, 因此不适合坡度变体。

变体 1. 相对于级别的位置。将使用参数设置卖出级别, 买入级别将相对于 0.5 的级别对称计算。 

参数:

  1. int ma_period — 均化周期;
  2. double level — 卖出级别。

iEnvelopes (包络)

指标是类似于布林带的通道, 但其应用更为容易。

变体 1. 如果价格高于通道的上边界, 则允许买入。如果低于通道低边界, 则允许卖出。  

参数:

  1. int ma_period — 中心线计算周期;
  2. int ma_shift — 指标的水平位移; 
  3. ENUM_MA_METHOD ma_method — 平滑类型;
  4. ENUM_APPLIED_PRICE applied_price — 价格类型; 
  5. double deviation — 通道边界距中心线的偏差。  

iForce (强推指数)

指标是一款在子窗口中的振荡器, 范围围绕 0 级。类似于 CCI 的指标应用。

变体 1. 相对于级别的位置。 

参数: 

  1. int ma_period — 均化周期; 
  2. ENUM_MA_METHOD ma_method — 平滑类型; 
  3. ENUM_APPLIED_VOLUME applied_volume — 计算使用交易量类型;
  4. double level — 级别。    

iFractals (分形)

指标没有参数。

变体 1. 依据最后一个分形类型定义。如果最后一个分形在上部, 则允许卖出。如果其在下部 — 允许买入。  

iFrAMA (Fractal Adaptive Moving Average)

指标是一款移动平均线。它的应用类似于 AMA。 

变体 1. 线坡度。 

参数

  1. int ma_period — 均化周期; 
  2. int ma_shift — 指标的水平位移; 
  3. ENUM_APPLIED_PRICE  applied_price — 价格类型;
  4. int shift2 — 相对于所检查坡度点的位移。

变体 2. 两条线: 快速和慢速。

参数

    1. int ma_period1 — 均化周期; 
    2. int ma_shift1 — 指标的水平位移; 
    3. ENUM_APPLIED_PRICE  applied_price1 — 价格类型;
    4. int ma_period2 — 均化周期; 
    5. int ma_shift2 — 指标的水平位移; 
    6. ENUM_APPLIED_PRICE  applied_price2 — 价格类型。
注意: 末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。 

iGator (鳄鱼振荡器)

指标是基于鳄鱼指标计算的。指标提供的信息可以从鳄鱼获得, 这就是为什么我们不会使用它。 

iIchimoku (Ichimoku Kinko Hyo)

下面的一对指标线可能令人感兴趣: Tenkan Kijun 和云图。在云图中, 我们可以定义两条位置变化的线 (顶部变化的线)。有两个指标使用变体。

变体 1. TenkanKijun 线的位置。如果 Tenkan 线 (红色) 高于 Kijun (蓝色), 此为上行趋势。如果 Tenkan 低于 Kijun — 此为下行趋势。

参数: 

    1. int tenkan_sen — Tenkan-sen 线周期; 
    2. int kijun_sen — Kijun-sen 线周期; 
    3. int senkou_span_b — Senkou Span B 周期。  

变体 2. 基于云图方向。云图如果由两线定义: SpanA 和 SpanB。如果 SpanA 高于 SpanB — 上行趋势, 反之 — 下行趋势。

参数: 

    1. int tenkan_sen — Tenkan-sen 线周期; 
    2. int kijun_sen — Kijun-sen 线周期; 
    3. int senkou_span_b — Senkou Span B 周期。  
iBWMFI (由 Bill Williams 开发的市场促进指数)

由于其特殊性质, 我们不会使用该指标。 

iMomentum (动量)

指标是一款子窗口中的振荡器。指标线非常不均匀, 不适合根据坡度判断趋势。将使用具有级别的版本。

变体 1. 级别。中性级别: 100。除了标准指标参数, 我们需要一个卖出级别。买入级别将相对于级别 100 对称计算。

参数:

    1. int mom_period — 均化周期; 
    2. ENUM_APPLIED_PRICE applied_price — 价格类型或句柄;
    3. double level — 卖出级别。  

iMFI (资金流指数)

指标是一款子窗口中的振荡器, 范围在 0 到 100 之间。中性级别: 50。指标线非常不均匀, 不适合根据线方向判断趋势。

变体 1. 按级别。除了标准参数, 我们需要一个参数来确定卖出级别。买入级别将相对于级别 50 对称计算。如果指标线高于买入级别 — 上行趋势, 如果低于卖出值 — 下行趋势。 

参数: 

    1. int ma_period — 均化周期;
    2. ENUM_APPLIED_VOLUME applied_volume — 计算使用交易量类型;
    3. double level — 卖出级别。  

iMA (移动均线)

这是一款简单的移动平均线。

变体 1. 线坡度。

参数

    1. int ma_period — 均化周期; 
    2. int ma_shift — 指标的水平位移; 
    3. ENUM_MA_METHOD ma_method — 平滑类型; 
    4. ENUM_APPLIED_PRICE applied_price — 价格类型;
    5. int shift2 — 相对于所检查坡度点的位移。

变体 2. 两条线: 快速和慢速。

参数

  1. int ma_period1 — 均化周期; 
  2. int ma_shift1 — 指标的水平位移; 
  3. ENUM_MA_METHOD ma_method1 — 平滑类型; 
  4. ENUM_APPLIED_PRICE applied_price1 — 价格类型;
  5. int ma_period2 — 均化周期; 
  6. int ma_shift2 — 指标的水平位移; 
  7. ENUM_MA_METHOD ma_method2 — 平滑类型; 
  8. ENUM_APPLIED_PRICE applied_price2 — 价格类型。
注意: 末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。 


iOsMA (振荡器 (MACD 直方图) 的移动平均值

OsMA 振荡器是一款子窗口中的直方图。两个可能的变体: 基于级别和方向。

变体 1. 按级别。如果直方条高于级别, 则允许买入, 如果低于同一级别的负值 — 允许卖出。

参数: 

  1. int fast_ema_period — 快速移动均线周期; 
  2. int slow_ema_period — 慢速移动均线周期; 
  3. int signal_period — 差分均化周期;
  4. ENUM_APPLIED_PRICE  applied_price — 价格类型或句柄; 
  5. double level — 级别。

变体 2. 基于方向。如果直方条指向上行趋势, 允许买入; 如果下行方向 — 允许卖出。我们需要一个额外的参数来判断第二个点相对于所定义方向的位移。  

参数:

  1. int fast_ema_period — 快速移动均线周期; 
  2. int slow_ema_period — 慢速移动均线周期; 
  3. int signal_period — 差分均化周期;
  4. ENUM_APPLIED_PRICE  applied_price — 价格类型或句柄; 
  5. int shift2 — 判断方向的第二点位移。 

iMACD (移动均线聚合-离散)

MACD 直方图在子窗口里绘制直方条和一条信号线。指标有三种使用方式: 基于直方图级别, 其方向, 和相对于信号线的位置。不过, 后者是一种特殊情况, OsMA 按级别检查 (当其级别设置为 0 时), 因此不会用到。  

变体 1. 按级别。如果直方条高于级别, 则允许买入, 如果低于同一级别的负值 — 允许卖出。

参数: 

  1. int fast_ema_period — 快速移动均线周期; 
  2. int slow_ema_period — 慢速移动均线周期; 
  3. int signal_period — 差分均化周期;
  4. ENUM_APPLIED_PRICE  applied_price — 价格类型或句柄; 
  5. double level — 级别。

变体 2. 基于方向。如果直方图指向上方 — 买入; 如果向下 — 卖出。我们需要一个额外的参数来判断第二个点相对于所定义方向的位移。  

参数:

  1. int fast_ema_period — 快速移动均线周期; 
  2. int slow_ema_period — 慢速移动均线周期; 
  3. int signal_period — 差分均化周期;
  4. ENUM_APPLIED_PRICE  applied_price — 价格类型或句柄; 
  5. int shift2 — 判断方向的第二点位移。 

iOBV (能量潮)

类似于 AD, 此指标没有特定的数值范围, 且波动剧烈。不会用到它。 

iSAR (抛物线停止和反转系统)

指标显示在价格图表上, 它绘制一行圆点。根据价格移动方向, 曲线可以位于柱线上方和下方。

变体 1. 相对于价格的位置。价格高于指标线 — 上行趋势, 价格低于指标线 — 下行趋势。 

参数:

  1. double step — 价格变化步幅 — 加速因子;
  2. double maximum — 最大步幅。 

iRSI (相对强度指数)

一款在子窗口中的振荡器。一个类似于 MFI 的应用程序变体。

变体 1. 按级别。

参数: 

  1. int ma_period — 均化周期; 
  2. ENUM_APPLIED_PRICE  price — 价格类型;
  3. double level — 卖出级别。  

iRVI (相对活力指数)

一款在子窗口中带有信号线的振荡器。数值围绕零变化。

变体 1. 按级别。高于级别 — 买入, 低于级别的负值 — 卖出。

参数:

    1. int ma_period — 均化周期;
    2. double level — 级别。  

变体 2. 主线和信号线。

参数:

    1. int ma_period — 均化周期。.  

iStdDev (标准偏差)

指标不依赖方向。类似于 ATR。

变体 1. 按级别。 

参数: 

    1. int ma_period — 均化周期; 
    2. int ma_shift — 指标的水平位移; 
    3. ENUM_MA_METHOD ma_method — 平滑类型; 
    4. ENUM_APPLIED_PRICE applied_price — 价格类型;
    5. double level — 级别。  

iStochastic (随机振荡器)

一款在子窗口中的带有主线和信号线的振荡器。 

变体 1. 主线的坡度。 

参数:

  1. int Kperiod — K-周期;
  2. int Dperiod — D-周期;
  3. int slowing — 最终平滑; 
  4. ENUM_MA_METHOD ma_method — 平滑类型; 
  5. ENUM_STO_PRICE price_field — 价格;
  6. int shift2 — 判断坡度的第二点位移。 

变体 2. 按级别。指标的数值从 0 到 100, 中性值为 50。需要额外的卖出级别参数; 买入级别将进行计算。 

参数:

  1. int Kperiod — K-周期;
  2. int Dperiod — D-周期;
  3. int slowing — 最终平滑; 
  4. ENUM_MA_METHOD ma_method — 平滑类型; 
  5. ENUM_STO_PRICE price_field — 价格;
  6. double level — 级别。

变体 3. 主线和信号线的位置。

参数:

  1. int Kperiod — K-周期;
  2. int Dperiod — D-周期;
  3. int slowing — 最终平滑; 
  4. ENUM_MA_METHOD ma_method — 平滑类型; 
  5. ENUM_STO_PRICE price_field — 价格。

iTEMA (三重指数移动均线)

另一款移动均线。

变体 1. 线坡度。

参数

  1. int ma_period — 均化周期; 
  2. int ma_shift — 指标的水平位移; 
  3. ENUM_APPLIED_PRICE applied_price — 价格类型;
  4. int shift2 — 相对于所检查坡度点的位移。

变体 2. 两条线: 快速和慢速。

参数

  1. int ma_period1 — 均化周期; 
  2. int ma_shift1 — 指标的水平位移; 
  3. ENUM_APPLIED_PRICE applied_price1 — 价格类型;
  4. int ma_period2 — 均化周期; 
  5. int ma_shift2 — 指标的水平位移; 
  6. ENUM_APPLIED_PRICE applied_price2 — 价格类型。
注意。末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。 

iTriX (三重指数移动均线振荡器)

一款在子窗口中的振荡器。一个变体 — 线坡度。其变化在于, 我们尝试使用第二个两条线的变体: 快速和慢速 

变体 1. 线坡度。

参数

  1. int ma_period — 均化周期; 
  2. int ma_shift — 指标的水平位移; 
  3. ENUM_APPLIED_PRICE applied_price — 价格类型;
  4. int shift2 — 相对于所检查坡度点的位移。
注意: iTriX 函数没有 ma_shift 参数, 但指标仍然可以位移。在任何情况下, 位移不会参照 shift 参数 (对于所有指标将被设置为 0), 而是参照计算的柱线索引执行

变体 2. 两条线: 快速和慢速。

参数

  1. int ma_period1 — 均化周期; 
  2. int ma_shift1 — 指标的水平位移; 
  3. ENUM_APPLIED_PRICE applied_price1 — 价格类型;
  4. int ma_period2 — 均化周期; 
  5. int ma_shift2 — 指标的水平位移; 
  6. ENUM_APPLIED_PRICE applied_price2 — 价格类型。
注意。末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。 

iWPR (威廉姆斯的百分比范围)

指标与慢速设定为 1 的随机振荡器的主线相同, 但尺度相反。将不会用到它。

iVIDyA (变指数动态平均)

另一款移动均线。

变体 1. 线坡度。

参数

  1. int cmo_period — CMO 周期 
  2. ema_period — 平滑周期 
  3. ma_shift — 指标的水平位移 
  4. ENUM_APPLIED_PRICE applied_price  — 价格类型
  5. int shift2 — 相对于所检查坡度点的位移。
变体 2. 两条线: 快速和慢速。

参数

  1. int ma_period1 — 均化周期; 
  2. int ma_shift1 — 指标的水平位移; 
  3. ENUM_APPLIED_PRICE applied_price1 — 价格类型;
  4. int ma_period2 — 均化周期; 
  5. int ma_shift2 — 指标的水平位移; 
  6. ENUM_APPLIED_PRICE applied_price2 — 价格类型。
注意: 末尾数字为 1 的变量用于快速线, 而 2 用于慢速线。 

iVolumes (交易量)

指标值不依赖于价格方向。指标的运用类似于 ATR 或 STD。

变体 1. 级别。如果指标值高于级别, 则允许买入和卖出。 

参数:

  1. ENUM_APPLIED_VOLUME  applied_volume — 交易量类型;
  2. double level — 级别。  

我们已定义了指标及其应用变体。现在我们来编写一个所有类型的枚举。在 Includes 文件夹里, 我们创建 UniTrend 文件夹, 并创建内含枚举的 UniTrendDefines.mqh 文件: 

enum EType{    Type_ADX_PDIMDI,          // ADX 指标 PDI 和 MDI 线的位置    Type_ADX_Level,           // 相对于级别的 ADX 线位置    Type_ADXW_PDIMDI,         // ADX Wilder 指标 PDI 和 MDI 线的位置    Type_ADXW_Level,          // 相对于 ADX Wilder 级别的 ADX 线位置    Type_Alligator,           // 鳄鱼    Type_AMA_Dir,             // АМА  的方向    Type_AMA_2MA,             // 两个 АМА    Type_ATR_Level,           // ATR    Type_BuBe_Level,          // 牛/熊动力及级别    Type_BuBe_Dir,            // 牛/熊动力的方向    Type_Bands,               // 布林带    Type_CCI_Level,           // CCI 和级别    Type_Chaikin_Level,       // Chaikin 振荡器和级别    Type_DEMA_Dir,            // DEMA 的方向    Type_DEMA_2MA,            // 两个 DEMA    Type_DeMarker_Level,      // DeMarker 和级别    Type_Envelopes,           // 包络    Type_Force_Level,         // 强推振荡器和级别    Type_Fractals,            // 分形    Type_FrAMA_Dir,           // FrAMA 的方向    Type_FrAMA_2MA,           // 两个 FrAMA    Type_Ichimoku_TK,         // Ichimoku: Tenkan 和 Kijun    Type_Ichimoku_SASB,       // Ichimoku: 云图    Type_Momentum_Level,      // 动量和级别    Type_MFI_Level,           // MFI 和级别    Type_MA_Dir,              // MA 的方向    Type_MA_2MA,              // 两个 MA    Type_OsMA_Dir,            // OsMA 的方向    Type_OsMA_Level,          // OsMA 和级别    Type_MACD_Dir,            // MACD 的方向    Type_MACD_Level,          // MACD 和级别    Type_SAR,                 // SAR    Type_RSI_Level,           // RSI 和级别    Type_RVI_Level,           // RVI 和级别    Type_RVI_MS,              // RVI 的主线和信号线    Type_STD_Level,           // 标准偏差和级别    Type_Sto_Dir,             // 随机振荡器的方向    Type_Sto_Level,           // 随机振荡器和级别    Type_Sto_MS,              // 随机振荡器的主线和信号线    Type_TEMA_Dir,            // TEMA 的方向    Type_TEMA_2MA,            // 两个 TEMA    Type_TriX_Dir,            // TriX 的方向    Type_TriX_2MA,            // 两个 TriX    Type_VIDyA_Dir,           // VIDyA 的方向    Type_VIDyA_2MA,           // 两个 VIDyA    Type_Volumes              // 交易量

};

外部参数

通过分析上述指标说明, 我们可以确定所需的外部参数集。所有需要的参数及其类型在表 1 里给出。我们基本上会在变量里使用前缀 "f_" (其意为快速)。对于带有快速和慢移动均值的变体, 我们将在第二组参数里使用前缀 "s_" (其意为慢速)。

表 1. 外部参数和它们的类型  

类型 名称
int  f_period1
int  f_shift1
int  f_period2
int  f_shift2
int  f_period3
int  f_shift3
ENUM_MA_METHOD  f_method
ENUM_APPLIED_PRICE  f_price
ENUM_APPLIED_VOLUME   f_volume
ENUM_STO_PRICE  f_sto_price
double  f_level
int  f_dot2shift
double  f_step 
double  f_maximum 
int  s_period1
int  s_shift1
int  s_period2
int  s_shift2
int  s_period3
int 
s_shift3
ENUM_MA_METHOD  s_method
ENUM_APPLIED_PRICE  s_price
int mult 
int  level_digits 
int  sar_step_digits 
int  sar_maximum_digits 

除了在指标描述里指定的参数, 在表格底部还提供了少量额外参数: 

  • mult — 参数的倍数取决于报价中的小数位数。诸如 MACD 和 OsMA 等一些指标的数值与价格挂钩, 因此将它们设为点数更为便捷。参数值需要根据报价中的小数位数进行调整。
  • level_digits — level 参数中的小数位数。在图形界面中, 使用 CSpinBox 控件 (带有 "+" 和 "-" 按钮的输入字段) 设置级别值, 因此, 它可以很轻松地为不同的指标设置不同的最小变化 (当按 "+" 或 "-" 时的变化量)。
  • sar_step_digits — SAR 指标 step 参数的小数位数。
  • sar_maximum_digits — SAR 指标 maximum 参数的小数位数。
现在很难确定下列参数的适当值 level_digits, sar_step_digitssar_maximum_digits, 所以我们将在 UniTrendDefines.mqh 文件里添加常量, 然后可以很容易地调整它们的值:
#define ADX_LEVEL_DIGITS         0 // 用于 ADX 指标
#define ADXW_LEVEL_DIGITS        0 // 用于 ADX Wilder 指标
#define ATR_LEVEL_DIGITS         1 // 用于 ATR 指标
#define BUBE_LEVELS_DIGITS       1 // 用于牛/熊动力指标
#define CCI_LEVEL_DIGITS         0 // 用于 CCI 指标
#define CHAIKIN_LEVEL_DIGITS     0 // 用于 Chaikin 指标
#define DEMARKER_LEVEL_DIGITS    2 // 用于 Demarker 指标 
#define FORCE_LEVEL_DIGITS       3 // 用于强推指标
#define MOMENTUM_LEVEL_DIGITS    2 // 用于动量指标
#define MFI_LEVEL_DIGITS         0 // 用于 MFI 指标
#define OSMA_LEVBEL_DIGITS       2 // 用于 OsMA 指标
#define MACD_LEVEL_DIGITS        2 // 用于 MACD 指标
#define RSI_LEVEL_DIGITS         0 // 用于 RSI 指标
#define RVI_LEVEL_DIGITS         2 // 用于 RVI 指标
#define STD_LEVEL_DIGITS         1 // 用于 STD 指标
#define STO_LEVEL_DIGITS         0 // 用于随机振荡器指标
#define BANDS_LEVEL_DIGITS       1 // 用于布林带
#define ENVELOPES_LEVEL_DIGITS   2 // 用于包络指标
#define SAR_STEP_DIGITS          3 // 用于 SAR 指标 (step 参数)
      #define SAR_MAXIMUM_DIGITS       2 // 用于 SAR 指标 (maximum 参数)

对于 ATR, 牛/熊, OsMA, MACD, STD 的级别将设为点数。 

我们来根据表 1 创建以下结构:

struct SExtParams{
   int                 f_period1;
   int                 f_shift1;
   int                 f_period2;
   int                 f_shift2;
   int                 f_period3;
   int                 f_shift3;
   long                f_method;
   long                f_price;
   long                f_volume;
   long                f_sto_price;
   double              f_level;
   int                 f_dot2shift;
   double              f_step;  
   double              f_maximum;  
   int                 s_period1;
   int                 s_shift1;
   int                 s_period2;
   int                 s_shift2;
   int                 s_period3;
   int                 s_shift3;
   long                s_method;
   long                s_price;
   int                 mult;
   int                 level_digits;
   int                 sar_step_digits;
   int                 sar_maximum_digits;      
            };

将参数组合到一个结构中可令我们将它们与其余的代码隔离开来。若是可用的选项不够, 我们需要通过添加新的趋势检测类型来修改指标, 这会非常便利。此外, 这大大简化了函数和类方法的参数传递。

由于本文中生成的指标将趋势显示为一排字符 (对于所有指标都是相同的), 我们可以轻松地向指标中添加函数来通知趋势开始。有人可能会对通知一个完全形成的趋势 (示意在一根完整的柱线上) 感兴趣, 而有人想第一时间抓住新趋势出现 (在新出现的柱线上)。所以, 我们将编写用于选择通知类型的枚举:

enum EAlerts{
   Alerts_off=0, // 禁用通知
   Alerts_Bar0=1, // 新出现柱线上通知
   Alerts_Bar1=2 // 完整柱线上通知
            };  

创建一款指标

在文章 "带有图形界面的通用振荡器" 和 "带有图形界面的通用通道" 中详细讨论了如何创建通用指标的类。我们来研究为它们创建与本文有关的具体功能。

所有上述判断趋势的方法分为两类: 一个或两个指标。在第一组中, 加载指标后, 我们需要检查一个句柄, 而第二组我们要检查其中两个。这意味着基类将有两个子类用不同的方法来检查句柄。反过来, 这些子类还会有自己的子类, 并在其中判断趋势。

基类:

class CUniTrend{
   protected:
  
      int m_handle1;
      int m_handle2;
      
      string m_name;
      string m_help;
      
      int m_ci;
      
      double m_b1[1];
      double m_b2[1];      
      double m_b3[1];    
      double m_b4[1];
            
      int m_shift;
      int m_shift1;    
      int m_shift2;
      int m_shift3;
      
      int m_dot2shift;
      
      double m_level;      
      
   public:
  
      void CUniTrend(){
         m_handle1=INVALID_HANDLE;
         m_handle2=INVALID_HANDLE;
      }
      
      void ~CUniTrend(){
         if(m_handle1!=INVALID_HANDLE){
            IndicatorRelease(m_handle1);
         }
         if(m_handle2!=INVALID_HANDLE){
            IndicatorRelease(m_handle2);
         }        
      }
  
      virtual int Calculate( const int rates_total,
                     const int prev_calculated,
                     double & upBuffer[],
                     double & dnBuffer[]
      ){
         return(rates_total);
      }
      
      virtual bool Calculated(){
         return(false);
      }
      
      virtual bool CheckHandles(){
         return(true);
      }      
      
      string Name(){
         return(m_name);
      }    
      string Help(){
         return(m_help);
      }
            };

保护部分包含各种辅助变量的声明, 这在子类中是有用的。指标句柄的变量在构造函数中初始化, 在析构函数中释放句柄。其余的方法是虚构的。

含有一个指标的子类变体:

class CUniTrend1:public CUniTrend{
   public:
      bool Calculated(){
         if(BarsCalculated(m_handle1)>0){
            return(true);
         }  
         else{
            return(false);
         }    
      }
      
      bool CheckHandles(){
         return(m_handle1!=INVALID_HANDLE);
      }
            };

该类有两个实际的方法: CheckHandle() — 它可以检查指标是否可以加载, Calculated() — 可以确定指标是否计算完整, 以及趋势缓冲区的显示内容是否已更新。

含有两个指标的子类变体:

class CUniTrend2:public CUniTrend{
   public:
      bool Calculated(){
         if(BarsCalculated(m_handle1)>0 && BarsCalculated(m_handle2)>0){
            return(true);
         }  
         else{
            return(false);
         }    
      }      
      
      bool CheckHandles(){
         return(m_handle1!=INVALID_HANDLE && m_handle2!=INVALID_HANDLE);
      }
            };

含有不同趋势识别变体的类都将是 CUniTrend1 或 CUniTrend2 的子类。研究下面的子类:

class CUniTrend_ADX_PDIMDI:public CUniTrend1{
   private:  
   public:
  
      void CUniTrend_ADX_PDIMDI( bool use_default,
                                 bool keep_previous,
                                 SExtParams & par){
        
         // 设置省缺参数
        
         if(use_default){
            if(keep_previous){
               if(par.f_period1==PARAMETER_EMPTY)par.f_period1=14;
            }
            else{
               par.f_period1=14;
            }      
         }          
         // 加载指标
         m_handle1=iADX(Symbol(),Period(),par.f_period1);
         // 形成指标名和参数提示
         m_name=StringFormat( "iADX_PDIMDI(%i)",
                              par.f_period1
                            );
  
         m_help=StringFormat( "adx_period - f_period1(%i)",
                              par.f_period1
                            );
      }
      
      int Calculate( const int rates_total,
                     const int prev_calculated,
                     double & upBuffer[],
                     double & dnBuffer[]
      ){
         int start;
        
         if(prev_calculated==0){
            start=1;
         }
         else{
            start=prev_calculated-1;
         }
      
         for(int i=start;i<rates_total;i++){
        
            upBuffer[i]=EMPTY_VALUE;        
            dnBuffer[i]=EMPTY_VALUE;
        
            m_ci=rates_total-i-1;
            
            if(CopyBuffer(m_handle1,PLUSDI_LINE,m_ci,1,m_b1)==-1){
               return(0);
            }
            
            if(CopyBuffer(m_handle1,MINUSDI_LINE,m_ci,1,m_b2)==-1){
               return(0);
            }
            
            if(m_b1[0]>m_b2[0]){
               upBuffer[i]=1;
            }
            else if(m_b1[0]<m_b2[0]){
               dnBuffer[i]=-1;            
            }
            
         }      
      
         return(rates_total);
      }
            };

类的构造函数中的主要重点已被注释, Calculate() 方法是指标标准 OnCalculate() 函数的模拟。函数代码与指标代码类似。

在本文附件中, 文件中的所有指标类都位于 Include/UniTrend/UniTrendIndicators.mqh 中。

现在, 所有的类我们都已准备就绪, 我们可以创建一个简单的指标来判断趋势, 就像我们在有关通用振荡器和通用通道的文章中所做的那样。在附件里的 Indicators/iUniTrend.mq5 文件中提供了一个不带图形界面的现成指标。

创建图形界面

图形界面的所有类位于文件 UniTrendForm.mqh 和 UniTrendControl.mqh 中。表单类在 UniTrendForm.mqh 文件里提供, 而 UniTrendControl.mqh 文件包含用于输入指标参数的通用控件类。无须详细讨论表单类的创建, 因为它已在有关通用振荡器和通用通道的文章中研究过了。在文章 "自定义图形控件。第 3 部分: 表单" 里也有详尽研究。我们来研究创造一个通用的控件。

通用控件的基础是 CUniTrendControl 基础类。该类的公有部分仅包含虚构方法, 保护部分包含一些用于下拉列表的辅助方法: 用于使用变体填充列表的方法, 以及用于设置列表中所选项目的方法。此处是带注释的基类代码:

class CUniTrendControl{
   protected:
      
      /* 用于计算最小值变化的函数
         通过 level 参数的小数位数
      */
      double SolveChange(int d){
         return(NormalizeDouble(1.0/pow(10,d),d));  
      }
      
      // 用 ENUM_MA_METHOD 变体填充列表
      void AddVariantsMethod(CComBox & cb){
         for(int i=0;i<ArraySize(e_method);i++){
            cb.AddItem(EnumToString((ENUM_MA_METHOD)e_method[i]));
         }
      }
      
      // 用 ENUM_APPLIED_PRICE 变体填充列表
      void AddVariantsPrice(CComBox & cb){
         for(int i=0;i<ArraySize(e_price);i++){
            cb.AddItem(EnumToString((ENUM_APPLIED_PRICE)e_price[i]));
         }
      }      
      
      // 用 ENUM_APPLIED_VOLUME 变体填充列表
      void AddVariantsVolume(CComBox & cb){
         for(int i=0;i<ArraySize(e_volume);i++){
            cb.AddItem(EnumToString((ENUM_APPLIED_VOLUME)e_volume[i]));
         }
      }  
      
      // 用 ENUM_STO_PRICE 变体填充列表     
      void AddVariantsStoPrice(CComBox & cb){
         for(int i=0;i<ArraySize(e_sto_price);i++){
            cb.AddItem(EnumToString((ENUM_STO_PRICE)e_sto_price[i]));
         }
      }      
      
      // 获取 ENUM_MA_METHOD 的索引值  
      int MethodIndex(long val){
         for(int i=ArraySize(e_method)-1;i>=0;i--){
            if(e_method[i]==val){
               return(i);
            }
         }
         return(-1);
      }
      
      // 获取 ENUM_APPLIED_PRICE 的索引值
      int PriceIndex(long val){
         for(int i=ArraySize(e_price)-1;i>=0;i--){
            if(e_price[i]==val){
               return(i);
            }
         }
         return(-1);
      }  
      
      // 获取 ENUM_APPLIED_VOLUME 的索引值   
      int VolumeIndex(long val){
         for(int i=ArraySize(e_volume)-1;i>=0;i--){
            if(e_volume[i]==val){
               return(i);
            }
         }
         return(-1);
      }  
      
      // 获取 ENUM_STO_PRICE 的索引值     
      int StoPriceIndex(long val){
         for(int i=ArraySize(e_sto_price)-1;i>=0;i--){
            if(e_sto_price[i]==val){
               return(i);
            }
         }
         return(-1);
      }      
      
   public:
      
      // 控件初始化
      virtual void Init(SExtParams & par){}
      
      // 设置数值
      virtual void SetValues(SExtParams & par){}      
      
      // 获取数值
      virtual void GetValues(SExtParams & par){}
      
      // 显示控件
      virtual void Show(int x,int y){}      
      
      // 隐藏控件
      virtual void Hide(){}      
      
      // 用于计算表单高度的控件数量
      virtual int ControlsCount(){
         return(0);
      }
      
      // 事件处理
      virtual int Event(int id,long lparam,double dparam,string sparam){
         return(0);
      }
      
};
    

我们来研究基于 ADX 和级别 (两个控件) 进行趋势检测的子类:

class CUniTrendControl_ADX_Level: public CUniTrendControl{
   private:
  
      // 简单控件的指针
      CSpinInputBox m_f_period1;
      CSpinInputBox m_f_level;
  
   public:
  
      // 控件初始化
      void Init(SExtParams & par){
         m_f_period1.Init("f_period1",SPIN_BOX_WIDTH,1," adx_period");
         m_f_period1.SetMinValue(1);
         m_f_period1.SetReadOnly(false);
         m_f_level.Init("f_level",COMBO_BOX_WIDTH,this.SolveChange(par.level_digits)," level");
         m_f_level.SetMinValue(0);
         m_f_level.SetReadOnly(false);
      }
  
      // 设置数值
      void SetValues(SExtParams & par){        
         m_f_period1.SetValue(par.f_period1);
         m_f_level.SetValue(par.f_level);
      }
  
      // 获取数值
      void GetValues(SExtParams & par){
         par.f_period1=(int)m_f_period1.Value();
         par.f_level=m_f_level.Value();
      }    
  
      // 显示控件
      void Show(int x,int y){
         m_f_period1.Show(x,y);
         y+=20;
         m_f_level.Show(x,y);
      }      
  
      // 隐藏控件
      void Hide(){
         m_f_period1.Hide();
         m_f_level.Hide();
      }
  
      // 用于计算表单高度的控件数量
      int ControlsCount(){
         return(2);
      }
  
      // 执行控件事件
      int Event(int id,long lparam,double dparam,string sparam){
         int e1=m_f_period1.Event(id,lparam,dparam,sparam);
         int e2=m_f_level.Event(id,lparam,dparam,sparam);
         if(e1!=0 || e2!=0){
            return(1);
         }
         return(0);
      }
};
    

传递一个 SExtParam 类型的变量到方法 SetValues() 和 GetValues(), 在每个子类中仅使用结构的必需字段。我们需要设置级别控件的最小变化值; 这可以在控件初始化期间完成, 因此带有参数的结构也传递给 Init() 方法。一般来说, 子类的创建对应于文章 "自定义图形控件。第 1 部分: 创建一个简单控件" 中描述的控件创建的所有原则, 期望在此仅创建必要的方法, 而非全部。 

图形界面与指标的结合

指标创建阶段与创建通用趋势/通道指标的相应阶段非常类似。我们来研究一下不同之处。

早先, 当指标以省缺值 ​​(UseDefault=true) 操作时, 所有参数初始化为 -1。现在这个选项不合时宜了, 因为某些指标的 level 参数可以是负数。因此, 采用在 UniTrendDefines.mqh 文件中声明的 PARAMETER_EMPTY 常量值来执行变量的初始化。常数的值为 INT_MAX (此值极大超过了实际值的限制)。

另一个小小区别是 OnTimer() 函数。调用 Calculated() 方法来检查指标是否已计算, 因为对于趋势的一些变体, 我们仅需检查一个指标, 而对于其它指标, 我们要检查两个指标。这只能在指标类的内部才知晓。

结果就是, 我们得到了另一款通用且十分方便的指标 (图例. 10)。

图例. 10. 带有图形界面的通用趋势指标
图例. 10. 带有图形界面的通用趋势指标

注意: 除了显示在表单内的列表中所选变体的名称之外, 所使用的指标类型也显示在指标子窗口的左上角。除了类型之外, 还显示所有参数的值 (括号中)。如果指定了级别点数值, 则写入用于计算实际级别值的表达式 (图例. 11)。

 
图例. 11. 显示指定参数的点数

现成的指标已在下面的附件中提供, 文件名为 Indicators/iUniTrendGUI.mq5。  

结论 

总而言之, 指标包括 46 种不同的选项来判断趋势。图形界面令您快速更改指标及其类型的参数, 从而方便地分析历史数据。通知功能的存在也令指标也有助于实际应用。

创建图形界面的新方法各具优点和缺点。其缺点是包含大量的代码, 因此工作量庞大。不像管理控件可视性的类 (如在通用振荡器和通用通道那样), 在此几乎为每个趋势检测的变体创建了几乎完整的控件元素。然而, 复合控件代码与表单代码之间的清晰隔离与独立性大大简化了开发过程, 同时也简化了指标的进一步扩展, 而这些应该是必要的。

附件

本文的附件是可下载的存档, 其中包含所需的文件。将文件置于正确的文件夹中。它们应保存到终端的相同文件夹中。