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,311 @@
1
+ """
2
+ 回测框架主模块
3
+
4
+ 提供统一的回测框架接口
5
+ """
6
+
7
+ import pandas as pd
8
+ import json
9
+ import os
10
+ import re
11
+ from typing import Callable, Optional, Dict, Tuple
12
+ from .factor_test import FactorTester
13
+ from .strategy_backtest import StrategyBacktester
14
+
15
+
16
+ class BacktestFramework:
17
+ """
18
+ 回测框架主类
19
+
20
+ 提供统一的接口来使用因子测试和策略回测功能
21
+ """
22
+
23
+ def __init__(self, data_path: Optional[str] = None, data: Optional[pd.DataFrame] = None):
24
+ """
25
+ 初始化回测框架
26
+
27
+ Args:
28
+ data_path: JSON数据文件路径(可选)
29
+ data: 直接提供DataFrame数据(可选)
30
+
31
+ 注意: data_path 和 data 必须提供其中一个
32
+ """
33
+ self.data_path = data_path
34
+ self.source_start_time = None
35
+ self.source_end_time = None
36
+
37
+ if data_path:
38
+ # 从JSON文件加载数据
39
+ with open(data_path, 'r', encoding='utf-8') as f:
40
+ json_data = json.load(f)
41
+
42
+ if isinstance(json_data, dict) and 'data' in json_data:
43
+ self.data = pd.DataFrame(json_data['data'])
44
+ # 从源数据中提取时间范围
45
+ if len(json_data['data']) > 0:
46
+ first_item = json_data['data'][0]
47
+ last_item = json_data['data'][-1]
48
+
49
+ # 提取开始时间(使用第一个数据的open_time_str)
50
+ if 'open_time_str' in first_item:
51
+ try:
52
+ self.source_start_time = pd.to_datetime(first_item['open_time_str'])
53
+ except Exception:
54
+ pass
55
+
56
+ # 提取结束时间(使用最后一个数据的close_time_str)
57
+ if 'close_time_str' in last_item:
58
+ try:
59
+ self.source_end_time = pd.to_datetime(last_item['close_time_str'])
60
+ except Exception:
61
+ pass
62
+ else:
63
+ self.data = pd.DataFrame(json_data)
64
+
65
+ elif data is not None:
66
+ self.data = data.copy()
67
+ else:
68
+ raise ValueError("必须提供 data_path 或 data 参数")
69
+
70
+ # 初始化测试器
71
+ self.factor_tester = FactorTester(self.data)
72
+ self.strategy_backtester = StrategyBacktester(self.data)
73
+
74
+ def _get_source_time_range(self) -> Tuple[Optional[str], Optional[str]]:
75
+ """
76
+ 获取源数据的时间范围
77
+
78
+ Returns:
79
+ (start_str, end_str): 开始日期和结束日期的字符串(格式:YYYYMMDD),如果无法获取则返回 (None, None)
80
+ """
81
+ if self.source_start_time is not None and self.source_end_time is not None:
82
+ try:
83
+ start_str = self.source_start_time.strftime('%Y%m%d %H%M%S')
84
+ end_str = self.source_end_time.strftime('%Y%m%d %H%M%S')
85
+ return start_str, end_str
86
+ except Exception:
87
+ pass
88
+ return None, None
89
+
90
+ def _extract_data_name(self, data_path: Optional[str] = None) -> Optional[str]:
91
+ """
92
+ 从数据路径中提取数据名称
93
+
94
+ Args:
95
+ data_path: 数据文件路径(如果为None,使用self.data_path)
96
+
97
+ Returns:
98
+ 数据名称(如 ETHUSDT),如果无法提取则返回None
99
+ """
100
+ path = data_path or self.data_path
101
+ if not path:
102
+ return None
103
+
104
+ # 从文件名中提取,例如:ETHUSDT_1d_1095_20251127_114210.json -> ETHUSDT
105
+ filename = os.path.basename(path)
106
+ # 匹配交易对名称(通常是大写字母+数字的组合,如 BTCUSDT, ETHUSDT)
107
+ match = re.match(r'^([A-Z0-9]+)', filename)
108
+ if match:
109
+ return match.group(1)
110
+
111
+ # 如果文件名不匹配,尝试从路径中提取
112
+ # 例如:.../ETHUSDT_futures/... -> ETHUSDT
113
+ path_parts = path.split(os.sep)
114
+ for part in path_parts:
115
+ if '_' in part:
116
+ # 尝试提取交易对名称
117
+ match = re.match(r'^([A-Z0-9]+)', part)
118
+ if match:
119
+ return match.group(1)
120
+
121
+ return None
122
+
123
+ def test_factor(
124
+ self,
125
+ factor_func: Callable[[pd.DataFrame, int], float],
126
+ forward_periods: int = 7,
127
+ min_periods: int = 0,
128
+ factor_name: str = "factor"
129
+ ) -> Dict:
130
+ """
131
+ 测试单因子胜率
132
+
133
+ Args:
134
+ factor_func: 因子计算函数,接受数据切片作为参数
135
+ (data_slice: pd.DataFrame) -> float
136
+ forward_periods: 向前看的周期数
137
+ min_periods: 最小需要的周期数
138
+ factor_name: 因子名称
139
+
140
+ Returns:
141
+ 测试结果字典
142
+ """
143
+ return self.factor_tester.test_factor(
144
+ factor_func=factor_func,
145
+ forward_periods=forward_periods,
146
+ min_periods=min_periods,
147
+ factor_name=factor_name
148
+ )
149
+
150
+ def backtest_strategy(
151
+ self,
152
+ signal_func: Callable,
153
+ min_periods: int = 0,
154
+ position_size: float = 1.0,
155
+ initial_capital: float = 10000.0,
156
+ commission_rate: float = 0.001,
157
+ take_profit: Optional[float] = None,
158
+ stop_loss: Optional[float] = None,
159
+ check_periods: int = 1,
160
+ strategy_name: Optional[str] = None
161
+ ) -> Dict:
162
+ """
163
+ 回测策略
164
+
165
+ Args:
166
+ signal_func: 信号生成函数,必须接受以下参数:
167
+ (data_slice, position, entry_price, entry_index, take_profit, stop_loss, check_periods) -> str
168
+ - data_slice: 数据切片(包含历史数据和当前数据点)
169
+ - position: 当前持仓数量
170
+ - entry_price: 入场价格
171
+ - entry_index: 入场索引
172
+ - take_profit: 止盈比例
173
+ - stop_loss: 止损比例
174
+ - check_periods: 检查未来多少个周期(只能为1,因为实际使用时无法看到未来数据)
175
+ 策略函数可以根据持仓信息自行决定是否止盈止损
176
+ min_periods: 最小需要的周期数
177
+ position_size: 每次交易的仓位大小
178
+ initial_capital: 初始资金
179
+ commission_rate: 手续费率
180
+ take_profit: 止盈比例(例如:0.1 表示 10%),传递给策略函数,由策略决定是否使用
181
+ stop_loss: 止损比例(例如:0.1 表示 10%),传递给策略函数,由策略决定是否使用
182
+ check_periods: 检查未来多少个周期(只能为1,默认1,即只检查当前周期)
183
+ 注意:回测时只能为1,因为实际交易中无法看到今天之后的数据
184
+ strategy_name: 策略名称(用于保存结果时的文件命名)
185
+
186
+ Returns:
187
+ 回测结果字典(包含 strategy_name 字段)
188
+
189
+ Raises:
190
+ ValueError: 如果 check_periods 不等于 1
191
+ """
192
+ # 验证 check_periods 只能为 1
193
+ if check_periods != 1:
194
+ raise ValueError(
195
+ f"回测策略时 check_periods 只能为 1,因为实际使用时无法看到今天之后的数据。"
196
+ f"当前值: {check_periods}"
197
+ )
198
+
199
+ # 更新初始资金和手续费率
200
+ self.strategy_backtester.initial_capital = initial_capital
201
+ self.strategy_backtester.commission_rate = commission_rate
202
+
203
+ results = self.strategy_backtester.backtest(
204
+ signal_func=signal_func,
205
+ min_periods=min_periods,
206
+ position_size=position_size,
207
+ take_profit=take_profit,
208
+ stop_loss=stop_loss,
209
+ check_periods=check_periods
210
+ )
211
+
212
+ # 将策略名称添加到结果中
213
+ if strategy_name:
214
+ results['strategy_name'] = strategy_name
215
+
216
+ return results
217
+
218
+ def plot_backtest_results(
219
+ self,
220
+ results: Dict,
221
+ figsize: tuple = (14, 10),
222
+ save_dir: Optional[str] = None,
223
+ strategy_name: Optional[str] = None,
224
+ data_name: Optional[str] = None
225
+ ):
226
+ """
227
+ 绘制回测结果
228
+
229
+ Args:
230
+ results: backtest_strategy返回的结果字典
231
+ figsize: 图形大小
232
+ save_dir: 保存目录(如果提供,将自动保存图片和JSON)
233
+ strategy_name: 策略名称(如果为None,尝试从results中获取)
234
+ data_name: 数据名称(如果为None,尝试从数据路径中提取)
235
+ """
236
+ # 获取策略名称
237
+ if strategy_name is None:
238
+ strategy_name = results.get('strategy_name')
239
+
240
+ # 获取数据名称
241
+ if data_name is None:
242
+ data_name = self._extract_data_name()
243
+
244
+ # 如果提供了save_dir但没有strategy_name或data_name,打印警告
245
+ if save_dir and (not strategy_name or not data_name):
246
+ if not strategy_name:
247
+ print(f"警告: 无法获取策略名称,将使用默认名称保存图片")
248
+ if not data_name:
249
+ print(f"警告: 无法从数据路径中提取数据名称,将使用默认名称保存图片")
250
+ print(f" 数据路径: {self.data_path}")
251
+
252
+ # 获取源数据的时间范围
253
+ source_start_str, source_end_str = self._get_source_time_range()
254
+
255
+ # 调用plot_results,如果提供了策略名称和数据名称,将自动保存
256
+ self.strategy_backtester.plot_results(
257
+ results=results,
258
+ figsize=figsize,
259
+ strategy_name=strategy_name,
260
+ data_name=data_name,
261
+ save_dir=save_dir,
262
+ source_start_str=source_start_str,
263
+ source_end_str=source_end_str
264
+ )
265
+
266
+ def print_factor_results(
267
+ self,
268
+ results: Dict,
269
+ save_dir: Optional[str] = None,
270
+ factor_name: Optional[str] = None,
271
+ data_name: Optional[str] = None
272
+ ):
273
+ """
274
+ 打印因子测试结果
275
+
276
+ Args:
277
+ results: test_factor返回的结果字典
278
+ save_dir: 保存目录(如果提供,将自动保存JSON)
279
+ factor_name: 因子名称(如果为None,尝试从results中获取)
280
+ data_name: 数据名称(如果为None,尝试从数据路径中提取)
281
+ """
282
+ # 获取因子名称
283
+ if factor_name is None:
284
+ factor_name = results.get('factor_name')
285
+
286
+ # 获取数据名称
287
+ if data_name is None:
288
+ data_name = self._extract_data_name()
289
+
290
+ # 获取源数据的时间范围
291
+ source_start_str, source_end_str = self._get_source_time_range()
292
+
293
+ # 调用print_results,如果提供了保存目录,将自动保存
294
+ self.factor_tester.print_results(
295
+ results=results,
296
+ save_dir=save_dir,
297
+ factor_name=factor_name,
298
+ data_name=data_name,
299
+ source_start_str=source_start_str,
300
+ source_end_str=source_end_str
301
+ )
302
+
303
+ def print_backtest_results(self, results: Dict):
304
+ """
305
+ 打印回测结果
306
+
307
+ Args:
308
+ results: backtest_strategy返回的结果字典
309
+ """
310
+ self.strategy_backtester.print_results(results)
311
+