PandasTA 原始碼解析(十六)

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

.\pandas-ta\pandas_ta\volatility\kc.py

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

# 定義函式 kc,用於計算 Keltner 通道(KC)指標
def kc(high, low, close, length=None, scalar=None, mamode=None, offset=None, **kwargs):
    """Indicator: Keltner Channels (KC)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則轉換為整數,否則設定為預設值 20
    length = int(length) if length and length > 0 else 20
    # 如果 scalar 存在且大於 0,則轉換為浮點數,否則設定為預設值 2
    scalar = float(scalar) if scalar and scalar > 0 else 2
    # 如果 mamode 是字串型別,則保持不變,否則設定為預設值 "ema"
    mamode = mamode if isinstance(mamode, str) else "ema"
    # 驗證 high、low、close 是否為有效的 Series,長度為 length
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)

    # 如果 high、low、close 存在空值,則返回空值
    if high is None or low is None or close is None: return

    # 計算結果
    # 判斷是否使用 True Range(TR),預設為 True
    use_tr = kwargs.pop("tr", True)
    if use_tr:
        range_ = true_range(high, low, close)
    else:
        range_ = high_low_range(high, low)

    # 計算基準線和波動範圍
    basis = ma(mamode, close, length=length)
    band = ma(mamode, range_, length=length)

    lower = basis - scalar * band
    upper = basis + scalar * band

    # 處理偏移量
    if offset != 0:
        lower = lower.shift(offset)
        basis = basis.shift(offset)
        upper = upper.shift(offset)

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

    # 命名並分類化
    _props = f"{mamode.lower()[0] if len(mamode) else ''}_{length}_{scalar}"
    lower.name = f"KCL{_props}"
    basis.name = f"KCB{_props}"
    upper.name = f"KCU{_props}"
    basis.category = upper.category = lower.category = "volatility"

    # 準備返回的 DataFrame
    data = {lower.name: lower, basis.name: basis, upper.name: upper}
    kcdf = DataFrame(data)
    kcdf.name = f"KC{_props}"
    kcdf.category = basis.category

    return kcdf


# 設定 kc 函式的文件字串
kc.__doc__ = \
"""Keltner Channels (KC)

A popular volatility indicator similar to Bollinger Bands and
Donchian Channels.

Sources:
    https://www.tradingview.com/wiki/Keltner_Channels_(KC)

Calculation:
    Default Inputs:
        length=20, scalar=2, mamode=None, tr=True
    TR = True Range
    SMA = Simple Moving Average
    EMA = Exponential Moving Average

    if tr:
        RANGE = TR(high, low, close)
    else:
        RANGE = high - low

    if mamode == "ema":
        BASIS = sma(close, length)
        BAND = sma(RANGE, length)
    elif mamode == "sma":
        BASIS = sma(close, length)
        BAND = sma(RANGE, length)

    LOWER = BASIS - scalar * BAND
    UPPER = BASIS + scalar * BAND

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

