基于ADX及EMA的交易系统

1 名词解释

缩写

全称

释义

ADX

Average Directional Indicator

平均趋势指数

EMA

Exponential Moving Average

指数平均数

DMI

Directional Movement Index

动向指标

1.1 ADX

ADX定义:ADX指标(平均方向性指数),即平均趋势指数,是一种趋势判断的技术指标。ADX指标不可以确定市场发展的方向,但可以确定市场趋势的强弱。换句话说,ADX指标可以分辨市场顺着趋势运作的概率,分辨市场前景是持续趋势或是反转

ADX数值分析:ADX数值在判断趋势强弱中,通常将20作为中间值,也就是被看作行情横盘中的没有趋势。

ADX大于30的时候,表示趋势运行强度比较大,行情仍然会沿着当前趋势发展;

ADX小于10的时候,往往预示着价格走势的趋势比较弱,需要警惕行情出现反转。

同时,还可以利用ADX和DI相互关系预测行情走势,一般来看:当-DI向上突破+DI时是黄金交叉,被认为是买入时机,当+DI向下突破-DI时是死亡交叉,被认为是卖出时机。

ADX指标计算:

在进行ADX计算时,需要先选定一个时间周期,一般为14天。

  1. 计算动向变化up:今天的最高价 – 昨天的最高价 down:昨天的最低价 – 今天的最低价 +DM:如果up大于max(down, 0),则+DM等于up,否则等于零 -DM:如果down大于max(up, 0),则-DM等于down,否则等于零

  1. 计算真实波幅

True Range

TR:max[(当天最高价-当天最低价), abs(当天最高价-昨日收盘价), abs(当天最低价-昨日收盘价)]

  1. 计算动向指数

+DI(14)=+DM(14)/TR(14)*100

-DI(14)=-DM(14)/TR(14)*100

  1. 计算ADX

DX:((+DI14)- (-DI14)/(+DI14)+(-DI14))*100

ADX:MA(DX,14)

MA代表移动平均数

up和down分别代表了价格正向和负向移动距离;+DI和-DI分别代表用波动率修正后上涨和下跌趋势。不管趋势是上涨还是下跌,只要存在明显的趋势行情,那么+DI和-DI中总有一个是较大的,因此DX的值会随着趋势的强弱指示在0~100之间;最后ADX则是DX的14天平均线。

1.2 EMA

EMA定义:指数移动平均值(Exponential Moving Average, EMA)和加权移动平均类似,但不同之处是各数值的加权按指数递减,而非线性递减。比较均价的趋势快慢时,用 EMA 更稳定;有时,在均价值不重要时,也用 EMA 来平滑和美观曲线。EMA指标由于其计算公式中着重考虑了当天价格(当期)行情的权重,决定了其作为一类趋势分析指标

这里表示权重的衰减程度

T是用来计算阿尔法的参数,它不代表指数衰减在T期后结束

权重因子一般小于1,大于0.5

WMA

EMA

2 参数设定

2.1 Params

Numeric DMI_N(14); //DMI的N值,计算DMI指标所需的周期数
Numeric DMI_M(30); //DMI的M值,计算ADX(Average Directional Index)均线所需的周期数
Numeric AvgLen(30); //最高最低价的EMA(Exponential Moving Average)周期数,用于计算通道宽度
Numeric EntryBar(2); //保持BuySetup触发BAR数,指当出现买入信号后,需要保持几根K线才真正发出买入信号

2.2 Vars

Numeric X; //循环计数器

//DMI最终算出结果需要的变量名。
NumericSeries ODMIPlus; //原始正向动向值序列
NumericSeries ODMIMinus; //原始负向动向值序列
NumericSeries ODMI; //原始动向指数序列
NumericSeries OADX; //原始ADX指标序列
NumericSeries OADXR; //原始ADXR指标序列
NumericSeries OVolty; //原始通道宽度序列

//DMI过程计算中需要的变量名。//
NumericSeries SDMI; //平滑后的动向指数序列
NumericSeries SADX; //平滑后的ADX指标序列
NumericSeries Cumm; //累计动向指数序列
NumericSeries SVolty; //平滑后的通道宽度序列
NumericSeries AvgPlusDM; //正向动向值的EMA序列
NumericSeries AvgMinusDM; //负向动向值的EMA序列
NumericSeries TRValue; //真实高点和真实低点的差值序列

