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
|
@@ -1,546 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
模拟实盘测试脚本
|
|
3
|
-
|
|
4
|
-
参考 cyqnt_trd.online_trading.realtime_price_tracker 中的 RealtimePriceTracker 类,
|
|
5
|
-
创建模拟实盘交易测试环境。
|
|
6
|
-
|
|
7
|
-
使用方法:
|
|
8
|
-
python test_realtime_price_tracker.py
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
import sys
|
|
13
|
-
import asyncio
|
|
14
|
-
import logging
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from datetime import datetime
|
|
17
|
-
from typing import Optional, Dict, Any
|
|
18
|
-
|
|
19
|
-
# 添加项目根目录到路径
|
|
20
|
-
project_root = Path(__file__).parent.parent
|
|
21
|
-
if str(project_root) not in sys.path:
|
|
22
|
-
sys.path.insert(0, str(project_root))
|
|
23
|
-
|
|
24
|
-
# 导入 cyqnt_trd 包
|
|
25
|
-
try:
|
|
26
|
-
from cyqnt_trd.online_trading.realtime_price_tracker import RealtimePriceTracker
|
|
27
|
-
from cyqnt_trd.trading_signal.signal.ma_signal import ma_signal, ma_cross_signal
|
|
28
|
-
from cyqnt_trd.trading_signal.signal.factor_based_signal import factor_based_signal
|
|
29
|
-
from cyqnt_trd.trading_signal.factor.ma_factor import ma_factor
|
|
30
|
-
from cyqnt_trd.trading_signal.factor.rsi_factor import rsi_factor
|
|
31
|
-
from cyqnt_trd.trading_signal.selected_alpha.alpha1 import alpha1_factor
|
|
32
|
-
except ImportError as e:
|
|
33
|
-
print(f"导入错误: {e}")
|
|
34
|
-
print("\n提示:请确保已安装 cyqnt_trd package: pip install -e /path/to/crypto_trading")
|
|
35
|
-
sys.exit(1)
|
|
36
|
-
|
|
37
|
-
# 配置日志
|
|
38
|
-
logging.basicConfig(
|
|
39
|
-
level=logging.INFO,
|
|
40
|
-
format='%(asctime)s - %(levelname)s - %(message)s'
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class SimulatedTradingBot:
|
|
45
|
-
"""
|
|
46
|
-
模拟交易机器人
|
|
47
|
-
|
|
48
|
-
使用 RealtimePriceTracker 获取实时数据,并根据交易信号执行模拟交易
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
def __init__(
|
|
52
|
-
self,
|
|
53
|
-
symbol: str,
|
|
54
|
-
interval: str = "3m",
|
|
55
|
-
lookback_periods: int = 100,
|
|
56
|
-
initial_capital: float = 10000.0,
|
|
57
|
-
position_size: float = 0.01, # 每次使用90%的资金
|
|
58
|
-
take_profit: float = 0.1, # 止盈10%
|
|
59
|
-
stop_loss: float = 0.05, # 止损5%
|
|
60
|
-
commission_rate: float = 0.0001, # 手续费0.01%
|
|
61
|
-
strategy: str = "ma5", # 策略类型: ma5, ma_cross, ma_factor, rsi_factor, alpha1
|
|
62
|
-
ssl_verify: bool = False
|
|
63
|
-
):
|
|
64
|
-
"""
|
|
65
|
-
初始化模拟交易机器人
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
symbol: 交易对符号
|
|
69
|
-
interval: 时间间隔
|
|
70
|
-
lookback_periods: 历史数据周期数
|
|
71
|
-
initial_capital: 初始资金
|
|
72
|
-
position_size: 每次交易使用的资金比例(0-1)
|
|
73
|
-
take_profit: 止盈比例(0-1)
|
|
74
|
-
stop_loss: 止损比例(0-1)
|
|
75
|
-
commission_rate: 手续费率(0-1)
|
|
76
|
-
strategy: 策略类型
|
|
77
|
-
ssl_verify: SSL证书验证
|
|
78
|
-
"""
|
|
79
|
-
self.symbol = symbol
|
|
80
|
-
self.interval = interval
|
|
81
|
-
self.initial_capital = initial_capital
|
|
82
|
-
self.position_size = position_size
|
|
83
|
-
self.take_profit = take_profit
|
|
84
|
-
self.stop_loss = stop_loss
|
|
85
|
-
self.commission_rate = commission_rate
|
|
86
|
-
self.strategy = strategy
|
|
87
|
-
|
|
88
|
-
# 创建价格跟踪器
|
|
89
|
-
self.tracker = RealtimePriceTracker(
|
|
90
|
-
symbol=symbol,
|
|
91
|
-
interval=interval,
|
|
92
|
-
lookback_periods=lookback_periods,
|
|
93
|
-
ssl_verify=ssl_verify
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
# 交易状态
|
|
97
|
-
self.position = 0.0 # 当前持仓数量
|
|
98
|
-
self.entry_price = 0.0 # 入场价格
|
|
99
|
-
self.entry_index = -1 # 入场索引
|
|
100
|
-
self.entry_time = None # 入场时间
|
|
101
|
-
|
|
102
|
-
# 账户状态
|
|
103
|
-
self.current_capital = initial_capital # 当前可用资金
|
|
104
|
-
self.total_assets = initial_capital # 总资产(包括持仓价值)
|
|
105
|
-
|
|
106
|
-
# 交易记录
|
|
107
|
-
self.completed_trades = [] # 已完成的交易
|
|
108
|
-
self.total_trades = 0
|
|
109
|
-
self.win_trades = 0
|
|
110
|
-
self.loss_trades = 0
|
|
111
|
-
self.total_profit = 0.0
|
|
112
|
-
self.max_drawdown = 0.0
|
|
113
|
-
self.peak_assets = initial_capital # 资产峰值
|
|
114
|
-
|
|
115
|
-
# 统计信息
|
|
116
|
-
self.start_time = datetime.now()
|
|
117
|
-
self.last_update_time = None
|
|
118
|
-
|
|
119
|
-
# 注册回调
|
|
120
|
-
self.tracker.register_on_new_kline(self._on_new_kline)
|
|
121
|
-
|
|
122
|
-
def _calculate_signal(self, data_df) -> Optional[str]:
|
|
123
|
-
"""
|
|
124
|
-
根据策略计算交易信号
|
|
125
|
-
|
|
126
|
-
Args:
|
|
127
|
-
data_df: 历史数据DataFrame
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
交易信号: 'buy', 'sell', 'hold' 或 None
|
|
131
|
-
"""
|
|
132
|
-
if len(data_df) < 10:
|
|
133
|
-
return None
|
|
134
|
-
|
|
135
|
-
# 使用足够的数据切片
|
|
136
|
-
data_slice = data_df.iloc[-30:].copy() if len(data_df) >= 30 else data_df.copy()
|
|
137
|
-
|
|
138
|
-
try:
|
|
139
|
-
if self.strategy == "ma5":
|
|
140
|
-
if len(data_slice) >= 6:
|
|
141
|
-
return ma_signal(
|
|
142
|
-
data_slice=data_slice,
|
|
143
|
-
position=self.position,
|
|
144
|
-
entry_price=self.entry_price,
|
|
145
|
-
entry_index=self.entry_index,
|
|
146
|
-
take_profit=self.take_profit,
|
|
147
|
-
stop_loss=self.stop_loss,
|
|
148
|
-
period=5
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
elif self.strategy == "ma_cross":
|
|
152
|
-
if len(data_slice) >= 22:
|
|
153
|
-
return ma_cross_signal(
|
|
154
|
-
data_slice=data_slice,
|
|
155
|
-
position=self.position,
|
|
156
|
-
entry_price=self.entry_price,
|
|
157
|
-
entry_index=self.entry_index,
|
|
158
|
-
take_profit=self.take_profit,
|
|
159
|
-
stop_loss=self.stop_loss,
|
|
160
|
-
check_periods=1,
|
|
161
|
-
short_period=5,
|
|
162
|
-
long_period=20
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
elif self.strategy == "ma_factor":
|
|
166
|
-
if len(data_slice) >= 6:
|
|
167
|
-
return factor_based_signal(
|
|
168
|
-
data_slice=data_slice,
|
|
169
|
-
position=self.position,
|
|
170
|
-
entry_price=self.entry_price,
|
|
171
|
-
entry_index=self.entry_index,
|
|
172
|
-
take_profit=self.take_profit,
|
|
173
|
-
stop_loss=self.stop_loss,
|
|
174
|
-
check_periods=1,
|
|
175
|
-
factor_func=lambda d: ma_factor(d, period=5),
|
|
176
|
-
factor_period=5
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
elif self.strategy == "rsi_factor":
|
|
180
|
-
if len(data_slice) >= 16:
|
|
181
|
-
return factor_based_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
|
-
factor_func=lambda d: rsi_factor(d, period=14),
|
|
190
|
-
factor_period=14
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
elif self.strategy == "alpha1":
|
|
194
|
-
if len(data_slice) >= 26:
|
|
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: alpha1_factor(d, lookback_days=5, stddev_period=20, power=2.0),
|
|
204
|
-
factor_period=25
|
|
205
|
-
)
|
|
206
|
-
except Exception as e:
|
|
207
|
-
logging.debug(f"计算信号时出错: {e}")
|
|
208
|
-
return None
|
|
209
|
-
|
|
210
|
-
return None
|
|
211
|
-
|
|
212
|
-
def _on_new_kline(self, kline_dict: Dict[str, Any], data_df):
|
|
213
|
-
"""
|
|
214
|
-
新K线数据回调函数
|
|
215
|
-
|
|
216
|
-
Args:
|
|
217
|
-
kline_dict: 新K线数据字典
|
|
218
|
-
data_df: 历史数据DataFrame
|
|
219
|
-
"""
|
|
220
|
-
current_price = kline_dict['close_price']
|
|
221
|
-
current_time = kline_dict['open_time_str']
|
|
222
|
-
|
|
223
|
-
# 更新总资产
|
|
224
|
-
if self.position > 0:
|
|
225
|
-
position_value = self.position * current_price
|
|
226
|
-
self.total_assets = self.current_capital + position_value
|
|
227
|
-
floating_profit_pct = (current_price - self.entry_price) / self.entry_price * 100
|
|
228
|
-
else:
|
|
229
|
-
self.total_assets = self.current_capital
|
|
230
|
-
floating_profit_pct = 0.0
|
|
231
|
-
|
|
232
|
-
# 更新最大回撤
|
|
233
|
-
if self.total_assets > self.peak_assets:
|
|
234
|
-
self.peak_assets = self.total_assets
|
|
235
|
-
drawdown = (self.peak_assets - self.total_assets) / self.peak_assets
|
|
236
|
-
if drawdown > self.max_drawdown:
|
|
237
|
-
self.max_drawdown = drawdown
|
|
238
|
-
|
|
239
|
-
# 计算交易信号
|
|
240
|
-
signal = self._calculate_signal(data_df)
|
|
241
|
-
|
|
242
|
-
# 显示状态
|
|
243
|
-
self._display_status(current_time, current_price, signal, floating_profit_pct)
|
|
244
|
-
|
|
245
|
-
# 执行交易
|
|
246
|
-
if signal == 'buy' and self.position == 0:
|
|
247
|
-
self._execute_buy(current_price, current_time, len(data_df) - 1)
|
|
248
|
-
elif signal == 'sell' and self.position > 0:
|
|
249
|
-
self._execute_sell(current_price, current_time)
|
|
250
|
-
|
|
251
|
-
self.last_update_time = datetime.now()
|
|
252
|
-
|
|
253
|
-
def _execute_buy(self, price: float, time_str: str, index: int):
|
|
254
|
-
"""
|
|
255
|
-
执行买入操作
|
|
256
|
-
|
|
257
|
-
Args:
|
|
258
|
-
price: 买入价格
|
|
259
|
-
time_str: 时间字符串
|
|
260
|
-
index: 数据索引
|
|
261
|
-
"""
|
|
262
|
-
# 计算买入金额(扣除手续费)
|
|
263
|
-
buy_amount = self.current_capital * self.position_size
|
|
264
|
-
commission = buy_amount * self.commission_rate
|
|
265
|
-
net_buy_amount = buy_amount - commission
|
|
266
|
-
|
|
267
|
-
# 计算买入数量
|
|
268
|
-
self.position = net_buy_amount / price
|
|
269
|
-
self.entry_price = price
|
|
270
|
-
self.entry_index = index
|
|
271
|
-
self.entry_time = time_str
|
|
272
|
-
|
|
273
|
-
# 更新资金
|
|
274
|
-
self.current_capital -= buy_amount
|
|
275
|
-
|
|
276
|
-
print(f"\n{'='*80}")
|
|
277
|
-
print(f"✅ 执行买入")
|
|
278
|
-
print(f" 时间: {time_str}")
|
|
279
|
-
print(f" 价格: {price:.2f}")
|
|
280
|
-
print(f" 数量: {self.position:.6f}")
|
|
281
|
-
print(f" 金额: {buy_amount:.2f}")
|
|
282
|
-
print(f" 手续费: {commission:.2f}")
|
|
283
|
-
print(f" 剩余资金: {self.current_capital:.2f}")
|
|
284
|
-
print(f"{'='*80}\n")
|
|
285
|
-
|
|
286
|
-
def _execute_sell(self, price: float, time_str: str):
|
|
287
|
-
"""
|
|
288
|
-
执行卖出操作
|
|
289
|
-
|
|
290
|
-
Args:
|
|
291
|
-
price: 卖出价格
|
|
292
|
-
time_str: 时间字符串
|
|
293
|
-
"""
|
|
294
|
-
# 计算卖出金额
|
|
295
|
-
sell_amount = self.position * price
|
|
296
|
-
commission = sell_amount * self.commission_rate
|
|
297
|
-
net_sell_amount = sell_amount - commission
|
|
298
|
-
|
|
299
|
-
# 计算盈亏
|
|
300
|
-
cost_basis = self.position * self.entry_price
|
|
301
|
-
profit_amount = net_sell_amount - cost_basis
|
|
302
|
-
profit_pct = (price - self.entry_price) / self.entry_price * 100
|
|
303
|
-
|
|
304
|
-
# 更新资金
|
|
305
|
-
self.current_capital += net_sell_amount
|
|
306
|
-
|
|
307
|
-
# 记录交易
|
|
308
|
-
trade_record = {
|
|
309
|
-
'entry_time': self.entry_time,
|
|
310
|
-
'exit_time': time_str,
|
|
311
|
-
'entry_price': self.entry_price,
|
|
312
|
-
'exit_price': price,
|
|
313
|
-
'quantity': self.position,
|
|
314
|
-
'profit_amount': profit_amount,
|
|
315
|
-
'profit_pct': profit_pct,
|
|
316
|
-
'commission': commission * 2 # 买入和卖出手续费
|
|
317
|
-
}
|
|
318
|
-
self.completed_trades.append(trade_record)
|
|
319
|
-
|
|
320
|
-
# 更新统计
|
|
321
|
-
self.total_trades += 1
|
|
322
|
-
self.total_profit += profit_amount
|
|
323
|
-
if profit_amount > 0:
|
|
324
|
-
self.win_trades += 1
|
|
325
|
-
else:
|
|
326
|
-
self.loss_trades += 1
|
|
327
|
-
|
|
328
|
-
print(f"\n{'='*80}")
|
|
329
|
-
print(f"✅ 执行卖出")
|
|
330
|
-
print(f" 时间: {time_str}")
|
|
331
|
-
print(f" 价格: {price:.2f}")
|
|
332
|
-
print(f" 入场价: {self.entry_price:.2f}")
|
|
333
|
-
print(f" 数量: {self.position:.6f}")
|
|
334
|
-
print(f" 盈亏金额: {profit_amount:+.2f}")
|
|
335
|
-
print(f" 盈亏比例: {profit_pct:+.2f}%")
|
|
336
|
-
print(f" 手续费: {commission:.2f}")
|
|
337
|
-
print(f" 当前资金: {self.current_capital:.2f}")
|
|
338
|
-
print(f"{'='*80}\n")
|
|
339
|
-
|
|
340
|
-
# 重置持仓
|
|
341
|
-
self.position = 0.0
|
|
342
|
-
self.entry_price = 0.0
|
|
343
|
-
self.entry_index = -1
|
|
344
|
-
self.entry_time = None
|
|
345
|
-
|
|
346
|
-
def _display_status(self, time_str: str, price: float, signal: Optional[str], floating_profit_pct: float):
|
|
347
|
-
"""
|
|
348
|
-
显示当前状态
|
|
349
|
-
|
|
350
|
-
Args:
|
|
351
|
-
time_str: 时间字符串
|
|
352
|
-
price: 当前价格
|
|
353
|
-
signal: 交易信号
|
|
354
|
-
floating_profit_pct: 浮动盈亏百分比
|
|
355
|
-
"""
|
|
356
|
-
# 计算统计信息
|
|
357
|
-
total_return_pct = (self.total_assets - self.initial_capital) / self.initial_capital * 100
|
|
358
|
-
runtime = datetime.now() - self.start_time
|
|
359
|
-
runtime_str = f"{runtime.days}天 {runtime.seconds // 3600}小时 {(runtime.seconds % 3600) // 60}分钟"
|
|
360
|
-
win_rate = (self.win_trades / self.total_trades * 100) if self.total_trades > 0 else 0.0
|
|
361
|
-
|
|
362
|
-
# 信号显示
|
|
363
|
-
if signal:
|
|
364
|
-
signal_emoji = "🟢" if signal == 'buy' else "🔴" if signal == 'sell' else "⚪"
|
|
365
|
-
signal_text = f"{signal_emoji} {signal.upper()}"
|
|
366
|
-
else:
|
|
367
|
-
signal_text = "⚪ HOLD"
|
|
368
|
-
|
|
369
|
-
print(f"\n{'='*80}")
|
|
370
|
-
print(f"📊 实时状态更新")
|
|
371
|
-
print(f"{'='*80}")
|
|
372
|
-
print(f"时间: {time_str}")
|
|
373
|
-
print(f"价格: {price:.2f}")
|
|
374
|
-
print(f"信号: {signal_text}")
|
|
375
|
-
if self.position > 0:
|
|
376
|
-
print(f"持仓: {self.position:.6f} | 入场价: {self.entry_price:.2f} | 浮动盈亏: {floating_profit_pct:+.2f}%")
|
|
377
|
-
else:
|
|
378
|
-
print(f"持仓: 无")
|
|
379
|
-
print(f"{'='*80}")
|
|
380
|
-
print(f"💰 账户统计:")
|
|
381
|
-
print(f" 初始资金: {self.initial_capital:.2f}")
|
|
382
|
-
print(f" 当前资金: {self.current_capital:.2f}")
|
|
383
|
-
if self.position > 0:
|
|
384
|
-
print(f" 持仓价值: {self.position * price:.2f}")
|
|
385
|
-
print(f" 总资产: {self.total_assets:.2f}")
|
|
386
|
-
print(f" 累计盈亏: {self.total_profit:+.2f} ({total_return_pct:+.2f}%)")
|
|
387
|
-
print(f" 最大回撤: {self.max_drawdown * 100:.2f}%")
|
|
388
|
-
print(f" 运行时间: {runtime_str}")
|
|
389
|
-
print(f" 总交易次数: {self.total_trades} | 盈利: {self.win_trades} | 亏损: {self.loss_trades} | 胜率: {win_rate:.2f}%")
|
|
390
|
-
print(f"{'='*80}\n")
|
|
391
|
-
|
|
392
|
-
def get_statistics(self) -> Dict[str, Any]:
|
|
393
|
-
"""
|
|
394
|
-
获取交易统计信息
|
|
395
|
-
|
|
396
|
-
Returns:
|
|
397
|
-
统计信息字典
|
|
398
|
-
"""
|
|
399
|
-
total_return_pct = (self.total_assets - self.initial_capital) / self.initial_capital * 100
|
|
400
|
-
runtime = datetime.now() - self.start_time
|
|
401
|
-
win_rate = (self.win_trades / self.total_trades * 100) if self.total_trades > 0 else 0.0
|
|
402
|
-
|
|
403
|
-
# 计算平均盈亏
|
|
404
|
-
avg_profit = self.total_profit / self.total_trades if self.total_trades > 0 else 0.0
|
|
405
|
-
|
|
406
|
-
# 计算夏普比率(简化版)
|
|
407
|
-
if len(self.completed_trades) > 0:
|
|
408
|
-
returns = [t['profit_pct'] / 100 for t in self.completed_trades]
|
|
409
|
-
import numpy as np
|
|
410
|
-
if len(returns) > 1:
|
|
411
|
-
sharpe_ratio = np.mean(returns) / np.std(returns) * np.sqrt(252) if np.std(returns) > 0 else 0.0
|
|
412
|
-
else:
|
|
413
|
-
sharpe_ratio = 0.0
|
|
414
|
-
else:
|
|
415
|
-
sharpe_ratio = 0.0
|
|
416
|
-
|
|
417
|
-
return {
|
|
418
|
-
'initial_capital': self.initial_capital,
|
|
419
|
-
'final_capital': self.total_assets,
|
|
420
|
-
'total_return': self.total_profit,
|
|
421
|
-
'total_return_pct': total_return_pct,
|
|
422
|
-
'total_trades': self.total_trades,
|
|
423
|
-
'win_trades': self.win_trades,
|
|
424
|
-
'loss_trades': self.loss_trades,
|
|
425
|
-
'win_rate': win_rate,
|
|
426
|
-
'max_drawdown': self.max_drawdown * 100,
|
|
427
|
-
'avg_profit': avg_profit,
|
|
428
|
-
'sharpe_ratio': sharpe_ratio,
|
|
429
|
-
'runtime': str(runtime),
|
|
430
|
-
'completed_trades': self.completed_trades
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
async def start(self):
|
|
434
|
-
"""
|
|
435
|
-
启动模拟交易
|
|
436
|
-
"""
|
|
437
|
-
print("="*80)
|
|
438
|
-
print("🚀 模拟实盘交易测试启动")
|
|
439
|
-
print("="*80)
|
|
440
|
-
print(f"交易对: {self.symbol}")
|
|
441
|
-
print(f"时间间隔: {self.interval}")
|
|
442
|
-
print(f"策略: {self.strategy}")
|
|
443
|
-
print(f"初始资金: {self.initial_capital:.2f}")
|
|
444
|
-
print(f"仓位大小: {self.position_size * 100:.0f}%")
|
|
445
|
-
print(f"止盈: {self.take_profit * 100:.0f}%")
|
|
446
|
-
print(f"止损: {self.stop_loss * 100:.0f}%")
|
|
447
|
-
print(f"手续费率: {self.commission_rate * 100:.4f}%")
|
|
448
|
-
print("="*80)
|
|
449
|
-
print("\n等待实时数据...\n")
|
|
450
|
-
|
|
451
|
-
await self.tracker.run_forever()
|
|
452
|
-
|
|
453
|
-
def print_final_report(self):
|
|
454
|
-
"""
|
|
455
|
-
打印最终报告
|
|
456
|
-
"""
|
|
457
|
-
stats = self.get_statistics()
|
|
458
|
-
|
|
459
|
-
print("\n" + "="*80)
|
|
460
|
-
print("📊 最终交易报告")
|
|
461
|
-
print("="*80)
|
|
462
|
-
print(f"交易对: {self.symbol}")
|
|
463
|
-
print(f"策略: {self.strategy}")
|
|
464
|
-
print(f"运行时间: {stats['runtime']}")
|
|
465
|
-
print(f"\n💰 资金统计:")
|
|
466
|
-
print(f" 初始资金: {stats['initial_capital']:.2f}")
|
|
467
|
-
print(f" 最终资产: {stats['final_capital']:.2f}")
|
|
468
|
-
print(f" 总盈亏: {stats['total_return']:+.2f}")
|
|
469
|
-
print(f" 总收益率: {stats['total_return_pct']:+.2f}%")
|
|
470
|
-
print(f"\n📈 交易统计:")
|
|
471
|
-
print(f" 总交易次数: {stats['total_trades']}")
|
|
472
|
-
print(f" 盈利次数: {stats['win_trades']}")
|
|
473
|
-
print(f" 亏损次数: {stats['loss_trades']}")
|
|
474
|
-
print(f" 胜率: {stats['win_rate']:.2f}%")
|
|
475
|
-
print(f" 平均盈亏: {stats['avg_profit']:.2f}")
|
|
476
|
-
print(f"\n📉 风险指标:")
|
|
477
|
-
print(f" 最大回撤: {stats['max_drawdown']:.2f}%")
|
|
478
|
-
print(f" 夏普比率: {stats['sharpe_ratio']:.2f}")
|
|
479
|
-
print("="*80)
|
|
480
|
-
|
|
481
|
-
# 显示最近10笔交易
|
|
482
|
-
if len(self.completed_trades) > 0:
|
|
483
|
-
print(f"\n最近10笔交易记录:")
|
|
484
|
-
print("-"*80)
|
|
485
|
-
for i, trade in enumerate(self.completed_trades[-10:], 1):
|
|
486
|
-
print(f"{i}. {trade['entry_time']} -> {trade['exit_time']}")
|
|
487
|
-
print(f" 入场: {trade['entry_price']:.2f} | 出场: {trade['exit_price']:.2f}")
|
|
488
|
-
print(f" 盈亏: {trade['profit_amount']:+.2f} ({trade['profit_pct']:+.2f}%)")
|
|
489
|
-
print("="*80)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
async def test_simulated_trading():
|
|
493
|
-
"""
|
|
494
|
-
测试模拟交易
|
|
495
|
-
"""
|
|
496
|
-
# 创建模拟交易机器人
|
|
497
|
-
bot = SimulatedTradingBot(
|
|
498
|
-
symbol="BTCUSDT",
|
|
499
|
-
interval="1m",
|
|
500
|
-
lookback_periods=100,
|
|
501
|
-
initial_capital=10000.0,
|
|
502
|
-
position_size=0.01,
|
|
503
|
-
take_profit=0.1,
|
|
504
|
-
stop_loss=0.05,
|
|
505
|
-
commission_rate=0.0001,
|
|
506
|
-
strategy="ma5", # 可选: ma5, ma_cross, ma_factor, rsi_factor, alpha1
|
|
507
|
-
ssl_verify=False
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
try:
|
|
511
|
-
# 启动交易
|
|
512
|
-
await bot.start()
|
|
513
|
-
except KeyboardInterrupt:
|
|
514
|
-
print("\n\n收到中断信号,正在停止...")
|
|
515
|
-
finally:
|
|
516
|
-
# 打印最终报告
|
|
517
|
-
bot.print_final_report()
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
def main():
|
|
521
|
-
"""
|
|
522
|
-
主函数
|
|
523
|
-
"""
|
|
524
|
-
print("="*80)
|
|
525
|
-
print("模拟实盘交易测试脚本")
|
|
526
|
-
print("="*80)
|
|
527
|
-
print("\n注意:")
|
|
528
|
-
print(" 1. 确保已安装 cyqnt_trd package")
|
|
529
|
-
print(" 2. 需要网络连接访问 Binance WebSocket")
|
|
530
|
-
print(" 3. 按 Ctrl+C 停止测试")
|
|
531
|
-
print(" 4. 测试结果将显示在控制台")
|
|
532
|
-
print()
|
|
533
|
-
|
|
534
|
-
try:
|
|
535
|
-
asyncio.run(test_simulated_trading())
|
|
536
|
-
except KeyboardInterrupt:
|
|
537
|
-
print("\n测试已停止")
|
|
538
|
-
except Exception as e:
|
|
539
|
-
print(f"\n测试过程中出现错误: {e}")
|
|
540
|
-
import traceback
|
|
541
|
-
traceback.print_exc()
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
if __name__ == "__main__":
|
|
545
|
-
main()
|
|
546
|
-
|
|
File without changes
|
|
File without changes
|