PandasTA 源码解析(十二)

.\pandas-ta\pandas_ta\statistics\stdev.py

# -*- coding: utf-8 -*-
# 从 numpy 导入 sqrt 函数,并将其命名为 npsqrt
from numpy import sqrt as npsqrt
# 从 variance 模块导入 variance 函数
from .variance import variance
# 从 pandas_ta 模块导入 Imports 类
from pandas_ta import Imports
# 从 pandas_ta.utils 模块导入 get_offset 和 verify_series 函数
from pandas_ta.utils import get_offset, verify_series


# 定义 stdev 函数,用于计算标准差
def stdev(close, length=None, ddof=None, talib=None, offset=None, **kwargs):
    """Indicator: Standard Deviation"""
    # 验证参数
    # 如果 length 存在且大于 0,则将其转换为整数,否则设为 30
    length = int(length) if length and length > 0 else 30
    # 如果 ddof 是整数且大于等于 0 且小于 length,则将其转换为整数,否则设为 1
    ddof = int(ddof) if isinstance(ddof, int) and ddof >= 0 and ddof < length else 1
    # 验证 close 参数是否为有效的 Series,并根据 length 进行截断
    close = verify_series(close, length)
    # 获取 offset 参数
    offset = get_offset(offset)
    # 如果 talib 存在且为布尔值,则使用 talib 参数值,否则默认为 True
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果 close 为 None,则返回
    if close is None: return

    # 计算结果
    # 如果 Imports 中有 "talib" 并且 mode_tal 为 True
    if Imports["talib"] and mode_tal:
        # 从 talib 中导入 STDDEV 函数,并计算标准差
        from talib import STDDEV
        stdev = STDDEV(close, length)
    else:
        # 否则使用自定义的 variance 函数计算方差,然后对结果应用平方根
        stdev = variance(close=close, length=length, ddof=ddof).apply(npsqrt)

    # 偏移结果
    if offset != 0:
        stdev = stdev.shift(offset)

    # 处理填充
    if "fillna" in kwargs:
        stdev.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        stdev.fillna(method=kwargs["fill_method"], inplace=True)

    # 设置名称和类别
    stdev.name = f"STDEV_{length}"
    stdev.category = "statistics"

    # 返回结果
    return stdev


# 设置 stdev 函数的文档字符串
stdev.__doc__ = \
"""Rolling Standard Deviation

Sources:

Calculation:
    Default Inputs:
        length=30
    VAR = Variance
    STDEV = variance(close, length).apply(np.sqrt)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 30
    ddof (int): Delta Degrees of Freedom.
                The divisor used in calculations is N - ddof,
                where N represents the number of elements. Default: 1
    talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib
        version. Default: True
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

.\pandas-ta\pandas_ta\statistics\tos_stdevall.py

# -*- coding: utf-8 -*-

# 从 numpy 库中导入 array 别名为 npArray
from numpy import array as npArray
# 从 numpy 库中导入 arange 别名为 npArange
from numpy import arange as npArange
# 从 numpy 库中导入 polyfit 别名为 npPolyfit
from numpy import polyfit as npPolyfit
# 从 numpy 库中导入 std 别名为 npStd
from numpy import std as npStd
# 从 pandas 库中导入 DataFrame、DatetimeIndex、Series
from pandas import DataFrame, DatetimeIndex, Series
# 从 .stdev 模块中导入 stdev 别名为 stdev
from .stdev import stdev as stdev
# 从 pandas_ta.utils 模块中导入 get_offset、verify_series 函数
from pandas_ta.utils import get_offset, verify_series

# 定义函数 tos_stdevall,计算 Think or Swim 标准偏差
def tos_stdevall(close, length=None, stds=None, ddof=None, offset=None, **kwargs):
    """Indicator: TD Ameritrade's Think or Swim Standard Deviation All"""
    # 验证参数
    # 如果 stds 是非空列表,则使用 stds,否则默认为 [1, 2, 3]
    stds = stds if isinstance(stds, list) and len(stds) > 0 else [1, 2, 3]
    # 如果 stds 中有小于等于 0 的数,则返回空
    if min(stds) <= 0: return
    # 如果 stds 中存在逆序排列,则将其反转为升序排列
    if not all(i < j for i, j in zip(stds, stds[1:])):
        stds = stds[::-1]
    # 将 ddof 转换为整数,确保在合理范围内,默认为 1
    ddof = int(ddof) if ddof and ddof >= 0 and ddof < length else 1
    # 获取偏移量
    offset = get_offset(offset)

    # 属性名称
    _props = f"TOS_STDEVALL"
    # 如果 length 为 None,则使用全部数据;否则,使用指定长度的数据
    if length is None:
        length = close.size
    else:
        # 将 length 转换为整数,确保大于 2,默认为 30
        length = int(length) if isinstance(length, int) and length > 2 else 30
        # 仅保留最近 length 个数据
        close = close.iloc[-length:]
        _props = f"{_props}_{length}"

    # 确保 close 是一个 Series,并且长度为 length
    close = verify_series(close, length)

    # 如果 close 为空,则返回空
    if close is None: return

    # 计算结果
    X = src_index = close.index
    # 如果 close 的索引是 DatetimeIndex 类型,则创建等差数组 X,并将 close 转换为数组
    if isinstance(close.index, DatetimeIndex):
        X = npArange(length)
        close = npArray(close)

    # 使用线性回归拟合得到斜率 m 和截距 b
    m, b = npPolyfit(X, close, 1)
    # 计算线性回归线 lr,索引与 close 保持一致
    lr = Series(m * X + b, index=src_index)
    # 计算标准差 stdev
    stdev = npStd(close, ddof=ddof)

    # 组装结果 DataFrame
    df = DataFrame({f"{_props}_LR": lr}, index=src_index)
    # 对于每个标准偏差值,计算上下界,并设置名称和分类
    for i in stds:
        df[f"{_props}_L_{i}"] = lr - i * stdev
        df[f"{_props}_U_{i}"] = lr + i * stdev
        df[f"{_props}_L_{i}"].name = df[f"{_props}_U_{i}"].name = f"{_props}"
        df[f"{_props}_L_{i}"].category = df[f"{_props}_U_{i}"].category = "statistics"

    # 对结果进行偏移
    if offset != 0:
        df = df.shift(offset)

    # 处理填充值
    if "fillna" in kwargs:
        df.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        df.fillna(method=kwargs["fill_method"], inplace=True)

    # 准备返回的 DataFrame
    df.name = f"{_props}"
    df.category = "statistics"

    return df

