PandasTA 原始碼解析(十)

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

.\pandas-ta\pandas_ta\overlap\sma.py

# -*- 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

# 定義簡單移動平均(Simple Moving Average,SMA)指標函式
def sma(close, length=None, talib=None, offset=None, **kwargs):
    """Indicator: Simple Moving Average (SMA)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則預設為 10
    length = int(length) if length and length > 0 else 10
    # 如果 kwargs 中存在 "min_periods" 並且不為 None,則將其轉換為整數,否則預設為 length
    min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
    # 驗證 close 序列,確保其長度至少為 length 或 min_periods
    close = verify_series(close, max(length, min_periods))
    # 獲取偏移量
    offset = get_offset(offset)
    # 如果 talib 存在且為布林型別,則將其轉換為布林值,否則預設為 True
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果 close 為 None,則返回
    if close is None: return

    # 計算結果
    # 如果 Imports["talib"] 為 True 且 mode_tal 為 True,則使用 TA Lib 庫中的 SMA 函式計算移動平均值
    if Imports["talib"] and mode_tal:
        from talib import SMA
        sma = SMA(close, length)
    else:
        # 否則,使用 pandas 庫中的 rolling 和 mean 方法計算移動平均值
        sma = close.rolling(length, min_periods=min_periods).mean()

    # 偏移
    # 如果偏移量不為 0,則對移動平均值進行偏移
    if offset != 0:
        sma = sma.shift(offset)

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

    # 名稱和類別
    # 設定移動平均值序列的名稱為 "SMA_長度"
    sma.name = f"SMA_{length}"
    # 設定移動平均值序列的類別為 "overlap"
    sma.category = "overlap"

    # 返回移動平均值序列
    return sma

# 設定 sma 函式的文件字串
sma.__doc__ = \
"""Simple Moving Average (SMA)

The Simple Moving Average is the classic moving average that is the equally
weighted average over n periods.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/

Calculation:
    Default Inputs:
        length=10
    SMA = SUM(close, length) / length

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    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:
    adjust (bool): Default: True
    presma (bool, optional): If True, uses SMA for initial value.
    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\overlap\ssf.py

# -*- coding: utf-8 -*-
# 匯入 numpy 庫,並將其中的 cos、exp、pi、sqrt 函式別名為 npCos、npExp、npPi、npSqrt
from numpy import cos as npCos
from numpy import exp as npExp
from numpy import pi as npPi
from numpy import sqrt as npSqrt
# 從 pandas_ta.utils 模組中匯入 get_offset、verify_series 函式
from pandas_ta.utils import get_offset, verify_series


# 定義函式 ssf,實現 Ehler 的超平滑濾波器(SSF)
def ssf(close, length=None, poles=None, offset=None, **kwargs):
    """Indicator: Ehler's Super Smoother Filter (SSF)"""
    # 驗證引數
    # 如果 length 不為空且大於 0,則將其轉換為整數,否則設為 10
    length = int(length) if length and length > 0 else 10
    # 如果 poles 不為空且在 [2, 3] 中,則將其轉換為整數,否則設為 2
    poles = int(poles) if poles in [2, 3] else 2
    # 驗證 close 是否為有效的 Series,長度為 length
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)

    # 如果 close 為空,則返回 None
    if close is None: return

    # 計算結果
    m = close.size
    # 複製 close 到 ssf
    ssf = close.copy()

    # 根據 poles 的值進行不同的計算
    if poles == 3:
        # 計算引數
        x = npPi / length # x = PI / n
        a0 = npExp(-x) # e^(-x)
        b0 = 2 * a0 * npCos(npSqrt(3) * x) # 2e^(-x)*cos(3^(.5) * x)
        c0 = a0 * a0 # e^(-2x)

        c4 = c0 * c0 # e^(-4x)
        c3 = -c0 * (1 + b0) # -e^(-2x) * (1 + 2e^(-x)*cos(3^(.5) * x))
        c2 = c0 + b0 # e^(-2x) + 2e^(-x)*cos(3^(.5) * x)
        c1 = 1 - c2 - c3 - c4

        # 迴圈計算 ssf
        for i in range(0, m):
            ssf.iloc[i] = c1 * close.iloc[i] + c2 * ssf.iloc[i - 1] + c3 * ssf.iloc[i - 2] + c4 * ssf.iloc[i - 3]

    else: # poles == 2
        # 計算引數
        x = npPi * npSqrt(2) / length # x = PI * 2^(.5) / n
        a0 = npExp(-x) # e^(-x)
        a1 = -a0 * a0 # -e^(-2x)
        b1 = 2 * a0 * npCos(x) # 2e^(-x)*cos(x)
        c1 = 1 - a1 - b1 # e^(-2x) - 2e^(-x)*cos(x) + 1

        # 迴圈計算 ssf
        for i in range(0, m):
            ssf.iloc[i] = c1 * close.iloc[i] + b1 * ssf.iloc[i - 1] + a1 * ssf.iloc[i - 2]

    # 偏移結果
    if offset != 0:
        ssf = ssf.shift(offset)

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

    # 設定名稱和類別
    ssf.name = f"SSF_{length}_{poles}"
    ssf.category = "overlap"

    return ssf


