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.
- cyqnt_trd/__init__.py +26 -0
- cyqnt_trd/backtesting/README.md +264 -0
- cyqnt_trd/backtesting/__init__.py +12 -0
- cyqnt_trd/backtesting/factor_test.py +332 -0
- cyqnt_trd/backtesting/framework.py +311 -0
- cyqnt_trd/backtesting/strategy_backtest.py +545 -0
- cyqnt_trd/diagnose_api.py +28 -0
- cyqnt_trd/get_data/__init__.py +15 -0
- cyqnt_trd/get_data/get_futures_data.py +472 -0
- cyqnt_trd/get_data/get_trending_data.py +771 -0
- cyqnt_trd/online_trading/__init__.py +13 -0
- cyqnt_trd/online_trading/realtime_price_tracker.py +1001 -0
- cyqnt_trd/test.py +119 -0
- cyqnt_trd/test_script/README.md +411 -0
- cyqnt_trd/test_script/get_network_info.py +192 -0
- cyqnt_trd/test_script/get_symbols_by_volume.py +227 -0
- cyqnt_trd/test_script/realtime_price_tracker.py +839 -0
- cyqnt_trd/test_script/test_alpha.py +261 -0
- cyqnt_trd/test_script/test_kline_data.py +479 -0
- cyqnt_trd/test_script/test_order.py +1360 -0
- cyqnt_trd/trading_signal/README.md +276 -0
- cyqnt_trd/trading_signal/__init__.py +17 -0
- cyqnt_trd/trading_signal/example_test_alpha.py +430 -0
- cyqnt_trd/trading_signal/example_usage.py +431 -0
- cyqnt_trd/trading_signal/factor/__init__.py +18 -0
- cyqnt_trd/trading_signal/factor/ma_factor.py +75 -0
- cyqnt_trd/trading_signal/factor/rsi_factor.py +56 -0
- cyqnt_trd/trading_signal/selected_alpha/__init__.py +158 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha1.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha10.py +90 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha100.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha101.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha11.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha12.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha13.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha14.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha15.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha16.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha17.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha18.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha19.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha2.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha20.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha21.py +89 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha22.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha23.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha24.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha25.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha26.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha27.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha28.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha29.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha3.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha30.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha31.py +90 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha32.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha33.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha34.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha35.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha36.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha37.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha38.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha39.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha4.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha40.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha41.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha42.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha43.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha44.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha45.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha46.py +89 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha47.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha48.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha49.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha5.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha50.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha51.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha52.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha53.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha54.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha55.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha56.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha57.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha58.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha59.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha6.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha60.py +89 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha61.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha62.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha63.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha64.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha65.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha66.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha67.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha68.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha69.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha7.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha70.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha71.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha72.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha73.py +91 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha74.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha75.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha76.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha77.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha78.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha79.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha8.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha80.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha81.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha82.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha83.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha84.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha85.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha86.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha87.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha88.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha89.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha9.py +90 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha90.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha91.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha92.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha93.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha94.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha95.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha96.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha97.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha98.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha99.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha_utils.py +342 -0
- cyqnt_trd/trading_signal/selected_alpha/create_all_alphas.py +279 -0
- cyqnt_trd/trading_signal/selected_alpha/generate_alphas.py +133 -0
- cyqnt_trd/trading_signal/selected_alpha/test_alpha.py +261 -0
- cyqnt_trd/trading_signal/signal/__init__.py +20 -0
- cyqnt_trd/trading_signal/signal/factor_based_signal.py +387 -0
- cyqnt_trd/trading_signal/signal/ma_signal.py +163 -0
- cyqnt_trd/utils/__init__.py +3 -0
- cyqnt_trd/utils/set_user.py +33 -0
- cyqnt_trd-0.1.2.dist-info/METADATA +148 -0
- cyqnt_trd-0.1.2.dist-info/RECORD +147 -0
- cyqnt_trd-0.1.2.dist-info/WHEEL +5 -0
- cyqnt_trd-0.1.2.dist-info/licenses/LICENSE +21 -0
- cyqnt_trd-0.1.2.dist-info/top_level.txt +2 -0
- test/real_time_trade.py +746 -0
- test/test_example_usage.py +381 -0
- test/test_get_data.py +310 -0
- 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
|
+
|