PandasTA 原始碼解析(七)

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

.\pandas-ta\pandas_ta\momentum\stc.py

# -*- coding: utf-8 -*-
從 pandas 庫中匯入 DataFrame 和 Series 類
從 pandas_ta.overlap 模組中匯入 ema 函式
從 pandas_ta.utils 模組中匯入 get_offset、non_zero_range 和 verify_series 函式


# 定義函式:Schaff Trend Cycle (STC)
def stc(close, tclength=None, fast=None, slow=None, factor=None, offset=None, **kwargs):
    # 驗證引數
    tclength = int(tclength) if tclength and tclength > 0 else 10
    fast = int(fast) if fast and fast > 0 else 12
    slow = int(slow) if slow and slow > 0 else 26
    factor = float(factor) if factor and factor > 0 else 0.5
    # 如果慢線小於快線,則交換它們的值
    if slow < fast:                
        fast, slow = slow, fast
    # 計算所需資料的長度,取最大值
    _length = max(tclength, fast, slow)
    # 驗證收盤價資料,返回驗證後的 Series 物件
    close = verify_series(close, _length)
    # 獲取偏移量
    offset = get_offset(offset)

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

    # kwargs 允許傳遞三個更多的 Series(ma1、ma2 和 osc),這些可以在這裡傳遞,
    # ma1 和 ma2 輸入會抵消內部的 ema 計算,osc 替代了兩個 ma。
    ma1 = kwargs.pop("ma1", False)
    ma2 = kwargs.pop("ma2", False)
    osc = kwargs.pop("osc", False)

    # 3 種不同的計算模式..
    if isinstance(ma1, Series) and isinstance(ma2, Series) and not osc:
        # 驗證輸入的兩個外部 Series 物件
        ma1 = verify_series(ma1, _length)
        ma2 = verify_series(ma2, _length)

        # 如果其中一個為 None,則返回
        if ma1 is None or ma2 is None: return
        # 根據外部提供的 Series 計算結果
        xmacd = ma1 - ma2
        # 呼叫共享計算函式
        pff, pf = schaff_tc(close, xmacd, tclength, factor)

    elif isinstance(osc, Series):
        # 驗證輸入的振盪器 Series 物件
        osc = verify_series(osc, _length)
        # 如果為 None,則返回
        if osc is None: return
        # 根據提供的振盪器計算結果(應在 0 軸附近)
        xmacd = osc
        # 呼叫共享計算函式
        pff, pf = schaff_tc(close, xmacd, tclength, factor)

    else:
        # 計算結果..(傳統/完整)
        # MACD 線
        fastma = ema(close, length=fast)
        slowma = ema(close, length=slow)
        xmacd = fastma - slowma
        # 呼叫共享計算函式
        pff, pf = schaff_tc(close, xmacd, tclength, factor)

    # 結果 Series
    stc = Series(pff, index=close.index)
    macd = Series(xmacd, index=close.index)
    stoch = Series(pf, index=close.index)

    # 偏移
    if offset != 0:
        stc = stc.shift(offset)
        macd = macd.shift(offset)
        stoch = stoch.shift(offset)

    # 填充缺失值
    if "fillna" in kwargs:
        stc.fillna(kwargs["fillna"], inplace=True)
        macd.fillna(kwargs["fillna"], inplace=True)
        stoch.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        stc.fillna(method=kwargs["fill_method"], inplace=True)
        macd.fillna(method=kwargs["fill_method"], inplace=True)
        stoch.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分類
    _props = f"_{tclength}_{fast}_{slow}_{factor}"
    stc.name = f"STC{_props}"
    macd.name = f"STCmacd{_props}"
    # 設定 stoch 物件的名稱屬性為包含 _props 的字串
    stoch.name = f"STCstoch{_props}"
    # 設定 stc 和 macd 物件的 category 屬性為 "momentum"
    stc.category = macd.category = stoch.category ="momentum"

    # 準備要返回的 DataFrame
    # 建立一個字典,包含 stc.name、macd.name 和 stoch.name 作為鍵,對應的物件為值
    data = {stc.name: stc, macd.name: macd, stoch.name: stoch}
    # 用 data 字典建立 DataFrame 物件
    df = DataFrame(data)
    # 設定 DataFrame 物件的名稱屬性為包含 _props 的字串
    df.name = f"STC{_props}"
    # 設定 DataFrame 物件的 category 屬性為 stc 物件的 category 屬性
    df.category = stc.category

    # 返回 DataFrame 物件
    return df
