expr-codegen 0.6.4__tar.gz → 0.7.0__tar.gz
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.
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/PKG-INFO +66 -44
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/README.md +65 -43
- expr_codegen-0.7.0/expr_codegen/_version.py +1 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/codes.py +12 -1
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/expr.py +9 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/model.py +7 -5
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/tool.py +23 -9
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen.egg-info/PKG-INFO +66 -44
- expr_codegen-0.6.4/expr_codegen/_version.py +0 -1
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/LICENSE +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/__init__.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/dag.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/latex/__init__.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/latex/printer.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/pandas/__init__.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/pandas/code.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/pandas/printer.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/pandas/template.py.j2 +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/polars/__init__.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/polars/code.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/polars/printer.py +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen/polars/template.py.j2 +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen.egg-info/SOURCES.txt +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen.egg-info/dependency_links.txt +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen.egg-info/requires.txt +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/expr_codegen.egg-info/top_level.txt +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/pyproject.toml +0 -0
- {expr_codegen-0.6.4 → expr_codegen-0.7.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: expr_codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: symbol expression to polars expression tool
|
|
5
5
|
Author-email: wukan <wu-kan@163.com>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -75,9 +75,48 @@ https://exprcodegen.streamlit.app
|
|
|
75
75
|
|
|
76
76
|
更完整示例访问[alpha_examples](https://github.com/wukan1986/alpha_examples)
|
|
77
77
|
|
|
78
|
-
##
|
|
78
|
+
## 使用示例
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
```python
|
|
81
|
+
import sys
|
|
82
|
+
|
|
83
|
+
# from polars_ta.prefix.talib import * # noqa
|
|
84
|
+
from polars_ta.prefix.cdl import * # noqa
|
|
85
|
+
from polars_ta.prefix.ta import * # noqa
|
|
86
|
+
from polars_ta.prefix.tdx import * # noqa
|
|
87
|
+
from polars_ta.prefix.wq import * # noqa
|
|
88
|
+
|
|
89
|
+
from expr_codegen.tool import codegen_exec
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _code_block_():
|
|
93
|
+
# 因子编辑区,可利用IDE的智能提示在此区域编辑因子
|
|
94
|
+
|
|
95
|
+
# 模板中已经默认导入了from polars_ta.prefix下大量的算子,但
|
|
96
|
+
# talib在模板中没有默认导入。这种写法可实现在生成的代码中导入
|
|
97
|
+
from polars_ta.prefix.talib import ts_LINEARREG_SLOPE # noqa
|
|
98
|
+
|
|
99
|
+
# 1. 下划线开头的变量是中间变量,会被自动更名,最终输出时会被剔除
|
|
100
|
+
# 2. 下划线开头的变量可重复使用。多个复杂因子多行书写时有重复中间变时不再冲突
|
|
101
|
+
_avg = ts_mean(corr, 20)
|
|
102
|
+
_std = ts_std_dev(corr, 20)
|
|
103
|
+
_beta = ts_LINEARREG_SLOPE(corr, 20)
|
|
104
|
+
|
|
105
|
+
# 3. 下划线开头的变量支持有环循环赋值。在调试时可快速用注释进行切换
|
|
106
|
+
_avg = cs_mad_zscore_resid(_avg, LOG_MC_ZS, ONE)
|
|
107
|
+
_std = cs_mad_zscore_resid(_std, LOG_MC_ZS, ONE)
|
|
108
|
+
# _beta = cs_mad_zscore_resid(_beta, LOG_MC_ZS, ONE)
|
|
109
|
+
|
|
110
|
+
_corr = cs_zscore(_avg) + cs_zscore(_std)
|
|
111
|
+
CPV = cs_zscore(_corr) + cs_zscore(_beta)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
df = None # 替换成真实的polars数据
|
|
115
|
+
df = codegen_exec(_code_block_, df, output_file=sys.stdout) # 打印代码
|
|
116
|
+
df = codegen_exec(_code_block_, df, output_file="output.py") # 保存到文件
|
|
117
|
+
df = codegen_exec(_code_block_, df) # 只执行,不保存代码
|
|
118
|
+
|
|
119
|
+
```
|
|
81
120
|
|
|
82
121
|
## 目录结构
|
|
83
122
|
|
|
@@ -86,8 +125,7 @@ https://exprcodegen.streamlit.app
|
|
|
86
125
|
├─data
|
|
87
126
|
│ prepare_date.py # 准备数据
|
|
88
127
|
├─examples
|
|
89
|
-
│
|
|
90
|
-
│ demo_cn.py # 中文注释示例。演示如何将表达式转换成代码
|
|
128
|
+
│ demo_express.py # 速成示例。演示如何将表达式转换成代码
|
|
91
129
|
│ demo_exec_pl.py # 演示调用转换后代码并绘图
|
|
92
130
|
│ demo_transformer.py # 演示将第三方表达式转成内部表达式
|
|
93
131
|
│ output.py # 结果输出。可不修改代码,直接被其它项目导入
|
|
@@ -144,7 +182,7 @@ https://exprcodegen.streamlit.app
|
|
|
144
182
|
|
|
145
183
|
## 二次开发
|
|
146
184
|
|
|
147
|
-
1. 备份后编辑`
|
|
185
|
+
1. 备份后编辑`demo_express.py`, `import`需要引入的函数
|
|
148
186
|
2. 然后`printer.py`有可能需要添加对应函数的打印代码
|
|
149
187
|
- 注意:需要留意是否要加括号`()`,不加时可能优先级混乱,可以每次都加括号,也可用提供的`parenthesize`简化处理
|
|
150
188
|
|
|
@@ -166,58 +204,42 @@ https://exprcodegen.streamlit.app
|
|
|
166
204
|
|
|
167
205
|
以上三种问题本项目都使用`ast`进行了处理,可以简化使用
|
|
168
206
|
|
|
169
|
-
##
|
|
207
|
+
## 转译结果示例
|
|
170
208
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```python
|
|
174
|
-
exprs_src = {
|
|
175
|
-
"expr_1": -ts_corr(cs_rank(ts_mean(OPEN, 10)), cs_rank(ts_mean(CLOSE, 10)), 10),
|
|
176
|
-
"expr_2": cs_rank(ts_mean(OPEN, 10)) - abs_(log(ts_mean(CLOSE, 10))) + gp_rank(sw_l1, CLOSE),
|
|
177
|
-
"expr_3": ts_mean(cs_rank(ts_mean(OPEN, 10)), 10),
|
|
178
|
-
"expr_4": cs_rank(ts_mean(cs_rank(OPEN), 10)),
|
|
179
|
-
"expr_5": -ts_corr(OPEN, CLOSE, 10),
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
转译后的代码片段,详细代码请参考[Polars版](codes)
|
|
209
|
+
转译后的代码片段,详细代码请参考[Polars版](examples/output_polars.py)
|
|
184
210
|
|
|
185
211
|
```python
|
|
186
212
|
def func_0_ts__asset(df: pl.DataFrame) -> pl.DataFrame:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
213
|
+
df = df.sort(by=[_DATE_])
|
|
214
|
+
# ========================================
|
|
215
|
+
df = df.with_columns(
|
|
216
|
+
_x_0=1 / ts_delay(OPEN, -1),
|
|
217
|
+
LABEL_CC_1=(-CLOSE + ts_delay(CLOSE, -1)) / CLOSE,
|
|
218
|
+
)
|
|
219
|
+
# ========================================
|
|
220
|
+
df = df.with_columns(
|
|
221
|
+
LABEL_OO_1=_x_0 * ts_delay(OPEN, -2) - 1,
|
|
222
|
+
LABEL_OO_2=_x_0 * ts_delay(OPEN, -3) - 1,
|
|
223
|
+
)
|
|
224
|
+
return df
|
|
199
225
|
```
|
|
200
226
|
|
|
201
227
|
转译后的代码片段,详细代码请参考[Pandas版](examples/output_pandas.py)
|
|
202
228
|
|
|
203
229
|
```python
|
|
204
230
|
def func_2_cs__date(df: pd.DataFrame) -> pd.DataFrame:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
231
|
+
# expr_4 = cs_rank(x_7)
|
|
232
|
+
df["expr_4"] = (df["x_7"]).rank(pct=True)
|
|
233
|
+
return df
|
|
208
234
|
|
|
209
235
|
|
|
210
236
|
def func_3_ts__asset__date(df: pd.DataFrame) -> pd.DataFrame:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
237
|
+
# expr_5 = -ts_corr(OPEN, CLOSE, 10)
|
|
238
|
+
df["expr_5"] = -(df["OPEN"]).rolling(10).corr(df["CLOSE"])
|
|
239
|
+
# expr_6 = ts_delta(OPEN, 10)
|
|
240
|
+
df["expr_6"] = df["OPEN"].diff(10)
|
|
241
|
+
return df
|
|
217
242
|
|
|
218
|
-
df = df.sort_values(by=["asset", "date"]).groupby(by=["asset"], group_keys=False).apply(func_0_ts__asset__date)
|
|
219
|
-
df = df.groupby(by=["date"], group_keys=False).apply(func_0_cs__date)
|
|
220
|
-
df = func_0_cl(df)
|
|
221
243
|
```
|
|
222
244
|
|
|
223
245
|
## 本地部署交互网页
|
|
@@ -25,9 +25,48 @@ https://exprcodegen.streamlit.app
|
|
|
25
25
|
|
|
26
26
|
更完整示例访问[alpha_examples](https://github.com/wukan1986/alpha_examples)
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## 使用示例
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
```python
|
|
31
|
+
import sys
|
|
32
|
+
|
|
33
|
+
# from polars_ta.prefix.talib import * # noqa
|
|
34
|
+
from polars_ta.prefix.cdl import * # noqa
|
|
35
|
+
from polars_ta.prefix.ta import * # noqa
|
|
36
|
+
from polars_ta.prefix.tdx import * # noqa
|
|
37
|
+
from polars_ta.prefix.wq import * # noqa
|
|
38
|
+
|
|
39
|
+
from expr_codegen.tool import codegen_exec
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _code_block_():
|
|
43
|
+
# 因子编辑区,可利用IDE的智能提示在此区域编辑因子
|
|
44
|
+
|
|
45
|
+
# 模板中已经默认导入了from polars_ta.prefix下大量的算子,但
|
|
46
|
+
# talib在模板中没有默认导入。这种写法可实现在生成的代码中导入
|
|
47
|
+
from polars_ta.prefix.talib import ts_LINEARREG_SLOPE # noqa
|
|
48
|
+
|
|
49
|
+
# 1. 下划线开头的变量是中间变量,会被自动更名,最终输出时会被剔除
|
|
50
|
+
# 2. 下划线开头的变量可重复使用。多个复杂因子多行书写时有重复中间变时不再冲突
|
|
51
|
+
_avg = ts_mean(corr, 20)
|
|
52
|
+
_std = ts_std_dev(corr, 20)
|
|
53
|
+
_beta = ts_LINEARREG_SLOPE(corr, 20)
|
|
54
|
+
|
|
55
|
+
# 3. 下划线开头的变量支持有环循环赋值。在调试时可快速用注释进行切换
|
|
56
|
+
_avg = cs_mad_zscore_resid(_avg, LOG_MC_ZS, ONE)
|
|
57
|
+
_std = cs_mad_zscore_resid(_std, LOG_MC_ZS, ONE)
|
|
58
|
+
# _beta = cs_mad_zscore_resid(_beta, LOG_MC_ZS, ONE)
|
|
59
|
+
|
|
60
|
+
_corr = cs_zscore(_avg) + cs_zscore(_std)
|
|
61
|
+
CPV = cs_zscore(_corr) + cs_zscore(_beta)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
df = None # 替换成真实的polars数据
|
|
65
|
+
df = codegen_exec(_code_block_, df, output_file=sys.stdout) # 打印代码
|
|
66
|
+
df = codegen_exec(_code_block_, df, output_file="output.py") # 保存到文件
|
|
67
|
+
df = codegen_exec(_code_block_, df) # 只执行,不保存代码
|
|
68
|
+
|
|
69
|
+
```
|
|
31
70
|
|
|
32
71
|
## 目录结构
|
|
33
72
|
|
|
@@ -36,8 +75,7 @@ https://exprcodegen.streamlit.app
|
|
|
36
75
|
├─data
|
|
37
76
|
│ prepare_date.py # 准备数据
|
|
38
77
|
├─examples
|
|
39
|
-
│
|
|
40
|
-
│ demo_cn.py # 中文注释示例。演示如何将表达式转换成代码
|
|
78
|
+
│ demo_express.py # 速成示例。演示如何将表达式转换成代码
|
|
41
79
|
│ demo_exec_pl.py # 演示调用转换后代码并绘图
|
|
42
80
|
│ demo_transformer.py # 演示将第三方表达式转成内部表达式
|
|
43
81
|
│ output.py # 结果输出。可不修改代码,直接被其它项目导入
|
|
@@ -94,7 +132,7 @@ https://exprcodegen.streamlit.app
|
|
|
94
132
|
|
|
95
133
|
## 二次开发
|
|
96
134
|
|
|
97
|
-
1. 备份后编辑`
|
|
135
|
+
1. 备份后编辑`demo_express.py`, `import`需要引入的函数
|
|
98
136
|
2. 然后`printer.py`有可能需要添加对应函数的打印代码
|
|
99
137
|
- 注意:需要留意是否要加括号`()`,不加时可能优先级混乱,可以每次都加括号,也可用提供的`parenthesize`简化处理
|
|
100
138
|
|
|
@@ -116,58 +154,42 @@ https://exprcodegen.streamlit.app
|
|
|
116
154
|
|
|
117
155
|
以上三种问题本项目都使用`ast`进行了处理,可以简化使用
|
|
118
156
|
|
|
119
|
-
##
|
|
157
|
+
## 转译结果示例
|
|
120
158
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
```python
|
|
124
|
-
exprs_src = {
|
|
125
|
-
"expr_1": -ts_corr(cs_rank(ts_mean(OPEN, 10)), cs_rank(ts_mean(CLOSE, 10)), 10),
|
|
126
|
-
"expr_2": cs_rank(ts_mean(OPEN, 10)) - abs_(log(ts_mean(CLOSE, 10))) + gp_rank(sw_l1, CLOSE),
|
|
127
|
-
"expr_3": ts_mean(cs_rank(ts_mean(OPEN, 10)), 10),
|
|
128
|
-
"expr_4": cs_rank(ts_mean(cs_rank(OPEN), 10)),
|
|
129
|
-
"expr_5": -ts_corr(OPEN, CLOSE, 10),
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
转译后的代码片段,详细代码请参考[Polars版](codes)
|
|
159
|
+
转译后的代码片段,详细代码请参考[Polars版](examples/output_polars.py)
|
|
134
160
|
|
|
135
161
|
```python
|
|
136
162
|
def func_0_ts__asset(df: pl.DataFrame) -> pl.DataFrame:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
163
|
+
df = df.sort(by=[_DATE_])
|
|
164
|
+
# ========================================
|
|
165
|
+
df = df.with_columns(
|
|
166
|
+
_x_0=1 / ts_delay(OPEN, -1),
|
|
167
|
+
LABEL_CC_1=(-CLOSE + ts_delay(CLOSE, -1)) / CLOSE,
|
|
168
|
+
)
|
|
169
|
+
# ========================================
|
|
170
|
+
df = df.with_columns(
|
|
171
|
+
LABEL_OO_1=_x_0 * ts_delay(OPEN, -2) - 1,
|
|
172
|
+
LABEL_OO_2=_x_0 * ts_delay(OPEN, -3) - 1,
|
|
173
|
+
)
|
|
174
|
+
return df
|
|
149
175
|
```
|
|
150
176
|
|
|
151
177
|
转译后的代码片段,详细代码请参考[Pandas版](examples/output_pandas.py)
|
|
152
178
|
|
|
153
179
|
```python
|
|
154
180
|
def func_2_cs__date(df: pd.DataFrame) -> pd.DataFrame:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
181
|
+
# expr_4 = cs_rank(x_7)
|
|
182
|
+
df["expr_4"] = (df["x_7"]).rank(pct=True)
|
|
183
|
+
return df
|
|
158
184
|
|
|
159
185
|
|
|
160
186
|
def func_3_ts__asset__date(df: pd.DataFrame) -> pd.DataFrame:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
187
|
+
# expr_5 = -ts_corr(OPEN, CLOSE, 10)
|
|
188
|
+
df["expr_5"] = -(df["OPEN"]).rolling(10).corr(df["CLOSE"])
|
|
189
|
+
# expr_6 = ts_delta(OPEN, 10)
|
|
190
|
+
df["expr_6"] = df["OPEN"].diff(10)
|
|
191
|
+
return df
|
|
167
192
|
|
|
168
|
-
df = df.sort_values(by=["asset", "date"]).groupby(by=["asset"], group_keys=False).apply(func_0_ts__asset__date)
|
|
169
|
-
df = df.groupby(by=["date"], group_keys=False).apply(func_0_cs__date)
|
|
170
|
-
df = func_0_cl(df)
|
|
171
193
|
```
|
|
172
194
|
|
|
173
195
|
## 本地部署交互网页
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.0"
|
|
@@ -51,7 +51,8 @@ class SympyTransformer(ast.NodeTransformer):
|
|
|
51
51
|
|
|
52
52
|
# 赋值给下划线开头代码时,对其进行重命名,方便重复书写表达式时不冲突
|
|
53
53
|
if old_target_id.startswith('_'):
|
|
54
|
-
|
|
54
|
+
# 减少与cse中_x_冲突
|
|
55
|
+
new_target_id = f'{old_target_id}_{len(self.targets_new)}_'
|
|
55
56
|
|
|
56
57
|
if old_target_id != new_target_id:
|
|
57
58
|
self.targets_new.add(new_target_id)
|
|
@@ -149,6 +150,16 @@ class SympyTransformer(ast.NodeTransformer):
|
|
|
149
150
|
self.generic_visit(node)
|
|
150
151
|
return node
|
|
151
152
|
|
|
153
|
+
def visit_UnaryOp(self, node):
|
|
154
|
+
# -x
|
|
155
|
+
if isinstance(node.operand, ast.Name):
|
|
156
|
+
self.args_old.add(node.operand.id)
|
|
157
|
+
node.operand.id = self.args_map.get(node.operand.id, node.operand.id)
|
|
158
|
+
self.args_new.add(node.operand.id)
|
|
159
|
+
|
|
160
|
+
self.generic_visit(node)
|
|
161
|
+
return node
|
|
162
|
+
|
|
152
163
|
|
|
153
164
|
def sources_to_asts(*sources):
|
|
154
165
|
"""输入多份源代码"""
|
|
@@ -132,6 +132,15 @@ def is_NegativeX(expr):
|
|
|
132
132
|
return False
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
def is_simple_expr(expr):
|
|
136
|
+
if isinstance(expr, Mul):
|
|
137
|
+
if expr.args[0] == -1 and len(expr.args) == 2 and expr.args[1].is_Atom:
|
|
138
|
+
return True
|
|
139
|
+
if isinstance(expr, Symbol):
|
|
140
|
+
return True
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
|
|
135
144
|
def get_current_by_prefix(expr, **kwargs):
|
|
136
145
|
"""表达式根节点信息。按名称前缀。例如
|
|
137
146
|
|
|
@@ -5,7 +5,7 @@ import networkx as nx
|
|
|
5
5
|
from sympy import symbols
|
|
6
6
|
|
|
7
7
|
from expr_codegen.dag import zero_indegree, hierarchy_pos, remove_paths_by_zero_outdegree
|
|
8
|
-
from expr_codegen.expr import CL, get_symbols, get_children, get_key, is_NegativeX
|
|
8
|
+
from expr_codegen.expr import CL, get_symbols, get_children, get_key, is_NegativeX, is_simple_expr
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ListDictList:
|
|
@@ -214,7 +214,7 @@ def merge_nodes_1(G: nx.DiGraph, keep_nodes, *args):
|
|
|
214
214
|
expr = dic['expr']
|
|
215
215
|
symbols = dic['symbols']
|
|
216
216
|
if key[0] == CL:
|
|
217
|
-
if
|
|
217
|
+
if is_simple_expr(expr):
|
|
218
218
|
# 检查表达式是否很简单, 是就替换,可能会替换多个
|
|
219
219
|
skip_expr_node(G, node, keep_nodes)
|
|
220
220
|
else:
|
|
@@ -253,7 +253,7 @@ def merge_nodes_2(G: nx.DiGraph, keep_nodes, *args):
|
|
|
253
253
|
for node in this_pred:
|
|
254
254
|
dic = G.nodes[node]
|
|
255
255
|
expr = dic['expr']
|
|
256
|
-
if not
|
|
256
|
+
if not is_simple_expr(expr):
|
|
257
257
|
continue
|
|
258
258
|
pred = G.pred[node]
|
|
259
259
|
for p in pred.copy():
|
|
@@ -337,8 +337,10 @@ def dag_start(exprs_dict, func, func_kwargs):
|
|
|
337
337
|
def dag_middle(G, exprs_names, func, func_kwargs):
|
|
338
338
|
"""删除几个没有必要的节点"""
|
|
339
339
|
G = remove_paths_by_zero_outdegree(G, exprs_names)
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
# 以下划线开头的节点,不保留
|
|
341
|
+
keep_nodes = [k for k in exprs_names if not k.startswith('_')]
|
|
342
|
+
G = merge_nodes_1(G, keep_nodes, *keep_nodes)
|
|
343
|
+
G = merge_nodes_2(G, keep_nodes, *keep_nodes)
|
|
342
344
|
|
|
343
345
|
# 由于表达式修改,需再次更新表达式
|
|
344
346
|
G = init_dag_exprs(G, func, func_kwargs)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from functools import lru_cache
|
|
3
|
+
from io import TextIOWrapper
|
|
3
4
|
from typing import Sequence, Dict, Optional
|
|
4
5
|
|
|
5
6
|
from black import Mode, format_str
|
|
@@ -223,21 +224,24 @@ class ExprTool:
|
|
|
223
224
|
return globals_['df_output']
|
|
224
225
|
|
|
225
226
|
@lru_cache(maxsize=64)
|
|
226
|
-
def _get_codes(self, source: str, extra_codes: str, output_file: str
|
|
227
|
+
def _get_codes(self, source: str, extra_codes: str, output_file: str,
|
|
228
|
+
style='polars', template_file='template.py.j2',
|
|
229
|
+
date='date', asset='asset') -> str:
|
|
227
230
|
"""通过字符串生成代码, 加了缓存,多次调用不重复生成"""
|
|
228
231
|
raw, exprs_dict = sources_to_exprs(self.globals_, source, safe=False)
|
|
229
232
|
|
|
230
233
|
# 生成代码
|
|
231
|
-
codes, G = _TOOL_.all(exprs_dict, style=
|
|
234
|
+
codes, G = _TOOL_.all(exprs_dict, style=style, template_file=template_file,
|
|
232
235
|
replace=True, regroup=True, format=True,
|
|
233
|
-
date=
|
|
236
|
+
date=date, asset=asset,
|
|
234
237
|
# 复制了需要使用的函数,还复制了最原始的表达式
|
|
235
238
|
extra_codes=(raw,
|
|
236
239
|
# 传入多个列的方法
|
|
237
240
|
extra_codes,
|
|
238
241
|
))
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
if isinstance(output_file, TextIOWrapper):
|
|
243
|
+
output_file.write(codes)
|
|
244
|
+
elif output_file is not None:
|
|
241
245
|
with open(output_file, 'w', encoding='utf-8') as f:
|
|
242
246
|
f.write(codes)
|
|
243
247
|
|
|
@@ -247,9 +251,13 @@ class ExprTool:
|
|
|
247
251
|
_TOOL_ = ExprTool()
|
|
248
252
|
|
|
249
253
|
|
|
250
|
-
def codegen_exec(code_block,
|
|
254
|
+
def codegen_exec(code_block,
|
|
255
|
+
df_input,
|
|
251
256
|
extra_codes: str = r'CS_SW_L1 = pl.col(r"^sw_l1_\d+$")',
|
|
252
|
-
output_file: Optional[str] = None
|
|
257
|
+
output_file: Optional[str] = None,
|
|
258
|
+
style: str = 'polars', template_file: str = 'template.py.j2',
|
|
259
|
+
date: str = 'date', asset: str = 'asset'
|
|
260
|
+
):
|
|
253
261
|
"""快速转换源代码并执行"""
|
|
254
262
|
# 此代码来自于sympy.var
|
|
255
263
|
frame = inspect.currentframe().f_back
|
|
@@ -261,6 +269,12 @@ def codegen_exec(code_block, df_input,
|
|
|
261
269
|
else:
|
|
262
270
|
source = inspect.getsource(code_block)
|
|
263
271
|
|
|
264
|
-
codes = _TOOL_._get_codes(source, extra_codes, output_file
|
|
272
|
+
codes = _TOOL_._get_codes(source, extra_codes, output_file,
|
|
273
|
+
style=style, template_file=template_file,
|
|
274
|
+
date=date, asset=asset,
|
|
275
|
+
)
|
|
265
276
|
|
|
266
|
-
|
|
277
|
+
if df_input is None:
|
|
278
|
+
return df_input
|
|
279
|
+
else:
|
|
280
|
+
return _TOOL_.exec(codes, df_input)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: expr_codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: symbol expression to polars expression tool
|
|
5
5
|
Author-email: wukan <wu-kan@163.com>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -75,9 +75,48 @@ https://exprcodegen.streamlit.app
|
|
|
75
75
|
|
|
76
76
|
更完整示例访问[alpha_examples](https://github.com/wukan1986/alpha_examples)
|
|
77
77
|
|
|
78
|
-
##
|
|
78
|
+
## 使用示例
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
```python
|
|
81
|
+
import sys
|
|
82
|
+
|
|
83
|
+
# from polars_ta.prefix.talib import * # noqa
|
|
84
|
+
from polars_ta.prefix.cdl import * # noqa
|
|
85
|
+
from polars_ta.prefix.ta import * # noqa
|
|
86
|
+
from polars_ta.prefix.tdx import * # noqa
|
|
87
|
+
from polars_ta.prefix.wq import * # noqa
|
|
88
|
+
|
|
89
|
+
from expr_codegen.tool import codegen_exec
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _code_block_():
|
|
93
|
+
# 因子编辑区,可利用IDE的智能提示在此区域编辑因子
|
|
94
|
+
|
|
95
|
+
# 模板中已经默认导入了from polars_ta.prefix下大量的算子,但
|
|
96
|
+
# talib在模板中没有默认导入。这种写法可实现在生成的代码中导入
|
|
97
|
+
from polars_ta.prefix.talib import ts_LINEARREG_SLOPE # noqa
|
|
98
|
+
|
|
99
|
+
# 1. 下划线开头的变量是中间变量,会被自动更名,最终输出时会被剔除
|
|
100
|
+
# 2. 下划线开头的变量可重复使用。多个复杂因子多行书写时有重复中间变时不再冲突
|
|
101
|
+
_avg = ts_mean(corr, 20)
|
|
102
|
+
_std = ts_std_dev(corr, 20)
|
|
103
|
+
_beta = ts_LINEARREG_SLOPE(corr, 20)
|
|
104
|
+
|
|
105
|
+
# 3. 下划线开头的变量支持有环循环赋值。在调试时可快速用注释进行切换
|
|
106
|
+
_avg = cs_mad_zscore_resid(_avg, LOG_MC_ZS, ONE)
|
|
107
|
+
_std = cs_mad_zscore_resid(_std, LOG_MC_ZS, ONE)
|
|
108
|
+
# _beta = cs_mad_zscore_resid(_beta, LOG_MC_ZS, ONE)
|
|
109
|
+
|
|
110
|
+
_corr = cs_zscore(_avg) + cs_zscore(_std)
|
|
111
|
+
CPV = cs_zscore(_corr) + cs_zscore(_beta)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
df = None # 替换成真实的polars数据
|
|
115
|
+
df = codegen_exec(_code_block_, df, output_file=sys.stdout) # 打印代码
|
|
116
|
+
df = codegen_exec(_code_block_, df, output_file="output.py") # 保存到文件
|
|
117
|
+
df = codegen_exec(_code_block_, df) # 只执行,不保存代码
|
|
118
|
+
|
|
119
|
+
```
|
|
81
120
|
|
|
82
121
|
## 目录结构
|
|
83
122
|
|
|
@@ -86,8 +125,7 @@ https://exprcodegen.streamlit.app
|
|
|
86
125
|
├─data
|
|
87
126
|
│ prepare_date.py # 准备数据
|
|
88
127
|
├─examples
|
|
89
|
-
│
|
|
90
|
-
│ demo_cn.py # 中文注释示例。演示如何将表达式转换成代码
|
|
128
|
+
│ demo_express.py # 速成示例。演示如何将表达式转换成代码
|
|
91
129
|
│ demo_exec_pl.py # 演示调用转换后代码并绘图
|
|
92
130
|
│ demo_transformer.py # 演示将第三方表达式转成内部表达式
|
|
93
131
|
│ output.py # 结果输出。可不修改代码,直接被其它项目导入
|
|
@@ -144,7 +182,7 @@ https://exprcodegen.streamlit.app
|
|
|
144
182
|
|
|
145
183
|
## 二次开发
|
|
146
184
|
|
|
147
|
-
1. 备份后编辑`
|
|
185
|
+
1. 备份后编辑`demo_express.py`, `import`需要引入的函数
|
|
148
186
|
2. 然后`printer.py`有可能需要添加对应函数的打印代码
|
|
149
187
|
- 注意:需要留意是否要加括号`()`,不加时可能优先级混乱,可以每次都加括号,也可用提供的`parenthesize`简化处理
|
|
150
188
|
|
|
@@ -166,58 +204,42 @@ https://exprcodegen.streamlit.app
|
|
|
166
204
|
|
|
167
205
|
以上三种问题本项目都使用`ast`进行了处理,可以简化使用
|
|
168
206
|
|
|
169
|
-
##
|
|
207
|
+
## 转译结果示例
|
|
170
208
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```python
|
|
174
|
-
exprs_src = {
|
|
175
|
-
"expr_1": -ts_corr(cs_rank(ts_mean(OPEN, 10)), cs_rank(ts_mean(CLOSE, 10)), 10),
|
|
176
|
-
"expr_2": cs_rank(ts_mean(OPEN, 10)) - abs_(log(ts_mean(CLOSE, 10))) + gp_rank(sw_l1, CLOSE),
|
|
177
|
-
"expr_3": ts_mean(cs_rank(ts_mean(OPEN, 10)), 10),
|
|
178
|
-
"expr_4": cs_rank(ts_mean(cs_rank(OPEN), 10)),
|
|
179
|
-
"expr_5": -ts_corr(OPEN, CLOSE, 10),
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
转译后的代码片段,详细代码请参考[Polars版](codes)
|
|
209
|
+
转译后的代码片段,详细代码请参考[Polars版](examples/output_polars.py)
|
|
184
210
|
|
|
185
211
|
```python
|
|
186
212
|
def func_0_ts__asset(df: pl.DataFrame) -> pl.DataFrame:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
213
|
+
df = df.sort(by=[_DATE_])
|
|
214
|
+
# ========================================
|
|
215
|
+
df = df.with_columns(
|
|
216
|
+
_x_0=1 / ts_delay(OPEN, -1),
|
|
217
|
+
LABEL_CC_1=(-CLOSE + ts_delay(CLOSE, -1)) / CLOSE,
|
|
218
|
+
)
|
|
219
|
+
# ========================================
|
|
220
|
+
df = df.with_columns(
|
|
221
|
+
LABEL_OO_1=_x_0 * ts_delay(OPEN, -2) - 1,
|
|
222
|
+
LABEL_OO_2=_x_0 * ts_delay(OPEN, -3) - 1,
|
|
223
|
+
)
|
|
224
|
+
return df
|
|
199
225
|
```
|
|
200
226
|
|
|
201
227
|
转译后的代码片段,详细代码请参考[Pandas版](examples/output_pandas.py)
|
|
202
228
|
|
|
203
229
|
```python
|
|
204
230
|
def func_2_cs__date(df: pd.DataFrame) -> pd.DataFrame:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
231
|
+
# expr_4 = cs_rank(x_7)
|
|
232
|
+
df["expr_4"] = (df["x_7"]).rank(pct=True)
|
|
233
|
+
return df
|
|
208
234
|
|
|
209
235
|
|
|
210
236
|
def func_3_ts__asset__date(df: pd.DataFrame) -> pd.DataFrame:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
237
|
+
# expr_5 = -ts_corr(OPEN, CLOSE, 10)
|
|
238
|
+
df["expr_5"] = -(df["OPEN"]).rolling(10).corr(df["CLOSE"])
|
|
239
|
+
# expr_6 = ts_delta(OPEN, 10)
|
|
240
|
+
df["expr_6"] = df["OPEN"].diff(10)
|
|
241
|
+
return df
|
|
217
242
|
|
|
218
|
-
df = df.sort_values(by=["asset", "date"]).groupby(by=["asset"], group_keys=False).apply(func_0_ts__asset__date)
|
|
219
|
-
df = df.groupby(by=["date"], group_keys=False).apply(func_0_cs__date)
|
|
220
|
-
df = func_0_cl(df)
|
|
221
243
|
```
|
|
222
244
|
|
|
223
245
|
## 本地部署交互网页
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.6.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|