Spaces:
Running
Running
| """Sector peer return features for ML training (C30). | |
| Adds prior-day mean return of same-sector peers as non-leaking features: | |
| peer_ret_1d — yesterday's 1-day return of sector peers (mean) | |
| peer_ret_5d — 5-day return of peers ending yesterday (mean) | |
| Stocks with no peers (telecom) get 0.0 fill. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from typing import Sequence | |
| import numpy as np | |
| import pandas as pd | |
| logger = logging.getLogger(__name__) | |
| SECTOR_MAP: dict[str, list[str]] = { | |
| "semis": ["2330", "2454", "2303"], | |
| "electronics": ["2317", "2382", "2308"], | |
| "financials": ["2881", "2882", "2886"], | |
| "etfs": ["0050", "0056"], | |
| "telecom": ["2412"], | |
| } | |
| _STOCK_SECTOR = {s: sec for sec, members in SECTOR_MAP.items() for s in members} | |
| def _peer_list(stock_no: str) -> list[str]: | |
| sector = _STOCK_SECTOR.get(stock_no, "") | |
| return [p for p in SECTOR_MAP.get(sector, []) if p != stock_no] | |
| def add_peer_returns(df: pd.DataFrame, stock_no: str) -> pd.DataFrame: | |
| """Fetch peer close prices and add peer_ret_1d / peer_ret_5d to df.""" | |
| peers = _peer_list(stock_no) | |
| if not peers or "date" not in df.columns: | |
| df["peer_ret_1d"] = 0.0 | |
| df["peer_ret_5d"] = 0.0 | |
| return df | |
| from services.predictor_service import _fetch_with_cache | |
| date_col = df["date"].astype(str) | |
| r1_series, r5_series = [], [] | |
| for peer in peers: | |
| try: | |
| peer_df = _fetch_with_cache(peer, months=24) | |
| if peer_df is None or peer_df.empty or "date" not in peer_df.columns: | |
| continue | |
| close = peer_df.set_index("date")["close"].astype(float) | |
| r1 = close.pct_change(1).shift(1) | |
| r5 = close.pct_change(5).shift(1) | |
| r1_series.append(date_col.map(r1.to_dict()).astype(float)) | |
| r5_series.append(date_col.map(r5.to_dict()).astype(float)) | |
| except Exception as exc: | |
| logger.debug("peer_returns fetch failed for %s peer %s: %s", stock_no, peer, exc) | |
| if not r1_series: | |
| df["peer_ret_1d"] = 0.0 | |
| df["peer_ret_5d"] = 0.0 | |
| return df | |
| idx = df.index | |
| df["peer_ret_1d"] = ( | |
| pd.concat(r1_series, axis=1).mean(axis=1) | |
| .ffill().bfill().fillna(0.0) | |
| .values | |
| ) | |
| df["peer_ret_5d"] = ( | |
| pd.concat(r5_series, axis=1).mean(axis=1) | |
| .ffill().bfill().fillna(0.0) | |
| .values | |
| ) | |
| return df | |