"""
    length (int): The short period.  Default: 20
    scalar (float): A positive float to scale the bands. Default: 2
    mamode (str): See ```help(ta.ma)```py. Default: 'ema'
    offset (int): How many periods to offset the result. Default: 0
# 函式引數:
#   tr (bool): 如果為 True,則使用 True Range 進行計算;如果為 False,則使用高 - 低作為範圍計算。預設值為 True
#   fillna (value, optional): pd.DataFrame.fillna(value) 的可選引數,用於指定填充缺失值的值
#   fill_method (value, optional): 填充方法的型別

# 返回值:
#   返回一個 pandas DataFrame,包含 lower、basis、upper 列。

.\pandas-ta\pandas_ta\volatility\massi.py

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


# 定義一個函式 massi,用於計算 Mass Index(MASSI)
def massi(high, low, fast=None, slow=None, offset=None, **kwargs):
    """Indicator: Mass Index (MASSI)"""
    # 驗證引數的有效性
    # 如果 fast 有值且大於 0,則將其轉換為整數,否則預設為 9
    fast = int(fast) if fast and fast > 0 else 9
    # 如果 slow 有值且大於 0,則將其轉換為整數,否則預設為 25
    slow = int(slow) if slow and slow > 0 else 25
    # 如果 slow 小於 fast,則交換它們的值
    if slow < fast:
        fast, slow = slow, fast
    # 計算引數的最大值
    _length = max(fast, slow)
    # 驗證 high 和 low 是否為有效序列
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 移除 kwargs 中的 "length" 鍵
    if "length" in kwargs: kwargs.pop("length")

    # 如果 high 或 low 為 None,則返回
    if high is None or low is None: return

    # 計算結果
    # 計算高低價範圍
    high_low_range = non_zero_range(high, low)
    # 計算高低價範圍的 EMA
    hl_ema1 = ema(close=high_low_range, length=fast, **kwargs)
    # 計算高低價範圍的 EMA 的 EMA
    hl_ema2 = ema(close=hl_ema1, length=fast, **kwargs)

    # 計算 hl_ratio
    hl_ratio = hl_ema1 / hl_ema2
    # 計算 MASSI
    massi = hl_ratio.rolling(slow, min_periods=slow).sum()

    # 調整偏移量
    if offset != 0:
        massi = massi.shift(offset)

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

    # 給結果命名並歸類
    massi.name = f"MASSI_{fast}_{slow}"
    massi.category = "volatility"

    return massi


# 將 massi 函式的文件字串重新賦值,用於說明該函式的功能、計算方法以及引數等資訊
massi.__doc__ = \
"""Mass Index (MASSI)

The Mass Index is a non-directional volatility indicator that utilitizes the
High-Low Range to identify trend reversals based on range expansions.

Sources:
    https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:mass_index
    mi = sum(ema(high - low, 9) / ema(ema(high - low, 9), 9), length)

Calculation:
    Default Inputs:
        fast: 9, slow: 25
    EMA = Exponential Moving Average
    hl = high - low
    hl_ema1 = EMA(hl, fast)
    hl_ema2 = EMA(hl_ema1, fast)
    hl_ratio = hl_ema1 / hl_ema2
    MASSI = SUM(hl_ratio, slow)

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    fast (int): The short period. Default: 9
    slow (int): The long period. Default: 25
    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\volatility\natr.py

# -*- coding: utf-8 -*-
# 匯入必要的庫和模組
from .atr import atr
from pandas_ta import Imports
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定義函式,計算標準化的平均真實範圍(NATR)
def natr(high, low, close, length=None, scalar=None, mamode=None, talib=None, drift=None, offset=None, **kwargs):
    """Indicator: Normalized Average True Range (NATR)"""
    # 驗證引數
    length = int(length) if length and length > 0 else 14
    mamode = mamode if isinstance(mamode, str) else "ema"
    scalar = float(scalar) if scalar else 100
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    drift = get_drift(drift)
    offset = get_offset(offset)
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 如果輸入的資料有缺失值,則返回空
    if high is None or low is None or close is None: return

    # 計算結果
    if Imports["talib"] and mode_tal:
        from talib import NATR
        natr = NATR(high, low, close, length)
    else:
        natr = scalar / close
        natr *= atr(high=high, low=low, close=close, length=length, mamode=mamode, drift=drift, offset=offset, **kwargs)

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

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

    # 設定名稱和分類
    natr.name = f"NATR_{length}"
    natr.category = "volatility"

    return natr

# 設定函式文件字串
natr.__doc__ = \
"""Normalized Average True Range (NATR)

Normalized Average True Range attempt to normalize the average true range.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/normalized-average-true-range-natr/

Calculation:
    Default Inputs:
        length=20
    ATR = Average True Range
    NATR = (100 / close) * ATR(high, low, close)

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series): Series of 'close's
    length (int): The short period. Default: 20
    scalar (float): How much to magnify. Default: 100
    mamode (str): See ```help(ta.ma)```py. Default: 'ema'
    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
"""

.\pandas-ta\pandas_ta\volatility\pdist.py

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

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

