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,414 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Модуль статистического анализа BQuant
|
|
3
|
+
|
|
4
|
+
Предоставляет функции для статистического анализа финансовых данных:
|
|
5
|
+
- Тестирование гипотез
|
|
6
|
+
- Корреляционный анализ
|
|
7
|
+
- Распределения данных
|
|
8
|
+
- Описательная статистика
|
|
9
|
+
- Временные ряды статистика
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Dict, List, Any, Optional, Tuple
|
|
13
|
+
import pandas as pd
|
|
14
|
+
import numpy as np
|
|
15
|
+
from scipy import stats
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
|
|
18
|
+
from ...core.logging_config import get_logger
|
|
19
|
+
from .. import BaseAnalyzer, AnalysisResult
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
# Версия модуля статистического анализа
|
|
24
|
+
__version__ = "0.0.0"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StatisticalAnalyzer(BaseAnalyzer):
|
|
28
|
+
"""
|
|
29
|
+
Базовый класс для статистических анализаторов.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
33
|
+
"""
|
|
34
|
+
Инициализация статистического анализатора.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
config: Конфигурация анализатора
|
|
38
|
+
"""
|
|
39
|
+
super().__init__("StatisticalAnalyzer", config)
|
|
40
|
+
|
|
41
|
+
# Параметры по умолчанию
|
|
42
|
+
self.default_alpha = self.config.get('alpha', 0.05)
|
|
43
|
+
self.min_sample_size = self.config.get('min_sample_size', 30)
|
|
44
|
+
|
|
45
|
+
def descriptive_statistics(self, data: pd.Series, name: str = "data") -> Dict[str, float]:
|
|
46
|
+
"""
|
|
47
|
+
Вычисление описательных статистик.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
data: Временной ряд данных
|
|
51
|
+
name: Название данных
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Словарь с описательными статистиками
|
|
55
|
+
"""
|
|
56
|
+
if data.empty:
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
stats_dict = {
|
|
60
|
+
'count': len(data),
|
|
61
|
+
'mean': data.mean(),
|
|
62
|
+
'std': data.std(),
|
|
63
|
+
'min': data.min(),
|
|
64
|
+
'max': data.max(),
|
|
65
|
+
'median': data.median(),
|
|
66
|
+
'q25': data.quantile(0.25),
|
|
67
|
+
'q75': data.quantile(0.75),
|
|
68
|
+
'skewness': data.skew(),
|
|
69
|
+
'kurtosis': data.kurtosis(),
|
|
70
|
+
'variance': data.var()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Добавляем коэффициент вариации
|
|
74
|
+
if stats_dict['mean'] != 0:
|
|
75
|
+
stats_dict['cv'] = stats_dict['std'] / abs(stats_dict['mean'])
|
|
76
|
+
else:
|
|
77
|
+
stats_dict['cv'] = np.nan
|
|
78
|
+
|
|
79
|
+
self.logger.debug(f"Calculated descriptive statistics for {name}")
|
|
80
|
+
return stats_dict
|
|
81
|
+
|
|
82
|
+
def normality_test(self, data: pd.Series, alpha: float = None) -> Dict[str, Any]:
|
|
83
|
+
"""
|
|
84
|
+
Тест на нормальность распределения.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
data: Данные для тестирования
|
|
88
|
+
alpha: Уровень значимости
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Результаты тестов нормальности
|
|
92
|
+
"""
|
|
93
|
+
if alpha is None:
|
|
94
|
+
alpha = self.default_alpha
|
|
95
|
+
|
|
96
|
+
results = {}
|
|
97
|
+
|
|
98
|
+
if len(data) < 3:
|
|
99
|
+
return {'error': 'Insufficient data for normality tests'}
|
|
100
|
+
|
|
101
|
+
# Shapiro-Wilk test (для небольших выборок)
|
|
102
|
+
if len(data) <= 5000:
|
|
103
|
+
try:
|
|
104
|
+
shapiro_stat, shapiro_p = stats.shapiro(data.dropna())
|
|
105
|
+
results['shapiro'] = {
|
|
106
|
+
'statistic': shapiro_stat,
|
|
107
|
+
'p_value': shapiro_p,
|
|
108
|
+
'is_normal': shapiro_p > alpha
|
|
109
|
+
}
|
|
110
|
+
except Exception as e:
|
|
111
|
+
self.logger.warning(f"Shapiro-Wilk test failed: {e}")
|
|
112
|
+
|
|
113
|
+
# Kolmogorov-Smirnov test
|
|
114
|
+
try:
|
|
115
|
+
ks_stat, ks_p = stats.kstest(data.dropna(), 'norm',
|
|
116
|
+
args=(data.mean(), data.std()))
|
|
117
|
+
results['kolmogorov_smirnov'] = {
|
|
118
|
+
'statistic': ks_stat,
|
|
119
|
+
'p_value': ks_p,
|
|
120
|
+
'is_normal': ks_p > alpha
|
|
121
|
+
}
|
|
122
|
+
except Exception as e:
|
|
123
|
+
self.logger.warning(f"Kolmogorov-Smirnov test failed: {e}")
|
|
124
|
+
|
|
125
|
+
# Anderson-Darling test
|
|
126
|
+
try:
|
|
127
|
+
ad_result = stats.anderson(data.dropna(), dist='norm')
|
|
128
|
+
# Сравниваем с критическим значением для alpha=0.05
|
|
129
|
+
critical_idx = 2 # Индекс для 5% уровня значимости
|
|
130
|
+
results['anderson_darling'] = {
|
|
131
|
+
'statistic': ad_result.statistic,
|
|
132
|
+
'critical_value': ad_result.critical_values[critical_idx],
|
|
133
|
+
'is_normal': ad_result.statistic < ad_result.critical_values[critical_idx]
|
|
134
|
+
}
|
|
135
|
+
except Exception as e:
|
|
136
|
+
self.logger.warning(f"Anderson-Darling test failed: {e}")
|
|
137
|
+
|
|
138
|
+
self.logger.debug(f"Performed normality tests on {len(data)} data points")
|
|
139
|
+
return results
|
|
140
|
+
|
|
141
|
+
def correlation_analysis(self, x: pd.Series, y: pd.Series,
|
|
142
|
+
methods: List[str] = None) -> Dict[str, Any]:
|
|
143
|
+
"""
|
|
144
|
+
Анализ корреляции между двумя переменными.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
x: Первая переменная
|
|
148
|
+
y: Вторая переменная
|
|
149
|
+
methods: Методы корреляции для использования
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Результаты корреляционного анализа
|
|
153
|
+
"""
|
|
154
|
+
if methods is None:
|
|
155
|
+
methods = ['pearson', 'spearman', 'kendall']
|
|
156
|
+
|
|
157
|
+
results = {}
|
|
158
|
+
|
|
159
|
+
# Убираем NaN значения
|
|
160
|
+
combined = pd.DataFrame({'x': x, 'y': y}).dropna()
|
|
161
|
+
if len(combined) < self.min_sample_size:
|
|
162
|
+
return {'error': f'Insufficient data after cleaning: {len(combined)} < {self.min_sample_size}'}
|
|
163
|
+
|
|
164
|
+
x_clean = combined['x']
|
|
165
|
+
y_clean = combined['y']
|
|
166
|
+
|
|
167
|
+
for method in methods:
|
|
168
|
+
try:
|
|
169
|
+
if method == 'pearson':
|
|
170
|
+
corr, p_value = stats.pearsonr(x_clean, y_clean)
|
|
171
|
+
elif method == 'spearman':
|
|
172
|
+
corr, p_value = stats.spearmanr(x_clean, y_clean)
|
|
173
|
+
elif method == 'kendall':
|
|
174
|
+
corr, p_value = stats.kendalltau(x_clean, y_clean)
|
|
175
|
+
else:
|
|
176
|
+
self.logger.warning(f"Unknown correlation method: {method}")
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
results[method] = {
|
|
180
|
+
'correlation': corr,
|
|
181
|
+
'p_value': p_value,
|
|
182
|
+
'is_significant': p_value < self.default_alpha,
|
|
183
|
+
'sample_size': len(x_clean)
|
|
184
|
+
}
|
|
185
|
+
except Exception as e:
|
|
186
|
+
self.logger.error(f"Correlation analysis failed for {method}: {e}")
|
|
187
|
+
results[method] = {'error': str(e)}
|
|
188
|
+
|
|
189
|
+
self.logger.debug(f"Performed correlation analysis using {methods}")
|
|
190
|
+
return results
|
|
191
|
+
|
|
192
|
+
def t_test(self, sample1: pd.Series, sample2: pd.Series = None,
|
|
193
|
+
mu: float = 0, alternative: str = 'two-sided') -> Dict[str, Any]:
|
|
194
|
+
"""
|
|
195
|
+
Выполнение t-теста.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
sample1: Первая выборка
|
|
199
|
+
sample2: Вторая выборка (для независимых выборок)
|
|
200
|
+
mu: Гипотетическое среднее (для одновыборочного теста)
|
|
201
|
+
alternative: Альтернативная гипотеза ('two-sided', 'less', 'greater')
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Результаты t-теста
|
|
205
|
+
"""
|
|
206
|
+
sample1_clean = sample1.dropna()
|
|
207
|
+
|
|
208
|
+
if sample2 is None:
|
|
209
|
+
# Одновыборочный t-тест
|
|
210
|
+
if len(sample1_clean) < 2:
|
|
211
|
+
return {'error': 'Insufficient data for one-sample t-test'}
|
|
212
|
+
|
|
213
|
+
t_stat, p_value = stats.ttest_1samp(sample1_clean, mu, alternative=alternative)
|
|
214
|
+
test_type = 'one_sample'
|
|
215
|
+
df = len(sample1_clean) - 1
|
|
216
|
+
else:
|
|
217
|
+
# Двухвыборочный t-тест
|
|
218
|
+
sample2_clean = sample2.dropna()
|
|
219
|
+
|
|
220
|
+
if len(sample1_clean) < 2 or len(sample2_clean) < 2:
|
|
221
|
+
return {'error': 'Insufficient data for two-sample t-test'}
|
|
222
|
+
|
|
223
|
+
t_stat, p_value = stats.ttest_ind(sample1_clean, sample2_clean, alternative=alternative)
|
|
224
|
+
test_type = 'two_sample'
|
|
225
|
+
df = len(sample1_clean) + len(sample2_clean) - 2
|
|
226
|
+
|
|
227
|
+
result = {
|
|
228
|
+
'test_type': test_type,
|
|
229
|
+
't_statistic': t_stat,
|
|
230
|
+
'p_value': p_value,
|
|
231
|
+
'degrees_of_freedom': df,
|
|
232
|
+
'alternative': alternative,
|
|
233
|
+
'is_significant': p_value < self.default_alpha,
|
|
234
|
+
'alpha': self.default_alpha
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if sample2 is None:
|
|
238
|
+
result['sample_size'] = len(sample1_clean)
|
|
239
|
+
result['sample_mean'] = sample1_clean.mean()
|
|
240
|
+
result['hypothesized_mean'] = mu
|
|
241
|
+
else:
|
|
242
|
+
result['sample1_size'] = len(sample1_clean)
|
|
243
|
+
result['sample2_size'] = len(sample2_clean)
|
|
244
|
+
result['sample1_mean'] = sample1_clean.mean()
|
|
245
|
+
result['sample2_mean'] = sample2_clean.mean()
|
|
246
|
+
|
|
247
|
+
self.logger.debug(f"Performed {test_type} t-test")
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
def analyze(self, data: pd.DataFrame, **kwargs) -> AnalysisResult:
|
|
251
|
+
"""
|
|
252
|
+
Выполнение комплексного статистического анализа.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
data: DataFrame с данными
|
|
256
|
+
**kwargs: Дополнительные параметры
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
AnalysisResult с результатами статистического анализа
|
|
260
|
+
"""
|
|
261
|
+
if not self.validate_data(data):
|
|
262
|
+
raise ValueError("Data validation failed")
|
|
263
|
+
|
|
264
|
+
results = {}
|
|
265
|
+
|
|
266
|
+
# Анализируем числовые колонки
|
|
267
|
+
numeric_columns = data.select_dtypes(include=[np.number]).columns
|
|
268
|
+
|
|
269
|
+
for col in numeric_columns:
|
|
270
|
+
col_results = {}
|
|
271
|
+
|
|
272
|
+
# Описательная статистика
|
|
273
|
+
col_results['descriptive'] = self.descriptive_statistics(data[col], col)
|
|
274
|
+
|
|
275
|
+
# Тест на нормальность
|
|
276
|
+
col_results['normality'] = self.normality_test(data[col])
|
|
277
|
+
|
|
278
|
+
results[col] = col_results
|
|
279
|
+
|
|
280
|
+
# Корреляционный анализ между числовыми колонками
|
|
281
|
+
if len(numeric_columns) > 1:
|
|
282
|
+
correlations = {}
|
|
283
|
+
for i, col1 in enumerate(numeric_columns):
|
|
284
|
+
for col2 in numeric_columns[i+1:]:
|
|
285
|
+
pair_key = f"{col1}_vs_{col2}"
|
|
286
|
+
correlations[pair_key] = self.correlation_analysis(data[col1], data[col2])
|
|
287
|
+
|
|
288
|
+
results['correlations'] = correlations
|
|
289
|
+
|
|
290
|
+
metadata = {
|
|
291
|
+
'analyzer': 'StatisticalAnalyzer',
|
|
292
|
+
'columns_analyzed': list(numeric_columns),
|
|
293
|
+
'total_columns': len(data.columns),
|
|
294
|
+
'config': self.config
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return AnalysisResult(
|
|
298
|
+
analysis_type='statistical',
|
|
299
|
+
results=results,
|
|
300
|
+
data_size=len(data),
|
|
301
|
+
metadata=metadata
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def get_statistical_analyzers() -> Dict[str, str]:
|
|
306
|
+
"""
|
|
307
|
+
Получить список доступных статистических анализаторов.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Словарь {анализатор: описание}
|
|
311
|
+
"""
|
|
312
|
+
return {
|
|
313
|
+
'statistical': 'Комплексный статистический анализ',
|
|
314
|
+
'hypothesis': 'Тестирование статистических гипотез',
|
|
315
|
+
'correlation': 'Корреляционный анализ',
|
|
316
|
+
'distribution': 'Анализ распределений'
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# Удобные функции для быстрого анализа
|
|
321
|
+
def quick_stats(data: pd.Series) -> Dict[str, float]:
|
|
322
|
+
"""
|
|
323
|
+
Быстрые описательные статистики.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
data: Данные для анализа
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Основные статистики
|
|
330
|
+
"""
|
|
331
|
+
analyzer = StatisticalAnalyzer()
|
|
332
|
+
return analyzer.descriptive_statistics(data)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_normality(data: pd.Series, alpha: float = 0.05) -> bool:
|
|
336
|
+
"""
|
|
337
|
+
Быстрый тест нормальности.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
data: Данные для тестирования
|
|
341
|
+
alpha: Уровень значимости
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
True если данные имеют нормальное распределение
|
|
345
|
+
"""
|
|
346
|
+
analyzer = StatisticalAnalyzer({'alpha': alpha})
|
|
347
|
+
results = analyzer.normality_test(data, alpha)
|
|
348
|
+
|
|
349
|
+
# Возвращаем True если хотя бы один тест показал нормальность
|
|
350
|
+
for test_name, test_result in results.items():
|
|
351
|
+
if isinstance(test_result, dict) and 'is_normal' in test_result:
|
|
352
|
+
if test_result['is_normal']:
|
|
353
|
+
return True
|
|
354
|
+
|
|
355
|
+
return False
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def correlation_matrix(data: pd.DataFrame, method: str = 'pearson') -> pd.DataFrame:
|
|
359
|
+
"""
|
|
360
|
+
Матрица корреляций с p-values.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
data: DataFrame с данными
|
|
364
|
+
method: Метод корреляции
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
DataFrame с корреляциями
|
|
368
|
+
"""
|
|
369
|
+
numeric_data = data.select_dtypes(include=[np.number])
|
|
370
|
+
|
|
371
|
+
if method == 'pearson':
|
|
372
|
+
corr_matrix = numeric_data.corr()
|
|
373
|
+
elif method == 'spearman':
|
|
374
|
+
corr_matrix = numeric_data.corr(method='spearman')
|
|
375
|
+
elif method == 'kendall':
|
|
376
|
+
corr_matrix = numeric_data.corr(method='kendall')
|
|
377
|
+
else:
|
|
378
|
+
raise ValueError(f"Unknown correlation method: {method}")
|
|
379
|
+
|
|
380
|
+
return corr_matrix
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# Импорт функций hypothesis testing
|
|
384
|
+
try:
|
|
385
|
+
from .hypothesis_testing import (
|
|
386
|
+
HypothesisTestResult,
|
|
387
|
+
HypothesisTestSuite,
|
|
388
|
+
run_all_hypothesis_tests,
|
|
389
|
+
test_single_hypothesis
|
|
390
|
+
)
|
|
391
|
+
_hypothesis_testing_available = True
|
|
392
|
+
except ImportError as e:
|
|
393
|
+
logger.warning(f"Hypothesis testing module not available: {e}")
|
|
394
|
+
_hypothesis_testing_available = False
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
# Экспорт
|
|
398
|
+
__all__ = [
|
|
399
|
+
'StatisticalAnalyzer',
|
|
400
|
+
'get_statistical_analyzers',
|
|
401
|
+
'quick_stats',
|
|
402
|
+
'test_normality',
|
|
403
|
+
'correlation_matrix',
|
|
404
|
+
'__version__'
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
# Добавляем hypothesis testing если доступен
|
|
408
|
+
if _hypothesis_testing_available:
|
|
409
|
+
__all__.extend([
|
|
410
|
+
'HypothesisTestResult',
|
|
411
|
+
'HypothesisTestSuite',
|
|
412
|
+
'run_all_hypothesis_tests',
|
|
413
|
+
'test_single_hypothesis'
|
|
414
|
+
])
|