Coverage for src / monte_neo / indicators / numba_funcs.py: 13%
118 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-28 16:27 +0200
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-28 16:27 +0200
1from __future__ import annotations
3import numpy as np
4from numba import njit
7@njit
8def sma_numba(data: np.ndarray, period: int) -> np.ndarray:
9 """Fast SMA calculation."""
10 res = np.full(data.shape, np.nan)
11 if len(data) < period:
12 return res
14 current_sum = 0.0
15 for i in range(period):
16 current_sum += data[i]
18 res[period - 1] = current_sum / period
20 for i in range(period, len(data)):
21 current_sum = current_sum - data[i - period] + data[i]
22 res[i] = current_sum / period
24 return res
27@njit
28def ema_numba(data: np.ndarray, period: int) -> np.ndarray:
29 """Fast EMA calculation."""
30 res = np.full(data.shape, np.nan)
31 if len(data) == 0:
32 return res
34 alpha = 2.0 / (period + 1)
35 res[0] = data[0]
37 for i in range(1, len(data)):
38 res[i] = (data[i] - res[i - 1]) * alpha + res[i - 1]
40 return res
43@njit
44def rsi_numba(data: np.ndarray, period: int) -> np.ndarray:
45 """Fast RSI calculation."""
46 n = len(data)
47 res = np.full(n, np.nan)
48 if n <= period:
49 return res
51 # First period calculation (SMA of gains/losses)
52 gain_sum = 0.0
53 loss_sum = 0.0
55 for i in range(1, period + 1):
56 change = data[i] - data[i-1]
57 if change > 0:
58 gain_sum += change
59 else:
60 loss_sum -= change
62 avg_gain = gain_sum / period
63 avg_loss = loss_sum / period
65 if avg_loss == 0:
66 res[period] = 100.0
67 else:
68 rs = avg_gain / avg_loss
69 res[period] = 100.0 - (100.0 / (1.0 + rs))
71 # Subsequent periods (Wilder's Smoothing)
72 for i in range(period + 1, n):
73 change = data[i] - data[i-1]
74 current_gain = change if change > 0 else 0.0
75 current_loss = -change if change < 0 else 0.0
77 avg_gain = (avg_gain * (period - 1) + current_gain) / period
78 avg_loss = (avg_loss * (period - 1) + current_loss) / period
80 if avg_loss == 0:
81 res[i] = 100.0
82 else:
83 rs = avg_gain / avg_loss
84 res[i] = 100.0 - (100.0 / (1.0 + rs))
86 return res
89@njit
90def sma_crossover_signals_numba(data: np.ndarray, fast_period: int, slow_period: int) -> np.ndarray:
91 """Full SMA crossover signal generation in a single Numba pass."""
92 n = len(data)
93 res = np.zeros(n, dtype=np.float32)
94 if n < slow_period or fast_period >= slow_period:
95 return res
97 sum_fast = 0.0
98 sum_slow = 0.0
100 # Initial sums
101 # Initial sums for the window ending at slow_period - 1
102 sum_fast = 0.0
103 sum_slow = 0.0
104 for i in range(slow_period - fast_period, slow_period):
105 sum_fast += data[i]
106 for i in range(slow_period):
107 sum_slow += data[i]
109 # Initial state at slow_period - 1
110 sma_fast = sum_fast / fast_period
111 sma_slow = sum_slow / slow_period
112 prev_state = 1 if sma_fast > sma_slow else -1
114 for i in range(slow_period, n):
115 sum_fast = sum_fast - data[i - fast_period] + data[i]
116 sum_slow = sum_slow - data[i - slow_period] + data[i]
118 sma_fast = sum_fast / fast_period
119 sma_slow = sum_slow / slow_period
121 current_state = 1 if sma_fast > sma_slow else -1
123 if current_state != prev_state:
124 res[i] = float(current_state)
125 prev_state = current_state
127 return res
130@njit
131def rsi_signals_numba(data: np.ndarray, period: int, oversold: float, overbought: float) -> np.ndarray:
132 """Fast RSI signal generation in a single Numba pass."""
133 n = len(data)
134 res = np.zeros(n, dtype=np.float32)
135 if n <= period:
136 return res
138 # Use existing rsi_numba
139 rsi_vals = rsi_numba(data, period)
141 for i in range(period, n):
142 if rsi_vals[i] < oversold:
143 res[i] = 1.0
144 elif rsi_vals[i] > overbought:
145 res[i] = -1.0
147 return res
150@njit
151def macd_signals_numba(data: np.ndarray, fast_p: int, slow_p: int, sig_p: int) -> np.ndarray:
152 """Fast MACD signal generation in a single Numba pass."""
153 n = len(data)
154 res = np.zeros(n, dtype=np.float32)
155 if n < slow_p + sig_p:
156 return res
158 fast_ema = ema_numba(data, fast_p)
159 slow_ema = ema_numba(data, slow_p)
160 macd_line = fast_ema - slow_ema
161 signal_line = ema_numba(macd_line, sig_p)
162 hist_vals = macd_line - signal_line
164 prev_sig = 0.0
165 for i in range(slow_p + sig_p, n):
166 curr_sig = 0.0
167 if hist_vals[i] > 0:
168 curr_sig = 1.0
169 elif hist_vals[i] < 0:
170 curr_sig = -1.0
172 if curr_sig != prev_sig:
173 res[i] = curr_sig
174 prev_sig = curr_sig
176 return res