# 定義函式 pdist,用於計算價格距離(PDIST)
def pdist(open_, high, low, close, drift=None, offset=None, **kwargs):
    """Indicator: Price Distance (PDIST)"""
    # 驗證引數的有效性,確保它們都是 pd.Series 型別
    open_ = verify_series(open_)
    high = verify_series(high)
    low = verify_series(low)
    close = verify_series(close)
    # 獲取漂移和偏移值,如果未提供,則使用預設值
    drift = get_drift(drift)
    offset = get_offset(offset)

    # 計算結果
    # PDIST = 2 * (high - low) - |close - open| + |open - close[drift]|
    pdist = 2 * non_zero_range(high, low)
    pdist += non_zero_range(open_, close.shift(drift)).abs()
    pdist -= non_zero_range(close, open_).abs()

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

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

    # 指定結果的名稱和分類
    pdist.name = "PDIST"
    pdist.category = "volatility"

    return pdist

# 為 pdist 函式新增文件字串
pdist.__doc__ = \
"""Price Distance (PDIST)

Measures the "distance" covered by price movements.

Sources:
    https://www.prorealcode.com/prorealtime-indicators/pricedistance/

Calculation:
    Default Inputs:
        drift=1

    PDIST = 2(high - low) - ABS(close - open) + ABS(open - close[drift])

Args:
    open_ (pd.Series): Series of 'opens's
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series): Series of 'close's
    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\volatility\rvi.py

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


# 定義Relative Volatility Index (RVI)指標函式
def rvi(close, high=None, low=None, length=None, scalar=None, refined=None, thirds=None, mamode=None, drift=None, offset=None, **kwargs):
    """Indicator: Relative Volatility Index (RVI)"""
    # 驗證引數
    length = int(length) if length and length > 0 else 14  # 如果length為正整數則保留,否則預設為14
    scalar = float(scalar) if scalar and scalar > 0 else 100  # 如果scalar為正浮點數則保留,否則預設為100
    refined = False if refined is None else refined  # 如果refined為None則預設為False
    thirds = False if thirds is None else thirds  # 如果thirds為None則預設為False
    mamode = mamode if isinstance(mamode, str) else "ema"  # 如果mamode為字串則保留,否則預設為"ema"
    close = verify_series(close, length)  # 驗證close是否為有效序列
    drift = get_drift(drift)  # 獲取漂移引數
    offset = get_offset(offset)  # 獲取偏移引數

    if close is None: return

    if refined or thirds:
        high = verify_series(high)  # 驗證high是否為有效序列
        low = verify_series(low)  # 驗證low是否為有效序列

    # 計算結果
    def _rvi(source, length, scalar, mode, drift):
        """RVI"""
        # 計算標準差
        std = stdev(source, length)
        # 獲取正差值和負差值
        pos, neg = unsigned_differences(source, amount=drift)

        # 計算正差值的標準差加權平均
        pos_std = pos * std
        pos_avg = ma(mode, pos_std, length=length)
        # 計算負差值的標準差加權平均
        neg_std = neg * std
        neg_avg = ma(mode, neg_std, length=length)

        # 計算RVI指標
        result = scalar * pos_avg
        result /= pos_avg + neg_avg
        return result

    _mode = ""
    if refined:  # 如果使用了refined模式
        # 計算高價RVI
        high_rvi = _rvi(high, length, scalar, mamode, drift)
        # 計算低價RVI
        low_rvi = _rvi(low, length, scalar, mamode, drift)
        # 計算RVI
        rvi = 0.5 * (high_rvi + low_rvi)
        _mode = "r"  # 設定模式為"r"
    elif thirds:  # 如果使用了thirds模式
        # 計算高價RVI
        high_rvi = _rvi(high, length, scalar, mamode, drift)
        # 計算低價RVI
        low_rvi = _rvi(low, length, scalar, mamode, drift)
        # 計算收盤價RVI
        close_rvi = _rvi(close, length, scalar, mamode, drift)
        # 計算RVI
        rvi = (high_rvi + low_rvi + close_rvi) / 3.0
        _mode = "t"  # 設定模式為"t"
    else:  # 如果未使用refined和thirds模式
        # 計算RVI
        rvi = _rvi(close, length, scalar, mamode, drift)

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

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

    # 命名和歸類
    rvi.name = f"RVI{_mode}_{length}"
    rvi.category = "volatility"

    return rvi


# 設定RVI指標的文件字串
rvi.__doc__ = \
"""Relative Volatility Index (RVI)

