Skip to content

以 Python ADX 为匙,开启算法交易之旅

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

写在前面的话:本文主要介绍如何用 Python 里的平均方向指数(ADX)来进行算法交易,还给出了 Python 代码实现,展示了怎么从头开始算 ADX,以及根据 ADX 值和 +DI 与 -DI 的关系来制定交易策略。通过对苹果股票数据的回溯测试,评估了这个策略的有效性,还把它的收益和 SPY ETF 做了比较。

技术指标有多个分支,其中使用最广泛的是趋势指标。这些工具帮助交易者确定市场趋势的方向和强度,使他们能够据此调整自己的交易。只要应用得当,趋势指标通常会产生积极的结果。

在本文中,我们将仔细研究最著名的趋势指标之一–平均方向指数 (ADX)。首先,我们将建立对 ADX 的基本认识,包括其目的和计算方法。然后,我们将从头开始开发该指标,并用 Python 实现基于该指标的交易策略。为了评估我们策略的有效性,我们将使用苹果股票对其进行回溯测试,并将其收益与跟踪标准普尔 500 指数的 SPY ETF 的收益进行比较。


一、什么是 ATR ?

在深入研究平均方向指数(ADX)之前,有必要了解平均真实范围(ATR),因为它在计算 ADX 时起着关键作用。

ATR 是一个技术指标,通过确定资产的平均波动幅度来衡量资产的波动性。它是一个滞后指标,这意味着它使用过去的价格数据来计算当前值,但无法预测未来价格。不过,这种滞后并不一定是一个缺点,因为 ATR 的设计目的是更准确地跟踪市场波动。此外,ATR 是非方向性的,这意味着它的变动与市场方向无关。要计算 ATR,请遵循以下步骤:

  1. 计算真实范围 (TR):真实范围是三个值中的最大值:
  • 市场最高价减去市场最低价
  • 市场最高价减去前收盘价
  • 前收盘价减去市场最低价 可以表示为:TR = MAX[ {HIGH - LOW}, {HIGH - P.CLOSE}, {P.CLOSE - LOW} ]

Where:

  • MAX 表示最大值
  • HIGH 是市场的高价位
  • LOW  为市场最低价
  • P.CLOSE  是前一个市场的收盘价

2.计算 ATR:计算出 TR 值后,就可以利用指定周期内 TR 值的平滑平均值来确定 ATR。虽然最初的 ATR 计算涉及 Wilder Wiles(指标创建者)自定义的平滑平均值,但也可以使用其他移动平均线,如 SMA 或 EMA。为简单起见,本文将使用 14 期均线,计算公式如下: ATR 14 = SMA 14 [ TR ]

Where:

  • ATR 14 是 14 期平均真实范围
  • SMA 14 是 14 期简单移动平均线
  • TR 是真实范围

尽管 ATR 已经落后,但它在跟踪市场波动方面仍然非常有用。在介绍了 ATR 之后,让我们来谈谈本文的重点:平均方向指数 (ADX)。

二、什么是 ADX

ADX 是一种广泛使用的技术指标,用于衡量市场趋势的强弱。虽然 ADX 并不指明趋势是看涨还是看跌,但它能显示趋势的强度。为确定趋势方向,ADX 与正方向指数 (+DI) 和负方向指数 (-DI) 搭配使用。顾名思义,+DI 表示正向趋势(看涨),而 -DI 表示负向趋势(看跌)。ADX 值的范围通常在 0 到 100 之间,是一种震荡指标。传统的 ADX 设置使用 14 期回溯。

要使用 14 期回溯法计算 ADX,请按以下步骤操作:

1. 确定正方向运动(+DM)和负方向运动(-DM):

  • +DM的计算方法是当前高点与前一高点之差
  • -DM的计算方法是前一个低点与当前低点之间的差值:

+DM = CURRENT HIGH - PREVIOUS HIGH ( 当前高点 – 前一个高点)
-DM = PREVIOUS LOW - CURRENT LOW (前一个低点 – 当前低点)

2. 计算 ATR: 如上一节所述,计算 14 期 ATR。

3. 计算 +DI 和 -DI:计算 +DI 时,用 +DM 的 14 期 EMA 除以 14 期 ATR 再乘以 100。对于 -DI,则使用 -DM 的 14 期 EMA。公式如下

