Skip to content

Commit 2bf33aa

Browse files
feat: add Bollinger Bands factor
1 parent 84194f1 commit 2bf33aa

File tree

4 files changed

+10
-6
lines changed

4 files changed

+10
-6
lines changed

.coverage

0 Bytes
Binary file not shown.

src/quant_research_starter/factors/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
"""Factors module public API."""
22

33
from .base import Factor
4+
from .bollinger import BollingerBandsFactor
45
from .momentum import CrossSectionalMomentum, MomentumFactor
56
from .size import SizeFactor
67
from .value import ValueFactor
78
from .volatility import IdiosyncraticVolatility, VolatilityFactor
8-
from .bollinger import BollingerBandsFactor
9-
109

1110
__all__ = [
1211
"Factor",
12+
"BollingerBandsFactor",
1313
"MomentumFactor",
1414
"CrossSectionalMomentum",
1515
"ValueFactor",

src/quant_research_starter/factors/bollinger.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pandas as pd
2+
23
from .base import Factor
34

45

@@ -8,7 +9,9 @@ class BollingerBandsFactor(Factor):
89
z = (price - rolling_mean) / rolling_std
910
"""
1011

11-
def __init__(self, name: str = "bollinger_bands", lookback: int = 20, num_std: float = 2.0):
12+
def __init__(
13+
self, name: str = "bollinger_bands", lookback: int = 20, num_std: float = 2.0
14+
):
1215
super().__init__(name=name, lookback=lookback)
1316
self.num_std = num_std
1417

@@ -26,4 +29,4 @@ def compute(self, prices: pd.DataFrame) -> pd.DataFrame:
2629
# Save results
2730
self._values = zscore
2831

29-
return zscore
32+
return zscore

tests/test_factors.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import pytest
66

77
from quant_research_starter.factors import (
8+
BollingerBandsFactor,
89
MomentumFactor,
910
SizeFactor,
1011
ValueFactor,
1112
VolatilityFactor,
12-
BollingerBandsFactor,
1313
)
1414

1515

@@ -197,6 +197,7 @@ def test_volatility_calculation(self):
197197
spearman_corr < -0.5
198198
), f"volatility factor should be negatively correlated with realized volatility (spearman={spearman_corr})"
199199

200+
200201
class TestBollingerBandsFactor:
201202
"""Test Bollinger Bands factor calculations."""
202203

@@ -211,7 +212,7 @@ def test_bollinger_basic(self, sample_prices):
211212
assert set(result.columns) == set(sample_prices.columns)
212213

213214
# Values should be finite where enough data exists
214-
valid_values = result.iloc[factor.lookback:].values.flatten()
215+
valid_values = result.iloc[factor.lookback :].values.flatten()
215216
assert np.all(np.isfinite(valid_values)), "NaNs found after lookback period"
216217

217218
# Mean of z-scores should be roughly centered near 0

0 commit comments

Comments
 (0)