The Relative Volatility Index (RVI) was created in 1993 and revised in 1995.
Instead of adding up price changes like RSI based on price direction, the RVI
adds up standard deviations based on price direction.

Sources:
    https://www.tradingview.com/wiki/Keltner_Channels_(KC)

Calculation:
    Default Inputs:
        length=14, scalar=100, refined=None, thirds=None
    EMA = Exponential Moving Average
    STDEV = Standard Deviation

    UP = STDEV(src, length) IF src.diff() > 0 ELSE 0
    DOWN = STDEV(src, length) IF src.diff() <= 0 ELSE 0

    UPSUM = EMA(UP, length)
    DOWNSUM = EMA(DOWN, length
"""
    # 計算相對強度指數(RSI),其計算公式為 RVI = scalar * (UPSUM / (UPSUM + DOWNSUM))
    RVI = scalar * (UPSUM / (UPSUM + DOWNSUM))
# 定義一個函式,用於計算布林帶指標(Bollinger Bands)
def bollinger_hband_indicator(high, low, close, length=14, scalar=100, refined=False, thirds=False, mamode='ema', offset=0, fillna=None, fill_method=None):
    """
    Args:
        high (pd.Series): 最高價的序列
        low (pd.Series): 最低價的序列
        close (pd.Series): 收盤價的序列
        length (int): 短週期。預設值為14
        scalar (float): 縮放帶的正浮點數。預設值為100
        refined (bool): 使用“精煉”計算,即 RVI(high) 和 RVI(low) 的平均值,而不是 RVI(close)。預設值為False
        thirds (bool): 使用最高價、最低價和收盤價的平均值。預設值為False
        mamode (str): 參見 ```help(ta.ma)```py。預設值為'ema'
        offset (int): 結果的偏移週期數。預設值為0

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

    Returns:
        pd.DataFrame: 包含 lower、basis、upper 列的資料框
    """

.\pandas-ta\pandas_ta\volatility\thermo.py

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

# 定義函式 thermo,計算 Elder's Thermometer 指標
def thermo(high, low, length=None, long=None, short=None, mamode=None, drift=None, offset=None, **kwargs):
    """Indicator: Elders Thermometer (THERMO)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則轉換為整數,否則預設為 20
    length = int(length) if length and length > 0 else 20
    # 如果 long 存在且大於 0,則轉換為浮點數,否則預設為 2
    long = float(long) if long and long > 0 else 2
    # 如果 short 存在且大於 0,則轉換為浮點數,否則預設為 0.5
    short = float(short) if short and short > 0 else 0.5
    # 如果 mamode 是字串,則保持不變,否則預設為 "ema"
    mamode = mamode if isinstance(mamode, str) else "ema"
    # 驗證 high 和 low 系列資料,長度為 length
    high = verify_series(high, length)
    low = verify_series(low, length)
    # 獲取 drift 和 offset
    drift = get_drift(drift)
    offset = get_offset(offset)
    # 從 kwargs 中彈出 asint 引數,預設為 True
    asint = kwargs.pop("asint", True)

    # 如果 high 或 low 為 None,則返回
    if high is None or low is None: return

    # 計算結果
    # 計算 Elder's Thermometer 的下限
    thermoL = (low.shift(drift) - low).abs()
    # 計算 Elder's Thermometer 的上限
    thermoH = (high - high.shift(drift)).abs()

    # 取較小的值作為 Elder's Thermometer
    thermo = thermoL
    thermo = thermo.where(thermoH < thermoL, thermoH)
    # 索引設定為 high 的索引
    thermo.index = high.index

    # 計算 Elder's Thermometer 的移動平均線
    thermo_ma = ma(mamode, thermo, length=length)

    # 生成訊號
    thermo_long = thermo < (thermo_ma * long)
    thermo_short = thermo > (thermo_ma * short)

    # 二進位制輸出,用於訊號
    if asint:
        thermo_long = thermo_long.astype(int)
        thermo_short = thermo_short.astype(int)

    # 調整偏移量
    if offset != 0:
        thermo = thermo.shift(offset)
        thermo_ma = thermo_ma.shift(offset)
        thermo_long = thermo_long.shift(offset)
        thermo_short = thermo_short.shift(offset)

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

    # 設定名稱和分類
    _props = f"_{length}_{long}_{short}"
    thermo.name = f"THERMO{_props}"
    thermo_ma.name = f"THERMOma{_props}"
    thermo_long.name = f"THERMOl{_props}"
    thermo_short.name = f"THERMOs{_props}"

    thermo.category = thermo_ma.category = thermo_long.category = thermo_short.category = "volatility"

    # 準備返回的 DataFrame
    data = {
        thermo.name: thermo,
        thermo_ma.name: thermo_ma,
        thermo_long.name: thermo_long,
        thermo_short.name: thermo_short
    }
    df = DataFrame(data)
    df.name = f"THERMO{_props}"
    df.category = thermo.category

    return df

# 為 thermo 函式新增文件字串
thermo.__doc__ = \
"""Elders Thermometer (THERMO)

Elder's Thermometer measures price volatility.

Sources:
    https://www.motivewave.com/studies/elders_thermometer.htm
    # 匯入所需的庫
    import requests
    
    # 定義函式`get_tradingview_script`用於獲取TradingView上的指令碼內容
    def get_tradingview_script(url):
        # 傳送GET請求獲取指定URL的頁面內容
        response = requests.get(url)
        # 返回頁面內容的文字
        return response.text
    
    # 定義變數`script_url`,儲存TradingView指令碼的URL
    script_url = "https://www.tradingview.com/script/HqvTuEMW-Elder-s-Market-Thermometer-LazyBear/"
    
    # 呼叫`get_tradingview_script`函式,獲取指定URL的指令碼內容
    script_content = get_tradingview_script(script_url)
# 計算熱力指標(thermo)和相關指標
Calculation:
    # 預設輸入引數
    length=20, drift=1, mamode=EMA, long=2, short=0.5
    # EMA為指數移動平均

    # 計算低價的漂移
    thermoL = (low.shift(drift) - low).abs()
    # 計算高價的漂移
    thermoH = (high - high.shift(drift)).abs()

    # 選擇較大的漂移值
    thermo = np.where(thermoH > thermoL, thermoH, thermoL)
    # 對漂移值進行指數移動平均
    thermo_ma = ema(thermo, length)

    # 判斷是否滿足買入條件
    thermo_long = thermo < (thermo_ma * long)
    # 判斷是否滿足賣出條件
    thermo_short = thermo > (thermo_ma * short)
    # 將布林值轉換為整數
    thermo_long = thermo_long.astype(int)
    thermo_short = thermo_short.astype(int)

Args:
    high (pd.Series): 'high' 的序列
    low (pd.Series): 'low' 的序列
    long(int): 買入因子
    short(float): 賣出因子
    length (int): 週期。預設值:20
    mamode (str): 參見 ```help(ta.ma)```py。預設值:'ema'
    drift (int): 漂移週期。預設值:1
    offset (int): 結果的偏移週期數。預設值:0

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

Returns:
    pd.DataFrame: 包含 thermo, thermo_ma, thermo_long, thermo_short 列的資料框

.\pandas-ta\pandas_ta\volatility\true_range.py

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

# 定義一個名為 true_range 的函式,用於計算真實波動範圍
def true_range(high, low, close, talib=None, drift=None, offset=None, **kwargs):
    """Indicator: True Range"""
    # 驗證引數
   high = verify_series(high)
    low = verify_series(low)
    close = verify_series(close)
    drift = get_drift(drift)
    offset = get_offset(offset)
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 計算結果
   if Imports["talib"] and mode_tal:
        # 如果匯入了 talib 庫並且 mode_tal 為 True,則使用 talib 庫中的 TRANGE 函式計算真實波動範圍
        from talib import TRANGE
        true_range = TRANGE(high, low, close)
    else:
        # 否則,計算高低範圍、前一日收盤價、以及真實波動範圍
        high_low_range = non_zero_range(high, low)
        prev_close = close.shift(drift)
        ranges = [high_low_range, high - prev_close, prev_close - low]
        true_range = concat(ranges, axis=1)
        true_range = true_range.abs().max(axis=1)
        true_range.iloc[:drift] = npNaN

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

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

    # 命名和分類
    true_range.name = f"TRUERANGE_{drift}"
    true_range.category = "volatility"

    return true_range

# 設定 true_range 函式的文件字串
true_range.__doc__ = \
"""True Range

An method to expand a classical range (high minus low) to include
possible gap scenarios.

Sources:
    https://www.macroption.com/true-range/

Calculation:
    Default Inputs:
        drift=1
    ABS = Absolute Value
    prev_close = close.shift(drift)
    TRUE_RANGE = ABS([high - low, high - prev_close, low - prev_close])

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    close (pd.Series): Series of 'close's
    talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib
        version. Default: True
    drift (int): The shift 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
"""

.\pandas-ta\pandas_ta\volatility\ui.py

# -*- coding: utf-8 -*-
# 從 numpy 庫匯入 sqrt 函式並重新命名為 npsqrt
from numpy import sqrt as npsqrt
# 從 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


# 定義函式 ui,計算 Ulcer Index(UI)
def ui(close, length=None, scalar=None, offset=None, **kwargs):
    """Indicator: Ulcer Index (UI)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則轉換為整數,否則預設為 14
    length = int(length) if length and length > 0 else 14
    # 如果 scalar 存在且大於 0,則轉換為浮點數,否則預設為 100
    scalar = float(scalar) if scalar and scalar > 0 else 100
    # 驗證 close 是否為有效的時間序列,並指定長度為 length
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)

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

    # 計算結果
    # 計算最近 length 個週期內的最高收盤價
    highest_close = close.rolling(length).max()
    # 計算下行波動性
    downside = scalar * (close - highest_close)
    downside /= highest_close
    d2 = downside * downside

    # 獲取 everget 引數,預設為 False
    everget = kwargs.pop("everget", False)
    # 如果 everget 為 True,則使用 SMA 而不是 SUM 進行計算
    if everget:
        # 使用 SMA 對 d2 進行計算,然後應用 npsqrt 函式
        ui = (sma(d2, length) / length).apply(npsqrt)
    else:
        # 使用 SUM 對 d2 進行計算,然後應用 npsqrt 函式
        ui = (d2.rolling(length).sum() / length).apply(npsqrt)

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

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

    # 設定指標名稱和分類
    ui.name = f"UI{'' if not everget else 'e'}_{length}"
    ui.category = "volatility"

    # 返回結果
    return ui


