""" 布林带因子:价格在布林带中的位置。 """ import pandas as pd from factors.base import BaseFactor class BollingerFactor(BaseFactor): """ 布林带位置 = (close - middle) / (upper - lower) 值在 0~1 之间:接近 0 表示在下轨,接近 1 表示在上轨。 """ category = "technical" def __init__(self, period: int = 20, std: float = 2.0): self.period = period self.std = std self.name = f"boll_{period}" def calculate(self, df: pd.DataFrame) -> pd.Series: middle = df["close"].rolling(window=self.period, min_periods=self.period).mean() std = df["close"].rolling(window=self.period, min_periods=self.period).std() upper = middle + self.std * std lower = middle - self.std * std band_width = upper - lower return ((df["close"] - lower) / band_width.replace(0, float("nan"))).clip(0, 1) def get_required_columns(self) -> list[str]: return ["close"] class BollingerWidthFactor(BaseFactor): """布林带宽度 = (upper - lower) / middle * 100""" category = "technical" def __init__(self, period: int = 20, std: float = 2.0): self.period = period self.std = std self.name = f"boll_width_{period}" def calculate(self, df: pd.DataFrame) -> pd.Series: middle = df["close"].rolling(window=self.period, min_periods=self.period).mean() std = df["close"].rolling(window=self.period, min_periods=self.period).std() band_width = 2 * self.std * std return band_width / middle.replace(0, float("nan")) * 100 def get_required_columns(self) -> list[str]: return ["close"]