PandasTA 源码解析(十八)

.\pandas-ta\pandas_ta\volume\pvol.py

# -*- coding: utf-8 -*- 
# 导入所需的库和函数
from pandas_ta.utils import get_offset, signed_series, verify_series

# 定义函数 pvol,计算价格和成交量的乘积
def pvol(close, volume, offset=None, **kwargs):
    """Indicator: Price-Volume (PVOL)"""
    # 验证参数的有效性
    close = verify_series(close)  # 验证价格序列
    volume = verify_series(volume)  # 验证成交量序列
    offset = get_offset(offset)  # 获取偏移量
    signed = kwargs.pop("signed", False)  # 获取 signed 参数,默认为 False

    # 计算结果
    pvol = close * volume  # 计算价格和成交量的乘积
    if signed:
         pvol *= signed_series(close, 1)  # 如果 signed 为 True,则乘以 close 的符号系列

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

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

    # 设置名称和分类
    pvol.name = f"PVOL"  # 设置结果的名称
    pvol.category = "volume"  # 设置结果的分类

    return pvol  # 返回计算结果


pvol.__doc__ = \
"""Price-Volume (PVOL)

Returns a series of the product of price and volume.

Calculation:
    if signed:
        pvol = signed_series(close, 1) * close * volume
    else:
        pvol = close * volume

Args:
    close (pd.Series): Series of 'close's
    volume (pd.Series): Series of 'volume's
    signed (bool): Keeps the sign of the difference in 'close's. 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\volume\pvr.py

# -*- coding: utf-8 -*-
# 从pandas_ta.utils模块中导入verify_series函数
from pandas_ta.utils import verify_series
# 从numpy中导入别名为npNaN的nan值
from numpy import nan as npNaN
# 从pandas模块中导入Series类
from pandas import Series

# 定义函数pvr,计算价格成交量等级指标
def pvr(close, volume):
    """ Indicator: Price Volume Rank"""
    # 验证参数
    close = verify_series(close)
    volume = verify_series(volume)

    # 计算结果
    # 计算收盘价的差分并填充NaN值为0
    close_diff = close.diff().fillna(0)
    # 计算成交量的差分并填充NaN值为0
    volume_diff = volume.diff().fillna(0)
    # 创建与close索引相同的Series对象,并填充NaN值为npNaN
    pvr_ = Series(npNaN, index=close.index)
    # 根据条件设置pvr_中的值
    pvr_.loc[(close_diff >= 0) & (volume_diff >= 0)] = 1
    pvr_.loc[(close_diff >= 0) & (volume_diff < 0)]  = 2
    pvr_.loc[(close_diff < 0) & (volume_diff >= 0)]  = 3
    pvr_.loc[(close_diff < 0) & (volume_diff < 0)]   = 4

    # 设置名称和分类
    pvr_.name = f"PVR"
    pvr_.category = "volume"

    return pvr_

# 设置函数pvr的文档字符串
pvr.__doc__ = \
"""Price Volume Rank

The Price Volume Rank was developed by Anthony J. Macek and is described in his
article in the June, 1994 issue of Technical Analysis of Stocks & Commodities
Magazine. It was developed as a simple indicator that could be calculated even
without a computer. The basic interpretation is to buy when the PV Rank is below
2.5 and sell when it is above 2.5.

Sources:
    https://www.fmlabs.com/reference/default.htm?url=PVrank.htm

Calculation:
    return 1 if 'close change' >= 0 and 'volume change' >= 0
    return 2 if 'close change' >= 0 and 'volume change' < 0
    return 3 if 'close change' < 0 and 'volume change' >= 0
    return 4 if 'close change' < 0 and 'volume change' < 0

Args:
    close (pd.Series): Series of 'close's
    volume (pd.Series): Series of 'volume's

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

.\pandas-ta\pandas_ta\volume\pvt.py

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

# 从 pandas_ta.momentum 模块中导入 roc 函数
from pandas_ta.momentum import roc
# 从 pandas_ta.utils 模块中导入 get_drift、get_offset、verify_series 函数
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定义函数 pvt,计算价格-成交量趋势指标(Price-Volume Trend,PVT)
def pvt(close, volume, drift=None, offset=None, **kwargs):
    """Indicator: Price-Volume Trend (PVT)"""
    # 验证参数
    close = verify_series(close)  # 验证并确保 close 是有效的 Series 类型
    volume = verify_series(volume)  # 验证并确保 volume 是有效的 Series 类型
    drift = get_drift(drift)  # 获取漂移参数的值
    offset = get_offset(offset)  # 获取偏移参数的值

    # 计算结果
    # 计算 ROC(收盘价的变化率)并乘以成交量
    pv = roc(close=close, length=drift) * volume
    # 计算 PVT 的累积值
    pvt = pv.cumsum()

    # 调整偏移
    if offset != 0:
        pvt = pvt.shift(offset)  # 将结果向前或向后偏移指定的周期数

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

    # 命名并分类化结果
    pvt.name = f"PVT"  # 设置结果 Series 的名称
    pvt.category = "volume"  # 设置结果 Series 的分类

    return pvt  # 返回 PVT 结果的 Series


