PandasTA 原始碼解析(十二)

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

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

相關文章