# 設定函式 ssf 的文件字串
ssf.__doc__ = \
"""Ehler's Super Smoother Filter (SSF) © 2013

John F. Ehlers's solution to reduce lag and remove aliasing noise with his
research in aerospace analog filter design. This indicator comes with two
versions determined by the keyword poles. By default, it uses two poles but
there is an option for three poles. Since SSF is a (Resursive) Digital Filter,
the number of poles determine how many prior recursive SSF bars to include in
the design of the filter. So two poles uses two prior SSF bars and three poles
uses three prior SSF bars for their filter calculations.

Sources:
    http://www.stockspotter.com/files/PredictiveIndicators.pdf
    https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/
    https://www.mql5.com/en/code/588
    https://www.mql5.com/en/code/589

Calculation:
    Default Inputs:
        length=10, poles=[2, 3]

    See the source code or Sources listed above.

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    poles (int): The number of poles to use, either 2 or 3. Default: 2

"""
    offset (int): How many periods to offset the result. Default: 0
# 定義函式引數
Kwargs:
    # fillna 引數:用於指定 pd.DataFrame.fillna 方法中的填充值
    fillna (value, optional): pd.DataFrame.fillna(value)
    # fill_method 引數:用於指定填充方法的型別
    fill_method (value, optional): Type of fill method

# 返回值說明
Returns:
    # 返回一個 Pandas Series 物件,表示生成的新特徵
    pd.Series: New feature generated.

.\pandas-ta\pandas_ta\overlap\supertrend.py

# -*- coding: utf-8 -*-
# 匯入 numpy 庫中的 nan 函式並重新命名為 npNaN
from numpy import nan as npNaN
# 匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta 庫中匯入 overlap 模組中的 hl2 函式
from pandas_ta.overlap import hl2
# 從 pandas_ta 庫中匯入 volatility 模組中的 atr 函式
from pandas_ta.volatility import atr
# 從 pandas_ta 庫中匯入 utils 模組中的 get_offset 和 verify_series 函式
from pandas_ta.utils import get_offset, verify_series

# 定義函式 supertrend,計算 Supertrend 指標
def supertrend(high, low, close, length=None, multiplier=None, offset=None, **kwargs):
    """Indicator: Supertrend"""
    # 驗證引數
    length = int(length) if length and length > 0 else 7
    multiplier = float(multiplier) if multiplier and multiplier > 0 else 3.0
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    offset = get_offset(offset)

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

    # 計算結果
    m = close.size
    dir_, trend = [1] * m, [0] * m
    long, short = [npNaN] * m, [npNaN] * m

    hl2_ = hl2(high, low)
    matr = multiplier * atr(high, low, close, length)
    upperband = hl2_ + matr
    lowerband = hl2_ - matr

    for i in range(1, m):
        if close.iloc[i] > upperband.iloc[i - 1]:
            dir_[i] = 1
        elif close.iloc[i] < lowerband.iloc[i - 1]:
            dir_[i] = -1
        else:
            dir_[i] = dir_[i - 1]
            if dir_[i] > 0 and lowerband.iloc[i] < lowerband.iloc[i - 1]:
                lowerband.iloc[i] = lowerband.iloc[i - 1]
            if dir_[i] < 0 and upperband.iloc[i] > upperband.iloc[i - 1]:
                upperband.iloc[i] = upperband.iloc[i - 1]

        if dir_[i] > 0:
            trend[i] = long[i] = lowerband.iloc[i]
        else:
            trend[i] = short[i] = upperband.iloc[i]

    # 準備要返回的 DataFrame
    _props = f"_{length}_{multiplier}"
    df = DataFrame({
            f"SUPERT{_props}": trend,
            f"SUPERTd{_props}": dir_,
            f"SUPERTl{_props}": long,
            f"SUPERTs{_props}": short,
        }, index=close.index)

    df.name = f"SUPERT{_props}"
    df.category = "overlap"

    # 如果需要,應用偏移量
    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)

    return df

