PandasTA 原始碼解析(九)

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

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

# -*- coding: utf-8 -*-
# 從 numpy 庫中匯入 average 函式並重新命名為 npAverage
# 從 numpy 庫中匯入 nan 函式並重新命名為 npNaN
# 從 numpy 庫中匯入 log 函式並重新命名為 npLog
# 從 numpy 庫中匯入 power 函式並重新命名為 npPower
# 從 numpy 庫中匯入 sqrt 函式並重新命名為 npSqrt
# 從 numpy 庫中匯入 zeros_like 函式並重新命名為 npZeroslike
# 從 pandas 庫中匯入 Series 類
# 從 pandas_ta.utils 模組中匯入 get_offset 和 verify_series 函式

def jma(close, length=None, phase=None, offset=None, **kwargs):
    """Indicator: Jurik Moving Average (JMA)"""
    # 驗證引數
    _length = int(length) if length and length > 0 else 7
    phase = float(phase) if phase and phase != 0 else 0
    close = verify_series(close, _length)
    offset = get_offset(offset)
    if close is None: return

    # 定義基本變數
    jma = npZeroslike(close)
    volty = npZeroslike(close)
    v_sum = npZeroslike(close)

    kv = det0 = det1 = ma2 = 0.0
    jma[0] = ma1 = uBand = lBand = close[0]

    # 靜態變數
    sum_length = 10
    length = 0.5 * (_length - 1)
    pr = 0.5 if phase < -100 else 2.5 if phase > 100 else 1.5 + phase * 0.01
    length1 = max((npLog(npSqrt(length)) / npLog(2.0)) + 2.0, 0)
    pow1 = max(length1 - 2.0, 0.5)
    length2 = length1 * npSqrt(length)
    bet = length2 / (length2 + 1)
    beta = 0.45 * (_length - 1) / (0.45 * (_length - 1) + 2.0)

    m = close.shape[0]
    for i in range(1, m):
        price = close[i]

        # 價格波動性
        del1 = price - uBand
        del2 = price - lBand
        volty[i] = max(abs(del1),abs(del2)) if abs(del1)!=abs(del2) else 0

        # 相對價格波動性因子
        v_sum[i] = v_sum[i - 1] + (volty[i] - volty[max(i - sum_length, 0)]) / sum_length
        avg_volty = npAverage(v_sum[max(i - 65, 0):i + 1])
        d_volty = 0 if avg_volty ==0 else volty[i] / avg_volty
        r_volty = max(1.0, min(npPower(length1, 1 / pow1), d_volty))

        # Jurik 波動性帶
        pow2 = npPower(r_volty, pow1)
        kv = npPower(bet, npSqrt(pow2))
        uBand = price if (del1 > 0) else price - (kv * del1)
        lBand = price if (del2 < 0) else price - (kv * del2)

        # Jurik 動態因子
        power = npPower(r_volty, pow1)
        alpha = npPower(beta, power)

        # 第一階段 - 透過自適應 EMA 進行初步平滑
        ma1 = ((1 - alpha) * price) + (alpha * ma1)

        # 第二階段 - 透過 Kalman 濾波器進行一次額外的初步平滑
        det0 = ((price - ma1) * (1 - beta)) + (beta * det0)
        ma2 = ma1 + pr * det0

        # 第三階段 - 透過獨特的 Jurik 自適應濾波器進行最終平滑
        det1 = ((ma2 - jma[i - 1]) * (1 - alpha) * (1 - alpha)) + (alpha * alpha * det1)
        jma[i] = jma[i-1] + det1

    # 移除初始回看資料並轉換為 pandas Series
    jma[0:_length - 1] = npNaN
    jma = Series(jma, index=close.index)

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

    # 處理填充
    if "fillna" in kwargs:
        jma.fillna(kwargs["fillna"], inplace=True)
    # 檢查是否存在 "fill_method" 引數在傳入的關鍵字引數 kwargs 中
    if "fill_method" in kwargs:
        # 如果存在,使用指定的填充方法對 DataFrame 進行填充,並在原地修改
        jma.fillna(method=kwargs["fill_method"], inplace=True)

    # 設定 JMA 物件的名稱為格式化後的字串,包含長度和相位資訊
    jma.name = f"JMA_{_length}_{phase}"
    # 設定 JMA 物件的類別為 "overlap"
    jma.category = "overlap"

    # 返回填充後的 JMA 物件
    return jma