# 設定 stc 的文件字串,描述 Schaff Trend Cycle(STC)指標的計算方法和用法
stc.__doc__ = \
"""Schaff Trend Cycle (STC)

The Schaff Trend Cycle is an evolution of the popular MACD incorportating two
cascaded stochastic calculations with additional smoothing.

The STC returns also the beginning MACD result as well as the result after the
first stochastic including its smoothing. This implementation has been extended
for Pandas TA to also allow for separatly feeding any other two moving Averages
(as ma1 and ma2) or to skip this to feed an oscillator (osc), based on which the
Schaff Trend Cycle should be calculated.

Feed external moving averages:
Internally calculation..
    stc = ta.stc(close=df["close"], tclen=stc_tclen, fast=ma1_interval, slow=ma2_interval, factor=stc_factor)
becomes..
    extMa1 = df.ta.zlma(close=df["close"], length=ma1_interval, append=True)
    extMa2 = df.ta.ema(close=df["close"], length=ma2_interval, append=True)
    stc = ta.stc(close=df["close"], tclen=stc_tclen, ma1=extMa1, ma2=extMa2, factor=stc_factor)

The same goes for osc=, which allows the input of an externally calculated oscillator, overriding ma1 & ma2.


Sources:
    Implemented by rengel8 based on work found here:
    https://www.prorealcode.com/prorealtime-indicators/schaff-trend-cycle2/

Calculation:
    STCmacd = Moving Average Convergance/Divergance or Oscillator
    STCstoch = Intermediate Stochastic of MACD/Osc.
    2nd Stochastic including filtering with results in the
    STC = Schaff Trend Cycle

Args:
    close (pd.Series): Series of 'close's, used for indexing Series, mandatory
    tclen (int): SchaffTC Signal-Line length.  Default: 10 (adjust to the half of cycle)
    fast (int): The short period.   Default: 12
    slow (int): The long period.   Default: 26
    factor (float): smoothing factor for last stoch. calculation.   Default: 0.5
    offset (int): How many periods to offset the result.  Default: 0

Kwargs:
    ma1: 1st moving average provided externally (mandatory in conjuction with ma2)
    ma2: 2nd moving average provided externally (mandatory in conjuction with ma1)
    osc: an externally feeded osillator
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.DataFrame: stc, macd, stoch
"""

# 定義 Schaff Trend Cycle(STC)計算函式
def schaff_tc(close, xmacd, tclength, factor):
    # 實際計算部分,這部分計算適用於不同的操作模式
    # 1. MACD 的 Stochastic
    # 計算區間 tclen 內 xmacd 的最小值
    lowest_xmacd = xmacd.rolling(tclength).min()  # min value in interval tclen
    # 計算區間 tclen 內 xmacd 的範圍(最大值 - 最小值)
    xmacd_range = non_zero_range(xmacd.rolling(tclength).max(), lowest_xmacd)
    # 獲取 xmacd 的長度
    m = len(xmacd)

    # 計算 MACD 的快速 %K
    # 初始化 stoch1 和 pf 列表
    stoch1, pf = list(xmacd), list(xmacd)
    # 第一個元素的值為 0
    stoch1[0], pf[0] = 0, 0
    # 迴圈計算 stoch1 和 pf 列表中的值
    for i in range(1, m):
        # 如果 lowest_xmacd[i] 大於 0,則計算快速 %K
        if lowest_xmacd[i] > 0:
            stoch1[i] = 100 * ((xmacd[i] - lowest_xmacd[i]) / xmacd_range[i])
        else:
            # 否則保持前一個值不變
            stoch1[i] = stoch1[i - 1]
        # 計算平滑後的 %D
        pf[i] = round(pf[i - 1] + (factor * (stoch1[i] - pf[i - 1])), 8)
    # 將 pf 轉換為 Series 型別,並以 close 的索引為索引
    pf = Series(pf, index=close.index)

    # 計算平滑後的 Percent Fast D, 'PF' 的隨機指標
    # 計算滾動視窗為 tclength 的最小值
    lowest_pf = pf.rolling(tclength).min()
    # 計算 pf 在滾動視窗為 tclength 的範圍內的非零範圍
    pf_range = non_zero_range(pf.rolling(tclength).max(), lowest_pf)

    # 計算 % Fast K of PF
    stoch2, pff = list(xmacd), list(xmacd)
    stoch2[0], pff[0] = 0, 0
    for i in range(1, m):
        if pf_range[i] > 0:
            # 計算 % Fast K of PF
            stoch2[i] = 100 * ((pf[i] - lowest_pf[i]) / pf_range[i])
        else:
            stoch2[i] = stoch2[i - 1]
        # 計算平滑後的 % Fast D of PF
        # 使用平滑因子 factor 進行平滑計算
        pff[i] = round(pff[i - 1] + (factor * (stoch2[i] - pff[i - 1])), 8)

    # 返回平滑後的 % Fast D of PF 和原始的 PF
    return [pff, pf]

# `.\pandas-ta\pandas_ta\momentum\stoch.py`

