cyqnt-trd 0.1.2__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 (147) hide show
  1. cyqnt_trd/__init__.py +26 -0
  2. cyqnt_trd/backtesting/README.md +264 -0
  3. cyqnt_trd/backtesting/__init__.py +12 -0
  4. cyqnt_trd/backtesting/factor_test.py +332 -0
  5. cyqnt_trd/backtesting/framework.py +311 -0
  6. cyqnt_trd/backtesting/strategy_backtest.py +545 -0
  7. cyqnt_trd/diagnose_api.py +28 -0
  8. cyqnt_trd/get_data/__init__.py +15 -0
  9. cyqnt_trd/get_data/get_futures_data.py +472 -0
  10. cyqnt_trd/get_data/get_trending_data.py +771 -0
  11. cyqnt_trd/online_trading/__init__.py +13 -0
  12. cyqnt_trd/online_trading/realtime_price_tracker.py +1001 -0
  13. cyqnt_trd/test.py +119 -0
  14. cyqnt_trd/test_script/README.md +411 -0
  15. cyqnt_trd/test_script/get_network_info.py +192 -0
  16. cyqnt_trd/test_script/get_symbols_by_volume.py +227 -0
  17. cyqnt_trd/test_script/realtime_price_tracker.py +839 -0
  18. cyqnt_trd/test_script/test_alpha.py +261 -0
  19. cyqnt_trd/test_script/test_kline_data.py +479 -0
  20. cyqnt_trd/test_script/test_order.py +1360 -0
  21. cyqnt_trd/trading_signal/README.md +276 -0
  22. cyqnt_trd/trading_signal/__init__.py +17 -0
  23. cyqnt_trd/trading_signal/example_test_alpha.py +430 -0
  24. cyqnt_trd/trading_signal/example_usage.py +431 -0
  25. cyqnt_trd/trading_signal/factor/__init__.py +18 -0
  26. cyqnt_trd/trading_signal/factor/ma_factor.py +75 -0
  27. cyqnt_trd/trading_signal/factor/rsi_factor.py +56 -0
  28. cyqnt_trd/trading_signal/selected_alpha/__init__.py +158 -0
  29. cyqnt_trd/trading_signal/selected_alpha/alpha1.py +87 -0
  30. cyqnt_trd/trading_signal/selected_alpha/alpha10.py +90 -0
  31. cyqnt_trd/trading_signal/selected_alpha/alpha100.py +74 -0
  32. cyqnt_trd/trading_signal/selected_alpha/alpha101.py +86 -0
  33. cyqnt_trd/trading_signal/selected_alpha/alpha11.py +86 -0
  34. cyqnt_trd/trading_signal/selected_alpha/alpha12.py +86 -0
  35. cyqnt_trd/trading_signal/selected_alpha/alpha13.py +86 -0
  36. cyqnt_trd/trading_signal/selected_alpha/alpha14.py +87 -0
  37. cyqnt_trd/trading_signal/selected_alpha/alpha15.py +87 -0
  38. cyqnt_trd/trading_signal/selected_alpha/alpha16.py +86 -0
  39. cyqnt_trd/trading_signal/selected_alpha/alpha17.py +88 -0
  40. cyqnt_trd/trading_signal/selected_alpha/alpha18.py +88 -0
  41. cyqnt_trd/trading_signal/selected_alpha/alpha19.py +87 -0
  42. cyqnt_trd/trading_signal/selected_alpha/alpha2.py +86 -0
  43. cyqnt_trd/trading_signal/selected_alpha/alpha20.py +88 -0
  44. cyqnt_trd/trading_signal/selected_alpha/alpha21.py +89 -0
  45. cyqnt_trd/trading_signal/selected_alpha/alpha22.py +87 -0
  46. cyqnt_trd/trading_signal/selected_alpha/alpha23.py +88 -0
  47. cyqnt_trd/trading_signal/selected_alpha/alpha24.py +88 -0
  48. cyqnt_trd/trading_signal/selected_alpha/alpha25.py +86 -0
  49. cyqnt_trd/trading_signal/selected_alpha/alpha26.py +87 -0
  50. cyqnt_trd/trading_signal/selected_alpha/alpha27.py +88 -0
  51. cyqnt_trd/trading_signal/selected_alpha/alpha28.py +88 -0
  52. cyqnt_trd/trading_signal/selected_alpha/alpha29.py +87 -0
  53. cyqnt_trd/trading_signal/selected_alpha/alpha3.py +86 -0
  54. cyqnt_trd/trading_signal/selected_alpha/alpha30.py +87 -0
  55. cyqnt_trd/trading_signal/selected_alpha/alpha31.py +90 -0
  56. cyqnt_trd/trading_signal/selected_alpha/alpha32.py +86 -0
  57. cyqnt_trd/trading_signal/selected_alpha/alpha33.py +86 -0
  58. cyqnt_trd/trading_signal/selected_alpha/alpha34.py +87 -0
  59. cyqnt_trd/trading_signal/selected_alpha/alpha35.py +88 -0
  60. cyqnt_trd/trading_signal/selected_alpha/alpha36.py +86 -0
  61. cyqnt_trd/trading_signal/selected_alpha/alpha37.py +86 -0
  62. cyqnt_trd/trading_signal/selected_alpha/alpha38.py +87 -0
  63. cyqnt_trd/trading_signal/selected_alpha/alpha39.py +87 -0
  64. cyqnt_trd/trading_signal/selected_alpha/alpha4.py +86 -0
  65. cyqnt_trd/trading_signal/selected_alpha/alpha40.py +86 -0
  66. cyqnt_trd/trading_signal/selected_alpha/alpha41.py +86 -0
  67. cyqnt_trd/trading_signal/selected_alpha/alpha42.py +86 -0
  68. cyqnt_trd/trading_signal/selected_alpha/alpha43.py +86 -0
  69. cyqnt_trd/trading_signal/selected_alpha/alpha44.py +87 -0
  70. cyqnt_trd/trading_signal/selected_alpha/alpha45.py +88 -0
  71. cyqnt_trd/trading_signal/selected_alpha/alpha46.py +89 -0
  72. cyqnt_trd/trading_signal/selected_alpha/alpha47.py +86 -0
  73. cyqnt_trd/trading_signal/selected_alpha/alpha48.py +74 -0
  74. cyqnt_trd/trading_signal/selected_alpha/alpha49.py +88 -0
  75. cyqnt_trd/trading_signal/selected_alpha/alpha5.py +86 -0
  76. cyqnt_trd/trading_signal/selected_alpha/alpha50.py +86 -0
  77. cyqnt_trd/trading_signal/selected_alpha/alpha51.py +88 -0
  78. cyqnt_trd/trading_signal/selected_alpha/alpha52.py +87 -0
  79. cyqnt_trd/trading_signal/selected_alpha/alpha53.py +86 -0
  80. cyqnt_trd/trading_signal/selected_alpha/alpha54.py +86 -0
  81. cyqnt_trd/trading_signal/selected_alpha/alpha55.py +88 -0
  82. cyqnt_trd/trading_signal/selected_alpha/alpha56.py +86 -0
  83. cyqnt_trd/trading_signal/selected_alpha/alpha57.py +86 -0
  84. cyqnt_trd/trading_signal/selected_alpha/alpha58.py +74 -0
  85. cyqnt_trd/trading_signal/selected_alpha/alpha59.py +74 -0
  86. cyqnt_trd/trading_signal/selected_alpha/alpha6.py +86 -0
  87. cyqnt_trd/trading_signal/selected_alpha/alpha60.py +89 -0
  88. cyqnt_trd/trading_signal/selected_alpha/alpha61.py +88 -0
  89. cyqnt_trd/trading_signal/selected_alpha/alpha62.py +86 -0
  90. cyqnt_trd/trading_signal/selected_alpha/alpha63.py +74 -0
  91. cyqnt_trd/trading_signal/selected_alpha/alpha64.py +86 -0
  92. cyqnt_trd/trading_signal/selected_alpha/alpha65.py +86 -0
  93. cyqnt_trd/trading_signal/selected_alpha/alpha66.py +86 -0
  94. cyqnt_trd/trading_signal/selected_alpha/alpha67.py +74 -0
  95. cyqnt_trd/trading_signal/selected_alpha/alpha68.py +86 -0
  96. cyqnt_trd/trading_signal/selected_alpha/alpha69.py +74 -0
  97. cyqnt_trd/trading_signal/selected_alpha/alpha7.py +88 -0
  98. cyqnt_trd/trading_signal/selected_alpha/alpha70.py +74 -0
  99. cyqnt_trd/trading_signal/selected_alpha/alpha71.py +92 -0
  100. cyqnt_trd/trading_signal/selected_alpha/alpha72.py +86 -0
  101. cyqnt_trd/trading_signal/selected_alpha/alpha73.py +91 -0
  102. cyqnt_trd/trading_signal/selected_alpha/alpha74.py +86 -0
  103. cyqnt_trd/trading_signal/selected_alpha/alpha75.py +86 -0
  104. cyqnt_trd/trading_signal/selected_alpha/alpha76.py +74 -0
  105. cyqnt_trd/trading_signal/selected_alpha/alpha77.py +92 -0
  106. cyqnt_trd/trading_signal/selected_alpha/alpha78.py +86 -0
  107. cyqnt_trd/trading_signal/selected_alpha/alpha79.py +74 -0
  108. cyqnt_trd/trading_signal/selected_alpha/alpha8.py +87 -0
  109. cyqnt_trd/trading_signal/selected_alpha/alpha80.py +74 -0
  110. cyqnt_trd/trading_signal/selected_alpha/alpha81.py +86 -0
  111. cyqnt_trd/trading_signal/selected_alpha/alpha82.py +74 -0
  112. cyqnt_trd/trading_signal/selected_alpha/alpha83.py +86 -0
  113. cyqnt_trd/trading_signal/selected_alpha/alpha84.py +86 -0
  114. cyqnt_trd/trading_signal/selected_alpha/alpha85.py +86 -0
  115. cyqnt_trd/trading_signal/selected_alpha/alpha86.py +86 -0
  116. cyqnt_trd/trading_signal/selected_alpha/alpha87.py +74 -0
  117. cyqnt_trd/trading_signal/selected_alpha/alpha88.py +92 -0
  118. cyqnt_trd/trading_signal/selected_alpha/alpha89.py +74 -0
  119. cyqnt_trd/trading_signal/selected_alpha/alpha9.py +90 -0
  120. cyqnt_trd/trading_signal/selected_alpha/alpha90.py +74 -0
  121. cyqnt_trd/trading_signal/selected_alpha/alpha91.py +74 -0
  122. cyqnt_trd/trading_signal/selected_alpha/alpha92.py +92 -0
  123. cyqnt_trd/trading_signal/selected_alpha/alpha93.py +74 -0
  124. cyqnt_trd/trading_signal/selected_alpha/alpha94.py +86 -0
  125. cyqnt_trd/trading_signal/selected_alpha/alpha95.py +86 -0
  126. cyqnt_trd/trading_signal/selected_alpha/alpha96.py +92 -0
  127. cyqnt_trd/trading_signal/selected_alpha/alpha97.py +74 -0
  128. cyqnt_trd/trading_signal/selected_alpha/alpha98.py +87 -0
  129. cyqnt_trd/trading_signal/selected_alpha/alpha99.py +86 -0
  130. cyqnt_trd/trading_signal/selected_alpha/alpha_utils.py +342 -0
  131. cyqnt_trd/trading_signal/selected_alpha/create_all_alphas.py +279 -0
  132. cyqnt_trd/trading_signal/selected_alpha/generate_alphas.py +133 -0
  133. cyqnt_trd/trading_signal/selected_alpha/test_alpha.py +261 -0
  134. cyqnt_trd/trading_signal/signal/__init__.py +20 -0
  135. cyqnt_trd/trading_signal/signal/factor_based_signal.py +387 -0
  136. cyqnt_trd/trading_signal/signal/ma_signal.py +163 -0
  137. cyqnt_trd/utils/__init__.py +3 -0
  138. cyqnt_trd/utils/set_user.py +33 -0
  139. cyqnt_trd-0.1.2.dist-info/METADATA +148 -0
  140. cyqnt_trd-0.1.2.dist-info/RECORD +147 -0
  141. cyqnt_trd-0.1.2.dist-info/WHEEL +5 -0
  142. cyqnt_trd-0.1.2.dist-info/licenses/LICENSE +21 -0
  143. cyqnt_trd-0.1.2.dist-info/top_level.txt +2 -0
  144. test/real_time_trade.py +746 -0
  145. test/test_example_usage.py +381 -0
  146. test/test_get_data.py +310 -0
  147. test/test_realtime_price_tracker.py +546 -0
