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,342 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Alpha因子计算辅助函数模块
|
|
3
|
+
|
|
4
|
+
提供所有alpha因子计算所需的辅助函数,包括时间序列函数、统计函数等。
|
|
5
|
+
这些函数适配crypto交易数据格式(open_price, high_price, low_price, close_price, volume)。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from scipy.stats import rankdata
|
|
11
|
+
from typing import Union
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ts_sum(series: pd.Series, window: int = 10) -> pd.Series:
|
|
15
|
+
"""
|
|
16
|
+
时间序列滚动求和
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
series: pandas Series
|
|
20
|
+
window: 滚动窗口大小
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
滚动求和结果
|
|
24
|
+
"""
|
|
25
|
+
return series.rolling(window).sum()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def sma(series: pd.Series, window: int = 10) -> pd.Series:
|
|
29
|
+
"""
|
|
30
|
+
简单移动平均
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
series: pandas Series
|
|
34
|
+
window: 滚动窗口大小
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
移动平均结果
|
|
38
|
+
"""
|
|
39
|
+
return series.rolling(window).mean()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def stddev(series: pd.Series, window: int = 10) -> pd.Series:
|
|
43
|
+
"""
|
|
44
|
+
滚动标准差
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
series: pandas Series
|
|
48
|
+
window: 滚动窗口大小
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
滚动标准差结果
|
|
52
|
+
"""
|
|
53
|
+
return series.rolling(window).std()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def correlation(x: pd.Series, y: pd.Series, window: int = 10) -> pd.Series:
|
|
57
|
+
"""
|
|
58
|
+
滚动相关系数
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
x: 第一个序列
|
|
62
|
+
y: 第二个序列
|
|
63
|
+
window: 滚动窗口大小
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
滚动相关系数结果
|
|
67
|
+
"""
|
|
68
|
+
return x.rolling(window).corr(y)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def covariance(x: pd.Series, y: pd.Series, window: int = 10) -> pd.Series:
|
|
72
|
+
"""
|
|
73
|
+
滚动协方差
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
x: 第一个序列
|
|
77
|
+
y: 第二个序列
|
|
78
|
+
window: 滚动窗口大小
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
滚动协方差结果
|
|
82
|
+
"""
|
|
83
|
+
return x.rolling(window).cov(y)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def rolling_rank(na: np.ndarray) -> float:
|
|
87
|
+
"""
|
|
88
|
+
辅助函数:用于rolling apply计算rank
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
na: numpy数组
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
最后一个值的rank
|
|
95
|
+
"""
|
|
96
|
+
if len(na) == 0:
|
|
97
|
+
return np.nan
|
|
98
|
+
return float(rankdata(na)[-1])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def ts_rank(series: pd.Series, window: int = 10) -> pd.Series:
|
|
102
|
+
"""
|
|
103
|
+
时间序列滚动rank
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
series: pandas Series
|
|
107
|
+
window: 滚动窗口大小
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
滚动rank结果
|
|
111
|
+
"""
|
|
112
|
+
return series.rolling(window).apply(rolling_rank, raw=True)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def rolling_prod(na: np.ndarray) -> float:
|
|
116
|
+
"""
|
|
117
|
+
辅助函数:计算数组的乘积
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
na: numpy数组
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
数组元素的乘积
|
|
124
|
+
"""
|
|
125
|
+
return float(np.prod(na))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def product(series: pd.Series, window: int = 10) -> pd.Series:
|
|
129
|
+
"""
|
|
130
|
+
滚动乘积
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
series: pandas Series
|
|
134
|
+
window: 滚动窗口大小
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
滚动乘积结果
|
|
138
|
+
"""
|
|
139
|
+
return series.rolling(window).apply(rolling_prod, raw=True)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def ts_min(series: pd.Series, window: int = 10) -> pd.Series:
|
|
143
|
+
"""
|
|
144
|
+
滚动最小值
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
series: pandas Series
|
|
148
|
+
window: 滚动窗口大小
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
滚动最小值结果
|
|
152
|
+
"""
|
|
153
|
+
return series.rolling(window).min()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def ts_max(series: pd.Series, window: int = 10) -> pd.Series:
|
|
157
|
+
"""
|
|
158
|
+
滚动最大值
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
series: pandas Series
|
|
162
|
+
window: 滚动窗口大小
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
滚动最大值结果
|
|
166
|
+
"""
|
|
167
|
+
return series.rolling(window).max()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def delta(series: pd.Series, period: int = 1) -> pd.Series:
|
|
171
|
+
"""
|
|
172
|
+
差分(当前值减去period期前的值)
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
series: pandas Series
|
|
176
|
+
period: 差分周期
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
差分结果
|
|
180
|
+
"""
|
|
181
|
+
return series.diff(period)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def delay(series: pd.Series, period: int = 1) -> pd.Series:
|
|
185
|
+
"""
|
|
186
|
+
滞后(shift)
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
series: pandas Series
|
|
190
|
+
period: 滞后周期
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
滞后结果
|
|
194
|
+
"""
|
|
195
|
+
return series.shift(period)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def rank(series: pd.Series) -> pd.Series:
|
|
199
|
+
"""
|
|
200
|
+
截面rank(百分比rank)
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
series: pandas Series
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
百分比rank结果(0-1之间)
|
|
207
|
+
"""
|
|
208
|
+
return series.rank(pct=True)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def scale(series: pd.Series, k: float = 1.0) -> pd.Series:
|
|
212
|
+
"""
|
|
213
|
+
缩放时间序列,使得sum(abs(series)) = k
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
series: pandas Series
|
|
217
|
+
k: 缩放因子
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
缩放后的序列
|
|
221
|
+
"""
|
|
222
|
+
abs_sum = series.abs().sum()
|
|
223
|
+
if abs_sum == 0 or np.isnan(abs_sum):
|
|
224
|
+
return series * 0
|
|
225
|
+
return series.mul(k).div(abs_sum)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def ts_argmax(series: pd.Series, window: int = 10) -> pd.Series:
|
|
229
|
+
"""
|
|
230
|
+
滚动窗口内最大值的索引位置(从1开始)
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
series: pandas Series
|
|
234
|
+
window: 滚动窗口大小
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
最大值索引位置(从1开始)
|
|
238
|
+
"""
|
|
239
|
+
return series.rolling(window).apply(lambda x: np.argmax(x) + 1 if len(x) > 0 else np.nan, raw=True)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def ts_argmin(series: pd.Series, window: int = 10) -> pd.Series:
|
|
243
|
+
"""
|
|
244
|
+
滚动窗口内最小值的索引位置(从1开始)
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
series: pandas Series
|
|
248
|
+
window: 滚动窗口大小
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
最小值索引位置(从1开始)
|
|
252
|
+
"""
|
|
253
|
+
return series.rolling(window).apply(lambda x: np.argmin(x) + 1 if len(x) > 0 else np.nan, raw=True)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def decay_linear(series: pd.Series, period: int = 10) -> pd.Series:
|
|
257
|
+
"""
|
|
258
|
+
线性加权移动平均(LWMA)
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
series: pandas Series
|
|
262
|
+
period: LWMA周期
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
LWMA结果
|
|
266
|
+
"""
|
|
267
|
+
# 清理数据
|
|
268
|
+
series_clean = series.ffill().bfill().fillna(0)
|
|
269
|
+
|
|
270
|
+
result = pd.Series(index=series.index, dtype=float)
|
|
271
|
+
|
|
272
|
+
divisor = period * (period + 1) / 2.0
|
|
273
|
+
weights = np.arange(1, period + 1) / divisor
|
|
274
|
+
|
|
275
|
+
for i in range(period - 1, len(series_clean)):
|
|
276
|
+
window_data = series_clean.iloc[i - period + 1:i + 1].values
|
|
277
|
+
if len(window_data) == period:
|
|
278
|
+
result.iloc[i] = np.dot(window_data, weights)
|
|
279
|
+
else:
|
|
280
|
+
result.iloc[i] = np.nan
|
|
281
|
+
|
|
282
|
+
return result
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def sign(x: Union[float, pd.Series]) -> Union[float, pd.Series]:
|
|
286
|
+
"""
|
|
287
|
+
符号函数
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
x: 输入值或序列
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
符号(-1, 0, 或 1)
|
|
294
|
+
"""
|
|
295
|
+
return np.sign(x)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def abs(x: Union[float, pd.Series]) -> Union[float, pd.Series]:
|
|
299
|
+
"""
|
|
300
|
+
绝对值
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
x: 输入值或序列
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
绝对值
|
|
307
|
+
"""
|
|
308
|
+
return np.abs(x)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def log(x: Union[float, pd.Series]) -> Union[float, pd.Series]:
|
|
312
|
+
"""
|
|
313
|
+
自然对数
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
x: 输入值或序列
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
自然对数
|
|
320
|
+
"""
|
|
321
|
+
if isinstance(x, pd.Series):
|
|
322
|
+
return x.apply(lambda v: np.log(v) if v > 0 else np.nan)
|
|
323
|
+
else:
|
|
324
|
+
return np.log(x) if x > 0 else np.nan
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def signed_power(x: Union[float, pd.Series], power: float) -> Union[float, pd.Series]:
|
|
328
|
+
"""
|
|
329
|
+
带符号的幂运算
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
x: 输入值或序列
|
|
333
|
+
power: 幂次
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
带符号的幂运算结果
|
|
337
|
+
"""
|
|
338
|
+
if isinstance(x, pd.Series):
|
|
339
|
+
return x.apply(lambda v: np.sign(v) * (np.abs(v) ** power))
|
|
340
|
+
else:
|
|
341
|
+
return np.sign(x) * (np.abs(x) ** power)
|
|
342
|
+
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""
|
|
2
|
+
创建所有101个Alpha因子文件的脚本
|
|
3
|
+
|
|
4
|
+
从alpha_cal_reference.py读取实现,转换为适配crypto数据格式的alpha因子文件。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
# 添加路径以便导入参考文件
|
|
12
|
+
sys.path.insert(0, '/Users/user/Desktop/repo/crypto_trading')
|
|
13
|
+
|
|
14
|
+
def convert_alpha_code(code, alpha_num):
|
|
15
|
+
"""
|
|
16
|
+
将参考代码转换为适配crypto数据格式的代码
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
code: 原始alpha实现代码
|
|
20
|
+
alpha_num: alpha编号
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
转换后的代码字符串
|
|
24
|
+
"""
|
|
25
|
+
# 替换变量名
|
|
26
|
+
code = code.replace('self.open', 'open_price')
|
|
27
|
+
code = code.replace('self.high', 'high_price')
|
|
28
|
+
code = code.replace('self.low', 'low_price')
|
|
29
|
+
code = code.replace('self.close', 'close_price')
|
|
30
|
+
code = code.replace('self.volume', 'volume')
|
|
31
|
+
code = code.replace('self.returns', 'returns')
|
|
32
|
+
code = code.replace('self.vwap', 'vwap')
|
|
33
|
+
|
|
34
|
+
# 处理DataFrame操作 - 移除.to_frame()和.CLOSE
|
|
35
|
+
code = re.sub(r'\.to_frame\(\)', '', code)
|
|
36
|
+
code = re.sub(r'\.CLOSE', '', code)
|
|
37
|
+
|
|
38
|
+
# 处理DataFrame创建 - 转换为Series
|
|
39
|
+
# 处理 pd.DataFrame(np.ones_like(...), index=..., columns=...) 的情况
|
|
40
|
+
code = re.sub(
|
|
41
|
+
r'pd\.DataFrame\(np\.(ones|zeros)_like\(([^)]+)\),\s*index=([^,)]+)(?:,\s*columns=[^)]+)?\)',
|
|
42
|
+
r'pd.Series(np.\1_like(\2), index=\3)',
|
|
43
|
+
code
|
|
44
|
+
)
|
|
45
|
+
code = re.sub(
|
|
46
|
+
r'pd\.DataFrame\(np\.(ones|zeros)_like\(([^)]+)\),\s*index=([^,)]+)\)',
|
|
47
|
+
r'pd.Series(np.\1_like(\2), index=\3)',
|
|
48
|
+
code
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# 处理DataFrame索引操作
|
|
52
|
+
code = re.sub(r'alpha\.at\[([^\]]+),[\'"]close[\'"]\]', r'alpha[\1]', code)
|
|
53
|
+
code = re.sub(r'alpha\[([^\]]+)\] =', r'alpha[\1] =', code)
|
|
54
|
+
|
|
55
|
+
# 处理pow方法 - Series的pow需要特殊处理
|
|
56
|
+
code = re.sub(r'(\w+)\.pow\((\d+)\)', r'(\1 ** \2)', code)
|
|
57
|
+
|
|
58
|
+
# 处理sum函数 - 确保使用ts_sum
|
|
59
|
+
code = re.sub(r'\bsum\((\w+), (\d+)\)', r'ts_sum(\1, \2)', code)
|
|
60
|
+
|
|
61
|
+
# 处理return语句 - 转换为result赋值
|
|
62
|
+
if 'return' in code:
|
|
63
|
+
# 提取所有return语句,保留最后一个
|
|
64
|
+
return_matches = list(re.finditer(r'return\s+(.+)', code, re.DOTALL))
|
|
65
|
+
if return_matches:
|
|
66
|
+
# 使用最后一个return
|
|
67
|
+
last_return = return_matches[-1]
|
|
68
|
+
return_expr = last_return.group(1).strip()
|
|
69
|
+
# 移除所有return语句
|
|
70
|
+
code = re.sub(r'return\s+.+', '', code, flags=re.DOTALL)
|
|
71
|
+
# 添加result赋值
|
|
72
|
+
code = code.rstrip() + f"\n result = {return_expr}"
|
|
73
|
+
else:
|
|
74
|
+
# 如果没有找到return表达式,添加result = None
|
|
75
|
+
code = code + "\n result = None"
|
|
76
|
+
else:
|
|
77
|
+
# 如果代码没有return,查找最后一个赋值
|
|
78
|
+
lines = [l.strip() for l in code.split('\n') if l.strip() and not l.strip().startswith('#')]
|
|
79
|
+
if lines:
|
|
80
|
+
last_line = lines[-1]
|
|
81
|
+
if '=' in last_line:
|
|
82
|
+
# 提取变量名
|
|
83
|
+
var_name = last_line.split('=')[0].strip()
|
|
84
|
+
code = code + f"\n result = {var_name}"
|
|
85
|
+
else:
|
|
86
|
+
# 假设最后一行是表达式
|
|
87
|
+
code = code + f"\n result = {last_line}"
|
|
88
|
+
else:
|
|
89
|
+
code = code + "\n result = None"
|
|
90
|
+
|
|
91
|
+
return code
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def extract_alpha_implementation(ref_file_path, alpha_num):
|
|
95
|
+
"""
|
|
96
|
+
从参考文件中提取指定alpha的实现
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
ref_file_path: 参考文件路径
|
|
100
|
+
alpha_num: alpha编号(如1, 2, 101)
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
alpha实现代码,如果没有找到则返回None
|
|
104
|
+
"""
|
|
105
|
+
with open(ref_file_path, 'r', encoding='utf-8') as f:
|
|
106
|
+
content = f.read()
|
|
107
|
+
|
|
108
|
+
# 构建alpha方法名
|
|
109
|
+
alpha_name = f'alpha{alpha_num:03d}'
|
|
110
|
+
|
|
111
|
+
# 查找alpha方法
|
|
112
|
+
pattern = rf'def {alpha_name}\(self\):(.*?)(?=\n # Alpha#|\n def alpha|\Z)'
|
|
113
|
+
match = re.search(pattern, content, re.DOTALL)
|
|
114
|
+
|
|
115
|
+
if match:
|
|
116
|
+
code = match.group(1).strip()
|
|
117
|
+
# 提取注释中的公式
|
|
118
|
+
formula_pattern = rf'# Alpha#{alpha_num}\s+(.+?)\n'
|
|
119
|
+
formula_match = re.search(formula_pattern, content)
|
|
120
|
+
formula = formula_match.group(1).strip() if formula_match else f"Alpha#{alpha_num} formula"
|
|
121
|
+
return code, formula
|
|
122
|
+
|
|
123
|
+
return None, None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def generate_alpha_file(alpha_num, implementation_code=None, formula=""):
|
|
127
|
+
"""
|
|
128
|
+
生成单个alpha文件
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
alpha_num: alpha编号
|
|
132
|
+
implementation_code: 实现代码(如果为None则生成占位符)
|
|
133
|
+
formula: 公式描述
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
if implementation_code:
|
|
137
|
+
# 转换代码
|
|
138
|
+
converted_code = convert_alpha_code(implementation_code, alpha_num)
|
|
139
|
+
|
|
140
|
+
# 生成完整实现
|
|
141
|
+
code_body = f""" # 实现Alpha因子逻辑
|
|
142
|
+
{converted_code}
|
|
143
|
+
|
|
144
|
+
# 返回最后一个值(如果是Series)或直接返回值
|
|
145
|
+
if isinstance(result, pd.Series):
|
|
146
|
+
result_value = result.iloc[-1] if len(result) > 0 else 0.0
|
|
147
|
+
elif isinstance(result, (int, float, np.number)):
|
|
148
|
+
result_value = float(result)
|
|
149
|
+
else:
|
|
150
|
+
result_value = 0.0
|
|
151
|
+
|
|
152
|
+
# 处理NaN和无穷大
|
|
153
|
+
if pd.isna(result_value) or np.isinf(result_value):
|
|
154
|
+
return 0.0
|
|
155
|
+
|
|
156
|
+
return float(result_value)"""
|
|
157
|
+
else:
|
|
158
|
+
# 生成占位符实现
|
|
159
|
+
code_body = f""" # TODO: 实现Alpha#{alpha_num}因子
|
|
160
|
+
# 公式: {formula}
|
|
161
|
+
# 注意:此alpha在参考文件中未找到实现,需要手动实现
|
|
162
|
+
|
|
163
|
+
return 0.0"""
|
|
164
|
+
|
|
165
|
+
file_content = f'''"""
|
|
166
|
+
Alpha#{alpha_num} 因子
|
|
167
|
+
|
|
168
|
+
公式: {formula}
|
|
169
|
+
|
|
170
|
+
说明:
|
|
171
|
+
此alpha因子基于WorldQuant的101个alpha因子公式实现。
|
|
172
|
+
适配crypto交易数据格式(open_price, high_price, low_price, close_price, volume, quote_volume)。
|
|
173
|
+
|
|
174
|
+
标签:待补充
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
import pandas as pd
|
|
178
|
+
import numpy as np
|
|
179
|
+
from typing import Optional
|
|
180
|
+
from .alpha_utils import (
|
|
181
|
+
ts_sum, sma, stddev, correlation, covariance,
|
|
182
|
+
ts_rank, product, ts_min, ts_max, delta, delay,
|
|
183
|
+
rank, scale, ts_argmax, ts_argmin, decay_linear,
|
|
184
|
+
sign, abs, log, signed_power
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def alpha{alpha_num}_factor(
|
|
189
|
+
data_slice: pd.DataFrame,
|
|
190
|
+
**kwargs
|
|
191
|
+
) -> float:
|
|
192
|
+
"""
|
|
193
|
+
Alpha#{alpha_num} 因子计算
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
data_slice: 数据切片,必须包含以下列:
|
|
197
|
+
- open_price: 开盘价
|
|
198
|
+
- high_price: 最高价
|
|
199
|
+
- low_price: 最低价
|
|
200
|
+
- close_price: 收盘价
|
|
201
|
+
- volume: 成交量
|
|
202
|
+
- quote_volume: 成交额(用于计算vwap)
|
|
203
|
+
**kwargs: 其他可选参数
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
因子值(最后一个时间点的值)
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
if len(data_slice) < 2:
|
|
210
|
+
return 0.0
|
|
211
|
+
|
|
212
|
+
# 提取数据列
|
|
213
|
+
open_price = data_slice['open_price']
|
|
214
|
+
high_price = data_slice['high_price']
|
|
215
|
+
low_price = data_slice['low_price']
|
|
216
|
+
close_price = data_slice['close_price']
|
|
217
|
+
volume = data_slice['volume']
|
|
218
|
+
quote_volume = data_slice.get('quote_volume', volume * close_price) # 如果没有quote_volume,使用volume*close_price估算
|
|
219
|
+
|
|
220
|
+
# 计算收益率
|
|
221
|
+
returns = close_price.pct_change().fillna(0)
|
|
222
|
+
|
|
223
|
+
# 计算VWAP (Volume Weighted Average Price)
|
|
224
|
+
# vwap = quote_volume / volume,如果volume为0则使用close_price
|
|
225
|
+
vwap = (quote_volume / (volume + 1e-10)).fillna(close_price)
|
|
226
|
+
|
|
227
|
+
# 计算adv20 (20日平均成交量)
|
|
228
|
+
adv20 = sma(volume, 20)
|
|
229
|
+
|
|
230
|
+
{code_body}
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
# 如果计算出错,返回0
|
|
234
|
+
return 0.0
|
|
235
|
+
'''
|
|
236
|
+
|
|
237
|
+
return file_content
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def main():
|
|
241
|
+
"""主函数:生成所有101个alpha文件"""
|
|
242
|
+
ref_file_path = '/Users/user/Desktop/repo/crypto_trading/alpha_cal_reference.py'
|
|
243
|
+
output_dir = '/Users/user/Desktop/repo/crypto_trading/cyqnt_trd/trading_signal/selected_alpha'
|
|
244
|
+
|
|
245
|
+
# 从参考文件中提取所有alpha编号
|
|
246
|
+
with open(ref_file_path, 'r', encoding='utf-8') as f:
|
|
247
|
+
content = f.read()
|
|
248
|
+
|
|
249
|
+
# 查找所有alpha方法
|
|
250
|
+
alpha_pattern = r'def alpha(\d+)\(self\):'
|
|
251
|
+
found_alphas = set(int(match.group(1)) for match in re.finditer(alpha_pattern, content))
|
|
252
|
+
|
|
253
|
+
print(f"找到 {len(found_alphas)} 个alpha实现")
|
|
254
|
+
|
|
255
|
+
# 生成所有101个alpha文件
|
|
256
|
+
for alpha_num in range(1, 102):
|
|
257
|
+
# 提取实现
|
|
258
|
+
if alpha_num in found_alphas:
|
|
259
|
+
impl_code, formula = extract_alpha_implementation(ref_file_path, alpha_num)
|
|
260
|
+
else:
|
|
261
|
+
impl_code, formula = None, f"Alpha#{alpha_num} (未在参考文件中找到)"
|
|
262
|
+
|
|
263
|
+
# 生成文件内容
|
|
264
|
+
file_content = generate_alpha_file(alpha_num, impl_code, formula)
|
|
265
|
+
|
|
266
|
+
# 写入文件
|
|
267
|
+
file_path = os.path.join(output_dir, f"alpha{alpha_num}.py")
|
|
268
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
269
|
+
f.write(file_content)
|
|
270
|
+
|
|
271
|
+
status = "✓" if alpha_num in found_alphas else "○"
|
|
272
|
+
print(f"{status} Generated alpha{alpha_num}.py")
|
|
273
|
+
|
|
274
|
+
print(f"\n完成!已生成101个alpha文件到 {output_dir}")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
if __name__ == "__main__":
|
|
278
|
+
main()
|
|
279
|
+
|