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.
Files changed (49) hide show
  1. bquant/__init__.py +31 -0
  2. bquant/analysis/__init__.py +297 -0
  3. bquant/analysis/candlestick/__init__.py +100 -0
  4. bquant/analysis/chart/__init__.py +98 -0
  5. bquant/analysis/statistical/__init__.py +414 -0
  6. bquant/analysis/statistical/hypothesis_testing.py +657 -0
  7. bquant/analysis/technical/__init__.py +98 -0
  8. bquant/analysis/timeseries/__init__.py +102 -0
  9. bquant/analysis/zones/__init__.py +547 -0
  10. bquant/analysis/zones/sequence_analysis.py +661 -0
  11. bquant/analysis/zones/zone_features.py +520 -0
  12. bquant/cli.py +160 -0
  13. bquant/core/__init__.py +112 -0
  14. bquant/core/cache.py +534 -0
  15. bquant/core/config.py +328 -0
  16. bquant/core/exceptions.py +351 -0
  17. bquant/core/logging_config.py +368 -0
  18. bquant/core/numpy_fix.py +99 -0
  19. bquant/core/performance.py +554 -0
  20. bquant/core/utils.py +327 -0
  21. bquant/data/__init__.py +85 -0
  22. bquant/data/loader.py +436 -0
  23. bquant/data/processor.py +644 -0
  24. bquant/data/samples/__init__.py +442 -0
  25. bquant/data/samples/datasets.py +246 -0
  26. bquant/data/samples/embedded/__init__.py +16 -0
  27. bquant/data/samples/embedded/mt_xauusd_m15.py +9018 -0
  28. bquant/data/samples/embedded/tv_xauusd_1h.py +17018 -0
  29. bquant/data/samples/utils.py +393 -0
  30. bquant/data/schemas.py +285 -0
  31. bquant/data/validator.py +498 -0
  32. bquant/indicators/__init__.py +115 -0
  33. bquant/indicators/base.py +487 -0
  34. bquant/indicators/calculators.py +404 -0
  35. bquant/indicators/library.py +514 -0
  36. bquant/indicators/loaders.py +413 -0
  37. bquant/indicators/macd.py +754 -0
  38. bquant/ml/__init__.py +19 -0
  39. bquant/visualization/__init__.py +316 -0
  40. bquant/visualization/charts.py +616 -0
  41. bquant/visualization/statistical.py +812 -0
  42. bquant/visualization/themes.py +599 -0
  43. bquant/visualization/zones.py +775 -0
  44. bquant-0.0.0.dist-info/METADATA +167 -0
  45. bquant-0.0.0.dist-info/RECORD +49 -0
  46. bquant-0.0.0.dist-info/WHEEL +5 -0
  47. bquant-0.0.0.dist-info/entry_points.txt +2 -0
  48. bquant-0.0.0.dist-info/licenses/LICENSE +21 -0
  49. bquant-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,487 @@
