量化投资指标

TA-LIB-内置 150 量化指标计算器

1 常见缩写

  • Time lags:过去时间点的特征,有不同的时间窗口
  • MA(Moving Average):移动平均
  • EMA(Exponential moving average):指数移动平均
  • SMA(Simple Moving Average):简单移动平滑
# Moving Averages Code

# Load the necessary packages and modules
import pandas as pd
import pandas.io.data as web
import matplotlib.pyplot as plt

# Simple Moving Average 
def SMA(data, ndays): 
 SMA = pd.Series(pd.rolling_mean(data['Close'], ndays), name = 'SMA') 
 data = data.join(SMA) 
 return data

# Exponentially-weighted Moving Average 
def EWMA(data, ndays): 
 EMA = pd.Series(pd.ewma(data['Close'], span = ndays, min_periods = ndays - 1), 
 name = 'EWMA_' + str(ndays)) 
 data = data.join(EMA) 
 return data

# Retrieve the Nifty data from Yahoo finance:
data = web.DataReader('^NSEI',data_source='yahoo',start='1/1/2013', end='1/1/2016')
data = pd.DataFrame(data) 
close = data['Close']

# Compute the 50-day SMA for NIFTY
n = 50
SMA_NIFTY = SMA(data,n)
SMA_NIFTY = SMA_NIFTY.dropna()
SMA = SMA_NIFTY['SMA']

# Compute the 200-day EWMA for NIFTY
ew = 200
EWMA_NIFTY = EWMA(data,ew)
EWMA_NIFTY = EWMA_NIFTY.dropna()
EWMA = EWMA_NIFTY['EWMA_200']

# Plotting the NIFTY Price Series chart and Moving Averages below
plt.figure(figsize=(9,5))
plt.plot(data['Close'],lw=1, label='NSE Prices')
plt.plot(SMA,'g',lw=1, label='50-day SMA (green)')
plt.plot(EWMA,'r', lw=1, label='200-day EWMA (red)')
plt.legend(loc=2,prop={'size':11})
plt.grid(True)
plt.setp(plt.gca().get_xticklabels(), rotation=30)

2 基础指标

  • $(P_{open}^{t}-P_{open}^{t-1})/P_{open}^{t}$:开盘价波动率

  • $(P_{high}^{t-1}-P_{high}^{t-2})/P_{high}^{t-1}$:最高价波动率

  • $(P_{low}^{t-1}-P_{low}^{t-2})/P_{low}^{t-1}$:最低价波动率

  • $(V_{open}^{t}-V_{open}^{t-1})/V_{open}^{t}$:开盘量波动率

  • $(V_{high}^{t-1}-V_{high}^{t-2})/V_{high}^{t-1}$:最高量波动率

  • $(V_{low}^{t-1}-V_{low}^{t-2})/V_{low}^{t-1}$:最低量波动率

  • $PH_{\lambda}^{t}=\mathop{max}\limits_{t-\lambda\leq i\leq t-1}P_{high}^{i}$:近期最高价

  • $PL_{\lambda}^{t}=\mathop{min}\limits_{t-\lambda\leq i\leq t-1}P_{low}^{i}$:近期最低价

  • $VH_{\lambda}^{t}=\mathop{max}\limits_{t-\lambda\leq i\leq t-1}V_{high}^{i}$:近期最高交易量

  • $VL_{\lambda}^{t}=\mathop{min}\limits_{t-\lambda\leq i\leq t-1}V_{low}^{i}$:近期最低交易量

  • $\frac{(P_{open}^{(t)})-(P_{open}^{(t-1)})}{PH_{\lambda}^{t}-PL_{\lambda}^{t}}$:开盘价波动与近期价格最值波动占比

  • $\frac{(V_{open}^{(t)})-(V_{open}^{(t-1)})}{VH_{\lambda}^{t}-VL_{\lambda}^{t}}$:开盘量波动与近期交易量最值波动占比

3 常见经济指标

常见经济指数

  • Crude Oil(原油)
  • Gold Price (黄金价格)

4 常见股指

常见股指:

  • S&P500(标普500)
  • DJLA(道琼斯工业平均指数)
  • Nikki(日经平均指数)
  • FTSE(富时100指数)
  • SSE(上海证券交易所综合股价指数)
  • STI(新加坡海峽時報指數)
  • NASDAQ(纳斯达克综合指数)

5 常见汇率

货币流动比率 CNY USD 人民币/美元 JPY USD 日元/美元 Euro USD 欧元/美元 AUD USD 澳元/美元

6 MTM 动量指标

动量指标(Momentum,简称MTM),也叫ROC(Rate of Change),是一种测量涨跌速率的技术指标

MTM的计算是利用“恒速原则”来判断涨跌速率的高低;“恒速原则”是指涨势中的每一段时涨幅相等,跌势中的每一段时间跌幅也应一致。

计算方法:MTM = 当日收盘价 - N日前收盘价

应用示例:

  • MTM值上升,且MTM的10日移动平均值由负转正时,为买进讯号
  • MTM值下降,且MTM的10日移动平均值由正转负时,为卖出讯号

Python 程序实现:

def calculate_mtm(df: pd.DataFrame, N=12, M=6):
    """
    计算市场动量指标 (MTM) 及其移动平均线 (MAMTM)。
    参数:
    df (pd.DataFrame): 包含至少 'close' 列的 DataFrame,代表每日收盘价。
    N (int): 用于计算 MTM 的时间窗口大小,默认为12。
    M (int): 用于计算 MAMTM 的时间窗口大小,默认为6。
    返回:
    pd.DataFrame: 包含 MTM 和 MAMTM 值的 DataFrame。
    """

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算 MTM,即当前收盘价减去 N 个周期前的收盘价
    mtm = data['close'] - data['close'].shift(N)

    # 计算 MAMTM,即 MTM 的 M 周期移动平均线
    mamtm = mtm.rolling(M).mean()

    # 将计算出的 MTM 和 MAMTM 添加到 DataFrame
    data['mtm'] = mtm
    data['mamtm'] = mamtm

    # 返回包含所有计算出指标的 DataFrame
    return data

#MTM #ROC #动量指标 #Momentum

7 BOLL指标

布林带(英语:Bollinger Bands,略称:BBands)是由美国作家、金融分析师约翰·包宁杰(John Bollinger)在1980年代发明的技术分析工具,通过该工具,投资者可以看到金融工具或商品的价格如何随著时间而波动。该工具结合了移动平均和标准差的概念,其基本的型态是由三条轨道线组成的带状通道(中轨和上、下轨各一条)。“中轨”为股价的平均成本,“上轨”和“下轨”可分别视为股价的压力线和支撑线。

“布林带”的简单计算:

  • 中轨 = N时间段的简单移动平均线
  • 上轨 = 中轨 + K × N时间段的标准差
  • 下轨 = 中轨 − K × N时间段的标准差

一般情况下,设定N=20和K=2

Python 程序实现:

def boll(df, N=20, X=2, Y=2):
    '''
    计算给定数据集的布林带指标(Bollinger Bands)。
    参数:
    ------
    df : pandas.DataFrame
        包含日期('date')和收盘价('close')列的DataFrame。
    N : int, 可选
        移动平均线的周期数,用于计算布林带上中下轨。默认值为20。
    X : float, 可选
        上轨计算时使用的标准差乘数。默认值为2。
    Y : float, 可选
        下轨计算时使用的标准差乘数。默认值为2。在大多数情况下,X和Y相同。
    返回:
    ------
    sorted_df : pandas.DataFrame
        扩展了布林带上轨('upper')、中轨('boll')和下轨('lower')列的原始DataFrame。
    注意:
    ------
    函数首先将'date'列转换为datetime类型,然后对DataFrame按日期进行排序,
    确保计算移动平均和标准差时数据是按时间序列排列的。
    '''

    # 将'date'列转换为datetime类型
    df['date'] = pd.to_datetime(df['date'])

    # 按'date'列对DataFrame进行排序
    sorted_df = df.sort_values('date')

    # 计算布林带中轨,即N周期的收盘价移动平均值
    sorted_df['boll_mean'] = sorted_df['close'].rolling(window=N).mean()

    # 计算布林带上轨,即中轨加上X倍的标准差
    sorted_df['boll_upper'] = sorted_df['boll'] + X * sorted_df['close'].rolling(window=N).std()

    # 计算布林带下轨,即中轨减去Y倍的标准差
    sorted_df['boll_lower'] = sorted_df['boll'] - Y * sorted_df['close'].rolling(window=N).std()

    # 返回扩展后的DataFrame
    return sorted_df

#布林通道 #保力加通道 #包宁杰带状 #布历加通道 #布林带 #BOLL #BBands #压力线 #支撑线

8 CCI指标

CCI (Commodity Channel Index)

  • CCI可以用来确定超买和超卖水平;高于100可能意味着超买,而低于−100可能意味着超卖
  • 当证券处于超买/超卖水平时,证券价格很有可能出现回调,当然也有可能继续之前走势~
  • CCI和价格还可以构成背离信号,当价格处于较低位置而CCI相对偏高时,则出现看涨背离,意味着下跌势头减弱;当价格处于较高位置而CCI相对偏低时,则出现看跌背离,意味着上涨势头减弱。

计算方式: $$ TYP = (最高价 + 最低价 + 收盘价) / 3 $$ $$ CCI = (TYP - TYP的N日简单移动平均 ) / (0.015 \times TYP的N日平均偏差) $$

Python 程序实现:

def calculate_cci(df: pd.DataFrame, N=14):
    '''
    计算 Commodity Channel Index (CCI)。
    参数:
    df (pd.DataFrame): 包含至少 'high', 'low', 'close' 列的DataFrame,分别代表每日最高价、最低价和收盘价。
    N (int): 用于计算CCI的时间窗口大小,默认为14。
    返回:
    pd.DataFrame: 包含 CCI 值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算典型价格,它是每日最高价、最低价和收盘价的平均值
    typical_price = (data['high'] + data['low'] + data['close']) / 3

    # 计算典型价格的N期简单移动平均线 (SMA)
    typical_price_ma = typical_price.rolling(N).mean()

    # 计算平均偏差,这是典型价格与典型价格移动平均线之间的绝对偏差的平均值
    mean_deviation = typical_price.rolling(N).apply(
        lambda x: np.mean(np.abs(x - x.mean())), raw=True)

    # 计算 CCI
    # CCI = (典型价格 - 典型价格的移动平均) / (常数 * 平均偏差)
    # 常数通常为0.015
    cci = (typical_price - typical_price_ma) / (0.015 * mean_deviation)

    # 将计算出的 CCI 添加到 DataFrame
    data['cci'] = cci

    # 返回包含所有计算出指标的 DataFrame
    return data

#CCI

9 EMV指标

EMV (Ease of Movement)计算方法

  • EVM考虑到价格上涨或下跌的容易程度与证券交易量的关系。
  • 例如,低成交量下的价格上涨意味着价格上涨相对轻松,几乎没有卖出压力。
  • EVM值为正值意味着市场走高没什么压力,而负值则表明市场可能很容易下跌。
# Load the necessary packages and modules
import pandas as pd
import pandas.io.data as web
import matplotlib.pyplot as plt

# Ease of Movement 
def EVM(data, ndays): 
    dm = ((data['High'] + data['Low'])/2) - ((data['High'].shift(1) + data['Low'].shift(1))/2)
    br = (data['Volume'] / 100000000) / ((data['High'] - data['Low']))
    EVM = dm / br 
    EVM_MA = pd.Series(pd.rolling_mean(EVM, ndays), name = 'EVM') 
    data = data.join(EVM_MA) 
    return data 

# Retrieve the AAPL data from Yahoo finance:
data = web.DataReader('AAPL',data_source='yahoo',start='1/1/2015', end='1/1/2016')
data = pd.DataFrame(data)

# Compute the 14-day Ease of Movement for AAPL
n = 14
AAPL_EVM = EVM(data, n)
EVM = AAPL_EVM['EVM']

# Plotting the Price Series chart and the Ease Of Movement below
fig = plt.figure(figsize=(7,5))
ax = fig.add_subplot(2, 1, 1)
ax.set_xticklabels([])
plt.plot(data['Close'],lw=1)
plt.title('AAPL Price Chart')
plt.ylabel('Close Price')
plt.grid(True)
bx = fig.add_subplot(2, 1, 2)
plt.plot(EVM,'k',lw=0.75,linestyle='-',label='EVM(14)')
plt.legend(loc=2,prop={'size':9})
plt.ylabel('EVM values')
plt.grid(True)
plt.setp(plt.gca().get_xticklabels(), rotation=30)

#EMV

10 FI指数

FI(ForceIndex)计算方法

  • FI指数考虑了股票价格的走向、股价运动的程度和成交量,形成一个衡量买卖压力的振荡器。
  • 例如,价格的大幅上涨(由价格运动的程度给出)表明存在强大的买入压力。成交量大的情况下出现大幅下跌,表明抛售压力很大
################# Force Index ########################################################

# Load the necessary packages and modules
import pandas as pd
import pandas.io.data as web

# Force Index 
def ForceIndex(data, ndays): 
    FI = pd.Series(data['Close'].diff(ndays) * data['Volume'], name = 'ForceIndex') 
    data = data.join(FI) 
    return data


# Retrieve the Apple data from Yahoo finance:
data = web.DataReader('AAPL',data_source='yahoo',start='1/1/2010', end='1/1/2016')
data = pd.DataFrame(data)

# Compute the Force Index for Apple 
n = 1
AAPL_ForceIndex = ForceIndex(data,n)
print(AAPL_ForceIndex)

#FI #ForceIndex #强力指数

其他补充:https://school.stockcharts.com/doku.php?id=technical_indicators

11 夏普指数

$$Sharp = \frac{E[R_a-R_0]}{\sigma_a}$$

  • $R_a$表示资产A的收益率;而$R_0$表示无风险收益率
  • 夏普值代表投资者额外承受的每一单位风险所获得的额外收益

#夏普指数 #夏普比率 #夏普值 #SharpIndex #SharpRate

12 价格动量 PMI

PMI 是根据 5、10、20、30、40、50、60和75 天移动均线构建的衡量价格势头强弱的指标

  • 若 ETF 收盘价格大于所有价格均线,上涨势头明显,那么 PMI 取值为 100
  • 若 ETF 收盘价格小于所有价格均线,下跌趋势已经形成,那么 PMI 取值为-10
  • 若收盘价格处于价格均线之间,当收盘价格仅大于 75 日均线时,PMI 值为-100 加上25;
  • 当收盘价大于 75 日均线且大于60 日均线时,PMI 值为-100 加上50:
  • 当收盘价格大于 75 日、60 日、50 日均线时PMI值为-100 加上75
  • 以此。PMI 越大,未来股票价格持续上涨的势头越强

$$ \left.s_{i,t}= \left\{\begin{array}{ll}20&if::tclose>MA_{t,k} \\0,&other\end{array}\right.\right. \ \ \ \ where \ \mathrm{ k~= 5, 10, 20, 30, 40, 50, 60, 75} $$

$$PMI_{t}=\sum_{i=1}^{n}s_{i,t}-100\ \ \ where \ \ \ 0<\mathrm{i}<10,\mathrm{i}\in\mathbb{N}^{*} $$

其中$tclose$是指收盘价,$MA$是指移动平均线

13 拥挤度

拥挤度是指投资者对某个资产或投资策略的集中程度

  • 当有大量投资者涌向同一类资产时,就会导致该资产或投资策略的拥挤度增加
  • 拥挤度增加可能导致市场出现错误定价的情况,因为投资者会受到追涨杀跌的非理性分析驱动
  • 高拥挤度也可能导致流动性风险和操作风险增加,因为当投资者纷纷想要退出时,市场可能没有足够的买家来接手这些投资者的头寸,从而导致价格下跌
  • 对于投资者来说,了解拥挤度可以帮助他们更好地管理风险,避免盲目跟风

拥挤度为当前 ETF 份额在过去 60 个交易日的历史分位数,历史分位数越高,拥挤程度越价格下跌的可能性及程度越大。计算公式如下: $$\begin{aligned}z_i&=1-1_{yr} \\ \\w_i&=\frac{z_i}{\sum_{i=1}^nz_i}\end{aligned}$$

14 MACD

MACD (Moving Average Convergence Divergence)指标由一组曲线与图形组成,通过收盘时股价或指数的快变及慢变的指数移动平均值(EMA)之间的差计算出来。“快”指更短时段的 EMA,而“慢”则指较长时段的 EMA,最常用的是 12 日及 26 日的 EMA

Python 程序实现:

def calculate_MACD(df: pd.DataFrame, fast_ma=12, slow_ma=26, signal_ma=9) -> pd.DataFrame:
    """
    计算MACD指标(移动平均收敛/发散指标)

    参数:
    df (pd.DataFrame): 包含'trade_date'和'close'列的DataFrame
    fast_ma (int): 快速EMA的周期,默认为12
    slow_ma (int): 慢速EMA的周期,默认为26
    signal_ma (int): 信号线(DEA)的周期,默认为9

    返回:
    pd.DataFrame: 包含MACD指标计算结果的DataFrame
    """
    # 创建DataFrame副本以避免修改原始数据
    data = df.copy().sort_values('trade_date')
    close = data['close']

    # 计算快速和慢速EMA
    fast_ema = close.ewm(span=fast_ma, adjust=False).mean()
    slow_ema = close.ewm(span=slow_ma, adjust=False).mean()

    # 计算DIFF(DIF)线 = 快速EMA - 慢速EMA
    diff = fast_ema - slow_ema

    # 计算DEA(信号线) = DIFF的EMA
    dea = diff.ewm(span=signal_ma, adjust=False).mean()

    # 计算MACD柱 = 2 * (DIFF - DEA)
    macd = 2 * (diff - dea)

    # 将结果添加到DataFrame
    data[f'ema{fast_ma}'] = fast_ema
    data[f'ema{slow_ma}'] = slow_ema
    data[f'diff{fast_ma}'] = diff
    data[f'dea{slow_ma}'] = dea
    data[f'macd{signal_ma}'] = macd

    return data

15 RSI 相对强度指数

相对强度指数(Relative Strength Index)

  • 通过指定阈值生成市场看涨和看跌势头的信号
  • 如果 RSI 大于 70%,则资产超买;如果低于 30%,则资产不足

计算公式:

  • RS(相对强度)= N日内收盘价涨数和之均值÷N日内收盘价跌数和之均值
  • $RSI=100-\frac{100}{1+RS}$

Python 程序实现:

def calculate_rsi(df: pd.DataFrame, window: int = 14) -> pd.DataFrame:
    """
    计算给定DataFrame中股票的相对强弱指数(Relative Strength Index, RSI)。
    参数
    ----------
    df : pd.DataFrame
        包含股票价格信息的DataFrame,必须包含'date'和'close'两列。
    window : int, optional
        用于计算RSI的周期窗口长度,默认为14天。
    返回值
    -------
    data : pd.DataFrame
        原始DataFrame,附加了名为'rsi'的新列,该列包含了计算出的RSI值。
    """

    data = df.copy()

    # 确保'date'列是datetime类型
    data['date'] = pd.to_datetime(data['date'])

    # 计算每日收盘价变化
    delta = data['close'].diff()

    # 将变化量分为正收益(gain)和负收益(loss)
    gain = delta.clip(lower=0)
    loss = (-delta).clip(lower=0)

    # 计算平均收益和平均损失的EMA
    avg_gain = gain.ewm(com=window-1, min_periods=window).mean()
    avg_loss = loss.ewm(com=window-1, min_periods=window).mean()

    # 避免分母为零的情况,如果avg_loss为零,则设RS为极大值
    rs = avg_gain / avg_loss
    rs = rs.fillna(0)  # 将NaN替换为0
    rs[rs == float('inf')] = 1000000  # 将无穷大替换为极大值

    # 计算RSI
    rsi = 100 - (100 / (1 + rs))

    # 当avg_loss为零时,确保RSI为100
    rsi[avg_loss == 0] = 100

    # 将计算出的RSI值添加到DataFrame中
    data['rsi'] = rsi

    return data

16 LON 钱龙长线指标

钱龙长线指标,即 DIF 线(平滑因子为 9 的指数加权移动平均结果)与 DEA 线(平滑因子为 19 的指数加权移动平均结果)的差值

应用法则:

  1. 买入点判断:当在零轴下方,lon(白线)向上交叉均线时开始关注该股。直到 lon 穿过零轴绿色柱状线翻红方可作为中长线介入点。
  2. 卖出点判断:当在零轴上方,一旦 lon 线向下交叉均线形成死叉就应该立即卖出

Python 程序实现:

def calculate_lon(df: pd.DataFrame, N=10) -> pd.DataFrame:
    '''
    计算 LON 及其移动平均线 LONMA。
    参数:
    df (pd.DataFrame): 包含至少 'high', 'low', 'close', 'volume' 列的 DataFrame,
                       分别代表每日最高价、最低价、收盘价和成交量。
    N (int): 用于计算 LON 移动平均线 (LONMA) 的时间窗口大小,默认为10。
    返回:
    pd.DataFrame: 包含 LON 和 LONMA 值的 DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算前一天的收盘价
    lc = df['close'].shift(1)

    # 计算连续两天的最高价和最低价的差值,并将其转换为百分比形式
    a = (df['high'].rolling(2).max() - df['low'].rolling(2).min()) * 100

    # 计算成交量指标(Volume Indicator),将两天内的成交量总和除以价格差百分比
    vid = df['volume'].rolling(2).sum() / a

    # 计算价格变动量(Price Change),即当前收盘价与前一天收盘价的差值
    rc = (df['close'] - lc) * vid

    # 计算累积价格变动量
    long = rc.cumsum()

    # 使用指数加权移动平均(EWMA)计算 DIF 线,平滑因子为9
    dif = long.ewm(com=9, adjust=False).mean()

    # 使用指数加权移动平均(EWMA)计算 DEA 线,平滑因子为19
    dea = long.ewm(com=19, adjust=False).mean()

    # 计算 LON 指标,即 DIF 线与 DEA 线的差值
    lon = dif - dea

    # 计算 LON 指标的 N 周期简单移动平均线 (SMA)
    lonma = lon.rolling(N).mean()

    # 将计算出的 LON 和 LONMA 值添加到 DataFrame
    data['lon'] = lon
    data['lonma'] = lonma

    # 返回包含所有计算出指标的 DataFrame

