Skip to content

Commit 1a791c8

Browse files
committed
Add kalman
1 parent 99e580b commit 1a791c8

File tree

6 files changed

+102
-3
lines changed

6 files changed

+102
-3
lines changed

docs/api/ta/kalman.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Kalman Filter
2+
3+
::: quantflow.ta.KalmanFilter

docs/api/ta/supersmoother.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ Reference:
1111
Ehlers, J. (2013). "Cycle Analytics for Traders"
1212

1313

14-
::: quantflow.ta.supersmoother.SuperSmoother
14+
::: quantflow.ta.SuperSmoother

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ nav:
5656
- api/ta/index.md
5757
- OHLC: api/ta/ohlc.md
5858
- Paths: api/ta/paths.md
59+
- Kalman Filter: api/ta/kalman.md
5960
- Super Smoother: api/ta/supersmoother.md
6061
- Utils:
6162
- api/utils/index.md

quantflow/ta/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .supersmoother import SuperSmoother
2+
from .kalman import KalmanFilter
3+
4+
5+
__all__ = ["SuperSmoother", "KalmanFilter"]

quantflow/ta/kalman.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from typing_extensions import Annotated, Doc
2+
from pydantic import BaseModel, Field, PrivateAttr
3+
4+
5+
class KalmanFilter(BaseModel):
6+
r"""One-dimensional Kalman filter for time series data.
7+
8+
This implementation uses a simple 1D state-space model:
9+
10+
$$
11+
\begin{align}
12+
x_t &= x_{t-1} + w_t, \quad w_t \sim \mathcal{N}(0, Q) \\
13+
z_t &= x_t + v_t, \quad v_t \sim \mathcal{N}(0, R)
14+
\end{align}
15+
$$
16+
17+
The Kalman filter estimates the hidden state $x_t$ given noisy measurements $z_t$.
18+
The ratio $Q/R$ determines the smoothing behavior.
19+
"""
20+
21+
R: float = Field(default=1.0, gt=0.0, description="Measurement noise covariance")
22+
Q: float = Field(default=0.01, gt=0.0, description="Process noise covariance")
23+
24+
_x: float | None = PrivateAttr(default=None) # State estimate
25+
_P: float = PrivateAttr(default=1.0) # Error covariance
26+
_K: float = PrivateAttr(default=0.0) # Kalman Gain
27+
28+
def value(self) -> float | None:
29+
"""Get the most recent smoothed value (state estimate), if available."""
30+
return self._x
31+
32+
def update(
33+
self,
34+
value: Annotated[float, Doc("New noisy measurement to update the filter")],
35+
) -> float:
36+
"""Update the filter with a new value and return the smoothed result."""
37+
# Initialize on first update
38+
if self._x is None:
39+
self._x = value
40+
self._P = self.R
41+
return value
42+
43+
# Prediction step
44+
# x_pred = x_prev (Random walk model)
45+
# P_pred = P_prev + Q
46+
x_pred = self._x
47+
P_pred = self._P + self.Q
48+
49+
# Update step
50+
# K = P_pred / (P_pred + R)
51+
# x_new = x_pred + K * (measurement - x_pred)
52+
# P_new = (1 - K) * P_pred
53+
K = P_pred / (P_pred + self.R)
54+
x_new = x_pred + K * (value - x_pred)
55+
P_new = (1 - K) * P_pred
56+
57+
# Update state
58+
self._x = x_new
59+
self._P = P_new
60+
self._K = K
61+
62+
return x_new
63+
64+
@property
65+
def error_covariance(self) -> float:
66+
"""Current estimated error covariance."""
67+
return self._P
68+
69+
@property
70+
def kalman_gain(self) -> float:
71+
"""Most recent Kalman gain."""
72+
return self._K

quantflow/ta/supersmoother.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,30 @@
66

77

88
class SuperSmoother(BaseModel):
9-
"""SuperSmoother filter for time series data.
9+
r"""SuperSmoother filter for time series data.
1010
1111
This implementation uses a two-pole Butterworth filter with adaptive smoothing.
12-
1312
The SuperSmoother filter is designed to remove high-frequency noise while
1413
preserving the underlying trend with minimal lag.
14+
The filter is defined by the following recurrence relation:
15+
16+
$$
17+
y_t = c_1 \frac{x_t + x_{t-1}}{2} + c_2 y_{t-1} + c_3 y_{t-2}
18+
$$
19+
20+
where the coefficients are calculated as:
21+
22+
$$
23+
\begin{align}
24+
\lambda &= \frac{\pi \sqrt{2}}{N} \\
25+
a &= \exp(-\lambda) \\
26+
c_2 &= 2 a \cos(\lambda) \\
27+
c_3 &= -a^2 \\
28+
c_1 &= 1 - c_2 - c_3
29+
\end{align}
30+
$$
31+
32+
where $N$ is the period.
1533
1634
## Example
1735

0 commit comments

Comments
 (0)