PandasTA 原始碼解析(十五)

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

.\pandas-ta\pandas_ta\utils\_signals.py

# -*- coding: utf-8 -*-
# 匯入 DataFrame 和 Series 類
from pandas import DataFrame, Series

# 匯入自定義函式
from ._core import get_offset, verify_series
from ._math import zero

# 定義函式 _above_below,用於比較兩個 Series 物件的大小關係
def _above_below(series_a: Series, series_b: Series, above: bool = True, asint: bool = True, offset: int = None, **kwargs):
    # 確保 series_a 和 series_b 是 Series 物件
    series_a = verify_series(series_a)
    series_b = verify_series(series_b)
    offset = get_offset(offset)

    # 將 series_a 和 series_b 中的零值替換為 NaN
    series_a.apply(zero)
    series_b.apply(zero)

    # 計算結果
    if above:
        current = series_a >= series_b
    else:
        current = series_a <= series_b

    if asint:
        current = current.astype(int)

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

    # 設定名稱和類別
    current.name = f"{series_a.name}_{'A' if above else 'B'}_{series_b.name}"
    current.category = "utility"

    return current

# 定義函式 above,用於比較兩個 Series 物件的大小關係,series_a 大於等於 series_b
def above(series_a: Series, series_b: Series, asint: bool = True, offset: int = None, **kwargs):
    return _above_below(series_a, series_b, above=True, asint=asint, offset=offset, **kwargs)

# 定義函式 above_value,用於比較 Series 物件和給定值的大小關係,series_a 大於等於 value
def above_value(series_a: Series, value: float, asint: bool = True, offset: int = None, **kwargs):
    if not isinstance(value, (int, float, complex)):
        print("[X] value is not a number")
        return
    series_b = Series(value, index=series_a.index, name=f"{value}".replace(".", "_"))

    return _above_below(series_a, series_b, above=True, asint=asint, offset=offset, **kwargs)

# 定義函式 below,用於比較兩個 Series 物件的大小關係,series_a 小於等於 series_b
def below(series_a: Series, series_b: Series, asint: bool = True, offset: int = None, **kwargs):
    return _above_below(series_a, series_b, above=False, asint=asint, offset=offset, **kwargs)

# 定義函式 below_value,用於比較 Series 物件和給定值的大小關係,series_a 小於等於 value
def below_value(series_a: Series, value: float, asint: bool = True, offset: int = None, **kwargs):
    if not isinstance(value, (int, float, complex)):
        print("[X] value is not a number")
        return
    series_b = Series(value, index=series_a.index, name=f"{value}".replace(".", "_"))
    return _above_below(series_a, series_b, above=False, asint=asint, offset=offset, **kwargs)

# 定義函式 cross_value,用於判斷 Series 物件和給定值是否交叉,above 為 True 表示交叉在上方
def cross_value(series_a: Series, value: float, above: bool = True, asint: bool = True, offset: int = None, **kwargs):
    series_b = Series(value, index=series_a.index, name=f"{value}".replace(".", "_"))

    return cross(series_a, series_b, above, asint, offset, **kwargs)

# 定義函式 cross,用於判斷兩個 Series 物件是否交叉,above 為 True 表示交叉在上方
def cross(series_a: Series, series_b: Series, above: bool = True, asint: bool = True, offset: int = None, **kwargs):
    series_a = verify_series(series_a)
    series_b = verify_series(series_b)
    offset = get_offset(offset)

    series_a.apply(zero)
    series_b.apply(zero)

    # 計算結果
    current = series_a > series_b  # current is above
    previous = series_a.shift(1) < series_b.shift(1)  # previous is below
    # above if both are true, below if both are false
    cross = current & previous if above else ~current & ~previous

    if asint:
        cross = cross.astype(int)

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

    # 設定名稱和類別
    # 設定交叉系列的名稱,根據條件選擇不同的字尾
    cross.name = f"{series_a.name}_{'XA' if above else 'XB'}_{series_b.name}"
    # 設定交叉系列的類別為"utility"
    cross.category = "utility"

    # 返回交叉系列物件
    return cross