# 設定 ui 函式的文件字串
ui.__doc__ = \
"""Ulcer Index (UI)

The Ulcer Index by Peter Martin measures the downside volatility with the use of
the Quadratic Mean, which has the effect of emphasising large drawdowns.

Sources:
    https://library.tradingtechnologies.com/trade/chrt-ti-ulcer-index.html
    https://en.wikipedia.org/wiki/Ulcer_index
    http://www.tangotools.com/ui/ui.htm

Calculation:
    Default Inputs:
        length=14, scalar=100
    HC = Highest Close
    SMA = Simple Moving Average

    HCN = HC(close, length)
    DOWNSIDE = scalar * (close - HCN) / HCN
    if kwargs["everget"]:
        UI = SQRT(SMA(DOWNSIDE^2, length) / length)
    else:
        UI = SQRT(SUM(DOWNSIDE^2, length) / length)

Args:
    high (pd.Series): Series of 'high's
    close (pd.Series): Series of 'close's
    length (int): The short period.  Default: 14
    scalar (float): A positive float to scale the bands. Default: 100
    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
    everget (value, optional): TradingView's Evergets SMA instead of SUM
        calculation. Default: False

Returns:
    pd.Series: New feature
"""

.\pandas-ta\pandas_ta\volatility\__init__.py