# 设定函数的文档字符串
tos_stdevall.__doc__ = \
"""TD Ameritrade's Think or Swim Standard Deviation All (TOS_STDEV)

A port of TD Ameritrade's Think or Swim Standard Deviation All indicator which
returns the standard deviation of data for the entire plot or for the interval
of the last bars defined by the length parameter.

Sources:
    https://tlc.thinkorswim.com/center/reference/thinkScript/Functions/Statistical/StDevAll

Calculation:
    Default Inputs:
        length=None (All), stds=[1, 2, 3], ddof=1
    LR = Linear Regression
    STDEV = Standard Deviation

    LR = LR(close, length)
    STDEV = STDEV(close, length, ddof)
    for level in stds:
        LOWER = LR - level * STDEV
        UPPER = LR + level * STDEV

Args:
    close (pd.Series): Series of 'close's
    length (int): Bars from current bar. Default: None

"""
    stds (list): 存储标准偏差的列表,按照从中心线性回归线开始增加的顺序排列。默认值为 [1,2,3]
    ddof (int): Delta 自由度。在计算中使用的除数是 N - ddof,其中 N 表示元素的数量。默认值为 1
    offset (int): 结果的偏移周期数。默认值为 0
# 函数参数说明,接受关键字参数
Kwargs:
    # fillna 参数,用于填充缺失值的数值
    fillna (value, optional): pd.DataFrame.fillna(value)
    # fill_method 参数,指定填充方法的类型
    fill_method (value, optional): Type of fill method

# 返回值说明,返回一个 pandas DataFrame 对象
Returns:
    # 返回一个 pandas DataFrame 对象,包含中心 LR 和基于标准差倍数的上下 LR 线对
    pd.DataFrame: Central LR, Pairs of Lower and Upper LR Lines based on
        mulitples of the standard deviation. Default: returns 7 columns.

.\pandas-ta\pandas_ta\statistics\variance.py

# 设置文件编码为 UTF-8
# -*- coding: utf-8 -*-

# 从 pandas_ta 库中导入 Imports 模块
from pandas_ta import Imports
# 从 pandas_ta.utils 中导入 get_offset 和 verify_series 函数
from pandas_ta.utils import get_offset, verify_series


