bquant 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bquant/__init__.py +31 -0
- bquant/analysis/__init__.py +297 -0
- bquant/analysis/candlestick/__init__.py +100 -0
- bquant/analysis/chart/__init__.py +98 -0
- bquant/analysis/statistical/__init__.py +414 -0
- bquant/analysis/statistical/hypothesis_testing.py +657 -0
- bquant/analysis/technical/__init__.py +98 -0
- bquant/analysis/timeseries/__init__.py +102 -0
- bquant/analysis/zones/__init__.py +547 -0
- bquant/analysis/zones/sequence_analysis.py +661 -0
- bquant/analysis/zones/zone_features.py +520 -0
- bquant/cli.py +160 -0
- bquant/core/__init__.py +112 -0
- bquant/core/cache.py +534 -0
- bquant/core/config.py +328 -0
- bquant/core/exceptions.py +351 -0
- bquant/core/logging_config.py +368 -0
- bquant/core/numpy_fix.py +99 -0
- bquant/core/performance.py +554 -0
- bquant/core/utils.py +327 -0
- bquant/data/__init__.py +85 -0
- bquant/data/loader.py +436 -0
- bquant/data/processor.py +644 -0
- bquant/data/samples/__init__.py +442 -0
- bquant/data/samples/datasets.py +246 -0
- bquant/data/samples/embedded/__init__.py +16 -0
- bquant/data/samples/embedded/mt_xauusd_m15.py +9018 -0
- bquant/data/samples/embedded/tv_xauusd_1h.py +17018 -0
- bquant/data/samples/utils.py +393 -0
- bquant/data/schemas.py +285 -0
- bquant/data/validator.py +498 -0
- bquant/indicators/__init__.py +115 -0
- bquant/indicators/base.py +487 -0
- bquant/indicators/calculators.py +404 -0
- bquant/indicators/library.py +514 -0
- bquant/indicators/loaders.py +413 -0
- bquant/indicators/macd.py +754 -0
- bquant/ml/__init__.py +19 -0
- bquant/visualization/__init__.py +316 -0
- bquant/visualization/charts.py +616 -0
- bquant/visualization/statistical.py +812 -0
- bquant/visualization/themes.py +599 -0
- bquant/visualization/zones.py +775 -0
- bquant-0.0.0.dist-info/METADATA +167 -0
- bquant-0.0.0.dist-info/RECORD +49 -0
- bquant-0.0.0.dist-info/WHEEL +5 -0
- bquant-0.0.0.dist-info/entry_points.txt +2 -0
- bquant-0.0.0.dist-info/licenses/LICENSE +21 -0
- bquant-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BQuant built-in indicator library
|
|
3
|
+
|
|
4
|
+
This module contains preloaded technical indicators implemented specifically for BQuant.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import numpy as np
|
|
9
|
+
from typing import Dict, Any, Optional, List, Tuple
|
|
10
|
+
|
|
11
|
+
from .base import PreloadedIndicator, IndicatorResult, IndicatorConfig, IndicatorSource
|
|
12
|
+
from ..core.exceptions import IndicatorCalculationError
|
|
13
|
+
from ..core.logging_config import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SimpleMovingAverage(PreloadedIndicator):
|
|
19
|
+
"""
|
|
20
|
+
Simple Moving Average (SMA) indicator.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, period: int = 20):
|
|
24
|
+
"""
|
|
25
|
+
Initialize SMA indicator.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
period: Period for moving average calculation
|
|
29
|
+
"""
|
|
30
|
+
self.period = period
|
|
31
|
+
super().__init__('sma', {'period': period})
|
|
32
|
+
|
|
33
|
+
def get_output_columns(self) -> List[str]:
|
|
34
|
+
"""Returns output columns."""
|
|
35
|
+
return [f'sma_{self.period}']
|
|
36
|
+
|
|
37
|
+
def get_description(self) -> str:
|
|
38
|
+
"""Returns indicator description."""
|
|
39
|
+
return f"Simple Moving Average with {self.period} period"
|
|
40
|
+
|
|
41
|
+
def get_min_records(self) -> int:
|
|
42
|
+
"""Returns minimum records required."""
|
|
43
|
+
return self.period
|
|
44
|
+
|
|
45
|
+
def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
|
|
46
|
+
"""
|
|
47
|
+
Calculate SMA.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
data: DataFrame with price data
|
|
51
|
+
**kwargs: Additional parameters
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
IndicatorResult with SMA values
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
self.validate_data(data)
|
|
58
|
+
|
|
59
|
+
# Получаем период из kwargs или используем значение по умолчанию
|
|
60
|
+
period = kwargs.get('period', self.period)
|
|
61
|
+
|
|
62
|
+
self.logger.info(f"Calculating SMA with period {period}")
|
|
63
|
+
|
|
64
|
+
# Вычисляем SMA
|
|
65
|
+
sma_values = data['close'].rolling(window=period).mean()
|
|
66
|
+
|
|
67
|
+
result_data = pd.DataFrame({
|
|
68
|
+
f'sma_{period}': sma_values
|
|
69
|
+
}, index=data.index)
|
|
70
|
+
|
|
71
|
+
return IndicatorResult(
|
|
72
|
+
name=self.name,
|
|
73
|
+
data=result_data,
|
|
74
|
+
config=self.config,
|
|
75
|
+
metadata={
|
|
76
|
+
'period': period,
|
|
77
|
+
'calculation_method': 'rolling_mean',
|
|
78
|
+
'first_valid_index': result_data.first_valid_index(),
|
|
79
|
+
'last_valid_index': result_data.last_valid_index()
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
raise IndicatorCalculationError(
|
|
85
|
+
f"Failed to calculate SMA: {e}",
|
|
86
|
+
{'indicator': self.name, 'period': period}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ExponentialMovingAverage(PreloadedIndicator):
|
|
91
|
+
"""
|
|
92
|
+
Exponential Moving Average (EMA) indicator.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, period: int = 20):
|
|
96
|
+
"""
|
|
97
|
+
Initialize EMA indicator.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
period: Period for exponential moving average calculation
|
|
101
|
+
"""
|
|
102
|
+
self.period = period
|
|
103
|
+
super().__init__('ema', {'period': period})
|
|
104
|
+
|
|
105
|
+
def get_output_columns(self) -> List[str]:
|
|
106
|
+
"""Returns output columns."""
|
|
107
|
+
return [f'ema_{self.period}']
|
|
108
|
+
|
|
109
|
+
def get_description(self) -> str:
|
|
110
|
+
"""Returns indicator description."""
|
|
111
|
+
return f"Exponential Moving Average with {self.period} period"
|
|
112
|
+
|
|
113
|
+
def get_min_records(self) -> int:
|
|
114
|
+
"""Returns minimum records required."""
|
|
115
|
+
return self.period
|
|
116
|
+
|
|
117
|
+
def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
|
|
118
|
+
"""
|
|
119
|
+
Calculate EMA.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
data: DataFrame with price data
|
|
123
|
+
**kwargs: Additional parameters
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
IndicatorResult with EMA values
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
self.validate_data(data)
|
|
130
|
+
|
|
131
|
+
period = kwargs.get('period', self.period)
|
|
132
|
+
|
|
133
|
+
self.logger.info(f"Calculating EMA with period {period}")
|
|
134
|
+
|
|
135
|
+
# Вычисляем EMA
|
|
136
|
+
ema_values = data['close'].ewm(span=period).mean()
|
|
137
|
+
|
|
138
|
+
result_data = pd.DataFrame({
|
|
139
|
+
f'ema_{period}': ema_values
|
|
140
|
+
}, index=data.index)
|
|
141
|
+
|
|
142
|
+
return IndicatorResult(
|
|
143
|
+
name=self.name,
|
|
144
|
+
data=result_data,
|
|
145
|
+
config=self.config,
|
|
146
|
+
metadata={
|
|
147
|
+
'period': period,
|
|
148
|
+
'calculation_method': 'exponential_weighted_mean',
|
|
149
|
+
'alpha': 2 / (period + 1),
|
|
150
|
+
'first_valid_index': result_data.first_valid_index(),
|
|
151
|
+
'last_valid_index': result_data.last_valid_index()
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
raise IndicatorCalculationError(
|
|
157
|
+
f"Failed to calculate EMA: {e}",
|
|
158
|
+
{'indicator': self.name, 'period': period}
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class RelativeStrengthIndex(PreloadedIndicator):
|
|
163
|
+
"""
|
|
164
|
+
Relative Strength Index (RSI) indicator.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(self, period: int = 14):
|
|
168
|
+
"""
|
|
169
|
+
Initialize RSI indicator.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
period: Period for RSI calculation
|
|
173
|
+
"""
|
|
174
|
+
self.period = period
|
|
175
|
+
super().__init__('rsi', {'period': period})
|
|
176
|
+
|
|
177
|
+
def get_output_columns(self) -> List[str]:
|
|
178
|
+
"""Returns output columns."""
|
|
179
|
+
return [f'rsi_{self.period}']
|
|
180
|
+
|
|
181
|
+
def get_description(self) -> str:
|
|
182
|
+
"""Returns indicator description."""
|
|
183
|
+
return f"Relative Strength Index with {self.period} period"
|
|
184
|
+
|
|
185
|
+
def get_min_records(self) -> int:
|
|
186
|
+
"""Returns minimum records required."""
|
|
187
|
+
return self.period + 1
|
|
188
|
+
|
|
189
|
+
def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
|
|
190
|
+
"""
|
|
191
|
+
Calculate RSI.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
data: DataFrame with price data
|
|
195
|
+
**kwargs: Additional parameters
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
IndicatorResult with RSI values
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
self.validate_data(data)
|
|
202
|
+
|
|
203
|
+
period = kwargs.get('period', self.period)
|
|
204
|
+
|
|
205
|
+
self.logger.info(f"Calculating RSI with period {period}")
|
|
206
|
+
|
|
207
|
+
# Вычисляем изменения цен
|
|
208
|
+
price_changes = data['close'].diff()
|
|
209
|
+
|
|
210
|
+
# Разделяем на положительные и отрицательные изменения
|
|
211
|
+
gains = price_changes.where(price_changes > 0, 0)
|
|
212
|
+
losses = -price_changes.where(price_changes < 0, 0)
|
|
213
|
+
|
|
214
|
+
# Вычисляем средние значения методом экспоненциального сглаживания
|
|
215
|
+
avg_gains = gains.ewm(alpha=1/period).mean()
|
|
216
|
+
avg_losses = losses.ewm(alpha=1/period).mean()
|
|
217
|
+
|
|
218
|
+
# Вычисляем RS и RSI
|
|
219
|
+
rs = avg_gains / avg_losses
|
|
220
|
+
rsi_values = 100 - (100 / (1 + rs))
|
|
221
|
+
|
|
222
|
+
result_data = pd.DataFrame({
|
|
223
|
+
f'rsi_{period}': rsi_values
|
|
224
|
+
}, index=data.index)
|
|
225
|
+
|
|
226
|
+
return IndicatorResult(
|
|
227
|
+
name=self.name,
|
|
228
|
+
data=result_data,
|
|
229
|
+
config=self.config,
|
|
230
|
+
metadata={
|
|
231
|
+
'period': period,
|
|
232
|
+
'calculation_method': 'ewm_smoothing',
|
|
233
|
+
'overbought_level': 70,
|
|
234
|
+
'oversold_level': 30,
|
|
235
|
+
'first_valid_index': result_data.first_valid_index(),
|
|
236
|
+
'last_valid_index': result_data.last_valid_index()
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
raise IndicatorCalculationError(
|
|
242
|
+
f"Failed to calculate RSI: {e}",
|
|
243
|
+
{'indicator': self.name, 'period': period}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class MACD(PreloadedIndicator):
|
|
248
|
+
"""
|
|
249
|
+
Moving Average Convergence Divergence (MACD) indicator.
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
def __init__(self, fast_period: int = 12, slow_period: int = 26, signal_period: int = 9):
|
|
253
|
+
"""
|
|
254
|
+
Initialize MACD indicator.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
fast_period: Fast EMA period
|
|
258
|
+
slow_period: Slow EMA period
|
|
259
|
+
signal_period: Signal line EMA period
|
|
260
|
+
"""
|
|
261
|
+
self.fast_period = fast_period
|
|
262
|
+
self.slow_period = slow_period
|
|
263
|
+
self.signal_period = signal_period
|
|
264
|
+
|
|
265
|
+
super().__init__('macd', {
|
|
266
|
+
'fast_period': fast_period,
|
|
267
|
+
'slow_period': slow_period,
|
|
268
|
+
'signal_period': signal_period
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
def get_output_columns(self) -> List[str]:
|
|
272
|
+
"""Returns output columns."""
|
|
273
|
+
return ['macd', 'macd_signal', 'macd_hist']
|
|
274
|
+
|
|
275
|
+
def get_description(self) -> str:
|
|
276
|
+
"""Returns indicator description."""
|
|
277
|
+
return f"MACD ({self.fast_period}, {self.slow_period}, {self.signal_period})"
|
|
278
|
+
|
|
279
|
+
def get_min_records(self) -> int:
|
|
280
|
+
"""Returns minimum records required."""
|
|
281
|
+
return self.slow_period + self.signal_period
|
|
282
|
+
|
|
283
|
+
def get_required_columns(self) -> List[str]:
|
|
284
|
+
"""Returns required input columns."""
|
|
285
|
+
return ['close']
|
|
286
|
+
|
|
287
|
+
def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
|
|
288
|
+
"""
|
|
289
|
+
Calculate MACD.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
data: DataFrame with price data
|
|
293
|
+
**kwargs: Additional parameters
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
IndicatorResult with MACD values
|
|
297
|
+
"""
|
|
298
|
+
try:
|
|
299
|
+
self.validate_data(data)
|
|
300
|
+
|
|
301
|
+
fast_period = kwargs.get('fast_period', self.fast_period)
|
|
302
|
+
slow_period = kwargs.get('slow_period', self.slow_period)
|
|
303
|
+
signal_period = kwargs.get('signal_period', self.signal_period)
|
|
304
|
+
|
|
305
|
+
self.logger.info(f"Calculating MACD ({fast_period}, {slow_period}, {signal_period})")
|
|
306
|
+
|
|
307
|
+
# Вычисляем быструю и медленную EMA
|
|
308
|
+
fast_ema = data['close'].ewm(span=fast_period).mean()
|
|
309
|
+
slow_ema = data['close'].ewm(span=slow_period).mean()
|
|
310
|
+
|
|
311
|
+
# Вычисляем MACD линию
|
|
312
|
+
macd_line = fast_ema - slow_ema
|
|
313
|
+
|
|
314
|
+
# Вычисляем сигнальную линию
|
|
315
|
+
signal_line = macd_line.ewm(span=signal_period).mean()
|
|
316
|
+
|
|
317
|
+
# Вычисляем гистограмму
|
|
318
|
+
histogram = macd_line - signal_line
|
|
319
|
+
|
|
320
|
+
result_data = pd.DataFrame({
|
|
321
|
+
'macd': macd_line,
|
|
322
|
+
'macd_signal': signal_line,
|
|
323
|
+
'macd_hist': histogram
|
|
324
|
+
}, index=data.index)
|
|
325
|
+
|
|
326
|
+
return IndicatorResult(
|
|
327
|
+
name=self.name,
|
|
328
|
+
data=result_data,
|
|
329
|
+
config=self.config,
|
|
330
|
+
metadata={
|
|
331
|
+
'fast_period': fast_period,
|
|
332
|
+
'slow_period': slow_period,
|
|
333
|
+
'signal_period': signal_period,
|
|
334
|
+
'calculation_method': 'ema_difference',
|
|
335
|
+
'first_valid_index': result_data.first_valid_index(),
|
|
336
|
+
'last_valid_index': result_data.last_valid_index()
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
except Exception as e:
|
|
341
|
+
fast_period = kwargs.get('fast_period', self.fast_period)
|
|
342
|
+
slow_period = kwargs.get('slow_period', self.slow_period)
|
|
343
|
+
signal_period = kwargs.get('signal_period', self.signal_period)
|
|
344
|
+
|
|
345
|
+
raise IndicatorCalculationError(
|
|
346
|
+
f"Failed to calculate MACD: {e}",
|
|
347
|
+
{
|
|
348
|
+
'indicator': self.name,
|
|
349
|
+
'fast_period': fast_period,
|
|
350
|
+
'slow_period': slow_period,
|
|
351
|
+
'signal_period': signal_period
|
|
352
|
+
}
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class BollingerBands(PreloadedIndicator):
|
|
357
|
+
"""
|
|
358
|
+
Bollinger Bands indicator.
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
def __init__(self, period: int = 20, std_dev: float = 2.0):
|
|
362
|
+
"""
|
|
363
|
+
Initialize Bollinger Bands indicator.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
period: Period for moving average and standard deviation
|
|
367
|
+
std_dev: Standard deviation multiplier
|
|
368
|
+
"""
|
|
369
|
+
self.period = period
|
|
370
|
+
self.std_dev = std_dev
|
|
371
|
+
super().__init__('bbands', {'period': period, 'std_dev': std_dev})
|
|
372
|
+
|
|
373
|
+
def get_output_columns(self) -> List[str]:
|
|
374
|
+
"""Returns output columns."""
|
|
375
|
+
return ['bb_upper', 'bb_middle', 'bb_lower', 'bb_width', 'bb_percent']
|
|
376
|
+
|
|
377
|
+
def get_description(self) -> str:
|
|
378
|
+
"""Returns indicator description."""
|
|
379
|
+
return f"Bollinger Bands ({self.period}, {self.std_dev})"
|
|
380
|
+
|
|
381
|
+
def get_min_records(self) -> int:
|
|
382
|
+
"""Returns minimum records required."""
|
|
383
|
+
return self.period
|
|
384
|
+
|
|
385
|
+
def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
|
|
386
|
+
"""
|
|
387
|
+
Calculate Bollinger Bands.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
data: DataFrame with price data
|
|
391
|
+
**kwargs: Additional parameters
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
IndicatorResult with Bollinger Bands values
|
|
395
|
+
"""
|
|
396
|
+
try:
|
|
397
|
+
self.validate_data(data)
|
|
398
|
+
|
|
399
|
+
period = kwargs.get('period', self.period)
|
|
400
|
+
std_dev = kwargs.get('std_dev', self.std_dev)
|
|
401
|
+
|
|
402
|
+
self.logger.info(f"Calculating Bollinger Bands ({period}, {std_dev})")
|
|
403
|
+
|
|
404
|
+
# Вычисляем среднюю линию (SMA)
|
|
405
|
+
middle_band = data['close'].rolling(window=period).mean()
|
|
406
|
+
|
|
407
|
+
# Вычисляем стандартное отклонение
|
|
408
|
+
std = data['close'].rolling(window=period).std()
|
|
409
|
+
|
|
410
|
+
# Вычисляем верхнюю и нижнюю полосы
|
|
411
|
+
upper_band = middle_band + (std * std_dev)
|
|
412
|
+
lower_band = middle_band - (std * std_dev)
|
|
413
|
+
|
|
414
|
+
# Дополнительные метрики
|
|
415
|
+
bb_width = (upper_band - lower_band) / middle_band * 100
|
|
416
|
+
bb_percent = (data['close'] - lower_band) / (upper_band - lower_band) * 100
|
|
417
|
+
|
|
418
|
+
result_data = pd.DataFrame({
|
|
419
|
+
'bb_upper': upper_band,
|
|
420
|
+
'bb_middle': middle_band,
|
|
421
|
+
'bb_lower': lower_band,
|
|
422
|
+
'bb_width': bb_width,
|
|
423
|
+
'bb_percent': bb_percent
|
|
424
|
+
}, index=data.index)
|
|
425
|
+
|
|
426
|
+
return IndicatorResult(
|
|
427
|
+
name=self.name,
|
|
428
|
+
data=result_data,
|
|
429
|
+
config=self.config,
|
|
430
|
+
metadata={
|
|
431
|
+
'period': period,
|
|
432
|
+
'std_dev': std_dev,
|
|
433
|
+
'calculation_method': 'sma_plus_std',
|
|
434
|
+
'first_valid_index': result_data.first_valid_index(),
|
|
435
|
+
'last_valid_index': result_data.last_valid_index()
|
|
436
|
+
}
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
except Exception as e:
|
|
440
|
+
raise IndicatorCalculationError(
|
|
441
|
+
f"Failed to calculate Bollinger Bands: {e}",
|
|
442
|
+
{'indicator': self.name, 'period': period, 'std_dev': std_dev}
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
# Реестр встроенных индикаторов
|
|
447
|
+
BUILTIN_INDICATORS = {
|
|
448
|
+
'sma': SimpleMovingAverage,
|
|
449
|
+
'ema': ExponentialMovingAverage,
|
|
450
|
+
'rsi': RelativeStrengthIndex,
|
|
451
|
+
'macd': MACD,
|
|
452
|
+
'bbands': BollingerBands,
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def register_builtin_indicators():
|
|
457
|
+
"""
|
|
458
|
+
Регистрация всех встроенных индикаторов в фабрике.
|
|
459
|
+
"""
|
|
460
|
+
from .base import IndicatorFactory
|
|
461
|
+
|
|
462
|
+
registered_count = 0
|
|
463
|
+
|
|
464
|
+
for name, indicator_class in BUILTIN_INDICATORS.items():
|
|
465
|
+
try:
|
|
466
|
+
IndicatorFactory.register_indicator(name, indicator_class)
|
|
467
|
+
registered_count += 1
|
|
468
|
+
except Exception as e:
|
|
469
|
+
logger.error(f"Failed to register {name}: {e}")
|
|
470
|
+
|
|
471
|
+
logger.info(f"Registered {registered_count} builtin indicators")
|
|
472
|
+
return registered_count
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def get_builtin_indicators() -> List[str]:
|
|
476
|
+
"""
|
|
477
|
+
Получить список встроенных индикаторов.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
Список названий встроенных индикаторов
|
|
481
|
+
"""
|
|
482
|
+
return list(BUILTIN_INDICATORS.keys())
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def create_indicator(name: str, **kwargs):
|
|
486
|
+
"""
|
|
487
|
+
Создать встроенный индикатор по имени.
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
name: Название индикатора
|
|
491
|
+
**kwargs: Параметры индикатора
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Экземпляр индикатора
|
|
495
|
+
"""
|
|
496
|
+
if name.lower() not in BUILTIN_INDICATORS:
|
|
497
|
+
raise ValueError(f"Unknown builtin indicator: {name}")
|
|
498
|
+
|
|
499
|
+
indicator_class = BUILTIN_INDICATORS[name.lower()]
|
|
500
|
+
return indicator_class(**kwargs)
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
# Экспорт
|
|
504
|
+
__all__ = [
|
|
505
|
+
'SimpleMovingAverage',
|
|
506
|
+
'ExponentialMovingAverage',
|
|
507
|
+
'RelativeStrengthIndex',
|
|
508
|
+
'MACD',
|
|
509
|
+
'BollingerBands',
|
|
510
|
+
'BUILTIN_INDICATORS',
|
|
511
|
+
'register_builtin_indicators',
|
|
512
|
+
'get_builtin_indicators',
|
|
513
|
+
'create_indicator'
|
|
514
|
+
]
|