2025-06-07 14:01:20 +08:00
|
|
|
"""
|
|
|
|
|
Bollinger Bands indicator implementation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
|
|
|
|
from ..base import BaseIndicator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BollingerBandsIndicator(BaseIndicator):
|
|
|
|
|
"""
|
|
|
|
|
Bollinger Bands technical indicator.
|
|
|
|
|
|
|
|
|
|
Calculates a set of lines plotted two standard deviations away from a simple moving average.
|
|
|
|
|
Handles sparse data appropriately without interpolation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def calculate(self, df: pd.DataFrame, period: int = 20,
|
2025-06-09 16:28:16 +08:00
|
|
|
std_dev: float = 2.0, price_column: str = 'close') -> pd.DataFrame:
|
2025-06-07 14:01:20 +08:00
|
|
|
"""
|
|
|
|
|
Calculate Bollinger Bands.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
df: DataFrame with OHLCV data
|
|
|
|
|
period: Number of periods for moving average (default 20)
|
|
|
|
|
std_dev: Number of standard deviations (default 2.0)
|
|
|
|
|
price_column: Price column to use ('open', 'high', 'low', 'close')
|
|
|
|
|
|
|
|
|
|
Returns:
|
2025-06-09 16:28:16 +08:00
|
|
|
DataFrame with Bollinger Bands values and metadata, indexed by timestamp
|
2025-06-07 14:01:20 +08:00
|
|
|
"""
|
|
|
|
|
# Validate input data
|
|
|
|
|
if not self.validate_dataframe(df, period):
|
2025-06-09 16:28:16 +08:00
|
|
|
return pd.DataFrame()
|
2025-06-07 14:01:20 +08:00
|
|
|
try:
|
2025-06-09 16:28:16 +08:00
|
|
|
df = df.copy()
|
2025-06-07 14:01:20 +08:00
|
|
|
df['middle_band'] = df[price_column].rolling(window=period, min_periods=period).mean()
|
|
|
|
|
df['std'] = df[price_column].rolling(window=period, min_periods=period).std()
|
2025-06-09 16:28:16 +08:00
|
|
|
df['upper_band'] = df['middle_band'] + (df['std'] * std_dev)
|
|
|
|
|
df['lower_band'] = df['middle_band'] - (df['std'] * std_dev)
|
|
|
|
|
# Only keep rows with valid bands, and only 'timestamp', 'upper_band', 'middle_band', 'lower_band' columns
|
|
|
|
|
result_df = df.loc[df['middle_band'].notna() & df['upper_band'].notna() & df['lower_band'].notna(), ['timestamp', 'upper_band', 'middle_band', 'lower_band']].copy()
|
|
|
|
|
result_df.set_index('timestamp', inplace=True)
|
|
|
|
|
return result_df
|
|
|
|
|
except Exception:
|
|
|
|
|
return pd.DataFrame()
|