def variance(close, length=None, ddof=None, talib=None, offset=None, **kwargs):
    """Indicator: Variance"""
    # 验证参数
    # 将长度转换为整数,如果长度存在且大于1,则为长度,否则为30
    length = int(length) if length and length > 1 else 30
    # 将 ddof 转换为整数,如果 ddof 是整数且大于等于0且小于长度,则为 ddof,否则为1
    ddof = int(ddof) if isinstance(ddof, int) and ddof >= 0 and ddof < length else 1
    # 如果kwargs中存在"min_periods",则将其转换为整数,否则为长度
    min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
    # 验证close是否为有效Series,长度为最大值(长度,min_periods)
    close = verify_series(close, max(length, min_periods))
    # 获取偏移量
    offset = get_offset(offset)
    # 确定是否使用 talib
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果close为None,则返回
    if close is None: return

    # 计算结果
    # 如果 pandas_ta 已导入 talib 并且 mode_tal 为真,则使用 talib 中的 VAR 函数计算方差
    if Imports["talib"] and mode_tal:
        from talib import VAR
        variance = VAR(close, length)
    # 否则,使用 rolling 方法计算滚动方差
    else:
        variance = close.rolling(length, min_periods=min_periods).var(ddof)

    # 偏移结果
    if offset != 0:
        variance = variance.shift(offset)

    # 处理填充
    # 如果kwargs中存在"fillna",则填充NaN值
    if "fillna" in kwargs:
        variance.fillna(kwargs["fillna"], inplace=True)
    # 如果kwargs中存在"fill_method",则使用指定的填充方法
    if "fill_method" in kwargs:
        variance.fillna(method=kwargs["fill_method"], inplace=True)

    # 设置名称和类别
    variance.name = f"VAR_{length}"
    variance.category = "statistics"

    return variance


# 设置 variance 函数的文档字符串
variance.__doc__ = \
"""Rolling Variance

Sources:

Calculation:
    Default Inputs:
        length=30
    VARIANCE = close.rolling(length).var()

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 30
    ddof (int): Delta Degrees of Freedom.
                The divisor used in calculations is N - ddof,
                where N represents the number of elements. Default: 0
    talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib
        version. Default: True
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

.\pandas-ta\pandas_ta\statistics\zscore.py

# -*- coding: utf-8 -*- 
# 从pandas_ta.overlap模块中导入sma函数
from pandas_ta.overlap import sma
# 从本地的stdev模块中导入stdev函数
from .stdev import stdev
# 从pandas_ta.utils模块中导入get_offset和verify_series函数
from pandas_ta.utils import get_offset, verify_series

# 定义函数zscore,用于计算Z分数指标
def zscore(close, length=None, std=None, offset=None, **kwargs):
    """Indicator: Z Score"""
    # 验证参数
    # 将length转换为整数,如果length存在且大于1,则取其值,否则默认为30
    length = int(length) if length and length > 1 else 30
    # 将std转换为浮点数,如果std存在且大于1,则取其值,否则默认为1
    std = float(std) if std and std > 1 else 1
    # 验证close是否为有效的Series,长度为length
    close = verify_series(close, length)
    # 获取offset值
    offset = get_offset(offset)

    # 如果close为空,则返回空
    if close is None: return

    # 计算结果
    # 将std乘以stdev函数计算的标准差值
    std *= stdev(close=close, length=length, **kwargs)
    # 计算均值,使用sma函数计算移动平均值
    mean = sma(close=close, length=length, **kwargs)
    # 计算Z分数
    zscore = (close - mean) / std

    # 调整偏移量
    if offset != 0:
        zscore = zscore.shift(offset)

    # 处理填充
    if "fillna" in kwargs:
        zscore.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        zscore.fillna(method=kwargs["fill_method"], inplace=True)

    # 设置指标名称和类别
    zscore.name = f"ZS_{length}"
    zscore.category = "statistics"

    return zscore


# 设置zscore函数的文档字符串
zscore.__doc__ = \
"""Rolling Z Score

Sources:

Calculation:
    Default Inputs:
        length=30, std=1
    SMA = Simple Moving Average
    STDEV = Standard Deviation
    std = std * STDEV(close, length)
    mean = SMA(close, length)
    ZSCORE = (close - mean) / std

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 30
    std (float): It's period. Default: 1
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

.\pandas-ta\pandas_ta\statistics\__init__.py

# 设置文件编码为 UTF-8,以支持中文等非 ASCII 字符
# 导入自定义模块中的函数,用于计算数据的不同统计量
from .entropy import entropy  # 导入 entropy 函数,用于计算数据的熵
from .kurtosis import kurtosis  # 导入 kurtosis 函数,用于计算数据的峰度
from .mad import mad  # 导入 mad 函数,用于计算数据的绝对中位差
from .median import median  # 导入 median 函数,用于计算数据的中位数
from .quantile import quantile  # 导入 quantile 函数,用于计算数据的分位数
from .skew import skew  # 导入 skew 函数,用于计算数据的偏度
from .stdev import stdev  # 导入 stdev 函数,用于计算数据的标准差
from .tos_stdevall import tos_stdevall  # 导入 tos_stdevall 函数,用于计算数据的时间序列标准差
from .variance import variance  # 导入 variance 函数,用于计算数据的方差
from .zscore import zscore  # 导入 zscore 函数,用于计算数据的 Z 分数

.\pandas-ta\pandas_ta\trend\adx.py

# -*- coding: utf-8 -*-
# 导入所需的库和模块
from pandas import DataFrame
from pandas_ta.overlap import ma
from pandas_ta.volatility import atr
from pandas_ta.utils import get_drift, get_offset, verify_series, zero