# 將 jma 的文件字串設定為 JMA 指標的說明文件
jma.__doc__ = \
"""Jurik Moving Average Average (JMA)

Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the "true"
underlying activity. It has extremely low lag, is very smooth and is responsive
to market gaps.

Sources:
    https://c.mql5.com/forextsd/forum/164/jurik_1.pdf
    https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/

Calculation:
    Default Inputs:
        length=7, phase=0

Args:
    close (pd.Series): Series of 'close's
    length (int): Period of calculation. Default: 7
    phase (float): How heavy/light the average is [-100, 100]. Default: 0
    offset (int): How many lengths to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

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

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


# 定義 Kaufman's Adaptive Moving Average (KAMA) 函式
def kama(close, length=None, fast=None, slow=None, drift=None, offset=None, **kwargs):
    """Indicator: Kaufman's Adaptive Moving Average (KAMA)"""
    # 驗證引數
    # 如果 length 不為空且大於 0,則將其轉換為整數,否則設為預設值 10
    length = int(length) if length and length > 0 else 10
    # 如果 fast 不為空且大於 0,則將其轉換為整數,否則設為預設值 2
    fast = int(fast) if fast and fast > 0 else 2
    # 如果 slow 不為空且大於 0,則將其轉換為整數,否則設為預設值 30
    slow = int(slow) if slow and slow > 0 else 30
    # 驗證 close 序列,長度為 fast、slow、length 中的最大值
    close = verify_series(close, max(fast, slow, length))
    # 獲取 drift 引數
    drift = get_drift(drift)
    # 獲取 offset 引數
    offset = get_offset(offset)

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

    # 計算結果
    # 定義 weight 函式,用於計算權重
    def weight(length: int) -> float:
        return 2 / (length + 1)

    # 計算 fast 和 slow 的權重
    fr = weight(fast)
    sr = weight(slow)

    # 計算絕對差和同側差
    abs_diff = non_zero_range(close, close.shift(length)).abs()
    peer_diff = non_zero_range(close, close.shift(drift)).abs()
    peer_diff_sum = peer_diff.rolling(length).sum()
    er = abs_diff / peer_diff_sum
    x = er * (fr - sr) + sr
    sc = x * x

    # 獲取 close 序列的長度
    m = close.size
    # 初始化結果列表,前 length-1 個值為 npNaN,最後一個值為 0
    result = [npNaN for _ in range(0, length - 1)] + [0]
    # 遍歷計算 KAMA
    for i in range(length, m):
        result.append(sc.iloc[i] * close.iloc[i] + (1 - sc.iloc[i]) * result[i - 1])

    # 將結果轉換為 Series 型別,索引為 close 序列的索引
    kama = Series(result, index=close.index)

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

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

    # 設定指標名稱和類別
    kama.name = f"KAMA_{length}_{fast}_{slow}"
    kama.category = "overlap"

    # 返回 KAMA 序列
    return kama


