PandasTA 原始碼解析(十三)

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

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

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

# 定義一個名為 decreasing 的函式,用於計算序列是否遞減
def decreasing(close, length=None, strict=None, asint=None, percent=None, drift=None, offset=None, **kwargs):
    """Indicator: Decreasing"""
    # 驗證引數
    # 如果 length 引數存在且大於 0,則將其轉換為整數,否則設為 1
    length = int(length) if length and length > 0 else 1
    # 如果 strict 引數是布林型別,則保持不變,否則設為 False
    strict = strict if isinstance(strict, bool) else False
    # 如果 asint 引數是布林型別,則保持不變,否則設為 True
    asint = asint if isinstance(asint, bool) else True
    # 對 close 序列進行驗證,並設定長度為 length
    close = verify_series(close, length)
    # 獲取 drift 和 offset 引數的值
    drift = get_drift(drift)
    offset = get_offset(offset)
    # 如果 percent 引數是百分比,則將其轉換為浮點數,否則設為 False
    percent = float(percent) if is_percent(percent) else False

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

    # 計算結果
    # 如果 percent 存在,則對 close 序列進行縮放
    close_ = (1 - 0.01 * percent) * close if percent else close
    # 如果 strict 為 True,則進行嚴格遞減的計算
    if strict:
        # 使用迴圈檢查連續遞減的情況
        decreasing = close < close_.shift(drift)
        for x in range(3, length + 1):
            decreasing = decreasing & (close.shift(x - (drift + 1)) < close_.shift(x - drift))

        # 填充缺失值為 0,並將結果轉換為布林型別
        decreasing.fillna(0, inplace=True)
        decreasing = decreasing.astype(bool)
    else:
        # 否則,使用簡單的遞減計算
        decreasing = close_.diff(length) < 0

    # 如果 asint 為 True,則將結果轉換為整數
    if asint:
        decreasing = decreasing.astype(int)

    # 對結果進行偏移
    if offset != 0:
        decreasing = decreasing.shift(offset)

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

    # 設定結果的名稱和類別
    _percent = f"_{0.01 * percent}" if percent else ''
    _props = f"{'S' if strict else ''}DEC{'p' if percent else ''}"
    decreasing.name = f"{_props}_{length}{_percent}"
    decreasing.category = "trend"

    return decreasing

# 為 decreasing 函式新增文件字串
decreasing.__doc__ = \
"""Decreasing

Returns True if the series is decreasing over a period, False otherwise.
If the kwarg 'strict' is True, it returns True if it is continuously decreasing
over the period. When using the kwarg 'asint', then it returns 1 for True
or 0 for False.

Calculation:
    if strict:
        decreasing = all(i > j for i, j in zip(close[-length:], close[1:]))
    else:
        decreasing = close.diff(length) < 0

    if asint:
        decreasing = decreasing.astype(int)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 1
    strict (bool): If True, checks if the series is continuously decreasing over the period. Default: False
    percent (float): Percent as an integer. Default: None
    asint (bool): Returns as binary. Default: True
    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:
    pd.Series: New feature generated.
"""

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

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


def dpo(close, length=None, centered=True, offset=None, **kwargs):
    """Indicator: Detrend Price Oscillator (DPO)"""
    # 驗證引數
    # 將長度引數轉換為整數,如果長度引數存在且大於0,則使用,否則預設為20
    length = int(length) if length and length > 0 else 20
    # 驗證 close 是否為有效的時間序列,長度為指定的長度
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 如果 kwargs 中的 "lookahead" 為 False,則將 centered 設定為 False
    if not kwargs.get("lookahead", True):
        centered = False

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

    # 計算結果
    # 計算 t,即 int(0.5 * length) + 1
    t = int(0.5 * length) + 1
    # 計算 close 的簡單移動平均
    ma = sma(close, length)

    # 計算 DPO,close 減去 ma 後向前位移 t 個週期
    dpo = close - ma.shift(t)
    # 如果 centered 為 True,則再將 DPO 向後位移 t 個週期
    if centered:
        dpo = (close.shift(t) - ma).shift(-t)

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

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

    # 命名和分類
    dpo.name = f"DPO_{length}"
    dpo.category = "trend"

    return dpo


