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.
- cyqnt_trd/CHANGELOG_0.1.7.md +111 -0
- cyqnt_trd/__init__.py +1 -1
- cyqnt_trd/backtesting/factor_test.py +3 -2
- cyqnt_trd/get_data/__init__.py +16 -1
- cyqnt_trd/get_data/get_futures_data.py +3 -3
- cyqnt_trd/get_data/get_kline_with_factor.py +808 -0
- cyqnt_trd/get_data/get_web3_trending_data.py +389 -0
- cyqnt_trd/online_trading/__init__.py +1 -0
- cyqnt_trd/online_trading/realtime_price_tracker.py +142 -2
- cyqnt_trd/trading_signal/example_usage.py +123 -7
- cyqnt_trd/trading_signal/factor/__init__.py +23 -0
- cyqnt_trd/trading_signal/factor/adx_factor.py +116 -0
- cyqnt_trd/trading_signal/factor/ao_factor.py +66 -0
- cyqnt_trd/trading_signal/factor/bbp_factor.py +68 -0
- cyqnt_trd/trading_signal/factor/cci_factor.py +65 -0
- cyqnt_trd/trading_signal/factor/ema_factor.py +102 -0
- cyqnt_trd/trading_signal/factor/macd_factor.py +97 -0
- cyqnt_trd/trading_signal/factor/momentum_factor.py +44 -0
- cyqnt_trd/trading_signal/factor/stochastic_factor.py +76 -0
- cyqnt_trd/trading_signal/factor/stochastic_tsi_factor.py +129 -0
- cyqnt_trd/trading_signal/factor/uo_factor.py +92 -0
- cyqnt_trd/trading_signal/factor/williams_r_factor.py +60 -0
- cyqnt_trd/trading_signal/factor_details.json +107 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha1.py +4 -2
- cyqnt_trd/trading_signal/selected_alpha/alpha15.py +4 -2
- cyqnt_trd/trading_signal/selected_alpha/generate_alphas.py +1 -0
- {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/METADATA +16 -12
- {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/RECORD +34 -23
- {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/WHEEL +1 -1
- test/real_time_trade.py +467 -10
- test/test_now_factor.py +1082 -0
- test/track_k_line_continue.py +372 -0
- cyqnt_trd/test_script/get_symbols_by_volume.py +0 -227
- cyqnt_trd/test_script/test_alpha.py +0 -261
- cyqnt_trd/test_script/test_kline_data.py +0 -479
- test/test_example_usage.py +0 -381
- test/test_get_data.py +0 -310
- test/test_realtime_price_tracker.py +0 -546
- {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {cyqnt_trd-0.1.2.dist-info → cyqnt_trd-0.1.7.dist-info}/top_level.txt +0 -0
test/real_time_trade.py
CHANGED
|
@@ -18,11 +18,16 @@ import os
|
|
|
18
18
|
import sys
|
|
19
19
|
import asyncio
|
|
20
20
|
import logging
|
|
21
|
+
import warnings
|
|
21
22
|
from pathlib import Path
|
|
22
23
|
from datetime import datetime
|
|
23
|
-
from typing import Optional, Dict, Any
|
|
24
|
+
from typing import Optional, Dict, Any, Callable
|
|
25
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
24
26
|
from cyqnt_trd.utils import set_user
|
|
25
27
|
|
|
28
|
+
# 抑制 pandas FutureWarning 关于 fillna 的警告
|
|
29
|
+
warnings.filterwarnings('ignore', category=FutureWarning, message='.*Downcasting object dtype arrays on .fillna.*')
|
|
30
|
+
|
|
26
31
|
# 添加项目根目录到路径
|
|
27
32
|
project_root = Path(__file__).parent.parent
|
|
28
33
|
if str(project_root) not in sys.path:
|
|
@@ -42,10 +47,17 @@ try:
|
|
|
42
47
|
cancel_futures_order
|
|
43
48
|
)
|
|
44
49
|
from cyqnt_trd.trading_signal.signal.ma_signal import ma_signal, ma_cross_signal
|
|
45
|
-
from cyqnt_trd.trading_signal.signal.factor_based_signal import factor_based_signal
|
|
50
|
+
from cyqnt_trd.trading_signal.signal.factor_based_signal import factor_based_signal, normalized_factor_signal
|
|
46
51
|
from cyqnt_trd.trading_signal.factor.ma_factor import ma_factor
|
|
47
52
|
from cyqnt_trd.trading_signal.factor.rsi_factor import rsi_factor
|
|
48
|
-
from cyqnt_trd.trading_signal.selected_alpha
|
|
53
|
+
from cyqnt_trd.trading_signal.selected_alpha import (
|
|
54
|
+
alpha1_factor, alpha3_factor, alpha7_factor, alpha9_factor,
|
|
55
|
+
alpha11_factor, alpha15_factor, alpha17_factor, alpha21_factor,
|
|
56
|
+
alpha23_factor, alpha25_factor, alpha29_factor, alpha33_factor,
|
|
57
|
+
alpha34_factor
|
|
58
|
+
)
|
|
59
|
+
from cyqnt_trd.backtesting.factor_test import FactorTester
|
|
60
|
+
import numpy as np
|
|
49
61
|
except ImportError as e:
|
|
50
62
|
print(f"导入错误: {e}")
|
|
51
63
|
print("\n提示:请确保已安装 cyqnt_trd package: pip install -e /path/to/crypto_trading")
|
|
@@ -147,6 +159,360 @@ class RealTimeTradingBot:
|
|
|
147
159
|
logger.warning("⚠️ 实盘模式:将执行真实订单!")
|
|
148
160
|
logger.warning("="*80)
|
|
149
161
|
|
|
162
|
+
def _calculate_normalized_alpha_factor(
|
|
163
|
+
self,
|
|
164
|
+
data_slice,
|
|
165
|
+
factor_func: Callable,
|
|
166
|
+
factor_name: str,
|
|
167
|
+
min_required: int = 30,
|
|
168
|
+
lookback_periods: int = 30,
|
|
169
|
+
**factor_kwargs
|
|
170
|
+
) -> Optional[Dict[str, Any]]:
|
|
171
|
+
"""
|
|
172
|
+
计算归一化Alpha因子的通用函数
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
data_slice: 数据切片
|
|
176
|
+
factor_func: 因子计算函数
|
|
177
|
+
factor_name: 因子名称(用于日志)
|
|
178
|
+
min_required: 因子计算所需的最小周期数
|
|
179
|
+
lookback_periods: 归一化回看周期数
|
|
180
|
+
**factor_kwargs: 传递给因子函数的额外参数
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
包含因子值和看多/看空结果的字典,如果计算失败则返回None
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
if len(data_slice) < min_required + 2:
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
available_periods = len(data_slice) - min_required - 1
|
|
190
|
+
if available_periods < 2:
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
actual_lookback = min(lookback_periods, max(2, available_periods))
|
|
194
|
+
|
|
195
|
+
# 计算因子值:当前周期和之前actual_lookback个周期(使用多线程并行计算)
|
|
196
|
+
def compute_factor_value(i):
|
|
197
|
+
"""计算单个时间点的因子值"""
|
|
198
|
+
end_idx = len(data_slice) - i
|
|
199
|
+
start_idx = max(0, end_idx - min_required - 1)
|
|
200
|
+
if end_idx <= start_idx:
|
|
201
|
+
return 0.0
|
|
202
|
+
|
|
203
|
+
period_slice = data_slice.iloc[start_idx:end_idx]
|
|
204
|
+
try:
|
|
205
|
+
# 调用因子函数,传入额外参数
|
|
206
|
+
factor_value = factor_func(period_slice, **factor_kwargs)
|
|
207
|
+
if factor_value is not None:
|
|
208
|
+
return factor_value
|
|
209
|
+
else:
|
|
210
|
+
return 0.0
|
|
211
|
+
except Exception:
|
|
212
|
+
return 0.0
|
|
213
|
+
|
|
214
|
+
# 使用多线程并行计算因子值
|
|
215
|
+
indices = list(range(actual_lookback + 1))
|
|
216
|
+
factor_values = []
|
|
217
|
+
with ThreadPoolExecutor() as executor:
|
|
218
|
+
# 返回顺序保证和原for循环一致
|
|
219
|
+
factor_values = list(executor.map(compute_factor_value, indices))
|
|
220
|
+
|
|
221
|
+
if len(factor_values) < 2:
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
# 归一化
|
|
225
|
+
factor_array = np.array(factor_values)
|
|
226
|
+
factor_min = factor_array.min()
|
|
227
|
+
factor_max = factor_array.max()
|
|
228
|
+
|
|
229
|
+
if factor_max == factor_min:
|
|
230
|
+
normalized_factors = np.zeros_like(factor_array)
|
|
231
|
+
else:
|
|
232
|
+
# Min-Max归一化到[-1, 1]区间
|
|
233
|
+
normalized_factors = 2 * (factor_array - factor_min) / (factor_max - factor_min) - 1
|
|
234
|
+
|
|
235
|
+
current_normalized = float(normalized_factors[0])
|
|
236
|
+
prev_normalized = float(normalized_factors[1]) if len(normalized_factors) > 1 else 0.0
|
|
237
|
+
|
|
238
|
+
# 判断信号:从负转正看多,从正转负看空
|
|
239
|
+
if prev_normalized <= 0 and current_normalized > 0:
|
|
240
|
+
signal = '看多'
|
|
241
|
+
elif prev_normalized >= 0 and current_normalized < 0:
|
|
242
|
+
signal = '看空'
|
|
243
|
+
else:
|
|
244
|
+
signal = '中性'
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
'value': current_normalized,
|
|
248
|
+
'signal': signal,
|
|
249
|
+
'raw_value': float(factor_values[0]) if len(factor_values) > 0 else 0.0,
|
|
250
|
+
'prev_normalized': prev_normalized
|
|
251
|
+
}
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.debug(f"计算归一化{factor_name}因子时出错: {e}")
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
def _calculate_factor_win_rate(
|
|
257
|
+
self,
|
|
258
|
+
data_df,
|
|
259
|
+
factor_func: Callable,
|
|
260
|
+
forward_periods: int = 24,
|
|
261
|
+
min_periods: int = 30,
|
|
262
|
+
factor_name: str = "factor"
|
|
263
|
+
) -> Optional[Dict[str, float]]:
|
|
264
|
+
"""
|
|
265
|
+
计算因子基于历史数据的胜率(使用FactorTester.test_factor)
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
data_df: 历史数据DataFrame
|
|
269
|
+
factor_func: 因子计算函数,接受数据切片作为参数,返回因子值
|
|
270
|
+
forward_periods: 向前看的周期数(默认24,即未来24个周期)
|
|
271
|
+
min_periods: 最小需要的周期数
|
|
272
|
+
factor_name: 因子名称
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
包含胜率信息的字典,如果计算失败则返回None
|
|
276
|
+
"""
|
|
277
|
+
try:
|
|
278
|
+
if len(data_df) < min_periods + forward_periods + 1:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
# 创建FactorTester实例
|
|
282
|
+
factor_tester = FactorTester(data_df)
|
|
283
|
+
|
|
284
|
+
# 调用test_factor计算胜率
|
|
285
|
+
test_results = factor_tester.test_factor(
|
|
286
|
+
factor_func=factor_func,
|
|
287
|
+
forward_periods=forward_periods,
|
|
288
|
+
min_periods=min_periods,
|
|
289
|
+
factor_name=factor_name
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# 提取需要的胜率信息
|
|
293
|
+
result = {
|
|
294
|
+
'long_win_rate': test_results.get('long_win_rate', 0.0),
|
|
295
|
+
'short_win_rate': test_results.get('short_win_rate', 0.0),
|
|
296
|
+
'overall_win_rate': test_results.get('overall_win_rate', 0.0),
|
|
297
|
+
'long_avg_return': test_results.get('long_avg_return', 0.0),
|
|
298
|
+
'short_avg_return': test_results.get('short_avg_return', 0.0),
|
|
299
|
+
'long_signals': test_results.get('long_signals', 0),
|
|
300
|
+
'short_signals': test_results.get('short_signals', 0),
|
|
301
|
+
'total_samples': test_results.get('total_samples', 0)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return result
|
|
305
|
+
|
|
306
|
+
except Exception as e:
|
|
307
|
+
logger.debug(f"计算因子胜率时出错: {e}")
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
def _calculate_factor_values(self, data_df) -> Dict[str, Any]:
|
|
311
|
+
"""
|
|
312
|
+
计算各种因子的因子值和看多/看空结果(使用多线程并行计算)
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
data_df: 历史数据DataFrame
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
包含因子值和看多/看空结果的字典
|
|
319
|
+
"""
|
|
320
|
+
result = {}
|
|
321
|
+
|
|
322
|
+
if len(data_df) < 10:
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
# 使用足够的数据切片(对于alpha因子,需要更多数据)
|
|
326
|
+
# 至少需要65个周期(30+30+5缓冲)用于归一化alpha因子计算
|
|
327
|
+
min_slice_size = 65
|
|
328
|
+
if len(data_df) >= min_slice_size:
|
|
329
|
+
data_slice = data_df.iloc[-min_slice_size:].copy()
|
|
330
|
+
elif len(data_df) >= 30:
|
|
331
|
+
data_slice = data_df.iloc[-30:].copy()
|
|
332
|
+
else:
|
|
333
|
+
data_slice = data_df.copy()
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
# 定义所有需要计算的因子任务
|
|
337
|
+
def calculate_ma_factor():
|
|
338
|
+
"""计算MA因子"""
|
|
339
|
+
if len(data_slice) < 6:
|
|
340
|
+
return None
|
|
341
|
+
try:
|
|
342
|
+
ma_factor_value = ma_factor(data_slice, period=5)
|
|
343
|
+
ma_win_rate = self._calculate_factor_win_rate(
|
|
344
|
+
data_df=data_df,
|
|
345
|
+
factor_func=lambda d: ma_factor(d, period=5),
|
|
346
|
+
forward_periods=2,
|
|
347
|
+
min_periods=6,
|
|
348
|
+
factor_name="MA5因子"
|
|
349
|
+
)
|
|
350
|
+
return ('ma_factor_5', {
|
|
351
|
+
'value': ma_factor_value,
|
|
352
|
+
'signal': '看多' if ma_factor_value > 0 else '看空' if ma_factor_value < 0 else '中性',
|
|
353
|
+
'raw_value': ma_factor_value,
|
|
354
|
+
'win_rate': ma_win_rate
|
|
355
|
+
})
|
|
356
|
+
except Exception as e:
|
|
357
|
+
logger.debug(f"计算MA因子时出错: {e}")
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
def calculate_normalized_alpha1():
|
|
361
|
+
"""计算归一化Alpha#1因子"""
|
|
362
|
+
normalized_result = self._calculate_normalized_alpha_factor(
|
|
363
|
+
data_slice=data_slice,
|
|
364
|
+
factor_func=alpha1_factor,
|
|
365
|
+
factor_name="Alpha#1",
|
|
366
|
+
min_required=30,
|
|
367
|
+
lookback_periods=30,
|
|
368
|
+
lookback_days=5,
|
|
369
|
+
stddev_period=20,
|
|
370
|
+
power=2.0
|
|
371
|
+
)
|
|
372
|
+
if normalized_result:
|
|
373
|
+
def normalized_alpha1_wrapper(d):
|
|
374
|
+
norm_res = self._calculate_normalized_alpha_factor(
|
|
375
|
+
data_slice=d,
|
|
376
|
+
factor_func=alpha1_factor,
|
|
377
|
+
factor_name="Alpha#1",
|
|
378
|
+
min_required=30,
|
|
379
|
+
lookback_periods=30,
|
|
380
|
+
lookback_days=5,
|
|
381
|
+
stddev_period=20,
|
|
382
|
+
power=2.0
|
|
383
|
+
)
|
|
384
|
+
if norm_res:
|
|
385
|
+
return norm_res['value']
|
|
386
|
+
return 0.0
|
|
387
|
+
|
|
388
|
+
alpha1_win_rate = self._calculate_factor_win_rate(
|
|
389
|
+
data_df=data_df,
|
|
390
|
+
factor_func=normalized_alpha1_wrapper,
|
|
391
|
+
forward_periods=2,
|
|
392
|
+
min_periods=65,
|
|
393
|
+
factor_name="归一化Alpha#1因子"
|
|
394
|
+
)
|
|
395
|
+
normalized_result['win_rate'] = alpha1_win_rate
|
|
396
|
+
return ('normalized_alpha1', normalized_result)
|
|
397
|
+
return None
|
|
398
|
+
|
|
399
|
+
def calculate_normalized_alpha15():
|
|
400
|
+
"""计算归一化Alpha#15因子"""
|
|
401
|
+
normalized_result = self._calculate_normalized_alpha_factor(
|
|
402
|
+
data_slice=data_slice,
|
|
403
|
+
factor_func=alpha15_factor,
|
|
404
|
+
factor_name="Alpha#15",
|
|
405
|
+
min_required=30,
|
|
406
|
+
lookback_periods=30
|
|
407
|
+
)
|
|
408
|
+
if normalized_result:
|
|
409
|
+
def normalized_alpha15_wrapper(d):
|
|
410
|
+
norm_res = self._calculate_normalized_alpha_factor(
|
|
411
|
+
data_slice=d,
|
|
412
|
+
factor_func=alpha15_factor,
|
|
413
|
+
factor_name="Alpha#15",
|
|
414
|
+
min_required=30,
|
|
415
|
+
lookback_periods=30
|
|
416
|
+
)
|
|
417
|
+
if norm_res:
|
|
418
|
+
return norm_res['value']
|
|
419
|
+
return 0.0
|
|
420
|
+
|
|
421
|
+
alpha15_win_rate = self._calculate_factor_win_rate(
|
|
422
|
+
data_df=data_df,
|
|
423
|
+
factor_func=normalized_alpha15_wrapper,
|
|
424
|
+
forward_periods=2,
|
|
425
|
+
min_periods=65,
|
|
426
|
+
factor_name="归一化Alpha#15因子"
|
|
427
|
+
)
|
|
428
|
+
normalized_result['win_rate'] = alpha15_win_rate
|
|
429
|
+
return ('normalized_alpha15', normalized_result)
|
|
430
|
+
return None
|
|
431
|
+
|
|
432
|
+
def calculate_normalized_alpha(factor_key, factor_func, min_req, alpha_num):
|
|
433
|
+
"""计算归一化Alpha因子的通用函数"""
|
|
434
|
+
try:
|
|
435
|
+
normalized_result = self._calculate_normalized_alpha_factor(
|
|
436
|
+
data_slice=data_slice,
|
|
437
|
+
factor_func=factor_func,
|
|
438
|
+
factor_name=f"Alpha#{alpha_num}",
|
|
439
|
+
min_required=min_req,
|
|
440
|
+
lookback_periods=30
|
|
441
|
+
)
|
|
442
|
+
if normalized_result:
|
|
443
|
+
def normalized_wrapper(d, func=factor_func, req=min_req, num=alpha_num):
|
|
444
|
+
norm_res = self._calculate_normalized_alpha_factor(
|
|
445
|
+
data_slice=d,
|
|
446
|
+
factor_func=func,
|
|
447
|
+
factor_name=f"Alpha#{num}",
|
|
448
|
+
min_required=req,
|
|
449
|
+
lookback_periods=30
|
|
450
|
+
)
|
|
451
|
+
if norm_res:
|
|
452
|
+
return norm_res['value']
|
|
453
|
+
return 0.0
|
|
454
|
+
|
|
455
|
+
win_rate = self._calculate_factor_win_rate(
|
|
456
|
+
data_df=data_df,
|
|
457
|
+
factor_func=normalized_wrapper,
|
|
458
|
+
forward_periods=2,
|
|
459
|
+
min_periods=65,
|
|
460
|
+
factor_name=f"归一化Alpha#{alpha_num}因子"
|
|
461
|
+
)
|
|
462
|
+
normalized_result['win_rate'] = win_rate
|
|
463
|
+
return (f'normalized_{factor_key}', normalized_result)
|
|
464
|
+
except Exception as e:
|
|
465
|
+
logger.debug(f"计算{factor_key}因子时出错: {e}")
|
|
466
|
+
return None
|
|
467
|
+
|
|
468
|
+
# 准备所有因子计算任务
|
|
469
|
+
tasks = []
|
|
470
|
+
|
|
471
|
+
# MA因子
|
|
472
|
+
if len(data_slice) >= 6:
|
|
473
|
+
tasks.append(calculate_ma_factor)
|
|
474
|
+
|
|
475
|
+
# 归一化Alpha#1和Alpha#15
|
|
476
|
+
tasks.append(calculate_normalized_alpha1)
|
|
477
|
+
tasks.append(calculate_normalized_alpha15)
|
|
478
|
+
|
|
479
|
+
# 其他归一化Alpha因子
|
|
480
|
+
alpha_factors_to_add = [
|
|
481
|
+
('alpha3', alpha3_factor, 30, '3'),
|
|
482
|
+
('alpha7', alpha7_factor, 30, '7'),
|
|
483
|
+
('alpha9', alpha9_factor, 30, '9'),
|
|
484
|
+
('alpha11', alpha11_factor, 30, '11'),
|
|
485
|
+
('alpha17', alpha17_factor, 30, '17'),
|
|
486
|
+
('alpha21', alpha21_factor, 30, '21'),
|
|
487
|
+
('alpha23', alpha23_factor, 30, '23'),
|
|
488
|
+
('alpha25', alpha25_factor, 30, '25'),
|
|
489
|
+
('alpha29', alpha29_factor, 30, '29'),
|
|
490
|
+
('alpha33', alpha33_factor, 30, '33'),
|
|
491
|
+
('alpha34', alpha34_factor, 30, '34'),
|
|
492
|
+
]
|
|
493
|
+
|
|
494
|
+
for factor_key, factor_func, min_req, alpha_num in alpha_factors_to_add:
|
|
495
|
+
# 使用默认参数捕获循环变量,避免闭包问题
|
|
496
|
+
tasks.append(lambda k=factor_key, f=factor_func, r=min_req, n=alpha_num:
|
|
497
|
+
calculate_normalized_alpha(k, f, r, n))
|
|
498
|
+
|
|
499
|
+
# 使用多线程并行计算所有因子
|
|
500
|
+
with ThreadPoolExecutor() as executor:
|
|
501
|
+
futures = [executor.submit(task) for task in tasks]
|
|
502
|
+
for future in futures:
|
|
503
|
+
try:
|
|
504
|
+
task_result = future.result()
|
|
505
|
+
if task_result is not None:
|
|
506
|
+
key, value = task_result
|
|
507
|
+
result[key] = value
|
|
508
|
+
except Exception as e:
|
|
509
|
+
logger.debug(f"计算因子任务时出错: {e}")
|
|
510
|
+
|
|
511
|
+
except Exception as e:
|
|
512
|
+
logger.debug(f"计算因子值时出错: {e}")
|
|
513
|
+
|
|
514
|
+
return result
|
|
515
|
+
|
|
150
516
|
def _calculate_signal(self, data_df) -> Optional[str]:
|
|
151
517
|
"""
|
|
152
518
|
根据策略计算交易信号
|
|
@@ -518,8 +884,8 @@ class RealTimeTradingBot:
|
|
|
518
884
|
# 计算交易信号
|
|
519
885
|
signal = self._calculate_signal(data_df)
|
|
520
886
|
|
|
521
|
-
#
|
|
522
|
-
self._display_status(current_time, current_price, signal)
|
|
887
|
+
# 显示状态(包含因子值)
|
|
888
|
+
self._display_status(current_time, current_price, signal, data_df)
|
|
523
889
|
|
|
524
890
|
# 检查止盈止损(如果有持仓)
|
|
525
891
|
if self.position > 0:
|
|
@@ -551,7 +917,7 @@ class RealTimeTradingBot:
|
|
|
551
917
|
self.last_signal = 'sell'
|
|
552
918
|
self.last_signal_time = datetime.now()
|
|
553
919
|
|
|
554
|
-
def _display_status(self, time_str: str, price: float, signal: Optional[str]):
|
|
920
|
+
def _display_status(self, time_str: str, price: float, signal: Optional[str], data_df=None):
|
|
555
921
|
"""
|
|
556
922
|
显示当前状态
|
|
557
923
|
|
|
@@ -559,6 +925,7 @@ class RealTimeTradingBot:
|
|
|
559
925
|
time_str: 时间字符串
|
|
560
926
|
price: 当前价格
|
|
561
927
|
signal: 交易信号
|
|
928
|
+
data_df: 历史数据DataFrame(用于计算因子值)
|
|
562
929
|
"""
|
|
563
930
|
# 计算统计信息
|
|
564
931
|
runtime = datetime.now() - self.start_time
|
|
@@ -586,6 +953,96 @@ class RealTimeTradingBot:
|
|
|
586
953
|
print(f"持仓: {self.position:.6f} | 入场价: {self.entry_price:.2f} | 浮动盈亏: {profit_pct:+.2f}%")
|
|
587
954
|
else:
|
|
588
955
|
print(f"持仓: 无")
|
|
956
|
+
|
|
957
|
+
# 计算并显示因子值
|
|
958
|
+
if data_df is not None:
|
|
959
|
+
try:
|
|
960
|
+
factor_results = self._calculate_factor_values(data_df)
|
|
961
|
+
if factor_results:
|
|
962
|
+
print(f"{'='*80}")
|
|
963
|
+
print(f"📈 因子分析:")
|
|
964
|
+
|
|
965
|
+
# MA因子
|
|
966
|
+
if 'ma_factor_5' in factor_results:
|
|
967
|
+
ma_info = factor_results['ma_factor_5']
|
|
968
|
+
signal_emoji = "🟢" if ma_info.get('signal') == '看多' else "🔴" if ma_info.get('signal') == '看空' else "⚪"
|
|
969
|
+
signal_text = ma_info.get('signal', '中性')
|
|
970
|
+
win_rate_info = ""
|
|
971
|
+
if ma_info.get('win_rate'):
|
|
972
|
+
wr = ma_info['win_rate']
|
|
973
|
+
if wr and isinstance(wr, dict):
|
|
974
|
+
if signal_text == '看多' and wr.get('long_win_rate') is not None:
|
|
975
|
+
win_rate_info = f" | 看多胜率={wr['long_win_rate']:.2%} (样本={wr.get('long_signals', 0)})"
|
|
976
|
+
elif signal_text == '看空' and wr.get('short_win_rate') is not None:
|
|
977
|
+
win_rate_info = f" | 看空胜率={wr['short_win_rate']:.2%} (样本={wr.get('short_signals', 0)})"
|
|
978
|
+
if wr.get('overall_win_rate') is not None:
|
|
979
|
+
win_rate_info += f" | 总体胜率={wr['overall_win_rate']:.2%}"
|
|
980
|
+
print(f" MA5因子: 因子值={ma_info.get('raw_value', 0):+.4f} | {signal_emoji} {signal_text}{win_rate_info}")
|
|
981
|
+
|
|
982
|
+
# # 归一化Alpha#1因子
|
|
983
|
+
# if 'normalized_alpha1' in factor_results:
|
|
984
|
+
# alpha1_info = factor_results['normalized_alpha1']
|
|
985
|
+
# signal_emoji = "🟢" if alpha1_info.get('signal') == '看多' else "🔴" if alpha1_info.get('signal') == '看空' else "⚪"
|
|
986
|
+
# signal_text = alpha1_info.get('signal', '中性')
|
|
987
|
+
# win_rate_info = ""
|
|
988
|
+
# if alpha1_info.get('win_rate'):
|
|
989
|
+
# wr = alpha1_info['win_rate']
|
|
990
|
+
# if wr and isinstance(wr, dict):
|
|
991
|
+
# if signal_text == '看多' and wr.get('long_win_rate') is not None:
|
|
992
|
+
# win_rate_info = f" | 看多胜率={wr['long_win_rate']:.2%} (样本={wr.get('long_signals', 0)})"
|
|
993
|
+
# elif signal_text == '看空' and wr.get('short_win_rate') is not None:
|
|
994
|
+
# win_rate_info = f" | 看空胜率={wr['short_win_rate']:.2%} (样本={wr.get('short_signals', 0)})"
|
|
995
|
+
# if wr.get('overall_win_rate') is not None:
|
|
996
|
+
# win_rate_info += f" | 总体胜率={wr['overall_win_rate']:.2%}"
|
|
997
|
+
# print(f" 归一化Alpha#1: 原始值={alpha1_info.get('raw_value', 0):+.6f} | 归一化值={alpha1_info.get('value', 0):+.4f} | {signal_emoji} {signal_text}{win_rate_info}")
|
|
998
|
+
|
|
999
|
+
# # 归一化Alpha#15因子
|
|
1000
|
+
# if 'normalized_alpha15' in factor_results:
|
|
1001
|
+
# alpha15_info = factor_results['normalized_alpha15']
|
|
1002
|
+
# signal_emoji = "🟢" if alpha15_info.get('signal') == '看多' else "🔴" if alpha15_info.get('signal') == '看空' else "⚪"
|
|
1003
|
+
# signal_text = alpha15_info.get('signal', '中性')
|
|
1004
|
+
# win_rate_info = ""
|
|
1005
|
+
# if alpha15_info.get('win_rate'):
|
|
1006
|
+
# wr = alpha15_info['win_rate']
|
|
1007
|
+
# if wr and isinstance(wr, dict):
|
|
1008
|
+
# if signal_text == '看多' and wr.get('long_win_rate') is not None:
|
|
1009
|
+
# win_rate_info = f" | 看多胜率={wr['long_win_rate']:.2%} (样本={wr.get('long_signals', 0)})"
|
|
1010
|
+
# elif signal_text == '看空' and wr.get('short_win_rate') is not None:
|
|
1011
|
+
# win_rate_info = f" | 看空胜率={wr['short_win_rate']:.2%} (样本={wr.get('short_signals', 0)})"
|
|
1012
|
+
# if wr.get('overall_win_rate') is not None:
|
|
1013
|
+
# win_rate_info += f" | 总体胜率={wr['overall_win_rate']:.2%}"
|
|
1014
|
+
# print(f" 归一化Alpha#15: 原始值={alpha15_info.get('raw_value', 0):+.6f} | 归一化值={alpha15_info.get('value', 0):+.4f} | {signal_emoji} {signal_text}{win_rate_info}")
|
|
1015
|
+
|
|
1016
|
+
# 显示其他归一化Alpha因子
|
|
1017
|
+
other_alpha_factors = [
|
|
1018
|
+
'normalized_alpha1', 'normalized_alpha15', 'normalized_alpha3', 'normalized_alpha7', 'normalized_alpha9',
|
|
1019
|
+
'normalized_alpha11', 'normalized_alpha17', 'normalized_alpha21',
|
|
1020
|
+
'normalized_alpha23', 'normalized_alpha25', 'normalized_alpha29',
|
|
1021
|
+
'normalized_alpha33', 'normalized_alpha34'
|
|
1022
|
+
]
|
|
1023
|
+
|
|
1024
|
+
for factor_key in other_alpha_factors:
|
|
1025
|
+
if factor_key in factor_results:
|
|
1026
|
+
alpha_info = factor_results[factor_key]
|
|
1027
|
+
alpha_num = factor_key.replace('normalized_alpha', '')
|
|
1028
|
+
signal_emoji = "🟢" if alpha_info.get('signal') == '看多' else "🔴" if alpha_info.get('signal') == '看空' else "⚪"
|
|
1029
|
+
signal_text = alpha_info.get('signal', '中性')
|
|
1030
|
+
win_rate_info = ""
|
|
1031
|
+
if alpha_info.get('win_rate'):
|
|
1032
|
+
wr = alpha_info['win_rate']
|
|
1033
|
+
if wr and isinstance(wr, dict):
|
|
1034
|
+
if signal_text == '看多' and wr.get('long_win_rate') is not None:
|
|
1035
|
+
win_rate_info = f" | 看多胜率={wr['long_win_rate']:.2%} (样本={wr.get('long_signals', 0)})"
|
|
1036
|
+
elif signal_text == '看空' and wr.get('short_win_rate') is not None:
|
|
1037
|
+
win_rate_info = f" | 看空胜率={wr['short_win_rate']:.2%} (样本={wr.get('short_signals', 0)})"
|
|
1038
|
+
if wr.get('overall_win_rate') is not None:
|
|
1039
|
+
win_rate_info += f" | 总体胜率={wr['overall_win_rate']:.2%}"
|
|
1040
|
+
print(f" 归一化Alpha#{alpha_num}: 原始值={alpha_info.get('raw_value', 0):+.6f} | 归一化值={alpha_info.get('value', 0):+.4f} | {signal_emoji} {signal_text}{win_rate_info}")
|
|
1041
|
+
except Exception as e:
|
|
1042
|
+
logger.debug(f"显示因子分析时出错: {e}")
|
|
1043
|
+
import traceback
|
|
1044
|
+
logger.debug(traceback.format_exc())
|
|
1045
|
+
|
|
589
1046
|
print(f"{'='*80}")
|
|
590
1047
|
print(f"💰 账户信息:")
|
|
591
1048
|
print(f" 可用余额: {available_balance:.2f}")
|
|
@@ -684,9 +1141,9 @@ async def test_real_time_trading():
|
|
|
684
1141
|
# 创建实盘交易机器人
|
|
685
1142
|
# ⚠️ 警告:设置 dry_run=False 将执行真实交易!
|
|
686
1143
|
bot = RealTimeTradingBot(
|
|
687
|
-
symbol="
|
|
688
|
-
interval="
|
|
689
|
-
lookback_periods=
|
|
1144
|
+
symbol="ETHUSDT",
|
|
1145
|
+
interval="5m",
|
|
1146
|
+
lookback_periods=800,
|
|
690
1147
|
market_type="futures", # 或 "spot"
|
|
691
1148
|
position_size_pct=0.01,
|
|
692
1149
|
take_profit=0.1,
|
|
@@ -694,7 +1151,7 @@ async def test_real_time_trading():
|
|
|
694
1151
|
strategy="ma5", # 可选: ma5, ma_cross, ma_factor, rsi_factor, alpha1
|
|
695
1152
|
min_order_quantity=0.00001,
|
|
696
1153
|
ssl_verify=False,
|
|
697
|
-
dry_run=
|
|
1154
|
+
dry_run=True # ⚠️ 设置为 False 将执行真实交易!
|
|
698
1155
|
)
|
|
699
1156
|
|
|
700
1157
|
try:
|