+DI 14 = 100 * [ EMA 14 ( +DM ) / ATR 14 ]
-DI 14 = 100 * [ EMA 14 ( -DM ) / ATR 14 ]


4. 确定方向指数(DI):方向指数的计算方法是用 +DI 和 -DI 的绝对差值除以它们的总和,然后乘以 100。计算公式为

DI 14 = | (+DI 14) - (-DI 14) | / | (+DI 14) + (-DI 14) | * 100

5.计算 ADX:ADX 是当前 DI 值和上一个 DI 值的加权平均值。具体来说,前一个 DI 值乘以 13(回溯期减去 1),然后与当前 DI 值相加。计算公式为

ADX 14 = [ ( PREV DI 14 * 13 ) + DI 14 ] / 14

最后,使用怀尔德-怀尔斯(Wilder Wiles)设计的定制移动平均线对 ADX 值进行平滑处理。这一平滑过程可确保 ADX 值的准确性和一致性,以便进行趋势分析。

既然我们已经介绍了 ADX 的计算方法,就可以探讨如何根据 ADX 指标制定交易策略了。


三、关于我们的交易策略

在本文中,我们将建立一个直接的交叉策略。只要 ADX 线在 25 以上交叉,且 +DI 线高于 -DI 线,该策略就会发出买入信号。反之,当 ADX 线在 25 以上交叉,但 -DI 线高于 +DI 线时,就会触发卖出信号。我们交易策略的逻辑可以表述为:

IF P.ADX < 25 AND C.ADX > 25 AND + DI LINE > - DI LINE ==> BUY
IF P.ADX < 25 AND C.ADX > 25 AND + DI LINE < - DI LINE ==> SELL

理论部分到此为止。现在,我们将用 Python 从头开始编码 ADX 指标,并基于该指标创建交易策略。然后,我们将使用苹果股票数据对其进行回溯测试,并将该策略的表现与 SPY ETF 进行比较,以评估我们的 ADX 交叉策略与基准的对比情况。让我们开始编码。

四、用 Python 实现

我们将把编码分成以下几个步骤:

  1. 导入软件包
  2. 激活 API 密钥
  3. 提取股票历史数据
  4. ADX 计算
  5. 绘制 ADX 指标图
  6. 实施交易策略
  7. 绘制买卖信号图
  8. 创建交易头寸
  9. 回溯测试
  10. 与 SPY ETF 回报率的比较

我们将按照这些步骤来指导我们的实施工作。

步骤 1:导入软件包

第一步是导入必要的 Python 软件包。我们将主要使用 eodhd 提取历史股票数据,使用 Pandas 进行数据操作,使用 NumPy 处理数组并执行数学运算,使用 Matplotlib 绘制图表。此外,我们还将使用 math 来进行各种计算,使用 termcolor 来设计输出样式(可选)。

# IMPORTING PACKAGES

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from termcolor import colored as cl
from math import floor
from eodhd import APIClient

plt.rcParams['figure.figsize'] = (20,10)
plt.style.use('fivethirtyeight')v

导入必要的软件包后,我们就可以使用 eodhd 库为苹果获取历史数据了。如果没有安装这些软件包,可以在终端运行 pip install 命令轻松安装。

步骤 2:激活 API 密钥