# 設定 KAMA 函式的文件字串
kama.__doc__ = \
"""Kaufman's Adaptive Moving Average (KAMA)

Developed by Perry Kaufman, Kaufman's Adaptive Moving Average (KAMA) is a moving average
designed to account for market noise or volatility. KAMA will closely follow prices when
the price swings are relatively small and the noise is low. KAMA will adjust when the
price swings widen and follow prices from a greater distance. This trend-following indicator
can be used to identify the overall trend, time turning points and filter price movements.

Sources:
    https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:kaufman_s_adaptive_moving_average
    https://www.tradingview.com/script/wZGOIz9r-REPOST-Indicators-3-Different-Adaptive-Moving-Averages/

Calculation:
    Default Inputs:
        length=10

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    fast (int): Fast MA period. Default: 2
    slow (int): Slow MA period. Default: 30
    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\overlap\linreg.py

# -*- coding: utf-8 -*-
# 從 numpy 庫匯入 array 並重新命名為 npArray,匯入 arctan 並重新命名為 npAtan,匯入 nan 並重新命名為 npNaN,匯入 pi 並重新命名為 npPi,從 numpy 版本匯入 version 並重新命名為 npVersion
from numpy import array as npArray
from numpy import arctan as npAtan
from numpy import nan as npNaN
from numpy import pi as npPi
from numpy.version import version as npVersion
# 從 pandas 庫匯入 Series,從 pandas_ta.utils 匯入 get_offset 和 verify_series 函式
from pandas import Series
from pandas_ta.utils import get_offset, verify_series


def linreg(close, length=None, offset=None, **kwargs):
    """Indicator: Linear Regression"""
    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則設為預設值 14
    length = int(length) if length and length > 0 else 14
    # 驗證 close 是否為有效的 Series,並設定長度
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 獲取其他引數:angle、intercept、degrees、r、slope、tsf
    angle = kwargs.pop("angle", False)
    intercept = kwargs.pop("intercept", False)
    degrees = kwargs.pop("degrees", False)
    r = kwargs.pop("r", False)
    slope = kwargs.pop("slope", False)
    tsf = kwargs.pop("tsf", False)

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

    # 計算結果
    # 生成 x 軸的值,範圍從 1 到 length
    x = range(1, length + 1)  # [1, 2, ..., n] from 1 to n keeps Sum(xy) low
    # 計算 x 的和
    x_sum = 0.5 * length * (length + 1)
    # 計算 x 的平方和
    x2_sum = x_sum * (2 * length + 1) / 3
    # 計算除數
    divisor = length * x2_sum - x_sum * x_sum

    # 定義線性迴歸函式
    def linear_regression(series):
        # 計算 y 的和
        y_sum = series.sum()
        # 計算 x*y 的和
        xy_sum = (x * series).sum()

        # 計算斜率
        m = (length * xy_sum - x_sum * y_sum) / divisor
        # 如果 slope 為 True,則返回斜率
        if slope:
            return m
        # 計算截距
        b = (y_sum * x2_sum - x_sum * xy_sum) / divisor
        # 如果 intercept 為 True,則返回截距
        if intercept:
            return b

        # 如果 angle 為 True,則計算角度
        if angle:
            theta = npAtan(m)
            # 如果 degrees 為 True,則將角度轉換為度
            if degrees:
                theta *= 180 / npPi
            return theta

        # 如果 r 為 True,則計算相關係數
        if r:
            # 計算 y^2 的和
            y2_sum = (series * series).sum()
            # 計算相關係數的分子
            rn = length * xy_sum - x_sum * y_sum
            # 計算相關係數的分母
            rd = (divisor * (length * y2_sum - y_sum * y_sum)) ** 0.5
            return rn / rd

        # 如果 tsf 為 True,則進行時間序列調整
        return m * length + b if tsf else m * (length - 1) + b

    # 定義滾動視窗函式
    def rolling_window(array, length):
        """https://github.com/twopirllc/pandas-ta/issues/285"""
        strides = array.strides + (array.strides[-1],)
        shape = array.shape[:-1] + (array.shape[-1] - length + 1, length)
        return as_strided(array, shape=shape, strides=strides)

    # 如果 numpy 版本大於等於 1.20.0,則使用 sliding_window_view 函式
    if npVersion >= "1.20.0":
        from numpy.lib.stride_tricks import sliding_window_view
        # 對於滑動視窗內的每個視窗,應用線性迴歸函式
        linreg_ = [linear_regression(_) for _ in sliding_window_view(npArray(close), length)]
    else:
        # 否則,使用 rolling_window 函式
        from numpy.lib.stride_tricks import as_strided
        # 對於滾動視窗內的每個視窗,應用線性迴歸函式
        linreg_ = [linear_regression(_) for _ in rolling_window(npArray(close), length)]

    # 建立 Series 物件,索引為 close 的索引,值為線性迴歸的結果
    linreg = Series([npNaN] * (length - 1) + linreg_, index=close.index)

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

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

    # 設定名稱和分類
    linreg.name = f"LR"
    if slope: linreg.name += "m"
    if intercept: linreg.name += "b"
    if angle: linreg.name += "a"
    if r: linreg.name += "r"

    linreg.name += f"_{length}"
    linreg.category = "overlap"

    return linreg