# 設定 supertrend 函式的文件字串
supertrend.__doc__ = \
"""Supertrend (supertrend)

Supertrend is an overlap indicator. It is used to help identify trend
direction, setting stop loss, identify support and resistance, and/or
generate buy & sell signals.

Sources:
    http://www.freebsensetips.com/blog/detail/7/What-is-supertrend-indicator-its-calculation

Calculation:
    Default Inputs:
        length=7, multiplier=3.0
    Default Direction:
    Set to +1 or bullish trend at start

    MID = multiplier * ATR
    LOWERBAND = HL2 - MID
    UPPERBAND = HL2 + MID

    if UPPERBAND[i] < FINAL_UPPERBAND[i-1] and close[i-1] > FINAL_UPPERBAND[i-1]:
        FINAL_UPPERBAND[i] = UPPERBAND[i]
    else:
        FINAL_UPPERBAND[i] = FINAL_UPPERBAND[i-1])
"""
    # 如果當前下軌大於前一天的最終下軌,並且前一天的收盤價小於前一天的最終下軌
    if LOWERBAND[i] > FINAL_LOWERBAND[i-1] and close[i-1] < FINAL_LOWERBAND[i-1]:
        # 將當前下軌作為最終下軌
        FINAL_LOWERBAND[i] = LOWERBAND[i]
    else:
        # 否則將前一天的最終下軌作為最終下軌
        FINAL_LOWERBAND[i] = FINAL_LOWERBAND[i-1]

    # 如果當前收盤價小於等於最終上軌
    if close[i] <= FINAL_UPPERBAND[i]:
        # 將最終上軌作為超級趨勢值
        SUPERTREND[i] = FINAL_UPPERBAND[i]
    else:
        # 否則將最終下軌作為超級趨勢值
        SUPERTREND[i] = FINAL_LOWERBAND[i]
# 定義函式引數
Args:
    high (pd.Series): 'high' 資料序列
    low (pd.Series): 'low' 資料序列
    close (pd.Series): 'close' 資料序列
    length (int) : ATR 計算的長度。預設值為 7
    multiplier (float): 上下軌距離中間範圍的係數。預設值為 3.0
    offset (int): 結果的偏移週期數。預設值為 0

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

Returns:
    pd.DataFrame: 包含 SUPERT (趨勢), SUPERTd (方向), SUPERTl (長), SUPERTs (短) 列的資料框

.\pandas-ta\pandas_ta\overlap\swma.py

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

# 從 pandas_ta.utils 模組中匯入 get_offset, symmetric_triangle, verify_series, weights 函式
from pandas_ta.utils import get_offset, symmetric_triangle, verify_series, weights

# 定義 Symmetric Weighted Moving Average (SWMA) 函式
def swma(close, length=None, asc=None, offset=None, **kwargs):
    """Indicator: Symmetric Weighted Moving Average (SWMA)"""
    
    # 驗證引數
    length = int(length) if length and length > 0 else 10
    asc = asc if asc else True
    close = verify_series(close, length)
    offset = get_offset(offset)

    if close is None: return

    # 計算結果
    triangle = symmetric_triangle(length, weighted=True)
    swma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True)

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

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

    # 設定名稱和類別
    swma.name = f"SWMA_{length}"
    swma.category = "overlap"

    return swma