# 根據給定的指標、閾值和引數,生成包含交叉訊號的資料框
def signals(indicator, xa, xb, cross_values, xserie, xserie_a, xserie_b, cross_series, offset) -> DataFrame:
    # 建立一個空的資料框
    df = DataFrame()
    
    # 如果 xa 不為空且為整數或浮點數型別
    if xa is not None and isinstance(xa, (int, float)):
        # 如果需要計算交叉值
        if cross_values:
            # 計算指標在閾值 xa 以上交叉的起始點
            crossed_above_start = cross_value(indicator, xa, above=True, offset=offset)
            # 計算指標在閾值 xa 以上交叉的結束點
            crossed_above_end = cross_value(indicator, xa, above=False, offset=offset)
            # 將交叉訊號起始點和結束點新增到資料框中
            df[crossed_above_start.name] = crossed_above_start
            df[crossed_above_end.name] = crossed_above_end
        else:
            # 計算指標在閾值 xa 以上的訊號
            crossed_above = above_value(indicator, xa, offset=offset)
            # 將訊號新增到資料框中
            df[crossed_above.name] = crossed_above

    # 如果 xb 不為空且為整數或浮點數型別
    if xb is not None and isinstance(xb, (int, float)):
        # 如果需要計算交叉值
        if cross_values:
            # 計算指標在閾值 xb 以下交叉的起始點
            crossed_below_start = cross_value(indicator, xb, above=True, offset=offset)
            # 計算指標在閾值 xb 以下交叉的結束點
            crossed_below_end = cross_value(indicator, xb, above=False, offset=offset)
            # 將交叉訊號起始點和結束點新增到資料框中
            df[crossed_below_start.name] = crossed_below_start
            df[crossed_below_end.name] = crossed_below_end
        else:
            # 計算指標在閾值 xb 以下的訊號
            crossed_below = below_value(indicator, xb, offset=offset)
            # 將訊號新增到資料框中
            df[crossed_below.name] = crossed_below

    # 如果 xserie_a 為空,則使用預設值 xserie
    if xserie_a is None:
        xserie_a = xserie
    # 如果 xserie_b 為空,則使用預設值 xserie
    if xserie_b is None:
        xserie_b = xserie

    # 如果 xserie_a 不為空且為有效的資料序列
    if xserie_a is not None and verify_series(xserie_a):
        # 如果需要計算交叉序列
        if cross_series:
            # 計算指標與 xserie_a 交叉的起始點
            cross_serie_above = cross(indicator, xserie_a, above=True, offset=offset)
        else:
            # 計算指標在 xserie_a 以上的訊號
            cross_serie_above = above(indicator, xserie_a, offset=offset)
        
        # 將訊號新增到資料框中
        df[cross_serie_above.name] = cross_serie_above

    # 如果 xserie_b 不為空且為有效的資料序列
    if xserie_b is not None and verify_series(xserie_b):
        # 如果需要計算交叉序列
        if cross_series:
            # 計算指標與 xserie_b 交叉的起始點
            cross_serie_below = cross(indicator, xserie_b, above=False, offset=offset)
        else:
            # 計算指標在 xserie_b 以下的訊號
            cross_serie_below = below(indicator, xserie_b, offset=offset)
        
        # 將訊號新增到資料框中
        df[cross_serie_below.name] = cross_serie_below

    # 返回生成的資料框
    return df

.\pandas-ta\pandas_ta\utils\_time.py

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

# 從 datetime 模組中匯入 datetime 類
from datetime import datetime
# 從 time 模組中匯入 localtime 和 perf_counter 函式
from time import localtime, perf_counter
# 從 typing 模組中匯入 Tuple 型別
from typing import Tuple
# 從 pandas 模組中匯入 DataFrame 和 Timestamp 類
from pandas import DataFrame, Timestamp
# 從 pandas_ta 模組中匯入 EXCHANGE_TZ 和 RATE 變數
from pandas_ta import EXCHANGE_TZ, RATE

# 定義函式 df_dates,接受一個 DataFrame 和日期元組作為引數,返回過濾後的 DataFrame
def df_dates(df: DataFrame, dates: Tuple[str, list] = None) -> DataFrame:
    """Yields the DataFrame with the given dates"""
    # 若日期元組為空,則返回 None
    if dates is None: return None
    # 如果日期元組不是列表型別,則將其轉換為列表
    if not isinstance(dates, list):
        dates = [dates]
    # 返回過濾後的 DataFrame,只包含日期元組中指定的日期
    return df[df.index.isin(dates)]

# 定義函式 df_month_to_date,接受一個 DataFrame 作為引數,返回當月的 DataFrame
def df_month_to_date(df: DataFrame) -> DataFrame:
    """Yields the Month-to-Date (MTD) DataFrame"""
    # 獲取當前日期的月初日期,並判斷 DataFrame 的索引是否大於等於該日期
    in_mtd = df.index >= Timestamp.now().strftime("%Y-%m-01")
    # 如果有資料在當月,則返回當月的 DataFrame
    if any(in_mtd): return df[in_mtd]
    # 否則返回原始 DataFrame
    return df

# 定義函式 df_quarter_to_date,接受一個 DataFrame 作為引數,返回當季的 DataFrame
def df_quarter_to_date(df: DataFrame) -> DataFrame:
    """Yields the Quarter-to-Date (QTD) DataFrame"""
    # 獲取當前日期,並遍歷季度開始的月份
    now = Timestamp.now()
    for m in [1, 4, 7, 10]:
        # 如果當前月份小於等於遍歷到的月份
        if now.month <= m:
                # 獲取季度開始日期,並判斷 DataFrame 的索引是否大於等於該日期
                in_qtr = df.index >= datetime(now.year, m, 1).strftime("%Y-%m-01")
                # 如果有資料在當季,則返回當季的 DataFrame
                if any(in_qtr): return df[in_qtr]
    # 否則返回從當前月份開始的 DataFrame
    return df[df.index >= now.strftime("%Y-%m-01")]

# 定義函式 df_year_to_date,接受一個 DataFrame 作為引數,返回當年的 DataFrame
def df_year_to_date(df: DataFrame) -> DataFrame:
    """Yields the Year-to-Date (YTD) DataFrame"""
    # 獲取當前日期的年初日期,並判斷 DataFrame 的索引是否大於等於該日期
    in_ytd = df.index >= Timestamp.now().strftime("%Y-01-01")
    # 如果有資料在當年,則返回當年的 DataFrame
    if any(in_ytd): return df[in_ytd]
    # 否則返回原始 DataFrame
    return df