# 設定檔案編碼格式為 UTF-8
# 匯入模組中的各種函式和類
from .aberration import aberration  # 匯入 aberration 模組中的函式或類
from .accbands import accbands  # 匯入 accbands 模組中的函式或類
from .atr import atr  # 匯入 atr 模組中的函式或類
from .bbands import bbands  # 匯入 bbands 模組中的函式或類
from .donchian import donchian  # 匯入 donchian 模組中的函式或類
from .hwc import hwc  # 匯入 hwc 模組中的函式或類
from .kc import kc  # 匯入 kc 模組中的函式或類
from .massi import massi  # 匯入 massi 模組中的函式或類
from .natr import natr  # 匯入 natr 模組中的函式或類
from .pdist import pdist  # 匯入 pdist 模組中的函式或類
from .rvi import rvi  # 匯入 rvi 模組中的函式或類
from .thermo import thermo  # 匯入 thermo 模組中的函式或類
from .true_range import true_range  # 匯入 true_range 模組中的函式或類
from .ui import ui  # 匯入 ui 模組中的函式或類

.\pandas-ta\pandas_ta\volume\ad.py

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

# 定義累積/分佈指標函式
def ad(high, low, close, volume, open_=None, talib=None, offset=None, **kwargs):
    """Indicator: Accumulation/Distribution (AD)"""
    # 驗證引數
    # 將 high、low、close、volume 分別驗證為 pandas.Series 型別
    high = verify_series(high)
    low = verify_series(low)
    close = verify_series(close)
    volume = verify_series(volume)
    # 將 offset 轉換為偏移量
    offset = get_offset(offset)
    # 判斷是否使用 talib,若 talib 引數為布林型別則以其值為準,否則預設為 True
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 如果匯入了 talib 並且 mode_tal 為真,則使用 talib 計算 AD 指標
        from talib import AD
        # 呼叫 talib 庫中的 AD 函式
        ad = AD(high, low, close, volume)
    else:
        # 如果沒有使用 talib 或者 mode_tal 為假,則根據情況計算 AD 指標
        if open_ is not None:
            # 如果提供了 open_ 引數,則使用 close 和 open_ 計算 AD
            open_ = verify_series(open_)
            # 計算 AD 指標,使用 close 和 open_
            ad = non_zero_range(close, open_)  # AD with Open
        else:
            # 如果未提供 open_ 引數,則使用 high、low 和 close 計算 AD
            ad = 2 * close - (high + low)  # AD with High, Low, Close

        # 計算 high-low 範圍
        high_low_range = non_zero_range(high, low)
        # 根據 high-low 範圍和交易量計算 AD
        ad *= volume / high_low_range
        # 對 AD 進行累積求和
        ad = ad.cumsum()

    # 偏移結果
    if offset != 0:
        # 如果偏移量不為零,則對 AD 進行偏移
        ad = ad.shift(offset)

    # 處理填充
    if "fillna" in kwargs:
        # 如果提供了 fillna 引數,則使用提供的值填充缺失值
        ad.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        # 如果提供了 fill_method 引數,則使用提供的填充方法填充缺失值
        ad.fillna(method=kwargs["fill_method"], inplace=True)

    # 命名和分類
    # 根據是否提供了 open_ 引數命名 AD 指標
    ad.name = "AD" if open_ is None else "ADo"
    # 將 AD 指標分類為“volume”
    ad.category = "volume"

    # 返回 AD 指標
    return ad


# 設定 AD 函式的文件字串
ad.__doc__ = \
"""Accumulation/Distribution (AD)

Accumulation/Distribution indicator utilizes the relative position
of the close to it's High-Low range with volume.  Then it is cumulated.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/accumulationdistribution-ad/

Calculation:
    CUM = Cumulative Sum
    if 'open':
        AD = close - open
    else:
        AD = 2 * close - high - low

    hl_range = high - low
    AD = AD * volume / hl_range
    AD = CUM(AD)

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
    open (pd.Series): Series of 'open's
    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.
"""

相關文章