# 更新文件字串
dpo.__doc__ = \
"""Detrend Price Oscillator (DPO)

Is an indicator designed to remove trend from price and make it easier to
identify cycles.

Sources:
    https://www.tradingview.com/scripts/detrendedpriceoscillator/
    https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/dpo
    http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:detrended_price_osci

Calculation:
    Default Inputs:
        length=20, centered=True
    SMA = Simple Moving Average
    t = int(0.5 * length) + 1

    DPO = close.shift(t) - SMA(close, length)
    if centered:
        DPO = DPO.shift(-t)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 1
    centered (bool): Shift the dpo back by int(0.5 * length) + 1. 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\trend\increasing.py

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

# 定義名為 increasing 的函式,用於計算序列是否遞增
def increasing(close, length=None, strict=None, asint=None, percent=None, drift=None, offset=None, **kwargs):
    """Indicator: Increasing"""
    # 驗證引數的有效性
    length = int(length) if length and length > 0 else 1  # 將長度轉換為整數,如果未提供或小於等於0,則設為1
    strict = strict if isinstance(strict, bool) else False  # 如果 strict 不是布林值,則設為 False
    asint = asint if isinstance(asint, bool) else True  # 如果 asint 不是布林值,則設為 True
    close = verify_series(close, length)  # 驗證並處理輸入的序列資料
    drift = get_drift(drift)  # 獲取漂移值
    offset = get_offset(offset)  # 獲取偏移值
    percent = float(percent) if is_percent(percent) else False  # 將百分比轉換為浮點數,如果不是百分比,則設為 False

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

    # 計算結果
    close_ = (1 + 0.01 * percent) * close if percent else close  # 如果有百分比引數,則對序列進行調整
    if strict:
        # 返回值是否為 float64?必須轉換為布林值
        increasing = close > close_.shift(drift)  # 檢查當前值是否大於移動後的值
        for x in range(3, length + 1):
            increasing = increasing & (close.shift(x - (drift + 1)) > close_.shift(x - drift))  # 檢查連續多個值是否遞增

        increasing.fillna(0, inplace=True)  # 填充缺失值為0
        increasing = increasing.astype(bool)  # 將結果轉換為布林值
    else:
        increasing = close_.diff(length) > 0  # 檢查序列是否在給定週期內遞增

    if asint:
        increasing = increasing.astype(int)  # 將結果轉換為整數型別

    # 偏移結果
    if offset != 0:
        increasing = increasing.shift(offset)  # 對結果進行偏移

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

    # 命名並分類結果
    _percent = f"_{0.01 * percent}" if percent else ''  # 根據是否存在百分比引數構建字尾
    _props = f"{'S' if strict else ''}INC{'p' if percent else ''}"  # 根據引數構建特性標識
    increasing.name = f"{_props}_{length}{_percent}"  # 構建結果的名稱
    increasing.category = "trend"  # 將結果分類為趨勢

    return increasing  # 返回計算結果


increasing.__doc__ = \
"""Increasing

Returns True if the series is increasing over a period, False otherwise.
If the kwarg 'strict' is True, it returns True if it is continuously increasing
over the period. When using the kwarg 'asint', then it returns 1 for True
or 0 for False.

Calculation:
    if strict:
        increasing = all(i < j for i, j in zip(close[-length:], close[1:]))
    else:
        increasing = close.diff(length) > 0

    if asint:
        increasing = increasing.astype(int)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 1
    strict (bool): If True, checks if the series is continuously increasing over the period. Default: False
    percent (float): Percent as an integer. Default: None
    asint (bool): Returns as binary. Default: True
    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:
    pd.Series: New feature generated.
