mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 16:44:15 +08:00
fix(strategy): import 白名单加 datetime + date 参数处理补进文档 (#123)
问题: ai_single_yang_unbroken 策略用 from datetime import date 做 date 类型参数转换, 被安全修复的 import 白名单(只允许 polars)拦截, 策略加载失败消失。同时 date 列(Polars Date)与字符串参数直接比较 报 InvalidOperationError 导致 500。 修复: - ai_generator.py: import 白名单加入 datetime (纯日期运算, 无文件/ 网络/进程能力, 安全)。验证 os/sys/subprocess 仍被拦。 - strategy-guide.md: params type 补全(float/int/bool/select/date); filter_history 要点新增 date 参数必须 fromisoformat 转换的代码示例 - strategy-guide-compact.md: 同步补充 (AI 运行时实际用的文档) 从源头避免 AI 生成的 date 类型参数策略再犯字符串 vs Date 列比较错误。 Co-authored-by: shy3130 <shy3130@users.noreply.github.com>
This commit is contained in:
@@ -116,9 +116,9 @@ class AIStrategyGenerator:
|
||||
return content.split("```", 1)[1].split("```", 1)[0].strip()
|
||||
return content.strip()
|
||||
|
||||
# import 白名单: 策略文件只允许 polars (见 strategy-guide.md「只 import polars」)。
|
||||
# import 白名单: 策略文件只允许 polars + datetime (纯日期运算, 无文件/网络/进程能力)。
|
||||
# 白名单而非黑名单 — 黑名单挡不住 ctypes/importlib/builtins/pickle 等未列出的危险模块。
|
||||
_ALLOWED_IMPORT_MODULES = frozenset({"polars", "__future__"})
|
||||
_ALLOWED_IMPORT_MODULES = frozenset({"polars", "__future__", "datetime"})
|
||||
|
||||
@classmethod
|
||||
def _validate_safety(cls, code: str) -> None:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 必须遵守
|
||||
|
||||
1. 只 `import polars as pl`,禁止 import 其他模块。
|
||||
1. 只允许 `import polars as pl` 和 `from datetime import date/datetime`(date 类型参数比较需要),禁止 import 其他模块。
|
||||
2. AI 策略只属于 `data/strategies/ai/`,`META.id` 使用用户给定的 `ai_` ID。
|
||||
3. 不要读写文件,不要使用 `open/exec/eval/compile/__import__/globals/locals/vars/dir/getattr/setattr/delattr/type/input`。
|
||||
4. `META.params` 只放用户可能调整的阈值;公式常数和固定窗口边界不必参数化。
|
||||
@@ -32,7 +32,7 @@ META = {
|
||||
"exclude_st": True,
|
||||
"exclude_new_days": 30,
|
||||
},
|
||||
"params": [],
|
||||
"params": [], # type: float/int/bool/select/date;float/int 带 min/max/step
|
||||
"scoring": {},
|
||||
"order_by": "score",
|
||||
"descending": True,
|
||||
@@ -80,6 +80,15 @@ def filter_history(df: pl.DataFrame, params: dict) -> pl.DataFrame:
|
||||
|
||||
`filter_history()` 必须返回所有匹配行,不要只过滤最新日期;回测需要全区间命中。
|
||||
|
||||
**date 类型参数必须先转换再与 `date` 列比较**:params 里 `"type": "date"` 的参数从 JSON 传来是字符串(如 `"2024-01-01"`),而数据中 `date` 列是 Polars Date 类型,**不能直接比较**(报 InvalidOperationError)。必须先转换:
|
||||
|
||||
```python
|
||||
from datetime import date as _date
|
||||
anchor_raw = params.get("anchor_date", "2024-01-01")
|
||||
anchor_date = _date.fromisoformat(anchor_raw) if isinstance(anchor_raw, str) else anchor_raw
|
||||
# 之后才能: pl.col("date") == anchor_date 或 pl.col("date") > anchor_date
|
||||
```
|
||||
|
||||
## 常用字段
|
||||
|
||||
通用:`symbol`, `date`, `name`
|
||||
|
||||
@@ -35,7 +35,8 @@ META = {
|
||||
},
|
||||
|
||||
# 策略参数 (只把用户可能调节的阈值放这里,公式常数不必参数化)
|
||||
# 每个参数含 id/label/type/default/min/max/step;select 类型用 options
|
||||
# type 支持: float / int / bool / select(带 options) / date(格式 "YYYY-MM-DD")
|
||||
# float/int 可带 min/max/step;select 带 options: [{label, value}];date 的 default 是字符串
|
||||
"params": [
|
||||
],
|
||||
|
||||
@@ -143,6 +144,14 @@ def filter_history(df: pl.DataFrame, params: dict) -> pl.DataFrame:
|
||||
- 只有遇到表达式难以描述的复杂状态机时,才使用 `partition_by("symbol")` + `to_dicts()` 逐股票分析
|
||||
- **返回所有匹配行,不要过滤 `latest`**;选股引擎会自动取最新日,回测引擎需要全区间命中
|
||||
- 未声明 `filter_history()` 的策略走普通 `filter()` 路径,不受影响
|
||||
- **date 类型参数必须先转换再与 `date` 列比较**:params 里的 `"type": "date"` 参数从 JSON 传来是字符串(如 `"2024-01-01"`),而数据中 `date` 列是 Polars Date 类型,**不能直接比较**,否则报错。必须先转换:
|
||||
|
||||
```python
|
||||
from datetime import date as _date
|
||||
anchor_raw = params.get("anchor_date", "2024-01-01")
|
||||
anchor_date = _date.fromisoformat(anchor_raw) if isinstance(anchor_raw, str) else anchor_raw
|
||||
# 之后才能: pl.col("date") == anchor_date 或 pl.col("date") > anchor_date
|
||||
```
|
||||
|
||||
## 3. 常用指标列(参考,可直接使用)
|
||||
|
||||
@@ -266,7 +275,7 @@ def filter_history(df: pl.DataFrame, params: dict) -> pl.DataFrame:
|
||||
3. 用户可能调节的数值阈值通过 `params` 暴露;公式常数、固定窗口边界、一次性内部变量不必强行参数化
|
||||
4. `scoring` 权重总和必须为 1.0
|
||||
5. 遵循 A 股 T+1 规则 (当日买入次日才能卖出)
|
||||
6. 只允许 `import polars as pl`,禁止 import 其他模块
|
||||
6. 只允许 `import polars as pl` 和 `from datetime import date/datetime`(date 类型参数比较需要),禁止 import 其他模块
|
||||
7. 禁止使用 `open()`, `exec()`, `eval()`, `os`, `sys`, `subprocess`
|
||||
8. **贴合用户需求优先**:第3/4节的指标列和信号列仅供参考,能用则用;如果用户需求需要自定义计算(如"前高""上次涨停价""N日内某个事件后X天"),直接在 `filter_history()` 中自行设计和计算,不需要局限于已有列
|
||||
9. `filter_history()` 中优先用 Polars 向量化语法;仅在复杂状态机无法清晰表达时,才用 `partition_by("symbol")` 逐股票分析
|
||||
|
||||
Reference in New Issue
Block a user