# 將文件字串賦值給線性迴歸移動平均函式的__doc__屬性,用於函式說明和文件生成
linreg.__doc__ = \
"""Linear Regression Moving Average (linreg)

Linear Regression Moving Average (LINREG). This is a simplified version of a
Standard Linear Regression. LINREG is a rolling regression of one variable. A
Standard Linear Regression is between two or more variables.

Source: TA Lib

Calculation:
    Default Inputs:
        length=14
    x = [1, 2, ..., n]
    x_sum = 0.5 * length * (length + 1)
    x2_sum = length * (length + 1) * (2 * length + 1) / 6
    divisor = length * x2_sum - x_sum * x_sum

    # 定義線性迴歸函式lr(series),用於計算移動平均
    lr(series):
        # 計算系列的總和、平方和以及x和y的乘積和
        y_sum = series.sum()
        y2_sum = (series* series).sum()
        xy_sum = (x * series).sum()

        # 計算迴歸線的斜率m和截距b
        m = (length * xy_sum - x_sum * y_sum) / divisor
        b = (y_sum * x2_sum - x_sum * xy_sum) / divisor
        return m * (length - 1) + b

    # 使用rolling函式對close進行移動視窗處理,並應用lr函式計算移動平均
    linreg = close.rolling(length).apply(lr)

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

Kwargs:
    angle (bool, optional): If True, returns the angle of the slope in radians.
        Default: False.
    degrees (bool, optional): If True, returns the angle of the slope in
        degrees. Default: False.
    intercept (bool, optional): If True, returns the angle of the slope in
        radians. Default: False.
    r (bool, optional): If True, returns it's correlation 'r'. Default: False.
    slope (bool, optional): If True, returns the slope. Default: False.
    tsf (bool, optional): If True, returns the Time Series Forecast value.
        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\overlap\ma.py

# 設定檔案編碼為 UTF-8
# 匯入 Series 類
from pandas import Series

# 匯入不同的移動平均方法
# 從模組中匯入指定的函式
from .dema import dema
from .ema import ema
from .fwma import fwma
from .hma import hma
from .linreg import linreg
from .midpoint import midpoint
from .pwma import pwma
from .rma import rma
from .sinwma import sinwma
from .sma import sma
from .swma import swma
from .t3 import t3
from .tema import tema
from .trima import trima
from .vidya import vidya
from .wma import wma
from .zlma import zlma

# 定義移動平均函式,用於簡化移動平均的選擇
def ma(name:str = None, source:Series = None, **kwargs) -> Series:
    """Simple MA Utility for easier MA selection

    Available MAs:
        dema, ema, fwma, hma, linreg, midpoint, pwma, rma,
        sinwma, sma, swma, t3, tema, trima, vidya, wma, zlma

    Examples:
        ema8 = ta.ma("ema", df.close, length=8)
        sma50 = ta.ma("sma", df.close, length=50)
        pwma10 = ta.ma("pwma", df.close, length=10, asc=False)

    Args:
        name (str): One of the Available MAs. Default: "ema"
        source (pd.Series): The 'source' Series.

    Kwargs:
        Any additional kwargs the MA may require.

    Returns:
        pd.Series: New feature generated.
    """

    # 支援的移動平均方法列表
    _mas = [
        "dema", "ema", "fwma", "hma", "linreg", "midpoint", "pwma", "rma",
        "sinwma", "sma", "swma", "t3", "tema", "trima", "vidya", "wma", "zlma"
    ]
    # 如果沒有指定移動平均方法和資料來源,則返回支援的移動平均方法列表
    if name is None and source is None:
        return _mas
    # 如果指定的移動平均方法是字串,並且在支援的方法列表中,則轉換為小寫
    elif isinstance(name, str) and name.lower() in _mas:
        name = name.lower()
    else: # "ema"
        # 如果未指定移動平均方法,則預設選擇 EMA
        name = _mas[1]

    # 根據選擇的移動平均方法呼叫相應的函式
    if   name == "dema": return dema(source, **kwargs)
    elif name == "fwma": return fwma(source, **kwargs)
    elif name == "hma": return hma(source, **kwargs)
    elif name == "linreg": return linreg(source, **kwargs)
    elif name == "midpoint": return midpoint(source, **kwargs)
    elif name == "pwma": return pwma(source, **kwargs)
    elif name == "rma": return rma(source, **kwargs)
    elif name == "sinwma": return sinwma(source, **kwargs)
    elif name == "sma": return sma(source, **kwargs)
    elif name == "swma": return swma(source, **kwargs)
    elif name == "t3": return t3(source, **kwargs)
    elif name == "tema": return tema(source, **kwargs)
    elif name == "trima": return trima(source, **kwargs)
    elif name == "vidya": return vidya(source, **kwargs)
    elif name == "wma": return wma(source, **kwargs)
    elif name == "zlma": return zlma(source, **kwargs)
    else: return ema(source, **kwargs)

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

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


def mcgd(close, length=None, offset=None, c=None, **kwargs):
    """Indicator: McGinley Dynamic Indicator"""
    # 驗證引數有效性
    # 如果 length 存在且大於 0,則將其轉換為整數;否則,預設為 10
    length = int(length) if length and length > 0 else 10
    # 如果 c 存在且在 0 到 1 之間,則將其轉換為浮點數;否則,預設為 1
    c = float(c) if c and 0 < c <= 1 else 1
    # 驗證 close 是否為有效的 Series,並將其長度限制為 length
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)

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

    # 計算結果
    # 複製 close Series,避免直接修改原始資料
    close = close.copy()

    # 定義 McGinley Dynamic Indicator 計算函式
    def mcg_(series):
        # 計算分母
        denom = (c * length * (series.iloc[1] / series.iloc[0]) ** 4)
        # 計算 McGinley Dynamic Indicator
        series.iloc[1] = (series.iloc[0] + ((series.iloc[1] - series.iloc[0]) / denom))
        return series.iloc[1]

    # 應用 mcg_ 函式到 rolling window 上,計算 McGinley Dynamic Indicator
    mcg_cell = close[0:].rolling(2, min_periods=2).apply(mcg_, raw=False)
    # 將第一個值新增回結果 Series 中
    mcg_ds = close[:1].append(mcg_cell[1:])

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

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

    # 設定結果 Series 的名稱和類別
    mcg_ds.name = f"MCGD_{length}"
    mcg_ds.category = "overlap"

    return mcg_ds


# 設定 McGinley Dynamic Indicator 的文件字串
mcgd.__doc__ = \
"""McGinley Dynamic Indicator