# 设置函数 pvt 的文档字符串
pvt.__doc__ = \
"""Price-Volume Trend (PVT)

The Price-Volume Trend utilizes the Rate of Change with volume to
and it's cumulative values to determine money flow.

Sources:
    https://www.tradingview.com/wiki/Price_Volume_Trend_(PVT)

Calculation:
    Default Inputs:
        drift=1
    ROC = Rate of Change
    pv = ROC(close, drift) * volume
    PVT = pv.cumsum()

Args:
    close (pd.Series): Series of 'close's
    volume (pd.Series): Series of 'volume's
    drift (int): The diff 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\volume\vp.py

# -*- coding: utf-8 -*-
# 从 numpy 库中导入 array_split 函数
from numpy import array_split
# 从 numpy 库中导入 mean 函数
from numpy import mean
# 从 pandas 库中导入 cut、concat、DataFrame 函数
from pandas import cut, concat, DataFrame
# 从 pandas_ta.utils 模块中导入 signed_series、verify_series 函数
from pandas_ta.utils import signed_series, verify_series


def vp(close, volume, width=None, **kwargs):
    """Indicator: Volume Profile (VP)"""
    # 验证参数
    # 如果宽度参数存在且大于 0,则将其转换为整数类型,否则设为默认值 10
    width = int(width) if width and width > 0 else 10
    # 验证 close 数据序列,确保长度为 width
    close = verify_series(close, width)
    # 验证 volume 数据序列,确保长度为 width
    volume = verify_series(volume, width)
    # 从 kwargs 中弹出 sort_close 参数,默认值为 False
    sort_close = kwargs.pop("sort_close", False)

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

    # 设置
    # 生成符号价格序列,即 close 的涨跌情况,用于后续计算正负成交量
    signed_price = signed_series(close, 1)
    # 正成交量为 volume 与符号价格序列大于 0 的乘积
    pos_volume = volume * signed_price[signed_price > 0]
    pos_volume.name = volume.name
    # 负成交量为 volume 与符号价格序列小于 0 的乘积,乘以 -1
    neg_volume = -volume * signed_price[signed_price < 0]
    neg_volume.name = volume.name
    # 合并 close、正成交量、负成交量 到一个 DataFrame 中
    vp = concat([close, pos_volume, neg_volume], axis=1)

    close_col = f"{vp.columns[0]}"
    high_price_col = f"high_{close_col}"
    low_price_col = f"low_{close_col}"
    mean_price_col = f"mean_{close_col}"

    volume_col = f"{vp.columns[1]}"
    pos_volume_col = f"pos_{volume_col}"
    neg_volume_col = f"neg_{volume_col}"
    total_volume_col = f"total_{volume_col}"
    vp.columns = [close_col, pos_volume_col, neg_volume_col]

    # sort_close: 在将数据切分为范围之前,是否根据收盘价进行排序。默认值为 False
    # 如果为 False,则根据日期索引或时间顺序排序,而不是根据价格
    if sort_close:
        # 将 mean_price_col 列设置为 close_col 列的值
        vp[mean_price_col] = vp[close_col]
        # 按照 close_col 列的值进行分组,并计算各范围内的平均价、正成交量、负成交量
        vpdf = vp.groupby(cut(vp[close_col], width, include_lowest=True, precision=2)).agg({
            mean_price_col: mean,
            pos_volume_col: sum,
            neg_volume_col: sum,
        })
        # 从索引中获取范围的最低价格和最高价格
        vpdf[low_price_col] = [x.left for x in vpdf.index]
        vpdf[high_price_col] = [x.right for x in vpdf.index]
        # 重置索引并重新排列列的顺序
        vpdf = vpdf.reset_index(drop=True)
        vpdf = vpdf[[low_price_col, mean_price_col, high_price_col, pos_volume_col, neg_volume_col]]
    else:
        # 将 vp DataFrame 切分为若干子 DataFrame,每个子 DataFrame 包含近似相等数量的数据点
        vp_ranges = array_split(vp, width)
        # 遍历每个子 DataFrame,计算范围内的最低价、平均价、最高价、正成交量、负成交量,并生成生成器对象
        result = ({
            low_price_col: r[close_col].min(),
            mean_price_col: r[close_col].mean(),
            high_price_col: r[close_col].max(),
            pos_volume_col: r[pos_volume_col].sum(),
            neg_volume_col: r[neg_volume_col].sum(),
        } for r in vp_ranges)
        # 将生成器对象转换为 DataFrame
        vpdf = DataFrame(result)
    # 计算总成交量,并添加到 DataFrame 中
    vpdf[total_volume_col] = vpdf[pos_volume_col] + vpdf[neg_volume_col]

    # 处理填充值
    # 如果 kwargs 中包含 fillna 参数,则使用该参数填充 NaN 值
    if "fillna" in kwargs:
        vpdf.fillna(kwargs["fillna"], inplace=True)
    # 如果 kwargs 中包含 fill_method 参数,则使用该参数填充 NaN 值
    if "fill_method" in kwargs:
        vpdf.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分类
    vpdf.name = f"VP_{width}"
    vpdf.category = "volume"

    # 返回结果 DataFrame
    return vpdf


# 将函数文档字符串设为指定内容
vp.__doc__ = \
"""Volume Profile (VP)

Calculates the Volume Profile by slicing price into ranges.
Note: Value Area is not calculated.

Sources:
    https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:volume_by_price
    https://www.tradingview.com/wiki/Volume_Profile
    http://www.ranchodinero.com/volume-tpo-essentials/
