cyqnt-trd 0.1.2__py3-none-any.whl → 0.1.7__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 (40) hide show
  1. cyqnt_trd/CHANGELOG_0.1.7.md +111 -0
  2. cyqnt_trd/__init__.py +1 -1
  3. cyqnt_trd/backtesting/factor_test.py +3 -2
  4. cyqnt_trd/get_data/__init__.py +16 -1
  5. cyqnt_trd/get_data/get_futures_data.py +3 -3
  6. cyqnt_trd/get_data/get_kline_with_factor.py +808 -0
  7. cyqnt_trd/get_data/get_web3_trending_data.py +389 -0
  8. cyqnt_trd/online_trading/__init__.py +1 -0
  9. cyqnt_trd/online_trading/realtime_price_tracker.py +142 -2
  10. cyqnt_trd/trading_signal/example_usage.py +123 -7
  11. cyqnt_trd/trading_signal/factor/__init__.py +23 -0
  12. cyqnt_trd/trading_signal/factor/adx_factor.py +116 -0
  13. cyqnt_trd/trading_signal/factor/ao_factor.py +66 -0
  14. cyqnt_trd/trading_signal/factor/bbp_factor.py +68 -0
  15. cyqnt_trd/trading_signal/factor/cci_factor.py +65 -0
  16. cyqnt_trd/trading_signal/factor/ema_factor.py +102 -0
  17. cyqnt_trd/trading_signal/factor/macd_factor.py +97 -0
  18. cyqnt_trd/trading_signal/factor/momentum_factor.py +44 -0
  19. cyqnt_trd/trading_signal/factor/stochastic_factor.py +76 -0
  20. cyqnt_trd/trading_signal/factor/stochastic_tsi_factor.py +129 -0
  21. cyqnt_trd/trading_signal/factor/uo_factor.py +92 -0
  22. cyqnt_trd/trading_signal/factor/williams_r_factor.py +60 -0
  23. cyqnt_trd/trading_signal/factor_details.json +107 -0
  24. cyqnt_trd/trading_signal/selected_alpha/alpha1.py +4 -2
  25. cyqnt_trd/trading_signal/selected_alpha/alpha15.py +4 -2
  26. cyqnt_trd/trading_signal/selected_alpha/generate_alphas.py +1 -0
  27. {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/METADATA +16 -12
  28. {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/RECORD +34 -23
  29. {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/WHEEL +1 -1
  30. test/real_time_trade.py +467 -10
  31. test/test_now_factor.py +1082 -0
  32. test/track_k_line_continue.py +372 -0
  33. cyqnt_trd/test_script/get_symbols_by_volume.py +0 -227
  34. cyqnt_trd/test_script/test_alpha.py +0 -261
  35. cyqnt_trd/test_script/test_kline_data.py +0 -479
  36. test/test_example_usage.py +0 -381
  37. test/test_get_data.py +0 -310
  38. test/test_realtime_price_tracker.py +0 -546
  39. {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/licenses/LICENSE +0 -0
  40. {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1082 @@
1
+ """
2
+ 计算当前币对合约各个factor的值、看多/看空信号和胜率
3
+
4
+ 使用方法:
5
+ python test_now_factor.py --symbol BTCUSDT --interval 30m --lookback 500
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ import argparse
11
+ import warnings
12
+ import tempfile
13
+ from pathlib import Path
14
+ from datetime import datetime, timedelta
15
+ from typing import Optional, Dict, Any, Callable
16
+ from concurrent.futures import ThreadPoolExecutor
17
+ import pandas as pd
18
+ import numpy as np
19
+
20
+ # 抑制 pandas FutureWarning 关于 fillna 的警告
21
+ warnings.filterwarnings('ignore', category=FutureWarning, message='.*Downcasting object dtype arrays on .fillna.*')
22
+
23
+ # 添加项目根目录到路径
24
+ project_root = Path(__file__).parent.parent
25
+ if str(project_root) not in sys.path:
26
+ sys.path.insert(0, str(project_root))
27
+
28
+ # 导入 cyqnt_trd 包
29
+ try:
30
+ from cyqnt_trd.get_data.get_futures_data import get_and_save_futures_klines
31
+ from cyqnt_trd.trading_signal.factor.ma_factor import ma_factor
32
+ from cyqnt_trd.trading_signal.factor.rsi_factor import rsi_factor
33
+ from cyqnt_trd.trading_signal.factor.stochastic_factor import stochastic_k_factor
34
+ from cyqnt_trd.trading_signal.factor.cci_factor import cci_factor
35
+ from cyqnt_trd.trading_signal.factor.adx_factor import adx_factor
36
+ from cyqnt_trd.trading_signal.factor.ao_factor import ao_factor
37
+ from cyqnt_trd.trading_signal.factor.momentum_factor import momentum_factor
38
+ from cyqnt_trd.trading_signal.factor.macd_factor import macd_level_factor
39
+ from cyqnt_trd.trading_signal.factor.stochastic_tsi_factor import stochastic_tsi_fast_factor
40
+ from cyqnt_trd.trading_signal.factor.williams_r_factor import williams_r_factor
41
+ from cyqnt_trd.trading_signal.factor.bbp_factor import bbp_factor
42
+ from cyqnt_trd.trading_signal.factor.uo_factor import uo_factor
43
+ from cyqnt_trd.trading_signal.factor.ema_factor import ema_factor, ema_cross_factor
44
+ from cyqnt_trd.trading_signal.selected_alpha import (
45
+ alpha1_factor, alpha3_factor, alpha7_factor, alpha9_factor,
46
+ alpha11_factor, alpha15_factor, alpha17_factor, alpha21_factor,
47
+ alpha23_factor, alpha25_factor, alpha29_factor, alpha33_factor,
48
+ alpha34_factor, ALPHA_FACTORS
49
+ )
50
+ from cyqnt_trd.backtesting.factor_test import FactorTester
51
+ from cyqnt_trd.utils import set_user
52
+ except ImportError as e:
53
+ print(f"导入错误: {e}")
54
+ print("\n提示:请确保已安装 cyqnt_trd package: pip install -e /path/to/crypto_trading")
55
+ sys.exit(1)
56
+
57
+
58
+ def klines_to_dataframe(klines_data: list) -> pd.DataFrame:
59
+ """
60
+ 将K线数据列表转换为DataFrame
61
+
62
+ Args:
63
+ klines_data: K线数据列表,每个元素是一个包含K线信息的列表或字典
64
+
65
+ Returns:
66
+ DataFrame,包含以下列:
67
+ - open_time: 开盘时间(毫秒时间戳)
68
+ - open_time_str: 开盘时间字符串
69
+ - open_price: 开盘价
70
+ - high_price: 最高价
71
+ - low_price: 最低价
72
+ - close_price: 收盘价
73
+ - volume: 成交量
74
+ - close_time: 收盘时间(毫秒时间戳)
75
+ - quote_volume: 成交额
76
+ - trades: 成交笔数
77
+ - taker_buy_base_volume: 主动买入成交量
78
+ - taker_buy_quote_volume: 主动买入成交额
79
+ """
80
+ if not klines_data:
81
+ return pd.DataFrame()
82
+
83
+ # 转换为DataFrame
84
+ df = pd.DataFrame(klines_data)
85
+
86
+ # 标准化列名(根据Binance API返回的格式)
87
+ if len(df.columns) >= 12:
88
+ df.columns = [
89
+ 'open_time', 'open_price', 'high_price', 'low_price', 'close_price',
90
+ 'volume', 'close_time', 'quote_volume', 'trades',
91
+ 'taker_buy_base_volume', 'taker_buy_quote_volume', 'ignore'
92
+ ]
93
+ elif len(df.columns) >= 11:
94
+ df.columns = [
95
+ 'open_time', 'open_price', 'high_price', 'low_price', 'close_price',
96
+ 'volume', 'close_time', 'quote_volume', 'trades',
97
+ 'taker_buy_base_volume', 'taker_buy_quote_volume'
98
+ ]
99
+
100
+ # 转换数据类型
101
+ numeric_columns = ['open_price', 'high_price', 'low_price', 'close_price',
102
+ 'volume', 'quote_volume', 'taker_buy_base_volume', 'taker_buy_quote_volume']
103
+ for col in numeric_columns:
104
+ if col in df.columns:
105
+ df[col] = pd.to_numeric(df[col], errors='coerce')
106
+
107
+ # 转换时间
108
+ if 'open_time' in df.columns:
109
+ df['open_time'] = pd.to_numeric(df['open_time'], errors='coerce')
110
+ df['open_time_str'] = df['open_time'].apply(
111
+ lambda x: datetime.fromtimestamp(x / 1000).strftime('%Y-%m-%d %H:%M:%S') if pd.notna(x) else ''
112
+ )
113
+
114
+ # 按时间排序
115
+ if 'open_time' in df.columns:
116
+ df = df.sort_values('open_time').reset_index(drop=True)
117
+
118
+ return df
119
+
120
+
121
+ def calculate_normalized_alpha_factor(
122
+ data_slice: pd.DataFrame,
123
+ factor_func: Callable,
124
+ factor_name: str,
125
+ min_required: int = 30,
126
+ lookback_periods: int = 30,
127
+ **factor_kwargs
128
+ ) -> Optional[Dict[str, Any]]:
129
+ """
130
+ 计算归一化Alpha因子的通用函数
131
+
132
+ Args:
133
+ data_slice: 数据切片
134
+ factor_func: 因子计算函数
135
+ factor_name: 因子名称(用于日志)
136
+ min_required: 因子计算所需的最小周期数
137
+ lookback_periods: 归一化回看周期数
138
+ **factor_kwargs: 传递给因子函数的额外参数
139
+
140
+ Returns:
141
+ 包含因子值和看多/看空结果的字典,如果计算失败则返回None
142
+ """
143
+ try:
144
+ if len(data_slice) < min_required + 2:
145
+ return None
146
+
147
+ available_periods = len(data_slice) - min_required - 1
148
+ if available_periods < 2:
149
+ return None
150
+
151
+ actual_lookback = min(lookback_periods, max(2, available_periods))
152
+
153
+ # 计算因子值:当前周期和之前actual_lookback个周期(使用多线程并行计算)
154
+ def compute_factor_value(i):
155
+ """计算单个时间点的因子值"""
156
+ end_idx = len(data_slice) - i
157
+ start_idx = max(0, end_idx - min_required - 1)
158
+ if end_idx <= start_idx:
159
+ return 0.0
160
+
161
+ period_slice = data_slice.iloc[start_idx:end_idx]
162
+ try:
163
+ # 调用因子函数,传入额外参数
164
+ factor_value = factor_func(period_slice, **factor_kwargs)
165
+ if factor_value is not None:
166
+ return factor_value
167
+ else:
168
+ return 0.0
169
+ except Exception:
170
+ return 0.0
171
+
172
+ # 使用多线程并行计算因子值
173
+ indices = list(range(actual_lookback + 1))
174
+ factor_values = []
175
+ with ThreadPoolExecutor() as executor:
176
+ # 返回顺序保证和原for循环一致
177
+ factor_values = list(executor.map(compute_factor_value, indices))
178
+
179
+ if len(factor_values) < 2:
180
+ return None
181
+
182
+ # 归一化
183
+ factor_array = np.array(factor_values)
184
+ factor_min = factor_array.min()
185
+ factor_max = factor_array.max()
186
+
187
+ if factor_max == factor_min:
188
+ normalized_factors = np.zeros_like(factor_array)
189
+ else:
190
+ # Min-Max归一化到[-1, 1]区间
191
+ normalized_factors = 2 * (factor_array - factor_min) / (factor_max - factor_min) - 1
192
+
193
+ current_normalized = float(normalized_factors[0])
194
+ prev_normalized = float(normalized_factors[1]) if len(normalized_factors) > 1 else 0.0
195
+
196
+ # 判断信号:从负转正看多,从正转负看空
197
+ if prev_normalized <= 0 and current_normalized > 0:
198
+ signal = '看多'
199
+ elif prev_normalized >= 0 and current_normalized < 0:
200
+ signal = '看空'
201
+ else:
202
+ signal = '中性'
203
+
204
+ return {
205
+ 'value': current_normalized,
206
+ 'signal': signal,
207
+ 'raw_value': float(factor_values[0]) if len(factor_values) > 0 else 0.0,
208
+ 'prev_normalized': prev_normalized
209
+ }
210
+ except Exception as e:
211
+ print(f"计算归一化{factor_name}因子时出错: {e}")
212
+ return None
213
+
214
+
215
+ def calculate_factor_win_rate(
216
+ data_df: pd.DataFrame,
217
+ factor_func: Callable,
218
+ forward_periods: int = 24,
219
+ min_periods: int = 30,
220
+ factor_name: str = "factor"
221
+ ) -> Optional[Dict[str, float]]:
222
+ """
223
+ 计算因子基于历史数据的胜率(使用FactorTester.test_factor)
224
+
225
+ Args:
226
+ data_df: 历史数据DataFrame
227
+ factor_func: 因子计算函数,接受数据切片作为参数,返回因子值
228
+ forward_periods: 向前看的周期数(默认24,即未来24个周期)
229
+ min_periods: 最小需要的周期数
230
+ factor_name: 因子名称
231
+
232
+ Returns:
233
+ 包含胜率信息的字典,如果计算失败则返回None
234
+ """
235
+ try:
236
+ if len(data_df) < min_periods + forward_periods + 1:
237
+ return None
238
+
239
+ # 创建FactorTester实例
240
+ factor_tester = FactorTester(data_df)
241
+
242
+ # 调用test_factor计算胜率
243
+ test_results = factor_tester.test_factor(
244
+ factor_func=factor_func,
245
+ forward_periods=forward_periods,
246
+ min_periods=min_periods,
247
+ factor_name=factor_name
248
+ )
249
+
250
+ # 提取需要的胜率信息
251
+ result = {
252
+ 'long_win_rate': test_results.get('long_win_rate', 0.0),
253
+ 'short_win_rate': test_results.get('short_win_rate', 0.0),
254
+ 'overall_win_rate': test_results.get('overall_win_rate', 0.0),
255
+ 'long_avg_return': test_results.get('long_avg_return', 0.0),
256
+ 'short_avg_return': test_results.get('short_avg_return', 0.0),
257
+ 'long_signals': test_results.get('long_signals', 0),
258
+ 'short_signals': test_results.get('short_signals', 0),
259
+ 'total_samples': test_results.get('total_samples', 0)
260
+ }
261
+
262
+ return result
263
+
264
+ except Exception as e:
265
+ print(f"计算因子胜率时出错: {e}")
266
+ return None
267
+
268
+
269
+ def calculate_all_factors(data_df: pd.DataFrame, forward_periods=24) -> Dict[str, Any]:
270
+ """
271
+ 计算所有因子的因子值、看多/看空信号和胜率
272
+
273
+ Args:
274
+ data_df: 历史数据DataFrame
275
+
276
+ Returns:
277
+ 包含所有因子结果的字典
278
+ """
279
+ result = {}
280
+
281
+ if len(data_df) < 10:
282
+ print("数据量不足,无法计算因子")
283
+ return result
284
+
285
+ # 使用足够的数据切片(对于alpha因子,需要更多数据)
286
+ min_slice_size = 65
287
+ if len(data_df) >= min_slice_size:
288
+ data_slice = data_df.iloc[-min_slice_size:].copy()
289
+ elif len(data_df) >= 30:
290
+ data_slice = data_df.iloc[-30:].copy()
291
+ else:
292
+ data_slice = data_df.copy()
293
+
294
+ try:
295
+ # 定义所有需要计算的因子任务
296
+ def calculate_ma_factor():
297
+ """计算MA因子"""
298
+ if len(data_slice) < 6:
299
+ return None
300
+ try:
301
+ ma_factor_value = ma_factor(data_slice, period=5)
302
+ ma_win_rate = calculate_factor_win_rate(
303
+ data_df=data_df,
304
+ factor_func=lambda d: ma_factor(d, period=5),
305
+ forward_periods=forward_periods,
306
+ min_periods=6,
307
+ factor_name="MA5因子"
308
+ )
309
+ return ('ma_factor_5', {
310
+ 'value': ma_factor_value,
311
+ 'signal': '看多' if ma_factor_value > 0 else '看空' if ma_factor_value < 0 else '中性',
312
+ 'raw_value': ma_factor_value,
313
+ 'win_rate': ma_win_rate
314
+ })
315
+ except Exception as e:
316
+ print(f"计算MA因子时出错: {e}")
317
+ return None
318
+
319
+ def calculate_rsi_factor():
320
+ """计算RSI因子"""
321
+ if len(data_slice) < 16:
322
+ return None
323
+ try:
324
+ rsi_factor_value = rsi_factor(data_slice, period=14)
325
+ rsi_win_rate = calculate_factor_win_rate(
326
+ data_df=data_df,
327
+ factor_func=lambda d: rsi_factor(d, period=14),
328
+ forward_periods=forward_periods,
329
+ min_periods=16,
330
+ factor_name="RSI14因子"
331
+ )
332
+ return ('rsi_factor_14', {
333
+ 'value': rsi_factor_value,
334
+ 'signal': '看多' if rsi_factor_value > 0 else '看空' if rsi_factor_value < 0 else '中性',
335
+ 'raw_value': rsi_factor_value,
336
+ 'win_rate': rsi_win_rate
337
+ })
338
+ except Exception as e:
339
+ print(f"计算RSI因子时出错: {e}")
340
+ return None
341
+
342
+ def calculate_stochastic_k_factor():
343
+ """计算Stochastic %K因子"""
344
+ if len(data_slice) < 18:
345
+ return None
346
+ try:
347
+ stoch_value = stochastic_k_factor(data_slice, period=14, k_smooth=3, d_smooth=3)
348
+ stoch_win_rate = calculate_factor_win_rate(
349
+ data_df=data_df,
350
+ factor_func=lambda d: stochastic_k_factor(d, period=14, k_smooth=3, d_smooth=3),
351
+ forward_periods=forward_periods,
352
+ min_periods=18,
353
+ factor_name="Stochastic %K(14,3,3)因子"
354
+ )
355
+ return ('stochastic_k_factor_14_3_3', {
356
+ 'value': stoch_value,
357
+ 'signal': '看多' if stoch_value > 0 else '看空' if stoch_value < 0 else '中性',
358
+ 'raw_value': stoch_value,
359
+ 'win_rate': stoch_win_rate
360
+ })
361
+ except Exception as e:
362
+ print(f"计算Stochastic %K因子时出错: {e}")
363
+ return None
364
+
365
+ def calculate_cci_factor():
366
+ """计算CCI因子"""
367
+ if len(data_slice) < 21:
368
+ return None
369
+ try:
370
+ cci_value = cci_factor(data_slice, period=20)
371
+ cci_win_rate = calculate_factor_win_rate(
372
+ data_df=data_df,
373
+ factor_func=lambda d: cci_factor(d, period=20),
374
+ forward_periods=forward_periods,
375
+ min_periods=21,
376
+ factor_name="CCI(20)因子"
377
+ )
378
+ return ('cci_factor_20', {
379
+ 'value': cci_value,
380
+ 'signal': '看多' if cci_value > 0 else '看空' if cci_value < 0 else '中性',
381
+ 'raw_value': cci_value,
382
+ 'win_rate': cci_win_rate
383
+ })
384
+ except Exception as e:
385
+ print(f"计算CCI因子时出错: {e}")
386
+ return None
387
+
388
+ def calculate_adx_factor():
389
+ """计算ADX因子"""
390
+ if len(data_slice) < 30:
391
+ return None
392
+ try:
393
+ adx_value = adx_factor(data_slice, period=14)
394
+ adx_win_rate = calculate_factor_win_rate(
395
+ data_df=data_df,
396
+ factor_func=lambda d: adx_factor(d, period=14),
397
+ forward_periods=forward_periods,
398
+ min_periods=30,
399
+ factor_name="ADX(14)因子"
400
+ )
401
+ return ('adx_factor_14', {
402
+ 'value': adx_value,
403
+ 'signal': '看多' if adx_value > 0 else '看空' if adx_value < 0 else '中性',
404
+ 'raw_value': adx_value,
405
+ 'win_rate': adx_win_rate
406
+ })
407
+ except Exception as e:
408
+ print(f"计算ADX因子时出错: {e}")
409
+ return None
410
+
411
+ def calculate_ao_factor():
412
+ """计算AO因子"""
413
+ if len(data_slice) < 36:
414
+ return None
415
+ try:
416
+ ao_value = ao_factor(data_slice)
417
+ ao_win_rate = calculate_factor_win_rate(
418
+ data_df=data_df,
419
+ factor_func=ao_factor,
420
+ forward_periods=forward_periods,
421
+ min_periods=36,
422
+ factor_name="AO因子"
423
+ )
424
+ return ('ao_factor', {
425
+ 'value': ao_value,
426
+ 'signal': '看多' if ao_value > 0 else '看空' if ao_value < 0 else '中性',
427
+ 'raw_value': ao_value,
428
+ 'win_rate': ao_win_rate
429
+ })
430
+ except Exception as e:
431
+ print(f"计算AO因子时出错: {e}")
432
+ return None
433
+
434
+ def calculate_momentum_factor():
435
+ """计算动量因子"""
436
+ if len(data_slice) < 11:
437
+ return None
438
+ try:
439
+ momentum_value = momentum_factor(data_slice, period=10)
440
+ momentum_win_rate = calculate_factor_win_rate(
441
+ data_df=data_df,
442
+ factor_func=lambda d: momentum_factor(d, period=10),
443
+ forward_periods=forward_periods,
444
+ min_periods=11,
445
+ factor_name="Momentum(10)因子"
446
+ )
447
+ return ('momentum_factor_10', {
448
+ 'value': momentum_value,
449
+ 'signal': '看多' if momentum_value > 0 else '看空' if momentum_value < 0 else '中性',
450
+ 'raw_value': momentum_value,
451
+ 'win_rate': momentum_win_rate
452
+ })
453
+ except Exception as e:
454
+ print(f"计算Momentum因子时出错: {e}")
455
+ return None
456
+
457
+ def calculate_macd_factor():
458
+ """计算MACD因子"""
459
+ if len(data_slice) < 48:
460
+ return None
461
+ try:
462
+ macd_value = macd_level_factor(data_slice, fast_period=12, slow_period=26)
463
+ macd_win_rate = calculate_factor_win_rate(
464
+ data_df=data_df,
465
+ factor_func=lambda d: macd_level_factor(d, fast_period=12, slow_period=26),
466
+ forward_periods=forward_periods,
467
+ min_periods=48,
468
+ factor_name="MACD(12,26)因子"
469
+ )
470
+ return ('macd_factor_12_26', {
471
+ 'value': macd_value,
472
+ 'signal': '看多' if macd_value > 0 else '看空' if macd_value < 0 else '中性',
473
+ 'raw_value': macd_value,
474
+ 'win_rate': macd_win_rate
475
+ })
476
+ except Exception as e:
477
+ print(f"计算MACD因子时出错: {e}")
478
+ return None
479
+
480
+ def calculate_stochastic_tsi_factor():
481
+ """计算Stochastic TSI因子"""
482
+ if len(data_slice) < 35:
483
+ return None
484
+ try:
485
+ stoch_tsi_value = stochastic_tsi_fast_factor(data_slice, r_period=3, s_period=3, tsi_period1=14, tsi_period2=14)
486
+ stoch_tsi_win_rate = calculate_factor_win_rate(
487
+ data_df=data_df,
488
+ factor_func=lambda d: stochastic_tsi_fast_factor(d, r_period=3, s_period=3, tsi_period1=14, tsi_period2=14),
489
+ forward_periods=24,
490
+ min_periods=35,
491
+ factor_name="Stochastic TSI Fast(3,3,14,14)因子"
492
+ )
493
+ return ('stochastic_tsi_factor_3_3_14_14', {
494
+ 'value': stoch_tsi_value,
495
+ 'signal': '看多' if stoch_tsi_value > 0 else '看空' if stoch_tsi_value < 0 else '中性',
496
+ 'raw_value': stoch_tsi_value,
497
+ 'win_rate': stoch_tsi_win_rate
498
+ })
499
+ except Exception as e:
500
+ print(f"计算Stochastic TSI因子时出错: {e}")
501
+ return None
502
+
503
+ def calculate_williams_r_factor():
504
+ """计算Williams %R因子"""
505
+ if len(data_slice) < 15:
506
+ return None
507
+ try:
508
+ williams_r_value = williams_r_factor(data_slice, period=14)
509
+ williams_r_win_rate = calculate_factor_win_rate(
510
+ data_df=data_df,
511
+ factor_func=lambda d: williams_r_factor(d, period=14),
512
+ forward_periods=forward_periods,
513
+ min_periods=15,
514
+ factor_name="Williams %R(14)因子"
515
+ )
516
+ return ('williams_r_factor_14', {
517
+ 'value': williams_r_value,
518
+ 'signal': '看多' if williams_r_value > 0 else '看空' if williams_r_value < 0 else '中性',
519
+ 'raw_value': williams_r_value,
520
+ 'win_rate': williams_r_win_rate
521
+ })
522
+ except Exception as e:
523
+ print(f"计算Williams %R因子时出错: {e}")
524
+ return None
525
+
526
+ def calculate_bbp_factor():
527
+ """计算BBP因子"""
528
+ if len(data_slice) < 14:
529
+ return None
530
+ try:
531
+ bbp_value = bbp_factor(data_slice, period=13)
532
+ bbp_win_rate = calculate_factor_win_rate(
533
+ data_df=data_df,
534
+ factor_func=lambda d: bbp_factor(d, period=13),
535
+ forward_periods=24,
536
+ min_periods=14,
537
+ factor_name="BBP因子"
538
+ )
539
+ return ('bbp_factor', {
540
+ 'value': bbp_value,
541
+ 'signal': '看多' if bbp_value > 0 else '看空' if bbp_value < 0 else '中性',
542
+ 'raw_value': bbp_value,
543
+ 'win_rate': bbp_win_rate
544
+ })
545
+ except Exception as e:
546
+ print(f"计算BBP因子时出错: {e}")
547
+ return None
548
+
549
+ def calculate_uo_factor():
550
+ """计算UO因子"""
551
+ if len(data_slice) < 29:
552
+ return None
553
+ try:
554
+ uo_value = uo_factor(data_slice, period1=7, period2=14, period3=28)
555
+ uo_win_rate = calculate_factor_win_rate(
556
+ data_df=data_df,
557
+ factor_func=lambda d: uo_factor(d, period1=7, period2=14, period3=28),
558
+ forward_periods=forward_periods,
559
+ min_periods=29,
560
+ factor_name="UO(7,14,28)因子"
561
+ )
562
+ return ('uo_factor_7_14_28', {
563
+ 'value': uo_value,
564
+ 'signal': '看多' if uo_value > 0 else '看空' if uo_value < 0 else '中性',
565
+ 'raw_value': uo_value,
566
+ 'win_rate': uo_win_rate
567
+ })
568
+ except Exception as e:
569
+ print(f"计算UO因子时出错: {e}")
570
+ return None
571
+
572
+ def calculate_ema_factors():
573
+ """计算EMA因子(多个周期)"""
574
+ results = {}
575
+ ema_periods = [10, 20, 30, 50, 100, 200]
576
+
577
+ for period in ema_periods:
578
+ if len(data_slice) < period + 1:
579
+ continue
580
+ try:
581
+ ema_value = ema_factor(data_slice, period=period)
582
+ ema_win_rate = calculate_factor_win_rate(
583
+ data_df=data_df,
584
+ factor_func=lambda d, p=period: ema_factor(d, period=p),
585
+ forward_periods=24,
586
+ min_periods=period + 1,
587
+ factor_name=f"EMA({period})因子"
588
+ )
589
+ results[f'ema_factor_{period}'] = {
590
+ 'value': ema_value,
591
+ 'signal': '看多' if ema_value > 0 else '看空' if ema_value < 0 else '中性',
592
+ 'raw_value': ema_value,
593
+ 'win_rate': ema_win_rate
594
+ }
595
+ except Exception as e:
596
+ print(f"计算EMA({period})因子时出错: {e}")
597
+
598
+ return results if results else None
599
+
600
+ def calculate_normalized_alpha(factor_key, factor_func, min_req, alpha_num, **kwargs):
601
+ """计算归一化Alpha因子的通用函数"""
602
+ try:
603
+ normalized_result = calculate_normalized_alpha_factor(
604
+ data_slice=data_slice,
605
+ factor_func=factor_func,
606
+ factor_name=f"Alpha#{alpha_num}",
607
+ min_required=min_req,
608
+ lookback_periods=30,
609
+ **kwargs
610
+ )
611
+ if normalized_result:
612
+ def normalized_wrapper(d, func=factor_func, req=min_req, num=alpha_num, kw=kwargs):
613
+ norm_res = calculate_normalized_alpha_factor(
614
+ data_slice=d,
615
+ factor_func=func,
616
+ factor_name=f"Alpha#{num}",
617
+ min_required=req,
618
+ lookback_periods=30,
619
+ **kw
620
+ )
621
+ if norm_res:
622
+ return norm_res['value']
623
+ return 0.0
624
+
625
+ win_rate = calculate_factor_win_rate(
626
+ data_df=data_df,
627
+ factor_func=normalized_wrapper,
628
+ forward_periods=forward_periods,
629
+ min_periods=65,
630
+ factor_name=f"归一化Alpha#{alpha_num}因子"
631
+ )
632
+ normalized_result['win_rate'] = win_rate
633
+ return (f'normalized_{factor_key}', normalized_result)
634
+ except Exception as e:
635
+ print(f"计算{factor_key}因子时出错: {e}")
636
+ return None
637
+
638
+ # 准备所有因子计算任务
639
+ tasks = []
640
+
641
+ # MA因子和RSI因子
642
+ if len(data_slice) >= 6:
643
+ tasks.append(calculate_ma_factor)
644
+ if len(data_slice) >= 16:
645
+ tasks.append(calculate_rsi_factor)
646
+
647
+ # 新增技术指标因子
648
+ if len(data_slice) >= 18:
649
+ tasks.append(calculate_stochastic_k_factor)
650
+ if len(data_slice) >= 21:
651
+ tasks.append(calculate_cci_factor)
652
+ if len(data_slice) >= 30:
653
+ tasks.append(calculate_adx_factor)
654
+ if len(data_slice) >= 36:
655
+ tasks.append(calculate_ao_factor)
656
+ if len(data_slice) >= 11:
657
+ tasks.append(calculate_momentum_factor)
658
+ if len(data_slice) >= 48:
659
+ tasks.append(calculate_macd_factor)
660
+ if len(data_slice) >= 35:
661
+ tasks.append(calculate_stochastic_tsi_factor)
662
+ if len(data_slice) >= 15:
663
+ tasks.append(calculate_williams_r_factor)
664
+ if len(data_slice) >= 14:
665
+ tasks.append(calculate_bbp_factor)
666
+ if len(data_slice) >= 29:
667
+ tasks.append(calculate_uo_factor)
668
+
669
+ # EMA因子(多个周期)
670
+ def calculate_ema_wrapper():
671
+ ema_results = calculate_ema_factors()
672
+ if ema_results:
673
+ return list(ema_results.items())
674
+ return None
675
+
676
+ if len(data_slice) >= 11:
677
+ tasks.append(calculate_ema_wrapper)
678
+
679
+ # 归一化Alpha因子(选择一些常用的)
680
+ alpha_factors_to_add = [
681
+ ('alpha1', alpha1_factor, 30, '1', {'lookback_days': 5, 'stddev_period': 20, 'power': 2.0}),
682
+ ('alpha3', alpha3_factor, 30, '3', {}),
683
+ ('alpha7', alpha7_factor, 30, '7', {}),
684
+ ('alpha9', alpha9_factor, 30, '9', {}),
685
+ ('alpha11', alpha11_factor, 30, '11', {}),
686
+ ('alpha15', alpha15_factor, 30, '15', {}),
687
+ ('alpha17', alpha17_factor, 30, '17', {}),
688
+ ('alpha21', alpha21_factor, 30, '21', {}),
689
+ ('alpha23', alpha23_factor, 30, '23', {}),
690
+ ('alpha25', alpha25_factor, 30, '25', {}),
691
+ ('alpha29', alpha29_factor, 30, '29', {}),
692
+ ('alpha33', alpha33_factor, 30, '33', {}),
693
+ ('alpha34', alpha34_factor, 30, '34', {}),
694
+ ]
695
+
696
+ for factor_key, factor_func, min_req, alpha_num, kwargs in alpha_factors_to_add:
697
+ # 使用默认参数捕获循环变量,避免闭包问题
698
+ tasks.append(lambda k=factor_key, f=factor_func, r=min_req, n=alpha_num, kw=kwargs:
699
+ calculate_normalized_alpha(k, f, r, n, **kw))
700
+
701
+ # 使用多线程并行计算所有因子
702
+ with ThreadPoolExecutor() as executor:
703
+ futures = [executor.submit(task) for task in tasks]
704
+ for future in futures:
705
+ try:
706
+ task_result = future.result()
707
+ if task_result is not None:
708
+ # 处理EMA因子返回的多个结果
709
+ if isinstance(task_result, list):
710
+ for key, value in task_result:
711
+ result[key] = value
712
+ else:
713
+ key, value = task_result
714
+ result[key] = value
715
+ except Exception as e:
716
+ print(f"计算因子任务时出错: {e}")
717
+
718
+ except Exception as e:
719
+ print(f"计算因子值时出错: {e}")
720
+ import traceback
721
+ traceback.print_exc()
722
+
723
+ return result
724
+
725
+
726
+ def print_factor_results(factor_results: Dict[str, Any]):
727
+ """
728
+ 打印因子结果
729
+
730
+ Args:
731
+ factor_results: 因子结果字典
732
+ """
733
+ print("\n" + "="*100)
734
+ print("📊 因子分析结果")
735
+ print("="*100)
736
+
737
+ if not factor_results:
738
+ print("未计算出任何因子结果")
739
+ return
740
+
741
+ # 按因子类型分组显示
742
+ print("\n【基础因子】")
743
+ print("-"*100)
744
+
745
+ # MA因子
746
+ if 'ma_factor_5' in factor_results:
747
+ ma_info = factor_results['ma_factor_5']
748
+ signal_emoji = "🟢" if ma_info.get('signal') == '看多' else "🔴" if ma_info.get('signal') == '看空' else "⚪"
749
+ signal_text = ma_info.get('signal', '中性')
750
+ print(f"MA5因子:")
751
+ print(f" 因子值: {ma_info.get('raw_value', 0):+.6f}")
752
+ print(f" 信号: {signal_emoji} {signal_text}")
753
+ if ma_info.get('win_rate'):
754
+ wr = ma_info['win_rate']
755
+ if wr and isinstance(wr, dict):
756
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
757
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
758
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
759
+ print()
760
+
761
+ # RSI因子
762
+ if 'rsi_factor_14' in factor_results:
763
+ rsi_info = factor_results['rsi_factor_14']
764
+ signal_emoji = "🟢" if rsi_info.get('signal') == '看多' else "🔴" if rsi_info.get('signal') == '看空' else "⚪"
765
+ signal_text = rsi_info.get('signal', '中性')
766
+ print(f"RSI14因子:")
767
+ print(f" 因子值: {rsi_info.get('raw_value', 0):+.6f}")
768
+ print(f" 信号: {signal_emoji} {signal_text}")
769
+ if rsi_info.get('win_rate'):
770
+ wr = rsi_info['win_rate']
771
+ if wr and isinstance(wr, dict):
772
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
773
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
774
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
775
+ print()
776
+
777
+ # Stochastic %K因子
778
+ if 'stochastic_k_factor_14_3_3' in factor_results:
779
+ stoch_info = factor_results['stochastic_k_factor_14_3_3']
780
+ signal_emoji = "🟢" if stoch_info.get('signal') == '看多' else "🔴" if stoch_info.get('signal') == '看空' else "⚪"
781
+ signal_text = stoch_info.get('signal', '中性')
782
+ print(f"Stochastic %K(14,3,3)因子:")
783
+ print(f" 因子值: {stoch_info.get('raw_value', 0):+.6f}")
784
+ print(f" 信号: {signal_emoji} {signal_text}")
785
+ if stoch_info.get('win_rate'):
786
+ wr = stoch_info['win_rate']
787
+ if wr and isinstance(wr, dict):
788
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
789
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
790
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
791
+ print()
792
+
793
+ # CCI因子
794
+ if 'cci_factor_20' in factor_results:
795
+ cci_info = factor_results['cci_factor_20']
796
+ signal_emoji = "🟢" if cci_info.get('signal') == '看多' else "🔴" if cci_info.get('signal') == '看空' else "⚪"
797
+ signal_text = cci_info.get('signal', '中性')
798
+ print(f"CCI(20)因子:")
799
+ print(f" 因子值: {cci_info.get('raw_value', 0):+.6f}")
800
+ print(f" 信号: {signal_emoji} {signal_text}")
801
+ if cci_info.get('win_rate'):
802
+ wr = cci_info['win_rate']
803
+ if wr and isinstance(wr, dict):
804
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
805
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
806
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
807
+ print()
808
+
809
+ # ADX因子
810
+ if 'adx_factor_14' in factor_results:
811
+ adx_info = factor_results['adx_factor_14']
812
+ signal_emoji = "🟢" if adx_info.get('signal') == '看多' else "🔴" if adx_info.get('signal') == '看空' else "⚪"
813
+ signal_text = adx_info.get('signal', '中性')
814
+ print(f"ADX(14)因子:")
815
+ print(f" 因子值: {adx_info.get('raw_value', 0):+.6f}")
816
+ print(f" 信号: {signal_emoji} {signal_text}")
817
+ if adx_info.get('win_rate'):
818
+ wr = adx_info['win_rate']
819
+ if wr and isinstance(wr, dict):
820
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
821
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
822
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
823
+ print()
824
+
825
+ # AO因子
826
+ if 'ao_factor' in factor_results:
827
+ ao_info = factor_results['ao_factor']
828
+ signal_emoji = "🟢" if ao_info.get('signal') == '看多' else "🔴" if ao_info.get('signal') == '看空' else "⚪"
829
+ signal_text = ao_info.get('signal', '中性')
830
+ print(f"AO因子:")
831
+ print(f" 因子值: {ao_info.get('raw_value', 0):+.6f}")
832
+ print(f" 信号: {signal_emoji} {signal_text}")
833
+ if ao_info.get('win_rate'):
834
+ wr = ao_info['win_rate']
835
+ if wr and isinstance(wr, dict):
836
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
837
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
838
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
839
+ print()
840
+
841
+ # Momentum因子
842
+ if 'momentum_factor_10' in factor_results:
843
+ momentum_info = factor_results['momentum_factor_10']
844
+ signal_emoji = "🟢" if momentum_info.get('signal') == '看多' else "🔴" if momentum_info.get('signal') == '看空' else "⚪"
845
+ signal_text = momentum_info.get('signal', '中性')
846
+ print(f"Momentum(10)因子:")
847
+ print(f" 因子值: {momentum_info.get('raw_value', 0):+.6f}")
848
+ print(f" 信号: {signal_emoji} {signal_text}")
849
+ if momentum_info.get('win_rate'):
850
+ wr = momentum_info['win_rate']
851
+ if wr and isinstance(wr, dict):
852
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
853
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
854
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
855
+ print()
856
+
857
+ # MACD因子
858
+ if 'macd_factor_12_26' in factor_results:
859
+ macd_info = factor_results['macd_factor_12_26']
860
+ signal_emoji = "🟢" if macd_info.get('signal') == '看多' else "🔴" if macd_info.get('signal') == '看空' else "⚪"
861
+ signal_text = macd_info.get('signal', '中性')
862
+ print(f"MACD(12,26)因子:")
863
+ print(f" 因子值: {macd_info.get('raw_value', 0):+.6f}")
864
+ print(f" 信号: {signal_emoji} {signal_text}")
865
+ if macd_info.get('win_rate'):
866
+ wr = macd_info['win_rate']
867
+ if wr and isinstance(wr, dict):
868
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
869
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
870
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
871
+ print()
872
+
873
+ # Stochastic TSI因子
874
+ if 'stochastic_tsi_factor_3_3_14_14' in factor_results:
875
+ stoch_tsi_info = factor_results['stochastic_tsi_factor_3_3_14_14']
876
+ signal_emoji = "🟢" if stoch_tsi_info.get('signal') == '看多' else "🔴" if stoch_tsi_info.get('signal') == '看空' else "⚪"
877
+ signal_text = stoch_tsi_info.get('signal', '中性')
878
+ print(f"Stochastic TSI Fast(3,3,14,14)因子:")
879
+ print(f" 因子值: {stoch_tsi_info.get('raw_value', 0):+.6f}")
880
+ print(f" 信号: {signal_emoji} {signal_text}")
881
+ if stoch_tsi_info.get('win_rate'):
882
+ wr = stoch_tsi_info['win_rate']
883
+ if wr and isinstance(wr, dict):
884
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
885
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
886
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
887
+ print()
888
+
889
+ # Williams %R因子
890
+ if 'williams_r_factor_14' in factor_results:
891
+ williams_r_info = factor_results['williams_r_factor_14']
892
+ signal_emoji = "🟢" if williams_r_info.get('signal') == '看多' else "🔴" if williams_r_info.get('signal') == '看空' else "⚪"
893
+ signal_text = williams_r_info.get('signal', '中性')
894
+ print(f"Williams %R(14)因子:")
895
+ print(f" 因子值: {williams_r_info.get('raw_value', 0):+.6f}")
896
+ print(f" 信号: {signal_emoji} {signal_text}")
897
+ if williams_r_info.get('win_rate'):
898
+ wr = williams_r_info['win_rate']
899
+ if wr and isinstance(wr, dict):
900
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
901
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
902
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
903
+ print()
904
+
905
+ # BBP因子
906
+ if 'bbp_factor' in factor_results:
907
+ bbp_info = factor_results['bbp_factor']
908
+ signal_emoji = "🟢" if bbp_info.get('signal') == '看多' else "🔴" if bbp_info.get('signal') == '看空' else "⚪"
909
+ signal_text = bbp_info.get('signal', '中性')
910
+ print(f"BBP因子:")
911
+ print(f" 因子值: {bbp_info.get('raw_value', 0):+.6f}")
912
+ print(f" 信号: {signal_emoji} {signal_text}")
913
+ if bbp_info.get('win_rate'):
914
+ wr = bbp_info['win_rate']
915
+ if wr and isinstance(wr, dict):
916
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
917
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
918
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
919
+ print()
920
+
921
+ # UO因子
922
+ if 'uo_factor_7_14_28' in factor_results:
923
+ uo_info = factor_results['uo_factor_7_14_28']
924
+ signal_emoji = "🟢" if uo_info.get('signal') == '看多' else "🔴" if uo_info.get('signal') == '看空' else "⚪"
925
+ signal_text = uo_info.get('signal', '中性')
926
+ print(f"UO(7,14,28)因子:")
927
+ print(f" 因子值: {uo_info.get('raw_value', 0):+.6f}")
928
+ print(f" 信号: {signal_emoji} {signal_text}")
929
+ if uo_info.get('win_rate'):
930
+ wr = uo_info['win_rate']
931
+ if wr and isinstance(wr, dict):
932
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
933
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
934
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
935
+ print()
936
+
937
+ # EMA因子(多个周期)
938
+ ema_factors = [k for k in factor_results.keys() if k.startswith('ema_factor_')]
939
+ if ema_factors:
940
+ print("\n【EMA因子】")
941
+ print("-"*100)
942
+ # 按周期排序
943
+ ema_factors.sort(key=lambda x: int(x.replace('ema_factor_', '')))
944
+ for factor_key in ema_factors:
945
+ ema_info = factor_results[factor_key]
946
+ period = factor_key.replace('ema_factor_', '')
947
+ signal_emoji = "🟢" if ema_info.get('signal') == '看多' else "🔴" if ema_info.get('signal') == '看空' else "⚪"
948
+ signal_text = ema_info.get('signal', '中性')
949
+ print(f"EMA({period})因子:")
950
+ print(f" 因子值: {ema_info.get('raw_value', 0):+.6f}")
951
+ print(f" 信号: {signal_emoji} {signal_text}")
952
+ if ema_info.get('win_rate'):
953
+ wr = ema_info['win_rate']
954
+ if wr and isinstance(wr, dict):
955
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
956
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
957
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
958
+ print()
959
+
960
+ # 归一化Alpha因子
961
+ print("\n【归一化Alpha因子】")
962
+ print("-"*100)
963
+
964
+ normalized_alpha_factors = [k for k in factor_results.keys() if k.startswith('normalized_alpha')]
965
+ if normalized_alpha_factors:
966
+ # 按Alpha编号排序
967
+ normalized_alpha_factors.sort(key=lambda x: int(x.replace('normalized_alpha', '')))
968
+
969
+ for factor_key in normalized_alpha_factors:
970
+ alpha_info = factor_results[factor_key]
971
+ alpha_num = factor_key.replace('normalized_alpha', '')
972
+ signal_emoji = "🟢" if alpha_info.get('signal') == '看多' else "🔴" if alpha_info.get('signal') == '看空' else "⚪"
973
+ signal_text = alpha_info.get('signal', '中性')
974
+
975
+ print(f"归一化Alpha#{alpha_num}:")
976
+ print(f" 原始值: {alpha_info.get('raw_value', 0):+.6f}")
977
+ print(f" 归一化值: {alpha_info.get('value', 0):+.4f}")
978
+ print(f" 信号: {signal_emoji} {signal_text}")
979
+ if alpha_info.get('win_rate'):
980
+ wr = alpha_info['win_rate']
981
+ if wr and isinstance(wr, dict):
982
+ print(f" 看多胜率: {wr.get('long_win_rate', 0):.2%} (样本={wr.get('long_signals', 0)})")
983
+ print(f" 看空胜率: {wr.get('short_win_rate', 0):.2%} (样本={wr.get('short_signals', 0)})")
984
+ print(f" 总体胜率: {wr.get('overall_win_rate', 0):.2%} (总样本={wr.get('total_samples', 0)})")
985
+ print()
986
+ else:
987
+ print("未计算出归一化Alpha因子结果")
988
+
989
+ print("="*100)
990
+
991
+
992
+ def main():
993
+ """
994
+ 主函数
995
+ """
996
+ parser = argparse.ArgumentParser(description='计算当前币对合约各个factor的值、看多/看空信号和胜率')
997
+ parser.add_argument('--symbol', type=str, default='ETHUSDT', help='交易对符号,例如 BTCUSDT, ETHUSDT')
998
+ parser.add_argument('--interval', type=str, default='5m', help='时间间隔,例如 1m, 5m, 30m, 1h, 1d')
999
+ parser.add_argument('--lookback', type=int, default=500, help='回看周期数(获取多少条历史数据)')
1000
+ parser.add_argument('--forward', type=int, default=1, help='向前看周期数(用于计算胜率)')
1001
+
1002
+ args = parser.parse_args()
1003
+
1004
+ # 设置用户(如果需要API认证)
1005
+ set_user()
1006
+
1007
+ print("="*100)
1008
+ print("📊 因子计算工具")
1009
+ print("="*100)
1010
+ print(f"交易对: {args.symbol}")
1011
+ print(f"时间间隔: {args.interval}")
1012
+ print(f"回看周期数: {args.lookback}")
1013
+ print(f"向前看周期数: {args.forward}")
1014
+ print("="*100)
1015
+
1016
+ # 计算开始时间和结束时间
1017
+ end_time = datetime.now()
1018
+ # 根据interval和lookback计算start_time
1019
+ interval_durations = {
1020
+ "1m": timedelta(minutes=1),
1021
+ "3m": timedelta(minutes=3),
1022
+ "5m": timedelta(minutes=5),
1023
+ "15m": timedelta(minutes=15),
1024
+ "30m": timedelta(minutes=30),
1025
+ "1h": timedelta(hours=1),
1026
+ "2h": timedelta(hours=2),
1027
+ "4h": timedelta(hours=4),
1028
+ "6h": timedelta(hours=6),
1029
+ "8h": timedelta(hours=8),
1030
+ "12h": timedelta(hours=12),
1031
+ "1d": timedelta(days=1),
1032
+ "3d": timedelta(days=3),
1033
+ "1w": timedelta(weeks=1),
1034
+ }
1035
+ interval_delta = interval_durations.get(args.interval, timedelta(hours=1))
1036
+ start_time = end_time - interval_delta * args.lookback
1037
+
1038
+ print(f"\n正在获取数据...")
1039
+ print(f" 开始时间: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
1040
+ print(f" 结束时间: {end_time.strftime('%Y-%m-%d %H:%M:%S')}")
1041
+
1042
+ # 获取数据(使用临时目录,但不保存文件)
1043
+ with tempfile.TemporaryDirectory() as temp_dir:
1044
+ klines_data = get_and_save_futures_klines(
1045
+ symbol=args.symbol,
1046
+ interval=args.interval,
1047
+ start_time=start_time,
1048
+ end_time=end_time,
1049
+ output_dir=temp_dir,
1050
+ save_csv=False,
1051
+ save_json=False
1052
+ )
1053
+
1054
+ if not klines_data:
1055
+ print("❌ 获取数据失败")
1056
+ return
1057
+
1058
+ print(f"✅ 成功获取 {len(klines_data)} 条数据")
1059
+
1060
+ # 转换为DataFrame
1061
+ data_df = klines_to_dataframe(klines_data)
1062
+
1063
+ if len(data_df) == 0:
1064
+ print("❌ 数据为空")
1065
+ return
1066
+
1067
+ print(f"✅ 数据已转换为DataFrame,共 {len(data_df)} 行")
1068
+ print(f" 时间范围: {data_df.iloc[0]['open_time_str']} 至 {data_df.iloc[-1]['open_time_str']}")
1069
+
1070
+ # 计算所有因子
1071
+ print(f"\n正在计算因子...")
1072
+ factor_results = calculate_all_factors(data_df,forward_periods=3)
1073
+
1074
+ # 打印结果
1075
+ print_factor_results(factor_results)
1076
+
1077
+ print("\n✅ 计算完成!")
1078
+
1079
+
1080
+ if __name__ == "__main__":
1081
+ main()
1082
+