```py
# -*- coding: utf-8 -*-
# 匯入DataFrame類
from pandas import DataFrame
# 從pandas_ta.overlap模組中匯入ma函式
from pandas_ta.overlap import ma
# 從pandas_ta.utils模組中匯入get_offset、non_zero_range和verify_series函式
from pandas_ta.utils import get_offset, non_zero_range, verify_series


# 定義Stochastic Oscillator (STOCH)函式
def stoch(high, low, close, k=None, d=None, smooth_k=None, mamode=None, offset=None, **kwargs):
    """Indicator: Stochastic Oscillator (STOCH)"""
    # 校驗引數
    # 如果k為正數則使用k,否則預設為14
    k = k if k and k > 0 else 14
    # 如果d為正數則使用d,否則預設為3
    d = d if d and d > 0 else 3
    # 如果smooth_k為正數則使用smooth_k,否則預設為3
    smooth_k = smooth_k if smooth_k and smooth_k > 0 else 3
    # 計算_max(k, d, smooth_k)
    _length = max(k, d, smooth_k)
    # 校驗high、low和close的長度是否為_length
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    close = verify_series(close, _length)
    # 獲取offset值
    offset = get_offset(offset)
    # 如果mamode不是字串則設為"sma"
    mamode = mamode if isinstance(mamode, str) else "sma"

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

    # 計算結果
    # 計算過去k個週期的最低值
    lowest_low = low.rolling(k).min()
    # 計算過去k個週期的最高值
    highest_high = high.rolling(k).max()

    # 計算stoch值
    stoch = 100 * (close - lowest_low)
    stoch /= non_zero_range(highest_high, lowest_low)

    # 計算stoch_k和stoch_d
    stoch_k = ma(mamode, stoch.loc[stoch.first_valid_index():,], length=smooth_k)
    stoch_d = ma(mamode, stoch_k.loc[stoch_k.first_valid_index():,], length=d)

    # 偏移處理
    if offset != 0:
        stoch_k = stoch_k.shift(offset)
        stoch_d = stoch_d.shift(offset)

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

    # 設定名稱和分類
    _name = "STOCH"
    _props = f"_{k}_{d}_{smooth_k}"
    stoch_k.name = f"{_name}k{_props}"
    stoch_d.name = f"{_name}d{_props}"
    stoch_k.category = stoch_d.category = "momentum"

    # 準備要返回的DataFrame
    data = {stoch_k.name: stoch_k, stoch_d.name: stoch_d}
    df = DataFrame(data)
    df.name = f"{_name}{_props}"
    df.category = stoch_k.category
    return df


# 設定stoch函式的文件字串
stoch.__doc__ = \
"""Stochastic (STOCH)

The Stochastic Oscillator (STOCH) was developed by George Lane in the 1950's.
He believed this indicator was a good way to measure momentum because changes in
momentum precede changes in price.

It is a range-bound oscillator with two lines moving between 0 and 100.
The first line (%K) displays the current close in relation to the period's
high/low range. The second line (%D) is a Simple Moving Average of the %K line.
The most common choices are a 14 period %K and a 3 period SMA for %D.

Sources:
    https://www.tradingview.com/wiki/Stochastic_(STOCH)
    https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=332&Name=KD_-_Slow

Calculation:
    Default Inputs:
        k=14, d=3, smooth_k=3
    SMA = Simple Moving Average
    LL  = low for last k periods
    HH  = high for last k periods

    STOCH = 100 * (close - LL) / (HH - LL)
    STOCHk = SMA(STOCH, smooth_k)
    STOCHd = SMA(FASTK, d)

Args:
    high (pd.Series): Series of 'high's

"""
    # 表示傳入函式的引數,分別為低價序列
    low (pd.Series): Series of 'low's
    # 表示傳入函式的引數,分別為收盤價序列
    close (pd.Series): Series of 'close's
    # 表示傳入函式的引數,表示快速 %K 的週期,預設為 14
    k (int): The Fast %K period. Default: 14
    # 表示傳入函式的引數,表示慢速 %K 的週期,預設為 3
    d (int): The Slow %K period. Default: 3
    # 表示傳入函式的引數,表示慢速 %D 的週期,預設為 3
    smooth_k (int): The Slow %D period. Default: 3
    # 表示傳入函式的引數,參見 ta.ma 的幫助文件。預設為 'sma'
    mamode (str): See ```help(ta.ma)```py. Default: 'sma'
    # 表示傳入函式的引數,表示結果的偏移週期數,預設為 0
    offset (int): How many periods to offset the result. Default: 0
# 引數說明:
# - fillna (value, optional): 使用 value 對 pd.DataFrame 進行填充
# - fill_method (value, optional): 填充方法的型別

# 返回值:
# - 返回一個 pd.DataFrame,包含 %K 和 %D 列

.\pandas-ta\pandas_ta\momentum\stochrsi.py

# -*- coding: utf-8 -*-
# 從 pandas 庫中匯入 DataFrame 類
from pandas import DataFrame
# 從 .rsi 模組中匯入 rsi 函式
from .rsi import rsi
# 從 pandas_ta.overlap 模組中匯入 ma 函式
from pandas_ta.overlap import ma
# 從 pandas_ta.utils 模組中匯入 get_offset, non_zero_range, verify_series 函式
from pandas_ta.utils import get_offset, non_zero_range, verify_series


# 定義函式 stochrsi,計算 Stochastic RSI Oscillator (STOCHRSI)
def stochrsi(close, length=None, rsi_length=None, k=None, d=None, mamode=None, offset=None, **kwargs):
    """Indicator: Stochastic RSI Oscillator (STOCHRSI)"""
    # 校驗引數
    length = length if length and length > 0 else 14
    rsi_length = rsi_length if rsi_length and rsi_length > 0 else 14
    k = k if k and k > 0 else 3
    d = d if d and d > 0 else 3
    # 校驗 close 序列
    close = verify_series(close, max(length, rsi_length, k, d))
    offset = get_offset(offset)
    # 確定 mamode 預設為 "sma",如果 mamode 不是字串則設為 "sma"
    mamode = mamode if isinstance(mamode, str) else "sma"

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

    # 計算結果
    # 計算 RSI
    rsi_ = rsi(close, length=rsi_length)
    # 計算最低 RSI
    lowest_rsi = rsi_.rolling(length).min()
    # 計算最高 RSI
    highest_rsi = rsi_.rolling(length).max()

    # 計算 stoch 值
    stoch = 100 * (rsi_ - lowest_rsi)
    stoch /= non_zero_range(highest_rsi, lowest_rsi)

    # 計算 STOCHRSI 的 %K 線和 %D 線
    stochrsi_k = ma(mamode, stoch, length=k)
    stochrsi_d = ma(mamode, stochrsi_k, length=d)

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

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

    # 命名並分類
    _name = "STOCHRSI"
    _props = f"_{length}_{rsi_length}_{k}_{d}"
    stochrsi_k.name = f"{_name}k{_props}"
    stochrsi_d.name = f"{_name}d{_props}"
    stochrsi_k.category = stochrsi_d.category = "momentum"

    # 準備返回的 DataFrame
    data = {stochrsi_k.name: stochrsi_k, stochrsi_d.name: stochrsi_d}
    df = DataFrame(data)
    df.name = f"{_name}{_props}"
    df.category = stochrsi_k.category

    return df


# 設定 stochrsi 函式的文件字串
stochrsi.__doc__ = \
"""Stochastic (STOCHRSI)

"Stochastic RSI and Dynamic Momentum Index" was created by Tushar Chande and Stanley Kroll and published in Stock & Commodities V.11:5 (189-199)

It is a range-bound oscillator with two lines moving between 0 and 100.
The first line (%K) displays the current RSI in relation to the period's
high/low range. The second line (%D) is a Simple Moving Average of the %K line.
The most common choices are a 14 period %K and a 3 period SMA for %D.

Sources:
    https://www.tradingview.com/wiki/Stochastic_(STOCH)

Calculation:
    Default Inputs:
        length=14, rsi_length=14, k=3, d=3
    RSI = Relative Strength Index
    SMA = Simple Moving Average

    RSI = RSI(high, low, close, rsi_length)
    LL  = lowest RSI for last rsi_length periods
    HH  = highest RSI for last rsi_length periods

    STOCHRSI  = 100 * (RSI - LL) / (HH - LL)
    STOCHRSIk = SMA(STOCHRSI, k)
    STOCHRSId = SMA(STOCHRSIk, d)

Args:
    high (pd.Series): Series of 'high's

"""
    low (pd.Series): 儲存股價的最低價序列
    close (pd.Series): 儲存股價的收盤價序列
    length (int): STOCHRSI 的週期。預設為 14
    rsi_length (int): RSI 的週期。預設為 14
    k (int): 快速 %K 的週期。預設為 3
    d (int): 慢速 %K 的週期。預設為 3
    mamode (str): 檢視 ```help(ta.ma)```py。預設為 'sma'(簡單移動平均)
    offset (int): 結果偏移的週期數。預設為 0
# 引數說明部分,描述函式的引數和返回值
Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

# 返回值說明部分,描述函式返回的資料型別和列名
Returns:
    pd.DataFrame: RSI %K, RSI %D columns.

.\pandas-ta\pandas_ta\momentum\td_seq.py

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

# 定義函式 td_seq,用於計算 Tom Demark Sequential(TD_SEQ)指標
def td_seq(close, asint=None, offset=None, **kwargs):
    """Indicator: Tom Demark Sequential (TD_SEQ)"""
    # 驗證引數 close 是否為有效的 Series 物件
    close = verify_series(close)
    # 獲取偏移量
    offset = get_offset(offset)
    # 如果 asint 不為布林值,則設定為 False
    asint = asint if isinstance(asint, bool) else False
    # 獲取引數中的 show_all,如果不存在則設定預設值為 True
    show_all = kwargs.setdefault("show_all", True)

    # 定義函式 true_sequence_count,用於計算連續的真值序列數量
    def true_sequence_count(series: Series):
        # 找到最後一個為 False 的索引
        index = series.where(series == False).last_valid_index()

        if index is None:
            # 如果索引為空,則返回序列的總數
            return series.count()
        else:
            # 否則,返回索引之後的序列數量
            s = series[series.index > index]
            return s.count()

    # 定義函式 calc_td,用於計算 TD_SEQ
    def calc_td(series: Series, direction: str, show_all: bool):
        # 計算 TD_SEQ 的布林值
        td_bool = series.diff(4) > 0 if direction=="up" else series.diff(4) < 0
        # 根據布林值計算 TD_SEQ 數值
        td_num = npWhere(
            td_bool, td_bool.rolling(13, min_periods=0).apply(true_sequence_count), 0
        )
        td_num = Series(td_num)

        if show_all:
            # 如果 show_all 為 True,則保留所有 TD_SEQ 值
            td_num = td_num.mask(td_num == 0)
        else:
            # 否則,只保留在 6 到 9 之間的 TD_SEQ 值
            td_num = td_num.mask(~td_num.between(6,9))

        return td_num

    # 計算上升序列的 TD_SEQ
    up_seq = calc_td(close, "up", show_all)
    # 計算下降序列的 TD_SEQ
    down_seq = calc_td(close, "down", show_all)

    # 如果需要將結果轉換為整數
    if asint:
        if up_seq.hasnans and down_seq.hasnans:
            # 填充缺失值為 0
            up_seq.fillna(0, inplace=True)
            down_seq.fillna(0, inplace=True)
        # 轉換結果為整數型別
        up_seq = up_seq.astype(int)
        down_seq = down_seq.astype(int)

    # 如果偏移量不為 0
    if offset != 0:
        # 對結果進行偏移
        up_seq = up_seq.shift(offset)
        down_seq = down_seq.shift(offset)

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

    if "fill_method" in kwargs:
        up_seq.fillna(method=kwargs["fill_method"], inplace=True)
        down_seq.fillna(method=kwargs["fill_method"], inplace=True)

    # 設定上升序列和下降序列的名稱和分類
    up_seq.name = f"TD_SEQ_UPa" if show_all else f"TD_SEQ_UP"
    down_seq.name = f"TD_SEQ_DNa" if show_all else f"TD_SEQ_DN"
    up_seq.category = down_seq.category = "momentum"

    # 準備要返回的 DataFrame
    df = DataFrame({up_seq.name: up_seq, down_seq.name: down_seq})
    df.name = "TD_SEQ"
    df.category = up_seq.category

    return df

# 設定函式文件字串
td_seq.__doc__ = \
"""TD Sequential (TD_SEQ)

Tom DeMark's Sequential indicator attempts to identify a price point where an
uptrend or a downtrend exhausts itself and reverses.

Sources:
    https://tradetrekker.wordpress.com/tdsequential/

Calculation:
    Compare current close price with 4 days ago price, up to 13 days. For the
    consecutive ascending or descending price sequence, display 6th to 9th day
    value.

Args:
    close (pd.Series): Series of 'close's
    asint (bool): If True, fillnas with 0 and change type to int. Default: False
    offset (int): How many periods to offset the result. Default: 0

Kwargs:

"""
    # 定義函式引數show_all,用於控制展示範圍,預設為True,即展示1到13;如果設定為False,僅展示6到9。
    show_all (bool): Show 1 - 13. If set to False, show 6 - 9. Default: True
    # 定義函式引數fillna,用於填充缺失值,引數value為填充的數值,預設為空。
    fillna (value, optional): pd.DataFrame.fillna(value)
# 返回型別說明:返回的是一個 Pandas DataFrame,其中包含了生成的新特徵。

.\pandas-ta\pandas_ta\momentum\trix.py

# -*- coding: utf-8 -*-
# 從 pandas 庫中匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta 庫中匯入 overlap 模組下的 ema 函式
from pandas_ta.overlap.ema import ema
# 從 pandas_ta 庫中匯入 utils 模組
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定義 Trix 指標函式,用於計算 Trix (TRIX) 指標
def trix(close, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs):
    """Indicator: Trix (TRIX)"""
    # 驗證引數
   length = int(length) if length and length > 0 else 30
    signal = int(signal) if signal and signal > 0 else 9
    scalar = float(scalar) if scalar else 100
    close = verify_series(close, max(length, signal))
    drift = get_drift(drift)
    offset = get_offset(offset)

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

    # 計算結果
    ema1 = ema(close=close, length=length, **kwargs)
    ema2 = ema(close=ema1, length=length, **kwargs)
    ema3 = ema(close=ema2, length=length, **kwargs)
    trix = scalar * ema3.pct_change(drift)

    trix_signal = trix.rolling(signal).mean()

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

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

    # 設定名稱和類別
    trix.name = f"TRIX_{length}_{signal}"
    trix_signal.name = f"TRIXs_{length}_{signal}"
    trix.category = trix_signal.category = "momentum"

    # 準備返回的 DataFrame
    df = DataFrame({trix.name: trix, trix_signal.name: trix_signal})
    df.name = f"TRIX_{length}_{signal}"
    df.category = "momentum"

    return df

# 設定 trix 函式的文件字串
trix.__doc__ = \
"""Trix (TRIX)

TRIX is a momentum oscillator to identify divergences.

Sources:
    https://www.tradingview.com/wiki/TRIX

Calculation:
    Default Inputs:
        length=18, drift=1
    EMA = Exponential Moving Average
    ROC = Rate of Change
    ema1 = EMA(close, length)
    ema2 = EMA(ema1, length)
    ema3 = EMA(ema2, length)
    TRIX = 100 * ROC(ema3, drift)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 18
    signal (int): It's period. Default: 9
    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:
    pd.Series: New feature generated.
"""

.\pandas-ta\pandas_ta\momentum\tsi.py

# -*- coding: utf-8 -*-
# 從 pandas 庫匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta.overlap 模組匯入 ema, ma 函式
from pandas_ta.overlap import ema, ma
# 從 pandas_ta.utils 模組匯入 get_drift, get_offset, verify_series 函式
from pandas_ta.utils import get_drift, get_offset, verify_series


# 定義 True Strength Index (TSI) 指標函式
def tsi(close, fast=None, slow=None, signal=None, scalar=None, mamode=None, drift=None, offset=None, **kwargs):
    """Indicator: True Strength Index (TSI)"""
    # 驗證引數有效性
    # 如果 fast 引數存在且大於 0,則將其轉換為整數,否則設為預設值 13
    fast = int(fast) if fast and fast > 0 else 13
    # 如果 slow 引數存在且大於 0,則將其轉換為整數,否則設為預設值 25
    slow = int(slow) if slow and slow > 0 else 25
    # 如果 signal 引數存在且大於 0,則將其轉換為整數,否則設為預設值 13
    signal = int(signal) if signal and signal > 0 else 13
    # 如果 close 序列為 None,則返回 None
    if close is None: return
    # 如果 scalar 存在,則將其轉換為浮點數,否則設為預設值 100
    scalar = float(scalar) if scalar else 100
    # 獲取漂移值,用於處理偏移
    drift = get_drift(drift)
    # 獲取偏移量,用於處理偏移
    offset = get_offset(offset)
    # 如果 mamode 不是字串型別,則將其設為預設值 "ema"
    mamode = mamode if isinstance(mamode, str) else "ema"
    # 如果 kwargs 中包含 "length" 鍵,則將其移除
    if "length" in kwargs: kwargs.pop("length")

    # 計算結果
    # 計算 close 序列的一階差分
    diff = close.diff(drift)
    # 計算 slow 期 EMA
    slow_ema = ema(close=diff, length=slow, **kwargs)
    # 計算 fast 期 EMA
    fast_slow_ema = ema(close=slow_ema, length=fast, **kwargs)

    # 計算絕對差分
    abs_diff = diff.abs()
    # 計算 slow 期絕對差分的 EMA
    abs_slow_ema = ema(close=abs_diff, length=slow, **kwargs)
    # 計算 fast 期絕對差分的 EMA
    abs_fast_slow_ema = ema(close=abs_slow_ema, length=fast, **kwargs)

    # 計算 TSI
    tsi = scalar * fast_slow_ema / abs_fast_slow_ema
    # 計算 TSI 的訊號線
    tsi_signal = ma(mamode, tsi, length=signal)

    # 處理偏移
    if offset != 0:
        tsi = tsi.shift(offset)
        tsi_signal = tsi_signal.shift(offset)

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

    # 命名並分類化指標
    tsi.name = f"TSI_{fast}_{slow}_{signal}"
    tsi_signal.name = f"TSIs_{fast}_{slow}_{signal}"
    tsi.category = tsi_signal.category =  "momentum"

    # 準備返回的 DataFrame
    df = DataFrame({tsi.name: tsi, tsi_signal.name: tsi_signal})
    df.name = f"TSI_{fast}_{slow}_{signal}"
    df.category = "momentum"

    return df


# 設定 tsi 函式的文件字串
tsi.__doc__ = \
"""True Strength Index (TSI)

The True Strength Index is a momentum indicator used to identify short-term
swings while in the direction of the trend as well as determining overbought
and oversold conditions.

Sources:
    https://www.investopedia.com/terms/t/tsi.asp

Calculation:
    Default Inputs:
        fast=13, slow=25, signal=13, scalar=100, drift=1
    EMA = Exponential Moving Average
    diff = close.diff(drift)

    slow_ema = EMA(diff, slow)
    fast_slow_ema = EMA(slow_ema, slow)

    abs_diff_slow_ema = absolute_diff_ema = EMA(ABS(diff), slow)
    abema = abs_diff_fast_slow_ema = EMA(abs_diff_slow_ema, fast)

    TSI = scalar * fast_slow_ema / abema
    Signal = EMA(TSI, signal)

Args:
    close (pd.Series): Series of 'close's
    fast (int): The short period. Default: 13
    slow (int): The long period. Default: 25
    signal (int): The signal period. Default: 13
"""
    scalar (float): How much to magnify. Default: 100
    # 定義一個浮點數變數 scalar,表示放大倍數,預設值為 100

    mamode (str): Moving Average of TSI Signal Line.
        See ```help(ta.ma)```py. Default: 'ema'
    # 定義一個字串變數 mamode,表示 TSI 訊號線的移動平均方式,預設值為 'ema',可檢視 ta.ma 的幫助文件

    drift (int): The difference period. Default: 1
    # 定義一個整數變數 drift,表示差分週期,預設值為 1

    offset (int): How many periods to offset the result. Default: 0
    # 定義一個整數變數 offset,表示結果的偏移週期數,預設值為 0
# 函式引數說明部分,kwargs表示可變關鍵字引數
Kwargs:
    # fillna引數,用於填充缺失值的值,型別為任意
    fillna (value, optional): pd.DataFrame.fillna(value)
    # fill_method引數,填充方法的型別
    fill_method (value, optional): Type of fill method

# 返回值說明部分,返回一個pandas DataFrame物件,包含tsi和signal兩列
Returns:
    # 返回的pandas DataFrame物件,包含tsi和signal兩列資料
    pd.DataFrame: tsi, signal.

.\pandas-ta\pandas_ta\momentum\uo.py

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

# 匯入 DataFrame 類
from pandas import DataFrame
# 匯入 Imports 類
from pandas_ta import Imports
# 匯入 get_drift 和 get_offset 函式
from pandas_ta.utils import get_drift, get_offset, verify_series


# 定義 Ultimate Oscillator(UO)指標函式
def uo(high, low, close, fast=None, medium=None, slow=None, fast_w=None, medium_w=None, slow_w=None, talib=None, drift=None, offset=None, **kwargs):
    """Indicator: Ultimate Oscillator (UO)"""
    # 驗證引數
    fast = int(fast) if fast and fast > 0 else 7
    fast_w = float(fast_w) if fast_w and fast_w > 0 else 4.0
    medium = int(medium) if medium and medium > 0 else 14
    medium_w = float(medium_w) if medium_w and medium_w > 0 else 2.0
    slow = int(slow) if slow and slow > 0 else 28
    slow_w = float(slow_w) if slow_w and slow_w > 0 else 1.0
    _length = max(fast, medium, slow)
    # 驗證 high、low、close 序列長度
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    close = verify_series(close, _length)
    # 獲取漂移和偏移量
    drift = get_drift(drift)
    offset = get_offset(offset)
    # 設定是否使用 talib 模式
    mode_tal = bool(talib) if isinstance(talib, bool) else True

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

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 使用 talib 計算 UO 指標
        from talib import ULTOSC
        uo = ULTOSC(high, low, close, fast, medium, slow)
    else:
        # 否則,使用自定義計算方法
        tdf = DataFrame({
            "high": high,
            "low": low,
            f"close_{drift}": close.shift(drift)
        })
        # 獲取最大最小值
        max_h_or_pc = tdf.loc[:, ["high", f"close_{drift}"]].max(axis=1)
        min_l_or_pc = tdf.loc[:, ["low", f"close_{drift}"]].min(axis=1)
        del tdf

        # 計算 buying pressure(bp)和 true range(tr)
        bp = close - min_l_or_pc
        tr = max_h_or_pc - min_l_or_pc

        # 計算 fast、medium、slow 平均值
        fast_avg = bp.rolling(fast).sum() / tr.rolling(fast).sum()
        medium_avg = bp.rolling(medium).sum() / tr.rolling(medium).sum()
        slow_avg = bp.rolling(slow).sum() / tr.rolling(slow).sum()

        # 計算總權重和加權平均值
        total_weight = fast_w + medium_w + slow_w
        weights = (fast_w * fast_avg) + (medium_w * medium_avg) + (slow_w * slow_avg)
        uo = 100 * weights / total_weight

    # 考慮偏移量
    if offset != 0:
        uo = uo.shift(offset)

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

    # 設定指標名稱和分類
    uo.name = f"UO_{fast}_{medium}_{slow}"
    uo.category = "momentum"

    # 返回 Ultimate Oscillator(UO)指標
    return uo


# 設定 Ultimate Oscillator(UO)指標的文件字串
uo.__doc__ = \
"""Ultimate Oscillator (UO)

The Ultimate Oscillator is a momentum indicator over three different
periods. It attempts to correct false divergence trading signals.

Sources:
    https://www.tradingview.com/wiki/Ultimate_Oscillator_(UO)

Calculation:
    Default Inputs:
        fast=7, medium=14, slow=28,
        fast_w=4.0, medium_w=2.0, slow_w=1.0, drift=1
    min_low_or_pc  = close.shift(drift).combine(low, min)
    max_high_or_pc = close.shift(drift).combine(high, max)

    bp = buying pressure = close - min_low_or_pc
    tr = true range = max_high_or_pc - min_low_or_pc

    fast_avg = SUM(bp, fast) / SUM(tr, fast)
"""
    # 計算中等速度的平均值,中等速度報告的總和除以中等速度報告的數量
    medium_avg = SUM(bp, medium) / SUM(tr, medium)
    
    # 計算慢速度的平均值,慢速度報告的總和除以慢速度報告的數量
    slow_avg = SUM(bp, slow) / SUM(tr, slow)
    
    # 計算所有速度權重的總和
    total_weight = fast_w + medium_w + slow_w
    
    # 計算加權平均值,每個速度報告的權重乘以其對應的平均值,然後相加
    weights = (fast_w * fast_avg) + (medium_w * medium_avg) + (slow_w * slow_avg)
    
    # 計算不確定性指標(UO),即權重總和乘以100除以所有權重的總和
    UO = 100 * weights / total_weight
# 引數說明:
# high (pd.Series): 'high' 資料序列
# low (pd.Series): 'low' 資料序列
# close (pd.Series): 'close' 資料序列
# fast (int): 快速 %K 週期。預設值:7
# medium (int): 慢速 %K 週期。預設值:14
# slow (int): 慢速 %D 週期。預設值:28
# fast_w (float): 快速 %K 週期。預設值:4.0
# medium_w (float): 慢速 %K 週期。預設值:2.0
# slow_w (float): 慢速 %D 週期。預設值:1.0
# talib (bool): 如果安裝了 TA Lib 並且 talib 為 True,則返回 TA Lib 版本。預設值:True
# drift (int): 差異週期。預設值:1
# offset (int): 結果的偏移週期數。預設值:0

# 可選引數:
# fillna (value, optional): pd.DataFrame.fillna(value) 的填充值
# fill_method (value, optional): 填充方法的型別

# 返回值:
# pd.Series: 生成的新特徵序列

.\pandas-ta\pandas_ta\momentum\willr.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


def willr(high, low, close, length=None, talib=None, offset=None, **kwargs):
    """Indicator: William's Percent R (WILLR)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則預設為 14
    length = int(length) if length and length > 0 else 14
    # 如果 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
    # 確定最終使用的長度為 length 和 min_periods 中的較大值
    _length = max(length, min_periods)
    # 驗證 high、low、close 系列,並設定它們的長度為 _length
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    close = verify_series(close, _length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 確定是否使用 TA-Lib
    mode_tal = bool(talib) if isinstance(talib, bool) else True

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

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 如果 TA-Lib 可用且 mode_tal 為 True,則使用 TA-Lib 中的 WILLR 函式計算
        from talib import WILLR
        willr = WILLR(high, low, close, length)
    else:
        # 否則,使用自定義方法計算 WILLR
        # 計算長度為 length 的最低低點
        lowest_low = low.rolling(length, min_periods=min_periods).min()
        # 計算長度為 length 的最高高點
        highest_high = high.rolling(length, min_periods=min_periods).max()
        # 計算 WILLR
        willr = 100 * ((close - lowest_low) / (highest_high - lowest_low) - 1)

    # 根據偏移量對結果進行偏移
    if offset != 0:
        willr = willr.shift(offset)

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

    # 設定指標名稱和類別
    willr.name = f"WILLR_{length}"
    willr.category = "momentum"

    # 返回計算結果
    return willr


# 設定函式文件字串
willr.__doc__ = \
"""William's Percent R (WILLR)

William's Percent R is a momentum oscillator similar to the RSI that
attempts to identify overbought and oversold conditions.

Sources:
    https://www.tradingview.com/wiki/Williams_%25R_(%25R)

Calculation:
    Default Inputs:
        length=20
    LL = low.rolling(length).min()
    HH = high.rolling(length).max()

    WILLR = 100 * ((close - LL) / (HH - LL) - 1)

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

相關文章