# 定義函式 final_time,接受一個起始時間引數,返回從起始時間到當前時間的耗時
def final_time(stime: float) -> str:
    """Human readable elapsed time. Calculates the final time elasped since
    stime and returns a string with microseconds and seconds."""
    # 計算當前時間與起始時間的差值
    time_diff = perf_counter() - stime
    # 返回耗時的字串,包含毫秒和秒
    return f"{time_diff * 1000:2.4f} ms ({time_diff:2.4f} s)"

# 定義函式 get_time,接受交易所名稱、是否顯示全資訊和是否返回字串作為引數,返回當前時間及交易所時間資訊
def get_time(exchange: str = "NYSE", full:bool = True, to_string:bool = False) -> Tuple[None, str]:
    """Returns Current Time, Day of the Year and Percentage, and the current
    time of the selected Exchange."""
    # 預設交易所時區為東部時間(NYSE)
    tz = EXCHANGE_TZ["NYSE"] 
    # 如果傳入的交易所名稱為字串型別
    if isinstance(exchange, str):
        # 將交易所名稱轉換為大寫
        exchange = exchange.upper()
        # 獲取對應交易所的時區資訊
        tz = EXCHANGE_TZ[exchange]

    # 獲取當前時間
    today = Timestamp.now()
    # 格式化日期字串
    date = f"{today.day_name()} {today.month_name()} {today.day}, {today.year}"

    # 獲取當前時間在交易所時區的時間
    _today = today.timetuple()
    exchange_time = f"{(_today.tm_hour + tz) % 24}:{_today.tm_min:02d}:{_today.tm_sec:02d}"

    # 如果需要顯示全資訊
    if full:
        # 獲取本地時間資訊
        lt = localtime()
        local_ = f"Local: {lt.tm_hour}:{lt.tm_min:02d}:{lt.tm_sec:02d} {lt.tm_zone}"
        # 計算當天在一年中的日期和百分比
        doy = f"Day {today.dayofyear}/365 ({100 * round(today.dayofyear/365, 2):.2f}%)"
        exchange_ = f"{exchange}: {exchange_time}"

        # 構建包含完整資訊的字串
        s = f"{date}, {exchange_}, {local_}, {doy}"
    else:
        # 構建簡略資訊的字串
        s = f"{date}, {exchange}: {exchange_time}"

    # 如果需要返回字串,則返回構建的字串,否則列印字串並返回 None
    return s if to_string else print(s)

# 定義函式 total_time,接受一個 DataFrame 和時間間隔型別引數作為輸入,返回 DataFrame 的總時間間隔
def total_time(df: DataFrame, tf: str = "years") -> float:
    """Calculates the total time of a DataFrame. Difference of the Last and
    First index. Options: 'months', 'weeks', 'days', 'hours', 'minutes'
    and 'seconds'. Default: 'years'.
    Useful for annualization."""
    # 計算 DataFrame 的總時間間
    TimeFrame = {
        "years": time_diff.days / RATE["TRADING_DAYS_PER_YEAR"],  # 計算時間差對應的年數
        "months": time_diff.days / 30.417,  # 計算時間差對應的月數
        "weeks": time_diff.days / 7,  # 計算時間差對應的週數
        "days": time_diff.days,  # 計算時間差對應的天數
        "hours": time_diff.days * 24,  # 計算時間差對應的小時數
        "minutes": time_diff.total_seconds() / 60,  # 計算時間差對應的分鐘數
        "seconds": time_diff.total_seconds()  # 計算時間差對應的秒數
    }

    if isinstance(tf, str) and tf in TimeFrame.keys():  # 檢查 tf 是否為字串且在 TimeFrame 字典的鍵中
        return TimeFrame[tf]  # 返回對應 tf 的時間差
    return TimeFrame["years"]  # 如果 tf 不在 TimeFrame 字典的鍵中,則返回預設的年數時間差
# 將 DataFrame 的索引轉換為 UTC 時區,或者使用 tz_convert 將索引設定為 UTC 時區
def to_utc(df: DataFrame) -> DataFrame:
    # 檢查 DataFrame 是否為空
    if not df.empty:
        try:
            # 嘗試將索引本地化為 UTC 時區
            df.index = df.index.tz_localize("UTC")
        except TypeError:
            # 如果出現 TypeError,則使用 tz_convert 將索引轉換為 UTC 時區
            df.index = df.index.tz_convert("UTC")
    # 返回處理後的 DataFrame
    return df

# 別名
mtd = df_month_to_date
qtd = df_quarter_to_date
ytd = df_year_to_date

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

# 設定檔案編碼為UTF-8,確保可以正確處理中文等特殊字元
# 匯入模組中的所有內容,這些模組包括 _candles、_core、_math、_signals、_time、_metrics 和 data
from ._candles import *
from ._core import *
from ._math import *
from ._signals import *
from ._time import *
from ._metrics import *
from .data import *

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

# -*- coding: utf-8 -*-
# from numpy import sqrt as npsqrt  # 匯入 numpy 中的 sqrt 函式並重新命名為 npsqrt(已註釋掉)
from pandas import DataFrame  # 從 pandas 庫中匯入 DataFrame 類
from .atr import atr  # 從當前包中的 atr 模組中匯入 atr 函式
from pandas_ta.overlap import hlc3, sma  # 從 pandas_ta.overlap 模組中匯入 hlc3 和 sma 函式
from pandas_ta.utils import get_offset, verify_series  # 從 pandas_ta.utils 模組中匯入 get_offset 和 verify_series 函式