17 KDJ 随机指标

KDJ (Stochastic Indicator) - 随机指标

  • 用于分析市场价格的波动和判断超买、超卖情况;KDJ由三条线组成,分别是K线、D线和J线
  • 这些线通过计算特定周期内的最高价、最低价和收盘价之间的关系来生成

Python 实现:

def calculate_kdj(df: pd.DataFrame, N=9, M1=3, M2=3):
    '''
    计算 KDJ 技术指标。
    参数:
    df (pd.DataFrame): 包含至少 'high', 'low', 'close' 列的 DataFrame,
                       分别代表每日最高价、最低价和收盘价。
    N (int): 用于计算 RSV 的时间窗口大小,默认为9。
    M1 (int): 用于计算 K 值的指数加权移动平均 (EWMA) 的平滑因子,默认为3。
    M2 (int): 用于计算 D 值的指数加权移动平均 (EWMA) 的平滑因子,默认为3。
    返回:
    pd.DataFrame: 包含 K, D, 和 J 值的 DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算 N 周期内的最低价 LLV 和最高价 HHV
    ln = data['low'].rolling(N, min_periods=1).min()
    hn = data['high'].rolling(N, min_periods=1).max()

    # 计算 RSV (Relative Strength Value)
    rsv = (data['close'] - ln) / (hn - ln) * 100

    # 计算 K 值,使用指数加权移动平均 (EWMA)
    k = rsv.ewm(alpha=1/M1, adjust=False).mean()

    # 计算 D 值,同样使用指数加权移动平均 (EWMA)
    d = k.ewm(alpha=1/M2, adjust=False).mean()

    # 计算 J 值
    j = 3 * k - 2 * d

    # 将计算出的 K, D, 和 J 值添加到 DataFrame
    data['k'] = k
    data['d'] = d
    data['j'] = j

    # 返回包含所有计算出指标的 DataFrame
    return data

18 SKDJ 慢速随机指标(推荐)

慢速KD指标,又称慢速随机指标(SKDJ),是随机指标的一种

  • KDJ指标是属于较快的随机波动,SKDJ指标则是属于较慢的随机波动
  • SKDJ指标由K、D两根曲线构成,而KDJ指标由K、D、J三根曲线构成
  • RSV = (Close - Lowest Low) / (Highest High - Lowest Low) * 100
  • K 曲线是 RSV 的指数加权移动平均,D 曲线是 RSV 的简单移动平均

SKDJ的应用示例:

  1. K、D值大于80时表明市场已经超买是卖出的信号
  2. K、D值小于20时表明市场已经超卖是买入的信号
  3. K在20左右向上交叉D时称为“金叉”是买入的信号
  4. K在80左右向下交叉D时称为“死叉”是卖出的信号

Python 实现:

def calculate_skdj(df: pd.DataFrame, N=9, M=3) -> pd.DataFrame:
    '''
    计算 Stochastic K-D (SKDJ) 指标。
    参数:
    df (pd.DataFrame): 包含至少 'high', 'low', 'close' 列的 DataFrame,
                       分别代表每日最高价、最低价和收盘价。
    N (int): 用于计算 RSV 的时间窗口大小,默认为9。
    M (int): 用于计算 K 和 D 值的指数加权移动平均 (EWMA) 和简单移动平均 (SMA) 的平滑因子,默认为3。
    返回:
    pd.DataFrame: 包含 Stochastic K (k) 和 Stochastic D (d) 的 DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 使用rolling方法计算N周期内的最低价
    ln = data['low'].rolling(N, min_periods=1).min()

    # 使用rolling方法计算N周期内的最高价
    hn = data['high'].rolling(N, min_periods=1).max()

    # 计算 RSV (Relative Strength Value)
    # 公式为:(Close - Lowest Low) / (Highest High - Lowest Low) * 100
    # 使用指数加权移动平均 (EWMA) 对 RSV 进行平滑处理
    rsv = ((data['close'] - ln) / (hn - ln) * 100).ewm(span=M, adjust=False).mean()

    # 计算 K 值,使用指数加权移动平均 (EWMA)
    k = rsv.ewm(span=M, adjust=False).mean()

    # 计算 D 值,使用简单移动平均 (SMA)
    d = k.rolling(M).mean()

    # 将计算出的 Stochastic K (k) 和 Stochastic D (d) 添加到 DataFrame
    data['k'] = k
    data['d'] = d

    # 返回包含所有计算出指标的 DataFrame
    return data

19 WR 威廉指数

_威廉指标_,Williams%R(%R)是技术分析中用于衡量动量的振荡指标;通过最低价和收盘价之间的关系(遇强则买,遇弱则卖),来判断股市的超买超卖现象,预测股价中短期的走势

$$ WR=\frac{H_{n}-C}{H_{n}-L_{n}}\times 100 $$

  • $H_{n}$ 是过去 $n$ 天内的最高价,$L_{n}$ 是过去 $n$ 天内的最低价,$C$ 是当前收盘价

Python 实现:

def calculate_wr(df: pd.DataFrame, N=10) -> pd.DataFrame:
    """
    计算威廉指标(Williams %R)并处理可能的除零错误.
    参数:
    ----------
    df : pd.DataFrame
        包含至少 'high', 'low' 和 'close' 列的DataFrame,分别表示每个周期的最高价、最低价和收盘价。
    N : int, 可选
        用于计算滚动窗口的周期数,默认值为10。
    返回值:
    -------
    data : pd.DataFrame
        原始DataFrame加上'Hn'(周期内最高价)、'Ln'(周期内最低价)和'WR'(威廉指标)三列。
    """

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 使用rolling方法计算N周期内的最高价
    data['Hn'] = data['high'].rolling(N).max()

    # 使用rolling方法计算N周期内的最低价
    data['Ln'] = data['low'].rolling(N).min()

    # 获取浮点数的最小正数值,以防分母为零
    epsilon = np.finfo(float).eps

    # 计算威廉指标(Williams %R)
    # 公式为:(Hn - close) / (Hn - Ln) * 100
    # 在分母中加入epsilon以避免除零错误
    data['WR'] = (data['Hn'] - data['close']) / (data['Hn'] - data['Ln'] + epsilon) * 100

    # 返回包含计算结果的新DataFrame
    return data

20 LWR 慢速威廉指标

