# -*- coding: utf-8 -*-
from numpy import sqrt as npSqrt
from pandas import DataFrame, Series
from pandas_ta.utils import get_offset, verify_series
[docs]def hwc(close, na=None, nb=None, nc=None, nd=None, scalar=None, channel_eval=None, offset=None, **kwargs):
"""Indicator: Holt-Winter Channel"""
# Validate Arguments
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)
# Calculate Result
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
for i in range(m):
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:
# channel width
chan_width.append(upper[i] - lower[i])
# channel percentage price position
chan_pct_width.append((close[i] - lower[i]) / (upper[i] - lower[i]))
# print('channel_eval (width|percentageWidth):', chan_width[i], chan_pct_width[i])
# update values
last_price = close[i]
last_a = A
last_f = F
last_v = V
last_var = var
last_result = result[i]
# Aggregate
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)
# Offset
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)
# Handle fills
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)
if "fill_method" in kwargs:
hwc.fillna(method=kwargs["fill_method"], inplace=True)
hwc_upper.fillna(method=kwargs["fill_method"], inplace=True)
hwc_lower.fillna(method=kwargs["fill_method"], inplace=True)
if channel_eval:
hwc_width.fillna(method=kwargs["fill_method"], inplace=True)
hwc_pctwidth.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
# suffix = f'{str(na).replace(".", "")}-{str(nb).replace(".", "")}-{str(nc).replace(".", "")}'
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"
# Prepare DataFrame to return
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}
df = DataFrame(data)
df.name = "HWC"
df.category = hwc.category
else:
data = {hwc.name: hwc, hwc_upper.name: hwc_upper, hwc_lower.name: hwc_lower}
df = DataFrame(data)
df.name = "HWC"
df.category = hwc.category
return df
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.
"""