def aberration(high, low, close, length=None, atr_length=None, offset=None, **kwargs):
    """Indicator: Aberration (ABER)"""
    # Validate arguments
    # 確認引數合法性,若引數未指定或非正整數,則使用預設值
    length = int(length) if length and length > 0 else 5
    atr_length = int(atr_length) if atr_length and atr_length > 0 else 15
    _length = max(atr_length, length)  # 選擇最大長度作為計算時使用的長度
    high = verify_series(high, _length)  # 確認 high Series 的合法性和長度
    low = verify_series(low, _length)  # 確認 low Series 的合法性和長度
    close = verify_series(close, _length)  # 確認 close Series 的合法性和長度
    offset = get_offset(offset)  # 獲取偏移量

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

    # Calculate Result
    # 計算結果
    atr_ = atr(high=high, low=low, close=close, length=atr_length)  # 計算 ATR 指標
    jg = hlc3(high=high, low=low, close=close)  # 計算 JG(typical price,即三價均價)

    zg = sma(jg, length)  # 計算 ZG(SMA of JG)
    sg = zg + atr_  # 計算 SG(ZG + ATR)
    xg = zg - atr_  # 計算 XG(ZG - ATR)

    # Offset
    # 偏移結果
    if offset != 0:
        zg = zg.shift(offset)  # 對 ZG 進行偏移
        sg = sg.shift(offset)  # 對 SG 進行偏移
        xg = xg.shift(offset)  # 對 XG 進行偏移
        atr_ = atr_.shift(offset)  # 對 ATR 進行偏移

    # Handle fills
    # 處理填充缺失值
    if "fillna" in kwargs:
        zg.fillna(kwargs["fillna"], inplace=True)  # 使用指定值填充 ZG 中的缺失值
        sg.fillna(kwargs["fillna"], inplace=True)  # 使用指定值填充 SG 中的缺失值
        xg.fillna(kwargs["fillna"], inplace=True)  # 使用指定值填充 XG 中的缺失值
        atr_.fillna(kwargs["fillna"], inplace=True)  # 使用指定值填充 ATR 中的缺失值
    if "fill_method" in kwargs:
        zg.fillna(method=kwargs["fill_method"], inplace=True)  # 使用指定的填充方法填充 ZG 中的缺失值
        sg.fillna(method=kwargs["fill_method"], inplace=True)  # 使用指定的填充方法填充 SG 中的缺失值
        xg.fillna(method=kwargs["fill_method"], inplace=True)  # 使用指定的填充方法填充 XG 中的缺失值
        atr_.fillna(method=kwargs["fill_method"], inplace=True)  # 使用指定的填充方法填充 ATR 中的缺失值

    # Name and Categorize it
    # 命名和分類
    _props = f"_{length}_{atr_length}"  # 用於生成屬性名稱的字尾
    zg.name = f"ABER_ZG{_props}"  # 設定 ZG Series 的名稱
    sg.name = f"ABER_SG{_props}"  # 設定 SG Series 的名稱
    xg.name = f"ABER_XG{_props}"  # 設定 XG Series 的名稱
    atr_.name = f"ABER_ATR{_props}"  # 設定 ATR Series 的名稱
    zg.category = sg.category = "volatility"  # 設定 ZG 和 SG Series 的分類為波動性
    xg.category = atr_.category = zg.category  # 設定 XG 和 ATR Series 的分類與 ZG 相同

    # Prepare DataFrame to return
    # 準備要返回的 DataFrame
    data = {zg.name: zg, sg.name: sg, xg.name: xg, atr_.name: atr_}  # 構建資料字典
    aberdf = DataFrame(data)  # 使用資料字典建立 DataFrame
    aberdf.name = f"ABER{_props}"  # 設定 DataFrame 的名稱
    aberdf.category = zg.category  # 設定 DataFrame 的分類與 ZG 相同

    return aberdf  # 返回計算結果的 DataFrame


aberration.__doc__ = \
"""Aberration

A volatility indicator similar to Keltner Channels.

Sources:
    Few internet resources on definitive definition.
    Request by Github user homily, issue #46

Calculation:
    Default Inputs:
        length=5, atr_length=15
    ATR = Average True Range
    SMA = Simple Moving Average

    ATR = ATR(length=atr_length)
    JG = TP = HLC3(high, low, close)
    ZG = SMA(JG, length)
    SG = ZG + ATR
    XG = ZG - ATR

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: 5
    atr_length (int): The short period. Default: 15
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
"""  # 設定 aberration 函式的文件字串
    fill_method (value, optional): 填充方法的型別
# 返回一個 pandas DataFrame,包含 zg、sg、xg、atr 列

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

# -*- coding: utf-8 -*-
# 從 pandas 庫中匯入 DataFrame 類
from pandas import DataFrame
# 從 pandas_ta 庫中的 overlap 模組中匯入 ma 函式
from pandas_ta.overlap import ma
# 從 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


