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
bquant/core/config.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration settings for the BQuant Project.
|
|
3
|
+
|
|
4
|
+
Universal configuration supporting multiple instruments, indicators, and analysis methods.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
|
|
11
|
+
# ============================================================================
|
|
12
|
+
# PROJECT STRUCTURE
|
|
13
|
+
# ============================================================================
|
|
14
|
+
|
|
15
|
+
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
|
16
|
+
DATA_DIR = PROJECT_ROOT / "data"
|
|
17
|
+
SCRIPTS_DIR = PROJECT_ROOT / "scripts"
|
|
18
|
+
NOTEBOOKS_DIR = PROJECT_ROOT / "research" / "notebooks"
|
|
19
|
+
ALLDATA_DIR = DATA_DIR / "alldata"
|
|
20
|
+
PROCESSED_DATA_DIR = DATA_DIR / "processed"
|
|
21
|
+
RESULTS_DIR = PROJECT_ROOT / "results"
|
|
22
|
+
|
|
23
|
+
# Создаем директории если их нет
|
|
24
|
+
for directory in [PROCESSED_DATA_DIR, RESULTS_DIR, PROJECT_ROOT / "logs"]:
|
|
25
|
+
directory.mkdir(exist_ok=True, parents=True)
|
|
26
|
+
|
|
27
|
+
# ============================================================================
|
|
28
|
+
# UNIVERSAL DATA CONFIGURATION
|
|
29
|
+
# ============================================================================
|
|
30
|
+
|
|
31
|
+
# ============================================================================
|
|
32
|
+
# TIMEFRAME MAPPING CONFIGURATION
|
|
33
|
+
# ============================================================================
|
|
34
|
+
|
|
35
|
+
# Маппинг таймфреймов для разных поставщиков данных
|
|
36
|
+
TIMEFRAME_MAPPING = {
|
|
37
|
+
'tradingview': {
|
|
38
|
+
# Внутричасовые таймфреймы (минуты)
|
|
39
|
+
'1m': '1', '2m': '2', '3m': '3', '4m': '4', '5m': '5', '6m': '6', '10m': '10',
|
|
40
|
+
'12m': '12', '15m': '15', '20m': '20', '30m': '30', '45m': '45',
|
|
41
|
+
# Часовые таймфреймы
|
|
42
|
+
'1h': '60', '2h': '120', '3h': '180', '4h': '240', '6h': '360', '8h': '480', '12h': '720',
|
|
43
|
+
# Дневные и выше
|
|
44
|
+
'1d': '1D', '1D': '1D', '1w': '1W', '1W': '1W', '1M': '1M', '3M': '3M', '6M': '6M', '12M': '12M'
|
|
45
|
+
},
|
|
46
|
+
'metatrader': {
|
|
47
|
+
# Внутричасовые таймфреймы (минуты)
|
|
48
|
+
'1m': 'M1', '2m': 'M2', '3m': 'M3', '4m': 'M4', '5m': 'M5', '6m': 'M6', '10m': 'M10',
|
|
49
|
+
'12m': 'M12', '15m': 'M15', '20m': 'M20', '30m': 'M30', '45m': 'M45',
|
|
50
|
+
# Часовые таймфреймы
|
|
51
|
+
'1h': 'H1', '2h': 'H2', '3h': 'H3', '4h': 'H4', '6h': 'H6', '8h': 'H8', '12h': 'H12',
|
|
52
|
+
# Дневные и выше
|
|
53
|
+
'1d': 'Daily', '1D': 'Daily', '1w': 'Weekly', '1W': 'Weekly', '1M': 'Monthly', '3M': 'Monthly', '6M': 'Monthly', '12M': 'Monthly'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Универсальные паттерны файлов для разных поставщиков
|
|
58
|
+
DATA_FILE_PATTERNS = {
|
|
59
|
+
'tradingview': {
|
|
60
|
+
'oanda': "OANDA_{symbol}, {timeframe}.csv",
|
|
61
|
+
'forexcom': "FOREXCOM_{symbol}, {timeframe}.csv",
|
|
62
|
+
'icmarkets': "ICMARKETS_{symbol}, {timeframe}.csv",
|
|
63
|
+
'default': "OANDA_{symbol}, {timeframe}.csv" # По умолчанию OANDA
|
|
64
|
+
},
|
|
65
|
+
'metatrader': {
|
|
66
|
+
'default': "{symbol}{timeframe}.csv"
|
|
67
|
+
},
|
|
68
|
+
'generic': {
|
|
69
|
+
'default': "{symbol}_{timeframe}.csv"
|
|
70
|
+
},
|
|
71
|
+
'custom': {
|
|
72
|
+
'default': "{symbol}_{timeframe}_{source}.csv"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# Поддерживаемые таймфреймы (универсальные)
|
|
77
|
+
SUPPORTED_TIMEFRAMES = {
|
|
78
|
+
# Внутричасовые таймфреймы (минуты)
|
|
79
|
+
'1m': '1 minute', '2m': '2 minutes', '3m': '3 minutes', '4m': '4 minutes',
|
|
80
|
+
'5m': '5 minutes', '6m': '6 minutes', '10m': '10 minutes', '12m': '12 minutes',
|
|
81
|
+
'15m': '15 minutes', '20m': '20 minutes', '30m': '30 minutes', '45m': '45 minutes',
|
|
82
|
+
# Часовые таймфреймы
|
|
83
|
+
'1h': '1 hour', '2h': '2 hours', '3h': '3 hours', '4h': '4 hours',
|
|
84
|
+
'6h': '6 hours', '8h': '8 hours', '12h': '12 hours',
|
|
85
|
+
# Дневные и выше
|
|
86
|
+
'1d': '1 day', '1D': '1 day', '1w': '1 week', '1W': '1 week',
|
|
87
|
+
'1M': '1 month', '3M': '3 months', '6M': '6 months', '12M': '12 months'
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Настройки валидации данных
|
|
91
|
+
DATA_VALIDATION = {
|
|
92
|
+
'required_columns': ['open', 'high', 'low', 'close'],
|
|
93
|
+
'optional_columns': ['volume'],
|
|
94
|
+
'min_records': 100,
|
|
95
|
+
'max_missing_ratio': 0.1,
|
|
96
|
+
'outlier_threshold': 3.0
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Настройки кэширования
|
|
100
|
+
CACHE_CONFIG = {
|
|
101
|
+
'enable_memory_cache': True,
|
|
102
|
+
'enable_disk_cache': True,
|
|
103
|
+
'memory_size': 100, # Количество записей в памяти
|
|
104
|
+
'default_ttl': 3600, # Время жизни по умолчанию (секунды)
|
|
105
|
+
'cache_dir': None, # None = используется ~/.cache/bquant
|
|
106
|
+
'auto_cleanup': True, # Автоматическая очистка истекших записей
|
|
107
|
+
'cleanup_interval': 300 # Интервал очистки в секундах
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# ============================================================================
|
|
111
|
+
# UNIVERSAL INDICATOR CONFIGURATION
|
|
112
|
+
# ============================================================================
|
|
113
|
+
|
|
114
|
+
# Универсальные параметры индикаторов
|
|
115
|
+
DEFAULT_INDICATORS = {
|
|
116
|
+
'macd': {
|
|
117
|
+
'fast': 12,
|
|
118
|
+
'slow': 26,
|
|
119
|
+
'signal': 9
|
|
120
|
+
},
|
|
121
|
+
'rsi': {
|
|
122
|
+
'length': 14
|
|
123
|
+
},
|
|
124
|
+
'bollinger_bands': {
|
|
125
|
+
'length': 20,
|
|
126
|
+
'std': 2
|
|
127
|
+
},
|
|
128
|
+
'atr': {
|
|
129
|
+
'length': 14
|
|
130
|
+
},
|
|
131
|
+
'sma': {
|
|
132
|
+
'length': 20
|
|
133
|
+
},
|
|
134
|
+
'ema': {
|
|
135
|
+
'length': 20
|
|
136
|
+
},
|
|
137
|
+
'stochastic': {
|
|
138
|
+
'k_length': 14,
|
|
139
|
+
'd_length': 3
|
|
140
|
+
},
|
|
141
|
+
'williams_r': {
|
|
142
|
+
'length': 14
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# ============================================================================
|
|
147
|
+
# UNIVERSAL ANALYSIS CONFIGURATION
|
|
148
|
+
# ============================================================================
|
|
149
|
+
|
|
150
|
+
# Универсальные параметры анализа
|
|
151
|
+
ANALYSIS_CONFIG = {
|
|
152
|
+
'zone_analysis': {
|
|
153
|
+
'min_duration': 2,
|
|
154
|
+
'min_amplitude': 0.001,
|
|
155
|
+
'normalization_method': 'atr', # 'atr', 'price', 'none'
|
|
156
|
+
'detection_method': 'sign_change' # 'sign_change', 'threshold', 'custom'
|
|
157
|
+
},
|
|
158
|
+
'pattern_analysis': {
|
|
159
|
+
'min_pattern_length': 3,
|
|
160
|
+
'max_pattern_length': 50,
|
|
161
|
+
'similarity_threshold': 0.8
|
|
162
|
+
},
|
|
163
|
+
'statistical_analysis': {
|
|
164
|
+
'confidence_level': 0.95,
|
|
165
|
+
'significance_level': 0.05,
|
|
166
|
+
'bootstrap_samples': 1000,
|
|
167
|
+
'random_state': 42
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# ============================================================================
|
|
172
|
+
# MACHINE LEARNING CONFIGURATION
|
|
173
|
+
# ============================================================================
|
|
174
|
+
|
|
175
|
+
ML_CONFIG = {
|
|
176
|
+
'random_state': 42,
|
|
177
|
+
'test_size': 0.3,
|
|
178
|
+
'validation_size': 0.2,
|
|
179
|
+
'cross_validation_folds': 5,
|
|
180
|
+
'feature_selection_method': 'mutual_info',
|
|
181
|
+
'hyperparameter_tuning': True
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# ============================================================================
|
|
185
|
+
# VISUALIZATION CONFIGURATION
|
|
186
|
+
# ============================================================================
|
|
187
|
+
|
|
188
|
+
VISUALIZATION = {
|
|
189
|
+
'figure_size': (12, 8),
|
|
190
|
+
'dpi': 100,
|
|
191
|
+
'style': 'seaborn-v0_8',
|
|
192
|
+
'color_palette': 'viridis',
|
|
193
|
+
'save_format': 'png',
|
|
194
|
+
'save_dpi': 300
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# ============================================================================
|
|
198
|
+
# LOGGING CONFIGURATION
|
|
199
|
+
# ============================================================================
|
|
200
|
+
|
|
201
|
+
LOGGING = {
|
|
202
|
+
'level': "INFO",
|
|
203
|
+
'format': "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
204
|
+
'file_logging': True,
|
|
205
|
+
'log_file': PROJECT_ROOT / "logs" / "bquant.log"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# ============================================================================
|
|
209
|
+
# EXPERIMENT CONFIGURATION
|
|
210
|
+
# ============================================================================
|
|
211
|
+
|
|
212
|
+
EXPERIMENT_CONFIG = {
|
|
213
|
+
'results_dir': RESULTS_DIR,
|
|
214
|
+
'save_intermediate': True,
|
|
215
|
+
'experiment_naming': 'timestamp', # 'timestamp', 'custom', 'auto'
|
|
216
|
+
'version_control': True
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# ============================================================================
|
|
220
|
+
# CONFIGURATION MANAGEMENT FUNCTIONS
|
|
221
|
+
# ============================================================================
|
|
222
|
+
|
|
223
|
+
def get_data_path(symbol: str, timeframe: str, data_source: str = 'tradingview', quote_provider: str = 'default') -> Path:
|
|
224
|
+
"""
|
|
225
|
+
Generate data file path based on symbol and timeframe for different data providers.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
symbol: Trading symbol (e.g., 'XAUUSD', 'EURUSD')
|
|
229
|
+
timeframe: Timeframe (e.g., '1h', '1d', '5m')
|
|
230
|
+
data_source: Data source ('tradingview', 'metatrader', 'generic', 'custom')
|
|
231
|
+
quote_provider: Quote provider for the data source (e.g., 'oanda', 'forexcom', 'icmarkets')
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Path to data file
|
|
235
|
+
"""
|
|
236
|
+
# Validate timeframe first
|
|
237
|
+
timeframe = validate_timeframe(timeframe)
|
|
238
|
+
|
|
239
|
+
# Get mapped timeframe for the specific data source
|
|
240
|
+
if data_source in TIMEFRAME_MAPPING:
|
|
241
|
+
mapped_timeframe = TIMEFRAME_MAPPING[data_source].get(timeframe, timeframe)
|
|
242
|
+
else:
|
|
243
|
+
mapped_timeframe = timeframe
|
|
244
|
+
|
|
245
|
+
# Get file pattern for the data source and quote provider
|
|
246
|
+
source_patterns = DATA_FILE_PATTERNS.get(data_source, DATA_FILE_PATTERNS['generic'])
|
|
247
|
+
|
|
248
|
+
if isinstance(source_patterns, dict):
|
|
249
|
+
# New format with quote providers
|
|
250
|
+
pattern = source_patterns.get(quote_provider, source_patterns.get('default', source_patterns))
|
|
251
|
+
else:
|
|
252
|
+
# Legacy format (backward compatibility)
|
|
253
|
+
pattern = source_patterns
|
|
254
|
+
|
|
255
|
+
# Generate filename
|
|
256
|
+
filename = pattern.format(symbol=symbol, timeframe=mapped_timeframe)
|
|
257
|
+
return DATA_DIR / filename
|
|
258
|
+
|
|
259
|
+
def get_indicator_params(indicator: str, **overrides) -> Dict[str, Any]:
|
|
260
|
+
"""
|
|
261
|
+
Get indicator parameters with optional overrides.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
indicator: Indicator name ('macd', 'rsi', etc.)
|
|
265
|
+
**overrides: Parameter overrides
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Dictionary with indicator parameters
|
|
269
|
+
"""
|
|
270
|
+
params = DEFAULT_INDICATORS.get(indicator, {}).copy()
|
|
271
|
+
params.update(overrides)
|
|
272
|
+
return params
|
|
273
|
+
|
|
274
|
+
def get_analysis_params(analysis_type: str, **overrides) -> Dict[str, Any]:
|
|
275
|
+
"""
|
|
276
|
+
Get analysis parameters with optional overrides.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
analysis_type: Analysis type ('zone_analysis', 'pattern_analysis', etc.)
|
|
280
|
+
**overrides: Parameter overrides
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
Dictionary with analysis parameters
|
|
284
|
+
"""
|
|
285
|
+
params = ANALYSIS_CONFIG.get(analysis_type, {}).copy()
|
|
286
|
+
params.update(overrides)
|
|
287
|
+
return params
|
|
288
|
+
|
|
289
|
+
def validate_timeframe(timeframe: str) -> str:
|
|
290
|
+
"""
|
|
291
|
+
Validate and normalize timeframe.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
timeframe: Timeframe string
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Validated timeframe
|
|
298
|
+
|
|
299
|
+
Raises:
|
|
300
|
+
ValueError: If timeframe is not supported
|
|
301
|
+
"""
|
|
302
|
+
if timeframe not in SUPPORTED_TIMEFRAMES:
|
|
303
|
+
raise ValueError(f"Unsupported timeframe: {timeframe}. Supported: {list(SUPPORTED_TIMEFRAMES.keys())}")
|
|
304
|
+
return timeframe
|
|
305
|
+
|
|
306
|
+
def get_results_path(experiment_name: str, file_type: str = 'csv') -> Path:
|
|
307
|
+
"""
|
|
308
|
+
Generate results file path.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
experiment_name: Name of the experiment
|
|
312
|
+
file_type: File extension
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Path to results file
|
|
316
|
+
"""
|
|
317
|
+
filename = f"{experiment_name}.{file_type}"
|
|
318
|
+
return RESULTS_DIR / filename
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def get_cache_config() -> Dict[str, Any]:
|
|
322
|
+
"""
|
|
323
|
+
Получить конфигурацию кэширования.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Словарь с настройками кэширования
|
|
327
|
+
"""
|
|
328
|
+
return CACHE_CONFIG.copy()
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Исключения системы BQuant
|
|
3
|
+
|
|
4
|
+
Определение кастомных исключений для различных компонентов системы.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional, Any, Dict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BQuantError(Exception):
|
|
11
|
+
"""
|
|
12
|
+
Базовое исключение BQuant
|
|
13
|
+
|
|
14
|
+
Все исключения BQuant должны наследоваться от этого класса.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
|
|
18
|
+
"""
|
|
19
|
+
Args:
|
|
20
|
+
message: Сообщение об ошибке
|
|
21
|
+
details: Дополнительные детали ошибки
|
|
22
|
+
"""
|
|
23
|
+
super().__init__(message)
|
|
24
|
+
self.message = message
|
|
25
|
+
self.details = details or {}
|
|
26
|
+
|
|
27
|
+
def __str__(self) -> str:
|
|
28
|
+
if self.details:
|
|
29
|
+
details_str = ', '.join(f"{k}={v}" for k, v in self.details.items())
|
|
30
|
+
return f"{self.message} (Details: {details_str})"
|
|
31
|
+
return self.message
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DataError(BQuantError):
|
|
35
|
+
"""
|
|
36
|
+
Ошибка данных
|
|
37
|
+
|
|
38
|
+
Возникает при проблемах с загрузкой, обработкой или валидацией данных.
|
|
39
|
+
"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DataValidationError(DataError):
|
|
44
|
+
"""
|
|
45
|
+
Ошибка валидации данных
|
|
46
|
+
|
|
47
|
+
Возникает когда данные не соответствуют ожидаемому формату или содержат ошибки.
|
|
48
|
+
"""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class DataLoadingError(DataError):
|
|
53
|
+
"""
|
|
54
|
+
Ошибка загрузки данных
|
|
55
|
+
|
|
56
|
+
Возникает при невозможности загрузить данные из источника.
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class DataProcessingError(DataError):
|
|
62
|
+
"""
|
|
63
|
+
Ошибка обработки данных
|
|
64
|
+
|
|
65
|
+
Возникает при ошибках во время обработки или трансформации данных.
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ConfigurationError(BQuantError):
|
|
71
|
+
"""
|
|
72
|
+
Ошибка конфигурации
|
|
73
|
+
|
|
74
|
+
Возникает при проблемах с конфигурационными параметрами.
|
|
75
|
+
"""
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class InvalidTimeframeError(ConfigurationError):
|
|
80
|
+
"""
|
|
81
|
+
Ошибка неверного таймфрейма
|
|
82
|
+
|
|
83
|
+
Возникает при использовании неподдерживаемого таймфрейма.
|
|
84
|
+
"""
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class InvalidIndicatorParametersError(ConfigurationError):
|
|
89
|
+
"""
|
|
90
|
+
Ошибка неверных параметров индикатора
|
|
91
|
+
|
|
92
|
+
Возникает при передаче некорректных параметров для расчета индикатора.
|
|
93
|
+
"""
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class AnalysisError(BQuantError):
|
|
98
|
+
"""
|
|
99
|
+
Ошибка анализа
|
|
100
|
+
|
|
101
|
+
Возникает при проблемах в аналитических модулях.
|
|
102
|
+
"""
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class IndicatorCalculationError(AnalysisError):
|
|
107
|
+
"""
|
|
108
|
+
Ошибка расчета индикатора
|
|
109
|
+
|
|
110
|
+
Возникает при невозможности рассчитать технический индикатор.
|
|
111
|
+
"""
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ZoneAnalysisError(AnalysisError):
|
|
116
|
+
"""
|
|
117
|
+
Ошибка анализа зон
|
|
118
|
+
|
|
119
|
+
Возникает при проблемах в процессе анализа зон MACD или других индикаторов.
|
|
120
|
+
"""
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class StatisticalAnalysisError(AnalysisError):
|
|
125
|
+
"""
|
|
126
|
+
Ошибка статистического анализа
|
|
127
|
+
|
|
128
|
+
Возникает при проблемах в статистических тестах или расчетах.
|
|
129
|
+
"""
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class VisualizationError(BQuantError):
|
|
134
|
+
"""
|
|
135
|
+
Ошибка визуализации
|
|
136
|
+
|
|
137
|
+
Возникает при проблемах с созданием графиков или визуализаций.
|
|
138
|
+
"""
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class MLError(BQuantError):
|
|
143
|
+
"""
|
|
144
|
+
Ошибка машинного обучения
|
|
145
|
+
|
|
146
|
+
Возникает при проблемах в ML модулях (пока в разработке).
|
|
147
|
+
"""
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class FeatureExtractionError(MLError):
|
|
152
|
+
"""
|
|
153
|
+
Ошибка извлечения признаков
|
|
154
|
+
|
|
155
|
+
Возникает при проблемах с извлечением признаков для ML.
|
|
156
|
+
"""
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ModelTrainingError(MLError):
|
|
161
|
+
"""
|
|
162
|
+
Ошибка обучения модели
|
|
163
|
+
|
|
164
|
+
Возникает при проблемах в процессе обучения ML модели.
|
|
165
|
+
"""
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class FileOperationError(BQuantError):
|
|
170
|
+
"""
|
|
171
|
+
Ошибка файловых операций
|
|
172
|
+
|
|
173
|
+
Возникает при проблемах с чтением/записью файлов.
|
|
174
|
+
"""
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class NotImplementedError(BQuantError):
|
|
179
|
+
"""
|
|
180
|
+
Ошибка незавершенной функциональности
|
|
181
|
+
|
|
182
|
+
Возникает при обращении к функциональности, которая еще не реализована.
|
|
183
|
+
"""
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# Вспомогательные функции для создания исключений
|
|
188
|
+
|
|
189
|
+
def create_data_validation_error(
|
|
190
|
+
message: str,
|
|
191
|
+
column: Optional[str] = None,
|
|
192
|
+
expected_type: Optional[str] = None,
|
|
193
|
+
actual_type: Optional[str] = None,
|
|
194
|
+
expected_shape: Optional[tuple] = None,
|
|
195
|
+
actual_shape: Optional[tuple] = None
|
|
196
|
+
) -> DataValidationError:
|
|
197
|
+
"""
|
|
198
|
+
Создать исключение валидации данных с деталями
|
|
199
|
+
"""
|
|
200
|
+
details = {}
|
|
201
|
+
if column:
|
|
202
|
+
details['column'] = column
|
|
203
|
+
if expected_type:
|
|
204
|
+
details['expected_type'] = expected_type
|
|
205
|
+
if actual_type:
|
|
206
|
+
details['actual_type'] = actual_type
|
|
207
|
+
if expected_shape:
|
|
208
|
+
details['expected_shape'] = expected_shape
|
|
209
|
+
if actual_shape:
|
|
210
|
+
details['actual_shape'] = actual_shape
|
|
211
|
+
|
|
212
|
+
return DataValidationError(message, details)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def create_indicator_calculation_error(
|
|
216
|
+
indicator_name: str,
|
|
217
|
+
message: str,
|
|
218
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
219
|
+
data_shape: Optional[tuple] = None
|
|
220
|
+
) -> IndicatorCalculationError:
|
|
221
|
+
"""
|
|
222
|
+
Создать исключение расчета индикатора с деталями
|
|
223
|
+
"""
|
|
224
|
+
details = {'indicator': indicator_name}
|
|
225
|
+
if parameters:
|
|
226
|
+
details['parameters'] = parameters
|
|
227
|
+
if data_shape:
|
|
228
|
+
details['data_shape'] = data_shape
|
|
229
|
+
|
|
230
|
+
return IndicatorCalculationError(message, details)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def create_configuration_error(
|
|
234
|
+
parameter_name: str,
|
|
235
|
+
message: str,
|
|
236
|
+
expected_values: Optional[list] = None,
|
|
237
|
+
actual_value: Optional[Any] = None
|
|
238
|
+
) -> ConfigurationError:
|
|
239
|
+
"""
|
|
240
|
+
Создать исключение конфигурации с деталями
|
|
241
|
+
"""
|
|
242
|
+
details = {'parameter': parameter_name}
|
|
243
|
+
if expected_values:
|
|
244
|
+
details['expected_values'] = expected_values
|
|
245
|
+
if actual_value is not None:
|
|
246
|
+
details['actual_value'] = actual_value
|
|
247
|
+
|
|
248
|
+
return ConfigurationError(message, details)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# Контекстные менеджеры для обработки исключений
|
|
252
|
+
|
|
253
|
+
class BQuantErrorContext:
|
|
254
|
+
"""
|
|
255
|
+
Контекстный менеджер для обработки исключений BQuant
|
|
256
|
+
"""
|
|
257
|
+
|
|
258
|
+
def __init__(self, operation: str, logger=None):
|
|
259
|
+
"""
|
|
260
|
+
Args:
|
|
261
|
+
operation: Описание операции
|
|
262
|
+
logger: Логгер для записи ошибок
|
|
263
|
+
"""
|
|
264
|
+
self.operation = operation
|
|
265
|
+
self.logger = logger
|
|
266
|
+
|
|
267
|
+
def __enter__(self):
|
|
268
|
+
return self
|
|
269
|
+
|
|
270
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
271
|
+
if exc_type is not None:
|
|
272
|
+
if self.logger:
|
|
273
|
+
self.logger.error(f"Ошибка в операции '{self.operation}': {exc_val}")
|
|
274
|
+
|
|
275
|
+
# Если это не наше исключение, оборачиваем его
|
|
276
|
+
if not isinstance(exc_val, BQuantError):
|
|
277
|
+
if exc_type in (ValueError, TypeError):
|
|
278
|
+
# Конвертируем стандартные исключения в наши
|
|
279
|
+
raise ConfigurationError(
|
|
280
|
+
f"Ошибка в операции '{self.operation}': {exc_val}"
|
|
281
|
+
) from exc_val
|
|
282
|
+
else:
|
|
283
|
+
# Оборачиваем все остальные в базовое исключение
|
|
284
|
+
raise BQuantError(
|
|
285
|
+
f"Неожиданная ошибка в операции '{self.operation}': {exc_val}"
|
|
286
|
+
) from exc_val
|
|
287
|
+
|
|
288
|
+
return False # Не подавляем исключение
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# Валидаторы с исключениями
|
|
292
|
+
|
|
293
|
+
def validate_timeframe(timeframe: str, supported_timeframes: list):
|
|
294
|
+
"""
|
|
295
|
+
Валидировать таймфрейм с выбросом исключения
|
|
296
|
+
"""
|
|
297
|
+
if timeframe not in supported_timeframes:
|
|
298
|
+
raise InvalidTimeframeError(
|
|
299
|
+
f"Неподдерживаемый таймфрейм: {timeframe}",
|
|
300
|
+
{
|
|
301
|
+
'timeframe': timeframe,
|
|
302
|
+
'supported_timeframes': supported_timeframes
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def validate_indicator_parameters(indicator: str, parameters: Dict[str, Any], required_params: list):
|
|
308
|
+
"""
|
|
309
|
+
Валидировать параметры индикатора с выбросом исключения
|
|
310
|
+
"""
|
|
311
|
+
missing_params = [param for param in required_params if param not in parameters]
|
|
312
|
+
if missing_params:
|
|
313
|
+
raise InvalidIndicatorParametersError(
|
|
314
|
+
f"Отсутствуют обязательные параметры для индикатора {indicator}: {missing_params}",
|
|
315
|
+
{
|
|
316
|
+
'indicator': indicator,
|
|
317
|
+
'missing_parameters': missing_params,
|
|
318
|
+
'provided_parameters': list(parameters.keys())
|
|
319
|
+
}
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def validate_ohlcv_data(data, required_columns: list = None):
|
|
324
|
+
"""
|
|
325
|
+
Валидировать OHLCV данные с выбросом исключения
|
|
326
|
+
"""
|
|
327
|
+
import pandas as pd
|
|
328
|
+
|
|
329
|
+
if not isinstance(data, pd.DataFrame):
|
|
330
|
+
raise create_data_validation_error(
|
|
331
|
+
"Данные должны быть pandas DataFrame",
|
|
332
|
+
expected_type="pandas.DataFrame",
|
|
333
|
+
actual_type=type(data).__name__
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
required_columns = required_columns or ['open', 'high', 'low', 'close']
|
|
337
|
+
missing_columns = [col for col in required_columns if col not in data.columns]
|
|
338
|
+
|
|
339
|
+
if missing_columns:
|
|
340
|
+
raise create_data_validation_error(
|
|
341
|
+
f"Отсутствуют обязательные колонки: {missing_columns}",
|
|
342
|
+
expected_type="OHLCV columns",
|
|
343
|
+
actual_type=list(data.columns)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
if len(data) == 0:
|
|
347
|
+
raise create_data_validation_error(
|
|
348
|
+
"DataFrame не содержит данных",
|
|
349
|
+
expected_shape="(n_rows > 0, n_cols)",
|
|
350
|
+
actual_shape=data.shape
|
|
351
|
+
)
|