Coverage for src / monte_neo / indicators / technical_lib.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-28 16:27 +0200

1from __future__ import annotations 

2 

3import pandas as pd 

4 

5 

6class TechnicalIndicators: 

7 """Collection of technical indicator calculations.""" 

8 

9 @staticmethod 

10 def sma(data: pd.Series, period: int) -> pd.Series: 

11 """Simple Moving Average.""" 

12 return data.rolling(window=int(period)).mean() 

13 

14 @staticmethod 

15 def ema(data: pd.Series, period: int) -> pd.Series: 

16 """Exponential Moving Average.""" 

17 return data.ewm(span=int(period), adjust=False).mean() 

18 

19 @staticmethod 

20 def rsi(data: pd.Series, period: int = 14) -> pd.Series: 

21 """Relative Strength Index.""" 

22 period = int(period) 

23 delta = data.diff() 

24 gain = (delta.where(delta > 0, 0)).rolling(period).mean() 

25 loss = (-delta.where(delta < 0, 0)).rolling(period).mean() 

26 

27 rs = gain / loss 

28 return 100 - (100 / (1 + rs)) 

29 

30 @staticmethod 

31 def macd( 

32 data: pd.Series, 

33 fast: int = 12, 

34 slow: int = 26, 

35 signal: int = 9, 

36 ) -> tuple[pd.Series, pd.Series, pd.Series]: 

37 """MACD indicator.""" 

38 fast_ema = data.ewm(span=int(fast), adjust=False).mean() 

39 slow_ema = data.ewm(span=int(slow), adjust=False).mean() 

40 macd_line = fast_ema - slow_ema 

41 signal_line = macd_line.ewm(span=int(signal), adjust=False).mean() 

42 histogram = macd_line - signal_line 

43 return macd_line, signal_line, histogram 

44 

45 @staticmethod 

46 def bollinger_bands( 

47 data: pd.Series, 

48 period: int = 20, 

49 std_dev: float = 2.0, 

50 ) -> tuple[pd.Series, pd.Series, pd.Series]: 

51 """Bollinger Bands.""" 

52 middle = data.rolling(period).mean() 

53 std = data.rolling(period).std() 

54 upper = middle + (std * std_dev) 

55 lower = middle - (std * std_dev) 

56 return upper, middle, lower 

57 

58 @staticmethod 

59 def atr(data: pd.DataFrame, period: int = 14) -> pd.Series: 

60 """Average True Range.""" 

61 period = int(period) 

62 high = data["high"] 

63 low = data["low"] 

64 close = data["close"] 

65 

66 tr1 = high - low 

67 tr2 = abs(high - close.shift()) 

68 tr3 = abs(low - close.shift()) 

69 

70 tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) 

71 return tr.rolling(period).mean() 

72 

73 @staticmethod 

74 def stochastic( 

75 data: pd.DataFrame, 

76 k_period: int = 14, 

77 d_period: int = 3, 

78 ) -> tuple[pd.Series, pd.Series]: 

79 """Stochastic Oscillator.""" 

80 k_period, d_period = int(k_period), int(d_period) 

81 low_min = data["low"].rolling(k_period).min() 

82 high_max = data["high"].rolling(k_period).max() 

83 

84 k = 100 * ((data["close"] - low_min) / (high_max - low_min)) 

85 d = k.rolling(d_period).mean() 

86 return k, d