"""

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

# -*- coding: utf-8 -*-
# 匯入必要的模組和函式
from .decreasing import decreasing  # 從當前目錄下的 decreasing 模組匯入 decreasing 函式
from .increasing import increasing  # 從當前目錄下的 increasing 模組匯入 increasing 函式
from pandas_ta.utils import get_offset, verify_series  # 從 pandas_ta.utils 模組匯入 get_offset 和 verify_series 函式


def long_run(fast, slow, length=None, offset=None, **kwargs):
    """Indicator: Long Run"""
    # Validate Arguments
    # 將 length 轉換為整數,如果 length 存在且大於 0;否則預設為 2
    length = int(length) if length and length > 0 else 2
    # 驗證 fast 和 slow 是否為有效的序列,並將其長度限制為 length
    fast = verify_series(fast, length)
    slow = verify_series(slow, length)
    # 獲取偏移量
    offset = get_offset(offset)

    # 如果 fast 或 slow 為空,則返回空值
    if fast is None or slow is None: return

    # Calculate Result
    # 計算可能的底部或底部的條件,即 fast 增長而 slow 減小
    pb = increasing(fast, length) & decreasing(slow, length)
    # 計算 fast 和 slow 同時增長的條件
    bi = increasing(fast, length) & increasing(slow, length)
    # 計算長期趨勢的條件,可能的底部或底部,以及 fast 和 slow 同時增長的情況
    long_run = pb | bi

    # Offset
    # 如果 offset 不為 0,則對長期趨勢進行偏移
    if offset != 0:
        long_run = long_run.shift(offset)

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

    # Name and Categorize it
    # 設定長期趨勢指標的名稱
    long_run.name = f"LR_{length}"
    # 設定長期趨勢指標的類別為 "trend"
    long_run.category = "trend"

    return long_run

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

# -*- coding: utf-8 -*-
# 匯入 numpy 庫中的 nan 作為 npNaN
from numpy import nan as npNaN
# 匯入 DataFrame 和 Series 類
from pandas import DataFrame, Series
# 匯入 get_offset, verify_series, zero 函式
from pandas_ta.utils import get_offset, verify_series, zero

# 定義 PSAR 指標函式,引數包括 high, low, close, af0, af, max_af, offset
def psar(high, low, close=None, af0=None, af=None, max_af=None, offset=None, **kwargs):
    """Indicator: Parabolic Stop and Reverse (PSAR)"""
    # 驗證引數
    high = verify_series(high)
    low = verify_series(low)
    af = float(af) if af and af > 0 else 0.02
    af0 = float(af0) if af0 and af0 > 0 else af
    max_af = float(max_af) if max_af and max_af > 0 else 0.2
    offset = get_offset(offset)

    # 定義 _falling 函式,用於返回最後一個 -DM 值
    def _falling(high, low, drift:int=1):
        """Returns the last -DM value"""
        # 不要與 ta.falling() 混淆
        up = high - high.shift(drift)
        dn = low.shift(drift) - low
        _dmn = (((dn > up) & (dn > 0)) * dn).apply(zero).iloc[-1]
        return _dmn > 0

    # 如果第一個 NaN -DM 為正,則為下降趨勢
    falling = _falling(high.iloc[:2], low.iloc[:2])
    if falling:
        sar = high.iloc[0]
        ep = low.iloc[0]
    else:
        sar = low.iloc[0]
        ep = high.iloc[0]

    # 如果存在 close 引數,則使用 close 的值
    if close is not None:
        close = verify_series(close)
        sar = close.iloc[0]

    # 初始化 long, short, reversal, _af
    long = Series(npNaN, index=high.index)
    short = long.copy()
    reversal = Series(0, index=high.index)
    _af = long.copy()
    _af.iloc[0:2] = af0

    # 計算結果
    m = high.shape[0]
    for row in range(1, m):
        high_ = high.iloc[row]
        low_ = low.iloc[row]

        if falling:
            _sar = sar + af * (ep - sar)
            reverse = high_ > _sar

            if low_ < ep:
                ep = low_
                af = min(af + af0, max_af)

            _sar = max(high.iloc[row - 1], high.iloc[row - 2], _sar)
        else:
            _sar = sar + af * (ep - sar)
            reverse = low_ < _sar

            if high_ > ep:
                ep = high_
                af = min(af + af0, max_af)

            _sar = min(low.iloc[row - 1], low.iloc[row - 2], _sar)

        if reverse:
            _sar = ep
            af = af0
            falling = not falling
            ep = low_ if falling else high_

        sar = _sar

        if falling:
            short.iloc[row] = sar
        else:
            long.iloc[row] = sar

        _af.iloc[row] = af
        reversal.iloc[row] = int(reverse)

    # 偏移
    if offset != 0:
        _af = _af.shift(offset)
        long = long.shift(offset)
        short = short.shift(offset)
        reversal = reversal.shift(offset)

    # 處理填充
    if "fillna" in kwargs:
        _af.fillna(kwargs["fillna"], inplace=True)
        long.fillna(kwargs["fillna"], inplace=True)
        short.fillna(kwargs["fillna"], inplace=True)
        reversal.fillna(kwargs["fillna"], inplace=True)
    # 檢查引數中是否包含'fill_method'關鍵字
    if "fill_method" in kwargs:
        # 使用指定的填充方法填充資料,直接修改原資料,無需返回值
        _af.fillna(method=kwargs["fill_method"], inplace=True)
        long.fillna(method=kwargs["fill_method"], inplace=True)
        short.fillna(method=kwargs["fill_method"], inplace=True)
        reversal.fillna(method=kwargs["fill_method"], inplace=True)

    # 準備要返回的 DataFrame
    # 根據引數構建用於命名列的字串
    _params = f"_{af0}_{max_af}"
    # 構建包含 PSAR 資料的字典
    data = {
        f"PSARl{_params}": long,
        f"PSARs{_params}": short,
        f"PSARaf{_params}": _af,
        f"PSARr{_params}": reversal,
    }
    # 從字典建立 DataFrame
    psardf = DataFrame(data)
    # 設定 DataFrame 的名稱
    psardf.name = f"PSAR{_params}"
    # 設定 DataFrame 及其列的類別屬性為 'trend'
    psardf.category = long.category = short.category = "trend"

    # 返回 PSAR 資料的 DataFrame
    return psardf
# 設定 psar 函式的文件字串,用於描述 Parabolic Stop and Reverse (PSAR) 指標的作用、計算方式和引數說明
psar.__doc__ = \
"""Parabolic Stop and Reverse (psar)