# 定义 ADX 指标函数
def adx(high, low, close, length=None, lensig=None, scalar=None, mamode=None, drift=None, offset=None, **kwargs):
    """Indicator: ADX"""
    # 验证参数
    length = length if length and length > 0 else 14
    lensig = lensig if lensig and lensig > 0 else length
    mamode = mamode if isinstance(mamode, str) else "rma"
    scalar = float(scalar) if scalar else 100
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    drift = get_drift(drift)
    offset = get_offset(offset)

    if high is None or low is None or close is None: return

    # 计算 ATR 指标
    atr_ = atr(high=high, low=low, close=close, length=length)

    # 计算上升和下降动向线
    up = high - high.shift(drift)  # high.diff(drift)
    dn = low.shift(drift) - low    # low.diff(-drift).shift(drift)

    pos = ((up > dn) & (up > 0)) * up
    neg = ((dn > up) & (dn > 0)) * dn

    pos = pos.apply(zero)
    neg = neg.apply(zero)

    k = scalar / atr_
    dmp = k * ma(mamode, pos, length=length)
    dmn = k * ma(mamode, neg, length=length)

    dx = scalar * (dmp - dmn).abs() / (dmp + dmn)
    adx = ma(mamode, dx, length=lensig)

    # 偏移
    if offset != 0:
        dmp = dmp.shift(offset)
        dmn = dmn.shift(offset)
        adx = adx.shift(offset)

    # 处理填充值
    if "fillna" in kwargs:
        adx.fillna(kwargs["fillna"], inplace=True)
        dmp.fillna(kwargs["fillna"], inplace=True)
        dmn.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        adx.fillna(method=kwargs["fill_method"], inplace=True)
        dmp.fillna(method=kwargs["fill_method"], inplace=True)
        dmn.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分类
    adx.name = f"ADX_{lensig}"
    dmp.name = f"DMP_{length}"
    dmn.name = f"DMN_{length}"

    adx.category = dmp.category = dmn.category = "trend"

    # 准备返回的 DataFrame
    data = {adx.name: adx, dmp.name: dmp, dmn.name: dmn}
    adxdf = DataFrame(data)
    adxdf.name = f"ADX_{lensig}"
    adxdf.category = "trend"

    return adxdf


