Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
66a7432
feat: 迁移 czsc 核心至 Rust + PyO3 混合架构,并完善中文注释
0xcjun May 7, 2026
fb0afce
fix(audit): P0 - bump version, delete stale stub, purge stray rs_czsc…
0xcjun May 7, 2026
06bdcbc
refactor(traders): retire _lazy_rs_czsc, route sig_parse through czsc…
0xcjun May 7, 2026
e37e1ba
refactor(api): retire __init__.py lazy loading and shrink LoC by 54%
0xcjun May 7, 2026
b984dcd
feat(utils): implement stoploss_by_direction (czsc-only) via TDD
0xcjun May 7, 2026
80af45f
docs(migration): expand Phase Q with deferred-items table and verific…
0xcjun May 7, 2026
1b8a1c6
refactor(envs): trim czsc.envs from 117 -> 49 lines (-58%)
0xcjun May 7, 2026
7ef3ff1
docs(claude): sync project memory with Phase H/J/Q current state
0xcjun May 7, 2026
12cf082
feat(stubs): auto-generate czsc/_native.pyi via pyo3-stub-gen (spec §…
0xcjun May 7, 2026
f8a9c5b
ci(stubs): add czsc/_native.pyi drift check to code-quality workflow
0xcjun May 7, 2026
ad5a678
feat(sensors): partial restore of czsc/sensors/ (spec §9)
0xcjun May 7, 2026
0bd3082
perf(bench): add CZSC::new criterion benchmark (spec §6 P1 = 96.585ms…
0xcjun May 7, 2026
b551530
perf(bench): add 222-signal dispatch benchmark (spec §6 P2 = 1.1µs/si…
0xcjun May 7, 2026
1262dac
fix(tests): exclude additional fields from meta comparison in run_res…
0xcjun May 7, 2026
8907569
fix(ci): isolate stub_gen bin via required-features to fix linker error
0xcjun May 7, 2026
1a27fa2
chore(claude): copy skills/ from rs_czsc
0xcjun May 7, 2026
6b53268
fix(smoke): skip wheel install test when dist/ is empty
0xcjun May 8, 2026
88077ed
Refactor test code for improved readability and consistency
0xcjun May 8, 2026
959e88e
refactor: simplify date handling and improve warnings in various func…
0xcjun May 8, 2026
6f29922
refactor: improve file handling and enhance code readability across m…
0xcjun May 8, 2026
bb955a1
Refactor tests and improve code readability
0xcjun May 8, 2026
3dacc18
refactor: improve type hinting and fix groupby mapping in trade utili…
0xcjun May 8, 2026
0e3e79f
chore(rust): 注释中文化 + 收紧 CI clippy 严格模式
0xcjun May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
incremental = true
104 changes: 104 additions & 0 deletions .claude/skills/czsc-write-signal-function/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
name: czsc-write-signal-function
description: 为 rs_czsc 编写或重构 Rust 信号函数(bar/tas/cxt/pos)并接入事件驱动链路(`#[signal]` 自动注册、SignalConfig、Event/Position 匹配)的工作流。遇到“新增信号函数”“修改信号模板”“注册信号函数”“排查信号不触发”时使用。
---

# CZSC Signal Authoring

## Overview

用这个技能在 `rs_czsc` 中稳定完成信号函数开发:
1. 判断信号类型(K线级 / Trader级)
2. 编写函数并保持 `Signal` 7段格式
3. 注册到正确注册表
4. 用 `SignalConfig` 和测试验证是否能触发 `Event -> Position`

先读:
- `references/event-driven-signal-pipeline.md`
- `references/signal-function-patterns.md`

需要快速起草函数时,先运行:
- `scripts/new_signal_stub.py`

## Decision Tree

1. 需要访问仓位(`Position`)或策略状态吗?
- 是:写 Trader 级信号(`pos.rs` 风格),用 `#[signal(category = "trader", ...)]` 自动注册。
- 否:写 K线级信号(`bar/tas/cxt` 风格),用 `#[signal(category = "kline", ...)]` 自动注册。

2. 需要技术指标缓存吗?
- 是:使用 `TaCache`,优先走 `update_*_cache`。
- 否:保留 `_cache` 参数但不使用。

3. 需要多个输出信号吗?
- 是:返回 `Vec<Signal>`,每个信号都保持 7 段格式。
- 否:返回单元素 `Vec`。

## Workflow

1. 明确输入输出
- 明确函数输入参数(`params`)和默认值。
- 明确 `k1/k2/k3` 模板和 `v1/v2/v3` 语义。
- 明确“数据不足时”返回策略:通常返回 `vec![]` 或 `其他` 信号。

2. 实现函数
- K线级函数签名:`fn(&CZSC, &ParamView, &mut TaCache) -> Vec<Signal>`。
- Trader级函数签名:`fn(&dyn TraderState, &ParamView) -> Vec<Signal>`。
- 参数解析优先复用工具:`get_usize_param` / `get_str_param`。
- 必须写详细注释(硬性格式,不可省略小节):
- 标题行:`/// <func_name>:<一句话功能>`
- `///`
- `/// 参数模板:\`"..."\``
- `///`
- `/// 信号逻辑:`
- `/// 1. ...`
- `/// 2. ...`
- `/// 3. ...`
- `///`
- `/// 信号列表示例:`
- `/// - Signal('...')`
- `/// - Signal('...')`
- `///`
- `/// 参数说明:`
- `/// - <param>: <含义与默认值>`
- `/// - <param>: <含义与默认值>`
- 与 Python 或历史版本对齐时,必须补一行:`/// 对齐说明:...`
- 关键分支注释:说明为什么这么判定,而不是只写“做了什么”。
- 用 `format!` 拼出完整字符串:`k1_k2_k3_v1_v2_v3_score`。
- 用 `Signal::from_str(...)` 或 `parse::<Signal>()` 做最终构造。

3. 接入注册表
- 在函数上添加 `#[signal(...)]`,由 `inventory` 自动收集。
- 注册名与函数名保持一一对应(注意 `_V` 与 `_v` 大小写风格)。
- 在 `#[signal(...)]` 中给出 `template`,确保 `SignalConfig` 反解析和人类阅读一致。

4. 接入调用侧
- K线级:通过 `SignalConfig { name, freq: Some(...), params }` 在 `CzscSignals::update_signals` 中执行。
- Trader级:通过 `SignalConfig { name, freq: None, params }` 在 `CzscTrader::update` 的 Trader 信号分支执行。

5. 验证
- 至少验证:
- 函数在样本上可运行,不 panic。
- 输出信号可被 `Signal` 正常解析(7段、score 0~100)。
- 注册后能被 `SignalConfig.name` 命中。
- 若用于交易事件,`Event.matches_signals*` 能按预期触发。
- 注释完整:他人仅看注释可理解参数、边界、输出语义和关键判定。
- 优先补充/运行相关测试:`signal_compare_tests`、`trader_tests` 或新增针对性测试。
- 注释格式校验(提交前必须人工检查):
- 每个新/改信号函数都包含 `参数模板/信号逻辑/信号列表示例/参数说明` 四段。
- `信号列表示例` 至少 2 条,且与当前函数输出字段一致(`k1_k2_k3_v1_v2_v3_score`)。

## Guardrails

- 不要输出非 7 段信号;否则会在 `Signal::from_str` 失败。
- 不要忽略大小写差异;注册表键是精确匹配。
- 不要在数据长度不足时硬算;先做边界检查。
- 不要绕过 `TaCache` 重复全量计算重指标。
- 不要在函数里偷偷改全局状态;保持纯计算风格(Trader级仅读取状态)。
- 不要写空洞注释(如“计算信号”);注释要解释判定依据和业务语义。

## Resources

- 架构与触发链路:`references/event-driven-signal-pipeline.md`
- 函数模板与常见坑:`references/signal-function-patterns.md`
- 骨架生成器:`scripts/new_signal_stub.py`
4 changes: 4 additions & 0 deletions .claude/skills/czsc-write-signal-function/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "CZSC Signal Writer"
short_description: "Write Rust CZSC signal functions and wire registries"
default_prompt: "Use $czsc-write-signal-function to implement and register a new CZSC signal function."
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Event-Driven Signal Pipeline (rs_czsc)

## 1. 总体调用链

1. `CzscTrader::update` 接收一根 `RawBar`
2. `CzscSignals::update_signals` 计算本根所有 K线级信号
3. `CzscTrader::update` 额外执行 Trader 级信号(pos 系列)
4. 合并信号后调用每个 `Position::update`
5. `Position` 按 `opens + exits` 顺序匹配 `Event`
6. `Event` 内部通过 `signals_not -> signals_all -> signals_any` 判定
7. 匹配后产生命令(`LO/LE/SO/SE/...`)并更新持仓记录

## 2. 关键文件

- `crates/czsc-trader/src/trader.rs`
- 交易主入口;组织信号计算与仓位更新
- `crates/czsc-trader/src/signals/czsc_signals.rs`
- K线级信号执行器;维护 `s` 与 `sigs`
- `crates/czsc-signals/src/registry.rs`
- 汇总 `inventory` 自动收集的 `SignalDescriptor` 到运行时注册表
- `crates/czsc-signals/src/types.rs`
- 信号函数签名类型定义
- `crates/czsc-core/src/objects/signal.rs`
- `Signal` 7段格式、`is_match`
- `crates/czsc-core/src/objects/event.rs`
- `Event` 匹配逻辑
- `crates/czsc-core/src/objects/position.rs`
- `Position::update` 执行开平仓规则
- `crates/czsc-core/src/objects/state.rs`
- `TraderState` trait(给 Trader 级信号读状态)

## 3. 两类信号函数

### K线级信号(bar / tas / cxt)

- 签名:
`fn(&CZSC, &ParamView, &mut TaCache) -> Vec<Signal>`
- 注册方式:函数上 `#[signal(category = "kline", ...)]` 自动收集
- 调用路径:`CzscSignals::update_signals`
- 适用场景:仅依赖某个周期的 K 线结构和指标

### Trader级信号(pos)

- 签名:
`fn(&dyn TraderState, &ParamView) -> Vec<Signal>`
- 注册方式:函数上 `#[signal(category = "trader", ...)]` 自动收集
- 调用路径:`CzscTrader::update` 中 `freq.is_none()` 分支
- 适用场景:需要 `Position` + `CZSC` 联合状态

## 4. 信号字符串规则

`Signal` 必须满足:
- 格式:`k1_k2_k3_v1_v2_v3_score`
- 总段数:7 段
- `score`:0~100

`CzscSignals` 会把它拆成:
- key:`k1_k2_k3`
- value:`v1_v2_v3_score`

`Event` 匹配时用 `Signal::is_match`:
- `score >= 事件要求score`
- `v1/v2/v3` 精确匹配或事件侧为 `任意`

## 5. 开发时最常见断点

- 注册表 name 与 `SignalConfig.name` 不一致(大小写、`_V`/`_v`)
- 输出不是 7 段,导致 `Signal::from_str` 失败
- `freq` 设置错误:
- K线级应为 `Some(freq)`
- Trader级应为 `None`
- 数据不足未做边界检查,触发索引错误或无意义信号

## 6. 最小联调路径

1. 在 `czsc-signals` 实现函数并添加 `#[signal(...)]` 标注
2. 构造 `SignalConfig`
3. 用 `CzscTrader::update` 喂历史 bars
4. 检查:
- `trader.signals.s` 中是否有目标 key
- `trader.signals.sigs` 中是否出现目标 `Signal`
- `position.operates` 是否按预期变化
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Signal Function Patterns

## 0. 注释规范(必须)

每个新信号函数都必须包含以下注释层级:

- 函数文档注释(`///`)至少写清 4 件事:
- 信号在策略中的业务含义
- 参数模板字符串(`param_template`)
- 核心判定逻辑(触发条件)
- 边界行为(数据不足返回什么)
- 关键分支前写“原因注释”,解释判定依据和设计意图。
- 与 Python/旧版行为对齐时,标注“对齐对象”和“为何这样对齐”。

## 1. K线级函数模板(bar/tas/cxt)

```rust
use crate::params::ParamView;
use crate::types::TaCache;
use crate::utils::sig::{get_str_param, get_usize_param};
use czsc_core::analyze::CZSC;
use czsc_core::objects::signal::Signal;
use czsc_signal_macros::signal;

/// xxx_v240101: 示例信号
///
/// 参数模板:"{freq}_D{di}{key}_示例V240101"
/// 信号语义:当满足示例条件时输出目标状态,否则输出“其他”。
/// 边界行为:当 bars 不足以支持 di 回看时,返回空信号。
#[signal(
category = "kline",
name = "xxx_V240101",
template = "{freq}_D{di}{key}_示例V240101",
opcode = "XxxV240101",
param_kind = "XxxV240101"
)]
pub fn xxx_v240101(
czsc: &CZSC,
params: &ParamView,
cache: &mut TaCache,
) -> Vec<Signal> {
let di = get_usize_param(params, "di", 1);
let key = get_str_param(params, "key", "DEFAULT");

// 1) 边界检查:数据不足时不输出错误信号,直接返回空结果。
if czsc.bars_raw.len() < di + 2 {
return vec![];
}

// 2) 计算 k1/k2/k3 与 v1/v2/v3:
// k1/k2/k3 用于事件匹配 key,v1/v2/v3 承载状态值语义。
let k1 = czsc.freq.to_string();
let k2 = format!("D{}{}", di, key);
let k3 = "示例V240101";
let v1 = "其他";
let v2 = "任意";
let v3 = "任意";

// 3) 严格 7 段
let sig_str = format!("{}_{}_{}_{}_{}_{}_0", k1, k2, k3, v1, v2, v3);
Signal::from_str(&sig_str).map_or_else(|_| vec![], |s| vec![s])
}
```

## 2. Trader级函数模板(pos)

```rust
use crate::params::ParamView;
use crate::utils::sig::get_str_param;
use czsc_core::objects::signal::Signal;
use czsc_core::objects::state::TraderState;
use czsc_signal_macros::signal;
use std::str::FromStr;

/// pos_xxx_v240101: 示例 Trader 级信号
///
/// 参数模板:"{pos_name}_示例V240101"
/// 信号语义:根据仓位是否存在输出“有效/其他”。
/// 边界行为:缺少 pos_name 或查询不到仓位时输出“其他”。
#[signal(
category = "trader",
name = "pos_xxx_V240101",
template = "{pos_name}_示例V240101",
opcode = "PosXxxV240101",
param_kind = "PosXxxV240101"
)]
pub fn pos_xxx_v240101(cat: &dyn TraderState, params: &ParamView) -> Vec<Signal> {
let pos_name = get_str_param(params, "pos_name", "").to_string();

let k1 = format!("{}_状态", pos_name);
let k2 = "其他";
let k3 = "示例V240101";

// Trader 级信号必须解释“为何读取 trader 状态”,避免后续误改为纯 K线逻辑。
let v1 = if cat.get_position(&pos_name).is_some() {
"有效"
} else {
"其他"
};

let sig_str = format!("{}_{}_{}_{}_任意_任意_0", k1, k2, k3, v1);
Signal::from_str(&sig_str).map_or_else(|_| vec![], |s| vec![s])
}
```

## 3. 注册模板

通过 `#[signal(...)]` 自动注册,无需手写 `registry.rs` 列表。
关键字段:

```rust
#[signal(
category = "kline|trader",
name = "..._Vxxxxxx",
template = "...",
opcode = "...",
param_kind = "..."
)]
```

## 4. 参数与模板约束

- `params` key 名与模板占位符保持一致。
- 模板中 `freq` 表示周期;Trader 级常常不用 `freq` 字段做路由,依赖 `freq1` 之类业务参数。
- 维护 `k2/k3` 语义稳定,避免同名逻辑漂移。

## 5. 事件触发兼容性检查

新增信号用于事件时,先写出事件侧信号字符串,再倒推函数输出:

1. 事件侧 `Signal` 是否 7 段且 score 合法
2. 事件侧 `k1_k2_k3` 是否与函数输出完全一致
3. 事件侧 `v1/v2/v3` 是否允许 `任意`
4. 事件侧 score 是否不高于函数输出 score

## 6. 常见坑

- 在 `SignalConfig` 里用错注册名(尤其 `_V` 大写)
- `freq: None` / `Some` 配置反了,导致函数完全不被调度
- 直接 `unwrap` 导致边界数据 panic;优先 `map_or_else` 或早返回
- 函数输出多信号时 key 冲突未预期,后写会覆盖 `s` 字典同 key 值
Loading
Loading