# 定義函式 accbands,用於計算加速帶指標
def accbands(high, low, close, length=None, c=None, drift=None, mamode=None, offset=None, **kwargs):
    """Indicator: Acceleration Bands (ACCBANDS)"""
    # 驗證引數
    # 若 length 存在且大於 0,則將其轉換為整數型別,否則設為 20
    length = int(length) if length and length > 0 else 20
    # 若 c 存在且大於 0,則將其轉換為浮點數型別,否則設為 4
    c = float(c) if c and c > 0 else 4
    # 若 mamode 不為字串型別,則設為 "sma"
    mamode = mamode if isinstance(mamode, str) else "sma"
    # 驗證 high、low、close 系列,使其長度為 length
    high = verify_series(high, length)
    low = verify_series(low, length)
    close = verify_series(close, length)
    # 獲取 drift 和 offset
    drift = get_drift(drift)
    offset = get_offset(offset)

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

    # 計算結果
    # 計算 high 和 low 的非零範圍
    high_low_range = non_zero_range(high, low)
    # 計算 high_low_range 與 (high + low) 的比值
    hl_ratio = high_low_range / (high + low)
    # 將 hl_ratio 乘以 c
    hl_ratio *= c
    # 計算下軌線 _lower
    _lower = low * (1 - hl_ratio)
    # 計算上軌線 _upper
    _upper = high * (1 + hl_ratio)

    # 計算移動平均值
    lower = ma(mamode, _lower, length=length)
    mid = ma(mamode, close, length=length)
    upper = ma(mamode, _upper, length=length)

    # 對結果進行位移
    if offset != 0:
        lower = lower.shift(offset)
        mid = mid.shift(offset)
        upper = upper.shift(offset)

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

    # 命名和分類
    lower.name = f"ACCBL_{length}"
    mid.name = f"ACCBM_{length}"
    upper.name = f"ACCBU_{length}"
    mid.category = upper.category = lower.category = "volatility"

    # 準備返回的 DataFrame
    data = {lower.name: lower, mid.name: mid, upper.name: upper}
    accbandsdf = DataFrame(data)
    accbandsdf.name = f"ACCBANDS_{length}"
    accbandsdf.category = mid.category

    return accbandsdf


# 設定函式文件字串
accbands.__doc__ = \
"""Acceleration Bands (ACCBANDS)

Acceleration Bands created by Price Headley plots upper and lower envelope
bands around a simple moving average.

Sources:
    https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/acceleration-bands-abands/

Calculation:
    Default Inputs:
        length=10, c=4
    EMA = Exponential Moving Average
    SMA = Simple Moving Average
    HL_RATIO = c * (high - low) / (high + low)
    LOW = low * (1 - HL_RATIO)
    HIGH = high * (1 + HL_RATIO)

    if 'ema':
        LOWER = EMA(LOW, length)
        MID = EMA(close, length)
        UPPER = EMA(HIGH, length)
    else:
        LOWER = SMA(LOW, length)
        MID = SMA(close, length)
        UPPER = SMA(HIGH, length)

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

"""
    # 表示引數 `length` 是一個整數,代表週期。預設值為 10
    length (int): It's period. Default: 10
    # 表示引數 `c` 是一個整數,代表乘數。預設值為 4
    c (int): Multiplier. Default: 4
    # 表示引數 `mamode` 是一個字串,參見 `ta.ma` 的幫助文件。預設值為 'sma'
    mamode (str): See ```help(ta.ma)```py. Default: 'sma'
    # 表示引數 `drift` 是一個整數,代表差異週期。預設值為 1
    drift (int): The difference period. Default: 1
    # 表示引數 `offset` 是一個整數,代表結果的偏移週期數。預設值為 0
    offset (int): How many periods to offset the result. Default: 0
# 函式引數,用於指定填充缺失值的值,可選引數
fillna (value, optional): pd.DataFrame.fillna(value)
# 函式引數,指定填充方法的型別,可選引數
fill_method (value, optional): Type of fill method

# 返回值,返回一個 DataFrame,包含 lower、mid、upper 列
pd.DataFrame: lower, mid, upper columns.

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

# -*- coding: utf-8 -*-
# 匯入 true_range 模組
from .true_range import true_range
# 匯入 Imports 模組
from pandas_ta import Imports
# 匯入 ma 模組
from pandas_ta.overlap import ma
# 匯入 get_drift, get_offset, verify_series 模組
from pandas_ta.utils import get_drift, get_offset, verify_series

# 定義 ATR 函式,計算平均真實範圍
def atr(high, low, close, length=None, mamode=None, talib=None, drift=None, offset=None, **kwargs):
    """Indicator: Average True Range (ATR)"""
    # 驗證引數
    length = int(length) if length and length > 0 else 14
    mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma"
    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 ATR
        atr = ATR(high, low, close, length)
    else:
        tr = true_range(high=high, low=low, close=close, drift=drift)
        atr = ma(mamode, tr, length=length)

    percentage = kwargs.pop("percent", False)
    if percentage:
        atr *= 100 / close

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

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

    # 命名和分類
    atr.name = f"ATR{mamode[0]}_{length}{'p' if percentage else ''}"
    atr.category = "volatility"

    return atr