# 设置 ADX 函数的文档字符串
adx.__doc__ = \
"""Average Directional Movement (ADX)

Average Directional Movement is meant to quantify trend strength by measuring
the amount of movement in a single direction.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/average-directional-movement-adx/
    TA Lib Correlation: >99%

Calculation:
        DMI ADX TREND 2.0 by @TraderR0BERT, NETWORTHIE.COM
            // 由 @TraderR0BERT, NETWORTHIE.COM 创建,最后更新日期为 01/26/2016
            // DMI 指标
            // 分辨率输入选项,用于更高/更低的时间框架
            study(title="DMI ADX TREND 2.0", shorttitle="ADX TREND 2.0")

        adxlen = input(14, title="ADX Smoothing")
        dilen = input(14, title="DI Length")
        thold = input(20, title="Threshold")

        threshold = thold

        //Script for Indicator
        dirmov(len) =>
            up = change(high)
            down = -change(low)
            truerange = rma(tr, len)
            plus = fixnan(100 * rma(up > down and up > 0 ? up : 0, len) / truerange)
            minus = fixnan(100 * rma(down > up and down > 0 ? down : 0, len) / truerange)
            [plus, minus]

        adx(dilen, adxlen) =>
            [plus, minus] = dirmov(dilen)
            sum = plus + minus
            adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen)
            [adx, plus, minus]

        [sig, up, down] = adx(dilen, adxlen)
        osob=input(40,title="Exhaustion Level for ADX, default = 40")
        col = sig >= sig[1] ? green : sig <= sig[1] ? red : gray

        //Plot Definitions Current Timeframe
        p1 = plot(sig, color=col, linewidth = 3, title="ADX")
        p2 = plot(sig, color=col, style=circles, linewidth=3, title="ADX")
        p3 = plot(up, color=blue, linewidth = 3, title="+DI")
        p4 = plot(up, color=blue, style=circles, linewidth=3, title="+DI")
        p5 = plot(down, color=fuchsia, linewidth = 3, title="-DI")
        p6 = plot(down, color=fuchsia, style=circles, linewidth=3, title="-DI")
        h1 = plot(threshold, color=black, linewidth =3, title="Threshold")

        trender = (sig >= up or sig >= down) ? 1 : 0
        bgcolor(trender>0?black:gray, transp=85)

        //Alert Function for ADX crossing Threshold
        Up_Cross = crossover(up, threshold)
        alertcondition(Up_Cross, title="DMI+ cross", message="DMI+ Crossing Threshold")
        Down_Cross = crossover(down, threshold)
        alertcondition(Down_Cross, title="DMI- cross", message="DMI- Crossing Threshold")
# 定义函数参数
Args:
    high (pd.Series): 'high' 数据序列
    low (pd.Series): 'low' 数据序列
    close (pd.Series): 'close' 数据序列
    length (int): 周期长度,默认为14
    lensig (int): 信号长度,类似于 TradingView 的默认 ADX 长度,默认为 length
    scalar (float): 放大倍数,默认为100
    mamode (str): 参考 ```help(ta.ma)```py,默认为 'rma'
    drift (int): 差异周期,默认为1
    offset (int): 结果偏移周期数,默认为0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value) 的填充值
    fill_method (value, optional): 填充方法类型

Returns:
    pd.DataFrame: 包含 adx、dmp、dmn 列的数据框

.\pandas-ta\pandas_ta\trend\amat.py

# -*- coding: utf-8 -*-
# 从 pandas 库中导入 DataFrame 类
from pandas import DataFrame
# 从当前目录下的 long_run 模块中导入 long_run 函数
from .long_run import long_run
# 从当前目录下的 short_run 模块中导入 short_run 函数
from .short_run import short_run
# 从 pandas_ta 包中的 overlap 模块中导入 ma 函数
from pandas_ta.overlap import ma
# 从 pandas_ta 包中的 utils 模块中导入 get_offset 和 verify_series 函数
from pandas_ta.utils import get_offset, verify_series

# 定义函数 amat,用于计算 Archer Moving Averages Trends (AMAT) 指标
def amat(close=None, fast=None, slow=None, lookback=None, mamode=None, offset=None, **kwargs):
    """Indicator: Archer Moving Averages Trends (AMAT)"""
    # 验证参数的有效性,如果未提供则使用默认值
    fast = int(fast) if fast and fast > 0 else 8
    slow = int(slow) if slow and slow > 0 else 21
    lookback = int(lookback) if lookback and lookback > 0 else 2
    # 将 mamode 转换为小写字符串,如果未提供则使用默认值 "ema"
    mamode = mamode.lower() if isinstance(mamode, str) else "ema"
    # 验证 close 参数,确保长度足够用于计算指标
    close = verify_series(close, max(fast, slow, lookback))
    # 获取偏移量
    offset = get_offset(offset)
    # 如果 kwargs 中包含 "length" 键,则移除它
    if "length" in kwargs: kwargs.pop("length")

    # 如果未提供 close 参数,则返回空值
    if close is None: return

    # 计算快速移动平均线和慢速移动平均线
    fast_ma = ma(mamode, close, length=fast, **kwargs)
    slow_ma = ma(mamode, close, length=slow, **kwargs)

    # 计算长期和短期运行趋势
    mas_long = long_run(fast_ma, slow_ma, length=lookback)
    mas_short = short_run(fast_ma, slow_ma, length=lookback)

    # 对结果进行偏移处理
    if offset != 0:
        mas_long = mas_long.shift(offset)
        mas_short = mas_short.shift(offset)

    # 如果 kwargs 中包含 "fillna" 键,则使用指定的值填充缺失值
    if "fillna" in kwargs:
        mas_long.fillna(kwargs["fillna"], inplace=True)
        mas_short.fillna(kwargs["fillna"], inplace=True)

    # 如果 kwargs 中包含 "fill_method" 键,则使用指定的填充方法填充缺失值
    if "fill_method" in kwargs:
        mas_long.fillna(method=kwargs["fill_method"], inplace=True)
        mas_short.fillna(method=kwargs["fill_method"], inplace=True)

    # 准备要返回的 DataFrame
    amatdf = DataFrame({
        f"AMAT{mamode[0]}_LR_{fast}_{slow}_{lookback}": mas_long,
        f"AMAT{mamode[0]}_SR_{fast}_{slow}_{lookback}": mas_short
    })

    # 设置 DataFrame 的名称和类别
    amatdf.name = f"AMAT{mamode[0]}_{fast}_{slow}_{lookback}"
    amatdf.category = "trend"

    # 返回结果 DataFrame
    return amatdf

.\pandas-ta\pandas_ta\trend\aroon.py

# -*- coding: utf-8 -*-
# 从 pandas 库中导入 DataFrame 类
from pandas import DataFrame
# 从 pandas_ta 库中导入 Imports 模块
from pandas_ta import Imports
# 从 pandas_ta.utils 模块中导入 get_offset, verify_series 函数
from pandas_ta.utils import get_offset, verify_series
# 从 pandas_ta.utils 模块中导入 recent_maximum_index, recent_minimum_index 函数
from pandas_ta.utils import recent_maximum_index, recent_minimum_index

# 定义函数 aroon,计算 Aroon 和 Aroon Oscillator 指标
def aroon(high, low, length=None, scalar=None, talib=None, offset=None, **kwargs):
    """Indicator: Aroon & Aroon Oscillator"""
    # 验证参数
    length = length if length and length > 0 else 14
    scalar = float(scalar) if scalar else 100
    high = verify_series(high, length)
    low = verify_series(low, length)
    offset = get_offset(offset)
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    if high is None or low is None: return

    # 计算结果
    if Imports["talib"] and mode_tal:
        from talib import AROON, AROONOSC
        aroon_down, aroon_up = AROON(high, low, length)
        aroon_osc = AROONOSC(high, low, length)
    else:
        periods_from_hh = high.rolling(length + 1).apply(recent_maximum_index, raw=True)
        periods_from_ll = low.rolling(length + 1).apply(recent_minimum_index, raw=True)

        aroon_up = aroon_down = scalar
        aroon_up *= 1 - (periods_from_hh / length)
        aroon_down *= 1 - (periods_from_ll / length)
        aroon_osc = aroon_up - aroon_down

    # 处理填充
    if "fillna" in kwargs:
        aroon_up.fillna(kwargs["fillna"], inplace=True)
        aroon_down.fillna(kwargs["fillna"], inplace=True)
        aroon_osc.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        aroon_up.fillna(method=kwargs["fill_method"], inplace=True)
        aroon_down.fillna(method=kwargs["fill_method"], inplace=True)
        aroon_osc.fillna(method=kwargs["fill_method"], inplace=True)

    # 偏移
    if offset != 0:
        aroon_up = aroon_up.shift(offset)
        aroon_down = aroon_down.shift(offset)
        aroon_osc = aroon_osc.shift(offset)

    # 命名和分类
    aroon_up.name = f"AROONU_{length}"
    aroon_down.name = f"AROOND_{length}"
    aroon_osc.name = f"AROONOSC_{length}"

    aroon_down.category = aroon_up.category = aroon_osc.category = "trend"

    # 准备要返回的 DataFrame
    data = {
        aroon_down.name: aroon_down,
        aroon_up.name: aroon_up,
        aroon_osc.name: aroon_osc,
    }
    aroondf = DataFrame(data)
    aroondf.name = f"AROON_{length}"
    aroondf.category = aroon_down.category

    return aroondf


# 设置函数 aroon 的文档字符串
aroon.__doc__ = \
"""Aroon & Aroon Oscillator (AROON)