Parabolic Stop and Reverse (PSAR) was developed by J. Wells Wilder, that is used
to determine trend direction and it's potential reversals in price. PSAR uses a
trailing stop and reverse method called "SAR," or stop and reverse, to identify
possible entries and exits. It is also known as SAR.

PSAR indicator typically appears on a chart as a series of dots, either above or
below an asset's price, depending on the direction the price is moving. A dot is
placed below the price when it is trending upward, and above the price when it
is trending downward.

Sources:
    https://www.tradingview.com/pine-script-reference/#fun_sar
    https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=66&Name=Parabolic

Calculation:
    Default Inputs:
        af0=0.02, af=0.02, max_af=0.2

    See Source links

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series, optional): Series of 'close's. Optional
    af0 (float): Initial Acceleration Factor. Default: 0.02
    af (float): Acceleration Factor. Default: 0.02
    max_af (float): Maximum Acceleration Factor. Default: 0.2
    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.DataFrame: long, short, af, and reversal columns.
"""

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

# -*- coding: utf-8 -*-
# 從 pandas_ta.overlap 模組匯入 dema, ema, hma, rma, sma 函式
from pandas_ta.overlap import dema, ema, hma, rma, sma
# 從 pandas_ta.utils 模組匯入 get_offset, non_zero_range, verify_series 函式
from pandas_ta.utils import get_offset, non_zero_range, verify_series

# 定義 Q Stick 指標函式
def qstick(open_, close, length=None, offset=None, **kwargs):
    """Indicator: Q Stick"""
    # 驗證引數
    length = int(length) if length and length > 0 else 10
    ma = kwargs.pop("ma", "sma")
    open_ = verify_series(open_, length)
    close = verify_series(close, length)
    offset = get_offset(offset)

    if open_ is None or close is None: return

    # 計算結果
    diff = non_zero_range(close, open_)

    if ma == "dema":
        qstick = dema(diff, length=length, **kwargs)
    elif ma == "ema":
        qstick = ema(diff, length=length, **kwargs)
    elif ma == "hma":
        qstick = hma(diff, length=length)
    elif ma == "rma":
        qstick = rma(diff, length=length)
    else: # "sma"
        qstick = sma(diff, length=length)

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

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

    # 命名和分類
    qstick.name = f"QS_{length}"
    qstick.category = "trend"

    return qstick

# 設定 Q Stick 函式的文件字串
qstick.__doc__ = \
"""Q Stick

The Q Stick indicator, developed by Tushar Chande, attempts to quantify and
identify trends in candlestick charts.

Sources:
    https://library.tradingtechnologies.com/trade/chrt-ti-qstick.html

Calculation:
    Default Inputs:
        length=10
    xMA is one of: sma (default), dema, ema, hma, rma
    qstick = xMA(close - open, length)

Args:
    open (pd.Series): Series of 'open's
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 1
    ma (str): The type of moving average to use. Default: None, which is 'sma'
    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\trend\short_run.py