The McGinley Dynamic looks like a moving average line, yet it is actually a
smoothing mechanism for prices that minimizes price separation, price whipsaws,
and hugs prices much more closely. Because of the calculation, the Dynamic Line
speeds up in down markets as it follows prices yet moves more slowly in up
markets. The indicator was designed by John R. McGinley, a Certified Market
Technician and former editor of the Market Technicians Association's Journal
of Technical Analysis.

Sources:
    https://www.investopedia.com/articles/forex/09/mcginley-dynamic-indicator.asp

Calculation:
    Default Inputs:
        length=10
        offset=0
        c=1

    def mcg_(series):
        denom = (constant * length * (series.iloc[1] / series.iloc[0]) ** 4)
        series.iloc[1] = (series.iloc[0] + ((series.iloc[1] - series.iloc[0]) / denom))
        return series.iloc[1]
    mcg_cell = close[0:].rolling(2, min_periods=2).apply(mcg_, raw=False)
    mcg_ds = close[:1].append(mcg_cell[1:])

Args:
    close (pd.Series): Series of 'close's
    length (int): Indicator's period. Default: 10
    offset (int): Number of periods to offset the result. Default: 0
    c (float): Multiplier for the denominator, sometimes set to 0.6. Default: 1

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

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

# 設定檔案編碼為 UTF-8
# -*- 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