必须在软件包中注册 EODHD API 密钥,才能使用其功能。如果您没有 EODHD API 密钥,请首先访问其网站(https://eodhd.com/),然后完成注册过程以创建 EODHD 账户,最后导航到控制面板(https://eodhd.com/cp/dashboard),在那里您可以找到您的 EODHD API 密钥。重要的是要确保这个秘密 API 密钥不会泄露给任何人。您可以按照以下代码激活 API 密钥:

api_key = '<YOUR API KEY>'
client = APIClient(api_key)

代码非常简单。第一行,我们将 EODHD API 密钥存储在 api_key 中,第二行,我们使用 eodhd 软件包提供的 APIClient 类激活 API 密钥,并将响应存储在客户端变量中。

请注意,您需要将 替换为您的 EODHD API 密钥。除了直接用文本存储 API 密钥外,还有其他方法可以提高安全性,例如利用环境变量等。

步骤 3:提取历史数据

在提取数据之前,了解什么是历史数据或日终(EOD)数据非常重要。这些数据记录了过去的市场活动,对于识别趋势、模式和整体市场行为至关重要。历史数据对于回溯测试策略和分析资产表现至关重要。使用 eodhd 软件包,您可以通过运行以下代码轻松检索任何可交易资产的历史数据:

# EXTRACTING HISTORICAL DATA

def get_historical_data(ticker, start_date):
    json_resp = client.get_eod_historical_stock_market_data(symbol = ticker, period = 'd', from_date = start_date, order = 'a')
    df = pd.DataFrame(json_resp)
    df = df.set_index('date')
    df.index = pd.to_datetime(df.index)
    return df

aapl = get_historical_data('AAPL', '2020-01-01')
aapl.tail()

在这段代码中,我们使用 eodhd 软件包中的 get_eod_historical_stock_market_data 函数来提取苹果公司经拆分调整后的历史股票数据。让我们分解一下关键参数:

  • ticker:要获取数据的股票代码(在本例中,AAPL 表示 Apple)。
  • period: 定义每个数据点之间的时间间隔,设置为每日间隔(’d’)。
  • from_date: 指定数据提取的开始日期,格式为 “YYYY-MM-DD”。
  • order: 可选参数,用于根据日期以升序(’a’)或降序(’d’)排列数据。

提取数据后,需要应用一些基本的数据整理步骤,将其格式化为 Pandas DataFrame。得到的数据如下所示


步骤 4:ADX 计算

在这一步中,我们将采用前面讨论过的方法计算平均方向指数 (ADX) 值。

Python 实现:

def get_adx(high, low, close, lookback):
    plus_dm = high.diff()
    minus_dm = low.diff()
    plus_dm[plus_dm < 0] = 0
    minus_dm[minus_dm > 0] = 0
    
    tr1 = pd.DataFrame(high - low)
    tr2 = pd.DataFrame(abs(high - close.shift(1)))
    tr3 = pd.DataFrame(abs(low - close.shift(1)))
    frames = [tr1, tr2, tr3]
    tr = pd.concat(frames, axis = 1, join = 'inner').max(axis = 1)
    atr = tr.rolling(lookback).mean()
    
    plus_di = 100 * (plus_dm.ewm(alpha = 1/lookback).mean() / atr)
    minus_di = abs(100 * (minus_dm.ewm(alpha = 1/lookback).mean() / atr))
    dx = (abs(plus_di - minus_di) / abs(plus_di + minus_di)) * 100
    adx = ((dx.shift(1) * (lookback - 1)) + dx) / lookback
    adx_smooth = adx.ewm(alpha = 1/lookback).mean()
    return plus_di, minus_di, adx_smooth

aapl['plus_di'] = pd.DataFrame(get_adx(aapl['high'], aapl['low'], aapl['close'], 14)[0]).rename(columns = {0:'plus_di'})
aapl['minus_di'] = pd.DataFrame(get_adx(aapl['high'], aapl['low'], aapl['close'], 14)[1]).rename(columns = {0:'minus_di'})
aapl['adx'] = pd.DataFrame(get_adx(aapl['high'], aapl['low'], aapl['close'], 14)[2]).rename(columns = {0:'adx'})
aapl = aapl.dropna()
aapl.tail()

输出:

代码解释:

首先,我们要定义一个名为 get_adx 的函数,它需要四个参数:股票的最高价(high)、最低价(low)和收盘价(close),以及回溯周期(lookback),它决定了计算时要考虑的周期数。

逐步分解:

  1. 定向移动 (+DM and -DM) :
  • 函数的第一步是计算正方向移动(+DM)和负方向移动(-DM)。
  • plus_dm 计算当前最高点与前一个最高点之间的差值。任何负值都将设为零。
  • minus_dm  计算前一个低点与当前低点之间的差值。任何正值都将设为零。

2. 真实范围 (TR):

  • 我们计算三种价格差:当前最高价减去最低价,绝对最高价减去前收盘价,绝对最低价减去前收盘价。
  • 真实范围 (TR) 是每个时间段这三个差值中的最大值,我们将其存储在 tr 变量中。

3. 平均真实范围 (ATR):

  • ATR 是以指定回溯周期内真实范围 (tr) 的滚动平均值计算的。我们将此值存储在 atr 变量中。

4. 方向指示器 (+DI and -DI):

  • 利用计算出的 +DM、-DM 和 ATR,我们可以计算出正向指标 (+DI) 和负向指标 (-DI)。
  • 计算方法是将方向性变动的指数移动平均线 (EMA) 除以 ATR,再将结果乘以 100。这些值分别存储在 plus_di 和 minus_di 变量中。

5. 方向指数 (DX):

  • 方向指数 (DX) 衡量 +DI 和 -DI 之间的差值。
  • DX 的计算方法是将 +DI 和 -DI 的绝对差值除以它们的总和,再乘以 100。DX 值存储在 dx 变量中。

6. 平均方向指数 (ADX):

  • ADX 采用平滑技术计算。当前 DX 值乘以回溯周期,上一个 ADX 值乘以上一个周期数。平滑后的 ADX 保存在 adx_smooth 变量中。

7.返回结果:

  • 函数最后返回 +DI、-DI 和平滑 ADX 的计算值。

8.调用函数:

  • 我们使用苹果公司的股票数据 (AAPL) 和 14 期回溯调用该函数,以检索 +DI、-DI 和 ADX 的计算值,供进一步使用。

这种循序渐进的方法可确保准确计算趋势强度(ADX)和方向性变动(+DI、-DI),这对于构建基于趋势强度的交易策略至关重要。

步骤-5:ADX 图

在本步骤中,我们将直观显示计算得出的苹果公司 (AAPL) 的 ADX 值,以便更好地了解趋势强度和方向性运动。这里的主要目的是观察曲线图,以便更好地了解平均方向性指数(ADX)以及正方向性指标(+DI)和负方向性指标(-DI)随时间的变化情况。

Python 实现:

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 5, colspan = 1)
ax1.plot(aapl['close'], linewidth = 2, color = '#ff9800')
ax1.set_title('AAPL CLOSING PRICE')
ax2.plot(aapl['plus_di'], color = '#26a69a', label = '+ DI 14', linewidth = 3, alpha = 0.3)
ax2.plot(aapl['minus_di'], color = '#f44336', label = '- DI 14', linewidth = 3, alpha = 0.3)
ax2.plot(aapl['adx'], color = '#2196f3', label = 'ADX 14', linewidth = 3)
ax2.axhline(25, color = 'grey', linewidth = 2, linestyle = '--')
ax2.legend()
ax2.set_title('AAPL ADX 14')
plt.show()