# -*- coding: utf-8 -*-
# 匯入必要的模組和函式
from .decreasing import decreasing  # 從當前目錄下的 decreasing 模組中匯入 decreasing 函式
from .increasing import increasing  # 從當前目錄下的 increasing 模組中匯入 increasing 函式
from pandas_ta.utils import get_offset, verify_series  # 從 pandas_ta 包中的 utils 模組匯入 get_offset 和 verify_series 函式

# 定義一個名為 short_run 的函式,用於計算短期趨勢
def short_run(fast, slow, length=None, offset=None, **kwargs):
    """Indicator: Short Run"""
    # 驗證引數
    length = int(length) if length and length > 0 else 2  # 如果 length 存在且大於0,則將其轉換為整數型別,否則設為預設值2
    fast = verify_series(fast, length)  # 驗證 fast 引數,確保其為有效的序列,並可能調整長度
    slow = verify_series(slow, length)  # 驗證 slow 引數,確保其為有效的序列,並可能調整長度
    offset = get_offset(offset)  # 獲取偏移量

    if fast is None or slow is None: return  # 如果 fast 或 slow 為空,則返回空值

    # 計算結果
    pt = decreasing(fast, length) & increasing(slow, length)  # 潛在頂部或頂部的條件
    bd = decreasing(fast, length) & decreasing(slow, length)  # 快速和慢速都在下降的條件
    short_run = pt | bd  # 判斷是否出現短期趨勢

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

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

    # 命名和分類
    short_run.name = f"SR_{length}"  # 設定結果的名稱
    short_run.category = "trend"  # 設定結果的分類為趨勢

    return short_run  # 返回計算結果

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

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

# 從 pandas 庫匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta.utils 模組匯入 get_drift, get_offset, verify_series 函式
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定義 tsignals 函式,用於計算趨勢訊號
def tsignals(trend, asbool=None, trend_reset=0, trade_offset=None, drift=None, offset=None, **kwargs):
    """Indicator: Trend Signals"""
    # 驗證引數
    # 將 trend 引數轉換為 pandas Series 物件
    trend = verify_series(trend)
    # 將 asbool 引數轉換為布林值,預設為 False
    asbool = bool(asbool) if isinstance(asbool, bool) else False
    # 將 trend_reset 引數轉換為整數,預設為 0
    trend_reset = int(trend_reset) if trend_reset and isinstance(trend_reset, int) else 0
    # 如果 trade_offset 不為 0,則將其轉換為整數,預設為 0
    if trade_offset != 0:
        trade_offset = int(trade_offset) if trade_offset and isinstance(trade_offset, int) else 0
    # 獲取 drift 和 offset 引數的值
    drift = get_drift(drift)
    offset = get_offset(offset)

    # 計算結果
    # 將趨勢值轉換為整數型別
    trends = trend.astype(int)
    # 計算交易訊號
    trades = trends.diff(drift).shift(trade_offset).fillna(0).astype(int)
    # 計算進入交易的訊號
    entries = (trades > 0).astype(int)
    # 計算退出交易的訊號
    exits = (trades < 0).abs().astype(int)

    # 如果 asbool 為 True,則將結果轉換為布林值
    if asbool:
        trends = trends.astype(bool)
        entries = entries.astype(bool)
        exits = exits.astype(bool)

    # 構建結果資料
    data = {
        f"TS_Trends": trends,
        f"TS_Trades": trades,
        f"TS_Entries": entries,
        f"TS_Exits": exits,
    }
    # 建立 DataFrame 物件
    df = DataFrame(data, index=trends.index)

    # 處理偏移
    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)

    # 設定名稱和類別
    df.name = f"TS"
    df.category = "trend"

    return df