LWR (Lateral William's %R Indicator) - 慢速威廉指标

  • 技术分析中常用的一种超买超卖指标,也常被称为KD指标的补数
  • 计算公式为:LWR1 = 100 - K,LWR2 = 100 - D,其中K和D分别是KD指标的K线和D线
  • 由两条曲线LWR1和LWR2构成,通过判断两条线之间的交叉来预测价格的走势

Python 实现:

def calculate_lwr(df: pd.DataFrame, N=9, M1=3, M2=3) -> pd.DataFrame:
    '''
    计算 Williams %R 指标及其两条移动平均线 LWR1 和 LWR2。
    参数:
    df (pd.DataFrame): 包含至少 'high', 'low', 'close' 列的 DataFrame,
                       分别代表每日最高价、最低价和收盘价。
    N (int): 用于计算 Williams %R 的时间窗口大小,默认为9。
    M1 (int): 用于计算 LWR1 的指数加权移动平均 (EWMA) 的平滑因子,默认为3。
    M2 (int): 用于计算 LWR2 的指数加权移动平均 (EWMA) 的平滑因子,默认为3。
    返回:
    pd.DataFrame: 包含 Williams %R (rsv) 和两条移动平均线 LWR1 和 LWR2 的 DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 使用rolling方法计算N周期内的最高价
    hn = data['high'].rolling(N, min_periods=1).max()

    # 使用rolling方法计算N周期内的最低价
    ln = data['low'].rolling(N, min_periods=1).min()

    # 计算 Williams %R 指标
    # 公式为:(Hn - close) / (Hn - Ln) * 100
    # 在分母中加入一个小数值 (epsilon) 以避免除零错误
    epsilon = 1e-10  # 很小的一个数,防止除以零
    rsv = (hn - data['close']) / (hn - ln + epsilon) * 100

    # 计算 LWR1,使用指数加权移动平均 (EWMA)
    lwr1 = rsv.ewm(alpha=1/M1, adjust=False).mean()

    # 计算 LWR2,同样使用指数加权移动平均 (EWMA)
    lwr2 = lwr1.ewm(alpha=1/M2, adjust=False).mean()

    # 将计算出的 Williams %R (rsv) 和两条移动平均线 LWR1 和 LWR2 添加到 DataFrame
    data['rsv'] = rsv
    data['lwr1'] = lwr1
    data['lwr2'] = lwr2

    # 返回包含所有计算出指标的 DataFrame
    return data

21 BIAS 乖离率

乖离率,代表当日股票收盘价或盘中市价与移动平均线的差距,以分析股价偏离某时期平均价的程度。乖离率可分为正乖离率与负乖离率,若股价在移动平均线之上,称为正乖离率;股价在移动平均线之下,则为负乖离率。乖离率可视作某时期的平均报酬率

计算方式: $$ BIAS=\frac{当日收盘价-N日平均收盘价}{N日平均收盘价}\times 100% $$

N一般取6、12、24

Python 实现:

def calculate_bias(df: pd.DataFrame, N=6) -> pd.DataFrame:
    '''
    计算给定DataFrame中股票价格的N周期乖离率。
    Parameters
    ----------
    df : pd.DataFrame
        包含股票价格数据的DataFrame,至少需要包含'close'列作为收盘价。
    N : int, optional
        用于计算移动平均线的周期数,默认为6。
    Returns
    -------
    data : pd.DataFrame
        DataFrame对象,新增了两列:'ma'表示N周期移动平均线,
        'bias'表示N周期乖离率。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 使用rolling方法计算N周期内的平均价
    # rolling(N)创建一个窗口,对窗口内数据进行操作;mean()计算均值
    data['ma'] = data['close'].rolling(N).mean()

    # 计算乖离率:(收盘价 - 移动平均线) / 移动平均线 * 100%
    # 这个计算结果是一个百分比,表示收盘价相对于移动平均线的偏离程度
    data['bias'] = (data['close'] - data['ma']) / data['ma'] * 100

    return data

22 BBI 多空指数

BBI (Bull And Bear lndex) - 多空指标,又称熊牛指数,将不同日数移动平均线加权平均之后的综合指标;目的是为了更准确地判断市场价格的走势,并解决中短期移动平均线参数选择的争议问题

BBI = (3日均价 + 6日均价 + 12日均价 + 24日均价) / 4

Python 实现:

def calculate_bbi(df: pd.DataFrame, N1=3, N2=6, N3=12, N4=24) -> pd.DataFrame:
    '''
    计算Bull and Bear Index (BBI),一种技术分析指标,用于识别市场趋势。
    参数:
    df (pd.DataFrame): 包含至少'close'列的DataFrame,代表收盘价。
    N1, N2, N3, N4 (int): 分别代表四个不同周期的简单移动平均线(SMA)的窗口大小。
    返回:
    pd.DataFrame: 包含BBI值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算四个不同周期的简单移动平均线
    data['ma_' + str(N1)] = data['close'].rolling(N1).mean()
    data['ma_' + str(N2)] = data['close'].rolling(N2).mean()
    data['ma_' + str(N3)] = data['close'].rolling(N3).mean()
    data['ma_' + str(N4)] = data['close'].rolling(N4).mean()

    # 计算BBI,即这四个SMA的均值
    data['bbi'] = (data['ma_' + str(N1)] + data['ma_' + str(N2)] +
                   data['ma_' + str(N3)] + data['ma_' + str(N4)]) / 4

    # 返回包含BBI指标的新DataFrame
    return data

23 ENE 轨道线

ENE(Envelope) - 轨道线, yongyu 股价趋势轨道运行的判断

  • 上升趋势中,股价一般在中轨和上轨之间运行,股价升至上轨时,有很大几率回调至中轨,跌至中轨时又有很大几率回升至上轨
  • 下跌趋势中,股价一般在中轨和下轨之间运行,股价跌到下轨时,有很大几率回升至中轨,而回到中轨又有很大几率会跌到下轨

计算方式:

  • UPPER=(1+M1/100) x 收盘价的N日简单移动平均
  • LOWER=(1-M2/100) x 收盘价的N日简单移动平均
  • ENE=(UPPER+LOWER)/2

Python 实现:

def calculate_ene(df: pd.DataFrame, N=10, X=11, Y=9) -> pd.DataFrame:
    '''
    计算给定DataFrame中股票价格的信封指标。
    参数
    ----------
    df : pd.DataFrame
        包含至少一个名为'close'的列,代表收盘价的DataFrame。
    N : int, 可选
        用于计算移动平均线的周期数。默认值是10。
    X : float, 可选
        移动平均线上方的百分比,用于绘制上信封线。默认值是11。
    Y : float, 可选
        移动平均线下方的百分比,用于绘制下信封线。默认值是9。
    返回
    -------
    data : pd.DataFrame
        原始DataFrame加上额外的列:移动平均线('ma'),上信封线('upper'),下信封线('lower')和信封指标('ene')。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 使用rolling方法计算N周期内的平均价
    # rolling(N)创建一个窗口,对窗口内数据进行操作;mean()计算均值
    data['ma'] = data['close'].rolling(N).mean()

    # 计算上信封线,即移动平均线乘以上方的百分比
    data['upper'] = (1 + X / 100) * data['ma']

    # 计算下信封线,即移动平均线乘以下方的百分比
    data['lower'] = (1 - Y / 100) * data['ma']

    # 计算信封指标,即上信封线和下信封线的平均值
    data['ene'] = (data['upper'] + data['lower']) / 2

    # 返回包含信封指标的新DataFrame
    return data

24 BRAR 情绪指数

BRAR (或 ARBR) 是情绪指标,由人气指标 (AR) 和意愿指标 (BR) 构成

  • BRAR 反映市场多空双方力量对比,推断市场情绪,从而对趋势形成与反转作出预判
  • AR 通过比较一段周期内的开盘价在该周期价格中的高低,来衡量市场交易人气
  • BR 通过比较一段周期内的收盘价在该周期价格波动中的地位,来衡量市场买卖意愿程度
  • BRAR 指标基于“反市场心理”,当市场人气狂热时,考虑卖出,当市场人气悲观时,考虑买入

Python 实现:

def calculate_brar(df: pd.DataFrame, N=26) -> pd.DataFrame:
    '''
    计算AR (人气线) 和 BR (买卖意愿比率), 技术分析指标,用于评估市场情绪和力量。
    参数:
    df (pd.DataFrame): 包含至少'high', 'low', 'open', 'close'列的DataFrame,
                       分别代表最高价、最低价、开盘价和收盘价。
    N (int): 用于计算AR和BR的周期数,通常为26。
    返回:
    pd.DataFrame: 包含AR和BR值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算AR (人气线)
    # AR计算公式:((HIGH - OPEN) / (OPEN - LOW)) * 100 的N期总和
    ar = ((data['high'] - data['open']).rolling(N, min_periods=1).sum() /
          (data['open'] - data['low']).rolling(N, min_periods=1).sum()) * 100
    data['ar'] = ar

    # 计算BR (买卖意愿比率)
    # 首先,计算(HIGH - REF(CLOSE, 1)),如果小于0则设为0
    high_ref_close = data['high'] - data['close'].shift(1)
    high_ref_close = high_ref_close.where(high_ref_close > 0, 0)

    # 然后,计算(REF(CLOSE, 1) - LOW),如果小于0则设为0
    ref_close_low = data['close'].shift(1) - data['low']
    ref_close_low = ref_close_low.where(ref_close_low > 0, 0)

    # 计算上面两个值的N期滚动求和
    sum_high_ref_close = high_ref_close.rolling(window=N, min_periods=1).sum()
    sum_ref_close_low = ref_close_low.rolling(window=N, min_periods=1).sum()

    # 最后,计算BR:(SUM(HIGH - REF(CLOSE, 1))) / (SUM(REF(CLOSE, 1) - LOW)) * 100
    br = sum_high_ref_close / sum_ref_close_low * 100

    data['br'] = br

    # 返回包含AR和BR指标的新DataFrame
    return data

25 OBV 能量潮

能量潮指标(OBV)是一个用于衡量买卖压力的技术分析指标

  • OBV 是一个累积指标,用于跟踪价格上升和下跌的日子
  • 当价格上涨时,当天的成交量被添加到累积OBV总数中
  • 而当价格下跌时,当天的交易量将从OBV总数中减去

应用示例:

  • 当股价创下新高时,但OBV指标未跟随创出新高,显示价格与指标之间存在背离
  • 当股价创下新低时,但OBV指标未跟随创出新低,同样代表价格与指标之间存在背离
  • 当OBV指标与价格走势同步时,表示市场趋势可能持续
  • OBV指标急速变化可能预示著市场情绪或资金流入流出的变化
  • 累积OBV指标可能显示市场资金流入的增加,暗示著市场可能持续向上

Python 实现:

def calculate_obv(df: pd.DataFrame, N=10) -> pd.DataFrame:
    '''
    计算On-Balance Volume (OBV) 和 OBV的N期移动平均线。
    参数:
    df (pd.DataFrame): 包含至少'close'和'volume'列的DataFrame,分别代表收盘价和成交量。
    N (int): 移动平均线的周期,默认值为10。
    返回:
    pd.DataFrame: 包含'obv'和'obv_ma'列的DataFrame,表示OBV值和OBV的移动平均线。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 定义一个函数来根据价格变化计算成交量信号
    def volume_signal(diff, volume):
        if diff > 0:   # 如果当日收盘价高于前一日,则成交量视为正值
            return volume
        elif diff < 0: # 如果当日收盘价低于前一日,则成交量视为负值
            return -volume
        else:          # 如果收盘价没有变化,则成交量视为0
            return 0

    # 计算收盘价的变化差额
    data['diff'] = data['close'].diff()

    # 应用volume_signal函数到'diff'和'volume'列上,并将结果保存在'volume_signal'列中
    data['volume_signal'] = data.apply(
        lambda x: volume_signal(x['diff'], x['volume']), axis=1)

    # 计算累积成交量(OBV)
    data['obv'] = data['volume_signal'].cumsum() / 10000  # 除以10000通常是为了缩放数据,使其更易读

    # 计算OBV的N期移动平均线
    data['obv_ma'] = data['obv'].rolling(N).mean()

    # 返回包含OBV和OBV移动平均线的新DataFrame
    return data

26 PSY 心理线

心理线(Psychological Line)是一种人气指标

  • 利用一段时间内大盘指数收盘价涨跌天数的比率,藉以观察投资人心理趋向,以作为买卖参考
  • 75以上是超买区;25以下为超卖区;10以下为严重超卖区;90以上表示股市呈现严重超买现象

计算方式:PSY=N 日内的上涨天数/N×100%

Python 实现:

def calculate_psy(df: pd.DataFrame, N=12, M=6):
    '''
    计算心理线指标 PSY 和其移动平均线 PSYMA。
    参数:
    df (pd.DataFrame): 包含至少 'close' 列的DataFrame,代表每日收盘价。
    N (int): 用于计算心理线指标的时间窗口大小,默认为12。
    M (int): 用于计算 PSYMA 的时间窗口大小,默认为6。
    返回:
    pd.DataFrame: 包含 PSY 和 PSYMA 值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 判断每日收盘价是否比前一日上涨,结果为布尔型序列
    up_days = data['close'] > data['close'].shift(1)

    # 计算过去 N 天内收盘价上涨的天数
    # shift(1) 用于获取前一日的收盘价,rolling(window=N) 创建一个滑动窗口
    # sum() 计算窗口内的上涨天数
    up_days_in_period = up_days.rolling(window=N, min_periods=1).sum()

    # 计算 PSY,即过去 N 天内上涨天数占总天数的百分比
    psy = (up_days_in_period / N) * 100

    # 计算 PSY 的移动平均线 PSYMA
    psy_ma = psy.rolling(window=M).mean()

    # 将计算出的 PSY 和 PSYMA 添加到 DataFrame
    data['psy'] = psy
    data['mapsy'] = psy_ma

    # 返回包含所有计算出指标的 DataFrame
    return data

27 VR 成交量变异率

VR 指标,即成交量变异率,是一种衡量股价热度的中短期技术指标

  • 通过比较一定周期内价格上升日成交量与下降日成交量的比值,从而掌握市场买卖气势的趋向
  • 在底部区域,VR 值通常低于40%,而在多头行情中,VR 值可能会超过260%.

计算公式: $$VR = (AVS+1/2CVS) / (BVS+1/2CVS) \times 100$$

  • AV = 股价上升日成交量;AVS = N日内AV求和
  • BV = 股价下跌日成交量;BVS = N日内BV求和
  • CV = 股价平盘日成交量;CVS = N日内CV求和

Python 实现:

def calculate_vr(df: pd.DataFrame, N=26, M=6):
    '''
    计算Volume Ratio (VR) 指标和其M天移动平均线 MAVR。
    参数:
    df (pd.DataFrame): 包含至少'close'和'volume'列的DataFrame,分别代表收盘价和成交量。
    N (int): 用于计算VR指标的时间窗口大小,默认为26。
    M (int): 用于计算MAVR的时间窗口大小,默认为6。
    返回:
    pd.DataFrame: 包含VR和MAVR值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算每日收盘价的变化
    data['diff'] = data['close'].diff()

    # 根据收盘价的变化,将成交量分为三类:
    # av (Accumulation Volume) - 上涨时的成交量
    # dv (Distribution Volume) - 下跌时的成交量
    # uv (Unchanged Volume) - 收盘价不变时的成交量
    data['av'] = np.where(data['diff'] > 0, data['volume'], 0)
    data['dv'] = np.where(data['diff'] < 0, abs(data['volume']), 0)
    data['uv'] = np.where(data['diff'] == 0, data['volume'], 0)

    # 计算过去N天的AV, DV, UV的滚动求和
    avs = data['av'].rolling(N, min_periods=M).sum()
    dvs = data['dv'].rolling(N, min_periods=M).sum()
    uvs = data['uv'].rolling(N, min_periods=M).sum()

    # 计算VR指标
    # VR = (AV_sum + 0.5 * UV_sum) / (DV_sum + 0.5 * UV_sum) * 100
    vr = (avs + 0.5 * uvs) / (dvs + 0.5 * uvs) * 100
    data['vr'] = vr

    # 计算VR指标的M天移动平均线 MAVR
    data['mavr'] = data['vr'].rolling(M).mean()

    # 返回包含VR和MAVR指标的DataFrame
    return data

28 TRIX 三重指数均值

三重平滑均线(TRIX)指标在技术分析中用作动量震荡指标

  • TRIX 由三重指数平滑移动平均线的变化率组成, 其产生的关键信号是发散和信号线交叉
  • 当该值为正时,动量上升;而当值为负时,动量下降(存在平滑均值固有的滞后性)

计算过程:

  1. 单一平滑EMA = 18个周期收盘价EMA
  2. 双重平滑EMA =单一平滑EMA的18个周期EMA
  3. 三重平滑EMA =双重平滑EMA的18个周期EMAx
  4. TRIX =三倍平滑EMA的1个周期百分比变化

Python 实现:

def calculate_trix(df: pd.DataFrame, N=12, M=9):
    '''
    计算TRIX指标及其M天的移动平均线(MATRIX)。
    参数:
    df (pd.DataFrame): 包含至少'close'列的DataFrame,代表收盘价。
    N (int): 第一阶段指数移动平均线的周期数,通常为12。
    M (int): 用于计算TRIX的移动平均线的周期数,通常为9。
    返回:
    pd.DataFrame: 包含TRIX和MATRIX值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算N天的收盘价的三次指数移动平均 (Trix EMA)
    # 这里使用Pandas的ewm方法,参数span=N表示我们正在计算N天的EMA
    ema1 = data['close'].ewm(span=N, adjust=False).mean()  # 第一次EMA
    ema2 = ema1.ewm(span=N, adjust=False).mean()           # 第二次EMA
    ema3 = ema2.ewm(span=N, adjust=False).mean()           # 第三次EMA

    # 计算TRIX
    # TRIX = ((今日EMA3 - 昨日EMA3) / 昨日EMA3) * 100
    # 这里使用diff()来计算差值,shift(1)来获取前一天的数据
    trix = (ema3.diff() / ema3.shift(1)) * 100
    data['trix'] = trix

    # 计算TRIX的M天移动平均线(MATRIX)
    matrix = data['trix'].rolling(M).mean()
    data['matrix'] = matrix

    # 返回包含TRIX和MATRIX指标的新DataFrame
    return data

29 DPO 区间震荡线

DPO(Detrended price Oscillato)指标又名区间震荡线,是一个排除“长期价格趋势“的震荡指标;DPO 通过扣除前期移动平均价来消除长期趋势对价格波动的干扰,从而便于发现价格短期的波动和超买、超卖水平

应用示例:

  • DPO>0,表明处于多头市场;DPO<0,表明处于空头市场
  • DPO由下向上穿越MADPO时,形成金叉,被视为买入信号
  • DPO由上向下穿越MADPO时,形成死叉,被视为卖出信号
  • 价格创新低,而MADPO未创新低,属底背离,是买入信号
  • 价格创新高,而MADPO未创新高,属顶背离,是卖出信号

计算方式:DPO = 收盘价 - N 日收盘价简单移动平均的第 (N/2 + 1) 日价格

Python 实现:

def calculate_dpo(df: pd.DataFrame, N=20, M=6):
    '''
    计算 Detrended Price Oscillator (DPO) 和其移动平均线 (DPO MA)。
    参数:
    df (pd.DataFrame): 包含至少 'close' 列的DataFrame,代表每日收盘价。
    N (int): 用于计算DPO的简单移动平均线 (SMA) 的时间窗口大小,默认为20。
    M (int): 用于计算DPO MA的时间窗口大小,默认为6。
    返回:
    pd.DataFrame: 包含 DPO 和 DPO MA 值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算 N 天的简单移动平均线 (SMA)
    ma = data['close'].rolling(N).mean()

    # 计算 DPO
    # DPO = Close - SMA.shift(ceil(N/2) + 1)
    # 其中,ceil(N/2) + 1 是为了使DPO的周期与SMA错开,从而消除趋势影响
    dpo = data['close'] - ma.shift(round(N/2) + 1)

    # 计算 DPO 的移动平均线 (DPO MA)
    madpo = dpo.rolling(M).mean()

    # 将计算出的 DPO 和 DPO MA 添加到 DataFrame
    data['dpo'] = dpo
    data['madpo'] = madpo

    # 返回包含所有计算出指标的 DataFrame
    return data

30 DMA 平均差

DMA 平均差,即平行线差指标,是一种中短期技术分析指标

  • 通过计算两条不同周期移动平均线的差值来判断价格趋势
  • DMA线向上交叉AMA线,做买。 DMA线向下交叉AMA线,做卖

计算方式:DMA = 收盘价N1日内短期均值 - 收盘价N2日内长期均值

Python 实现:

def calculate_dma(df: pd.DataFrame, N1=10, N2=50, M=6):
    """
    计算两种不同周期的移动平均线差值 (DMA) 及其移动平均线 (DMA MA)。
    参数:
    df (pd.DataFrame): 包含至少 'close' 列的DataFrame,代表每日收盘价。
    N1 (int): 第一个用于计算DMA的简单移动平均线 (SMA) 的时间窗口大小,默认为10。
    N2 (int): 第二个用于计算DMA的简单移动平均线 (SMA) 的时间窗口大小,默认为50。
    M (int): 用于计算DMA MA的时间窗口大小,默认为6。
    返回:
    pd.DataFrame: 包含 DMA 和 DMA MA 值的DataFrame。
    """

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算 N1 和 N2 天的简单移动平均线 (SMA),然后计算差值作为 DMA
    dma = data['close'].rolling(window=N1).mean() - data['close'].rolling(window=N2).mean()

    # 计算 DMA 的移动平均线 (DMA MA),使用窗口大小 M
    madma = dma.rolling(window=M).mean()

    # 将计算出的 DMA 和 DMA MA 添加到 DataFrame
    data['dma'] = dma
    data['madma'] = madma

    # 返回包含所有计算出指标的 DataFrame
    return data

31 CR 中间意愿指标

CR (Chande Momentum Oscillator) 指标就是能量指标,又叫中间意愿指标

  • CR 利用最高价、最低价与昨日的中间价的关系来反应市场买卖意愿
  • CR指标上升,表明多方力量增强;反之,CR指标下降,表明空方力量增强

计算过程: $$CR 指标 = 多方强度 / 空方强度 \times 100$$

  • 中间价 = 当日最高价和最低价的平均值
  • 上升值 = 当日最高价与前一日中间价之差,若差值小于 0 则设为 0
  • 下跌值 = 前一日中间价与当日最低价之差,若差值小于 0 则设为 0
  • 多方强度 = N 个周期内上升值的总和
  • 空方强度 = N 个周期内下跌值的总和

Python 实现:

def calculate_cr(df: pd.DataFrame, N=26, M1=10, M2=20, M3=40, M4=62):
    """
    计算 Chaikin 振荡指标(CR)及其移动平均线。
    参数:
    df (pd.DataFrame): 包含至少 'high' 和 'low' 列的 DataFrame,代表每日最高价和最低价。
    N (int): 用于计算多方和空方强度的时间窗口大小,默认为26。
    M1 (int): 第一条 CR 移动平均线的时间窗口大小,默认为10。
    M2 (int): 第二条 CR 移动平均线的时间窗口大小,默认为20。
    M3 (int): 第三条 CR 移动平均线的时间窗口大小,默认为40。
    M4 (int): 第四条 CR 移动平均线的时间窗口大小,默认为62。
    返回:
    pd.DataFrame: 包含 CR 指标和其四条移动平均线的 DataFrame。
    """

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算中间价,作为当日最高价和最低价的平均值
    mid_price = (data['high'] + data['low']) / 2

    # 计算上升值和下跌值
    # 上升值是当日最高价与前一日中间价之差,若差值小于0则设为0
    up_move = data['high'] - mid_price.shift(1)
    up_move[up_move < 0] = 0
    # 下跌值是前一日中间价与当日最低价之差,若差值小于0则设为0
    down_move = mid_price.shift(1) - data['low']
    down_move[down_move < 0] = 0

    # 计算多方强度和空方强度,即在 N 个周期内上升值和下跌值的总和
    positive_strength = up_move.rolling(N, min_periods=1).sum()
    negative_strength = down_move.rolling(N, min_periods=1).sum()

    # 计算 CR 指标,它是多方强度除以空方强度后乘以100
    cr = (positive_strength / negative_strength) * 100

    # 计算 CR 指标的多条移动平均线,每个移动平均线都有不同的时间窗口
    a = cr.rolling(M1).mean().shift(5)
    b = cr.rolling(M2).mean().shift(9)
    c = cr.rolling(M3).mean().shift(17)
    d = cr.rolling(M4).mean().shift(28)

    # 将计算出的 CR 指标和移动平均线添加到 DataFrame
    data['cr'] = cr
    data['a'] = a
    data['b'] = b
    data['c'] = c
    data['d'] = d

    # 返回包含所有计算出指标的 DataFrame
    return data

32 DKX 多空线

多空线 (DKX) 是一个统计性指标,计算重心线的移动平均数

  • 多空线指标是将主动买、主动卖的成交按时间区间分别统计而形成的一个曲线
  • 多空线有两条线,以交叉方式提示买入卖出(默认的周期参数为10)

Python 实现:

def calculate_dkx(df: pd.DataFrame, N=10):
    """
    计算 DKX 指标及其移动平均线 MADKX。
    参数:
    df (pd.DataFrame): 包含至少 'open', 'high', 'low', 'close' 列的 DataFrame,
                       分别代表每日开盘价、最高价、最低价和收盘价。
    N (int): 用于计算 MADKX 的时间窗口大小,默认为10。
    返回:
    pd.DataFrame: 包含原始数据及计算出的DKX和MADKX值的 DataFrame。
    """

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 计算中间价 (Mid-price),这里是采用收盘价、开盘价、最高价和最低价的加权平均
    mid = (3*data['close'] + data['high'] + data['low'] + data['open']) / 6

    # 初始化 DKX 指标
    dkx = 0
    a = 0

    # 计算 DKX 指标
    # 这里使用了一个循环来计算最近20天的加权平均
    for i in range(1, 21):
        a += i  # 累加权重
        tmp = mid.shift(i-1) * (21 - i)  # 计算加权项
        dkx += tmp  # 累加加权项

    # 计算最终的 DKX 指标值
    dkx = dkx / a

    # 计算 DKX 指标的 N 周期移动平均线 MADKX
    madkx = dkx.rolling(N).mean()

    # 将计算出的 DKX 和 MADKX 值添加到 DataFrame
    data['dkx'] = dkx
    data['madkx'] = madkx

    # 返回包含所有计算出指标的 DataFrame
    return data

33 DMI 动向指标

DMI (Directional Movement Index) - 动向指标,又称动向指标、趋向指标

  • 其基本原理是通过分析股票价格在上升及下跌过程中供需关系的均衡点,即供需关系受价格变动之影响而发生由均衡到失衡的循环过程,从而提供对趋势判断的依据
  • 动向的指数有三条线:上升指标线,下降指标线和平均动向指数线。三条线均可设定天数,一般为14天
  • 相较MACD为中期技术指标,DMI可视为中长期技术指标

Python 实现:

def calculate_dmi(df: pd.DataFrame, N=14, M=6):
    '''
    计算 DMI 相关指标,包括 +DI, -DI, ADX, 和 ADXR。
    参数:
    df (pd.DataFrame): 包含至少 'high', 'low', 'close' 列的DataFrame,分别代表最高价,最低价,和收盘价。
    N (int): 用于计算 EMA 的时间窗口大小,默认为14。
    M (int): 用于计算 ADXR 的时间窗口大小,默认为6。
    返回:
    pd.DataFrame: 包含 +DI, -DI, ADX, 和 ADXR 值的DataFrame。
    '''

    # 创建一个df的副本以避免修改原始数据
    data = df.copy()

    # 提取高、低、收盘价
    high = data['high']
    low = data['low']
    close = data['close']

    # 计算 True Range (TR)
    # TR 是每日的最高价和最低价之差,或最高价和昨日收盘价之差的绝对值,或最低价和昨日收盘价之差的绝对值的最大值
    tr1 = high - low
    tr2 = abs(high - close.shift(1))
    tr3 = abs(close.shift(1) - low)
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
    mtr = tr.rolling(N, min_periods=1).sum()

    # 计算 +DM 和 -DM
    # +DM 在价格上升时为当前最高价和前一交易日最高价之差
    # -DM 在价格下降时为前一交易日最低价和当前最低价之差
    hd = high - high.shift(1)
    ld = low.shift(1) - low  # 注意这里修正了 shift 的使用
    hd[hd < 0] = 0  # 当差值小于0时置零
    hd[hd < ld] = 0  # 当+DM小于-DM时置零
    ld[ld < 0] = 0  # 当差值小于0时置零
    ld[ld < hd] = 0  # 当-DM小于+DM时置零

    # 使用简单移动平均 (SMA) 计算平滑后的 TR, +DM, 和 -DM
    dmp = hd.rolling(N, min_periods=1).sum()
    dmm = ld.rolling(N, min_periods=1).sum()

    # 计算 +DI 和 -DI
    # DI 是平滑后的 +DM 或 -DM 与平滑后的 TR 的比率
    pdi = 100 * (dmp / mtr)
    mdi = 100 * (dmm / mtr)

    # 计算 ADX
    # ADX 是 +DI 和 -DI 之间的差异与它们的和的比率的平滑版本
    dx = 100 * abs(mdi - pdi) / (mdi + pdi)
    adx = dx.rolling(M).mean()

    # 计算 ADXR
    # ADXR 是当前 ADX 和 M 个周期前的 ADX 的平均值
    adxr = (adx + adx.shift(M)) / 2

    # 将计算出的指标添加到 DataFrame
    data['pdi'] = pdi
    data['mdi'] = mdi
    data['adx'] = adx
    data['adxr'] = adxr

    # 返回包含所有计算出的指标的 DataFrame
    return data

34

往年同期文章