Download src/env/indicators.py from chen470/drl-trading-bot-dev2: direct link, hf CLI and curl.
- Browser
- Download file 5.3 kB
-
https://huggingface.co/spaces/chen470/drl-trading-bot-dev2/resolve/main/src/env/indicators.py
- Command line
-
hf download hf://spaces/chen470/drl-trading-bot-dev2/src/env/indicators.py
-
curl -L -o indicators.py https://huggingface.co/spaces/chen470/drl-trading-bot-dev2/resolve/main/src/env/indicators.py
5.3 kB
| """ | |
| Technical Indicators Module | |
| Wrapper around pandas_ta for normalized technical indicators. | |
| """ | |
| import numpy as np | |
| import pandas as pd | |
| from typing import Dict, List, Optional | |
| class TechnicalIndicators: | |
| """ | |
| Computes and normalizes technical indicators for the trading environment. | |
| All indicators are normalized to [-1, 1] or [0, 1] range for neural network input. | |
| """ | |
| def __init__(self, config: Optional[Dict] = None): | |
| """ | |
| Initialize technical indicators with configuration. | |
| Args: | |
| config: Dictionary with indicator parameters | |
| """ | |
| self.config = config or { | |
| 'rsi_period': 14, | |
| 'macd_fast': 12, | |
| 'macd_slow': 26, | |
| 'macd_signal': 9, | |
| 'bb_period': 20, | |
| 'bb_std': 2, | |
| 'atr_period': 14, | |
| 'ema_periods': [9, 21, 50], | |
| } | |
| def compute_all(self, df: pd.DataFrame) -> pd.DataFrame: | |
| """ | |
| Compute all technical indicators and add them to the dataframe. | |
| Using pure pandas for reliability without external TA libraries. | |
| """ | |
| df = df.copy() | |
| df.columns = df.columns.str.lower() | |
| close = df['close'] | |
| high = df['high'] | |
| low = df['low'] | |
| volume = df['volume'] | |
| # RSI | |
| rsi_period = self.config['rsi_period'] | |
| delta = close.diff() | |
| gain = delta.clip(lower=0) | |
| loss = -1 * delta.clip(upper=0) | |
| avg_gain = gain.ewm(com=rsi_period-1, adjust=False).mean() | |
| avg_loss = loss.ewm(com=rsi_period-1, adjust=False).mean() | |
| rs = avg_gain / avg_loss | |
| df['rsi'] = 100 - (100 / (1 + rs)) | |
| df['rsi_norm'] = df['rsi'] / 100.0 | |
| # MACD | |
| ema_fast = close.ewm(span=self.config['macd_fast'], adjust=False).mean() | |
| ema_slow = close.ewm(span=self.config['macd_slow'], adjust=False).mean() | |
| df['macd'] = ema_fast - ema_slow | |
| df['macd_signal'] = df['macd'].ewm(span=self.config['macd_signal'], adjust=False).mean() | |
| df['macd_hist'] = df['macd'] - df['macd_signal'] | |
| # Normalize MACD by price | |
| df['macd_norm'] = df['macd'] / close | |
| df['macd_signal_norm'] = df['macd_signal'] / close | |
| df['macd_hist_norm'] = df['macd_hist'] / close | |
| # Bollinger Bands | |
| bb_period = self.config['bb_period'] | |
| bb_std = self.config['bb_std'] | |
| sma = close.rolling(window=bb_period).mean() | |
| std = close.rolling(window=bb_period).std() | |
| df['bb_lower'] = sma - bb_std * std | |
| df['bb_mid'] = sma | |
| df['bb_upper'] = sma + bb_std * std | |
| bb_range = df['bb_upper'] - df['bb_lower'] | |
| # Avoid division by zero | |
| bb_range = bb_range.replace(0, 1e-8) | |
| df['bb_bandwidth'] = bb_range / df['bb_mid'] | |
| df['bb_position'] = 2 * (close - df['bb_lower']) / bb_range - 1 | |
| df['bb_position'] = df['bb_position'].clip(-2, 2) | |
| # ATR | |
| tr1 = high - low | |
| tr2 = (high - close.shift()).abs() | |
| tr3 = (low - close.shift()).abs() | |
| tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) | |
| df['atr'] = tr.rolling(window=self.config['atr_period']).mean() | |
| df['atr_norm'] = df['atr'] / close | |
| # EMAs | |
| for period in self.config['ema_periods']: | |
| df[f'ema_{period}'] = close.ewm(span=period, adjust=False).mean() | |
| df[f'ema_{period}_dist'] = (close - df[f'ema_{period}']) / close | |
| # Volume indicators | |
| df['volume_sma'] = volume.rolling(window=20).mean() | |
| df['volume_ratio'] = volume / df['volume_sma'].replace(0, 1e-8) | |
| df['volume_ratio'] = df['volume_ratio'].clip(0, 5) | |
| # Price momentum | |
| df['returns'] = close.pct_change() | |
| df['returns_5'] = close.pct_change(5) | |
| df['returns_10'] = close.pct_change(10) | |
| # Volatility | |
| df['volatility'] = df['returns'].rolling(window=20).std() | |
| return df | |
| def get_feature_columns(self) -> List[str]: | |
| """ | |
| Returns the list of feature columns to use for the observation space. | |
| """ | |
| return [ | |
| 'rsi_norm', | |
| 'macd_norm', 'macd_signal_norm', 'macd_hist_norm', | |
| 'bb_position', 'bb_bandwidth', | |
| 'atr_norm', | |
| 'ema_9_dist', 'ema_21_dist', 'ema_50_dist', | |
| 'volume_ratio', | |
| 'returns', 'returns_5', 'returns_10', | |
| 'volatility', | |
| ] | |
| def get_normalized_features(self, df: pd.DataFrame) -> np.ndarray: | |
| """ | |
| Extract normalized feature array from dataframe. | |
| """ | |
| features = df[self.get_feature_columns()].values | |
| features = np.nan_to_num(features, nan=0.0, posinf=1.0, neginf=-1.0) | |
| return features | |
| def compute_indicators(ohlcv_data: pd.DataFrame, config: Optional[Dict] = None) -> pd.DataFrame: | |
| """ | |
| Convenience function to compute all indicators. | |
| Args: | |
| ohlcv_data: DataFrame with OHLCV columns | |
| config: Optional indicator configuration | |
| Returns: | |
| DataFrame with all indicators computed | |
| """ | |
| indicators = TechnicalIndicators(config) | |
| return indicators.compute_all(ohlcv_data) | |