1
+ """
2
+ Base classes and architecture for BQuant indicators
3
+
4
+ This module provides the foundation for all technical indicators in BQuant.
5
+ """
6
+
7
+ import pandas as pd
8
+ import numpy as np
9
+ from abc import ABC, abstractmethod
10
+ from enum import Enum
11
+ from typing import Dict, Any, Optional, List, Union, Type, Callable
12
+ from dataclasses import dataclass
13
+ from datetime import datetime
14
+
15
+ from ..core.exceptions import IndicatorCalculationError
16
+ from ..core.logging_config import get_logger
17
+ from ..core.cache import cached, get_cache_manager
18
+
19
+ # Получаем логгер для модуля
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ class IndicatorSource(Enum):
24
+ """
25
+ Источники индикаторов для BQuant.
26
+
27
+ PRELOADED - предзагруженные индикаторы (встроенные в BQuant)
28
+ LIBRARY - индикаторы из внешних библиотек (pandas-ta, talib)
29
+ CUSTOM - пользовательские индикаторы
30
+ """
31
+ PRELOADED = "preloaded"
32
+ LIBRARY = "library"
33
+ CUSTOM = "custom"
34
+
35
+
36
+ @dataclass
37
+ class IndicatorConfig:
38
+ """
39
+ Конфигурация для индикаторов.
40
+
41
+ Attributes:
42
+ name: Название индикатора
43
+ parameters: Параметры индикатора
44
+ source: Источник индикатора
45
+ columns: Ожидаемые колонки результата
46
+ description: Описание индикатора
47
+ """
48
+ name: str
49
+ parameters: Dict[str, Any]
50
+ source: IndicatorSource
51
+ columns: List[str]
52
+ description: str = ""
53
+
54
+
55
+ @dataclass
56
+ class IndicatorResult:
57
+ """
58
+ Результат вычисления индикатора.
59
+
60
+ Attributes:
61
+ name: Название индикатора
62
+ data: DataFrame с результатами
63
+ config: Конфигурация индикатора
64
+ metadata: Дополнительная информация
65
+ """
66
+ name: str
67
+ data: pd.DataFrame
68
+ config: IndicatorConfig
69
+ metadata: Dict[str, Any]
70
+
71
+
72
+ class BaseIndicator(ABC):
73
+ """
74
+ Базовый класс для всех индикаторов BQuant.
75
+
76
+ Этот класс определяет общий интерфейс для всех технических индикаторов.
77
+ """
78
+
79
+ def __init__(self, name: str, config: Optional[IndicatorConfig] = None):
80
+ """
81
+ Инициализация базового индикатора.
82
+
83
+ Args:
84
+ name: Название индикатора
85
+ config: Конфигурация индикатора
86
+ """
87
+ self.name = name
88
+ self.config = config or IndicatorConfig(
89
+ name=name,
90
+ parameters={},
91
+ source=IndicatorSource.CUSTOM,
92
+ columns=[],
93
+ description=""
94
+ )
95
+ self.logger = get_logger(f"{__name__}.{name}")
96
+
97
+ # Кэширование результатов
98
+ self._cache = {}
99
+ self._cache_enabled = True
100
+
101
+ @abstractmethod
102
+ def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
103
+ """
104
+ Абстрактный метод для вычисления индикатора.
105
+
106
+ Args:
107
+ data: DataFrame с ценовыми данными
108
+ **kwargs: Дополнительные параметры
109
+
110
+ Returns:
111
+ IndicatorResult с результатами вычисления
112
+ """
113
+ pass
114
+
115
+ def validate_data(self, data: pd.DataFrame) -> bool:
116
+ """
117
+ Валидация входных данных.
118
+
119
+ Args:
120
+ data: DataFrame для валидации
121
+
122
+ Returns:
123
+ True если данные корректны
124
+
125
+ Raises:
126
+ IndicatorCalculationError: Если данные некорректны
127
+ """
128
+ if data.empty:
129
+ raise IndicatorCalculationError(
130
+ f"Empty DataFrame provided for indicator {self.name}",
131
+ {'indicator': self.name}
132
+ )
133
+
134
+ # Проверка минимального количества записей
135
+ min_records = self.get_min_records()
136
+ if len(data) < min_records:
137
+ raise IndicatorCalculationError(
138
+ f"Insufficient data for {self.name}: {len(data)} < {min_records}",
139
+ {'indicator': self.name, 'records': len(data), 'required': min_records}
140
+ )
141
+
142
+ # Проверка обязательных колонок
143
+ required_columns = self.get_required_columns()
144
+ missing_columns = [col for col in required_columns if col not in data.columns]
145
+ if missing_columns:
146
+ raise IndicatorCalculationError(
147
+ f"Missing required columns for {self.name}: {missing_columns}",
148
+ {'indicator': self.name, 'missing_columns': missing_columns}
149
+ )
150
+
151
+ return True
152
+
153
+ def get_min_records(self) -> int:
154
+ """
155
+ Минимальное количество записей для вычисления индикатора.
156
+
157
+ Returns:
158
+ Минимальное количество записей
159
+ """
160
+ return 1
161
+
162
+ def get_required_columns(self) -> List[str]:
163
+ """
164
+ Обязательные колонки для вычисления индикатора.
165
+
166
+ Returns:
167
+ Список обязательных колонок
168
+ """
169
+ return ['close']
170
+
171
+ def enable_cache(self, enabled: bool = True):
172
+ """Включить/выключить кэширование результатов."""
173
+ self._cache_enabled = enabled
174
+ if not enabled:
175
+ self._cache.clear()
176
+
177
+ def clear_cache(self):
178
+ """Очистить кэш результатов."""
179
+ self._cache.clear()
180
+
181
+ def _get_cache_key(self, data: pd.DataFrame, **kwargs) -> str:
182
+ """Генерация ключа для кэширования."""
183
+ data_hash = pd.util.hash_pandas_object(data).sum()
184
+ params_str = str(sorted(kwargs.items()))
185
+ return f"{self.name}_{data_hash}_{hash(params_str)}"
186
+
187
+ def calculate_with_cache(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
188
+ """
189
+ Вычисление индикатора с кэшированием.
190
+
191
+ Args:
192
+ data: DataFrame с данными
193
+ **kwargs: Дополнительные параметры
194
+
195
+ Returns:
196
+ IndicatorResult с результатами
197
+ """
198
+ # Используем новую систему кэширования
199
+ cache_manager = get_cache_manager()
200
+
201
+ # Генерируем ключ кэша
202
+ func_name = f"{self.__class__.__name__}.calculate"
203
+ cache_key = cache_manager.memory_cache._generate_key(func_name, (data,), kwargs)
204
+
205
+ # Проверяем кэш
206
+ cached_result = cache_manager.get(cache_key)
207
+ if cached_result is not None:
208
+ self.logger.debug(f"Cache hit for {self.name}")
209
+ return cached_result
210
+
211
+ # Вычисляем результат
212
+ self.logger.debug(f"Cache miss for {self.name}, calculating...")
213
+ result = self.calculate(data, **kwargs)
214
+
215
+ # Сохраняем в кэш (время жизни 1 час)
216
+ cache_manager.put(cache_key, result, ttl=3600, disk=True)
217
+
218
+ return result
219
+
220
+
221
+ class PreloadedIndicator(BaseIndicator):
222
+ """
223
+ Базовый класс для предзагруженных (встроенных) индикаторов BQuant.
224
+ """
225
+
226
+ def __init__(self, name: str, parameters: Dict[str, Any] = None):
227
+ """
228
+ Инициализация предзагруженного индикатора.
229
+
230
+ Args:
231
+ name: Название индикатора
232
+ parameters: Параметры индикатора
233
+ """
234
+ config = IndicatorConfig(
235
+ name=name,
236
+ parameters=parameters or {},
237
+ source=IndicatorSource.PRELOADED,
238
+ columns=self.get_output_columns(),
239
+ description=self.get_description()
240
+ )
241
+ super().__init__(name, config)
242
+
243
+ @abstractmethod
244
+ def get_output_columns(self) -> List[str]:
245
+ """Возвращает список выходных колонок индикатора."""
246
+ pass
247
+
248
+ @abstractmethod
249
+ def get_description(self) -> str:
250
+ """Возвращает описание индикатора."""
251
+ pass
252
+
253
+
254
+ class LibraryIndicator(BaseIndicator):
255
+ """
256
+ Базовый класс для индикаторов из внешних библиотек.
257
+ """
258
+
259
+ def __init__(self, name: str, library_func: Callable, parameters: Dict[str, Any] = None):
260
+ """
261
+ Инициализация индикатора из библиотеки.
262
+
263
+ Args:
264
+ name: Название индикатора
265
+ library_func: Функция из библиотеки
266
+ parameters: Параметры для функции
267
+ """
268
+ self.library_func = library_func
269
+
270
+ config = IndicatorConfig(
271
+ name=name,
272
+ parameters=parameters or {},
273
+ source=IndicatorSource.LIBRARY,
274
+ columns=[], # Будет определено динамически
275
+ description=f"Library indicator: {name}"
276
+ )
277
+ super().__init__(name, config)
278
+
279
+ def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
280
+ """
281
+ Вычисление индикатора через библиотечную функцию.
282
+
283
+ Args:
284
+ data: DataFrame с данными
285
+ **kwargs: Дополнительные параметры
286
+
287
+ Returns:
288
+ IndicatorResult с результатами
289
+ """
290
+ try:
291
+ self.validate_data(data)
292
+
293
+ # Объединяем параметры из конфига и kwargs
294
+ params = {**self.config.parameters, **kwargs}
295
+
296
+ self.logger.info(f"Calculating {self.name} using library function")
297
+
298
+ # Вызываем библиотечную функцию
299
+ result_data = self.library_func(data, **params)
300
+
301
+ # Убеждаемся, что результат - DataFrame
302
+ if not isinstance(result_data, pd.DataFrame):
303
+ if isinstance(result_data, pd.Series):
304
+ result_data = result_data.to_frame(name=self.name)
305
+ else:
306
+ raise IndicatorCalculationError(
307
+ f"Library function returned unexpected type: {type(result_data)}",
308
+ {'indicator': self.name}
309
+ )
310
+
311
+ # Обновляем конфигурацию с фактическими колонками
312
+ self.config.columns = list(result_data.columns)
313
+
314
+ return IndicatorResult(
315
+ name=self.name,
316
+ data=result_data,
317
+ config=self.config,
318
+ metadata={
319
+ 'library_function': str(self.library_func),
320
+ 'calculation_time': datetime.now(),
321
+ 'data_shape': result_data.shape
322
+ }
323
+ )
324
+
325
+ except Exception as e:
326
+ raise IndicatorCalculationError(
327
+ f"Failed to calculate {self.name}: {e}",
328
+ {'indicator': self.name, 'error': str(e)}
329
+ )
330
+
331
+
332
+ class IndicatorFactory:
333
+ """
334
+ Фабрика для создания индикаторов.
335
+
336
+ Предоставляет единый интерфейс для создания различных типов индикаторов.
337
+ """
338
+
339
+ _registry = {}
340
+ _library_functions = {}
341
+
342
+ @classmethod
343
+ def register_indicator(cls, name: str, indicator_class: Type[BaseIndicator]):
344
+ """
345
+ Регистрация индикатора в фабрике.
346
+
347
+ Args:
348
+ name: Название индикатора
349
+ indicator_class: Класс индикатора
350
+ """
351
+ cls._registry[name.lower()] = indicator_class
352
+ logger.info(f"Registered indicator: {name}")
353
+
354
+ @classmethod
355
+ def register_library_function(cls, name: str, func: Callable):
356
+ """
357
+ Регистрация функции из библиотеки.
358
+
359
+ Args:
360
+ name: Название индикатора
361
+ func: Функция из библиотеки
362
+ """
363
+ cls._library_functions[name.lower()] = func
364
+ logger.info(f"Registered library function: {name}")
365
+
366
+ @classmethod
367
+ def create_indicator(cls, name: str, data: pd.DataFrame = None, **kwargs) -> BaseIndicator:
368
+ """
369
+ Создание индикатора по имени.
370
+
371
+ Args:
372
+ name: Название индикатора
373
+ data: DataFrame с данными (для проверки)
374
+ **kwargs: Параметры индикатора
375
+
376
+ Returns:
377
+ Экземпляр индикатора
378
+ """
379
+ name_lower = name.lower()
380
+
381
+ # Сначала ищем в зарегистрированных индикаторах
382
+ if name_lower in cls._registry:
383
+ return cls._registry[name_lower](**kwargs)
384
+
385
+ # Затем ищем в библиотечных функциях
386
+ if name_lower in cls._library_functions:
387
+ return LibraryIndicator(
388
+ name=name,
389
+ library_func=cls._library_functions[name_lower],
390
+ parameters=kwargs
391
+ )
392
+
393
+ # Если не найден, возвращаем заглушку
394
+ logger.warning(f"Indicator {name} not found, creating stub")
395
+ return _StubIndicator(name, **kwargs)
396
+
397
+ @classmethod
398
+ def list_indicators(cls) -> Dict[str, str]:
399
+ """
400
+ Получить список доступных индикаторов.
401
+
402
+ Returns:
403
+ Словарь {название: источник}
404
+ """
405
+ indicators = {}
406
+
407
+ for name in cls._registry:
408
+ indicators[name] = "preloaded"
409
+
410
+ for name in cls._library_functions:
411
+ indicators[name] = "library"
412
+
413
+ return indicators
414
+
415
+ @classmethod
416
+ def get_indicator_info(cls, name: str) -> Optional[Dict[str, Any]]:
417
+ """
418
+ Получить информацию об индикаторе.
419
+
420
+ Args:
421
+ name: Название индикатора
422
+
423
+ Returns:
424
+ Информация об индикаторе или None
425
+ """
426
+ name_lower = name.lower()
427
+
428
+ if name_lower in cls._registry:
429
+ indicator_class = cls._registry[name_lower]
430
+ return {
431
+ 'name': name,
432
+ 'source': 'preloaded',
433
+ 'class': indicator_class.__name__,
434
+ 'description': getattr(indicator_class, 'get_description', lambda: 'No description')()
435
+ }
436
+
437
+ if name_lower in cls._library_functions:
438
+ func = cls._library_functions[name_lower]
439
+ return {
440
+ 'name': name,
441
+ 'source': 'library',
442
+ 'function': str(func),
443
+ 'description': getattr(func, '__doc__', 'No description')
444
+ }
445
+
446
+ return None
447
+
448
+
449
+ class _StubIndicator(BaseIndicator):
450
+ """
451
+ Заглушка для неизвестных индикаторов.
452
+ """
453
+
454
+ def __init__(self, name: str, **kwargs):
455
+ super().__init__(name)
456
+ self.parameters = kwargs
457
+
458
+ def calculate(self, data: pd.DataFrame, **kwargs) -> IndicatorResult:
459
+ """
460
+ Возвращает заглушку результата.
461
+ """
462
+ self.logger.warning(f"Using stub for indicator {self.name}")
463
+
464
+ # Создаем DataFrame с NaN значениями
465
+ result_data = pd.DataFrame(
466
+ index=data.index,
467
+ data={f"{self.name}_value": np.nan}
468
+ )
469
+
470
+ return IndicatorResult(
471
+ name=self.name,
472
+ data=result_data,
473
+ config=self.config,
474
+ metadata={'stub': True, 'parameters': self.parameters}
475
+ )
476
+
477
+
478
+ # Экспорт основных классов
479
+ __all__ = [
480
+ 'IndicatorSource',
481
+ 'IndicatorConfig',
482
+ 'IndicatorResult',
483
+ 'BaseIndicator',
484
+ 'PreloadedIndicator',
485
+ 'LibraryIndicator',
486
+ 'IndicatorFactory'
487
+ ]