有网站后台发布外链的平台有哪些
在阅读本文前,可查阅以往文章: 技术指标-ATR
技术指标有几个分支,其中趋势指标使用最广泛。这些工具可帮助交易者确定市场趋势的方向和强度,使他们能够相应地调整交易。如果趋势指标得到有效应用,它们通常会产生积极的结果。
在本文中,将仔细研究最突出的趋势指标之一——平均方向指数 (ADX)。首先,将对 ADX 有一个基本的了解,包括其用途和计算。之后,将从头开始开发指标,并在 Python 中基于它实施交易策略。为了评估策略的有效性,将使用 Apple 股票对其进行回溯测试,并将其回报与跟踪标准普尔 500 指数的 SPY ETF 的回报进行比较。
平均真实范围 (ATR)
在深入研究平均方向指数 (ADX) 之前,了解平均真实范围 (ATR) 至关重要,因为它在计算 ADX 中起着关键作用。
ATR 是一种技术指标,用于通过确定资产的平均波动量来衡量资产的波动性。它是一个滞后指标,这意味着它使用过去的价格数据来计算当前值,但无法预测未来价格。然而,这种滞后不一定是缺点,因为 ATR 旨在更准确地跟踪市场波动。此外,ATR 是非方向性的,这意味着它的走势独立于市场方向。要计算 ATR,请执行以下步骤:
- 计算真实范围 (TR):真实范围是三个值中的最大值:
- 市场最高价减去市场最低价
- 市场最高价减去前一收盘价
- 前一收盘价减去市场低点这可以表示为:
TR = MAX[ {HIGH - LOW}, {HIGH - P.CLOSE}, {P.CLOSE - LOW} ]
- 计算 ATR:计算 TR 后,ATR 是通过取指定周期数的 TR 值的平滑平均值来确定的。虽然最初的 ATR 计算涉及 Wilder Wiles(指标的创建者)的自定义平滑平均线,但也可以使用其他移动平均线,例如 SMA 或 EMA。为简单起见,本文将使用 14 周期 SMA,计算公式如下:
ATR 14 = SMA 14 [ TR ]
尽管 ATR 滞后,但它对于跟踪市场波动仍然非常有用。现在我们已经介绍了 ATR,让我们继续讨论本文的主要重点:平均方向指数 (ADX)
平均方向指数 (ADX)
ADX 是一种广泛使用的技术指标,用于衡量市场趋势的强度。虽然 ADX 没有指定趋势是看涨还是看跌,但它表明该趋势的强度。为了确定趋势方向,ADX 与正方向指数 (+DI) 和负方向指数 (-DI) 配对。顾名思义,+DI 衡量积极趋势(看涨),而 -DI 衡量消极趋势(看跌)。ADX 值通常介于 0 到 100 之间,使其成为振荡器。传统的 ADX 设置使用 14 周期回溯。
要使用 14 周期回溯计算 ADX,请执行以下步骤:
- 确定正向 (+DM) 和负向移动 (-DM):
-
+DM 计算为当前高点与前期高点之间的差值
-
-DM 计算为前一个低点与当前低点之间的差值
+DM = CURRENT HIGH - PREVIOUS HIGH-DM = PREVIOUS LOW - CURRENT LOW
-
计算 ATR:如上一节所述计算 14 周期 ATR。
-
计算 +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 ]
- 确定方向指数 (DI):方向指数的计算方法是将 +DI 和 -DI 之间的绝对差除以它们的总和,然后乘以 100。公式为:
DI 14 = | (+DI 14) - (-DI 14) | / | (+DI 14) + (-DI 14) | * 100
- 计算 ADX:ADX 是通过取当前 DI 值和先前 DI 值的加权平均值来计算的。具体来说,前一个 DI 乘以 13(回溯期减 1)并添加到当前 DI 中。公式为:
DX 14 = [ ( PREV DI 14 * 13 ) + DI 14 ] / 14
最后,使用 Wilder Wiles 设计的自定义移动平均线对 ADX 值进行平滑处理。此平滑过程可确保 ADX 值对于趋势分析是准确且一致的。
交易策略
在本文中,将构建一个简单的交叉策略。每当 ADX 线超过 25 且 +DI 线高于 -DI 线时,该策略就会发出买入信号。相反,当 ADX 线越过 25 以上,但 -DI 线高于 +DI 线时,会触发卖出信号。我们交易策略的逻辑可以表示为:
IF P.ADX < 25 AND C.ADX > 25 AND + DI LINE > - DI LINE ==> BUYIF P.ADX < 25 AND C.ADX > 25 AND + DI LINE < - DI LINE ==> SELL
将继续在 Python 中从头开始编写 ADX 指标,并基于该指标创建交易策略。然后,将使用 Apple 股票数据对其进行回溯测试,并将该策略的表现与 SPY ETF 进行比较,以评估我们的 ADX 交叉策略与基准相比的表现。让我们深入研究编码。
Python 中的实现
步骤 1:导入包
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
# Suppress warnings for cleaner output
import warnings
warnings.filterwarnings("ignore")plt.rcParams['figure.figsize'] = (20,10)
plt.style.use('fivethirtyeight')
第 2 步:API 密钥激活
api_key = '<YOUR API KEY>'
client = APIClient(api_key)
步骤 3:提取历史数据
# EXTRACTING HISTORICAL DATAdef 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 dfdef get_stock_data_csv(filepath):stock = pd.read_csv(filepath,parse_dates=['Date'])stock['Date'] = pd.to_datetime(stock['Date'], utc=True).dt.tz_localize(None)stock = stock.set_index('Date')return stockaapl = get_stock_data_csv('./data.csv')
spy = get_stock_data_csv('./SPY.csv')
print(aapl.tail())
print(spy.tail())
# aapl = get_historical_data('AAPL', '2020-01-01')
# aapl.tail()
Open High Low Close Volume \
Date
2024-12-11 05:00:00 247.960007 250.800003 246.259995 246.490005 45205800
2024-12-12 05:00:00 246.889999 248.740005 245.679993 247.960007 32777500
2024-12-13 05:00:00 247.820007 249.289993 246.240005 248.130005 33155300
2024-12-16 05:00:00 247.990005 251.380005 247.649994 251.039993 51694800
2024-12-17 05:00:00 250.080002 253.830002 249.779999 253.479996 51321700 DividendsStockSplits
Date
2024-12-11 05:00:00 0.0 0.0
2024-12-12 05:00:00 0.0 0.0
2024-12-13 05:00:00 0.0 0.0
2024-12-16 05:00:00 0.0 0.0
2024-12-17 05:00:00 0.0 0.0Close Open High Low Vol 涨跌幅
Date
2024-01-16 474.93 475.26 476.61 473.06 85.01M-0.37%
2024-01-12 476.68 477.84 478.60 475.23 58.03M 0.07%
2024-01-11 476.35 477.59 478.12 472.26 77.94M-0.04%
2024-01-10 476.56 474.16 477.45 473.87 67.31M 0.57%
2024-01-09 473.88 471.87 474.93 471.35 65.93M-0.15%
第 4 步:ADX 计算
计算平均方向指数 (ADX) 值
def get_adx(high, low, close, lookback):plus_dm = high.diff()minus_dm = low.diff()plus_dm[plus_dm < 0] = 0minus_dm[minus_dm > 0] = 0tr1 = 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)) * 100adx = ((dx.shift(1) * (lookback - 1)) + dx) / lookbackadx_smooth = adx.ewm(alpha = 1/lookback).mean()return plus_di, minus_di, adx_smoothaapl['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()
Open | High | Low | Close | Volume | Dividends | Stock Splits | plus_di | minus_di | adx | |
---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||
2024-12-11 05:00:00 | 247.960007 | 250.800003 | 246.259995 | 246.490005 | 45205800 | 0.0 | 0.0 | 39.240212 | 10.421796 | 26.914880 |
2024-12-12 05:00:00 | 246.889999 | 248.740005 | 245.679993 | 247.960007 | 32777500 | 0.0 | 0.0 | 37.541729 | 11.235419 | 29.116418 |
2024-12-13 05:00:00 | 247.820007 | 249.289993 | 246.240005 | 248.130005 | 33155300 | 0.0 | 0.0 | 35.755395 | 10.344918 | 30.894996 |
2024-12-16 05:00:00 | 247.990005 | 251.380005 | 247.649994 | 251.039993 | 51694800 | 0.0 | 0.0 | 37.541790 | 9.560514 | 32.647216 |
2024-12-17 05:00:00 | 250.080002 | 253.830002 | 249.779999 | 253.479996 | 51321700 | 0.0 | 0.0 | 38.999477 | 8.626999 | 34.580789 |
首先定义一个名为 get_adx 的函数,该函数采用四个参数:股票的最高价 (high)、最低价 (low) 和收盘价 (close) 价格,以及回溯期 (lookback),它决定了计算时要考虑的周期数。
- plus_dm 计算当前高点和前一高点之间的差值。任何负值都设置为零。
- minus_dm 计算前一个低点和当前低点之间的差值。任何正值都设置为零
- 真实范围 (TR)
- 我们计算三个价格差值:当前最高价减去最低价,绝对最高价减去前一收盘价,绝对最低价减去前一收盘价。
- 真实范围 (TR) 是每个时间段这三个差异中的最大值,我们将其存储在 tr 变量中。
- ATR 计算为指定回溯期内真实范围 (tr) 的滚动平均值。我们将此值存储在 atr 变量中。
- 方向指数 (DX)
- 方向指数 (DX) 衡量 +DI 和 -DI 之间的差异。
- DX 的计算方法是将 +DI 和 -DI 之间的绝对差除以它们的总和,然后乘以 100。DX 值存储在 dx 变量中。
- ADX 是使用平滑技术计算的。当前 DX 乘以回溯期,之前的 ADX 乘以上一期计数。此平滑的 ADX 存储在 adx_smooth 变量中。
第 5 步:ADX 图
将可视化计算出的 Apple (AAPL) 的 ADX 值,以更好地了解趋势强度和方向性运动。这里的主要目标是观察该图,以便更好地了解平均方向指数 (ADX) 以及正方向指标 (+DI) 和负方向指标 (-DI) 如何随时间变化。
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()
png
上图分为两个部分:顶部显示苹果的收盘价,而底部显示 ADX 指标的组成部分。在下方面板中,与 ADX 一起,您将看到一条设置为 25 级别的灰色虚线,用作阈值。如前所述,ADX 不衡量趋势方向,而是衡量趋势的强度。这在图表中很明显,因为 ADX 线在市场表现出强劲趋势(无论是向上还是向下)时上升,在市场盘整期间下跌。
这同样适用于方向索引 (+DI 和 -DI) 行。+DI 线往往在稳固的上升趋势中上升,在市场下跌时下降。相反,-DI 线在下降趋势中增加,在上升趋势中下降。
ADX 不仅可用于衡量趋势强度;它也是识别区间市场的绝佳工具(当一只股票在定义的高点和低点之间波动而没有明显的动量时)。当 +DI 和 -DI 线靠得更近时,市场通常是区间波动的。相反,这些线之间的差距越大,市场趋势的可能性就越大。对于那些不熟悉 ADX 图表的人来说,线条运动和市场方向之间的反比关系最初可能看起来令人困惑。
第 6 步:创建交易策略
首先定义函数 implement_adx_strategy,它采用四个参数:股票价格 (prices)、正方向指数 (pdi)、负方向指数 (ndi) 和平均方向指数 (adx)。
def implement_adx_strategy(prices, pdi, ndi, adx):buy_price = []sell_price = []adx_signal = []signal = 0for i in range(len(prices)):if adx[i-1] < 25and adx[i] > 25and pdi[i] > ndi[i]:if signal != 1:buy_price.append(prices[i])sell_price.append(np.nan)signal = 1adx_signal.append(signal)else:buy_price.append(np.nan)sell_price.append(np.nan)adx_signal.append(0)elif adx[i-1] < 25and adx[i] > 25and ndi[i] > pdi[i]:if signal != -1:buy_price.append(np.nan)sell_price.append(prices[i])signal = -1adx_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_signalbuy_price, sell_price, adx_signal = implement_adx_strategy(aapl['Close'], aapl['plus_di'], aapl['minus_di'], aapl['adx'])
第 7 步:绘制交易信号
将可视化生成的交易信号以及 Apple 的股票价格和 ADX 值。该图将帮助我们了解触发买入和卖出信号的点,以及市场趋势的整体强度。
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()
第 8 步:创建位置
将创建一个头寸列表,该列表根据 ADX 策略生成的买入和卖出信号指示我们是否持有股票(用 1 表示)或不持有(用 0 表示)。
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] = 1elif adx_signal[i] == -1:position[i] = 0else: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
Close | plus_di | minus_di | adx | adx_signal | adx_position | |
---|---|---|---|---|---|---|
Date | ||||||
2024-01-09 05:00:00 | 184.237411 | 13.867265 | 43.053447 | 51.275152 | 0 | 1 |
2024-01-10 05:00:00 | 185.282288 | 16.287091 | 37.208587 | 50.824545 | 0 | 1 |
2024-01-11 05:00:00 | 184.685211 | 16.551815 | 33.924799 | 46.506508 | 0 | 1 |
2024-01-12 05:00:00 | 185.013611 | 15.624095 | 32.023333 | 43.140585 | 0 | 1 |
2024-01-16 05:00:00 | 182.734787 | 13.298633 | 40.283191 | 41.391108 | 0 | 1 |
... | ... | ... | ... | ... | ... | ... |
2024-12-11 05:00:00 | 246.490005 | 39.240212 | 10.421796 | 26.914880 | 0 | 1 |
2024-12-12 05:00:00 | 247.960007 | 37.541729 | 11.235419 | 29.116418 | 0 | 1 |
2024-12-13 05:00:00 | 248.130005 | 35.755395 | 10.344918 | 30.894996 | 0 | 1 |
2024-12-16 05:00:00 | 251.039993 | 37.541790 | 9.560514 | 32.647216 | 0 | 1 |
2024-12-17 05:00:00 | 253.479996 | 38.999477 | 8.626999 | 34.580789 | 0 | 1 |
238 rows × 6 columns
第 9 步:回测
将使用 Apple (AAPL) 的历史股票数据为我们的平均方向指数 (ADX) 交易策略实施回溯测试流程。这将帮助我们评估我们的策略在选定时间段内的表现。让我们继续实施此过程。
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('Profit gained from the ADX strategy by investing $100k in AAPL : {}'.format(total_investment_ret))
print('Profit percentage of the ADX strategy : {}%'.format(profit_percentage))
Profit gained from the ADX strategy by investing $100k in AAPL : 29391.81
Profit percentage of the ADX strategy : 29%
第 10 步:SPY ETF 比较
将使用我们创建的“get_historical_data”函数检索 SPY ETF 数据,并将其回报与应用于 Apple 的平均方向指数策略的回报进行比较。
def get_benchmark(spy_holp, start_date, investment_value):#spy = get_historical_data('SPY', start_date)['Close']spy = spy_holp['Close']benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})investment_value = investment_valuebenchmark_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_dfbenchmark = get_benchmark(spy,'2024-01-09', 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(('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret)))
print(('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage)))
print(('ADX Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage)))
Benchmark profit by investing $100k : -23540.15
Benchmark Profit percentage : -24%
ADX Strategy profit is 53% higher than the Benchmark Profit
结论
在了解了理论和编码方面之后,我们对平均方向指数 (ADX) 以及如何使用 Python 构建基于 ADX 的基本交易策略有了深入的了解。
ADX 的真正潜力在于与其他技术指标(例如 RSI)相结合,以微调交易中的进入和退出点。因此,我鼓励您通过调整 ADX 策略,将其与其他指标集成,并对其进行广泛的回溯测试,从而进一步阅读本文。这样做有助于在实际交易场景中获得更精细的结果。