# 定義函式 midpoint,用於計算 Midpoint 指標
def midpoint(close, length=None, talib=None, offset=None, **kwargs):
    """Indicator: Midpoint"""
    # 驗證引數
    # 如果 length 不為空且大於 0,則轉換為整數,否則設為預設值 2
    length = int(length) if length and length > 0 else 2
    # 如果 kwargs 中包含 "min_periods" 鍵並且值不為空,則轉換為整數,否則設為與 length 相同的值
    min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
    # 驗證 close 是否為有效序列,長度至少為 length 或 min_periods
    close = verify_series(close, max(length, min_periods))
    # 獲取偏移量
    offset = get_offset(offset)
    # 判斷是否使用 talib 庫,預設為 True
    mode_tal = bool(talib) if isinstance(talib, bool) else True

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

    # 計算結果
    # 如果匯入了 talib 並且 mode_tal 為 True
    if Imports["talib"] and mode_tal:
        # 從 talib 庫中匯入 MIDPOINT 函式,計算 Midpoint 指標
        from talib import MIDPOINT
        midpoint = MIDPOINT(close, length)
    else:
        # 使用 rolling 函式計算最低價和最高價的移動視窗,然後計算 Midpoint
        lowest = close.rolling(length, min_periods=min_periods).min()
        highest = close.rolling(length, min_periods=min_periods).max()
        midpoint = 0.5 * (lowest + highest)

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

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

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

    # 返回 Midpoint 指標
    return midpoint

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

# 設定檔案編碼為 UTF-8
# -*- 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 midprice(high, low, length=None, talib=None, offset=None, **kwargs):
    """Indicator: Midprice"""
    # 驗證引數
    # 如果 length 存在且大於 0,則轉換為整數;否則,預設為 2
    length = int(length) if length and length > 0 else 2
    # 如果 kwargs 中存在 "min_periods",則將其轉換為整數;否則,預設為 length
    min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
    # 計算有效期長度
    _length = max(length, min_periods)
    # 驗證 high 和 low 系列
    high = verify_series(high, _length)
    low = verify_series(low, _length)
    # 獲取偏移量
    offset = get_offset(offset)
    # 判斷是否啟用 talib 模式
    mode_tal = bool(talib) if isinstance(talib, bool) else True

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

    # 計算結果
    if Imports["talib"] and mode_tal:
        # 使用 talib 計算 MIDPRICE 指標
        from talib import MIDPRICE
        midprice = MIDPRICE(high, low, length)
    else:
        # 計算最低低點和最高高點的滾動視窗
        lowest_low = low.rolling(length, min_periods=min_periods).min()
        highest_high = high.rolling(length, min_periods=min_periods).max()
        # 計算中間價
        midprice = 0.5 * (lowest_low + highest_high)

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

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

    # 設定指標名稱和分類
    midprice.name = f"MIDPRICE_{length}"
    midprice.category = "overlap"

    # 返回中間價指標結果
    return midprice

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

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

# 定義一個名為 ohlc4 的函式,用於計算 OHLC4 指標
def ohlc4(open_, high, low, close, offset=None, **kwargs):
    """Indicator: OHLC4"""
    # 驗證輸入引數,確保它們都是 pandas Series 型別
    open_ = verify_series(open_)
    high = verify_series(high)
    low = verify_series(low)
    close = verify_series(close)
    # 獲取位移值
    offset = get_offset(offset)

    # 計算 OHLC4 指標的值,使用開盤價、最高價、最低價和收盤價的均值
    ohlc4 = 0.25 * (open_ + high + low + close)

    # 如果存在位移值,則將 OHLC4 指標的值向前位移相應數量的週期
    if offset != 0:
        ohlc4 = ohlc4.shift(offset)

    # 設定 OHLC4 指標的名稱和類別
    ohlc4.name = "OHLC4"
    ohlc4.category = "overlap"

    # 返回計算得到的 OHLC4 指標
    return ohlc4

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

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

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

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

    if close is None: return

    # 計算結果
    triangle = pascals_triangle(n=length - 1, weighted=True)
    pwma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True)

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

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

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

    return pwma


