PandasTA 原始碼解析(十八)

绝不原创的飞龙發表於2024-04-15

.\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")

相關文章