"""
    # 访问指定网址以获取相关信息,这是一个网页链接
    https://www.tradingtechnologies.com/blog/2013/05/15/volume-at-price/
# 计算函数
Calculation:
    # 默认输入参数:宽度为10
    Default Inputs:
        width=10

    # 将 'close'、'pos_volume'、'neg_volume' 三个 Series 按列合并成一个 DataFrame
    vp = pd.concat([close, pos_volume, neg_volume], axis=1)
    # 如果需要按 'close' 排序
    if sort_close:
        # 将 'close' 列按照指定宽度切割成不同范围的区间
        vp_ranges = cut(vp[close_col], width)
        # 对于每个区间,计算以下统计量:左边界、平均 'close'、右边界、'pos_volume'、'neg_volume',结果为一个字典
        result = ({range_left, mean_close, range_right, pos_volume, neg_volume} foreach range in vp_ranges
    # 如果不需要按 'close' 排序
    else:
        # 将 DataFrame 按照指定宽度等分成不同的区间
        vp_ranges = np.array_split(vp, width)
        # 对于每个区间,计算以下统计量:最低 'close'、平均 'close'、最高 'close'、'pos_volume'、'neg_volume',结果为一个字典
        result = ({low_close, mean_close, high_close, pos_volume, neg_volume} foreach range in vp_ranges
    # 将结果字典转换成 DataFrame
    vpdf = pd.DataFrame(result)
    # 计算总交易量并添加到 DataFrame 中
    vpdf['total_volume'] = vpdf['pos_volume'] + vpdf['neg_volume']

# 参数说明
Args:
    # 'close' 的 Series 数据
    close (pd.Series): Series of 'close's
    # 'volume' 的 Series 数据
    volume (pd.Series): Series of 'volume's
    # 将价格分布到的区间数,默认为10
    width (int): How many ranges to distrubute price into. Default: 10

# 可选参数说明
Kwargs:
    # 对于缺失值的填充值,默认为 pd.DataFrame.fillna(value)
    fillna (value, optional): pd.DataFrame.fillna(value)
    # 填充方法的类型,默认为 None
    fill_method (value, optional): Type of fill method
    # 是否在切割成区间之前按 'close' 进行排序,默认为 False
    sort_close (value, optional): Whether to sort by close before splitting
        into ranges. Default: False

# 返回结果
Returns:
    # 生成的新特征的 DataFrame
    pd.DataFrame: New feature generated.

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

# -*- coding: utf-8 -*-
# 从当前包中导入ad模块
from .ad import ad
# 从当前包中导入adosc模块
from .adosc import adosc
# 从当前包中导入aobv模块
from .aobv import aobv
# 从当前包中导入cmf模块
from .cmf import cmf
# 从当前包中导入efi模块
from .efi import efi
# 从当前包中导入eom模块
from .eom import eom
# 从当前包中导入kvo模块
from .kvo import kvo
# 从当前包中导入mfi模块
from .mfi import mfi
# 从当前包中导入nvi模块
from .nvi import nvi
# 从当前包中导入obv模块
from .obv import obv
# 从当前包中导入pvi模块
from .pvi import pvi
# 从当前包中导入pvol模块
from .pvol import pvol
# 从当前包中导入pvr模块
from .pvr import pvr
# 从当前包中导入pvt模块
from .pvt import pvt
# 从当前包中导入vp模块
from .vp import vp

.\pandas-ta\pandas_ta\__init__.py

# 定义模块名称
name = "pandas_ta"
"""
.. moduleauthor:: Kevin Johnson
"""

# 导入模块
from importlib.util import find_spec
from pathlib import Path
from pkg_resources import get_distribution, DistributionNotFound

# 获取已安装的模块分布对象
_dist = get_distribution("pandas_ta")
try:
    # 获取模块所在路径
    here = Path(_dist.location) / __file__
    # 检查文件是否存在
    if not here.exists():
        # 如果未安装,但存在其他已安装版本
        raise DistributionNotFound
except DistributionNotFound:
    # 如果未找到分布对象,则提示安装该项目
    __version__ = "Please install this project with setup.py"

# 获取模块版本号
version = __version__ = _dist.version

# 检查导入的模块是否存在
Imports = {
    "alphaVantage-api": find_spec("alphaVantageAPI") is not None,
    "matplotlib": find_spec("matplotlib") is not None,
    "mplfinance": find_spec("mplfinance") is not None,
    "numba": find_spec("numba") is not None,
    "yaml": find_spec("yaml") is not None,
    "scipy": find_spec("scipy") is not None,
    "sklearn": find_spec("sklearn") is not None,
    "statsmodels": find_spec("statsmodels") is not None,
    "stochastic": find_spec("stochastic") is not None,
    "talib": find_spec("talib") is not None,
    "tqdm": find_spec("tqdm") is not None,
    "vectorbt": find_spec("vectorbt") is not None,
    "yfinance": find_spec("yfinance") is not None,
}

# 不是最理想的,也不是动态的,但它可以工作。
# 之后会找到一个动态的解决方案。
Category = {
    # 蜡烛图
    "candles": [
        "cdl_pattern", "cdl_z", "ha"
    ],
    # 周期
    "cycles": ["ebsw"],
    # 动量
    "momentum": [
        "ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
        "coppock", "cti", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd",
        "mom", "pgo", "ppo", "psl", "pvo", "qqe", "roc", "rsi", "rsx", "rvgi",
        "slope", "smi", "squeeze", "squeeze_pro", "stc", "stoch", "stochrsi", "td_seq", "trix",
        "tsi", "uo", "willr"
    ],
    # 重叠
    "overlap": [
        "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku",
        "jma", "kama", "linreg", "mcgd", "midpoint", "midprice", "ohlc4",
        "pwma", "rma", "sinwma", "sma", "ssf", "supertrend", "swma", "t3",
        "tema", "trima", "vidya", "vwap", "vwma", "wcp", "wma", "zlma"
    ],
    # 性能
    "performance": ["log_return", "percent_return"],
    # 统计
    "statistics": [
        "entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev",
        "tos_stdevall", "variance", "zscore"
    ],
    # 趋势
    "trend": [
        "adx", "amat", "aroon", "chop", "cksp", "decay", "decreasing", "dpo",
        "increasing", "long_run", "psar", "qstick", "short_run", "tsignals",
        "ttm_trend", "vhf", "vortex", "xsignals"
    ],
    # 波动性
    "volatility": [
        "aberration", "accbands", "atr", "bbands", "donchian", "hwc", "kc", "massi",
        "natr", "pdist", "rvi", "thermo", "true_range", "ui"
    ],

    # 交易量,"vp" 或 "Volume Profile" 是独特的
}
    # "volume" 键对应的值是一个列表,包含了各种技术指标的名称
    "volume": [
        "ad",    # AD 指标,积累/分配线
        "adosc", # AD 指标,震荡指标
        "aobv",  # 指标,累积/派发线
        "cmf",   # CMF 指标,资金流量指标
        "efi",   # EFI 指标,振荡器
        "eom",   # EOM 指标,指标
        "kvo",   # Klinger Oscillator(克林格震荡器)指标
        "mfi",   # MFI 指标,资金流指标
        "nvi",   # NVI 指标,价值线
        "obv",   # OBV 指标,累积/分配线
        "pvi",   # PVI 指标,价值线
        "pvol",  # PVO 指标,价值线
        "pvr",   # PVR 指标,价值线
        "pvt"    # PVT 指标,价值线
    ],
# 字典,用于指定聚合函数的方式,对于开盘价、最高价、最低价、收盘价和成交量分别指定了不同的聚合方式
CANGLE_AGG = {
    "open": "first",    # 开盘价取第一个值
    "high": "max",      # 最高价取最大值
    "low": "min",       # 最低价取最小值
    "close": "last",    # 收盘价取最后一个值
    "volume": "sum"     # 成交量取总和
}

# 字典,用于记录各个交易所的时区偏移
EXCHANGE_TZ = {
    "NZSX": 12,         # 新西兰股票交易所,时区偏移为+12
    "ASX": 11,          # 澳大利亚证券交易所,时区偏移为+11
    "TSE": 9,           # 东京证券交易所,时区偏移为+9
    "HKE": 8,           # 香港证券交易所,时区偏移为+8
    "SSE": 8,           # 上海证券交易所,时区偏移为+8
    "SGX": 8,           # 新加坡证券交易所,时区偏移为+8
    "NSE": 5.5,         # 印度证券交易所,时区偏移为+5.5
    "DIFX": 4,          # 迪拜金融市场,时区偏移为+4
    "RTS": 3,           # 莫斯科证券交易所,时区偏移为+3
    "JSE": 2,           # 南非证券交易所,时区偏移为+2
    "FWB": 1,           # 法兰克福证券交易所,时区偏移为+1
    "LSE": 1,           # 伦敦证券交易所,时区偏移为+1
    "BMF": -2,          # 巴西商品与期货交易所,时区偏移为-2
    "NYSE": -4,         # 纽约证券交易所,时区偏移为-4
    "TSX": -4           # 多伦多证券交易所,时区偏移为-4
}

# 字典,用于定义一些时间相关的常量
RATE = {
    "DAYS_PER_MONTH": 21,              # 每月交易日数
    "MINUTES_PER_HOUR": 60,            # 每小时分钟数
    "MONTHS_PER_YEAR": 12,             # 每年月份数
    "QUARTERS_PER_YEAR": 4,            # 每年季度数
    "TRADING_DAYS_PER_YEAR": 252,      # 每年交易日数,保持为偶数
    "TRADING_HOURS_PER_DAY": 6.5,      # 每日交易小时数
    "WEEKS_PER_YEAR": 52,              # 每年周数
    "YEARLY": 1                         # 年度
}

# 从 pandas_ta.core 模块导入所有内容
from pandas_ta.core import *

.\pandas-ta\setup.py

# -*- coding: utf-8 -*-
# 导入 setup 函数,用于设置 Python 包的元数据和安装信息
from distutils.core import setup

# 定义长描述信息
long_description = "An easy to use Python 3 Pandas Extension with 130+ Technical Analysis Indicators. Can be called from a Pandas DataFrame or standalone like TA-Lib. Correlation tested with TA-Lib."

# 设置函数调用,用于设置 Python 包的元数据和安装信息
setup(
    # 包的名称
    name="pandas_ta",
    # 包含的子包列表
    packages=[
        "pandas_ta",
        "pandas_ta.candles",
        "pandas_ta.cycles",
        "pandas_ta.momentum",
        "pandas_ta.overlap",
        "pandas_ta.performance",
        "pandas_ta.statistics",
        "pandas_ta.trend",
        "pandas_ta.utils",
        "pandas_ta.utils.data",
        "pandas_ta.volatility",
        "pandas_ta.volume"
    ],
    # 版本号
    version=".".join(("0", "3", "14b")),
    # 简要描述
    description=long_description,
    # 长描述
    long_description=long_description,
    # 作者
    author="Kevin Johnson",
    # 作者邮箱
    author_email="appliedmathkj@gmail.com",
    # 项目 URL
    url="https://github.com/twopirllc/pandas-ta",
    # 维护者
    maintainer="Kevin Johnson",
    # 维护者邮箱
    maintainer_email="appliedmathkj@gmail.com",
    # 下载 URL
    download_url="https://github.com/twopirllc/pandas-ta.git",
    # 关键字列表
    keywords=["technical analysis", "trading", "python3", "pandas"],
    # 许可证
    license="The MIT License (MIT)",
    # 分类信息列表
    classifiers=[
        "Development Status :: 4 - Beta",
        "Programming Language :: Python :: 3.6",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Operating System :: OS Independent",
        "License :: OSI Approved :: MIT License",
        "Natural Language :: English",
        "Intended Audience :: Developers",
        "Intended Audience :: Financial and Insurance Industry",
        "Intended Audience :: Science/Research",
        "Topic :: Office/Business :: Financial",
        "Topic :: Office/Business :: Financial :: Investment",
        "Topic :: Scientific/Engineering",
        "Topic :: Scientific/Engineering :: Information Analysis",
    ],
    # 包数据配置
    package_data={
        "data": ["data/*.csv"],
    },
    # 安装依赖项
    install_requires=["pandas"],
    # 列出额外的依赖组(例如开发依赖)
    extras_require={
        "dev": [
            "alphaVantage-api", "matplotlib", "mplfinance", "scipy",
            "sklearn", "statsmodels", "stochastic",
            "talib", "tqdm", "vectorbt", "yfinance",
        ],
        "test": ["ta-lib"],
    },
)

.\pandas-ta\tests\config.py

# 导入 os 模块,用于操作系统相关功能
import os
# 导入 pandas 模块中的 DatetimeIndex 和 read_csv 函数
from pandas import DatetimeIndex, read_csv

# 设定是否显示详细信息的标志
VERBOSE = True

# 设定警报和信息提示的图标
ALERT = f"[!]"
INFO = f"[i]"

# 设定相关性分析方法,这里选择使用 'corr',也可以选择 'sem'
CORRELATION = "corr"  # "sem"
# 设定相关性阈值,小于 0.99 视为不理想
CORRELATION_THRESHOLD = 0.99  # Less than 0.99 is undesirable

# 读取样本数据集,使用 pandas 的 read_csv 函数
sample_data = read_csv(
    f"data/SPY_D.csv",  # 文件路径
    index_col=0,  # 以第一列作为索引
    parse_dates=True,  # 解析日期
    infer_datetime_format=True,  # 推断日期格式
    keep_date_col=True,  # 保留日期列
)
# 将日期列设置为索引,并丢弃原始日期列
sample_data.set_index(DatetimeIndex(sample_data["date"]), inplace=True, drop=True)
sample_data.drop("date", axis=1, inplace=True)

# 定义错误分析函数,用于输出错误信息
def error_analysis(df, kind, msg, icon=INFO, newline=True):
    if VERBOSE:  # 如果 VERBOSE 为 True,则输出信息
        s = f"{icon} {df.name}['{kind}']: {msg}"  # 构造输出字符串
        if newline:  # 如果需要换行
            s = f"\n{s}"  # 在字符串前添加换行符
        print(s)  # 打印信息


# `.\pandas-ta\tests\context.py`

```py
# 导入 os 模块,提供对操作系统功能的访问
import os
# 导入 sys 模块,提供对 Python 解释器的访问和控制
import sys

# 将当前文件所在目录的父目录添加到 Python 模块搜索路径中,以便导入自定义模块
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

# 导入 pandas_ta 模块,该模块提供了一系列用于技术分析的函数和指标
import pandas_ta

.\pandas-ta\tests\test_ext_indicator_candle.py

# 从config模块中导入sample_data
from .config import sample_data
# 从context模块中导入pandas_ta
from .context import pandas_ta
# 从unittest模块中导入TestCase和skip
from unittest import TestCase, skip
# 从pandas模块中导入DataFrame
from pandas import DataFrame

# 定义测试类TestCandleExtension,继承自TestCase类
class TestCandleExtension(TestCase):
    # 设置类方法setUpClass,在所有测试用例执行前执行一次
    @classmethod
    def setUpClass(cls):
        cls.data = sample_data

    # 设置类方法tearDownClass,在所有测试用例执行后执行一次
    @classmethod
    def tearDownClass(cls):
        del cls.data

    # 定义实例方法setUp,在每个测试用例执行前执行一次
    def setUp(self): pass
    # 定义实例方法tearDown,在每个测试用例执行后执行一次
    def tearDown(self): pass

    # 测试CDL_DOJI_10_0.1模式的扩展
    def test_cdl_doji_ext(self):
        self.data.ta.cdl_pattern("doji", append=True)
        # 断言self.data是DataFrame类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言self.data的最后一列的列名为"CDL_DOJI_10_0.1"
        self.assertEqual(self.data.columns[-1], "CDL_DOJI_10_0.1")

    # 测试CDL_INSIDE模式的扩展
    def test_cdl_inside_ext(self):
        self.data.ta.cdl_pattern("inside", append=True)
        # 断言self.data是DataFrame类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言self.data的最后一列的列名为"CDL_INSIDE"
        self.assertEqual(self.data.columns[-1], "CDL_INSIDE")

    # 测试CDL_Z指标的扩展
    def test_cdl_z_ext(self):
        self.data.ta.cdl_z(append=True)
        # 断言self.data是DataFrame类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言self.data的倒数第四列到最后一列的列名为["open_Z_30_1", "high_Z_30_1", "low_Z_30_1", "close_Z_30_1"]
        self.assertEqual(list(self.data.columns[-4:]), ["open_Z_30_1", "high_Z_30_1", "low_Z_30_1", "close_Z_30_1"])

    # 测试HA指标的扩展
    def test_ha_ext(self):
        self.data.ta.ha(append=True)
        # 断言self.data是DataFrame类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言self.data的倒数第四列到最后一列的列名为["HA_open", "HA_high", "HA_low", "HA_close"]
        self.assertEqual(list(self.data.columns[-4:]), ["HA_open", "HA_high", "HA_low", "HA_close"])

.\pandas-ta\tests\test_ext_indicator_cycles.py

# 从 pandas 库的 core.series 模块中导入 Series 类
from pandas.core.series import Series
# 从当前目录中的 config 模块中导入 sample_data 变量
from .config import sample_data
# 从当前目录中的 context 模块中导入 pandas_ta 模块
from .context import pandas_ta
# 从 unittest 模块中导入 TestCase 类
from unittest import TestCase
# 从 pandas 库中导入 DataFrame 类
from pandas import DataFrame

# 定义测试类 TestCylesExtension,继承自 TestCase 类
class TestCylesExtension(TestCase):
    # 在整个测试类运行之前执行的方法,设置测试数据
    @classmethod
    def setUpClass(cls):
        cls.data = sample_data

    # 在整个测试类运行之后执行的方法,清理测试数据
    @classmethod
    def tearDownClass(cls):
        del cls.data

    # 在每个测试方法运行之前执行的方法
    def setUp(self): pass
    # 在每个测试方法运行之后执行的方法
    def tearDown(self): pass

    # 定义测试方法 test_ebsw_ext,测试 EBSW 扩展函数
    def test_ebsw_ext(self):
        # 调用数据框对象的 ta 属性中的 ebsw 方法,并将结果追加到原数据框中
        self.data.ta.ebsw(append=True)
        # 断言数据框对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言数据框对象的最后一列的列名为 "EBSW_40_10"
        self.assertEqual(self.data.columns[-1], "EBSW_40_10")

.\pandas-ta\tests\test_ext_indicator_momentum.py

# 从config模块中导入sample_data变量
from .config import sample_data
# 从context模块中导入pandas_ta模块
from .context import pandas_ta
# 从unittest模块中导入skip和TestCase类
from unittest import skip, TestCase
# 从pandas模块中导入DataFrame类
from pandas import DataFrame

# 定义测试类TestMomentumExtension,继承自TestCase类
class TestMomentumExtension(TestCase):
    # 类方法setUpClass,用于设置测试类的初始状态
    @classmethod
    def setUpClass(cls):
        # 初始化数据,使用sample_data
        cls.data = sample_data

    # 类方法tearDownClass,用于清理测试类的状态
    @classmethod
    def tearDownClass(cls):
        # 删除数据
        del cls.data

    # 实例方法setUp,用于设置每个测试方法的初始状态
    def setUp(self): pass
    # 实例方法tearDown,用于清理每个测试方法的状态
    def tearDown(self): pass

    # 测试AO扩展函数
    def test_ao_ext(self):
        # 在数据上计算AO指标,并将结果追加到数据中
        self.data.ta.ao(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"AO_5_34"
        self.assertEqual(self.data.columns[-1], "AO_5_34")

    # 测试APO扩展函数
    def test_apo_ext(self):
        # 在数据上计算APO指标,并将结果追加到数据中
        self.data.ta.apo(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"APO_12_26"
        self.assertEqual(self.data.columns[-1], "APO_12_26")

    # 测试BIAS扩展函数
    def test_bias_ext(self):
        # 在数据上计算BIAS指标,并将结果追加到数据中
        self.data.ta.bias(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"BIAS_SMA_26"
        self.assertEqual(self.data.columns[-1], "BIAS_SMA_26")

    # 测试BOP扩展函数
    def test_bop_ext(self):
        # 在数据上计算BOP指标,并将结果追加到数据中
        self.data.ta.bop(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"BOP"
        self.assertEqual(self.data.columns[-1], "BOP")

    # 测试BRAR扩展函数
    def test_brar_ext(self):
        # 在数据上计算BRAR指标,并将结果追加到数据中
        self.data.ta.brar(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言倒数第二列和最后一列的列名分别为"AR_26"和"BR_26"
        self.assertEqual(list(self.data.columns[-2:]), ["AR_26", "BR_26"])

    # 测试CCI扩展函数
    def test_cci_ext(self):
        # 在数据上计算CCI指标,并将结果追加到数据中
        self.data.ta.cci(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"CCI_14_0.015"
        self.assertEqual(self.data.columns[-1], "CCI_14_0.015")

    # 测试CFO扩展函数
    def test_cfo_ext(self):
        # 在数据上计算CFO指标,并将结果追加到数据中
        self.data.ta.cfo(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"CFO_9"
        self.assertEqual(self.data.columns[-1], "CFO_9")

    # 测试CG扩展函数
    def test_cg_ext(self):
        # 在数据上计算CG指标,并将结果追加到数据中
        self.data.ta.cg(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"CG_10"
        self.assertEqual(self.data.columns[-1], "CG_10")

    # 测试CMO扩展函数
    def test_cmo_ext(self):
        # 在数据上计算CMO指标,并将结果追加到数据中
        self.data.ta.cmo(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"CMO_14"
        self.assertEqual(self.data.columns[-1], "CMO_14")

    # 测试Coppock指标扩展函数
    def test_coppock_ext(self):
        # 在数据上计算Coppock指标,并将结果追加到数据中
        self.data.ta.coppock(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"COPC_11_14_10"
        self.assertEqual(self.data.columns[-1], "COPC_11_14_10")

    # 测试CTI扩展函数
    def test_cti_ext(self):
        # 在数据上计算CTI指标,并将结果追加到数据中
        self.data.ta.cti(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"CTI_12"
        self.assertEqual(self.data.columns[-1], "CTI_12")

    # 测试ER扩展函数
    def test_er_ext(self):
        # 在数据上计算ER指标,并将结果追加到数据中
        self.data.ta.er(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"ER_10"
        self.assertEqual(self.data.columns[-1], "ER_10")

    # 测试ERI扩展函数
    def test_eri_ext(self):
        # 在数据上计算ERI指标,并将结果追加到数据中
        self
    # 测试计算惯性指标,并将结果追加到数据框中
    def test_inertia_ext(self):
        # 调用惯性指标计算函数,将结果追加到数据框中
        self.data.ta.inertia(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"INERTIA_20_14"
        self.assertEqual(self.data.columns[-1], "INERTIA_20_14")

    # 测试计算经过优化的惯性指标,并将结果追加到数据框中
    def test_inertia_refined_ext(self):
        # 调用经过优化的惯性指标计算函数,将结果追加到数据框中
        self.data.ta.inertia(refined=True, append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"INERTIAr_20_14"
        self.assertEqual(self.data.columns[-1], "INERTIAr_20_14")

    # 测试计算经过划分的惯性指标,并将结果追加到数据框中
    def test_inertia_thirds_ext(self):
        # 调用经过划分的惯性指标计算函数,将结果追加到数据框中
        self.data.ta.inertia(thirds=True, append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"INERTIAt_20_14"
        self.assertEqual(self.data.columns[-1], "INERTIAt_20_14")

    # 测试计算KDJ指标,并将结果追加到数据框中
    def test_kdj_ext(self):
        # 调用KDJ指标计算函数,将结果追加到数据框中
        self.data.ta.kdj(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后三列的列名分别为"K_9_3", "D_9_3", "J_9_3"
        self.assertEqual(list(self.data.columns[-3:]), ["K_9_3", "D_9_3", "J_9_3"])

    # 测试计算KST指标,并将结果追加到数据框中
    def test_kst_ext(self):
        # 调用KST指标计算函数,将结果追加到数据框中
        self.data.ta.kst(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名分别为"KST_10_15_20_30_10_10_10_15", "KSTs_9"
        self.assertEqual(list(self.data.columns[-2:]), ["KST_10_15_20_30_10_10_10_15", "KSTs_9"])

    # 测试计算MACD指标,并将结果追加到数据框中
    def test_macd_ext(self):
        # 调用MACD指标计算函数,将结果追加到数据框中
        self.data.ta.macd(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后三列的列名分别为"MACD_12_26_9", "MACDh_12_26_9", "MACDs_12_26_9"
        self.assertEqual(list(self.data.columns[-3:]), ["MACD_12_26_9", "MACDh_12_26_9", "MACDs_12_26_9"])

    # 测试计算动量指标,并将结果追加到数据框中
    def test_mom_ext(self):
        # 调用动量指标计算函数,将结果追加到数据框中
        self.data.ta.mom(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"MOM_10"
        self.assertEqual(self.data.columns[-1], "MOM_10")

    # 测试计算价格振荡指标,并将结果追加到数据框中
    def test_pgo_ext(self):
        # 调用价格振荡指标计算函数,将结果追加到数据框中
        self.data.ta.pgo(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"PGO_14"
        self.assertEqual(self.data.columns[-1], "PGO_14")

    # 测试计算价格百分比振荡指标,并将结果追加到数据框中
    def test_ppo_ext(self):
        # 调用价格百分比振荡指标计算函数,将结果追加到数据框中
        self.data.ta.ppo(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后三列的列名分别为"PPO_12_26_9", "PPOh_12_26_9", "PPOs_12_26_9"
        self.assertEqual(list(self.data.columns[-3:]), ["PPO_12_26_9", "PPOh_12_26_9", "PPOs_12_26_9"])

    # 测试计算平滑移动线指标,并将结果追加到数据框中
    def test_psl_ext(self):
        # 调用平滑移动线指标计算函数,将结果追加到数据框中
        self.data.ta.psl(append=True)
        # 断言数据类型为DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为"PSL_12"
        self.assertEqual(self.data.columns[-1], "PSL_12")

    # 测试计算成交量价格振荡指标,并将结果追加到数据框中
    def test_pvo_ext(self):
        # 调用成交量价格振荡指标计算函数,将结果追加到数据框中
        self.data.ta.pvo(append=True)
        # 断言数据类型为
    # 测试 RSX 指标是否正确计算并追加到数据中
    def test_rsx_ext(self):
        # 计算 RSX 指标并追加到数据中
        self.data.ta.rsx(append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为 "RSX_14"
        self.assertEqual(self.data.columns[-1], "RSX_14")

    # 测试 RVGI 指标是否正确计算并追加到数据中
    def test_rvgi_ext(self):
        # 计算 RVGI 指标并追加到数据中
        self.data.ta.rvgi(append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名为 "RVGI_14_4" 和 "RVGIs_14_4"
        self.assertEqual(list(self.data.columns[-2:]), ["RVGI_14_4", "RVGIs_14_4"])

    # 测试斜率指标是否正确计算并追加到数据中
    def test_slope_ext(self):
        # 计算斜率指标并追加到数据中
        self.data.ta.slope(append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为 "SLOPE_1"
        self.assertEqual(self.data.columns[-1], "SLOPE_1")

        # 计算角度形式的斜率指标并追加到数据中
        self.data.ta.slope(append=True, as_angle=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为 "ANGLEr_1"
        self.assertEqual(self.data.columns[-1], "ANGLEr_1")

        # 计算以角度形式表示的斜率指标并追加到数据中
        self.data.ta.slope(append=True, as_angle=True, to_degrees=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为 "ANGLEd_1"
        self.assertEqual(self.data.columns[-1], "ANGLEd_1")

    # 测试 SMI 指标是否正确计算并追加到数据中
    def test_smi_ext(self):
        # 计算 SMI 指标并追加到数据中
        self.data.ta.smi(append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后三列的列名为 "SMI_5_20_5", "SMIs_5_20_5", "SMIo_5_20_5"
        self.assertEqual(list(self.data.columns[-3:]), ["SMI_5_20_5", "SMIs_5_20_5", "SMIo_5_20_5"])

        # 计算带有自定义标量的 SMI 指标并追加到数据中
        self.data.ta.smi(scalar=10, append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后三列的列名为 "SMI_5_20_5_10.0", "SMIs_5_20_5_10.0", "SMIo_5_20_5_10.0"
        self.assertEqual(list(self.data.columns[-3:]), ["SMI_5_20_5_10.0", "SMIs_5_20_5_10.0", "SMIo_5_20_5_10.0"])

    # 测试挤牌指标是否正确计算并追加到数据中
    def test_squeeze_ext(self):
        # 计算挤牌指标并追加到数据中
        self.data.ta.squeeze(append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后四列的列名为 "SQZ_20_2.0_20_1.5", "SQZ_ON", "SQZ_OFF", "SQZ_NO"
        self.assertEqual(list(self.data.columns[-4:]), ["SQZ_20_2.0_20_1.5", "SQZ_ON", "SQZ_OFF", "SQZ_NO"])

        # 计算不带有挤牌 true range 的挤牌指标并追加到数据中
        self.data.ta.squeeze(tr=False, append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后四列的列名为 "SQZ_ON", "SQZ_OFF", "SQZ_NO", "SQZhlr_20_2.0_20_1.5"
        self.assertEqual(list(self.data.columns[-4:]),
            ["SQZ_ON", "SQZ_OFF", "SQZ_NO", "SQZhlr_20_2.0_20_1.5"]
        )

    # 测试高级挤牌指标是否正确计算并追加到数据中
    def test_squeeze_pro_ext(self):
        # 计算高级挤牌指标并追加到数据中
        self.data.ta.squeeze_pro(append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后四列的列名为 "SQZPRO_ON_NORMAL", "SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO"
        self.assertEqual(list(self.data.columns[-4:]), ["SQZPRO_ON_NORMAL", "SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO"])

        # 计算不带有挤牌 true range 的高级挤牌指标并追加到数据中
        self.data.ta.squeeze_pro(tr=False, append=True)
        # 断言数据类型为 DataFrame
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后四列的列名为 "SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO", "SQZPROhlr_20_2.0_20_2_1.5_1"
        self.assertEqual(
            list(self.data.columns[-4:]),
            ["SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO", "SQZPROhlr_20_2.0_20_2_1.5_1"]
        )

    # 测试 ST
    # 测试 Stochastic RSI 扩展功能
    def test_stochrsi_ext(self):
        # 计算 Stochastic RSI,并将结果追加到数据框中
        self.data.ta.stochrsi(append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名为指定值
        self.assertEqual(list(self.data.columns[-2:]), ["STOCHRSIk_14_14_3_3", "STOCHRSId_14_14_3_3"])

    # 跳过该测试
    @skip
    def test_td_seq_ext(self):
        """TS Sequential DataFrame: Working but SLOW implementation"""
        # 计算 TD Sequential,并将结果追加到数据框中
        self.data.ta.td_seq(show_all=False, append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名为指定值
        self.assertEqual(list(self.data.columns[-2:]), ["TD_SEQ_UP", "TD_SEQ_DN"])

        # 计算 TD Sequential,并将结果追加到数据框中
        self.data.ta.td_seq(show_all=True, append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名为指定值
        self.assertEqual(list(self.data.columns[-2:]), ["TD_SEQ_UPa", "TD_SEQ_DNa"])

    # 测试 TRIX 扩展功能
    def test_trix_ext(self):
        # 计算 TRIX,并将结果追加到数据框中
        self.data.ta.trix(append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名为指定值
        self.assertEqual(list(self.data.columns[-2:]), ["TRIX_30_9", "TRIXs_30_9"])

    # 测试 TSI 扩展功能
    def test_tsi_ext(self):
        # 计算 TSI,并将结果追加到数据框中
        self.data.ta.tsi(append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后两列的列名为指定值
        self.assertEqual(list(self.data.columns[-2:]), ["TSI_13_25_13", "TSIs_13_25_13"])

    # 测试 Ultimate Oscillator 扩展功能
    def test_uo_ext(self):
        # 计算 Ultimate Oscillator,并将结果追加到数据框中
        self.data.ta.uo(append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为指定值
        self.assertEqual(self.data.columns[-1], "UO_7_14_28")

    # 测试 Williams %R 扩展功能
    def test_willr_ext(self):
        # 计算 Williams %R,并将结果追加到数据框中
        self.data.ta.willr(append=True)
        # 断言数据对象是 DataFrame 类型
        self.assertIsInstance(self.data, DataFrame)
        # 断言最后一列的列名为指定值
        self.assertEqual(self.data.columns[-1], "WILLR_14")
posted @ 2024-04-15 13:47  绝不原创的飞龙  阅读(26)  评论(0编辑  收藏  举报