# 設定 SWMA 函式的文件字串
swma.__doc__ = \
"""Symmetric Weighted Moving Average (SWMA)

Symmetric Weighted Moving Average where weights are based on a symmetric
triangle.  For example: n=3 -> [1, 2, 1], n=4 -> [1, 2, 2, 1], etc...
This moving average has variable length in contrast to TradingView's fixed
length of 4.

Source:
    https://www.tradingview.com/study-script-reference/#fun_swma

Calculation:
    Default Inputs:
        length=10

    def weights(w):
        def _compute(x):
            return np.dot(w * x)
        return _compute

    triangle = utils.symmetric_triangle(length - 1)
    SWMA = close.rolling(length)_.apply(weights(triangle), raw=True)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    asc (bool): Recent values weigh more. 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\overlap\t3.py

# -*- coding: utf-8 -*-
# 匯入 ema 函式
from .ema import ema
# 匯入 Imports 模組
from pandas_ta import Imports
# 匯入 get_offset 和 verify_series 函式
from pandas_ta.utils import get_offset, verify_series

# 定義 T3 函式,計算 T3 指標
def t3(close, length=None, a=None, talib=None, offset=None, **kwargs):
    """Indicator: T3"""
    # 驗證引數
   # 將 length 轉換為整數,如果 length 大於 0 則使用 length,否則使用預設值 10
    length = int(length) if length and length > 0 else 10
    # 將 a 轉換為浮點數,如果 a 在 0 和 1 之間則使用 a,否則使用預設值 0.7
    a = float(a) if a and a > 0 and a < 1 else 0.7
    # 驗證 close 資料型別為 Series,長度為 length
    close = verify_series(close, length)
    # 獲取 offset
    offset = get_offset(offset)
    # 判斷是否使用 talib,預設為 True
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果 close 為 None,則返回
    if close is None: return

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 如果匯入了 talib 並且 mode_tal 為 True,則使用 talib 中的 T3 函式
        from talib import T3
        t3 = T3(close, length, a)
    else:
        # 計算 T3 指標的係數
        c1 = -a * a**2
        c2 = 3 * a**2 + 3 * a**3
        c3 = -6 * a**2 - 3 * a - 3 * a**3
        c4 = a**3 + 3 * a**2 + 3 * a + 1

        # 計算 ema1 到 ema6
        e1 = ema(close=close, length=length, **kwargs)
        e2 = ema(close=e1, length=length, **kwargs)
        e3 = ema(close=e2, length=length, **kwargs)
        e4 = ema(close=e3, length=length, **kwargs)
        e5 = ema(close=e4, length=length, **kwargs)
        e6 = ema(close=e5, length=length, **kwargs)
        # 計算 T3
        t3 = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3

    # 偏移結果
    if offset != 0:
        t3 = t3.shift(offset)

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

    # 設定指標名稱和類別
    t3.name = f"T3_{length}_{a}"
    t3.category = "overlap"

    return t3

# 設定 T3 函式的文件字串
t3.__doc__ = """Tim Tillson's T3 Moving Average (T3)

Tim Tillson's T3 Moving Average is considered a smoother and more responsive
moving average relative to other moving averages.

Sources:
    http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/

Calculation:
    Default Inputs:
        length=10, a=0.7
    c1 = -a^3
    c2 = 3a^2 + 3a^3 = 3a^2 * (1 + a)
    c3 = -6a^2 - 3a - 3a^3
    c4 = a^3 + 3a^2 + 3a + 1

    ema1 = EMA(close, length)
    ema2 = EMA(ema1, length)
    ema3 = EMA(ema2, length)
    ema4 = EMA(ema3, length)
    ema5 = EMA(ema4, length)
    ema6 = EMA(ema5, length)
    T3 = c1 * ema6 + c2 * ema5 + c3 * ema4 + c4 * ema3

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    a (float): 0 < a < 1. Default: 0.7
    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:
    adjust (bool): Default: True
    presma (bool, optional): If True, uses SMA for initial value.
    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\overlap\tema.py

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

# 從 ema 模組中匯入 ema 函式
from .ema import ema
# 從 pandas_ta 模組中匯入 Imports 類
from pandas_ta import Imports
# 從 pandas_ta.utils 模組中匯入 get_offset 和 verify_series 函式
from pandas_ta.utils import get_offset, verify_series