# 設定 ATR 函式的文件字串
atr.__doc__ = \
"""Average True Range (ATR)

Averge True Range is used to measure volatility, especially volatility caused by
gaps or limit moves.

Sources:
    https://www.tradingview.com/wiki/Average_True_Range_(ATR)

Calculation:
    Default Inputs:
        length=14, drift=1, percent=False
    EMA = Exponential Moving Average
    SMA = Simple Moving Average
    WMA = Weighted Moving Average
    RMA = WildeR's Moving Average
    TR = True Range

    tr = TR(high, low, close, drift)
    if 'ema':
        ATR = EMA(tr, length)
    elif 'sma':
        ATR = SMA(tr, length)
    elif 'wma':
        ATR = WMA(tr, length)
    else:
        ATR = RMA(tr, length)

    if percent:
        ATR *= 100 / 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): It's period. Default: 14
    mamode (str): See ```help(ta.ma)```py. Default: 'rma'
    talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib
        version. Default: True
    drift (int): The difference period. Default: 1
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    percent (bool, optional): Return as percentage. Default: False
    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\bbands.py

# -*- coding: utf-8 -*-
# 匯入所需的庫和模組
from pandas import DataFrame
from pandas_ta import Imports
from pandas_ta.overlap import ma
from pandas_ta.statistics import stdev
from pandas_ta.utils import get_offset, non_zero_range, tal_ma, verify_series

# 定義函式,計算布林帶指標
def bbands(close, length=None, std=None, ddof=0, mamode=None, talib=None, offset=None, **kwargs):
    """Indicator: Bollinger Bands (BBANDS)"""
    # 驗證引數
    length = int(length) if length and length > 0 else 5
    std = float(std) if std and std > 0 else 2.0
    mamode = mamode if isinstance(mamode, str) else "sma"
    ddof = int(ddof) if ddof >= 0 and ddof < length else 1
    close = verify_series(close, length)
    offset = get_offset(offset)
    mode_tal = bool(talib) if isinstance(talib, bool) else True

    if close is None: return

    # 計算結果
    if Imports["talib"] and mode_tal:
        from talib import BBANDS
        upper, mid, lower = BBANDS(close, length, std, std, tal_ma(mamode))
    else:
        standard_deviation = stdev(close=close, length=length, ddof=ddof)
        deviations = std * standard_deviation
        # deviations = std * standard_deviation.loc[standard_deviation.first_valid_index():,]

        mid = ma(mamode, close, length=length, **kwargs)
        lower = mid - deviations
        upper = mid + deviations

    ulr = non_zero_range(upper, lower)
    bandwidth = 100 * ulr / mid
    percent = non_zero_range(close, lower) / ulr

    # 偏移
    if offset != 0:
        lower = lower.shift(offset)
        mid = mid.shift(offset)
        upper = upper.shift(offset)
        bandwidth = bandwidth.shift(offset)
        percent = bandwidth.shift(offset)

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

    # 命名和分類
    lower.name = f"BBL_{length}_{std}"
    mid.name = f"BBM_{length}_{std}"
    upper.name = f"BBU_{length}_{std}"
    bandwidth.name = f"BBB_{length}_{std}"
    percent.name = f"BBP_{length}_{std}"
    upper.category = lower.category = "volatility"
    mid.category = bandwidth.category = upper.category

    # 準備返回的 DataFrame
    data = {
        lower.name: lower, mid.name: mid, upper.name: upper,
        bandwidth.name: bandwidth, percent.name: percent
    }
    bbandsdf = DataFrame(data)
    bbandsdf.name = f"BBANDS_{length}_{std}"
    bbandsdf.category = mid.category

    return bbandsdf

# 設定函式文件字串
bbands.__doc__ = \
"""
Bollinger Bands (BBANDS)

John Bollinger 的一種流行的波動率指標。

Sources:
    https://www.tradingview.com/wiki/Bollinger_Bands_(BB)

Calculation:
    計算方法:
        預設引數:
            length=5, std=2, mamode="sma", ddof=0
        EMA = 指數移動平均
        SMA = 簡單移動平均
        STDEV = 標準差
        計算標準差 stdev = STDEV(close, length, ddof)
        如果使用 EMA:
            計算 MID = EMA(close, length)
        否則:
            計算 MID = SMA(close, length)

        LOWER = MID - std * stdev
        UPPER = MID + std * stdev

        BANDWIDTH = 100 * (UPPER - LOWER) / MID
        PERCENT = (close - LOWER) / (UPPER - LOWER)

Args:
    close (pd.Series): 'close' 的序列
    length (int): 短週期。預設值:5
    std (int): 長週期。預設值:2
    ddof (int): 使用的自由度。預設值:0
    mamode (str): 參見 ```help(ta.ma)```py。預設值:'sma'
    talib (bool): 如果安裝了 TA Lib 並且 talib 為 True,則返回 TA Lib 版本。預設值:True
    offset (int): 結果偏移了多少週期。預設值:0

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

Returns:
    pd.DataFrame: lower、mid、upper、bandwidth 和 percent 列。
