Skip to content

Commit 9d45cff

Browse files
mjvakilicclauss
andauthored
Streaming moving average (#11826)
* Implementation of streaming moving average * Added reference to streaming moving average * fixed a bug and added return type to __init__ * fractional differentiation * dev fracdiff in new branch * updating DIRECTORY.md --------- Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: cclauss <cclauss@users.noreply.github.com>
1 parent ae429ad commit 9d45cff

2 files changed

Lines changed: 92 additions & 0 deletions

File tree

‎DIRECTORY.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@
561561
* [Sharpe Ratio](financial/sharpe_ratio.py)
562562
* [Simple Moving Average](financial/simple_moving_average.py)
563563
* [Straight Line Depreciation](financial/straight_line_depreciation.py)
564+
* [Streaming Moving Average](financial/streaming_moving_average.py)
564565
* [Time And Half Pay](financial/time_and_half_pay.py)
565566
* [Value At Risk](financial/value_at_risk.py)
566567

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""This module contains a class that
2+
calculates the moving average in real-time.
3+
4+
Reference
5+
---------
6+
https://en.wikipedia.org/wiki/Moving_average
7+
"""
8+
9+
from collections import deque
10+
11+
12+
class StreamingMovingAverage:
13+
"""Streaming Moving Average calculator that updates
14+
the average in real-time.
15+
16+
Attributes
17+
----------
18+
window_size : int
19+
The size of the moving average window.
20+
21+
Methods
22+
-------
23+
update(new_value: float) -> None
24+
Update the moving average with a new data point.
25+
mov_avg() -> float
26+
Return the current moving average value.
27+
28+
Raises
29+
------
30+
ValueError
31+
If the window size is less than 1.
32+
33+
34+
Examples
35+
--------
36+
>>> mov_avg_calculator = StreamingMovingAverage(3)
37+
>>> mov_avg_calculator.update(10.)
38+
>>> mov_avg_calculator.update(12.)
39+
>>> mov_avg_calculator.update(2.)
40+
>>> mov_avg_calculator.update(1.)
41+
>>> mov_avg_calculator.mov_avg
42+
5.0
43+
>>> mov_avg_calculator = StreamingMovingAverage(5)
44+
>>> mov_avg_calculator.update(2.)
45+
>>> mov_avg_calculator.update(3.)
46+
>>> mov_avg_calculator.mov_avg
47+
2.5
48+
"""
49+
50+
def __init__(self, window_size: int) -> None:
51+
if window_size < 1:
52+
raise ValueError("Window size must be a positive integer")
53+
self.window_size = window_size
54+
self.window: deque[float] = deque(maxlen=window_size)
55+
self._mov_avg: float = 0.0 # Placeholder for online moving average.
56+
57+
def update(self, new_value: float) -> None:
58+
"""Update the moving average with a new data point.
59+
60+
Parameters
61+
----------
62+
new_value : float
63+
The new data point to update the moving average.
64+
65+
Raises
66+
------
67+
TypeError
68+
If the type of new value is not float.
69+
"""
70+
if not isinstance(new_value, float):
71+
raise TypeError("Type of new_value must be either float.")
72+
self.window.append(new_value)
73+
self._mov_avg = sum(self.window) / len(self.window)
74+
75+
@property
76+
def mov_avg(self) -> float:
77+
"""Return the current moving average value."""
78+
return self._mov_avg
79+
80+
81+
if __name__ == "__main__":
82+
import doctest
83+
import random
84+
85+
doctest.testmod()
86+
moving_average_calculator = StreamingMovingAverage(window_size=3)
87+
example_data = [float(random.randint(a=1, b=10)) for _ in range(10)]
88+
for value in example_data:
89+
print(f"adding {value=} to streaming data.")
90+
moving_average_calculator.update(new_value=value)
91+
print(f"updated moving average={moving_average_calculator.mov_avg}")

0 commit comments

Comments
 (0)