# 設定函式文件字串
pwma.__doc__ = \
"""Pascal's Weighted Moving Average (PWMA)

Pascal's Weighted Moving Average is similar to a symmetric triangular window
except PWMA's weights are based on Pascal's Triangle.

Source: Kevin Johnson

Calculation:
    Default Inputs:
        length=10

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

    triangle = utils.pascals_triangle(length + 1)
    PWMA = close.rolling(length)_.apply(weights(triangle), raw=True)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period.  Default: 10
    asc (bool): Recent values weigh more. Default: True
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

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

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

# 定義函式 rma,計算 wildeR 的移動平均值(RMA)
def rma(close, length=None, offset=None, **kwargs):
    """Indicator: wildeR's Moving Average (RMA)"""
    # 驗證引數
    # 如果 length 存在且大於 0,則將其轉換為整數,否則設為 10
    length = int(length) if length and length > 0 else 10
    # 計算 alpha 值,如果 length 大於 0 則為 1/length,否則為 0.5
    alpha = (1.0 / length) if length > 0 else 0.5
    # 驗證 close 引數是否為有效序列,並指定長度為 length
    close = verify_series(close, length)
    # 獲取偏移量
    offset = get_offset(offset)

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

    # 計算結果
    # 使用指數加權移動平均(Exponential Moving Average,EMA)計算 RMA
    rma = close.ewm(alpha=alpha, min_periods=length).mean()

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

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

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

    return rma


# 設定 rma 函式的文件字串
rma.__doc__ = \
"""wildeR's Moving Average (RMA)

The WildeR's Moving Average is simply an Exponential Moving Average (EMA) with
a modified alpha = 1 / length.

Sources:
    https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing
    https://www.incrediblecharts.com/indicators/wilder_moving_average.php

Calculation:
    Default Inputs:
        length=10
    EMA = Exponential Moving Average
    alpha = 1 / length
    RMA = EMA(close, alpha=alpha)

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

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

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

# -*- coding: utf-8 -*-
# 匯入必要的庫
from numpy import pi as npPi  # 匯入 numpy 庫中的 pi 並重新命名為 npPi
from numpy import sin as npSin  # 匯入 numpy 庫中的 sin 函式並重新命名為 npSin
from pandas import Series  # 匯入 pandas 庫中的 Series 類
from pandas_ta.utils import get_offset, verify_series, weights  # 從 pandas_ta.utils 模組匯入 get_offset, verify_series, weights 函式


def sinwma(close, length=None, offset=None, **kwargs):
    """Indicator: Sine Weighted Moving Average (SINWMA) by Everget of TradingView"""
    # Validate Arguments
    # 驗證引數
    length = int(length) if length and length > 0 else 14  # 將 length 轉換為整數,如果未提供或小於等於 0,則設定為預設值 14
    close = verify_series(close, length)  # 驗證 close 引數是否為 Series 型別,並確保長度為 length
    offset = get_offset(offset)  # 獲取偏移量

    if close is None: return

    # Calculate Result
    # 計算結果
    sines = Series([npSin((i + 1) * npPi / (length + 1)) for i in range(0, length)])  # 生成長度為 length 的正弦週期權重序列
    w = sines / sines.sum()  # 將權重序列標準化

    sinwma = close.rolling(length, min_periods=length).apply(weights(w), raw=True)  # 使用權重計算 SINWMA

    # Offset
    # 偏移結果
    if offset != 0:
        sinwma = sinwma.shift(offset)  # 將結果向前或向後偏移 offset 個週期

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

    # Name & Category
    # 設定指標名稱和類別
    sinwma.name = f"SINWMA_{length}"  # 設定指標名稱
    sinwma.category = "overlap"  # 設定指標類別為 overlap

    return sinwma  # 返回計算結果


sinwma.__doc__ = \
"""Sine Weighted Moving Average (SWMA)

A weighted average using sine cycles. The middle term(s) of the average have the
highest weight(s).

Source:
    https://www.tradingview.com/script/6MWFvnPO-Sine-Weighted-Moving-Average/
    Author: Everget (https://www.tradingview.com/u/everget/)

Calculation:
    Default Inputs:
        length=10

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

    sines = Series([sin((i + 1) * pi / (length + 1)) for i in range(0, length)])
    w = sines / sines.sum()
    SINWMA = close.rolling(length, min_periods=length).apply(weights(w), raw=True)

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

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""

相關文章