DockerSpace / data /peer_returns.py
DennisChan0909's picture
Wire C30 peer returns into production + add C29 MetaLabel log
70307c7
Raw
History Blame Contribute Delete
2.43 kB
"""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