# 設定 tsignals 函式的文件字串
tsignals.__doc__ = \
"""Trend Signals

Given a Trend, Trend Signals returns the Trend, Trades, Entries and Exits as
boolean integers. When 'asbool=True', it returns Trends, Entries and Exits as
boolean values which is helpful when combined with the vectorbt backtesting
package.

A Trend can be a simple as: 'close' > 'moving average' or something more complex
whose values are boolean or integers (0 or 1).

Examples:
ta.tsignals(close > ta.sma(close, 50), asbool=False)
ta.tsignals(ta.ema(close, 8) > ta.ema(close, 21), asbool=True)

Source: Kevin Johnson

Calculation:
    Default Inputs:
        asbool=False, trend_reset=0, trade_offset=0, drift=1

    trades = trends.diff().shift(trade_offset).fillna(0).astype(int)
    entries = (trades > 0).astype(int)
    exits = (trades < 0).abs().astype(int)

Args:
    trend (pd.Series): Series of 'trend's. The trend can be either a boolean or
        integer series of '0's and '1's
    asbool (bool): If True, it converts the Trends, Entries and Exits columns to
        booleans. When boolean, it is also useful for backtesting with
        vectorbt's Portfolio.from_signal(close, entries, exits) Default: False
    trend_reset (value): Value used to identify if a trend has ended. Default: 0
    trade_offset (value): Value used shift the trade entries/exits Use 1 for
        backtesting and 0 for live. Default: 0
    drift (int): The difference period. Default: 1

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

# 返回值說明部分,描述了函式返回的 DataFrame 的列
Returns:
    # 返回一個 pandas DataFrame,包含以下列:
    pd.DataFrame with columns:
    # Trends 列,表示趨勢,有趨勢為 1,無趨勢為 0
    Trends (trend: 1, no trend: 0),
    # Trades 列,表示交易,進入為 1,退出為 -1,其他情況為 0
    Trades (Enter: 1, Exit: -1, Otherwise: 0),
    # Entries 列,表示進入,進入為 1,無操作為 0
    Entries (entry: 1, nothing: 0),
    # Exits 列,表示退出,退出為 1,無操作為 0
    Exits (exit: 1, nothing: 0)

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

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

# 從 pandas 庫匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta.overlap 模組匯入 hl2 函式
from pandas_ta.overlap import hl2
# 從 pandas_ta.utils 模組匯入 get_offset 和 verify_series 函式
from pandas_ta.utils import get_offset, verify_series


# 定義 ttm_trend 函式,用於計算 TTM Trend 指標
def ttm_trend(high, low, close, length=None, offset=None, **kwargs):
    """Indicator: TTM Trend (TTM_TRND)"""

    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則將其設定為預設值 6
    length = int(length) if length and length > 0 else 6
    # 驗證 high、low、close 資料是否為 Series,並確保它們的長度為 length
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    # 獲取 offset 的偏移量
    offset = get_offset(offset)

    # 如果 high、low、close 有任何一個為 None,則返回空
    if high is None or low is None or close is None: return

    # 計算結果
    # 計算中間價(hl2)的平均值
    trend_avg = hl2(high, low)
    for i in range(1, length):
        trend_avg = trend_avg + hl2(high.shift(i), low.shift(i))

    trend_avg = trend_avg / length

    # 計算 ttm_trend,如果 close 大於 trend_avg,則設定為 1,否則設定為 -1
    tm_trend = (close > trend_avg).astype(int)
    tm_trend.replace(0, -1, inplace=True)

    # 調整結果的偏移量
    if offset != 0:
        tm_trend = tm_trend.shift(offset)

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

    # 設定名稱和類別
    tm_trend.name = f"TTM_TRND_{length}"
    tm_trend.category = "momentum"

    # 準備返回的 DataFrame
    data = {tm_trend.name: tm_trend}
    df = DataFrame(data)
    df.name = f"TTMTREND_{length}"
    df.category = tm_trend.category

    return df


# 設定 ttm_trend 函式的文件字串
ttm_trend.__doc__ = \
"""TTM Trend (TTM_TRND)

This indicator is from John Carters book “Mastering the Trade” and plots the
bars green or red. It checks if the price is above or under the average price of
the previous 5 bars. The indicator should hep you stay in a trade until the
colors chance. Two bars of the opposite color is the signal to get in or out.

Sources:
    https://www.prorealcode.com/prorealtime-indicators/ttm-trend-price/

Calculation:
    Default Inputs:
        length=6
    averageprice = (((high[5]+low[5])/2)+((high[4]+low[4])/2)+((high[3]+low[3])/2)+
    ((high[2]+low[2])/2)+((high[1]+low[1])/2)+((high[6]+low[6])/2)) / 6

    if close > averageprice:
        drawcandle(open,high,low,close) coloured(0,255,0)

    if close < averageprice:
        drawcandle(open,high,low,close) coloured(255,0,0)

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: 6
    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.DataFrame: ttm_trend.