输出:

上图分为两部分:上部分显示苹果的收盘价,下部分显示 ADX 指标的组成部分。在下部面板中,除了 ADX 指标外,您还会看到一条灰色虚线,其水平线为 25,这是一个阈值。如前所述,ADX 并不衡量趋势方向,而是衡量趋势的强度。这一点在图表中很明显,因为当市场呈现强劲趋势(无论是向上还是向下)时,ADX 线会上升,而在市场盘整期间,ADX 线会下降。

方向指数线(+DI 和 -DI)也是如此。+DI线往往在稳固的上升趋势中上升,而在市场下行时下降。相反,-DI 线在下跌趋势中上升,在上升趋势中下降。

ADX 不仅可以用来衡量趋势的强弱,还是识别区间市场(股票在确定的高位和低位之间反弹,没有明显的动能)的绝佳工具。当 +DI 线和 -DI 线靠拢时,市场通常处于区间震荡。相反,这两条线之间的差距越大,市场越有可能处于趋势之中。对于那些不熟悉 ADX 图表的人来说,这些线的移动与市场方向之间的反向关系最初可能会让人感到困惑。

步骤 6:创建我们的交易策略

在这一步中,我们使用 Python 实现平均方向指数 (ADX) 交易策略。

Python 实现:

我们首先定义函数 implement_adx_strategy,它需要四个参数:股票价格 (price)、正方向指数 (pdi)、负方向指数 (ndi) 和平均方向指数 (adx)。