"""

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

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

# 定義函式:唐奇安通道(Donchian Channels,DC)
def donchian(high, low, lower_length=None, upper_length=None, offset=None, **kwargs):
    """Indicator: Donchian Channels (DC)"""
    # 驗證引數
    # 如果 lower_length 存在且大於 0,則將其轉換為整數,否則設為預設值 20
    lower_length = int(lower_length) if lower_length and lower_length > 0 else 20
    # 如果 upper_length 存在且大於 0,則將其轉換為整數,否則設為預設值 20
    upper_length = int(upper_length) if upper_length and upper_length > 0 else 20
    # 如果 kwargs 中存在 "lower_min_periods",則將其轉換為整數,否則設為 lower_length 的值
    lower_min_periods = int(kwargs["lower_min_periods"]) if "lower_min_periods" in kwargs and kwargs["lower_min_periods"] is not None else lower_length
    # 如果 kwargs 中存在 "upper_min_periods",則將其轉換為整數,否則設為 upper_length 的值
    upper_min_periods = int(kwargs["upper_min_periods"]) if "upper_min_periods" in kwargs and kwargs["upper_min_periods"] is not None else upper_length
    # 計算有效期最大值
    _length = max(lower_length, lower_min_periods, upper_length, upper_min_periods)
    # 驗證 high 和 low Series
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    # 獲取偏移量
    offset = get_offset(offset)

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

    # 計算結果
    # 計算下界,使用滾動視窗計算最小值
    lower = low.rolling(lower_length, min_periods=lower_min_periods).min()
    # 計算上界,使用滾動視窗計算最大值
    upper = high.rolling(upper_length, min_periods=upper_min_periods).max()
    # 計算中位數
    mid = 0.5 * (lower + upper)

    # 填充缺失值
    if "fillna" in kwargs:
        lower.fillna(kwargs["fillna"], inplace=True)
        mid.fillna(kwargs["fillna"], inplace=True)
        upper.fillna(kwargs["fillna"], inplace=True)
    if "fill_method" in kwargs:
        lower.fillna(method=kwargs["fill_method"], inplace=True)
        mid.fillna(method=kwargs["fill_method"], inplace=True)
        upper.fillna(method=kwargs["fill_method"], inplace=True)

    # 偏移結果
    if offset != 0:
        lower = lower.shift(offset)
        mid = mid.shift(offset)
        upper = upper.shift(offset)

    # 設定名稱和類別
    lower.name = f"DCL_{lower_length}_{upper_length}"
    mid.name = f"DCM_{lower_length}_{upper_length}"
    upper.name = f"DCU_{lower_length}_{upper_length}"
    mid.category = upper.category = lower.category = "volatility"

    # 準備返回的 DataFrame
    data = {lower.name: lower, mid.name: mid, upper.name: upper}
    dcdf = DataFrame(data)
    dcdf.name = f"DC_{lower_length}_{upper_length}"
    dcdf.category = mid.category

    return dcdf

# 將函式的文件字串設定為說明唐奇安通道的計算方法和引數含義
donchian.__doc__ = \
"""Donchian Channels (DC)

Donchian Channels are used to measure volatility, similar to
Bollinger Bands and Keltner Channels.

Sources:
    https://www.tradingview.com/wiki/Donchian_Channels_(DC)

Calculation:
    Default Inputs:
        lower_length=upper_length=20
    LOWER = low.rolling(lower_length).min()
    UPPER = high.rolling(upper_length).max()
    MID = 0.5 * (LOWER + UPPER)

Args:
    high (pd.Series): Series of 'high's
    low (pd.Series): Series of 'low's
    lower_length (int): The short period. Default: 20
    upper_length (int): The short period. Default: 20
    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

# 填充方法(可選引數):填充方法的型別
# 返回一個 pandas DataFrame 物件,包含 lower、mid 和 upper 列。

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

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