# 定義 Triple Exponential Moving Average (TEMA) 指標函式
def tema(close, length=None, talib=None, offset=None, **kwargs):
    """Indicator: Triple Exponential Moving Average (TEMA)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則設為預設值 10
    length = int(length) if length and length > 0 else 10
    # 驗證 close 資料,並設定長度
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 判斷是否使用 talib 模式
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果 close 為空,則返回空值
    if close is None: return

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 如果匯入了 talib 模組並且使用 talib 模式,則呼叫 talib 中的 TEMA 函式
        from talib import TEMA
        tema = TEMA(close, length)
    else:
        # 否則,使用 ema 函式計算三次指數移動平均
        ema1 = ema(close=close, length=length, **kwargs)
        ema2 = ema(close=ema1, length=length, **kwargs)
        ema3 = ema(close=ema2, length=length, **kwargs)
        tema = 3 * (ema1 - ema2) + ema3

    # 偏移結果
    if offset != 0:
        tema = tema.shift(offset)

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

    # 設定指標名稱和類別
    tema.name = f"TEMA_{length}"
    tema.category = "overlap"

    return tema

# 設定 TEMA 函式的文件字串
tema.__doc__ = \
"""Triple Exponential Moving Average (TEMA)

A less laggy Exponential Moving Average.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/

Calculation:
    Default Inputs:
        length=10
    EMA = Exponential Moving Average
    ema1 = EMA(close, length)
    ema2 = EMA(ema1, length)
    ema3 = EMA(ema2, length)
    TEMA = 3 * (ema1 - ema2) + ema3

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    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:
    adjust (bool): Default: True
    presma (bool, optional): If True, uses SMA for initial value.
    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\overlap\trima.py

# -*- coding: utf-8 -*-
# 從sma模組匯入sma函式
from .sma import sma
# 從pandas_ta模組匯入Imports
from pandas_ta import Imports
# 從pandas_ta.utils模組匯入get_offset和verify_series函式
from pandas_ta.utils import get_offset, verify_series

# 定義Triangular Moving Average (TRIMA)指標函式
def trima(close, length=None, talib=None, offset=None, **kwargs):
    """Indicator: Triangular Moving Average (TRIMA)"""
    # 驗證引數
    # 如果length存在且大於0,則將其轉換為整數,否則設為10
    length = int(length) if length and length > 0 else 10
    # 驗證close序列,確保長度為length
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 判斷是否使用talib,預設為True
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果close為空,則返回空
    if close is None: return

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 如果匯入了talib並且mode_tal為True,則使用talib庫計算TRIMA
        from talib import TRIMA
        trima = TRIMA(close, length)
    else:
        # 否則,計算TRIMA的一半長度
        half_length = round(0.5 * (length + 1))
        # 計算SMA1
        sma1 = sma(close, length=half_length)
        # 計算TRIMA
        trima = sma(sma1, length=half_length)

    # 偏移結果
    if offset != 0:
        trima = trima.shift(offset)

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

    # 設定名稱和類別
    trima.name = f"TRIMA_{length}"
    trima.category = "overlap"

    return trima