def implement_adx_strategy(prices, pdi, ndi, adx):
    buy_price = []
    sell_price = []
    adx_signal = []
    signal = 0
    
    for i in range(len(prices)):
        if adx[i-1] < 25 and adx[i] > 25 and pdi[i] > ndi[i]:
            if signal != 1:
                buy_price.append(prices[i])
                sell_price.append(np.nan)
                signal = 1
                adx_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                adx_signal.append(0)
        elif adx[i-1] < 25 and adx[i] > 25 and ndi[i] > pdi[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(prices[i])
                signal = -1
                adx_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                adx_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            adx_signal.append(0)
            
    return buy_price, sell_price, adx_signal

buy_price, sell_price, adx_signal = implement_adx_strategy(aapl['close'], aapl['plus_di'], aapl['minus_di'], aapl['adx'])

代码解释:

  1. 初始化:我们首先初始化三个空列表:buy_price、sell_price 和 adx_signal。它们将根据生成的交易信号存储各自的值。
  2. 循环数据:然后,我们对价格数据的长度进行循环,根据涉及 ADX、+DI 和 -DI 的条件生成买入和卖出信号。
  3. 买入信号:当 ADX 穿过 25 以上(即从 25 以下移动到 25 以上)且 +DI 大于 -DI 时,买入信号就会触发。如果满足这一条件,股价将被添加到 buy_price 列表中,信号标记为 1。
  4. 卖出信号:当 ADX 穿过 25 以上,但 -DI 大于 +DI 时,就会产生卖出信号。在这种情况下,股票价格会附加到卖出价列表中,信号标记为-1。
  5. 无信号:如果两个条件都不满足,则买入价和卖出价列表均以 NaN 填充,不采取任何交易行动(信号为 0)。
  6. 返回值:处理数据后,函数返回生成的买入价、卖出价和 adx_signal 列表。
  7. 调用函数:然后使用苹果股票的历史数据调用该函数,并将结果存储在相应的变量(buy_price、sell_price、adx_signal)中。

通过这种基于 ADX 的交易策略,我们可以根据 ADX 和方向指数显示的趋势强度生成买入和卖出信号。下一步,我们可以将这些信号可视化,以便更好地了解它们在不同市场条件下的有效性。

步骤 7:绘制交易信号图

在这一步中,我们将可视化生成的交易信号以及苹果公司的股价和 ADX 值。该图将帮助我们了解买入和卖出信号的触发点,以及市场趋势的整体强度。

Python 实现:

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 5, colspan = 1)
ax1.plot(aapl['close'], linewidth = 3, color = '#ff9800', alpha = 0.6)
ax1.set_title('AAPL CLOSING PRICE')
ax1.plot(aapl.index, buy_price, marker = '^', color = '#26a69a', markersize = 14, linewidth = 0, label = 'BUY SIGNAL')
ax1.plot(aapl.index, sell_price, marker = 'v', color = '#f44336', markersize = 14, linewidth = 0, label = 'SELL SIGNAL')
ax2.plot(aapl['plus_di'], color = '#26a69a', label = '+ DI 14', linewidth = 3, alpha = 0.3)
ax2.plot(aapl['minus_di'], color = '#f44336', label = '- DI 14', linewidth = 3, alpha = 0.3)
ax2.plot(aapl['adx'], color = '#2196f3', label = 'ADX 14', linewidth = 3)
ax2.axhline(25, color = 'grey', linewidth = 2, linestyle = '--')
ax2.legend()
ax2.set_title('AAPL ADX 14')
plt.show()

输出:


代码解释:

我们正在绘制平均方向指数 (ADX) 的组成部分,以及我们的交易策略所产生的买入和卖出信号。通过该图,我们可以直观地查看该策略与 ADX、+DI 和 -DI 指标的关系。

  • Buy signals 当 ADX 穿过 25 以上,且 +DI 线高于 -DI 线时,就会出现买入信号(以绿色向上三角形表示)。这表明看涨趋势强劲。
  • Sell signals 当 ADX 穿过 25 以上,+DI 线低于-DI 线,表示强烈的看跌趋势时,就会出现卖出信号(用红色向下三角形表示)。

这种可视化方法有助于确认交易策略的准确性,因为它能清楚地识别 ADX 发出市场趋势信号的时刻,并采取相应的买入或卖出行动。

