codestr 0.2.0__tar.gz → 0.3.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.
- {codestr-0.2.0 → codestr-0.3.0}/PKG-INFO +25 -1
- {codestr-0.2.0 → codestr-0.3.0}/README.md +24 -0
- {codestr-0.2.0 → codestr-0.3.0}/docs/operators.md +36 -15
- codestr-0.3.0/docs/superpowers/plans/2026-07-17-min-samples.md +636 -0
- codestr-0.3.0/docs/superpowers/plans/2026-07-17-mixed-window-planner.md +1458 -0
- codestr-0.3.0/docs/superpowers/specs/2026-07-17-min-samples-design.md +200 -0
- codestr-0.3.0/docs/superpowers/specs/2026-07-17-mixed-window-planner-design.md +324 -0
- {codestr-0.2.0 → codestr-0.3.0}/pyproject.toml +1 -1
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/compiler.py +10 -1
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/engine.py +76 -32
- codestr-0.3.0/src/codestr/planner.py +257 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/syntax.py +2 -2
- codestr-0.3.0/src/codestr/udf/ts_udf.py +238 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/conftest.py +14 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_compiler.py +51 -2
- codestr-0.3.0/tests/test_engine.py +552 -0
- codestr-0.3.0/tests/test_planner.py +174 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_syntax.py +5 -2
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_ts_udf.py +126 -0
- {codestr-0.2.0 → codestr-0.3.0}/uv.lock +1 -1
- codestr-0.2.0/src/codestr/udf/ts_udf.py +0 -110
- codestr-0.2.0/tests/test_engine.py +0 -285
- {codestr-0.2.0 → codestr-0.3.0}/.github/workflows/ci.yml +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/.github/workflows/release.yml +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/.gitignore +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/.python-version +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/CHANGELOG.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/CLAUDE.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/CONTRIBUTING.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/LICENSE +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/docs/superpowers/plans/2026-07-16-ts-ema-keyword-arguments.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/docs/superpowers/specs/2026-07-16-polars-minimum-version-design.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/docs/superpowers/specs/2026-07-16-ts-ema-keyword-arguments-design.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/review.md +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/__init__.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/errors.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/parser.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/tokens.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/udf/__init__.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/udf/base_udf.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/udf/cs_udf.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/src/codestr/udf/registry.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/__init__.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_base_udf.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_cs_udf.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_parser.py +0 -0
- {codestr-0.2.0 → codestr-0.3.0}/tests/test_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codestr
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: DSL → Polars Expr compile engine for quantitative factor mining
|
|
5
5
|
Project-URL: Repository, https://github.com/huangbogeng/codestr
|
|
6
6
|
Author: huangbogeng
|
|
@@ -115,6 +115,23 @@ cs = CodeStr(df,
|
|
|
115
115
|
| **TS (时序)** | `over(partition_by=partition_by, order_by=order_by)` |
|
|
116
116
|
| **CS (截面)** | `over(partition_by=order_by, order_by=partition_by)` |
|
|
117
117
|
|
|
118
|
+
### 混合窗口
|
|
119
|
+
|
|
120
|
+
`CodeStr.sql()` 会自动把 TS/CS 混合窗口拆成连续的 lazy projection:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
cs.sql(
|
|
124
|
+
"close * 2 as scaled",
|
|
125
|
+
"ts_mean(cs_moderate(scaled), 60) as factor",
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
其语义等价于先生成 `cs_moderate(scaled)` 中间列,再沿资产时间轴
|
|
130
|
+
计算 `ts_mean`。中间列不会出现在返回结果中。
|
|
131
|
+
|
|
132
|
+
纯 `cs.compile()` 只能返回一个 `pl.Expr`,因此会明确拒绝需要多阶段
|
|
133
|
+
执行的 TS/CS 混合窗口。TS→TS 和 CS→CS 同域嵌套不受影响。
|
|
134
|
+
|
|
118
135
|
## 自定义算子
|
|
119
136
|
|
|
120
137
|
```python
|
|
@@ -139,6 +156,13 @@ cs.sql("ts_ewm(close, 10) as ewm10")
|
|
|
139
156
|
|
|
140
157
|
**时序算子** (`ts_udf`):`ts_mean`, `ts_ema`, `ts_sum`, `ts_std`, `ts_var`, `ts_skew`, `ts_kurt`, `ts_max`, `ts_min`, `ts_mid`, `ts_delay`, `ts_delta`, `ts_mad` 等
|
|
141
158
|
|
|
159
|
+
滚动统计与 `ts_ema` 支持关键字参数 `min_samples`。滚动统计省略该参数时沿用 Polars 默认值 `None`(需要完整窗口),`ts_ema` 则沿用 Polars 默认值 `1`:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
cs.sql("ts_mean(close, 20, min_samples=5) as ma20")
|
|
163
|
+
cs.sql("ts_ema(close, 10, min_samples=3) as ema10")
|
|
164
|
+
```
|
|
165
|
+
|
|
142
166
|
## 项目结构
|
|
143
167
|
|
|
144
168
|
```
|
|
@@ -84,6 +84,23 @@ cs = CodeStr(df,
|
|
|
84
84
|
| **TS (时序)** | `over(partition_by=partition_by, order_by=order_by)` |
|
|
85
85
|
| **CS (截面)** | `over(partition_by=order_by, order_by=partition_by)` |
|
|
86
86
|
|
|
87
|
+
### 混合窗口
|
|
88
|
+
|
|
89
|
+
`CodeStr.sql()` 会自动把 TS/CS 混合窗口拆成连续的 lazy projection:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
cs.sql(
|
|
93
|
+
"close * 2 as scaled",
|
|
94
|
+
"ts_mean(cs_moderate(scaled), 60) as factor",
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
其语义等价于先生成 `cs_moderate(scaled)` 中间列,再沿资产时间轴
|
|
99
|
+
计算 `ts_mean`。中间列不会出现在返回结果中。
|
|
100
|
+
|
|
101
|
+
纯 `cs.compile()` 只能返回一个 `pl.Expr`,因此会明确拒绝需要多阶段
|
|
102
|
+
执行的 TS/CS 混合窗口。TS→TS 和 CS→CS 同域嵌套不受影响。
|
|
103
|
+
|
|
87
104
|
## 自定义算子
|
|
88
105
|
|
|
89
106
|
```python
|
|
@@ -108,6 +125,13 @@ cs.sql("ts_ewm(close, 10) as ewm10")
|
|
|
108
125
|
|
|
109
126
|
**时序算子** (`ts_udf`):`ts_mean`, `ts_ema`, `ts_sum`, `ts_std`, `ts_var`, `ts_skew`, `ts_kurt`, `ts_max`, `ts_min`, `ts_mid`, `ts_delay`, `ts_delta`, `ts_mad` 等
|
|
110
127
|
|
|
128
|
+
滚动统计与 `ts_ema` 支持关键字参数 `min_samples`。滚动统计省略该参数时沿用 Polars 默认值 `None`(需要完整窗口),`ts_ema` 则沿用 Polars 默认值 `1`:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
cs.sql("ts_mean(close, 20, min_samples=5) as ma20")
|
|
132
|
+
cs.sql("ts_ema(close, 10, min_samples=3) as ema10")
|
|
133
|
+
```
|
|
134
|
+
|
|
111
135
|
## 项目结构
|
|
112
136
|
|
|
113
137
|
```
|
|
@@ -274,33 +274,35 @@ cs.sql("cs_midby(factor, sector, cap_group) as group_median")
|
|
|
274
274
|
|
|
275
275
|
> 编译器自动注入窗口配置:`over(partition_by=asset_cols, order_by=datetime_cols)`
|
|
276
276
|
|
|
277
|
-
|
|
277
|
+
滚动统计的通用签名为 `(expr, windows, *, min_samples=None)`,其中 `windows` 是整数,表示回溯窗口大小;`min_samples` 是仅限关键字的正整数参数,省略时沿用 Polars 默认值 `None`(需要完整窗口)。`ts_ema` 的签名为 `(expr, span, *, min_samples=1)`,`span` 和 `min_samples` 都必须是正整数。滞后与差分算子仍使用 `(expr, windows)`,不接收 `min_samples`。
|
|
278
278
|
|
|
279
279
|
### 滚动统计
|
|
280
280
|
|
|
281
281
|
| 算子 | 说明 | 底层实现 |
|
|
282
282
|
|------|------|------|
|
|
283
|
-
| `ts_mean` | 滚动均值 | `rolling_mean(windows)` |
|
|
284
|
-
| `ts_ema` | 递归指数移动平均 | `ewm_mean(span=span, adjust=False, min_samples=
|
|
285
|
-
| `ts_sum` | 滚动求和 | `rolling_sum(windows)` |
|
|
286
|
-
| `ts_std` | 滚动标准差 | `rolling_std(windows)` |
|
|
287
|
-
| `ts_var` | 滚动方差 | `rolling_var(windows)` |
|
|
288
|
-
| `ts_skew` | 滚动偏度 | `rolling_skew(windows)` |
|
|
289
|
-
| `ts_kurt` | 滚动峰度 | `rolling_kurtosis(windows)` |
|
|
290
|
-
| `ts_max` | 滚动最大值 | `rolling_max(windows)` |
|
|
291
|
-
| `ts_min` | 滚动最小值 | `rolling_min(windows)` |
|
|
292
|
-
| `ts_mid` | 滚动中位数 | `rolling_median(windows)` |
|
|
293
|
-
| `ts_mad` | 滚动 MAD | `
|
|
294
|
-
|
|
295
|
-
> `ts_mad`
|
|
283
|
+
| `ts_mean` | 滚动均值 | `rolling_mean(windows, min_samples=min_samples)` |
|
|
284
|
+
| `ts_ema` | 递归指数移动平均 | `ewm_mean(span=span, adjust=False, min_samples=min_samples)` |
|
|
285
|
+
| `ts_sum` | 滚动求和 | `rolling_sum(windows, min_samples=min_samples)` |
|
|
286
|
+
| `ts_std` | 滚动标准差 | `rolling_std(windows, min_samples=min_samples)` |
|
|
287
|
+
| `ts_var` | 滚动方差 | `rolling_var(windows, min_samples=min_samples)` |
|
|
288
|
+
| `ts_skew` | 滚动偏度 | `rolling_skew(windows, min_samples=min_samples)` |
|
|
289
|
+
| `ts_kurt` | 滚动峰度 | `rolling_kurtosis(windows, min_samples=min_samples)` |
|
|
290
|
+
| `ts_max` | 滚动最大值 | `rolling_max(windows, min_samples=min_samples)` |
|
|
291
|
+
| `ts_min` | 滚动最小值 | `rolling_min(windows, min_samples=min_samples)` |
|
|
292
|
+
| `ts_mid` | 滚动中位数 | `rolling_median(windows, min_samples=min_samples)` |
|
|
293
|
+
| `ts_mad` | 滚动 MAD | 内外两层 `rolling_median` 使用相同的 `min_samples` |
|
|
294
|
+
|
|
295
|
+
> `ts_mad` 计算 `median(|x - rolling_median(x)|) × 1.4826`。常数 1.4826 使其在正态分布下与标准差一致,内外两层滚动中位数共享同一个 `min_samples`。
|
|
296
296
|
|
|
297
297
|
示例:
|
|
298
298
|
```python
|
|
299
299
|
cs.sql("ts_mean(close, 5) as ma5")
|
|
300
|
+
cs.sql("ts_mean(close, 20, min_samples=5) as ma20_partial")
|
|
300
301
|
cs.sql("ts_ema(close, 10) as ema10")
|
|
302
|
+
cs.sql("ts_ema(close, 10, min_samples=3) as ema10_warmup")
|
|
301
303
|
cs.sql("ts_std(close, 20) as vol20")
|
|
302
304
|
cs.sql("ts_max(high, 20) as hh20")
|
|
303
|
-
cs.sql("ts_mad(close, 20) as mad20")
|
|
305
|
+
cs.sql("ts_mad(close, 20, min_samples=5) as mad20")
|
|
304
306
|
```
|
|
305
307
|
|
|
306
308
|
### 滞后与差分
|
|
@@ -373,6 +375,25 @@ cs = CodeStr(df,
|
|
|
373
375
|
|
|
374
376
|
CS 与 TS 的 `partition_by` / `order_by` **刚好交换**,分别实现"按时间分组,沿实体统计"(CS)和"按实体分组,沿时间统计"(TS)。
|
|
375
377
|
|
|
378
|
+
### 混合窗口规划
|
|
379
|
+
|
|
380
|
+
TS 与 CS 使用不同分组轴,不能安全地嵌套成一个 Polars window
|
|
381
|
+
expression。`CodeStr.sql()` 会检测窗口祖先链上的域变化,并生成多个
|
|
382
|
+
连续 `with_columns` 阶段:
|
|
383
|
+
|
|
384
|
+
```text
|
|
385
|
+
ts_mean(cs_moderate(x), 60)
|
|
386
|
+
|
|
387
|
+
stage 1: internal = cs_moderate(x)
|
|
388
|
+
stage 2: result = ts_mean(internal, 60)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
该过程保持 lazy,不会在阶段间调用 `collect()`。同一次 `sql()` 中,
|
|
392
|
+
后续表达式也可以引用前一条表达式创建的别名。
|
|
393
|
+
|
|
394
|
+
`CodeStr.compile()` 仍返回单个 `pl.Expr`,对混合窗口抛出
|
|
395
|
+
`CompileError`。同域嵌套和并列的 TS/CS 表达式继续正常编译。
|
|
396
|
+
|
|
376
397
|
---
|
|
377
398
|
|
|
378
399
|
## 自定义算子
|