# 設定TRIMA函式的文件字串
trima.__doc__ = \
"""Triangular Moving Average (TRIMA)

A weighted moving average where the shape of the weights are triangular and the
greatest weight is in the middle of the period.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/
    tma = sma(sma(src, ceil(length / 2)), floor(length / 2) + 1)  # Tradingview
    trima = sma(sma(x, n), n)  # Tradingview

Calculation:
    Default Inputs:
        length=10
    SMA = Simple Moving Average
    half_length = round(0.5 * (length + 1))
    SMA1 = SMA(close, half_length)
    TRIMA = SMA(SMA1, half_length)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    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:
    adjust (bool): Default: True
    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\overlap\vidya.py

# -*- coding: utf-8 -*-
# 匯入 numpy 庫中的 nan 並重新命名為 npNaN
from numpy import nan as npNaN
# 從 pandas 庫中匯入 Series 類
from pandas import Series
# 從 pandas_ta.utils 模組中匯入 get_drift, get_offset, verify_series 函式
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定義變數指數動態平均值(VIDYA)指標函式
def vidya(close, length=None, drift=None, offset=None, **kwargs):
    """Indicator: Variable Index Dynamic Average (VIDYA)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則設為預設值 14
    length = int(length) if length and length > 0 else 14
    # 驗證 close 是否為 Series 型別,並且長度符合要求
    close = verify_series(close, length)
    # 獲取漂移值
    drift = get_drift(drift)
    # 獲取偏移值
    offset = get_offset(offset)

    # 如果 close 為 None,則返回空
    if close is None: return

    # 定義 Chande 動量振盪器(CMO)函式
    def _cmo(source: Series, n:int , d: int):
        """Chande Momentum Oscillator (CMO) Patch
        For some reason: from pandas_ta.momentum import cmo causes
        pandas_ta.momentum.coppock to not be able to import it's
        wma like from pandas_ta.overlap import wma?
        Weird Circular TypeError!?!
        """
        # 計算動量
        mom = source.diff(d)
        # 獲取正值
        positive = mom.copy().clip(lower=0)
        # 獲取負值
        negative = mom.copy().clip(upper=0).abs()
        # 計算正值的滾動和
        pos_sum = positive.rolling(n).sum()
        # 計算負值的滾動和
        neg_sum = negative.rolling(n).sum()
        # 返回 CMO 值
        return (pos_sum - neg_sum) / (pos_sum + neg_sum)

    # 計算結果
    m = close.size
    alpha = 2 / (length + 1)
    abs_cmo = _cmo(close, length, drift).abs()
    vidya = Series(0, index=close.index)
    for i in range(length, m):
        vidya.iloc[i] = alpha * abs_cmo.iloc[i] * close.iloc[i] + vidya.iloc[i - 1] * (1 - alpha * abs_cmo.iloc[i])
    vidya.replace({0: npNaN}, inplace=True)

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

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

    # 設定名稱和類別
    vidya.name = f"VIDYA_{length}"
    vidya.category = "overlap"

    return vidya


# 設定函式文件字串
vidya.__doc__ = \
"""Variable Index Dynamic Average (VIDYA)

Variable Index Dynamic Average (VIDYA) was developed by Tushar Chande. It is
similar to an Exponential Moving Average but it has a dynamically adjusted
lookback period dependent on relative price volatility as measured by Chande
Momentum Oscillator (CMO). When volatility is high, VIDYA reacts faster to
price changes. It is often used as moving average or trend identifier.

Sources:
    https://www.tradingview.com/script/hdrf0fXV-Variable-Index-Dynamic-Average-VIDYA/
    https://www.perfecttrendsystem.com/blog_mt4_2/en/vidya-indicator-for-mt4

Calculation:
    Default Inputs:
        length=10, adjust=False, sma=True
    if sma:
        sma_nth = close[0:length].sum() / length
        close[:length - 1] = np.NaN
        close.iloc[length - 1] = sma_nth
    EMA = close.ewm(span=length, adjust=adjust).mean()

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

Kwargs:
    adjust (bool, optional): Use adjust option for EMA calculation. Default: False

"""
    # sma (bool, optional): 如果為 True,使用簡單移動平均(SMA)作為 EMA 計算的初始值。預設為 True
    sma (bool, optional): If True, uses SMA for initial value for EMA calculation. Default: True
    
    # talib (bool): 如果為 True,則使用 TA-Lib 的實現來計算 CMO。否則使用 EMA 版本。預設為 True
    talib (bool): If True, uses TA-Libs implementation for CMO. Otherwise uses EMA version. Default: True
    
    # fillna (value, optional): pd.DataFrame.fillna(value) 的引數,用於指定填充缺失值的值
    fillna (value, optional): Parameter for pd.DataFrame.fillna(value) to specify the value to fill missing values with.
    
    # fill_method (value, optional): 填充缺失值的方法型別
    fill_method (value, optional): Type of fill method.
# 返回型別宣告:pd.Series,表示生成的新特徵是一個 Pandas 的 Series 物件
pd.Series: New feature generated.

.\pandas-ta\pandas_ta\overlap\vwap.py

# -*- coding: utf-8 -*-
# 匯入依賴庫中的hlc3函式
from .hlc3 import hlc3
# 匯入輔助函式
from pandas_ta.utils import get_offset, is_datetime_ordered, verify_series