Aroon attempts to identify if a security is trending and how strong.

Sources:
    https://www.tradingview.com/wiki/Aroon
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/aroon-ar/

Calculation:
    Default Inputs:
        length=1, scalar=100

    recent_maximum_index(x): return int(np.argmax(x[::-1]))
    recent_minimum_index(x): return int(np.argmin(x[::-1]))

    periods_from_hh = high.rolling(length + 1).apply(recent_maximum_index, raw=True)

"""
    # 计算 Aroon 指标中的上升线,使用公式:scalar * (1 - (periods_from_hh / length))
    AROON_UP = scalar * (1 - (periods_from_hh / length))

    # 计算 Aroon 指标中的下降线,使用公式:scalar * (1 - (periods_from_ll / length))
    periods_from_ll = low.rolling(length + 1).apply(recent_minimum_index, raw=True)
    AROON_DN = scalar * (1 - (periods_from_ll / length))

    # 计算 Aroon 指标的震荡值,使用公式:AROON_UP - AROON_DN
    AROON_OSC = AROON_UP - AROON_DN
# 定义函数参数
Args:
    close (pd.Series): 包含'close'价格数据的Series
    length (int): 计算指标的周期,默认为14
    scalar (float): 放大倍数,默认为100
    talib (bool): 如果安装了TA Lib并且talib为True,则返回TA Lib版本,默认为True
    offset (int): 结果的偏移周期数,默认为0

# 定义函数关键字参数
Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)的填充值
    fill_method (value, optional): 填充方法的类型

# 返回值
Returns:
    pd.DataFrame: 包含aroon_up、aroon_down、aroon_osc列的DataFrame

.\pandas-ta\pandas_ta\trend\chop.py

# -*- coding: utf-8 -*-

# 从 numpy 库中导入 log10 函数并重命名为 npLog10
from numpy import log10 as npLog10
# 从 numpy 库中导入 log 函数并重命名为 npLn
from numpy import log as npLn
# 从 pandas_ta 库中导入 volatility 模块中的 atr 函数
from pandas_ta.volatility import atr
# 从 pandas_ta 库中导入 utils 模块中的 get_drift, get_offset, verify_series 函数
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定义函数 chop,计算 Choppiness Index (CHOP)
def chop(high, low, close, length=None, atr_length=None, ln=None, scalar=None, drift=None, offset=None, **kwargs):
    """Indicator: Choppiness Index (CHOP)"""
    # 验证参数
    length = int(length) if length and length > 0 else 14
    atr_length = int(atr_length) if atr_length is not None and atr_length > 0 else 1
    ln = bool(ln) if isinstance(ln, bool) else False
    scalar = float(scalar) if scalar else 100
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    drift = get_drift(drift)
    offset = get_offset(offset)

    # 如果 high、low、close 中有任何一个为 None,则返回
    if high is None or low is None or close is None: return

    # 计算结果
    diff = high.rolling(length).max() - low.rolling(length).min()

    atr_ = atr(high=high, low=low, close=close, length=atr_length)
    atr_sum = atr_.rolling(length).sum()

    chop = scalar
    if ln:
        chop *= (npLn(atr_sum) - npLn(diff)) / npLn(length)
    else:
        chop *= (npLog10(atr_sum) - npLog10(diff)) / npLog10(length)

    # 偏移结果
    if offset != 0:
        chop = chop.shift(offset)

    # 处理填充值
    if "fillna" in kwargs:
        chop.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        chop.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分类
    chop.name = f"CHOP{'ln' if ln else ''}_{length}_{atr_length}_{scalar}"
    chop.category = "trend"

    return chop

# 设置 chop 函数的文档字符串
chop.__doc__ = \
"""Choppiness Index (CHOP)