# 定義 Holt-Winter Channel 指標函式,接受 close 資料和一系列引數
def hwc(close, na=None, nb=None, nc=None, nd=None, scalar=None, channel_eval=None, offset=None, **kwargs):
    """Indicator: Holt-Winter Channel"""
    # 驗證引數
   na = float(na) if na and na > 0 else 0.2
    nb = float(nb) if nb and nb > 0 else 0.1
    nc = float(nc) if nc and nc > 0 else 0.1
    nd = float(nd) if nd and nd > 0 else 0.1
    scalar = float(scalar) if scalar and scalar > 0 else 1
    channel_eval = bool(channel_eval) if channel_eval and channel_eval else False
    close = verify_series(close)
    offset = get_offset(offset)

    # 計算結果
    last_a = last_v = last_var = 0
    last_f = last_price = last_result = close[0]
    lower, result, upper = [], [], []
    chan_pct_width, chan_width = [], []

    m = close.size
    # 遍歷 close 資料
    for i in range(m):
        # 計算 F、V、A
        F = (1.0 - na) * (last_f + last_v + 0.5 * last_a) + na * close[i]
        V = (1.0 - nb) * (last_v + last_a) + nb * (F - last_f)
        A = (1.0 - nc) * last_a + nc * (V - last_v)
        result.append((F + V + 0.5 * A))

        # 計算方差和標準差
        var = (1.0 - nd) * last_var + nd * (last_price - last_result) * (last_price - last_result)
        stddev = npSqrt(last_var)
        upper.append(result[i] + scalar * stddev)
        lower.append(result[i] - scalar * stddev)

        if channel_eval:
            # 計算通道寬度和價格位置
            chan_width.append(upper[i] - lower[i])
            chan_pct_width.append((close[i] - lower[i]) / (upper[i] - lower[i]))

        # 更新數值
        last_price = close[i]
        last_a = A
        last_f = F
        last_v = V
        last_var = var
        last_result = result[i]

    # 聚合結果
    hwc = Series(result, index=close.index)
    hwc_upper = Series(upper, index=close.index)
    hwc_lower = Series(lower, index=close.index)
    if channel_eval:
        hwc_width = Series(chan_width, index=close.index)
        hwc_pctwidth = Series(chan_pct_width, index=close.index)

    # 偏移資料
    if offset != 0:
        hwc = hwc.shift(offset)
        hwc_upper = hwc_upper.shift(offset)
        hwc_lower = hwc_lower.shift(offset)
        if channel_eval:
            hwc_width = hwc_width.shift(offset)
            hwc_pctwidth = hwc_pctwidth.shift(offset)

    # 處理��充值
    if "fillna" in kwargs:
        hwc.fillna(kwargs["fillna"], inplace=True)
        hwc_upper.fillna(kwargs["fillna"], inplace=True)
        hwc_lower.fillna(kwargs["fillna"], inplace=True)
        if channel_eval:
            hwc_width.fillna(kwargs["fillna"], inplace=True)
            hwc_pctwidth.fillna(kwargs["fillna"], inplace=True)
    # 檢查是否在引數 kwargs 中包含了 "fill_method" 鍵
    if "fill_method" in kwargs:
        # 對 hwc DataFrame 中的缺失值進行填充,使用 kwargs 中指定的填充方法,inplace 引數設為 True 表示就地修改
        hwc.fillna(method=kwargs["fill_method"], inplace=True)
        # 對 hwc_upper DataFrame 中的缺失值進行填充,使用 kwargs 中指定的填充方法,inplace 引數設為 True 表示就地修改
        hwc_upper.fillna(method=kwargs["fill_method"], inplace=True)
        # 對 hwc_lower DataFrame 中的缺失值進行填充,使用 kwargs 中指定的填充方法,inplace 引數設為 True 表示就地修改
        hwc_lower.fillna(method=kwargs["fill_method"], inplace=True)
        # 如果需要進行通道評估
        if channel_eval:
            # 對 hwc_width DataFrame 中的缺失值進行填充,使用 kwargs 中指定的填充方法,inplace 引數設為 True 表示就地修改
            hwc_width.fillna(method=kwargs["fill_method"], inplace=True)
            # 對 hwc_pctwidth DataFrame 中的缺失值進行填充,使用 kwargs 中指定的填充方法,inplace 引數設為 True 表示就地修改
            hwc_pctwidth.fillna(method=kwargs["fill_method"], inplace=True)

    # 給 DataFrame 新增名稱和分類
    hwc.name = "HWM"
    hwc_upper.name = "HWU"
    hwc_lower.name = "HWL"
    hwc.category = hwc_upper.category = hwc_lower.category = "volatility"
    # 如果需要進行通道評估
    if channel_eval:
        hwc_width.name = "HWW"
        hwc_pctwidth.name = "HWPCT"

    # 準備要返回的 DataFrame
    if channel_eval:
        # 構建一個包含各列的字典
        data = {hwc.name: hwc, hwc_upper.name: hwc_upper, hwc_lower.name: hwc_lower,
                hwc_width.name: hwc_width, hwc_pctwidth.name: hwc_pctwidth}
        # 使用字典構建 DataFrame
        df = DataFrame(data)
        # 設定 DataFrame 的名稱
        df.name = "HWC"
        # 設定 DataFrame 的分類
        df.category = hwc.category
    else:
        # 構建一個包含各列的字典
        data = {hwc.name: hwc, hwc_upper.name: hwc_upper, hwc_lower.name: hwc_lower}
        # 使用字典構建 DataFrame
        df = DataFrame(data)
        # 設定 DataFrame 的名稱
        df.name = "HWC"
        # 設定 DataFrame 的分類
        df.category = hwc.category

    # 返回構建好的 DataFrame
    return df
# 將 hwc 物件的文件字串設定為指定的內容,用於描述 Holt-Winter 通道指標 HWC(Holt-Winters Channel)
hwc.__doc__ = \
"""HWC (Holt-Winter Channel)

Channel indicator HWC (Holt-Winters Channel) based on HWMA - a three-parameter
moving average calculated by the method of Holt-Winters.

This version has been implemented for Pandas TA by rengel8 based on a
publication for MetaTrader 5 extended by width and percentage price position
against width of channel.

Sources:
    https://www.mql5.com/en/code/20857

Calculation:
    HWMA[i] = F[i] + V[i] + 0.5 * A[i]
    where..
    F[i] = (1-na) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + na * Price[i]
    V[i] = (1-nb) * (V[i-1] + A[i-1]) + nb * (F[i] - F[i-1])
    A[i] = (1-nc) * A[i-1] + nc * (V[i] - V[i-1])

    Top = HWMA + Multiplier * StDt
    Bottom = HWMA - Multiplier * StDt
    where..
    StDt[i] = Sqrt(Var[i-1])
    Var[i] = (1-d) * Var[i-1] + nD * (Price[i-1] - HWMA[i-1]) * (Price[i-1] - HWMA[i-1])

Args:
    na - parameter of the equation that describes a smoothed series (from 0 to 1)
    nb - parameter of the equation to assess the trend (from 0 to 1)
    nc - parameter of the equation to assess seasonality (from 0 to 1)
    nd - parameter of the channel equation (from 0 to 1)
    scaler - multiplier for the width of the channel calculated
    channel_eval - boolean to return width and percentage price position against price
    close (pd.Series): Series of 'close's

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method
Returns:
    pd.DataFrame: HWM (Mid), HWU (Upper), HWL (Lower) columns.
"""

相關文章