步骤 8:创建我们的位置

在这一步中,我们将根据 ADX 策略生成的买入和卖出信号,创建一个仓位列表,显示我们是否持有股票(用 1 表示)(用 0 表示)。

Python 实现:

position = []
for i in range(len(adx_signal)):
    if adx_signal[i] > 1:
        position.append(0)
    else:
        position.append(1)
        
for i in range(len(aapl['close'])):
    if adx_signal[i] == 1:
        position[i] = 1
    elif adx_signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]
        
close_price = aapl['close']
plus_di = aapl['plus_di']
minus_di = aapl['minus_di']
adx = aapl['adx']
adx_signal = pd.DataFrame(adx_signal).rename(columns = {0:'adx_signal'}).set_index(aapl.index)
position = pd.DataFrame(position).rename(columns = {0:'adx_position'}).set_index(aapl.index)

frames = [close_price, plus_di, minus_di, adx, adx_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy

输出:


代码解释:

我们首先创建一个名为 position 的空列表。该列表最终将显示我们是持有股票(1)还是未持有股票(0)。

  • 在第一个 for 循环中,我们使用占位符值初始化位置列表,以匹配 adx_signal 列表的长度。
  • 在第二个 for 循环中,我们遍历 adx_signal 列表中的值:
  • 如果信号显示 “买入”(1),我们就会在持仓列表中添加 1,表示我们持有该股票。
  • 如果信号显示 “卖出”(-1),我们就会在持仓列表中添加 0,表示我们没有持有该股票。
  • 如果信号保持中性(0),则位置与前一数值相同。

最后,我们执行一些数据处理,将仓位、adx_signal 和其他值(如收盘价、+DI、-DI 和 ADX 值)合并到一个名为 strategy 的单一数据框中。该数据帧可帮助我们跟踪仓位状态和交易信号。

从输出中可以看到,第一行的仓位仍为 1,表示我们持有该股票。但是,一旦 adx_signal 生成卖出信号(-1),仓位就会变为 0,表明股票已卖出。在信号再次变化之前,仓位将保持为 0。

现在,我们已经设置好了交易策略,准备进入回溯测试流程!

步骤-9:回溯测试


在深入了解代码之前,我们先来了解一下什么是回溯测试。回溯测试是交易员和分析师用来评估特定交易策略在历史数据中的表现的一种方法。通过将策略应用于过去的股价数据,我们可以观察到该策略的有效性,从而获得洞察力并相应地改进策略。

在这一步中,我们将使用苹果公司 (AAPL) 的历史股票数据,对我们的平均方向指数 (ADX) 交易策略实施回溯测试流程。这将帮助我们评估我们的策略在选定时间段内的表现。让我们继续实施这一过程。

Python 实现:

aapl_ret = pd.DataFrame(np.diff(aapl['close'])).rename(columns = {0:'returns'})
adx_strategy_ret = []

for i in range(len(aapl_ret)):
    returns = aapl_ret['returns'][i]*strategy['adx_position'][i]
    adx_strategy_ret.append(returns)
    
adx_strategy_ret_df = pd.DataFrame(adx_strategy_ret).rename(columns = {0:'adx_returns'})
investment_value = 100000
adx_investment_ret = []

for i in range(len(adx_strategy_ret_df['adx_returns'])):
    number_of_stocks = floor(investment_value/aapl['close'][i])
    returns = number_of_stocks*adx_strategy_ret_df['adx_returns'][i]
    adx_investment_ret.append(returns)

adx_investment_ret_df = pd.DataFrame(adx_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(adx_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the ADX strategy by investing $100k in AAPL : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the ADX strategy : {}%'.format(profit_percentage), attrs = ['bold']))

输出:

Profit gained from the ADX strategy by investing $100k in AAPL : 54719.46
Profit percentage of the ADX strategy : 54%

代码解释:

  1. 股票回报率计算:我们首先使用 NumPy 的 diff 函数计算苹果公司的股票回报率,并将结果存储在 aapl_ret 数据帧中。
  2. 策略收益计算:使用 for 循环遍历 aapl_ret 值,计算 ADX 交易策略产生的收益。这些收益被收集到 adx_strategy_ret 列表中,随后转换为数据帧并存储为 adx_strategy_ret_df。
  3. 投资设置:为了进行回溯测试,我们模拟在 ADX 策略中投资 100,000 美元。该金额保存在 investment_value 变量中。
  4. 股票数量计算:用投资额除以收盘价,即可计算出投资可购买的苹果股票数量。为确保输出为整数,我们使用了数学软件包中的 floor 函数,该函数与四舍五入函数不同,可以消除小数点后的数值。
  5. 投资回报计算:使用另一个 for 循环来计算这项投资的回报,然后将数据格式化并整理成相关结构。
  6. 最终收益输出:我们打印出 100,000 美元投资的总收益,显示出该策略在三年内产生了约 54,000 美元的利润。

接下来,我们将这些收益与跟踪标准普尔 500 指数的 SPY ETF 收益进行比较。

步骤-10:SPY ETF 对比

这一步是可选的,但强烈建议使用,因为它可以让我们深入了解我们的交易策略相对于基准(本例中为 SPY ETF)的表现如何。在此,我们将使用创建的 “get_historical_data “函数检索 SPY ETF 数据,并将其收益与应用于苹果的平均方向性指数策略的收益进行比较。

def get_benchmark(start_date, investment_value):
    spy = get_historical_data('SPY', start_date)['close']
    benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})
    
    investment_value = investment_value
    benchmark_investment_ret = []
    
    for i in range(len(benchmark['benchmark_returns'])):
        number_of_stocks = floor(investment_value/spy[i])
        returns = number_of_stocks*benchmark['benchmark_returns'][i]
        benchmark_investment_ret.append(returns)

    benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
    return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)

investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('ADX Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

输出:

Benchmark profit by investing $100k : 37538.96  
Benchmark Profit percentage : 37%  
ADX Strategy profit is 17% higher than the Benchmark Profit

代码说明:该代码与上一步回溯测试类似,但我们现在投资的不是苹果股票,而是 SPY ETF,没有应用任何特定的交易策略。从输出结果可以看出,我们的平均方向指数(ADX)交易策略的表现比 SPY ETF 高出 17%。这是一个积极的结果!

五、观点回顾

通过理论和编码两方面的学习,我们对平均方向指数 (ADX) 有了扎实的了解,并掌握了如何使用 Python 构建基于 ADX 的基本交易策略。

在我看来,当 ADX 与其他技术指标(如 RSI)相结合,对交易的进入和退出点进行微调时,ADX 的真正潜力就会显现出来。因此,我鼓励大家进一步学习本文,调整 ADX 策略,将其与其他指标相结合,并进行广泛的回溯测试。这样做有助于在实际交易场景中取得更精细的结果。

  • ADX 是一个衡量市场趋势强度的重要指标,它本身并不指示趋势的方向,而是通过与 +DI 和 -DI 的结合来判断市场是否处于强势趋势状态。
  • 平滑 ADX 值的计算对于确保交易策略的准确性至关重要,这通常通过使用 Wilder Wiles 设计的自定义移动平均线来实现。
  • 交易策略的制定基于 ADX 的值和 +DI 与 -DI 的关系,例如当 ADX 超过 25 且 +DI 高于 -DI 时生成买入信号,反之则生成卖出信号。
  • 回溯测试是评估交易策略有效性的关键步骤,通过对历史数据的分析,可以得出策略在过去的市场环境下的表现,并与基准进行比较。
  • 与 SPY ETF 的比较提供了一个衡量交易策略表现的参考点,这有助于交易员理解策略相对于市场平均水平的优势。
  • 文章鼓励读者进一步探索和优化 ADX 策略,例如通过结合其他技术指标(如 RSI)来提高交易信号的准确性。
  • 投资者和分析师应该注意,回溯测试并不能保证未来的表现,因此在实际交易中需要谨慎行事,并考虑进行更多的研究和分析。

感谢您阅读到最后。如果对文中的内容有任何疑问,请给我留言,必复。


文内容仅仅是技术探讨和学习,并不构成任何投资建议。

转发请注明原作者和出处。

Published inAI&Invest专栏

Be First to Comment

    发表回复