Spaces:
Runtime error
Runtime error
| """Full V5 Feature Engineering β mirrors cleaned notebook Part A (pandas version)""" | |
| import pandas as pd | |
| import numpy as np | |
| from ta.momentum import RSIIndicator # type: ignore | |
| from ta.trend import MACD # type: ignore | |
| def add_v5_features(df: pd.DataFrame) -> tuple[pd.DataFrame, list]: | |
| df = df.copy() | |
| if not isinstance(df.index, pd.DatetimeIndex): | |
| df = df.set_index('timestamp').sort_index() | |
| c, h, l, v, o = df['close'], df['high'], df['low'], df['volume'], df['open'] | |
| # === Core V5 blocks from notebook === | |
| for lag in [1, 2, 4, 6, 12, 24, 48, 168]: | |
| df[f'return_{lag}h'] = c.pct_change(lag) | |
| df['candle_body_ratio'] = (c - o) / (h - l + 1e-10) | |
| df['range_pct'] = (h - l) / (c + 1e-10) | |
| df['close_location_value'] = (c - l) / (h - l + 1e-10) | |
| for w in [4, 12, 24, 48, 168]: | |
| df[f'sma_{w}h'] = c.rolling(w).mean() | |
| df[f'vol_{w}h'] = c.rolling(w).std() | |
| df[f'vol_avg_{w}h'] = v.rolling(w).mean() | |
| df[f'price_vs_sma_{w}h'] = c / df[f'sma_{w}h'] - 1 | |
| for period in [6, 14, 24]: | |
| df[f'rsi_{period}h'] = RSIIndicator(c, window=period).rsi() | |
| macd = MACD(c, window_slow=26, window_fast=12, window_sign=9) | |
| df['macd_h'] = macd.macd() | |
| df['macd_signal_h'] = macd.macd_signal() | |
| df['macd_hist_h'] = macd.macd_diff() | |
| df['vol_ratio_24h'] = v / (v.rolling(24).mean() + 1e-10) | |
| df['vol_ratio_168h'] = v / (v.rolling(168).mean() + 1e-10) | |
| # On-chain momentum | |
| for col in ['tx_count', 'active_senders', 'active_receivers', 'total_eth_transferred', 'total_gas_used']: | |
| if col in df.columns: | |
| df[f'{col}_change_24h'] = df[col] / (df[col].shift(24) + 1e-10) - 1 | |
| df[f'{col}_ma24h'] = df[col].rolling(24).mean() | |
| # Cyclical | |
| df['hour_sin'] = np.sin(2 * np.pi * df.index.hour / 24) # type: ignore | |
| df['hour_cos'] = np.cos(2 * np.pi * df.index.hour / 24) # type: ignore | |
| df['dow_sin'] = np.sin(2 * np.pi * df.index.dayofweek / 7) # type: ignore | |
| df['dow_cos'] = np.cos(2 * np.pi * df.index.dayofweek / 7) # type: ignore | |
| # 30d regime features (used by filter & routing) | |
| df['ret_30d'] = c.pct_change(30*24) # β30d on 1h bars | |
| df['vol_30d'] = df['return'].rolling(30*24).std() | |
| df = df.dropna() | |
| feature_cols = [col for col in df.columns if col not in | |
| {'open','high','low','close','volume','timestamp'}] | |
| print(f"β Built {len(feature_cols)} V5 features (matches notebook Part A)") | |
| return df, feature_cols |