# VWAP函式定義
def vwap(high, low, close, volume, anchor=None, offset=None, **kwargs):
    """Indicator: Volume Weighted Average Price (VWAP)"""
    # 驗證引數
    high = verify_series(high)  # 驗證high序列
    low = verify_series(low)  # 驗證low序列
    close = verify_series(close)  # 驗證close序列
    volume = verify_series(volume)  # 驗證volume序列
    anchor = anchor.upper() if anchor and isinstance(anchor, str) and len(anchor) >= 1 else "D"  # 將anchor轉換為大寫字母,如果為空則預設為"D"
    offset = get_offset(offset)  # 獲取offset值

    # 計算typical_price
    typical_price = hlc3(high=high, low=low, close=close)
    # 檢查volume序列是否按時間排序
    if not is_datetime_ordered(volume):
        print(f"[!] VWAP volume series is not datetime ordered. Results may not be as expected.")
    # 檢查typical_price序列是否按時間排序
    if not is_datetime_ordered(typical_price):
        print(f"[!] VWAP price series is not datetime ordered. Results may not be as expected.")

    # 計算結果
    wp = typical_price * volume  # 計算加權價格
    vwap  = wp.groupby(wp.index.to_period(anchor)).cumsum()  # 計算累積加權價格
    vwap /= volume.groupby(volume.index.to_period(anchor)).cumsum()  # 計算累積成交量

    # 偏移
    if offset != 0:
        vwap = vwap.shift(offset)  # 偏移vwap序列

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

    # 設定名稱和類別
    vwap.name = f"VWAP_{anchor}"  # 設定VWAP的名稱
    vwap.category = "overlap"  # 設定VWAP的類別為overlap(重疊型指標)

    return vwap  # 返回VWAP序列


# VWAP文件字串
vwap.__doc__ = \
"""Volume Weighted Average Price (VWAP)

The Volume Weighted Average Price that measures the average typical price
by volume.  It is typically used with intraday charts to identify general
direction.

Sources:
    https://www.tradingview.com/wiki/Volume_Weighted_Average_Price_(VWAP)
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/volume-weighted-average-price-vwap/
    https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:vwap_intraday

Calculation:
    tp = typical_price = hlc3(high, low, close)
    tpv = tp * volume
    VWAP = tpv.cumsum() / volume.cumsum()

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series): Series of 'close's
    volume (pd.Series): Series of 'volume's
    anchor (str): How to anchor VWAP. Depending on the index values, it will
        implement various Timeseries Offset Aliases as listed here:
        https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases
        Default: "D".
    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\overlap\vwma.py

# -*- coding: utf-8 -*-
# 從sma模組中匯入sma函式
from .sma import sma
# 從pandas_ta.utils中匯入get_offset和verify_series函式
from pandas_ta.utils import get_offset, verify_series

# 定義一個名為vwma的函式,計算Volume Weighted Moving Average(VWMA)
def vwma(close, volume, length=None, offset=None, **kwargs):
    """Indicator: Volume Weighted Moving Average (VWMA)"""
    # 驗證引數
    # 將length轉換為整數,如果length存在且大於0,否則設為10
    length = int(length) if length and length > 0 else 10
    # 驗證close序列,長度為length
    close = verify_series(close, length)
    # 驗證volume序列,長度為length
    volume = verify_series(volume, length)
    # 獲取偏移量
    offset = get_offset(offset)

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

    # 計算結果
    # 計算pv(close * volume)
    pv = close * volume
    # 計算vwma(pv的SMA / volume的SMA)
    vwma = sma(close=pv, length=length) / sma(close=volume, length=length)

    # 偏移結果
    if offset != 0:
        # 偏移vwma
        vwma = vwma.shift(offset)

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

    # 名稱和類別
    # 設定vwma的名稱為"VWMA_長度"
    vwma.name = f"VWMA_{length}"
    # 設定vwma的類別為"overlap"
    vwma.category = "overlap"

    return vwma

# 設定vwma函式的文件字串
vwma.__doc__ = \
"""Volume Weighted Moving Average (VWMA)

Volume Weighted Moving Average.

Sources:
    https://www.motivewave.com/studies/volume_weighted_moving_average.htm

Calculation:
    Default Inputs:
        length=10
    SMA = Simple Moving Average
    pv = close * volume
    VWMA = SMA(pv, length) / SMA(volume, length)

Args:
    close (pd.Series): Series of 'close's
    volume (pd.Series): Series of 'volume's
    length (int): It's period. Default: 10
    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.
"""

相關文章