Numeric PlusDM; //正向动向值
Numeric MinusDM; //负向动向值
Numeric UpperMove; //通道上轨
Numeric LowerMove; //通道下轨
Numeric SumPlusDM; //正向动向值的和
Numeric SumMinusDM; //负向动向值的和
Numeric SumTR; //真实高点和真实低点的差值的和
Numeric Divisor; //用于计算DX(Directional Index)的分母

//计算EMA用的变量名。//
NumericSeries UpperMA; //计算30根K线最高价的EMA序列
NumericSeries LowerMA; //计算30根K线最低价的EMA序列
NumericSeries ADXValue; //计算ADX均线序列
NumericSeries ChanSpread; //通过EMA计算出通道宽度序列
NumericSeries BuyTarget; //多头触发价序列
NumericSeries SellTarget; //空头触发价序列
NumericSeries MROBS; //上一次买入条件到现在的周期数序列
NumericSeries MROSS; //上一次卖出条件到现在的周期数序列
Numeric N; //下单手数
Numeric T; //时间间
Numeric SF; //平滑因子


Numeric BuySetup; //买入条件
Numeric SellSetup; //卖出条件

3 信号发出

在信号发出之前,首先进行了DMI(Directional Movement Index)指标的计算,计算过程如下:

  • 如果当前是第DMI_N:14个K线,则根据前DMI_N个K线的高、低价计算出PlusDM(上升动向值)和MinusDM(下降动向值)的平均值,以及真实波幅(TRValue)的平均值。

        If(CurrentBar == DMI_N) //当根K线是第DMI_N根
        {
            For X = 0 To DMI_N - 1
            {
                PlusDM = 0;
                MinusDM = 0;
                UpperMove = High[X] - High[X + 1]; //高点差值
                LowerMove = Low[X + 1] - Low[X]; //低点差值
                If(UpperMove > LowerMove && UpperMove > 0) //高点差值大于低点差值且大于0
                {
                    PlusDM = UpperMove; //取高点差值
                }
                Else If(LowerMove > UpperMove && LowerMove > 0) //低点差值大于高点差值且大于0
                {
                    MinusDM = LowerMove; //取低点差值
                }
                SumPlusDM = SumPlusDM + PlusDM; //高点差值的和
                SumMinusDM = SumMinusDM + MinusDM; //低点差值的和
                SumTR = SumTR + TRValue[X]; //真实高点和低点差值的和
            }
            AvgPlusDM = SumPlusDM / DMI_N;
            AvgMinusDM = SumMinusDM / DMI_N;
            SVolty = SumTR / DMI_N;
        }
  • 如果当前是第DMI_N+1个或以后的K线,则根据当前K线的高、低价计算出PlusDM和MinusDM的新值,并以1/DMI_N的比例加入到之前DMI_N个K线的平均值中。真实波幅的计算同样使用平均值法。

        Else If(CurrentBar > DMI_N) //当根K线大于DMI_N根
        {
            PlusDM = 0;
            MinusDM = 0;
            UpperMove = High - High[1]; //高点差值
            LowerMove = Low[1] - Low; //低点差值
            If(UpperMove > LowerMove && UpperMove > 0) //高点差值大于低点差值且大于0
            {
                PlusDM = UpperMove; //取高点差值
            }
            Else If(LowerMove > UpperMove && LowerMove > 0) //低点差值大于高点差值且大于0
            {
                MinusDM = LowerMove; //取低点差值
            }
            AvgPlusDM = AvgPlusDM[1] + SF * (PlusDM - AvgPlusDM[1]);
            AvgMinusDM = AvgMinusDM[1] + SF * (MinusDM - AvgMinusDM[1]);
            SVolty = SVolty[1] + SF * (TRValue - SVolty[1]);
  • 如果 SVolty 大于 0,则计算 ODMIPlus 和 ODMIMinus。

        If(SVolty > 0)
        {
            ODMIPlus = 100 * AvgPlusDM / SVolty;
            ODMIMinus = 100 * AvgMinusDM / SVolty;
        }
        Else
        {
            ODMIPlus = 0;
            ODMIMinus = 0;
        }
  • 计算 Divisor,然后使用 ODMIPlus 和 ODMIMinus 计算 SDMI。

        Divisor = ODMIPlus + ODMIMinus;
        If(Divisor > 0)
        {
            SDMI = 100 * Abs(ODMIPlus - ODMIMinus) / Divisor;
        }
        Else
        {
            SDMI = 0;
        }

接着,根据DMI指标计算出ADX(Average Directional Index)均线。计算过程如下:

  • 如果当前K线数量小于等于DMI_M,则将SDMI(Smoothed Directional Movement Index)的累计值除以当前K线数量,得到ADX的初值SADX,并计算出OADXR的初值。

            If(CurrentBar <= DMI_M)
            {
                SADX = Cumm / CurrentBar;
                OADXR = (SADX + SADX[CurrentBar - 1]) * 0.5;
            }
  • 如果当前K线数量大于DMI_M,则以1/DMI_N的比例加入SDMI的新值,并以EMA的方式计算出新的SADX值,再计算OADXR的值。

            Else
            {
                SADX = SADX[1] + SF * (SDMI - SADX[1]);
                OADXR = (SADX + SADX[DMI_M - 1]) * 0.5;
            }
  • 买入条件

  • 当前收盘价高于30根K线最高价的EMA(即UpperMA);

  • ADX指标上涨,即ADXValue大于前一根K线的ADXValue。

  • 卖出条件

  • 当前收盘价低于30根K线最低价的EMA(即LowerMA);

  • ADX指标上涨,即ADXValue大于前一根K线的ADXValue。

当价格突破噪音通道的上边界并且趋势强度指标ADX在上升时,就会发出买入信号;当价格突破噪音通道的下边界并且ADX在上升时,就会发出卖出信号。这样的策略假设市场存在一定的趋势性,并且在趋势中会有一些波动,即噪音,噪音通道的作用就是把噪音滤掉,只留下趋势信号。而ADX指标则是用来衡量市场的趋势强度,只有在趋势强度较强时才会发出信号。

        OVolty = SVolty;
        ODMI = SDMI;
        OADX = SADX;
        //DMI计算结束
        ADXValue = OADX; //计算ADX均线
        UpperMA = Ema(High,AvgLen); //计算30根K线最高价的EMA
        LowerMA = Ema(Low,AvgLen); //计算30根K线最低价的EMA
        ChanSpread = (UpperMA - LowerMA) / 2; //通过EMA计算出噪音通道宽度
        BuySetup = Close > UpperMA && ADXValue > ADXValue[1]; //买入条件
        SellSetup = Close < LowerMA && ADXValue > ADXValue[1]; //卖出条件

如果当前的BuySetup变量值为1,说明满足了买入的条件,此时就可以根据Close(当前的收盘价)和ChanSpread(一个预设的偏移量)计算出目标价位BuyTarget,即多头触发价。

计算了一个名为MROBS的变量,它的值是上一次满足买入条件的BAR数目距离当前BAR的距离。


If(BuySetup == 1)
        {
            BuyTarget = Close + ChanSpread; //多头触发价
        }
        MROBS = BarsLast(BuySetup); //上次满足买入条件距离当前BAR的数目 
        If(MROBS > EntryBar) //如果距离上次买入条件超过EntryBar
        {
            MROBS = 0;
        }
  1. 首先计算上次满足买入条件(BuySetup)距离当前BAR的数目,并判断是否超过EntryBar。如果超过EntryBar,则将MROBS置为0。

  1. 如果MROBS[1]不为0(说明之前已经满足买入条件),MarketPosition(当前持仓方向)为0(说明当前没有持仓),并且CurrentBar(当前BAR的序号)大于100(为了避免过早的交易),则进入多头系统入场的逻辑:

  • 如果当前价格高于等于买入多头触发价(BuyTarget[1])且成交量(Vol)大于0,则进行多头开仓(BK)。

  1. 如果当前持有多单(MarketPosition为1)且BarsSinceEntry(持仓周期数)大于0(为了避免在入场后立即出场),且成交量大于0,则进入多头系统出场的逻辑:

  • 如果当前价格低于等于30根K线最高价的EMA减去MinPrice,则进行多头平仓(SP)。

当前价格低于等于30根K线最高价的EMA减去MinPrice是一个卖出信号,表示价格可能已经下破上升趋势的支撑位。因此,根据策略规则,持有多头仓位时,如果当前价格下破30根K线最高价的EMA减去MinPrice,则需要进行多头平仓(SP),以避免进一步损失。
  1. 如果满足卖出条件(SellSetup == 1),则计算上次满足卖出条件(SellSetup)距离当前BAR的数目,并判断是否超过EntryBar。如果超过EntryBar,则将MROSS置为0。

  1. 如果MROSS[1]不为0(说明之前已经满足卖出条件),MarketPosition为0(说明当前没有持仓),并且CurrentBar大于100,则进入空头系统入场的逻辑:

  • 如果当前价格低于等于空头触发价(SellTarget[1])且成交量大于0,则进行空头开仓(SK)。

  1. 如果当前持有空单(MarketPosition为-1)且BarsSinceEntry大于0,且成交量大于0,则进入空头系统出场的逻辑:

  • 如果当前价格高于等于30根K线最低价的EMA加上MinPrice,则进行空头平仓(BP)

当前价格高于等于30根K线最低价的EMA加上MinPrice时,意味着价格已经突破了该EMA的上限,这可能表明市场进入了一个新的趋势,其中空头力量已经开始减弱。因此,这是一个合适的时机来平仓空头头寸,以防止价格反弹并造成损失。
        MROBS = BarsLast(BuySetup); //上次满足买入条件距离当前BAR的数目 
        If(MROBS > EntryBar) //如果距离上次买入条件超过EntryBar
        {
            MROBS = 0;
        }
        //多头系统入场
        //满足买入条件后EntryBar内,且大于等于买入多头触发价,多单入场
        If(MROBS[1] <> 0 && MarketPosition == 0 && CurrentBar > 100)
        {
            If(High >= BuyTarget[1] && Vol > 0)
            {
                BK(DefaultVol);
            }
        }
        //多头系统出场
        //当持有多单且当前价格下破30根K线最高价的EMA,多单出场
        If(MarketPosition == 1 && BarsSinceEntry > 0 && Vol > 0)
        {
            If(Low <= UpperMA[1] - MinPrice)
            {
                SP(DefaultVol);
            }
        }
        If(SellSetup == 1) //如果满足卖出条件
        {
            SellTarget = Close - ChanSpread; //空头触发价
        }
        MROSS = BarsLast(SellSetup); //上次满足卖出条件距离当前BAR的数目
        If(MROSS > EntryBar) //距离上次卖出条件超过EntryBar
        {
            MROSS = 0;
        }
        //空头系统入场
        //满足卖出条件后EntryBar内,且小于等于空头触发价,空单入场 
        If(MROSS[1] <> 0 && MarketPosition == 0 && CurrentBar > 100)
        {
            If(Low <= SellTarget[1] && Vol > 0)
            {
                SK(DefaultVol);
            }
        }
        //空头系统出场
        //当持有空单且当前价格上破30根K线最低价的EMA,多单出场
        If(MarketPosition == -1 && BarsSinceEntry > 0 && Vol > 0)
        {
            If(High >= LowerMA[1] + MinPrice)
            {
                BP(DefaultVol);
            }
        }

4 策略简化

规则要素:

  1. 计算30根k线最高价和最低价的EMA价差;

  2. 计算14根k线的ADX.

入场条件:

  1.满足上30根K线的收盘价收于EMA30之上,且ADX向上的条件 在EntryBarBAR内该条件成立;

  2.当前价大于等于BuySetup,做多,当条件满足超过EntryBarBAR后,取消入场.

出场条件:

  1.当前价格下破30根K线最高价的EMA.

5 Q&A

  1. 为什么计算计算DMI指标所需的周期数是14而计算ADX(Average Directional Index)均线所需的周期数是30

DMI指标(Directional Movement Index)的计算是基于14个周期的数据,这是因为该指标主要测量价格趋势的强度和方向性,以帮助判断价格是否处于上涨趋势、下跌趋势或震荡市场。14个周期的数据通常可以提供足够的历史数据来反映当前的趋势方向和强度。

而ADX指标的计算则需要更长的时间跨度,一般为30个周期。这是因为ADX指标是基于DMI指标的三个线路进行计算的,包括+DI(上升方向指标)、-DI(下降方向指标)和ADX本身。ADX的计算需要将+DI和-DI线路的差异进行比较,并结合最近30个周期的数据来确定价格趋势的强度和方向性。因此,ADX需要更长的时间跨度来确保指标的稳定性和准确性。

  1. 为什么当前k线大于 DMI_N还要继续计算

在该程序中,如果当前K线的数量大于DMI_N,意味着已经有足够的数据进行DMI指标的计算。因此,程序将继续计算DMI指标并更新它们的值,以反映当前市场条件。具体地说,程序会利用已经计算出的前DMI_N根K线的值,以及最新的一根K线的价高、价低等数据,计算出当前的加向动向指数(+DMI)、减向动向指数(-DMI)和平均真实波幅(ATR)等指标的值。

在技术分析中,DMI指标是一种用于测量趋势强度和趋势方向的技术指标。由于DMI指标的计算需要利用前面DMI_N根K线的数据,因此在计算DMI指标时需要等待前DMI_N根K线的数据。一旦有足够的数据,程序就会开始计算DMI指标,并在之后的每根K线更新它们的值。因此,即使当前K线数量已经大于DMI_N,程序仍然需要继续计算DMI指标以反映当前市场的变化。

  1. 为什么当前k线大于DMI_N后的计算要用到SF

在代码中,SF代表一个系数,其值为1/DMI_N,用于计算移动平均值。在当前K线大于DMI_N的情况下,需要计算的是加权平均值。通过使用SF系数,可以为后续的DMI计算提供平滑的结果。

具体而言,当当前K线大于DMI_N时,程序需要将当前加权平均值设置为前一个加权平均值加上当前值与前一个加权平均值之差的SF倍数。这个过程被称为指数移动平均(EMA)。使用EMA可以让计算结果更加平滑,也可以在一定程度上减小数据的噪音。

  1. 为什么只有SVolty 大于 0,则计算 ODMIPlus 和 ODMIMinus。

在这段代码中,SVolty代表的是真实波动幅度的平均值。因为DMI的计算涉及到正向动向(PlusDM)和负向动向(MinusDM)的比较,如果SVolty为0或者负数,则表示该时期内价格波动幅度过小,无法区分正向和负向的动向,因此无法计算DMI值。如果SVolty大于0,则表示市场有足够的波动性,价格存在上涨和下跌的动向,此时才可以根据正向和负向动向的大小计算ODMIPlus和ODMIMinus,从而计算出SDMI和ADX值。

因为 SDMI 是 DMI 指标中的一个关键部分,它用来衡量多空双方力量的平衡程度。SDMI 的计算依赖于 ODMIPlus 和 ODMIMinus 的数值,而 ODMIPlus 和 ODMIMinus 的分母是 SVolty。如果 SVolty 为零,则 ODMIPlus 和 ODMIMinus 的值为零,此时计算 SDMI 将会出现除数为零的情况,因此需要先判断 SVolty 是否大于零,以避免出现除数为零的错误。只有 SVolty 大于零时,ODMIPlus 和 ODMIMinus 才有意义,才可以用来计算 SDMI。

  1. MROBS的变量作用

这个变量的作用是为了控制交易频率。如果该变量的值大于EntryBar(一个预设的距离值),就说明距离上一次买入条件的满足已经超过了预设值,此时将MROBS的值设为0,表示可以再次进行买入操作。如果MROBS的值小于或等于EntryBar,就不能再次进行买入操作,因为距离上次买入操作还没有达到预设值。这样做的目的是为了防止过于频繁地进行交易,从而减少交易的风险。

使用MROBS[1] <> 0是为了确保在当前BAR之前存在一根BAR满足买入条件,即已经形成了买入信号。如果没有这个判断条件,会出现在当前BAR形成买入信号后,下一根BAR又形成了买入信号,导致交易频率过高,这是不利于交易的。同时,使用MROBS[1] <> 0并结合EntryBar的限制,可以控制交易的频率和节奏,让交易更加有规律和可控。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值