"""

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

# -*- coding: utf-8 -*-
# 匯入 fabs 函式並重新命名為 npFabs
from numpy import fabs as npFabs
# 從 pandas_ta.utils 模組中匯入 get_drift、get_offset、non_zero_range、verify_series 函式
from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series


# 定義垂直水平過濾器(VHF)指標函式
def vhf(close, length=None, drift=None, offset=None, **kwargs):
    """Indicator: Vertical Horizontal Filter (VHF)"""
    # 驗證引數
    # 將長度轉換為整數,如果長度大於0,則為指定的長度,否則為預設值28
    length = int(length) if length and length > 0 else 28
    # 驗證收盤價序列,長度為指定的長度
    close = verify_series(close, length)
    # 獲取漂移值
    drift = get_drift(drift)
    # 獲取偏移值
    offset = get_offset(offset)

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

    # 計算結果
    # 最高收盤價
    hcp = close.rolling(length).max()
    # 最低收盤價
    lcp = close.rolling(length).min()
    # 收盤價變化的絕對值
    diff = npFabs(close.diff(drift))
    # 垂直水平過濾器值
    vhf = npFabs(non_zero_range(hcp, lcp)) / diff.rolling(length).sum()

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

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

    # 命名和分類
    vhf.name = f"VHF_{length}"
    vhf.category = "trend"

    return vhf


# 設定 VHF 函式的文件字串
vhf.__doc__ = \
"""Vertical Horizontal Filter (VHF)

VHF was created by Adam White to identify trending and ranging markets.

Sources:
    https://www.incrediblecharts.com/indicators/vertical_horizontal_filter.php

Calculation:
    Default Inputs:
        length = 28
    HCP = Highest Close Price in Period
    LCP = Lowest Close Price in Period
    Change = abs(Ct - Ct-1)
    VHF = (HCP - LCP) / RollingSum[length] of Change

Args:
    source (pd.Series): Series of prices (usually close).
    length (int): The period length. Default: 28
    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\trend\vortex.py

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

# 從 pandas 庫匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta.volatility 模組匯入 true_range 函式
from pandas_ta.volatility import true_range
# 從 pandas_ta.utils 模組匯入 get_drift, get_offset, verify_series 函式
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定義函式 vortex,用於計算 Vortex 指標
def vortex(high, low, close, length=None, drift=None, offset=None, **kwargs):
    """Indicator: Vortex"""
    # 驗證引數
    length = length if length and length > 0 else 14
    min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
    _length = max(length, min_periods)
    # 驗證並處理輸入的 high、low、close 資料
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    close = verify_series(close, _length)
    drift = get_drift(drift)  # 獲取 drift 引數
    offset = get_offset(offset)  # 獲取 offset 引數

    # 若輸入資料中有空值,則返回空
    if high is None or low is None or close is None: return

    # 計算結果
    tr = true_range(high=high, low=low, close=close)  # 計算真實範圍
    tr_sum = tr.rolling(length, min_periods=min_periods).sum()  # 對真實範圍進行滾動求和

    # 計算正向運動價格動量 (VMP) 和反向運動價格動量 (VMM)
    vmp = (high - low.shift(drift)).abs()
    vmm = (low - high.shift(drift)).abs()

    # 計算正向和反向運動指標 (VIP 和 VIM)
    vip = vmp.rolling(length, min_periods=min_periods).sum() / tr_sum
    vim = vmm.rolling(length, min_periods=min_periods).sum() / tr_sum

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

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

    # 命名並分類化指標
    vip.name = f"VTXP_{length}"
    vim.name = f"VTXM_{length}"
    vip.category = vim.category = "trend"

    # 準備返回的 DataFrame
    data = {vip.name: vip, vim.name: vim}
    vtxdf = DataFrame(data)
    vtxdf.name = f"VTX_{length}"
    vtxdf.category = "trend"

    return vtxdf


# 設定函式文件字串
vortex.__doc__ = \
"""Vortex

Two oscillators that capture positive and negative trend movement.

Sources:
    https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:vortex_indicator

Calculation:
    Default Inputs:
        length=14, drift=1
    TR = True Range
    SMA = Simple Moving Average
    tr = TR(high, low, close)
    tr_sum = tr.rolling(length).sum()

    vmp = (high - low.shift(drift)).abs()
    vmn = (low - high.shift(drift)).abs()

    VIP = vmp.rolling(length).sum() / tr_sum
    VIM = vmn.rolling(length).sum() / tr_sum

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series): Series of 'close's
    length (int): ROC 1 period. Default: 14
    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:
    pd.DataFrame: vip and vim columns
"""

相關文章