@@ -0,0 +1,746 @@
1
+ """
2
+ 实盘交易脚本
3
+
4
+ 结合 RealtimePriceTracker 和订单执行功能,实现基于实时数据的自动交易。
5
+
6
+ ⚠️ 警告:此脚本会执行真实交易,请谨慎使用!
7
+ 建议:
8
+ 1. 先在测试网络或使用小额资金测试
9
+ 2. 仔细检查所有参数
10
+ 3. 确保策略已经过充分回测
11
+ 4. 设置合理的止盈止损
12
+
13
+ 使用方法:
14
+ python real_time_trade.py
15
+ """
16
+
17
+ import os
18
+ import sys
19
+ import asyncio
20
+ import logging
21
+ from pathlib import Path
22
+ from datetime import datetime
23
+ from typing import Optional, Dict, Any
24
+ from cyqnt_trd.utils import set_user
25
+
26
+ # 添加项目根目录到路径
27
+ project_root = Path(__file__).parent.parent
28
+ if str(project_root) not in sys.path:
29
+ sys.path.insert(0, str(project_root))
30
+
31
+ # 导入 cyqnt_trd 包
32
+ try:
33
+ from cyqnt_trd.online_trading.realtime_price_tracker import RealtimePriceTracker
34
+ from cyqnt_trd.test_script.test_order import (
35
+ test_futures_order,
36
+ test_spot_order,
37
+ get_futures_balance,
38
+ get_spot_balance,
39
+ show_futures_balances,
40
+ show_spot_balances,
41
+ get_futures_open_orders,
42
+ cancel_futures_order
43
+ )
44
+ 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
46
+ from cyqnt_trd.trading_signal.factor.ma_factor import ma_factor
47
+ from cyqnt_trd.trading_signal.factor.rsi_factor import rsi_factor
48
+ from cyqnt_trd.trading_signal.selected_alpha.alpha1 import alpha1_factor
49
+ except ImportError as e:
50
+ print(f"导入错误: {e}")
51
+ print("\n提示:请确保已安装 cyqnt_trd package: pip install -e /path/to/crypto_trading")
52
+ sys.exit(1)
53
+
54
+ # 配置日志
55
+ logging.basicConfig(
56
+ level=logging.INFO,
57
+ format='%(asctime)s - %(levelname)s - %(message)s'
58
+ )
59
+ logger = logging.getLogger(__name__)
60
+
61
+
62
+ class RealTimeTradingBot:
63
+ """
64
+ 实盘交易机器人
65
+
66
+ 使用 RealtimePriceTracker 获取实时数据,根据交易信号执行真实订单
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ symbol: str,
72
+ interval: str = "1m",
73
+ lookback_periods: int = 100,
74
+ market_type: str = "futures", # "futures" 或 "spot"
75
+ position_size_pct: float = 0.01, # 每次使用资金的百分比
76
+ take_profit: float = 0.1, # 止盈10%
77
+ stop_loss: float = 0.05, # 止损5%
78
+ strategy: str = "ma5", # 策略类型
79
+ min_order_quantity: float = 0.001, # 最小下单数量
80
+ ssl_verify: bool = False,
81
+ dry_run: bool = True # 是否为模拟模式(不实际下单)
82
+ ):
83
+ """
84
+ 初始化实盘交易机器人
85
+
86
+ Args:
87
+ symbol: 交易对符号
88
+ interval: 时间间隔
89
+ lookback_periods: 历史数据周期数
90
+ market_type: 市场类型,"futures" 或 "spot"
91
+ position_size_pct: 每次交易使用的资金比例(0-1)
92
+ take_profit: 止盈比例(0-1)
93
+ stop_loss: 止损比例(0-1)
94
+ strategy: 策略类型
95
+ min_order_quantity: 最小下单数量
96
+ ssl_verify: SSL证书验证
97
+ dry_run: 是否为模拟模式(True=不实际下单,False=真实下单)
98
+ """
99
+ self.symbol = symbol.upper()
100
+ self.interval = interval
101
+ self.market_type = market_type
102
+ self.position_size_pct = position_size_pct
103
+ self.take_profit = take_profit
104
+ self.stop_loss = stop_loss
105
+ self.strategy = strategy
106
+ self.min_order_quantity = min_order_quantity
107
+ self.dry_run = dry_run
108
+
109
+ # 创建价格跟踪器
110
+ self.tracker = RealtimePriceTracker(
111
+ symbol=symbol,
112
+ interval=interval,
113
+ lookback_periods=lookback_periods,
114
+ market_type=market_type,
115
+ ssl_verify=ssl_verify
116
+ )
117
+
118
+ # 交易状态
119
+ self.position = 0.0 # 当前持仓数量
120
+ self.entry_price = 0.0 # 入场价格
121
+ self.entry_index = -1 # 入场索引
122
+ self.entry_time = None # 入场时间
123
+ self.entry_order_id = None # 入场订单ID
124
+
125
+ # 交易记录
126
+ self.completed_trades = [] # 已完成的交易
127
+ self.total_trades = 0
128
+ self.win_trades = 0
129
+ self.loss_trades = 0
130
+ self.total_profit = 0.0
131
+
132
+ # 统计信息
133
+ self.start_time = datetime.now()
134
+ self.last_signal = None
135
+ self.last_signal_time = None
136
+
137
+ # 注册回调
138
+ self.tracker.register_on_new_kline(self._on_new_kline)
139
+
140
+ # 显示初始状态
141
+ if dry_run:
142
+ logger.warning("="*80)
143
+ logger.warning("⚠️ 模拟模式:不会执行真实订单")
144
+ logger.warning("="*80)
145
+ else:
146
+ logger.warning("="*80)
147
+ logger.warning("⚠️ 实盘模式:将执行真实订单!")
148
+ logger.warning("="*80)
149
+
150
+ def _calculate_signal(self, data_df) -> Optional[str]:
151
+ """
152
+ 根据策略计算交易信号
153
+
154
+ Args:
155
+ data_df: 历史数据DataFrame
156
+
157
+ Returns:
158
+ 交易信号: 'buy', 'sell', 'hold' 或 None
159
+ """
160
+ if len(data_df) < 10:
161
+ return None
162
+
163
+ # 使用足够的数据切片
164
+ data_slice = data_df.iloc[-30:].copy() if len(data_df) >= 30 else data_df.copy()
165
+
166
+ try:
167
+ if self.strategy == "ma5":
168
+ if len(data_slice) >= 6:
169
+ return ma_signal(
170
+ data_slice=data_slice,
171
+ position=self.position,
172
+ entry_price=self.entry_price,
173
+ entry_index=self.entry_index,
174
+ take_profit=self.take_profit,
175
+ stop_loss=self.stop_loss,
176
+ period=5
177
+ )
178
+
179
+ elif self.strategy == "ma_cross":
180
+ if len(data_slice) >= 22:
181
+ return ma_cross_signal(
182
+ data_slice=data_slice,
183
+ position=self.position,
184
+ entry_price=self.entry_price,
185
+ entry_index=self.entry_index,
186
+ take_profit=self.take_profit,
187
+ stop_loss=self.stop_loss,
188
+ check_periods=1,
189
+ short_period=5,
190
+ long_period=20
191
+ )
192
+
193
+ elif self.strategy == "ma_factor":
194
+ if len(data_slice) >= 6:
195
+ return factor_based_signal(
196
+ data_slice=data_slice,
197
+ position=self.position,
198
+ entry_price=self.entry_price,
199
+ entry_index=self.entry_index,
200
+ take_profit=self.take_profit,
201
+ stop_loss=self.stop_loss,
202
+ check_periods=1,
203
+ factor_func=lambda d: ma_factor(d, period=5),
204
+ factor_period=5
205
+ )
206
+
207
+ elif self.strategy == "rsi_factor":
208
+ if len(data_slice) >= 16:
209
+ return factor_based_signal(
210
+ data_slice=data_slice,
211
+ position=self.position,
212
+ entry_price=self.entry_price,
213
+ entry_index=self.entry_index,
214
+ take_profit=self.take_profit,
215
+ stop_loss=self.stop_loss,
216
+ check_periods=1,
217
+ factor_func=lambda d: rsi_factor(d, period=14),
218
+ factor_period=14
219
+ )
220
+
221
+ elif self.strategy == "alpha1":
222
+ if len(data_slice) >= 26:
223
+ return factor_based_signal(
224
+ data_slice=data_slice,
225
+ position=self.position,
226
+ entry_price=self.entry_price,
227
+ entry_index=self.entry_index,
228
+ take_profit=self.take_profit,
229
+ stop_loss=self.stop_loss,
230
+ check_periods=1,
231
+ factor_func=lambda d: alpha1_factor(d, lookback_days=5, stddev_period=20, power=2.0),
232
+ factor_period=25
233
+ )
234
+ except Exception as e:
235
+ logger.debug(f"计算信号时出错: {e}")
236
+ return None
237
+
238
+ return None
239
+
240
+ def _get_available_balance(self) -> float:
241
+ """
242
+ 获取可用余额
243
+
244
+ Returns:
245
+ 可用余额
246
+ """
247
+ try:
248
+ if self.market_type == "futures":
249
+ result = get_futures_balance("USDT")
250
+ if result.get("success"):
251
+ balance_info = result.get("balances", {})
252
+ return balance_info.get("available", 0.0)
253
+ else:
254
+ # 从交易对中提取报价货币
255
+ quote_asset = "USDT" # 默认
256
+ if self.symbol.endswith("USDT"):
257
+ quote_asset = "USDT"
258
+ elif self.symbol.endswith("BUSD"):
259
+ quote_asset = "BUSD"
260
+ elif self.symbol.endswith("USDC"):
261
+ quote_asset = "USDC"
262
+
263
+ result = get_spot_balance(quote_asset)
264
+ if result.get("success"):
265
+ balance_info = result.get("balances", {})
266
+ return balance_info.get("free", 0.0)
267
+ except Exception as e:
268
+ logger.error(f"获取余额失败: {e}")
269
+
270
+ return 0.0
271
+
272
+ def _calculate_order_quantity(self, price: float, side: str) -> float:
273
+ """
274
+ 计算订单数量
275
+
276
+ Args:
277
+ price: 当前价格
278
+ side: 买卖方向,"BUY" 或 "SELL"
279
+
280
+ Returns:
281
+ 订单数量
282
+ """
283
+ if side == "BUY":
284
+ # 买入:使用可用余额的百分比
285
+ available = self._get_available_balance()
286
+ order_value = available * self.position_size_pct
287
+ quantity = order_value / price
288
+
289
+ # 确保不小于最小下单数量
290
+ if quantity < self.min_order_quantity:
291
+ return 0.0
292
+
293
+ return quantity
294
+ else:
295
+ # 卖出:使用当前持仓
296
+ return self.position
297
+
298
+ def _execute_buy_order(self, price: float, time_str: str) -> bool:
299
+ """
300
+ 执行买入订单
301
+
302
+ Args:
303
+ price: 买入价格
304
+ time_str: 时间字符串
305
+
306
+ Returns:
307
+ 是否成功
308
+ """
309
+ quantity = self._calculate_order_quantity(price, "BUY")
310
+
311
+ if quantity < self.min_order_quantity:
312
+ logger.warning(f"计算出的数量 {quantity} 小于最小下单数量 {self.min_order_quantity}")
313
+ return False
314
+
315
+ logger.info(f"准备买入: {self.symbol}, 数量: {quantity:.6f}, 价格: {price:.2f}")
316
+
317
+ if self.dry_run:
318
+ logger.info("🔵 [模拟] 执行买入订单")
319
+ logger.info(f" 时间: {time_str}")
320
+ logger.info(f" 价格: {price:.2f}")
321
+ logger.info(f" 数量: {quantity:.6f}")
322
+ logger.info(f" 金额: {quantity * price:.2f}")
323
+
324
+ # 模拟更新状态
325
+ self.position = quantity
326
+ self.entry_price = price
327
+ self.entry_time = time_str
328
+ return True
329
+ else:
330
+ # 真实下单
331
+ try:
332
+ if self.market_type == "futures":
333
+ result = test_futures_order(
334
+ symbol=self.symbol,
335
+ side="BUY",
336
+ order_type="MARKET",
337
+ quantity=quantity
338
+ )
339
+ else:
340
+ result = test_spot_order(
341
+ symbol=self.symbol,
342
+ side="BUY",
343
+ order_type="MARKET",
344
+ quantity=quantity
345
+ )
346
+
347
+ if result.get("success"):
348
+ order_data = result.get("order", {})
349
+ executed_qty = float(order_data.get("executedQty", order_data.get("executed_qty", quantity)))
350
+ avg_price = float(order_data.get("avgPrice", order_data.get("avg_price", price)))
351
+ order_id = order_data.get("orderId", order_data.get("order_id"))
352
+
353
+ logger.info(f"✅ 买入订单成功")
354
+ logger.info(f" 订单ID: {order_id}")
355
+ logger.info(f" 成交数量: {executed_qty:.6f}")
356
+ logger.info(f" 成交均价: {avg_price:.2f}")
357
+
358
+ # 更新状态
359
+ self.position = executed_qty
360
+ self.entry_price = avg_price
361
+ self.entry_time = time_str
362
+ self.entry_order_id = order_id
363
+
364
+ return True
365
+ else:
366
+ logger.error(f"买入订单失败: {result.get('error')}")
367
+ return False
368
+ except Exception as e:
369
+ logger.error(f"执行买入订单时出错: {e}")
370
+ import traceback
371
+ logger.error(traceback.format_exc())
372
+ return False
373
+
374
+ def _execute_sell_order(self, price: float, time_str: str) -> bool:
375
+ """
376
+ 执行卖出订单
377
+
378
+ Args:
379
+ price: 卖出价格
380
+ time_str: 时间字符串
381
+
382
+ Returns:
383
+ 是否成功
384
+ """
385
+ quantity = self._calculate_order_quantity(price, "SELL")
386
+
387
+ if quantity < self.min_order_quantity:
388
+ logger.warning(f"持仓数量 {quantity} 小于最小下单数量 {self.min_order_quantity}")
389
+ return False
390
+
391
+ logger.info(f"准备卖出: {self.symbol}, 数量: {quantity:.6f}, 价格: {price:.2f}")
392
+
393
+ if self.dry_run:
394
+ # 计算盈亏
395
+ profit_amount = (price - self.entry_price) * quantity
396
+ profit_pct = (price - self.entry_price) / self.entry_price * 100
397
+
398
+ logger.info("🔴 [模拟] 执行卖出订单")
399
+ logger.info(f" 时间: {time_str}")
400
+ logger.info(f" 价格: {price:.2f}")
401
+ logger.info(f" 入场价: {self.entry_price:.2f}")
402
+ logger.info(f" 数量: {quantity:.6f}")
403
+ logger.info(f" 盈亏金额: {profit_amount:+.2f}")
404
+ logger.info(f" 盈亏比例: {profit_pct:+.2f}%")
405
+
406
+ # 记录交易
407
+ trade_record = {
408
+ 'entry_time': self.entry_time,
409
+ 'exit_time': time_str,
410
+ 'entry_price': self.entry_price,
411
+ 'exit_price': price,
412
+ 'quantity': quantity,
413
+ 'profit_amount': profit_amount,
414
+ 'profit_pct': profit_pct
415
+ }
416
+ self.completed_trades.append(trade_record)
417
+
418
+ # 更新统计
419
+ self.total_trades += 1
420
+ self.total_profit += profit_amount
421
+ if profit_amount > 0:
422
+ self.win_trades += 1
423
+ else:
424
+ self.loss_trades += 1
425
+
426
+ # 重置持仓
427
+ self.position = 0.0
428
+ self.entry_price = 0.0
429
+ self.entry_time = None
430
+ self.entry_order_id = None
431
+
432
+ return True
433
+ else:
434
+ # 真实下单
435
+ try:
436
+ if self.market_type == "futures":
437
+ result = test_futures_order(
438
+ symbol=self.symbol,
439
+ side="SELL",
440
+ order_type="MARKET",
441
+ quantity=quantity,
442
+ reduce_only="true" # 只减仓
443
+ )
444
+ else:
445
+ result = test_spot_order(
446
+ symbol=self.symbol,
447
+ side="SELL",
448
+ order_type="MARKET",
449
+ quantity=quantity
450
+ )
451
+
452
+ if result.get("success"):
453
+ order_data = result.get("order", {})
454
+ executed_qty = float(order_data.get("executedQty", order_data.get("executed_qty", quantity)))
455
+ avg_price = float(order_data.get("avgPrice", order_data.get("avg_price", price)))
456
+ order_id = order_data.get("orderId", order_data.get("order_id"))
457
+
458
+ # 计算盈亏
459
+ profit_amount = (avg_price - self.entry_price) * executed_qty
460
+ profit_pct = (avg_price - self.entry_price) / self.entry_price * 100
461
+
462
+ logger.info(f"✅ 卖出订单成功")
463
+ logger.info(f" 订单ID: {order_id}")
464
+ logger.info(f" 成交数量: {executed_qty:.6f}")
465
+ logger.info(f" 成交均价: {avg_price:.2f}")
466
+ logger.info(f" 盈亏金额: {profit_amount:+.2f}")
467
+ logger.info(f" 盈亏比例: {profit_pct:+.2f}%")
468
+
469
+ # 记录交易
470
+ trade_record = {
471
+ 'entry_time': self.entry_time,
472
+ 'exit_time': time_str,
473
+ 'entry_price': self.entry_price,
474
+ 'exit_price': avg_price,
475
+ 'quantity': executed_qty,
476
+ 'profit_amount': profit_amount,
477
+ 'profit_pct': profit_pct,
478
+ 'entry_order_id': self.entry_order_id,
479
+ 'exit_order_id': order_id
480
+ }
481
+ self.completed_trades.append(trade_record)
482
+
483
+ # 更新统计
484
+ self.total_trades += 1
485
+ self.total_profit += profit_amount
486
+ if profit_amount > 0:
487
+ self.win_trades += 1
488
+ else:
489
+ self.loss_trades += 1
490
+
491
+ # 重置持仓
492
+ self.position = 0.0
493
+ self.entry_price = 0.0
494
+ self.entry_time = None
495
+ self.entry_order_id = None
496
+
497
+ return True
498
+ else:
499
+ logger.error(f"卖出订单失败: {result.get('error')}")
500
+ return False
501
+ except Exception as e:
502
+ logger.error(f"执行卖出订单时出错: {e}")
503
+ import traceback
504
+ logger.error(traceback.format_exc())
505
+ return False
506
+
507
+ def _on_new_kline(self, kline_dict: Dict[str, Any], data_df):
508
+ """
509
+ 新K线数据回调函数
510
+
511
+ Args:
512
+ kline_dict: 新K线数据字典
513
+ data_df: 历史数据DataFrame
514
+ """
515
+ current_price = kline_dict['close_price']
516
+ current_time = kline_dict['open_time_str']
517
+
518
+ # 计算交易信号
519
+ signal = self._calculate_signal(data_df)
520
+
521
+ # 显示状态
522
+ self._display_status(current_time, current_price, signal)
523
+
524
+ # 检查止盈止损(如果有持仓)
525
+ if self.position > 0:
526
+ profit_pct = (current_price - self.entry_price) / self.entry_price
527
+ if profit_pct >= self.take_profit:
528
+ logger.info(f"触发止盈: {profit_pct*100:.2f}% >= {self.take_profit*100:.2f}%")
529
+ self._execute_sell_order(current_price, current_time)
530
+ return
531
+ elif profit_pct <= -self.stop_loss:
532
+ logger.info(f"触发止损: {profit_pct*100:.2f}% <= -{self.stop_loss*100:.2f}%")
533
+ self._execute_sell_order(current_price, current_time)
534
+ return
535
+
536
+ # 执行交易
537
+ if signal == 'buy' and self.position == 0:
538
+ # 避免频繁交易:检查上次信号时间
539
+ if self.last_signal == 'buy' and self.last_signal_time:
540
+ time_diff = (datetime.now() - self.last_signal_time).total_seconds()
541
+ if time_diff < 60: # 至少间隔60秒
542
+ logger.debug("买入信号过于频繁,跳过")
543
+ return
544
+
545
+ self._execute_buy_order(current_price, current_time)
546
+ self.last_signal = 'buy'
547
+ self.last_signal_time = datetime.now()
548
+
549
+ elif signal == 'sell' and self.position > 0:
550
+ self._execute_sell_order(current_price, current_time)
551
+ self.last_signal = 'sell'
552
+ self.last_signal_time = datetime.now()
553
+
554
+ def _display_status(self, time_str: str, price: float, signal: Optional[str]):
555
+ """
556
+ 显示当前状态
557
+
558
+ Args:
559
+ time_str: 时间字符串
560
+ price: 当前价格
561
+ signal: 交易信号
562
+ """
563
+ # 计算统计信息
564
+ runtime = datetime.now() - self.start_time
565
+ runtime_str = f"{runtime.days}天 {runtime.seconds // 3600}小时 {(runtime.seconds % 3600) // 60}分钟"
566
+ win_rate = (self.win_trades / self.total_trades * 100) if self.total_trades > 0 else 0.0
567
+
568
+ # 获取余额
569
+ available_balance = self._get_available_balance()
570
+
571
+ # 信号显示
572
+ if signal:
573
+ signal_emoji = "🟢" if signal == 'buy' else "🔴" if signal == 'sell' else "⚪"
574
+ signal_text = f"{signal_emoji} {signal.upper()}"
575
+ else:
576
+ signal_text = "⚪ HOLD"
577
+
578
+ print(f"\n{'='*80}")
579
+ print(f"📊 实时状态更新")
580
+ print(f"{'='*80}")
581
+ print(f"时间: {time_str}")
582
+ print(f"价格: {price:.2f}")
583
+ print(f"信号: {signal_text}")
584
+ if self.position > 0:
585
+ profit_pct = (price - self.entry_price) / self.entry_price * 100
586
+ print(f"持仓: {self.position:.6f} | 入场价: {self.entry_price:.2f} | 浮动盈亏: {profit_pct:+.2f}%")
587
+ else:
588
+ print(f"持仓: 无")
589
+ print(f"{'='*80}")
590
+ print(f"💰 账户信息:")
591
+ print(f" 可用余额: {available_balance:.2f}")
592
+ print(f" 累计盈亏: {self.total_profit:+.2f}")
593
+ print(f" 运行时间: {runtime_str}")
594
+ print(f" 总交易次数: {self.total_trades} | 盈利: {self.win_trades} | 亏损: {self.loss_trades} | 胜率: {win_rate:.2f}%")
595
+ print(f"{'='*80}\n")
596
+
597
+ def get_statistics(self) -> Dict[str, Any]:
598
+ """
599
+ 获取交易统计信息
600
+
601
+ Returns:
602
+ 统计信息字典
603
+ """
604
+ runtime = datetime.now() - self.start_time
605
+ win_rate = (self.win_trades / self.total_trades * 100) if self.total_trades > 0 else 0.0
606
+ avg_profit = self.total_profit / self.total_trades if self.total_trades > 0 else 0.0
607
+
608
+ return {
609
+ 'total_trades': self.total_trades,
610
+ 'win_trades': self.win_trades,
611
+ 'loss_trades': self.loss_trades,
612
+ 'win_rate': win_rate,
613
+ 'total_profit': self.total_profit,
614
+ 'avg_profit': avg_profit,
615
+ 'runtime': str(runtime),
616
+ 'completed_trades': self.completed_trades
617
+ }
618
+
619
+ def print_final_report(self):
620
+ """
621
+ 打印最终报告
622
+ """
623
+ stats = self.get_statistics()
624
+
625
+ print("\n" + "="*80)
626
+ print("📊 最终交易报告")
627
+ print("="*80)
628
+ print(f"交易对: {self.symbol}")
629
+ print(f"市场类型: {self.market_type}")
630
+ print(f"策略: {self.strategy}")
631
+ print(f"运行时间: {stats['runtime']}")
632
+ print(f"\n📈 交易统计:")
633
+ print(f" 总交易次数: {stats['total_trades']}")
634
+ print(f" 盈利次数: {stats['win_trades']}")
635
+ print(f" 亏损次数: {stats['loss_trades']}")
636
+ print(f" 胜率: {stats['win_rate']:.2f}%")
637
+ print(f" 总盈亏: {stats['total_profit']:+.2f}")
638
+ print(f" 平均盈亏: {stats['avg_profit']:.2f}")
639
+ print("="*80)
640
+
641
+ # 显示最近10笔交易
642
+ if len(self.completed_trades) > 0:
643
+ print(f"\n最近10笔交易记录:")
644
+ print("-"*80)
645
+ for i, trade in enumerate(self.completed_trades[-10:], 1):
646
+ print(f"{i}. {trade['entry_time']} -> {trade['exit_time']}")
647
+ print(f" 入场: {trade['entry_price']:.2f} | 出场: {trade['exit_price']:.2f}")
648
+ print(f" 盈亏: {trade['profit_amount']:+.2f} ({trade['profit_pct']:+.2f}%)")
649
+ print("="*80)
650
+
651
+ async def start(self):
652
+ """
653
+ 启动实盘交易
654
+ """
655
+ print("="*80)
656
+ print("🚀 实盘交易机器人启动")
657
+ print("="*80)
658
+ print(f"交易对: {self.symbol}")
659
+ print(f"市场类型: {self.market_type}")
660
+ print(f"时间间隔: {self.interval}")
661
+ print(f"策略: {self.strategy}")
662
+ print(f"仓位大小: {self.position_size_pct * 100:.0f}%")
663
+ print(f"止盈: {self.take_profit * 100:.0f}%")
664
+ print(f"止损: {self.stop_loss * 100:.0f}%")
665
+ print(f"模式: {'模拟模式' if self.dry_run else '实盘模式'}")
666
+ print("="*80)
667
+
668
+ # 显示账户余额
669
+ print("\n账户余额:")
670
+ if self.market_type == "futures":
671
+ show_futures_balances()
672
+ else:
673
+ show_spot_balances()
674
+
675
+ print("\n等待实时数据...\n")
676
+
677
+ await self.tracker.run_forever()
678
+
679
+
680
+ async def test_real_time_trading():
681
+ """
682
+ 测试实盘交易
683
+ """
684
+ # 创建实盘交易机器人
685
+ # ⚠️ 警告:设置 dry_run=False 将执行真实交易!
686
+ bot = RealTimeTradingBot(
687
+ symbol="BNBUSDT",
688
+ interval="3m",
689
+ lookback_periods=1000,
690
+ market_type="futures", # 或 "spot"
691
+ position_size_pct=0.01,
692
+ take_profit=0.1,
693
+ stop_loss=0.1,
694
+ strategy="ma5", # 可选: ma5, ma_cross, ma_factor, rsi_factor, alpha1
695
+ min_order_quantity=0.00001,
696
+ ssl_verify=False,
697
+ dry_run=False # ⚠️ 设置为 False 将执行真实交易!
698
+ )
699
+
700
+ try:
701
+ # 启动交易
702
+ await bot.start()
703
+ except KeyboardInterrupt:
704
+ print("\n\n收到中断信号,正在停止...")
705
+ finally:
706
+ # 打印最终报告
707
+ bot.print_final_report()
708
+
709
+
710
+ def main():
711
+ """
712
+ 主函数
713
+ """
714
+ set_user()
715
+ print("="*80)
716
+ print("实盘交易脚本")
717
+ print("="*80)
718
+ print("\n⚠️ 重要提示:")
719
+ print(" 1. 此脚本会执行真实交易(当 dry_run=False 时)")
720
+ print(" 2. 建议先在模拟模式下测试(dry_run=True)")
721
+ print(" 3. 确保策略已经过充分回测")
722
+ print(" 4. 设置合理的止盈止损")
723
+ print(" 5. 确保账户有足够的余额")
724
+ print(" 6. 按 Ctrl+C 停止交易")
725
+ print()
726
+
727
+ # 确认是否继续
728
+ if not os.getenv("AUTO_CONFIRM"):
729
+ response = input("是否继续?(yes/no): ")
730
+ if response.lower() != "yes":
731
+ print("已取消")
732
+ return
733
+
734
+ try:
735
+ asyncio.run(test_real_time_trading())
736
+ except KeyboardInterrupt:
737
+ print("\n交易已停止")
738
+ except Exception as e:
739
+ print(f"\n交易过程中出现错误: {e}")
740
+ import traceback
741
+ traceback.print_exc()
742
+
743
+
744
+ if __name__ == "__main__":
745
+ main()
746
+