The Choppiness Index was created by Australian commodity trader
E.W. Dreiss and is designed to determine if the market is choppy
(trading sideways) or not choppy (trading within a trend in either
direction). Values closer to 100 implies the underlying is choppier
whereas values closer to 0 implies the underlying is trending.

Sources:
    https://www.tradingview.com/scripts/choppinessindex/
    https://www.motivewave.com/studies/choppiness_index.htm

Calculation:
    Default Inputs:
        length=14, scalar=100, drift=1
    HH = high.rolling(length).max()
    LL = low.rolling(length).min()

    ATR_SUM = SUM(ATR(drift), length)
    CHOP = scalar * (LOG10(ATR_SUM) - LOG10(HH - LL))
    CHOP /= LOG10(length)

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 14
    atr_length (int): Length for ATR. Default: 1
    ln (bool): If True, uses ln otherwise log10. Default: False
    scalar (float): How much to magnify. Default: 100
    drift (int): The difference period. Default: 1
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
"""
    # 创建一个 Pandas Series 对象,表示生成了一个新特征
    pd.Series: New feature generated.
# 这是一个空的字符串,通常用作多行注释的起始

.\pandas-ta\pandas_ta\trend\cksp.py

# -*- coding: utf-8 -*-

# 从 pandas 库中导入 DataFrame 类
from pandas import DataFrame
# 从 pandas_ta 库中导入 atr 函数
from pandas_ta.volatility import atr
# 从 pandas_ta 库中导入 get_offset 和 verify_series 函数
from pandas_ta.utils import get_offset, verify_series

# 定义函数 cksp,计算 Chande Kroll Stop (CKSP) 指标
def cksp(high, low, close, p=None, x=None, q=None, tvmode=None, offset=None, **kwargs):
    """Indicator: Chande Kroll Stop (CKSP)"""
    
    # 验证参数
    p = int(p) if p and p > 0 else 10
    x = float(x) if x and x > 0 else 1 if tvmode is True else 3
    q = int(q) if q and q > 0 else 9 if tvmode is True else 20
    _length = max(p, q, x)

    high = verify_series(high, _length)
    low = verify_series(low, _length)
    close = verify_series(close, _length)
    if high is None or low is None or close is None: return

    offset = get_offset(offset)
    tvmode = tvmode if isinstance(tvmode, bool) else True
    mamode = "rma" if tvmode is True else "sma"

    # 计算结果
    atr_ = atr(high=high, low=low, close=close, length=p, mamode=mamode)

    long_stop_ = high.rolling(p).max() - x * atr_
    long_stop = long_stop_.rolling(q).max()

    short_stop_ = low.rolling(p).min() + x * atr_
    short_stop = short_stop_.rolling(q).min()

    # 偏移
    if offset != 0:
        long_stop = long_stop.shift(offset)
        short_stop = short_stop.shift(offset)

    # 处理填充
    if "fillna" in kwargs:
        long_stop.fillna(kwargs["fillna"], inplace=True)
        short_stop.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        long_stop.fillna(method=kwargs["fill_method"], inplace=True)
        short_stop.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分类
    _props = f"_{p}_{x}_{q}"
    long_stop.name = f"CKSPl{_props}"
    short_stop.name = f"CKSPs{_props}"
    long_stop.category = short_stop.category = "trend"

    # 准备返回的 DataFrame
    ckspdf = DataFrame({long_stop.name: long_stop, short_stop.name: short_stop})
    ckspdf.name = f"CKSP{_props}"
    ckspdf.category = long_stop.category

    return ckspdf

# 设置函数 cksp 的文档字符串
cksp.__doc__ = \
"""Chande Kroll Stop (CKSP)

