Skip to content

Commit e6456f6

Browse files
Merge branch 'aditya' of https://github.com/adityacosmos24/QuantResearch_Opcode into aditya
2 parents a4fecd2 + 18fe964 commit e6456f6

File tree

1 file changed

+15
-35
lines changed

1 file changed

+15
-35
lines changed

src/quant_research_starter/factors/volatility.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from __future__ import annotations
2222

23+
import warnings
2324
from typing import Optional
2425

2526
import numpy as np
@@ -35,9 +36,7 @@
3536
except Exception:
3637
# Minimal Factor stub so this module can be inspected/tested in isolation.
3738
class Factor:
38-
def __init__(
39-
self, name: Optional[str] = None, lookback: Optional[int] = None
40-
):
39+
def __init__(self, name: Optional[str] = None, lookback: Optional[int] = None):
4140
self.name = name or "factor"
4241
self.lookback = lookback or 0
4342
self._values: Optional[pd.DataFrame] = None
@@ -49,7 +48,6 @@ def _validate_data(self, prices: pd.DataFrame) -> None:
4948
def __repr__(self) -> str:
5049
return f"<Factor name={self.name} lookback={self.lookback}>"
5150

52-
5351
# Constants
5452
TRADING_DAYS = 252
5553

@@ -95,17 +93,15 @@ def compute(self, prices: pd.DataFrame) -> pd.DataFrame:
9593
self._validate_data(prices)
9694

9795
if prices.shape[0] < self.lookback:
98-
raise ValueError(
99-
f"Need at least {self.lookback} rows of data to compute volatility"
100-
)
96+
raise ValueError(f"Need at least {self.lookback} rows of data to compute volatility")
10197

10298
# pct change -> returns
10399
returns = prices.pct_change()
104100

105101
# rolling std (population, ddof=0) and annualize
106-
vol = returns.rolling(window=self.lookback, min_periods=self.lookback).std(
107-
ddof=0
108-
) * np.sqrt(TRADING_DAYS)
102+
vol = returns.rolling(window=self.lookback, min_periods=self.lookback).std(ddof=0) * np.sqrt(
103+
TRADING_DAYS
104+
)
109105

110106
# Trim initial rows that don't correspond to a full window
111107
if self.lookback > 1:
@@ -152,42 +148,26 @@ def compute(self, prices: pd.DataFrame) -> pd.DataFrame:
152148

153149
# require enough rows to compute returns and rolling windows
154150
if prices.shape[0] < self.lookback + 1:
155-
raise ValueError(
156-
f"Need at least {self.lookback + 1} rows of data to compute idiosyncratic volatility"
157-
)
151+
raise ValueError(f"Need at least {self.lookback + 1} rows of data to compute idiosyncratic volatility")
158152

159153
# daily returns
160154
returns = prices.pct_change().dropna()
161155
if returns.shape[0] < self.lookback:
162-
raise ValueError(
163-
f"Need at least {self.lookback} non-NA return rows to compute idio-vol"
164-
)
156+
raise ValueError(f"Need at least {self.lookback} non-NA return rows to compute idio-vol")
165157

166158
# Market proxy: equal-weighted mean across assets
167159
market = returns.mean(axis=1)
168160

169161
# Rolling means for covariance decomposition
170-
returns_mean = returns.rolling(
171-
window=self.lookback, min_periods=self.lookback
172-
).mean()
173-
market_mean = market.rolling(
174-
window=self.lookback, min_periods=self.lookback
175-
).mean()
162+
returns_mean = returns.rolling(window=self.lookback, min_periods=self.lookback).mean()
163+
market_mean = market.rolling(window=self.lookback, min_periods=self.lookback).mean()
176164

177165
# Compute cov(ri, rm) via E[ri*rm] - E[ri]*E[rm]
178-
e_ri_rm = (
179-
returns.mul(market, axis=0)
180-
.rolling(window=self.lookback, min_periods=self.lookback)
181-
.mean()
182-
)
166+
e_ri_rm = returns.mul(market, axis=0).rolling(window=self.lookback, min_periods=self.lookback).mean()
183167
cov_with_mkt = e_ri_rm - returns_mean.mul(market_mean, axis=0)
184168

185169
# market variance (vector) -- guard zeros
186-
market_var = (
187-
market.rolling(window=self.lookback, min_periods=self.lookback)
188-
.var(ddof=0)
189-
.replace(0, np.nan)
190-
)
170+
market_var = market.rolling(window=self.lookback, min_periods=self.lookback).var(ddof=0).replace(0, np.nan)
191171

192172
# Beta: cov / var (division broadcasted over columns)
193173
beta = cov_with_mkt.div(market_var, axis=0)
@@ -199,9 +179,9 @@ def compute(self, prices: pd.DataFrame) -> pd.DataFrame:
199179
residuals = returns - predicted
200180

201181
# Rolling std of residuals (annualized)
202-
idio_vol = residuals.rolling(
203-
window=self.lookback, min_periods=self.lookback
204-
).std(ddof=0) * np.sqrt(TRADING_DAYS)
182+
idio_vol = residuals.rolling(window=self.lookback, min_periods=self.lookback).std(ddof=0) * np.sqrt(
183+
TRADING_DAYS
184+
)
205185

206186
# Trim to first full-window row
207187
if self.lookback > 1:

0 commit comments

Comments
 (0)