The Tushar Chande and Stanley Kroll in their book
“The New Technical Trader”. It is a trend-following indicator,
identifying your stop by calculating the average true range of
the recent market volatility. The indicator defaults to the implementation
found on tradingview but it provides the original book implementation as well,
which differs by the default periods and moving average mode. While the trading
view implementation uses the Welles Wilder moving average, the book uses a
simple moving average.

Sources:
    https://www.multicharts.com/discussion/viewtopic.php?t=48914
    "The New Technical Trader", Wikey 1st ed. ISBN 9780471597803, page 95

Calculation:
    Default Inputs:
        p=10, x=1, q=9, tvmode=True
    ATR = Average True Range

    LS0 = high.rolling(p).max() - x * ATR(length=p)
    LS = LS0.rolling(q).max()

    SS0 = high.rolling(p).min() + x * ATR(length=p)
    SS = SS0.rolling(q).min()

Args:
"""
    # 'close'是一个包含收盘价的Series对象
    # p是ATR和第一个停止期的值,以整数表示。在两种模式下默认值均为10
    # x是ATR的标量值,在Trading View模式下默认值为1,在其他模式下默认值为3
    # q是第二个停止期的值,以整数表示。在Trading View模式下默认值为9,在其他模式下默认值为20
    # tvmode是一个布尔值,表示是否使用Trading View模式或书中实现模式。默认为True表示使用Trading View模式
    # offset是结果的偏移周期数。默认值为0
# 定义函数的参数列表,这里使用了可选参数
Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)  # 填充缺失值的数值
    fill_method (value, optional): Type of fill method  # 填充方法的类型

# 返回值说明
Returns:
    pd.DataFrame: long and short columns.  # 返回一个包含长列和短列的 Pandas 数据帧

.\pandas-ta\pandas_ta\trend\decay.py

# -*- coding: utf-8 -*-
# 从 numpy 导入 exp 函数并重命名为 npExp
from numpy import exp as npExp
# 从 pandas 导入 DataFrame 类
from pandas import DataFrame
# 从 pandas_ta.utils 导入 get_offset 和 verify_series 函数
from pandas_ta.utils import get_offset, verify_series


def decay(close, kind=None, length=None, mode=None, offset=None, **kwargs):
    """Indicator: Decay"""
    # 验证参数
    # 如果 length 存在且大于 0,则转换为整数,否则设置为默认值 5
    length = int(length) if length and length > 0 else 5
    # 如果 mode 是字符串,则转换为小写,否则设置为默认值 "linear"
    mode = mode.lower() if isinstance(mode, str) else "linear"
    # 验证 close 是否是有效的 Series,长度为 length
    close = verify_series(close, length)
    # 获取偏移量
    offset = get_offset(offset)

    # 如果 close 为空,则返回 None
    if close is None: return

    # 计算结果
    # 默认模式为线性模式
    _mode = "L"
    # 如果 mode 是 "exp" 或 kind 是 "exponential",则使用指数模式
    if mode == "exp" or kind == "exponential":
        _mode = "EXP"
        # 计算差异,利用指数函数 exp(-length)
        diff = close.shift(1) - npExp(-length)
    else:  # 默认为 "linear"
        # 计算差异,利用线性函数 (1 / length)
        diff = close.shift(1) - (1 / length)
    # 将第一个元素设置为 close 的第一个值
    diff[0] = close[0]
    # 创建 DataFrame,包含 close、diff 和 0 列
    tdf = DataFrame({"close": close, "diff": diff, "0": 0})
    # 计算最大值
    ld = tdf.max(axis=1)

    # 偏移结果
    if offset != 0:
        ld = ld.shift(offset)

    # 处理填充
    # 如果 kwargs 中包含 "fillna",则使用指定值填充缺失值
    if "fillna" in kwargs:
        ld.fillna(kwargs["fillna"], inplace=True)
    # 如果 kwargs 中包含 "fill_method",则使用指定的填充方法
    if "fill_method" in kwargs:
        ld.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分类
    # 设置 Series 的名称为模式和长度的组合
    ld.name = f"{_mode}DECAY_{length}"
    # 设置 Series 的分类为 "trend"
    ld.category = "trend"

    return ld


# 设置 decay 函数的文档字符串
decay.__doc__ = \
"""Decay

Creates a decay moving forward from prior signals like crosses. The default is
"linear". Exponential is optional as "exponential" or "exp".

Sources:
    https://tulipindicators.org/decay

Calculation:
    Default Inputs:
        length=5, mode=None

    if mode == "exponential" or mode == "exp":
        max(close, close[-1] - exp(-length), 0)
    else:
        max(close, close[-1] - (1 / length), 0)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 1
    mode (str): If 'exp' then "exponential" decay. Default: 'linear'
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""
posted @ 2024-04-15 13:43  绝不原创的飞龙  阅读(14)  评论(0编辑  收藏  举报