czsc Documentation

缠中说禅技术分析工具;缠论;股票;期货;Quant;量化交易

6,428 stars Rust Rust 18 files · ~26,469 tokens #czsc#quant#tushare
RAW Doc

Technical Documentation: waditu/czsc

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

czsc - 缠中说禅技术分析工具





项目文档 |
投研数据共享 |
信号函数编写规范 |
DEVIN生成的文档

| 胜可知 · 大模型量化策略研究平台(内测)
使用大模型辅助量化策略研究,当前重点支持时序量价择时策略

邀请码:5862EDBH2H9CDTGZ

新手路线 · 研究方法 · 使用案例 · 产品介绍

1.0.X 版本开始,缠论核心算法(分型、笔、中枢等)已全部迁移到 Rust 实现,通过 PyO3 扩展(czsc._native)暴露给 Python。 需要了解旧 Python 实现逻辑的,可查看 0.9.X 版本。

czsc_skills

源于缠中说缠博客,原始博客中的内容不太完整,且没有评论,以下是网友整理的原文备份

架构概览

CZSC 1.0 采用 Rust + Python 混合架构:

text
czsc (Python 包)
├── czsc._native          ← Rust 扩展(PyO3),缠论核心
│   ├── CZSC / FX / BI / ZS / RawBar / NewBar / BarGenerator
│   ├── Freq / Mark / Direction / Signal / Event / Position / Operate
│   ├── CzscTrader / CzscSignals / generate_czsc_signals
│   ├── signals.*         ← 220+ 信号函数(Python 端 7 个子模块;详见 crates/czsc-signals/src/)
│   └── ta.*              ← Rust TA 算子 (ema/sma/boll 等,本次清理 起仅内部使用)
├── czsc.traders          ← Python 门面,汇聚 Rust 交易 API
├── czsc.utils            ← 工具函数(绘图/缓存/统计/交易工具)
├── czsc.connectors       ← 数据源连接器(天勤/Tushare/CCXT/本地缓存)
├── czsc.strategies       ← 策略门面(CzscStrategyBase/CzscJsonStrategy)
├── czsc.fsa              ← 飞书自动化工具
├── czsc.mock             ← 测试用模拟数据(转发自 wbt)
└── czsc.envs             ← 环境变量管理

底层 Rust workspace 包含 9 个 crate:czsc / czsc-core / czsc-derive / czsc-signals /
czsc-trader / czsc-utils / czsc-ta / czsc-signal-macros /
czsc-python(PyO3 绑定入口)。

项目贡献

  • 缠论的 分型、笔 的自动识别,由 Rust 实现并通过 czsc._native 暴露
  • 定义并实现 信号-事件-交易 量化交易逻辑体系,事件通过 signals_all/signals_any/signals_not 实现信号的逻辑组合
  • 定义并实现了 220+ 信号函数(Rust 实现),见 czsc._native.signals
  • 缠论多级别联立决策分析交易,详见 CzscTrader
  • HTML 可视化(plotly + lightweight-charts):czsc.utils.plotting.{kline,weight,lightweight}.*

安装使用

注意: Python 版本必须 ≥ 3.10

从 PyPI 安装预编译版本(推荐):

bash
pip install czsc -U

使用 uv 安装(推荐开发环境):

bash
uv pip install czsc

从源码构建(需要 Rust 工具链和 maturin):

bash
# 安装 Rust:https://rustup.rs/
# 安装 maturin
pip install maturin

# 克隆并构建
git clone https://github.com/waditu/czsc.git
cd czsc
maturin develop --release

Rust 构建环境约束:底层依赖 pyo3 / pyo3-stub-gen 0.22 要求 Python ≥ 3.10。
当系统默认 Python 低于 3.10 时,请通过环境变量显式指定:

bash
export PYO3_PYTHON=$(which python3.12)   # 或任意 3.10+ 的解释器

否则 cargo build / cargo test 会在 crates/czsc-python/build.rs 提前 panic 并给出修复建议。
用 uv sync --extra dev 走 UV 流程时,UV 会自动选择项目声明的 Python,不需要额外设置。

快速开始

核心缠论分析

python
import czsc
from czsc import CZSC, Freq, format_standard_kline
from czsc.mock import generate_symbol_kines

# 生成模拟 K 线数据
df = generate_symbol_kines('000001', '30分钟', '20240101', '20240601')

# 转换为 RawBar 对象列表
bars = format_standard_kline(df, freq=Freq.F30)

# 创建 CZSC 分析对象(自动识别分型、笔、中枢)
czsc_obj = CZSC(bars)
print(f"笔数量:{len(czsc_obj.bi_list)}")
print(f"中枢数量:{len(czsc_obj.zs_list)}")

K 线合成与多级别分析

python
from czsc import BarGenerator, Freq

# 使用 BarGenerator 进行 K 线合成
bg = BarGenerator(base_freq='1分钟', freqs=['5分钟', '30分钟', '日线'])
for bar in raw_bars:
    bg.update(bar)

# 获取各周期 K 线
bars_5m = bg.bars['5分钟']
bars_30m = bg.bars['30分钟']

信号生成

python
from czsc import generate_czsc_signals, get_signals_config, get_signals_freqs

# 配置信号序列(使用 Rust 实现的信号函数)
signals_seq = [
    "czsc._native.signals.bar.bar_end_V230331",
    "czsc._native.signals.cxt.cxt_bi_status_V230101",
]

# 解析信号所需的周期配置
freqs = get_signals_freqs(signals_seq)
config = get_signals_config(signals_seq)

# 生成信号序列
results = generate_czsc_signals(bars, signals_seq)

权重回测

python
from czsc import WeightBacktest
from czsc.mock import generate_klines_with_weights

# 生成带权重的模拟数据
dfw = generate_klines_with_weights(seed=42)

# 运行权重回测
wb = WeightBacktest(dfw, fee_rate=0.0002)
print(wb.stats)  # 回测统计汇总

策略研究

python
from czsc import run_research, run_replay

# 单品种回放
run_replay(bars, signals_seq, pos_seq, res_path='./results/')

# 批量品种研究
run_research(symbols, signals_seq, pos_seq, res_path='./results/')

缠论可视化与回测报告

python
# 缠论 K 线(多周期联立,自包含 HTML,离线即可打开)
from czsc.utils.plotting.lightweight import plot_czsc, plot_czsc_trader

html = plot_czsc(c, output="html")  # 单周期
plot_czsc_trader(ct, output="html", path="trader.html")  # 多周期

# 回测 HTML 报告:用 wbt.generate_backtest_report 一键产出
from wbt import generate_backtest_report
generate_backtest_report(df=dfw, output_path="report.html", weight_type="ts")

使用案例

更多端到端案例汇总见飞书 wiki:CZSC 1.0.X 版本使用说明和案例。

序号 案例 一句话介绍
1 基于 Event 的策略回测 + wbt HTML 报告 5 行命令跑通 Event → Position → 回测 → 可交互 HTML 报告的完整闭环。
2 lightweight_charts 缠论可视化 几行代码把缠论结构(分型/笔/线段/中枢)导出为自包含离线 HTML,便于分享与嵌入。
3 把信号函数画到 K 线主图(plot_czsc_signals) 把信号触发点叠在 K 线主图上,直观调试 signals_config 与多周期联立。
4 性能基准测试:在本地复现 CZSC / CzscTrader 吞吐量 一行命令本地评估吞吐量,配套关键指标速查表,用于容量规划与加速效果核对。
5 信号函数模块深入介绍 系统讲解 czsc-signals 架构与 246 个信号函数的模块划分、参数模板、信号表达示例。

核心 API 一览

类型 符号 说明
缠论对象 CZSC, FX, BI, ZS 缠论核心数据结构(Rust)
K线对象 RawBar, NewBar, BarGenerator K线与合成器(Rust)
枚举 Freq, Mark, Direction, Operate 方向/频率等枚举(Rust)
信号/事件 Signal, Event, Position 信号与持仓逻辑(Rust)
分析工具 check_fx, check_bi, remove_include 分型/笔校验工具(Rust)
TA算子顶层别名 czsc.ema, czsc.sma, czsc.rolling_rank, czsc.boll_positions, czsc.ultimate_smoother 技术指标算子顶层别名(Rust)
交易器 CzscTrader, CzscSignals 多级别交易决策(Rust)
信号生成 generate_czsc_signals 批量信号生成(Rust)
权重回测 WeightBacktest 权重序列回测(来自 wbt)
策略 CzscStrategyBase, CzscJsonStrategy 策略封装(Python)
模拟数据 generate_symbol_kines 测试用 K线数据(来自 wbt)
格式转换 format_standard_kline DataFrame → RawBar 列表

数据源连接器

czsc.connectors 提供多个数据源适配器:

模块 数据源 说明
tq_connector.py 天勤(TQSdk) 期货实时/历史行情
ts_connector.py Tushare A股历史数据
ccxt_connector.py CCXT 数字货币交易所
local_data.py 投研数据本地缓存 CZSC 共享数据本地读取入口

可视化(HTML 输出)

可视化统一以 plotly + lightweight-charts 输出 HTML:

模块 功能
czsc.utils.plotting.kline 单周期 K 线 + 缠论结构(plotly Figure)
czsc.utils.plotting.weight 权重时序图(plotly Figure)
czsc.utils.plotting.lightweight lightweight-charts 自包含 HTML,多周期联立 + 信号叠加
累计收益 / 回撤 / 月度热力图 / 综合回测概览 改用 wbt.generate_backtest_report 或直接 plotly,见 docs/migration/cleanup-non-czsc-core.md

使用 plot_czsc、plot_czsc_trader 或 plot_czsc_signals 生成自包含 HTML 后,可直接在浏览器中打开。

相关项目(生态依赖)

czsc 在权重回测、权重落地与 TA 算子一致性校验上依赖以下三个独立开源项目,它们与 czsc 形成"分析 + 回测 + 落地 + 校验"的完整闭环:

项目 GitHub 角色 与 czsc 的关系
wbt https://github.com/zengbin93/wbt 策略持仓权重回测引擎(Weight Back Test) 硬依赖。提供 WeightBacktest / daily_performance / top_drawdowns / generate_backtest_report 等;czsc.mock 也转发自 wbt.mock。czsc 顶层 from czsc import WeightBacktest 即来自此包。
wmr https://github.com/zengbin93/wmr 策略持仓权重管理系统(Weight Manager,DuckDB / ClickHouse 双后端) 下游配套(非硬依赖)。wbt 负责"离线回测权重",wmr 负责"实盘 / 投研环境下权重的持久化、版本管理与查询"。czsc 产出信号 / 持仓 → wbt 跑回测 → wmr 落库供下游消费。
talib-rs https://github.com/0xcjun/talib-rs 纯 Rust 实现的 TA-Lib 替代库 测试可选依赖。仅 tests/unit/test_ta_parity.py 用它对 czsc._native.ta(EMA / SMA 等)做数值 parity 校验,确保 czsc 自研 TA 算子与 TA-Lib 行为一致。运行时代码不依赖。

简言之:wbt = 回测,wmr = 权重落地,talib-rs = TA 基准;czsc 自身专注缠论核心算法(Rust)与信号-事件-交易体系,其余环节通过这三个项目解耦协同。

开发环境搭建

bash
# 使用 UV 管理依赖(推荐)
uv sync --extra dev

# 构建 Rust 扩展(开发模式)
maturin develop

# 运行测试
uv run pytest tests/ -v

# 代码格式化
uv run ruff format czsc/ tests/
uv run ruff check czsc/ tests/

关键环境变量

变量 说明 默认值
CZSC_MIN_BI_LEN 最小笔长度 6
CZSC_MAX_BI_NUM 最大笔数量 50
CZSC_VERBOSE 是否输出详细日志 False

使用前必看

  • 1.0.X 版本核心算法已迁移到 Rust,与 0.9.X 版本 不兼容;旧代码需按新 API 迁移;
  • 免责声明:项目开源仅用于技术交流!
  • 如果你发现了项目中的 Bug,可以先读一下《如何有效地报告 Bug》,然后在 issues 中报告 Bug

缠论精华

学了本ID的理论,去再看其他的理论,就可以更清楚地看到其缺陷与毛病,因此,广泛地去看不同的理论,不仅不影响本ID理论的学习,更能明白本ID理论之所以与其他理论不同的根本之处。

为什么要去了解其他理论,就是这些理论操作者的行为模式,将构成以后我们猎杀的对象,他们操作模式的缺陷,就是以后猎杀他们的最好武器,这就如同学独孤九剑,必须学会发现所有派别招数的缺陷,这也是本ID理论学习中一个极为关键的步骤。

真正的预测,就是不测而测。所有预测的基础,就是分类,把所有可能的情况进行完全分类。有人可能说,分类以后,把不可能的排除,最后一个结果就是精确的。
这是脑子锈了的想法,任何的排除,等价于一次预测,每排除一个分类,按概率的乘法原则,就使得最后的所谓精确变得越不精确,最后还是逃不掉概率的套子。
对于预测分类的唯一正确原则就是不进行任何排除,而是要严格分清每种情况的边界条件。任何的分类,其实都等价于一个分段函数,就是要把这分段函数的边界条件确定清楚。
边界条件分段后,就要确定一旦发生哪种情况就如何操作,也就是把操作也同样给分段化了。然后,把所有情况交给市场本身,让市场自己去当下选择。
所有的操作,其实都是根据不同分段边界的一个结果,只是每个人的分段边界不同而已。因此,问题不是去预测什么,而是确定分段边界。

原文整理

资料分享


Overview (`crates/czsc-core/README.md`)

czsc-core

💡 大多数用户应优先使用 facade crate czsc:
cargo add czsc 一行拿到全部公共 API,本 crate 中的核心类型已在 facade
顶层 re-export。仅当你只想要缠论核心算法、不需要工具/信号/trader 时
才单独依赖本 crate。

缠论(CZSC,缠中说禅技术分析理论)核心数据结构与算法的 Rust 实现。

提供分型(FX)、笔(BI)、中枢(ZS)、CZSC 分析器等核心类型,以及 K 线
包含关系处理(remove_include)、笔识别(check_bi)等基础算法。

用法

toml
[dependencies]
czsc-core = "1.0"
rust
use czsc_core::objects::{RawBar, Freq};
use czsc_core::analyze::CZSC;

let bars: Vec<RawBar> = /* ... */;
let czsc = CZSC::new(bars, 50, 6);
println!("最新一笔方向: {:?}", czsc.bi_list.last().map(|b| b.direction));

特性

  • python:启用 PyO3 binding,供 czsc-python crate 聚合到 czsc._native 扩展。

项目主页


Overview (`crates/czsc-derive/README.md`)

czsc-derive

⚙️ 本 crate 是 czsc 框架的内部 proc-macro,普通用户不需要直接依赖。
想用 czsc 量化分析框架请 cargo add czsc(facade crate)。
本 crate 之所以独立发布是因为 cargo 强制 proc-macro 必须为单独的 crate,
仅供 czsc-core /
czsc-utils /
czsc-trader 等内部使用,也可被
其它需要 anyhow ↔ enum 自动桥接的 Rust 项目复用。

CZSC 工作区的过程式派生宏(procedural derive macros)。

当前提供 #[derive(CZSCErrorDerive)]:为枚举错误类型自动实现

  • From<anyhow::Error> —— 把任意 anyhow::Error 装进枚举的 #[from] 变体
  • serde::Serialize —— 让错误结构跨 FFI / API 边界可序列化为字符串

用法

toml
[dependencies]
czsc-derive = "1.0"
anyhow      = "1"
thiserror   = "2"
serde       = { version = "1", features = ["derive"] }
rust
use czsc_derive::CZSCErrorDerive;

#[derive(CZSCErrorDerive, thiserror::Error, Debug)]
pub enum MyError {
    #[error("invalid bar: {0}")]
    InvalidBar(String),
    #[from]
    #[error("other: {0}")]
    Other(anyhow::Error),
}

历史

本 crate 在 1.0.0 之前曾名为 error-macros。重命名为 czsc-derive 是为了
避免占用 crates.io 上「error-macros」这一过于通用的命名空间。API 完全保持
兼容,仅 use 路径与 Cargo.toml 依赖名变化。

项目主页


Overview (`crates/czsc-signal-macros/README.md`)

czsc-signal-macros

⚙️ 本 crate 是 czsc 框架的内部 proc-macro,普通用户不需要直接依赖。
想用 czsc 量化分析框架请 cargo add czsc(facade crate)。
本 crate 之所以独立发布是因为 cargo 强制 proc-macro 必须为单独的 crate,
由 czsc-signals 内部使用;
仅当你想为自己的 Rust 项目集成 czsc 风格的 inventory 信号注册系统时才
需要直接依赖。

czsc-signals 配套的过程宏 crate,
为缠论信号函数提供编译期签名校验与自动注册。

提供的宏

  • #[signal(category = "kline", template = "{freq}_{name}_{ver}")]
    —— 标记一个函数为信号入口,校验签名并把元数据写入 inventory
  • #[signal_module(category = "kline")]
    —— 标记一个 mod 为信号模块,统一应用类别默认值

用法

name 必须与函数名的版本后缀一致(如 foo_v230101 对应
foo_V230101);template 描述实际输出的 k1_k2_k3,k3 末尾完整连续
ASCII 数字串必须与注册版本完全相等,不能仅包含相同尾部。保留模板既有标记形式(如 V230101
或 BS辅助221211),实际输出必须与模板一致。

toml
[dependencies]
czsc-signal-macros = "1.0"
czsc-signals       = "1.0"
inventory          = "0.3"
rust
use czsc_signal_macros::{signal, signal_module};

#[signal_module(category = "kline")]
pub mod my_signals {
    use super::*;

    #[signal(template = "{freq}_我的信号_V250101")]
    pub fn sig_v250101(/* ... */) -> /* ... */ { /* ... */ }
}

项目主页


Overview (`crates/czsc-signals/README.md`)

czsc-signals

💡 大多数用户应优先使用 facade crate czsc:
cargo add czsc 一行拿到全部公共 API,本 crate 已在 czsc::signals
命名空间中 re-export。仅当你只想接入 czsc 的信号函数库到自己的
框架(不用 czsc-trader 状态机)时才单独依赖本 crate。

缠论信号函数库 —— 30+ 量化信号函数,按类别分组:

  • bar:基础 K 线形态信号
  • cxt:缠论上下文信号(笔、中枢相关)
  • tas:技术分析叠加信号(MACD、RSI 等)
  • vol:成交量信号
  • pressure / obv / cvolp:资金、压力、量价信号

每个信号通过 #[signal(...)]
属性宏在编译期自动注册到 inventory,运行期可按字符串配置(如
"日线_D1#10_看多V240520")解析、调用。

用法

toml
[dependencies]
czsc-signals = "1.0"
rust
// 通过 inventory 拿到注册的信号函数
let funcs = czsc_signals::all_signals();
println!("registered: {}", funcs.len());

项目主页


Overview (`crates/czsc-ta/README.md`)

czsc-ta

💡 如果你需要的是完整的缠论框架,请优先使用 facade crate
czsc
:cargo add czsc 一行拿到
全部 API(含本 crate 的算子)。本 crate 是少数适合单独依赖的场景——
当你只想要纯算子库(EMA/SMA/rolling_rank/...)、不需要任何缠论
业务代码时,可以零额外依赖地 cargo add czsc-ta。

技术分析算子库 —— 缠论分析与量化策略常用的一组高性能数值算子。

包含 EMA、SMA、rolling_rank、ultimate_smoother 等纯 Rust 实现,附带可选的
numpy 互操作层(rust-numpy feature)以便供 PyO3 binding 直接零拷贝调用。

用法

toml
[dependencies]
czsc-ta = "1.0"
rust
use czsc_ta::pure::ema::ema;

let prices = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let smoothed = ema(&prices, 3);

特性

  • python:导出 PyO3 module。
  • rust-numpy:在 python 之上加 numpy 数组互操作(零拷贝 in/out)。

项目主页


Overview (`crates/czsc-trader/README.md`)

czsc-trader

💡 大多数用户应优先使用 facade crate czsc:
cargo add czsc 一行拿到全部公共 API,CzscTrader / CzscSignals /
SignalConfig 等核心类型已在 facade 顶层 re-export。仅当你已经
单独依赖 czsc-core/signals 并自己组装 trader 状态机
时才直接依赖本
crate。

缠论多策略交易引擎、信号编译与参数优化。

核心组件:

  • engine_v2 —— 事件驱动式 v2 执行引擎
  • signals —— 信号字符串 → SignalConfig 编译,与 czsc-signals 配合
  • trader —— CzscTrader / CzscSignals 状态机
  • optimize —— 持仓策略参数网格搜索

权重回测(WeightBacktest)按 czsc 设计文档由外部 wbt
crate 提供,czsc-trader 只负责生成信号与持仓权重序列。

用法

toml
[dependencies]
czsc-trader = "1.0"

项目主页


Overview (`crates/czsc-utils/README.md`)

czsc-utils

💡 大多数用户应优先使用 facade crate czsc:
cargo add czsc 一行拿到全部公共 API,本 crate 暴露的 BarGenerator /
is_trading_time / freq_data 已在 facade 中 re-export。仅当你只用 K
线合成或交易日历工具
、不需要其它 czsc 功能时才单独依赖本 crate。

缠论分析的 K 线合成与交易日历工具。

提供:

  • BarGenerator —— 多周期 K 线合成器
  • freq_data —— 频率周期与对齐时间计算
  • is_trading_time —— 国内 A 股 / 期货市场交易时段判断

用法

toml
[dependencies]
czsc-utils = "1.0"
rust
use czsc_utils::is_trading_time;
use chrono::NaiveDateTime;

let dt: NaiveDateTime = "2024-01-02 10:30:00".parse().unwrap();
assert!(is_trading_time("000001.SZ", dt));

特性

  • python:启用 PyO3 binding。

项目主页


Overview (`crates/czsc/README.md`)

czsc


🚀 这是 czsc 量化分析框架的官方推荐入口(facade crate)。
Rust 用户只需 cargo add czsc 即可获得全部公共 API。

CZSC(缠中说禅)量化分析框架的 Rust 入口。

本 crate 是一个 facade:本身不实现任何功能,只把 czsc Rust workspace 中
的 5 个业务 crate(czsc-core / czsc-utils / czsc-ta /
czsc-signals / czsc-trader)汇总到一个 crate 名下,让纯 Rust 用户
不用一行一行加 5 个依赖。

想用 Python 接口?请直接 pip install czsc,那是基于 maturin 编译的
CPython 扩展,与本 facade 同源但接口面不同。

用法

toml
[dependencies]
czsc = "1.0"
rust
use czsc::{CZSC, RawBar, Freq, BarGenerator, is_trading_time};
use czsc::analyze::CZSC as Analyzer;

let bars: Vec<RawBar> = /* ... */;
let analyzer = Analyzer::new(bars, 50);
println!("最新一笔方向: {:?}", analyzer.bi_list.last().map(|b| b.direction));

也可以按子模块组织 import:

rust
use czsc::objects::bi::BI;
use czsc::ta::ema;
use czsc::signals::registry::TRADER_SIGNAL_REGISTRY;
use czsc::trader::{CzscSignals, SignalConfig};

命名空间速查

路径 等价于
czsc::analyze czsc_core::analyze
czsc::objects::* czsc_core::objects::*
czsc::error_chain czsc_core::error_chain
czsc::ta czsc_ta::pure
czsc::bar_generator czsc_utils::bar_generator
czsc::freq_data czsc_utils::freq_data
czsc::trading_time czsc_utils::trading_time
czsc::signals czsc_signals
czsc::trader czsc_trader 的对外公共面

顶层 czsc::* 还直接挂了最常用的核心类型:CZSC / RawBar / NewBar /
Freq / FX / BI / ZS / Mark / Direction / Operate / Event /
Position / Market / BarGenerator / CzscSignals / SignalConfig /
is_trading_time。

Features

  • (default) —— 纯 Rust API
  • python —— 透传给 czsc-core / czsc-utils / czsc-ta,给嵌入 PyO3
    的下游使用。一般用户不需要

工作区其它 crate

crate 说明
czsc-core 缠论核心数据结构与算法
czsc-utils BarGenerator / freq_data / trading_time
czsc-ta 技术分析算子
czsc-signals 30+ 量化信号函数
czsc-trader 交易引擎与信号编译
czsc-derive 错误派生宏(内部用)
czsc-signal-macros 信号注册宏(内部用)

项目主页


Overview (`scripts/auto-czsc-quant/README.md`)

auto-czsc-quant

auto-czsc-quant 是一个脚本级实验编排器,用来跑通“大模型改 Position 配置 + CZSC 自动评分”的闭环。

当前实现不改 czsc 核心包;它可以读取人工候选 JSONL,也可以调用 .env 中配置的 Anthropic 兼容 LLM 生成完整 position 候选,然后用 mock、Tushare 或标准 feather 行情完成校验、回测、评分和实验记录。

快速运行

从仓库根目录执行:

bash
PYTHONPATH=scripts/auto-czsc-quant uv run python -m auto_quant.cli run \
  --config scripts/auto-czsc-quant/configs/example.json

运行后会生成:

text
scripts/auto-czsc-quant/results/mock_position_demo/<run_id>/
  config.json
  accepted.jsonl
  rejected.jsonl
  leaderboard.csv
  journal.md
  report.html
  best_positions/
  curves/              # 每个候选的组合日收益序列,供报告叠加收益曲线对比

report.html 是结构化优化报告,包含运行配置、top-k 策略累计收益曲线对比、最佳对比、数据加载、执行记录、leaderboard、候选详情、拒绝原因和产物索引。

优化语义

优化只允许两种合法操作(可重复执行上百、上千次,逐步累积变好):

  • 入场优化:修改 opens 中某个 event 的 signals_all / signals_any / signals_not,换用不同的完全分类 signal,改进入场点。
  • 出场优化:在 exits 中新增 event,让策略提前正确止盈/止损,降低收益回撤。

每次迭代必须真正改变 opens/exits 的 event 信号,否则候选会被校验拒绝:

  • 信号必须真实存在:候选里每个信号都要能被 czsc.derive_signals_config 解析(即在 czsc 信号注册表中),臆造信号一律拒绝。
  • 必须与 baseline 不同:opens/exits 信号签名与 baseline 完全相同的候选(无效克隆)会被拒绝;同一批里签名重复的候选也会被拒。

LLM 优化(candidate_mode=llm 或 /goal 提示)会把一份「信号目录」注入 prompt:从 czsc 注册表 + signal-functions skill 文档中提取经校验可直接使用的开多/平多方向信号字符串(参考 .claude/skills/signal-functions),让模型每轮都从真实信号池里选。

interval / timeout / stop_loss / T0 不参与优化:无论候选提交什么值,都会被 baseline(若配置 baseline_position_path)或安全默认值强制覆盖。/goal 下一轮提示会把上一轮 best 的 Position 作为新 baseline 喂回去,让优化持续迭代。

候选协议

候选文件是 JSONL,一行一个完整候选:

json
{"id":"trial_001","hypothesis":"入场优化:在开多 event 加入顶分型过滤","position":{...}}

约束:

  • id 必须唯一。
  • hypothesis 必须非空。
  • position 必须能被 czsc.Position.load 加载。
  • position.opens 不能为空。
  • 候选数量不能超过配置中的 max_candidates。
  • interval / timeout / stop_loss / T0 即便填写也会被系统锁定覆盖。

数据源

data_source 支持三种模式:

  • mock:使用 czsc.mock.generate_symbol_kines 生成确定性行情,适合测试。
  • tushare:直接通过 czsc.connectors.ts_connector.get_raw_bars 拉取真实行情;需要在环境变量或仓库根 .env 配置 TUSHARE_TOKEN。
  • feather:读取用户传入的标准行情 feather/ipc 文件;必需列为 dt,symbol,open,close,high,low,vol,amount。

Tushare + 真实 LLM 示例:

bash
PYTHONPATH=scripts/auto-czsc-quant uv run python -m auto_quant.cli run \
  --config scripts/auto-czsc-quant/configs/example.tushare-llm.json

.env 中默认读取:

text
TUSHARE_TOKEN=...
ANTHROPIC_BASE_URL=...
ANTHROPIC_API_KEY=...
ANTHROPIC_MODEL=...

CLI

bash
PYTHONPATH=scripts/auto-czsc-quant uv run python -m auto_quant.cli run --config <config.json>
PYTHONPATH=scripts/auto-czsc-quant uv run python -m auto_quant.cli prompt --run-dir <results/.../run_id>

prompt 会读取上一轮 leaderboard.csv 和 journal.md,输出一个适合交给 /goal 的窄目标:只生成下一轮候选 JSONL,不修改源码。


Overview (`tests/compat/baselines/README.md`)

PR-1 基线快照

由 PR-1(盘点冻结)记录的"清理前"状态。

文件 说明
streamlit_imports.txt czsc/ tests/ docs/ 下所有 import streamlit / from streamlit 行
svc_refs.txt 同上范围下所有 from czsc.svc / czsc.svc.* 引用行
ta_refs.txt 同上范围下所有 from czsc.ta / czsc.ta. / from .ta 引用行

后续 PR 在 review 阶段用以下命令对比,每个 PR 应当只减不增:

bash
git grep -nE "import streamlit|from streamlit" -- 'czsc/**/*.py' 'tests/**/*.py' 'docs/**/*.py' | diff - tests/compat/baselines/streamlit_imports.txt

最终 PR-5 完成时,三份基线对应的现状都应为空。


Examples (`docs/examples.md`)

CZSC 核心功能案例库

目的:让用户在 30 分钟内对 czsc 库的核心能力建立完整的认知,并能围绕"缠论分析 → 信号 → 事件 → 策略 → 回测 → 可视化"这条主线动手实践。

适用版本:czsc ≥ 1.0(Rust + PyO3 架构,Python 端不再保留缠论算法回退)。

每个案例都是一个独立的 Python 脚本,位于 docs/examples/。
除明确标注 Tushare 的真实数据案例外,所有脚本仅依赖 czsc 顶层包(czsc.mock 提供模拟 K 线 / 权重数据,无需外部行情源),可以直接:

bash
# 普通脚本
uv run python docs/examples/01_quick_start.py

# HTML 可视化脚本(生成自包含 HTML,浏览器直接打开)
uv run python docs/examples/13_lightweight_charts_html.py

统一产物目录:03/07/08/09 这类有落盘产物的脚本,全部把输出写到
docs/examples/_output/(HTML 报告、parquet、Position JSON 等)。
该目录已在仓库 .gitignore 中忽略,无需手工清理;要重置只需 rm -rf docs/examples/_output。


0. 系统全貌

text
┌──────────────┐    ┌─────────────────┐    ┌─────────────┐
│  RawBar 列表 │ ─► │  CZSC(缠论分析) │ ─► │  分型/笔/中枢 │
└──────────────┘    └─────────────────┘    └─────────────┘
        │                    │
        │ BarGenerator        │   CzscSignals.update_signals(bar)
        │(多周期合成)        │
        ▼                    ▼
┌──────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   多周期 bg   │ ─► │  signals_config  │ ─► │  信号字典 cs.s   │
└──────────────┘    └─────────────────┘    └─────────────────┘
                             │
                             ▼ Event(signals_all/any/not)
                    ┌─────────────────┐
                    │  Position(仓位)│
                    └─────────────────┘
                             │
                  CzscTrader.update(bar) / CzscStrategyBase.backtest
                             ▼
                    ┌─────────────────────────────────────┐
                    │ pairs / holds / signals (Arrow)     │
                    └─────────────────────────────────────┘
                             │
              ┌──────────────┴──────────────┐
              ▼                             ▼
       WeightBacktest                 plot_*绘图
       (时序/截面)                    (离线 HTML)

1. 案例索引

第一组:缠论核心(必读)

# 文件 核心 API 你将学到
01 01_quick_start.py czsc.mock.generate_symbol_kines · format_standard_kline · CZSC 数据准备、构造分析对象、增量 update 推进
02 02_chan_structures.py FX · BI · ZS · Mark · Direction 分型/笔/中枢的属性、力度(power/SNR/slope/angle)、未完成笔(ubi)

第二组:多级别与信号

# 文件 核心 API 你将学到
04 04_bar_generator.py BarGenerator · freqs_sorted 用低周期 K 线实时合成高周期 K 线,构造多级别 CZSC
05 05_signals.py CzscSignals · generate_czsc_signals · get_signals_config 三种信号生成方式 + 反推信号配置/涉及周期

第三组:事件驱动策略

# 文件 核心 API 你将学到
06 06_event_position.py Event · Position · Operate · Event.is_match 信号 → 事件(all/any/not 逻辑组合) → 持仓
07 07_strategy_backtest.py CzscStrategyBase · tactic.backtest · tactic.replay · tactic.save_positions 完整的策略类定义 + 内存回测 + 落盘回放 + 持仓序列化(产物:_output/07_strategy_backtest/)

第四组:回测与离线可视化

# 文件 核心 API 你将学到
08 08_weight_backtest.py WeightBacktest · daily_performance · top_drawdowns 时序权重回测的完整链路 + 核心绩效指标
13 13_event_weight_backtest.py CzscStrategyBase.backtest · wbt.generate_backtest_report 从 Event 出发的策略回测 + 一键生成 HTML 报告(single / multi-event)

第五组:lightweight-charts HTML(自包含交互看图)

# 文件 关键 API 你将看到
13 13_lightweight_charts_html.py plot_czsc · plot_czsc_trader 缠论 K 线 + 多周期联立,自包含 HTML(无需服务端)
15 15_lightweight_signals_html.py plot_czsc_signals 信号叠加版本,含 signal timeline + tooltip

第六组:真实数据研究(需 `TUSHARE_TOKEN`)

# 文件 核心 API 你将看到
14 14_tushare_event_backtest.py get_raw_bars · CzscStrategyBase · WeightBacktest 单标的 30 分钟 Event 策略回测与 HTML 报告
18 18_tushare_daily_event_universe.py generate_czsc_signals · adjust_holding_weights · WeightBacktest 日线股票池事件选股与 HTML 报告

运行真实数据案例前,请设置 TUSHARE_TOKEN;缺少令牌时脚本会明确退出,不影响本地 mock 案例验证。

第七组:性能基准

# 文件 核心 API 你将看到
17 17_perf_benchmark.py CZSC · CzscTrader 20 年 5 分钟 K 线下 CZSC / CzscTrader 两条路径的吞吐量基准(纯文本输出)

2. 模块关联速查

想做的事 应使用的模块 / 函数 案例编号
拿到模拟 K 线 / 权重数据 czsc.mock.generate_symbol_kines / generate_klines_with_weights 01, 08
转 DataFrame → RawBar czsc.format_standard_kline 01
跑缠论分析 czsc.CZSC(bars) 01, 02
多周期 K 线合成 czsc.BarGenerator 04, 11
单次批量计算信号 czsc.generate_czsc_signals 05
实盘流式信号计算 czsc.CzscSignals + update_signals(bar) 05, 11
信号字符串 → 配置 czsc.get_signals_config / get_signals_freqs 05, 06
事件逻辑组合 czsc.Event(signals_all/any/not) 06
完整持仓策略 czsc.Position / czsc.CzscStrategyBase 06, 07
多级别交易决策 czsc.CzscTrader 11
内存回测 tactic.backtest(bars) 或 czsc.run_research 07
落盘回放 tactic.replay(bars, res_path) 或 czsc.run_replay 07
持仓序列化 Position.dump / tactic.save_positions / CzscJsonStrategy 07
权重回测 czsc.WeightBacktest(来自 wbt) 08, 12
绩效指标 czsc.daily_performance / top_drawdowns 08
后验时段标记 czsc.mark_cta_periods / mark_volatility —
离线 K 线绘图 czsc.utils.plotting.lightweight.plot_czsc{,_trader,_signals} 13, 15
回测 HTML 报告 wbt.generate_backtest_report 13

3. 典型工作流

3.1 "策略研究 → 回测 → 可视化" 完整流水线

text
[01] 准备数据
   ↓
[02] 看 CZSC 缠论结构(确认笔/分型识别正常)
   ↓
[04] 决定使用哪些周期,构造 BarGenerator
   ↓
[05] 设计信号配置(哪些信号 × 哪些周期)
   ↓
[06] 把信号组装成 Event;多个 Event 组装成 Position
   ↓
[07] 用 CzscStrategyBase 子类化 + 跑 backtest/replay
   ↓
[08] 把 trader / replay 输出的权重做 WeightBacktest 精细回测
   ↓
[09] 配合 EDA 工具(如 mark_cta_periods)做切片分析
   ↓
[13/15] 生成自包含 HTML 报告分享给协作者

3.2 "看盘 / Demo" 工作流

text
[01] 跑通 mock 数据 → CZSC
   ↓
[03] 离线生成一份 HTML 看图(plotly)
   ↓
[13/15] lightweight-charts 自包含 HTML,浏览器直接打开

4. 必须知道的几件事

4.1 数据格式约定

  • K 线 DataFrame 必须包含列:dt / symbol / open / close / high / low / vol / amount
    (czsc.mock.generate_symbol_kines 返回的就是该 schema)。
  • 权重 DataFrame 必须包含列:dt / symbol / weight / price,weight ∈ [-1, 1]
    (>0 多头,<0 空头,0 空仓);用于 WeightBacktest。
  • 信号字符串 格式:"{freq}_{key2}_{key3}_{v1}_{v2}_{v3}_{score}"
    (前 3 段是 key,后 4 段是 value),例:
    "30分钟_D1_表里关系V230101_向上_任意_任意_0"。

4.2 信号函数命名

  • Rust 端注册的信号函数名是简短形式;#[signal] 宏自动注册,不再使用 V<yyMMdd> 版本后缀,
    例如:cxt_bi_status_V230101、bar_zdt_V230331、tas_ma_base_V221101。
  • 完整列表:czsc._native.signals.bar.list_signal_names()
    (bar / cxt / tas / vol / pressure / obv / cvolp 七个子模块入口都返回全集)。
  • 每个信号的 key 模板:czsc._native.signals.bar.get_signal_template(name)。

4.3 RawBar → DataFrame

  • pd.DataFrame(c.bars_raw) 在新版 RawBar 上不能正确识别字段;
    请使用 c.bars_raw_df(CZSC 自带属性)或 pd.DataFrame([b.to_dict() for b in bars])。

4.4 Position.operates 的时间字段

  • Position.operates 是 list of dict,其中 dt 是 unix 时间戳(秒),
    画图时需要 pd.to_datetime(value, unit="s") 转成 datetime。

4.5 环境变量(`czsc.envs`)

变量 默认 说明
CZSC_VERBOSE False 是否打印详细日志
CZSC_MIN_BI_LEN 6 笔最小长度
CZSC_MAX_BI_NUM 50 单个 CZSC 实例保留的最大笔数

4.6 缓存

  • 默认缓存目录:czsc.home_path
  • 查看大小:czsc.get_dir_size(czsc.home_path)
  • 清空:czsc.empty_cache_path()

5. 可视化兼容性提示

可视化统一使用 czsc.utils.plotting.*(plotly + HTML)或 czsc.utils.plotting.lightweight.*(lightweight-charts)。


6. 进一步阅读


Public Api (`docs/public_api.md`)

CZSC 公开 API 参考手册

本文档列出 czsc 包通过 __all__ 暴露给用户的所有公开 API,按功能分类。
每个 API 标注:用途说明、真正的功能实现位置、依赖的内部函数/模块。

生成日期:2026-05-24 | 基于 czsc/__init__.py 中 __all__ 列表


目录

  1. 缠论核心类型(Rust 实现)
  2. 缠论核心算法函数(Rust 实现)
  3. 交易器与信号管理
  4. 策略门面
  5. 研究 / 回测 / 优化入口
  6. 数据格式转换
  7. 技术指标(TA)算子
  8. 探索性数据分析(EDA)
  9. 回测与绩效(来自 wbt)
  10. 缓存与数据管理
  11. IO 工具
  12. 交易工具
  13. 通用工具函数
  14. K 线质量检查
  15. 日志与告警工具
  16. 可视化
  17. 飞书集成(fsa)
  18. 数据连接器(connectors)
  19. 模拟数据(mock)
  20. 环境变量(envs)
  21. 包元信息

1. 缠论核心类型(Rust 实现)

所有核心类型由 Rust crate czsc-core 实现,通过 czsc-python(PyO3)暴露为 czsc._native.*,
顶层 czsc 命名空间做 re-export。

API 用途 实现位置 内部依赖
CZSC 缠论分析主对象,管理原始 K 线、分型、笔列表 crates/czsc-core/src/czsc.rs → czsc._native.CZSC RawBar, NewBar, FX, BI, check_fx, check_bi, remove_include
RawBar 原始 K 线数据结构 crates/czsc-core/src/bar.rs → czsc._native.RawBar Freq
NewBar 去包含关系后的 K 线数据结构 crates/czsc-core/src/bar.rs → czsc._native.NewBar RawBar
FX 分型(顶分型/底分型)数据结构 crates/czsc-core/src/fx.rs → czsc._native.FX NewBar, Mark
BI 笔数据结构,包含力度、R²、斜率等统计量 crates/czsc-core/src/bi.rs → czsc._native.BI FX, NewBar, Direction, FakeBI
FakeBI 笔内部分型连接得到的近似次级别笔 crates/czsc-core/src/bi.rs → czsc._native.FakeBI FX
ZS 中枢数据结构 crates/czsc-core/src/zs.rs → czsc._native.ZS BI, Direction
Freq K 线周期枚举(F1/F5/F15/F30/F60/D/W/M 等) crates/czsc-core/src/enums.rs → czsc._native.Freq 无
Mark 分型标记枚举(顶/底/无) crates/czsc-core/src/enums.rs → czsc._native.Mark 无
Direction 方向枚举(向上/向下/无) crates/czsc-core/src/enums.rs → czsc._native.Direction 无
Operate 操作枚举(开多/开空/平多/平空等) crates/czsc-core/src/enums.rs → czsc._native.Operate 无
Signal 信号数据结构 crates/czsc-core/src/signal.rs → czsc._native.Signal 无
Event 事件数据结构(信号的逻辑组合:AND/OR/NOT) crates/czsc-trader/src/event.rs → czsc._native.Event Signal, Operate
Position 持仓/仓位数据结构(开仓事件+平仓事件+风控) crates/czsc-trader/src/position.rs → czsc._native.Position Event, Operate
BarGenerator 多周期 K 线合成器(基础周期→多个大级别) crates/czsc-trader/src/bar_generator.rs → czsc._native.BarGenerator RawBar, Freq, freq_end_time, is_trading_time
ParsedSignalDoc 信号文档解析结果 crates/czsc-signals/ → czsc._native.ParsedSignalDoc 无

2. 缠论核心算法函数(Rust 实现)

API 用途 实现位置 内部依赖
check_fx 检查三根无包含 K 线是否构成分型 crates/czsc-core/src/analyze.rs → czsc._native.check_fx NewBar, FX, Mark
check_fxs 批量检查分型序列 crates/czsc-core/src/analyze.rs → czsc._native.check_fxs check_fx, FX
check_bi 检查两个分型之间是否构成笔 crates/czsc-core/src/analyze.rs → czsc._native.check_bi FX, BI, NewBar
remove_include 去除 K 线包含关系 crates/czsc-core/src/analyze.rs → czsc._native.remove_include RawBar, NewBar
freq_end_time 计算指定周期的 K 线结束时间 crates/czsc-core/src/utils.rs → czsc._native.freq_end_time Freq
is_trading_time 判断给定时间是否为交易时间 crates/czsc-core/src/utils.rs → czsc._native.is_trading_time Freq
parse_signal_doc 解析信号函数的文档字符串 crates/czsc-signals/ → czsc._native.parse_signal_doc ParsedSignalDoc

3. 交易器与信号管理

统一入口:czsc.traders(facade 模块),实际实现在 czsc._native(Rust)。

API 用途 实现位置 内部依赖
CzscSignals 多周期联立信号分析器(不含仓位管理) crates/czsc-trader/src/signals.rs → czsc._native.CzscSignals BarGenerator, CZSC, 信号函数注册表
CzscTrader 多周期联立交易器(含仓位管理+信号) crates/czsc-trader/src/trader.rs → czsc._native.CzscTrader CzscSignals, Position, Event
generate_czsc_signals 在 K 线序列上批量生成信号,返回 DataFrame crates/czsc-trader/ → czsc._native.generate_czsc_signals BarGenerator, CzscTrader, 信号函数注册表
get_signals_config 将信号序列解析为信号配置列表(运行时格式) crates/czsc-trader/ → czsc._native.get_signals_config derive_signals_config
get_signals_freqs 从信号配置中提取涉及的周期列表 crates/czsc-trader/ → czsc._native.get_signals_freqs derive_signals_freqs
derive_signals_config 从信号 key 列表派生信号配置 crates/czsc-trader/ → czsc._native.derive_signals_config 信号函数注册表
derive_signals_freqs 从信号配置列表派生周期列表 crates/czsc-trader/ → czsc._native.derive_signals_freqs 无
get_unique_signals 从 Position 列表中提取去重的唯一信号 key crates/czsc-trader/ → czsc._native.get_unique_signals Position

4. 策略门面

API 用途 实现位置 内部依赖
CzscStrategyBase 策略开发抽象基类;子类实现 positions 属性即可获得回测/回放/序列化能力 czsc/strategies.py:56 czsc._native.{derive_signals_config, derive_signals_freqs, strategy_unique_signals, strategy_save_position, strategy_load_position}, czsc._runtime_adapters.{bars_to_dataframe, sort_freqs}, czsc.research.{run_research, run_replay}
CzscJsonStrategy 从 JSON 文件加载持仓定义的策略包装器(继承 CzscStrategyBase) czsc/strategies.py:284 CzscStrategyBase.load_positions

CzscStrategyBase 关键方法/属性

方法/属性 用途 内部依赖
positions (抽象) 子类必须实现,返回 list[Position] —
unique_signals 汇总所有 Position 的信号 key(去重) czsc._native.strategy_unique_signals
signals_config 由 unique_signals 派生的信号配置列表 czsc._native.derive_signals_config
freqs 策略涉及的所有周期(排序去重) czsc._native.derive_signals_freqs, czsc._runtime_adapters.sort_freqs
base_freq 策略基础周期(最高频) freqs
backtest(bars, **kwargs) 内存模式回测 czsc.research.run_research
replay(bars, res_path, **kwargs) 回放并落盘 czsc.research.run_replay
save_positions(path) 持仓序列化为 JSON(带 sha256 校验) czsc._native.strategy_save_position
load_positions(files, check) 从 JSON 加载 Position 列表 czsc._native.strategy_load_position

5. 研究 / 回测 / 优化入口

统一入口:czsc.research(Python 薄封装)→ Rust 后端。

API 用途 实现位置 内部依赖
run_research 内存模式策略研究,返回 Arrow 格式结果 czsc/research.py:102 czsc._native.run_research, czsc._utils._df_convert.pandas_to_arrow_bytes, czsc.models.ResearchResult
run_replay 单标的回放,可选落盘 parquet czsc/research.py:150 czsc._native.run_replay, czsc.models.ReplayResult
run_optimize_batch 批量参数优化任务 czsc/research.py:192 czsc._native.run_optimize_batch, czsc._runtime_adapters.normalize_candidate_events, czsc.models.OptimizeResult
build_open_optim_positions 构造开仓优化候选仓位(不执行回测) czsc/research.py:242 czsc._native.build_open_optim_positions
build_exit_optim_positions 构造平仓优化候选仓位(不执行回测) czsc/research.py:264 czsc._native.build_exit_optim_positions, czsc._runtime_adapters.normalize_candidate_events

优化框架辅助类(`czsc.utils.optimize`)

通过 czsc.utils 暴露,非顶层 __all__,但属于常用公开 API:

API 用途 实现位置 内部依赖
OpensOptimize 开仓参数批量优化外观类 czsc/utils/optimize.py:281 run_optimize_batch, CzscOpenOptimStrategy, czsc._runtime_adapters.bars_to_dataframe
ExitsOptimize 平仓参数批量优化外观类 czsc/utils/optimize.py:422 run_optimize_batch, CzscExitOptimStrategy, czsc._runtime_adapters.normalize_candidate_events
CzscOpenOptimStrategy 开仓优化策略(按候选信号展开变体仓位) czsc/utils/optimize.py:150 CzscStrategyBase, Position
CzscExitOptimStrategy 平仓优化策略(按候选事件展开变体仓位) czsc/utils/optimize.py:204 CzscStrategyBase, Position, czsc._runtime_adapters.normalize_candidate_event

6. 数据格式转换

API 用途 实现位置 内部依赖
format_standard_kline 将标准 K 线 DataFrame 转换为 list[RawBar] czsc/_format_standard_kline.py:57 czsc._native.{RawBar, Freq}
resample_bars 将基础周期 K 线(DataFrame 或 list[RawBar])重采样到目标周期 czsc/_resample_bars.py:61 czsc._native.{resample_bars, RawBar, Freq}

7. 技术指标(TA)算子

Rust 实现,位于 crates/czsc-ta/,通过 czsc._native.ta.* 暴露,顶层有别名。

API 用途 实现位置 内部依赖
ema 指数移动平均 crates/czsc-ta/ → czsc._native.ema 无
sma 简单移动平均 crates/czsc-ta/ → czsc._native.sma 无
rolling_rank 滚动排名 crates/czsc-ta/ → czsc._native.rolling_rank 无
boll_positions 布林带位置指标 crates/czsc-ta/ → czsc._native.boll_positions 无
ultimate_smoother 终极平滑器 crates/czsc-ta/ → czsc._native.ultimate_smoother 无

8. 探索性数据分析(EDA)

API 用途 实现位置 内部依赖
monotonicity 单调性分析(等价于 scipy.stats.spearmanr) crates/czsc-core/ → czsc._native.monotonicity 无(Rust 实现)
mark_cta_periods 【后验】标记 CTA 最容易/最难赚钱的时间段 czsc/utils/mark_cta_periods.py:22 czsc.{CZSC, Freq, format_standard_kline}
mark_volatility 【后验】标记时序/截面波动率最大/最小的时间段 czsc/utils/mark_volatility.py:14 无(纯 pandas/numpy)

9. 回测与绩效(来自 wbt)

这三个 API 来自硬依赖 wbt 包,在 czsc 顶层直接 re-export。

API 用途 实现位置 内部依赖
WeightBacktest 基于权重序列的回测引擎 wbt.WeightBacktest wbt 内部
daily_performance 日线绩效指标计算 wbt.daily_performance wbt 内部
top_drawdowns 最大回撤区间识别 wbt.top_drawdowns wbt 内部

10. 缓存与数据管理

定义于 czsc/utils/data/,通过 czsc.utils → czsc 顶层暴露。

API 用途 实现位置 内部依赖
home_path 缓存根目录(~/.czsc,可通过 CZSC_HOME 环境变量覆盖) czsc/utils/data/cache.py:25 无
get_dir_size 获取目录大小(单位:Bytes) czsc/utils/data/cache.py:29 无
empty_cache_path 清空缓存目录 czsc/utils/data/cache.py:41 home_path
DiskCache 磁盘缓存类(支持 pkl/json/csv/parquet 等多种格式) czsc/utils/data/cache.py:47 home_path
disk_cache 磁盘缓存装饰器 czsc/utils/data/cache.py:175 DiskCache
clear_cache 清空指定缓存文件夹 czsc/utils/data/cache.py:230 无
clear_expired_cache 清除过期缓存文件 czsc/utils/data/cache.py:214 无
DataClient 统一数据接口客户端(兼容 Tushare,支持本地缓存) czsc/utils/data/client.py:48 get_url_token, get_dir_size
set_url_token 设置数据接口凭证码 czsc/utils/data/client.py:13 无
get_url_token 获取数据接口凭证码 czsc/utils/data/client.py:29 无

11. IO 工具

定义于 czsc/utils/io.py,通过 czsc.utils → czsc 顶层暴露。

API 用途 实现位置 内部依赖
dill_dump 使用 dill 序列化对象到文件 czsc/utils/io.py:7 dill
dill_load 从文件反序列化 dill 对象 czsc/utils/io.py:13 dill
save_json 将 dict 保存为 JSON 文件(UTF-8) czsc/utils/io.py:30 无
read_json 从 JSON 文件读取 dict czsc/utils/io.py:35 无

12. 交易工具

定义于 czsc/utils/trade.py,通过 czsc.utils → czsc 顶层暴露。

API 用途 实现位置 内部依赖
risk_free_returns 创建无风险收益率序列 czsc/utils/trade.py:17 _get_trade_dates(内部函数)
update_nxb 计算并添加后 N 根 bar 的累计收益列 czsc/utils/trade.py:33 无(纯 pandas)
update_bbars 计算并添加前 N 根 bar 的累计收益列 czsc/utils/trade.py:67 无(纯 pandas)
update_tbars 计算带 Event 方向信息的未来收益 czsc/utils/trade.py:89 无(纯 pandas)
resample_to_daily 将非日线数据转换为日线数据 czsc/utils/trade.py:110 _get_trade_dates(内部函数)
adjust_holding_weights 按固定持仓周期调整权重 czsc/utils/trade.py:161 无(纯 pandas)

13. 通用工具函数

API 用途 实现位置 内部依赖
import_by_name 通过字符串导入模块/类/函数 czsc/utils/__init__.py:175 无
freqs_sorted K 线周期列表排序并去重 czsc/utils/__init__.py:198 czsc._runtime_adapters.sort_freqs
print_df_sample 以 reST 表格形式打印 DataFrame 前 N 行 czsc/utils/__init__.py:211 tabulate
to_arrow 将 DataFrame 转换为 Arrow IPC 字节串 czsc/utils/__init__.py:222 pyarrow
get_py_namespace 获取 Python 脚本文件中的 namespace czsc/utils/__init__.py:128 无
code_namespace 获取 Python 代码字符串中的 namespace czsc/utils/__init__.py:156 无
cross_sectional_ic 计算截面相关性(IC / ICIR) czsc/utils/analysis/corr.py:20 tqdm, pandas
index_composition 按收益率加权合成指数 K 线 czsc/utils/index_composition.py:11 无(纯 pandas)

14. K 线质量检查

API 用途 实现位置 内部依赖
check_kline_quality 综合检查多 symbol K 线数据质量(缺失值/类型/顺序/价格/成交量等) czsc/utils/kline_quality.py:267 check_missing_values, check_data_types, check_datetime_order, check_price_reasonableness, check_volume_amount, check_symbol_consistency, check_duplicate_records, check_extreme_values(均为同文件内部函数)

15. 日志与告警工具

API 用途 实现位置 内部依赖
log_strategy_info 打印策略数据的详细信息(品种、时间范围、权重统计) czsc/utils/log.py:5 loguru
capture_warnings 上下文管理器,捕获 warning 信息 czsc/utils/warning_capture.py:14 无
execute_with_warning_capture 执行函数并捕获 warning 信息 czsc/utils/warning_capture.py:66 capture_warnings

16. 可视化

lightweight-charts HTML 输出

定义于 czsc/utils/plotting/lightweight/,按需导入。

API 用途 实现位置 内部依赖
plot_czsc 单周期 CZSC 对象 → lightweight-charts HTML(三 sub-pane:K 线+成交量+MACD) czsc/utils/plotting/lightweight/__init__.py:45 czsc._native.CZSC, _data.build_from_czsc, _html_renderer.render, _theme
plot_czsc_trader 多周期 CzscTrader/CzscSignals → 多 pane HTML czsc/utils/plotting/lightweight/__init__.py:87 _data.build_from_trader, _html_renderer.render, _theme
plot_czsc_signals K 线+信号函数历史触发点叠加 → HTML czsc/utils/plotting/lightweight/__init__.py:127 generate_czsc_signals, get_signals_freqs, BarGenerator, CzscTrader, _signals.build_signal_overlays

使用方式:

python
from czsc.utils.plotting.lightweight import plot_czsc, plot_czsc_trader, plot_czsc_signals

17. 飞书集成(fsa)

定义于 czsc/fsa/,通过 czsc.fsa 子包暴露。

API 用途 实现位置 内部依赖
FeishuApiBase 飞书 API 基类(封装鉴权) czsc/fsa/base.py requests
request 飞书 API 通用请求函数 czsc/fsa/base.py requests
SpreadSheets 飞书电子表格操作类 czsc/fsa/spreed_sheets.py FeishuApiBase
SingleSheet 单个工作表操作类 czsc/fsa/spreed_sheets.py FeishuApiBase
IM 飞书即时消息操作类 czsc/fsa/im.py FeishuApiBase
BiTable 飞书多维表格操作类 czsc/fsa/bi_table.py FeishuApiBase
push_text 推送文本消息到飞书群聊 czsc/fsa/__init__.py:40 requests
push_card 推送卡片消息到飞书群聊 czsc/fsa/__init__.py:61 requests
push_message 使用飞书 APP 批量推送消息 czsc/fsa/__init__.py:131 IM
read_feishu_sheet 读取飞书电子表格 czsc/fsa/__init__.py:95 SpreadSheets
update_spreadsheet 更新飞书电子表格 czsc/fsa/__init__.py:168 SpreadSheets
get_feishu_members_by_mobiles 根据手机号获取飞书用户 ID czsc/fsa/__init__.py:115 IM
push_strategy_latest 推送最新策略结果到飞书 czsc/fsa/push_strategy_latest.py IM, SpreadSheets

18. 数据连接器(connectors)

定义于 czsc/connectors/,通过 czsc.connectors 子包暴露。

模块 用途 文件位置
tq_connector 天勤量化数据源连接器 czsc/connectors/tq_connector.py
ts_connector Tushare 数据源连接器 czsc/connectors/ts_connector.py
ccxt_connector 数字货币(CCXT)数据源连接器 czsc/connectors/ccxt_connector.py
local_data 投研数据本地缓存读取入口 czsc/connectors/local_data.py

使用方式:

python
from czsc.connectors import ts_connector
# 或
from czsc.connectors.ts_connector import get_raw_bars

19. 模拟数据(mock)

定义于 czsc/mock.py,转发到 wbt.mock。

API 用途 实现位置 内部依赖
generate_symbol_kines 生成单标的、单周期随机 K 线 DataFrame czsc/mock.py:33 → 转发 wbt.mock.mock_symbol_kline wbt.mock
generate_klines_with_weights 生成带权重列的多标的 K 线(用于 WeightBacktest 打样) czsc/mock.py:61 → 转发 wbt.mock.mock_weights wbt.mock

使用方式:

python
from czsc.mock import generate_symbol_kines
df = generate_symbol_kines("000001", "30分钟", "20240101", "20240601")

20. 环境变量(envs)

定义于 czsc/envs.py,通过 czsc.envs 子模块暴露。

API 用途 实现位置 内部依赖
get_verbose 获取是否启用详细日志(CZSC_VERBOSE) czsc/envs.py:35 无
get_min_bi_len 获取笔最小长度(CZSC_MIN_BI_LEN,默认 6) czsc/envs.py:40 无
get_max_bi_num 获取最大笔数量(CZSC_MAX_BI_NUM,默认 50) czsc/envs.py:46 无

21. 包元信息

API 用途 实现位置
__version__ 包版本号(唯一来源:Cargo.toml [workspace.package].version) czsc/__init__.py:140
__author__ 作者 czsc/__init__.py:144
__email__ 邮箱 czsc/__init__.py:145
__date__ 发布日期 czsc/__init__.py:146
welcome 打印版本号、随机格言、缓存提示 czsc/__init__.py:258

附录 A:预加载子包一览

通过 czsc/__init__.py 顶层 import 的子包,用户可直接 czsc.xxx 访问:

子包 职责
czsc.connectors 数据源连接器(天勤/Tushare/CCXT/本地缓存)
czsc.envs 环境变量读取(verbose/min_bi_len/max_bi_num)
czsc.traders 交易器与信号管理 facade
czsc.utils 通用工具(缓存/IO/分析/交易/绘图/优化)
czsc.fsa 飞书 API 集成
czsc.aphorism 缠中说禅格言(czsc.aphorism.print_one())
czsc.mock 模拟数据生成

附录 B:信号函数(`czsc._native.signals`)

信号函数由 Rust 实现,通过 PyO3 暴露为 czsc._native.signals.*。Python 端有 7 个子模块:

子模块 说明
czsc._native.signals.bar 基于 K 线形态的信号
czsc._native.signals.cvolp 量价关系信号
czsc._native.signals.cxt 基于缠论结构(分型/笔/中枢)的信号
czsc._native.signals.obv OBV 指标信号
czsc._native.signals.pressure 压力/支撑信号
czsc._native.signals.tas 技术分析指标信号(MACD/KDJ/RSI/BOLL 等)
czsc._native.signals.vol 成交量信号

底层 Rust 源文件位于 crates/czsc-signals/src/(22 个 .rs 文件),信号通过 #[signal] 宏自动注册到 SIGNAL_REGISTRY。

使用方式:信号函数一般不直接调用,而是通过 signals_config 配置交给 generate_czsc_signals / CzscTrader / CzscSignals 使用。查询可用信号请参考 signal-functions skill 或执行 ls crates/czsc-signals/src/。


附录 C:数据模型(`czsc.models`)

定义于 czsc/models.py,用于策略研究流程的返回值容器。

类型 用途 实现位置
StrategyConfig 策略配置 TypedDict(类型标注用) czsc/models.py:22
ResearchResult 研究/回测结果容器(含 Arrow 字节流,提供 signals_df() / pairs_df() / holds_df() 方法) czsc/models.py:52
ReplayResult 单标的回放结果容器(结构同 ResearchResult) czsc/models.py:96
OptimizeResult 参数优化结果容器(含 message 字段) czsc/models.py:109

Release Checklist (`docs/release_checklist.md`)

版本发布前的检查清单

适用范围:czsc(Python wheel via PyPI + Rust crates via crates.io,共用 git tag v<X.Y.Z>)。
目的:在 push tag 之前堵住 CI 覆盖不到的质量风险与隐藏缺陷。
CI 已强制项(版本一致性、stub 漂移、ruff/cargo fmt、clippy -D warnings、py3.10–3.13 pytest)不在本清单重复。


1. 代码冻结前(提交期)

  • 所有面向用户的 PR 已合并、关闭或显式延期;master 当前 commit 即是要发布的内容
  • CHANGELOG 已更新:列出本次 release 的 breaking changes、新增 API、修复项;Rust 端如有签名变更必须显式标出
  • 公开 API 改名/删除已有迁移说明:放在 docs/migration/ 或 CHANGELOG,避免下游升级炸裂

代码 vs Markdown 文档一致性(硬性检查)

**原则:以代码为准。**任何不一致都改文档,不要反向去改代码迁就文档措辞。

检查范围与方法:

  • README.md:安装命令、最小示例、API 调用片段、特性列表都能跑通;版本徽章/截图同步
  • CLAUDE.md:模块路径、目录结构描述与环境变量名均与当前代码一致
  • docs/examples/*.py:每个示例真实可运行(至少 python -c import 不报错);删除的示例同步从 docs/examples.md 索引里删掉
  • docs/migration/:历史迁移说明里引用的旧 API/路径,与当前实际"已删除/已改名"的事实对齐
  • 公开 docstring:czsc/ 顶层导出对象、crates/*/src/lib.rs 公开 item 的 docstring 中的代码示例可执行
  • API 文档(飞书 / readthedocs)链接的有效性:本次若有大规模重构,外链文档需手动确认未误导

快速定位漂移点的命令:

bash
# 1) 找 md 里引用的 czsc.xxx 符号,对照实际是否存在
rg -n 'czsc\.[a-zA-Z_][a-zA-Z0-9_.]*' README.md CLAUDE.md docs/ \
   | awk -F'czsc\\.' '{print $2}' | awk '{print $1}' | sort -u \
   > /tmp/md_refs.txt
uv run python -c "
import czsc, sys
refs = [l.strip().rstrip('.,)`\"') for l in open('/tmp/md_refs.txt') if l.strip()]
for r in refs:
    root = r.split('.')[0].rstrip('()')
    if root and not hasattr(czsc, root):
        print('MISSING in czsc:', r)
"

# 2) 确认面向用户文档没有引用已删除的模块路径
rg -n 'czsc\.svc|czsc\.signals\.|czsc\.core\b' README.md docs/ \
   && echo "::error:: 上述命中均为已删除模块路径,需从文档中清理"

# 3) 校验案例索引与 docs/examples/ 实际文件一致
python - <<'PY'
from pathlib import Path
import re
files = {p.name for p in Path("docs/examples").glob("*.py")}
listed = set(re.findall(r"\]\(\./examples/([^)]*\.py)\)", Path("docs/examples.md").read_text()))
for name in sorted(files ^ listed):
    print("MISSING example index entry:", name)
PY
  • 以上三段命令均无输出(或所有命中都已修正)
  • docs/examples/ 与 docs/examples.md 索引一致:文件删除/新增都同步索引

2. 版本号(单一版本源 = `Cargo.toml [workspace.package].version`)

  • Cargo.toml [workspace.package].version 已 bump 到目标版本
  • 未硬编码 pyproject.toml 的 version,仍为 dynamic = ["version"](硬编码会被 crates/czsc-python/build.rs 编译期拒绝)
  • 计划打的 git tag 是 v<Cargo.toml version>(CI 会做三方校验:tag == Cargo == wheel filename)

语义化版本(SemVer)规则

本次发布的明确例外(2026-10-15):维护者指定 1.0.2,并保留 #368 的 34 项破坏性信号键迁移,
不提供旧键别名或双协议。CHANGELOG 与 docs/signal_contracts.md 必须保留迁移警告,不能将本次
称为兼容升级;该例外不修改今后的一般版本规则,也不豁免任何功能、安全、产物或上传授权门禁。

格式:MAJOR.MINOR.PATCH(如 1.2.3),三段含义对应"兼容性合同":

段位 何时 +1 典型场景 同时清零
MAJOR 破坏向后兼容 删除/重命名公开 API、改函数签名、改字段语义、调整信号默认参数、Rust 类型签名变更 MINOR、PATCH → 0
MINOR 向后兼容地新增 新信号函数、新公开 API、新可选参数、新示例 PATCH → 0
PATCH 向后兼容地修 bug 算法修正、性能优化、文档/类型提示修正、内部重构(不影响外部行为) —

判定决策树:

  1. 用户升级后会报错或行为变化吗?→ MAJOR
  2. 用户能用上新东西且老代码不动也跑得通吗?→ MINOR
  3. 只是修了已有功能的 bug,外部接口与行为不变?→ PATCH

补充约束:

  • 任意 breaking change(即使再小)必须 bump MAJOR,且在 CHANGELOG 顶部用 ### Breaking Changes 单独成段
  • 不允许"小版本里夹 breaking"——这是下游升级最容易翻车的反模式
  • 预发布用 1.2.0-rc.1 / 1.2.0-beta.1(PyPI 与 crates.io 都支持),tag 同步打 v1.2.0-rc.1
  • 0.x.y 阶段(如果未来有):MINOR 段位的语义可视作 MAJOR——即 0.3.0 → 0.4.0 允许带 breaking;本项目当前已稳定在 1.x,不适用
  • Rust crates 与 Python wheel 共享同一版本号(已由 CI 强校验),不允许某一侧单独 bump

3. 本地 Rust 验证(CI 覆盖盲点)

CI 因 pyo3/extension-module 限制,只对 5 个纯 Rust crate 跑 cargo test。czsc-signals / czsc-trader / czsc-python 的 Rust 测试只能本地跑:

  • cargo build --workspace --release 通过
  • cargo test -p czsc-derive -p czsc-core -p czsc-utils -p czsc-ta -p czsc-signal-macros 通过(与 CI 同步)
  • cargo clippy --workspace --all-targets -- -D warnings 通过
  • cargo fmt --all -- --check 通过
  • stub 已重新生成并 commit:
    bash
    PYO3_PYTHON=$(uv run python -c 'import sys; print(sys.executable)') \
      cargo run --bin stub_gen -p czsc-python --no-default-features --features stub-gen
    git diff --exit-code czsc/_native/__init__.pyi   # 必须无差异

4. 本地 Python 验证

  • uv sync --extra all(仅在依赖变更后跑一次)+ uv run maturin develop --release 构建本地扩展
  • 全量测试含 slow 用例(CI 默认跳过 @pytest.mark.slow,发布前必须显式跑):
    bash
    uv run --no-sync pytest --run-slow
  • uv run --no-sync pytest --cov=czsc 覆盖率无显著回退
  • uv run --no-sync ruff format --check czsc/ tests/ & uv run --no-sync ruff check czsc/ tests/ 通过
  • 冒烟 import 顺序无 side-effect:
    bash
    uv run python -c "import czsc; print(czsc.__version__)"
    uv run python -c "from czsc import CZSC, RawBar, Freq, BarGenerator, CzscTrader; print('core OK')"
    uv run python -c "from czsc._native import signals; print(len(dir(signals)))"
    uv run python -c "from czsc._native import ta; print(sorted(dir(ta))[:5])"
  • czsc.__version__ 输出 == 目标版本(验证 importlib.metadata 注入正确)

5. 本地 Wheel 验证(覆盖 CI smoke 盲区)

CI smoke 仅覆盖 Linux x86_64 / macOS x86_64 / macOS arm64 / Windows x64。Linux aarch64 与 musllinux 必须本地验证:

  • uv run maturin build --release --strip 产物可被 pip install --no-deps 安装并 import czsc 成功
  • 如本次有触及 PyO3 边界(参数 / 返回值序列化、Arrow IPC、GIL release):用 docker --platform linux/arm64 或 alpine 镜像本地拉一遍 wheel 安装 + import
  • Wheel 文件名版本符合 czsc-<X.Y.Z>-cp310-abi3-.whl(abi3 单 wheel 覆盖 3.10–3.13)

6. 发布动作(按顺序)

任何一步失败都先排查再前进,不要靠重发覆盖。tag 一旦发布到 PyPI 不可重用。

  1. TestPyPI 演练(强烈推荐,尤其是 breaking 版本):
    • Actions → Build & Publish Python Package → workflow_dispatch → publish_to_testpypi=true
    • 在干净虚拟环境 pip install -i https://test.pypi.org/simple/ czsc==<X.Y.Z> 实测
  2. Rust crates dry-run(push tag 时自动跑,可提前 workflow_dispatch 验证):
    • Actions → Publish Rust crates → 默认 dry-run;查看日志确认 cargo publish --workspace --dry-run 通过
  3. Push tag 触发 PyPI 真发:
    bash
    git tag v<X.Y.Z>
    git push origin v<X.Y.Z>
    • CI 自动:build wheels → sdist → smoke-test → 版本三方校验 → PyPI 发布 → GitHub Release + sigstore 签名
  4. Rust crates 真发:tag push 不会自动真发,需手动:
    • Actions → Publish Rust crates → workflow_dispatch → do_publish=true
    • 若中途撞 crates.io rate-limit(新 crate ~1/10min),用 start_layer / end_layer 断点续发
  5. PyPI 与 crates.io 任一发布失败 → 不要急着 yank,先在 Issue 记录、修复后 bump 一个 patch 版本重发(已上传不可覆盖)

7. 发布后验证

  • PyPI 页面可见目标版本,wheel 平台齐全(6 平台 + sdist):https://pypi.org/project/czsc/
  • crates.io 页面可见目标版本,全部 6 个 crate 都 OK(czsc-derive / czsc-signal-macros / czsc-core / czsc-ta / czsc-utils / czsc-signals / czsc-trader / czsc)
  • GitHub Release 已创建,包含 wheel + sdist + sigstore 签名文件
  • 干净环境冒烟:
    bash
    pip install czsc==<X.Y.Z>
    python -c "import czsc; print(czsc.__version__)"
  • cargo add czsc@=<X.Y.Z> 在干净 Rust 项目里能 cargo check 真编通过(注意:仅"可解析"不够 —— 1.0.0-rc.8 发版时踩过坑,下游 cargo add 后解析成功但 cargo check 失败。完整命令:
    bash
    TMPD=$(mktemp -d); cd "$TMPD"
    cargo init --name release_smoke --quiet
    cargo add 'czsc@=<X.Y.Z>' --quiet
    cargo check 2>&1 | tail -20   # 必须 0 错误
    cargo tree -i polars-core 2>&1 | head -10   # 必须单一版本,无 0.42/0.52 双版本冲突
    cd - && rm -rf "$TMPD"
    常见失败:(a) SemVer prerelease 解析:crates.io 同时有 1.0.0 stable 与 1.0.0-rc.* 时,cargo 会优先选 stable,造成依赖图错位 —— 必须用 = 严格匹配 + cargo yank 误发 stable;(b) pyo3 / pyo3-stub-gen / numpy 被无条件依赖 —— 纯 Rust 用户也被强制拉 Python 工具链,撞上游兼容性 bug,必须 feature-gate。详见 §8 雷达表)
  • 至少跑一个最小 CZSC + 信号配置的 demo(如 docs/examples/13_lightweight_charts_html.py)确认运行时无回归

8. 隐藏缺陷雷达(按历史踩坑频度排序)

风险 触发条件 防御
Stub 漂移 改了 #[gen_stub_*] 但忘了重跑 stub_gen §3 最后一项 + CI stub-drift job
版本三方不一致 只 bump 了 Cargo 没 push tag、或反之 §2 + CI verify_version_consistency
pyproject.toml 误改 version 手抖去掉 dynamic build.rs 编译期校验
Slow 测试漏跑 平时 CI 不跑 --run-slow §4 强制本地跑
czsc-signals / czsc-trader Rust 端回归 CI 不能 cargo test 这两个 crate §3 + Python pytest 端到端覆盖
ARM/musllinux wheel 加载失败 CI smoke 不覆盖 §5 本地 docker 验证
Rust crates 漏发 tag push 只触发 dry-run,真发要手动 dispatch §6 step 4 强提醒
crates.io rate-limit 新 crate 创建 ~1/10min start_layer/end_layer 断点续发
PyPI 已存在版本无法覆盖 发布失败后误以为可重发 §6 step 5:bump patch 重发,不要 yank
下游升级炸裂 breaking change 没写迁移说明 §1 CHANGELOG + docs/migration/
SemVer prerelease 被 stable 抢解析 crates.io 同时有 1.0.0 与 1.0.0-rc.*,下游 cargo add czsc 会解析到 stable 而非 rc,导致依赖图错位(rc.8 实战遇到) 必须 workspace.dependencies 全用 version = "=<X.Y.Z>" 严格匹配;误发的 stable 必须 cargo yank;§7 cargo check ratchet 抓得到
pyo3 / pyo3-stub-gen / numpy 无条件硬依赖 纯 Rust 下游被强制拉 Python 工具链,撞上游兼容性 bug 编不出(rc.8 实战遇到) czsc-core / czsc-utils / czsc-signals / czsc-trader 把 pyo3 系列 dep 改 optional = true + [features] python = [...];§7 cargo check ratchet 抓得到

9. 一键自检脚本(可选)

把 §3 + §4 + §5 的本地命令串成一个脚本,在 push tag 前跑一遍:

bash
set -e
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo build --workspace --release
cargo test -p czsc-derive -p czsc-core -p czsc-utils -p czsc-ta -p czsc-signal-macros
PYO3_PYTHON=$(uv run python -c 'import sys; print(sys.executable)') \
  cargo run --bin stub_gen -p czsc-python --no-default-features --features stub-gen
git diff --exit-code czsc/_native/__init__.pyi
uv run maturin develop --release
uv run --no-sync ruff format --check czsc/ tests/
uv run --no-sync ruff check czsc/ tests/
uv run --no-sync pytest --run-slow
uv run python -c "import czsc; print('version:', czsc.__version__)"

# §7 cargo check ratchet(rc.8 教训)—— 模拟下游纯 Rust 用户拉发布产物
# 注意:本地 cargo check --workspace 会因 path = "..." 短路,看不到
# crates.io 上的版本解析问题;必须在干净项目里 cargo add 才能复现。
# 这一段建议在 PR push tag **之后**、cargo publish dispatch **之前**
# 跑一次(用 dry-run 产物或 TestPyPI/crates.io-staging 镜像)。

echo "✅ 本地预检全部通过"

全绿之后再做 §6 的发布动作。


Release Security Review (`docs/release_security_review.md`)

1.0.2 发布安全处置记录

维护者已选择 1.0.2 并保留新信号键。本次只同步项目版本和内部精确依赖,不回退下列安全修复或
变更第三方解析图。下文是此前 2.0.0 候选阶段对同一依赖图的审核记录;历史证据不是新 1.0.2
产物验证或全库安全认证,新版本仍须重新构建、安装和运行检查。

本轮针对审核 head f7e5cc6 的 30 项 open Dependabot 告警逐项处置。
来源为只读 gh api --paginate 'repos/waditu/czsc/dependabot/alerts?state=open&per_page=100';漏洞条件与修复阈值来自各项 security_advisory / security_vulnerability。
共 1 critical、13 high、13 medium、3 low。该快照不是全库安全认证;GitHub 默认分支告警不会因草稿 PR 的锁文件更新自动关闭,本任务不 dismiss 告警。

每项影响与处理

“升级锁”只表示该 PR 解析图排除了本次审核涉及的漏洞版本,不表示已复现利用、用户旧环境已修复或所有平台产物已验证。版本分支由 Python marker 决定。

告警/级别 GHSA 影响及触发条件 版本与可达路径 处理 / 残余风险编号
#93 low GHSA-6g2r-675j-hx59 短自定义 xxh3 secret 可能越界 xxhash-rust:0.8.15 → 0.8.16;native/Polars 传递依赖 升级至首个修复版本;R1
#92 high GHSA-8988-9cw3-xx77 HTTPS 代理可能忽略 TLS 配置 urllib3:2.7.0 → 2.8.0;运行时 requests,以及发布工具 升级锁并声明 urllib3>=2.8.0;R3
#91 medium GHSA-3jqq-pw4j-pqcj 恶意语言包导致前端 XSS jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#90 medium GHSA-3325-v43h-43rv 扩展卸载参数注入与文件/URL 泄漏 jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#89 high GHSA-6966-vjj6-99xv 剪贴板单元格触发 XSS jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#88 high GHSA-6966-vjj6-99xv 剪贴板单元格触发 XSS notebook:7.5.0 → 7.6.3;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#87 high GHSA-c2m8-h5v5-343r 静态文件符号链接越界读取 tornado:6.5.7 → 6.5.10;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#86 medium GHSA-3hv7-mjh2-fv65 无限查询参数阻塞事件循环 tornado:6.5.7 → 6.5.10;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#85 high GHSA-chx6-46f5-w4vp 解压响应缺少大小限制导致 OOM tornado:6.5.7 → 6.5.10;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#84 medium GHSA-gh4c-6fx4-qh6g Deflate 分块流可能无限循环 urllib3:2.7.0 → 2.8.0;运行时 requests,以及发布工具 升级锁并声明 urllib3>=2.8.0;R3
#83 high GHSA-jwv3-5hgf-82ww 重复自签中间证书导致指数路径搜索 cryptography:48.0.1 → 50.0.2;Linux release/all extra:Twine→keyring→secretstorage 升级锁;R4
#82 medium GHSA-m2h6-j472-rp4c 通配 DNS 名可逃逸证书子树约束 cryptography:48.0.1 → 50.0.2;Linux release/all extra:Twine→keyring→secretstorage 升级锁;R4
#81 medium GHSA-5p39-cfhj-2xmp 进程池 stderr 不排空导致永久阻塞 anyio:4.10.0 → 4.14.2/4.15.1;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#80 critical GHSA-82r6-8w77-94w6 非 ASCII 域名的旧 IDNA 编码可能接受攻击者证书 anyio:4.10.0 → 4.14.2/4.15.1;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#79 medium GHSA-gjv8-xp57-g29c 恶意 CSS selector 正则二次复杂度 soupsieve:2.8.4 → 2.10/3.0.3;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#78 medium GHSA-j934-xhv5-fg8f 合法 selector 尾部空白/注释触发 ReDoS soupsieve:2.8.4 → 2.10/3.0.3;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#77 high GHSA-mpf4-983q-p7j4 表单字段无限解析阻塞事件循环 tornado:6.5.7 → 6.5.10;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#76 high GHSA-6m44-fpc8-c3rq 过多 Markdown emphasis 导致递归异常/DoS mistune:3.3.0 → 3.3.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#75 medium GHSA-8423-8fgw-73vq multipart split 临时列表导致内存放大 tornado:6.5.7 → 6.5.10;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#74 low GHSA-wwv5-g3v4-889x cookie 属性注入修复可被旧 kwargs 路径绕过 tornado:6.5.7 → 6.5.10;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#73 high GHSA-g6cj-pr64-35w5 PKCS#7 解密错误/时序可形成 padding oracle cryptography:48.0.1 → 50.0.2;Linux release/all extra:Twine→keyring→secretstorage 升级锁;R4
#72 high GHSA-4w2j-m93h-cj5j 乱序流重组无界内存导致远程耗尽 quinn-proto:0.11.14 → 0.11.15;锁内,默认 normal tree 不活动 升级至首个修复版本;R2
#71 low GHSA-whvh-wf3x-g77j 直接安装扩展可绕过允许/禁止列表 jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#70 high GHSA-gx64-gj6p-pc4c 恶意图片在新标签触发 XSS jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#69 high GHSA-pppj-hq3g-57pj 恶意 settings 文件触发 XSS jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#68 medium GHSA-h5v5-8746-g7mm PluginManager 锁规则可绕过 jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#67 medium GHSA-89vp-jrxv-24w8 扩展 blocklist 包名规范化可绕过 jupyterlab:4.5.9 → 4.6.4;Jupyter 开发依赖 / dev、all extra 升级锁与必要兼容依赖;R4
#66 medium GHSA-h35f-9h28-mq5c Unicode 规范化可绕过 sdist MANIFEST 排除 setuptools:80.9.0 → 已移除;旧构建/开发传递依赖 移出锁图,maturin 构建不依赖它;R5
#52 medium GHSA-chgr-c6px-7xpp Python closure 缺 Sync 约束可能跨线程竞态 pyo3:0.28.3 → 0.29.3;native 实际依赖 升级 PyO3 及 NumPy/stub 兼容链;R1
#51 high GHSA-36hh-v3qg-5jq4 list/tuple nth/nth_back 溢出导致越界读 pyo3:0.28.3 → 0.29.3;native 实际依赖 升级 PyO3 及 NumPy/stub 兼容链;R1

残余风险和接受边界

  • R1:PyO3 位于 native 路径,更新至 0.29.3;NumPy Rust binding 0.29.0、stub generator 0.23.1 是匹配升级,编译与回归必须重新执行。项目直接调用中未找到 Python list/tuple nth/nth_back 或 PyCFunction::new_closure,不是第三方依赖不可达证明。xxhash 的短 secret 攻击未做压力/内存安全复现;以修复版本及产物重建处置,不靠未发现直接调用豁免。
  • R2:cargo tree --workspace -e normal -i quinn-proto --locked 默认图不活动;未来开启 HTTP/3/相关 feature 可能改变可达性。因此仍升级到 0.11.15,不仅记录“当前没用”。未做远程 DoS 攻击复现。
  • R3:urllib3 位于正常 requests 路径,新增 wheel 安装下限 urllib3>=2.8.0,不能只升级开发锁。依赖解析与安装检查不代表部署代理/TLS 配置正确,须部署方核查。
  • R4:AnyIO critical 与其他 Jupyter 告警已按锁升级;JupyterLab 4.6.4 是 Notebook 7.6.3 的兼容要求,不是无关升级。cryptography 在 Linux 发布工具 extra,经 Twine/keyring/secretstorage 可达,不是正常 wheel native 库。没有运行恶意 notebook、域名、证书、HTTP/Markdown 压力复现。开发/发布环境应按该锁重建;用户自选旧版 extras 不会被 wheel 强制升级,须固定安全基线并限制不可信文档、扩展和输入。
  • R5:setuptools 已从本次锁图移除;原生构建后端仍为 maturin。用户外部构建工具/系统环境中的 setuptools 不在本 PR 处置范围,尤其 macOS sdist 路径仍应核查其版本。

本 PR 没有将 CodeQL 成功当作依赖安全通过。tests/test_release_dependencies.py 为这次处置的版本阈值和 urllib3 安装下限加了防回退守卫;未来新告警仍需重新审查,不能复用本表作为永久豁免。

可执行追溯

bash
python -m pytest tests/test_release_dependencies.py -q
cargo tree --workspace --all-features -e normal -i pyo3 --locked
cargo tree --workspace -e normal -i quinn-proto --locked
cargo tree --workspace -e normal -i xxhash-rust --locked
uv tree --universal --locked --invert --package anyio
uv tree --universal --locked --invert --package cryptography
uv tree --universal --locked --invert --package urllib3

跨平台重建、平台漏洞行为验证、线上利用路径、上传凭据有效性仍不可由版本守卫证明;剩余发布门禁见 docs/release_validation.md。正式版/RC、发布范围和风险接受须维护者确认。


Release Validation (`docs/release_validation.md`)

1.0.2 发布审核补证

当前版本与验收边界(2026-10-15)

维护者明确选择 1.0.2,保留 #368 的 34 项输出键迁移及新键契约,不增加别名或双协议,
不再推进兼容改造或 2.0.0 发布。CHANGELOG 保留破坏性警告;该范围决定不是合并、tag 或上传授权。
版本源、八个内部精确约束和九个工作区锁包必须统一 1.0.2,Python 版本仍由 maturin 动态读取。
uv.lock 的项目为 dynamic/editable,没有待替换的项目版本;第三方包的 2.0.0 不是项目版本。

bash
REPO_ROOT="$PWD"
python -m pytest tests/test_release_dependencies.py .github/scripts/tests/test_release_wheel_smoke.py -q
uvx maturin build --release --locked --offline --interpreter "$PYTHON" --out target/release-1.0.2-dist
uvx maturin sdist --out target/release-1.0.2-dist
mkdir -p target/release-1.0.2-smoke-cwd
(cd target/release-1.0.2-smoke-cwd && "$PYTHON" "$REPO_ROOT/.github/scripts/smoke_release_wheel.py" \
  --expected-version 1.0.2 --expected-machine arm64 --wheel-dir "$REPO_ROOT/target/release-1.0.2-dist")

构建后必须在新 venv 完整安装实际 1.0.2 wheel,并在仓库外运行 smoke;上面的 $PYTHON 对安装和
检查命令应指向该环境,不能从源码目录导入旧 native 冒充新产物。检查版本/架构/来源/native 字节、
RawBar/CZSC、SMA、八个时间精度组合及实际新信号键/配置往返、旧键拒绝,不放宽原功能门禁。
原六平台 × Python 3.10–3.13 的 24 组合保持完整在线/no-cache 安装、pip check 和所有上传 needs;
只无上传演练 publish_to_testpypi=false,新 producer SHA/产物哈希及一次执行快照随任务评论交付,
没有结果的项目明确未验证,不轮询等待外部 CI。

历史 2.0.0 候选记录(以下至文末均非新版本通过证明)

下文保留此前源码、命令、版本、文件名、哈希及局部运行缺口的原貌,不改名复用历史产物。
其后独立复审已验收 eeeb731 / run 38615098709 的六平台与 24/24 组合,三个上传 job 均 skipped;
该结论只属于原 2.0.0 产物。未变化的算法/安全图证据可按边界保留,不能替代 1.0.2 的重建和运行。

本次验证对应 PR #369 的审核修复内容,不等于发布批准。版本源仍为 Cargo workspace;正式版/RC、范围和上传授权未确认。禁止把以下本地通过替代六平台产物、线上 registry 安装或上传权限证据。

修复与可复现回归

  • 时间精度:输入/merge 键/输出统一 datetime64[ns],覆盖 s/ms/us/ns 和交易日/自然日。没有改笔/分型算法。
  • 测试隔离:测试包导入不再写进程配置;默认值测试的环境清理通过 monkeypatch 恢复。没有调整收集顺序、刷新快照或为通过而设置笔长 6。
  • 安全处置:30 项逐项见 release_security_review.md;版本阈值与普通安装 urllib3 下限有防回退守卫。
  • 红灯证据:初始组合 11 failed / 6 passed,锁版本守卫 12 failed;环境恢复测试 1 failed、urllib3 安装下限测试 1 failed。
  • 绿灯证据:首组修复 45 passed;原样核心/环境/安全组合两种顺序各 37 passed。顺序变换只用于证明隔离,不是修复手段。
bash
PYTHON=.venv/bin/python
"$PYTHON" -m pytest tests/unit/test_core_parity.py -q
"$PYTHON" -m pytest tests/test_testing_configuration.py tests/test_release_dependencies.py \
  tests/unit/test_core_parity.py tests/test_envs.py -q
"$PYTHON" -m pytest tests/test_envs.py tests/unit/test_core_parity.py \
  tests/test_release_dependencies.py tests/test_testing_configuration.py -q
"$PYTHON" -m pytest tests/test_trade_utils.py tests/unit/test_resample_bars.py -q

分别使用 pandas 2.3.2 与 pandas 3.0.6 的 Python 3.11.15 环境,不把降级 pandas 当作修复。两套完整回归均为 546 passed / 1 deselected(分别 18.00s / 27.38s),执行命令如下;数量有交叉,不累加:

bash
"$PYTHON" -m pytest tests --run-slow --import-mode=importlib \
  -k 'not test_wheel_install_in_clean_venv' -q

唯一未执行项是在线 pip clean-install 测试;独立环境离线安装、native import、依赖检查另行取证,不把离线安装等同在线无约束 resolver 验证。pandas 2 环境检查 53 个包、pandas 3 环境检查 41 个包,均报告 All installed packages are compatible。完整新 wheel 的本地结果和 SHA256 以本轮交付日志为准。

构建、typing 和 Rust 产物

bash
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --exclude czsc-python --locked
PYO3_PYTHON="$("$PYTHON" -c 'import sys; print(sys.executable)')" \
  cargo test -p czsc-core --features python --test test_python_enum_extraction --locked
PYO3_PYTHON="$("$PYTHON" -c 'import sys; print(sys.executable)')" \
  cargo run --locked --bin stub_gen -p czsc-python --no-default-features --features stub-gen
git diff --exit-code -- czsc/_native/__init__.pyi tests/unit/snapshots/
uvx maturin build --release --locked --interpreter "$PYTHON"
uvx maturin sdist --out dist
cargo package --workspace --exclude czsc-python --allow-dirty --locked --offline
  • 已验证 Rust 273 passed / 1 ignored,额外 Python enum 提取契约 2 passed;fmt/Clippy 通过。
  • 新 stub generator 已执行,stub 与原提交字节一致。debug stub_gen 的 macOS 链接器报告 __eh_frame 超过 compact unwind 16MB 限制;没有屏蔽告警,异常展开性能未测,不能将其称为 release 运行失败或宣称性能无影响。
  • 八个可发布 crate 均实际 Packaged 和 Verifying 成功;没有使用 --no-verify,不只编译 workspace path 依赖。Cargo 在本地临时 registry 中解析本次 =2.0.0 依赖。
  • 额外纯 Rust 消费者使用 czsc = "=2.0.0",无 path dependency、无 [patch]、无 Python feature。将八个 .crate 与 cargo vendor --locked --offline --versioned-dirs 的第三方源放入隔离 source mirror,生成 archive/file SHA256 校验后,cargo run --offline 成功,czsc::Freq::F1 实际运行;normal tree 没有 pyo3/numpy。
  • 此消费者验证的是本次归档在离线镜像里的行为,不是正式 crates.io 安装;2.0.0 尚未上传,不能制造线上验证证据。

Wheel 内容与独立环境验证

bash
"$PYTHON" - <<'PY'
from pathlib import Path
from zipfile import ZipFile
from email import message_from_bytes
import hashlib
wheels = list(Path('dist').glob('czsc-*.whl'))
assert wheels, 'No release wheel found'
for wheel in wheels:
    with ZipFile(wheel) as archive:
        names = archive.namelist()
        metadata = message_from_bytes(archive.read(next(name for name in names if name.endswith('/METADATA'))))
        assert metadata['Version'] == '2.0.0'
        assert 'urllib3>=2.8.0' in metadata.get_all('Requires-Dist', [])
        payload = [name for name in names if name.startswith('czsc/') and name.endswith(('.py', '.pyi', 'py.typed'))]
        assert all(archive.read(name) == Path(name).read_bytes() for name in payload)
        assert any(name.startswith('czsc/_native') and name.endswith(('.so', '.pyd')) for name in names)
        assert 'czsc/py.typed' in names and 'czsc/_native/__init__.pyi' in names
        assert any(name.endswith('/LICENSE') for name in names)
        print(wheel.name, len(payload), hashlib.sha256(wheel.read_bytes()).hexdigest())
PY

普通安装与本地源测试是两种证据。先在全新 venv 安装完整依赖快照与新 wheel,在非仓库目录执行 native import 和 uv pip check,再执行对应环境的 pandas 回归;避免从仓库导入旧 .so 冒充新产物。

本轮重新构建的 czsc-2.0.0-cp310-abi3-macosx_11_0_arm64.whl SHA256 为 93b89636192d89a3dcb49fce6f4bdf6fa22ccddfa603f4d2fa278a8ed25d180f;97 个 Python/stub/typing payload 与修复源码逐字节一致。native 使用升级后的 PyO3/NumPy 重新编译,不复用审核前产物。wheel 在修复提交前的 worktree 构建,源码一致性已验证,但不能将其描述为 CI 的 clean-checkout 产物。

在线无约束 pip resolver 与所有 Python/平台组合尚不能由离线依赖快照证明。原 test_wheel_install_in_clean_venv 的在线安装项若未执行,必须明确列出,不得把 deselected 写成 passed。

未验证门禁(不得标绿)

项目 本次证据 / 剩余动作
macOS arm64 / Python 3.11 本机 release wheel、metadata/源码/stub/native 与独立环境验证;详见本轮日志
Linux x86_64、Linux arm64、musllinux、macOS Intel、Windows wheel 本机未验证;需收集同一 head 的六平台 workflow 产物与真实运行环境 smoke
Python 3.10/3.12/3.13 本机未重新运行;PR CI 是开发构建回归,不能替代对应 release wheel 验证
ARM/musllinux/macOS Intel import smoke 已给现有 workflow 增加原生 ARM/Intel runner 与 Alpine 运行检查;配置不等于执行通过,本机 Docker daemon 不可用,结果仍须单独收集
官方 crates.io 下游解析/安装 未发布,尚不可验证;离线镜像验证不是线上证据
PyPI Trusted Publisher / crates.io token 仅知道 repo environment / secret 名存在,未验证有效性,不读取 secret 值
TestPyPI/正式上传与发布后验收 无授权,未触发;正式版/RC 和范围须维护者确认

安全构建演练可以在同一新 head 的分支上使用现有 workflow 的 publish_to_testpypi=false;它仅构建与测试,上传 job 条件不成立,仍必须核对 run 输入/事件与结果:

bash
gh workflow run python-publish.yml --repo waditu/czsc \
  --ref agent/codex/SKZ-964-release-2.0.0 -f publish_to_testpypi=false

不要改成 true 或推 v* tag 来获取证据:前者会上传 TestPyPI,后者会自动上传正式 PyPI。Rust 真正发布需要授权后在同一 tag dispatch do_publish=true。本任务不合并、不打 tag、不上传包。

补充 release wheel 运行检查

现有 python-publish.yml 的 smoke 已扩展至六个平台 × Python 3.10/3.11/3.12/3.13(24 个组合),不是重新运行全套算法回归:

  • manylinux x86_64、aarch64 分别在 x86_64、ubuntu-24.04-arm 原生 runner 执行;macOS Intel 在 macos-15-intel,arm64 在 Apple Silicon,Windows 在 x64 runner 执行。
  • musllinux x86_64 在对应 Python 版本的 Alpine Docker 环境执行,不能用 glibc Linux 或交叉编译结果替代实际加载。
  • 每个组合创建新 venv,从公开 PyPI 在线解析完整运行时依赖,安装同一 run 的 release wheel,执行 pip check;取消原最小依赖/no-deps smoke。依赖若缺少该平台发行包或编译环境,必须失败并记录,不能跳过依赖伪装普通安装成功。
  • 在仓库之外执行 .github/scripts/smoke_release_wheel.py;校验版本、runner 架构、模块属于目标 venv、native 二进制与下载 wheel 字节一致,并实际运行 RawBar/CZSC、Rust SMA 和八种时间精度组合。
  • 两个 smoke job 仅有 contents/actions read 权限,没有上传步骤或 OIDC write。原 PyPI/TestPyPI 上传条件不变,两个上传 job 新增 musl smoke 为依赖;演练必须继续显式 publish_to_testpypi=false。
  • 新脚本与门禁回归使用独立安装环境执行:python -m pytest .github/scripts/tests/test_release_wheel_smoke.py -q。原始红灯 5 failed,修复后 5 passed。此工具回归不混入先前 546 项算法回归计数。

当前配置的静态检查、本机在线安装与实际 CI wheel 执行日志随任务评论交付。远程 24 组合是否通过只能由新 head 的 job 执行结果证明;dispatch 成功或 queued 不算运行通过。未完成时只记录快照,不轮询等待外部 CI,也不申请发布批准。

Windows 编码与 musl 安装环境补证(2026-10-15)

已完成演练的实际结果

run 38607249078 对应
bba2c0802931db51c43e523c19d2ee33f6ab4a7c,已结束且失败,不是仍在等待。
六个平台 wheel 和 sdist 构建成功;24 个运行组合为 16 success / 8 failure。
manylinux x86_64/aarch64、macOS Intel/arm64 的 Python 3.10–3.13 全部完成完整在线无缓存安装、
pip check 和 wheel/native/功能检查。Windows 四组合在 Cargo 版本读取阶段失败;
musl 四组合在 wbt 源包的构建准备阶段失败,均尚未执行 native smoke。
PyPI、TestPyPI、GitHub Release 三个 job 均 skipped。该远程证据更新前文的本地未验证记录,
但不把历史产物或旧 SHA 的结果改写成修复 head 的结果。

最小修复与回归

  • 两个 smoke 版本命令显式使用 UTF-8 读取 Cargo.toml;不设置全局 UTF-8/locale 环境绕开缺陷。
    参数化回归直接执行 workflow 中的两个真实 Python 命令,在默认编码 cp1252 下先复现
    UnicodeDecodeError(2 failed),然后验证显式 UTF-8(2 passed)。
  • musl 容器在创建新 venv 前安装 build-base libgcc libstdc++ rustup pkgconf openssl-dev,
    用 rustup-init -y --profile minimal --default-toolchain stable 显式准备 musl Rust 工具链,
    把 $HOME/.cargo/bin 加入 PATH,并打印 Alpine、APK、rustc 和 Cargo 版本。
    不再依赖 Python 构建后端隐式安装 Rust;仅补 libgcc 不足以构建 wbt。
  • 以 CARGO_BUILD_JOBS=2 限制源码编译并行度,musl job 预算由 20 分钟改为 60 分钟,
    为 Polars 及 wbt 的 release 编译预留资源。这不是运行成功证明,仍可能内存不足或超时。
    不改变 wbt 的 LTO/profile,不更改 Python 依赖约束,不跳过完整安装,不删除平台或放宽上传 needs。
  • 环境配置回归先因缺少 APK 前置安装失败(1 failed),补齐后通过;8 项工具回归包含
    编码、环境顺序、原完整安装/上传约束和本地既有已安装 wheel 的正反例。
    静态配置断言不能代替真正的 Alpine 编译、在线依赖解析及 native 加载。

runner 补齐与用户分发要求不同

核对公开 PyPI 的 wbt 0.9.2 文件清单及校验 SHA256 的 sdist:目前无 musllinux wheel,
sdist 使用 maturin、Rust edition 2024、PyO3 0.28 与 Polars 0.53,Cargo.lock 无 git source。
所以普通 Alpine 完整安装会构建 wbt 源包,需要可用的 Rust/C/C++ 工具链、头文件、链接器、
运行库、网络以及足够 CPU/内存/时间;CZSC 的 abi3 musllinux wheel 本身不提供这些依赖。
APK 的 rustup 包提供 rustup-init 并依赖 libgcc,可由
Alpine 包清单 核对。
容器日志中的实际 Alpine/工具链版本才是本次执行环境,不假定浮动标签永远不变。

不能把 PyArrow 一概归为源码构建阻塞:本次 PyPI 清单中,25.0.1 有 Python 3.10 的
musllinux_1_2_x86_64 wheel,26.0.0 有 3.11–3.13 对应 wheel;旧 3.11 失败日志也已选中
PyArrow 的 musllinux wheel。因此此修复不安装或绑定发行版 Arrow SDK。
若将来 resolver 选到只有 sdist 的 PyArrow,则需匹配的 Arrow C++ SDK、CMake 等,
参见 Arrow 源码构建说明;届时必须重新验证,
不能使用 distro Python 包或 no-deps 假装满足普通 pip 安装。

维护者需决定是否接受“musl 完整安装需要构建 wbt 源码”的用户要求。建议协调 wbt 上游补齐
musllinux wheel,以降低用户编译成本;该跨仓库构建/上传不属于本任务,也未获得授权。
本轮仅补 runner 基线,不调整依赖或支持范围。若完整安装仍失败,应继续记录实际缺口,
由维护者选择补充工具/资源、上游 wheel 分发或其他明确方案;不能自行删掉 musl 门禁。

证据复用与新 SHA 范围

本次不修改 CZSC native/Python 运行源码、依赖图、wheel 构建参数或功能 smoke 脚本。
既有算法、Rust、归档、安全审核和旧 head 的 16 个成功组合可作为历史证据复用,
但新 run 的 wheel 必须标明其自身 producer SHA,不能标记旧产物为新产物。
重点复核 Windows/musl × Python 3.10–3.13 八组合。由于共享版本命令也影响另外四平台,
沿用现有完整 24 组合发布 smoke 演练、重建同一 head 产物,不另造只跑子集却能发布的通道;
这会再次运行短 smoke,不重复已充分验证的全套算法测试。
新演练显式 publish_to_testpypi=false,只取一次快照,不等待/轮询外部 CI。
Windows 本机不可用,Docker daemon 不可用,未将本地编码/静态回归宣称为这八组合已通过。

最终复审需核对实际修复 head、八组合安装/native 结果、其余平台共享命令结果以及所有上传
仍 skipped。正式版/RC、最终发布范围、维护者审批、旧 extras 等残余风险接受和上传权限
继续独立待确认;无上传演练通过也不是发布授权。


Signal Contracts (`docs/signal_contracts.md`)

信号输出与注册模板契约

注册模板的版本标记是信号契约的一部分,不能以历史 Python 无后缀输出为理由删除。
本次恢复 34 个原始注册模板,并逐项修正实际 k3 与之完全一致;不是对所有输出
机械拼接函数名。bar_channel_V230508 使用 通道V230508,三法、分手线保留
完整八位版本。其他已一致的模板(包括 BS辅助221211)不改变标记形式。
宏同时校验函数名/注册名一致性,以及 k3 末尾完整连续 ASCII 数字串与注册版本
完全相等(不能用更长数字串包含相同尾部)。本次仅升级目标 k3,不改变 v1/v2/v3
业务分类值;普通十字线仍输出分类 十字线。

旧键迁移(破坏性变更)

本迁移纳入维护者于 2026-10-15 明确选择的 1.0.2,未撤回迁移、增加旧键别名或双协议。
版本号仍为 1.x 不代表旧策略可直接升级;以下迁移与历史数据重建要求保持有效。

函数注册名、调用参数和公开 API 不变。旧无版本键及通道旧版本键不提供静默别名,
反解析不再接受这些旧键,旧策略条件也不会匹配新输出。请更新 Event/Position/策略的
signals_all、signals_any、signals_not、保存的信号字符串、过滤器及缓存;需要
历史回测对齐时重新生成历史信号和快照。以下占位符仍按原参数取值。

注册名 旧 k3 新 k3
bar_operate_span_V221111 时间区间 时间区间V221111
bar_mean_amount_V221112 {th1}至{th2}千万 {th1}至{th2}千万V221112
bar_zdf_V221203 {t1}至{t2} {t1}至{t2}V221203
bar_amount_acc_V230214 累计超{t}千万 累计超{t}千万V230214
bar_single_V230214 状态 状态V230214
bar_big_solid_V230215 MID MIDV230215
bar_section_momentum_V221112 阈值{th}BP 阈值{th}BPV221112
bar_vol_bs1_V230224 BS1辅助 BS1辅助V230224
bar_fake_break_V230204 假突破 假突破V230204
bar_channel_V230508 通道V230507 通道V230508
byi_symmetry_zs_V221107 对称中枢 对称中枢V221107
coo_td_V221110 TD TDV221110
cxt_fx_power_V221107 分型强弱 分型强弱V221107
cxt_first_buy_V221126 BUY1 BUY1V221126
cxt_first_sell_V221126 SELL1 SELL1V221126
jcc_san_xing_xian_V221023 伞形线 伞形线V221023
jcc_ten_mo_V221028 吞没形态 吞没形态V221028
jcc_wu_yun_gai_ding_V221101 乌云盖顶 乌云盖顶V221101
jcc_ci_tou_V221101 刺透形态 刺透形态V221101
jcc_san_fa_V20221118 三法A 三法AV20221118
jcc_san_fa_V20221115 三法 三法V20221115
jcc_xing_xian_V221118 星形线 星形线V221118
jcc_fen_shou_xian_V20221113 分手线 分手线V20221113
jcc_zhu_huo_xian_V221027 烛火线 烛火线V221027
jcc_yun_xian_V221118 孕线 孕线V221118
jcc_ping_tou_V221113 平头形态 平头形态V221113
jcc_two_crow_V221108 两只乌鸦 两只乌鸦V221108
jcc_three_crow_V221108 三只乌鸦 三只乌鸦V221108
jcc_szx_V221111 十字线 十字线V221111
jcc_san_szx_V221122 三星 三星V221122
jcc_fan_ji_xian_V221121 反击线 反击线V221121
jcc_shan_chun_V221121 山川形态 山川形态V221121
jcc_gap_yin_yang_V221121 并列阴阳 并列阴阳V221121
jcc_ta_xing_V221124 塔形 塔形V221124

参数与验证范围

反解析对整个模板做占位符匹配,不再要求 k3 与模板第三段字面相等。
模板参数不一定直接等于调用参数:时间区间的 t1/t2 还原为 HHMM 字符串,
涨跌幅的 t1/t2 合并为 span,伞形线的 th 还原其 100 倍编码。
逆变换失败的候选继续匹配其他模板,不能提前终止整个注册表搜索。

当前注册模板中,相邻占位符只有 bar_zdf_V221203 的 {di}{mode},
以及 tas_ma_base_V221101、tas_ma_base_V221203、
tas_second_bs_V230303 的 {di}{ma_type}。共享解析器要求相邻参数中的
di 为完整非负整数前缀,不能将多位数字的尾部放入 mode/ma_type。
非空数据测试覆盖 di=10/11/100、ZF/DF,以及三个 MA 信号的 SMA/EMA/WMA
参数精确还原和完整信号键/值往返;其中二买信号不代表其笔形态分支已覆盖。

回归测试覆盖这 34 个信号的默认/空 K 线键、模板实例化和键往返,
四个动态 k3 的默认/自定义参数还原,以及全量 descriptor 的名称、opcode
唯一性与注册表映射。它不代表所有指标计算分支均已得到验证。

分类子模块待决事项

docs/public_api.md 附录 B 当前只承诺 bar/cxt/tas/vol/pressure/obv/cvolp
七个 Python 子模块,与现有 dispatcher 创建的模块一致。byi/coo/jcc/xl/zdy
等其他分类是否应新增分类导入路径,尚未确认属于当前支持范围;本次不扩展
API,也不将分类模块缺口等同于信号未注册。


Superpowers Plans 2026 06 02 Czsc Cli (`docs/superpowers/plans/2026-06-02-czsc-cli.md`)

czsc CLI 模块实施计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 为 czsc 库新增一个用 typer 写的统一 CLI(入口 czsc),把信号目录、缠论分析、策略回测、研究、可视化、性能基准等能力暴露成子命令,人机两用、--json LLM 友好、schema 自发现。

Architecture: 新建 czsc/cli/ 子包,按命令组拆文件;_io.py 收口输入加载/输出渲染/错误边界/连接器分发。每个命令只做「解析参数 → 调 czsc.* 公共 API → 交 _io.py 渲染」,不写业务/算法逻辑(符合开发宪法)。pyproject.toml 注册 [project.scripts] czsc = "czsc.cli:app"。

Tech Stack: Python 3.10+,typer(+click),pandas,pytest(typer.testing.CliRunner),ruff(line-length 120),uv。

设计文档: 飞书 wiki https://s0cqcxuy3p.feishu.cn/wiki/IoGRwkHxwiiHP8kOWwJc1s85nSb


已验证的 API 契约(实测,实现时直接用)

  • czsc._native.list_all_signals() -> list[dict](246 项;key:name / param_template / category / namespace)
  • czsc._native.list_signal_names(category=None) -> list[str]
  • czsc._native.get_signal_template(name) -> str、czsc._native.get_signal_category(name) -> str
  • czsc.Freq 成员 .value 为中文(Freq.F30.value == "30分钟",Freq.D.value == "日线" …)
  • czsc.format_standard_kline(df, freq) -> list[RawBar];df 必含 dt,symbol,open,close,high,low,vol,amount
  • czsc.mock.generate_symbol_kines(symbol, freq, sdt="20100101", edt="20250101", seed=42) -> df(列同上)
  • CZSC(bars):c.fx_list(FX:symbol,dt,mark,fx,high,low,power_volume,power_str)、c.bi_list(BI:symbol,sdt,edt,direction,high,low,length,power,angle)。无 zs_list,中枢不在 v1。
  • czsc.check_kline_quality(df) -> dict[symbol -> dict[check -> {description, rows}]]
  • czsc.get_signals_config(list[str]) -> list[dict]、czsc.get_signals_freqs(list[dict]) -> list[str]
  • czsc.Position:to_json()->str、from_json(str)->Position、save(path)、load_from_file(path)->Position、dump()->dict、unique_signals->list[str]
  • czsc.CzscStrategyBase 是抽象类(positions 抽象属性)→ CLI 内定义薄子类注入 positions
  • strat.backtest(bars) -> ResearchResult;res.holds_df() 列 dt,pos,price,n1b,symbol,pos_name
  • czsc.WeightBacktest(w, fee_rate=2e-4).stats -> dict(25 项:年化收益/夏普比率/最大回撤/日胜率…);w 列 dt,symbol,weight,price
  • wbt.generate_backtest_report(df, output_path, title=...) -> str(df 列 dt,symbol,weight,price)
  • from czsc.utils.plotting.lightweight import plot_czsc, plot_czsc_trader, plot_czsc_signals;plot_czsc(c, output='html', path=None):path=None 返回 HTML 字符串,传 path 写文件返回路径;plot_czsc_signals(bars, signals_config=[{'name': '...'}], output='html', path=None)
  • 连接器(懒加载,import 即可能抛错):local_data.get_raw_bars(symbol, freq, sdt, edt) -> list[RawBar];ts_connector.get_raw_bars(symbol, freq, sdt, edt) -> list[RawBar];ccxt_connector.get_raw_bars(symbol=, period=, sdt=, edt=) -> DataFrame(列 dt,open,high,low,close,vol,amount,无 symbol)

文件结构(先定边界)

文件 职责
czsc/cli/__init__.py 组装 typer app,add_typer 各子命令组,注册顶层命令
czsc/cli/_io.py load_bars_df / emit / fail / error_boundary / resolve_bars_for_symbol / freq_to_cn
czsc/cli/signals.py signals list / signals doc
czsc/cli/analyze.py analyze
czsc/cli/backtest.py backtest(_PositionsStrategy + 多 symbol + 连接器 + WeightBacktest + HTML)
czsc/cli/research.py research run / research replay / research config
czsc/cli/data.py data mock / data quality
czsc/cli/plot.py plot czsc / plot trader / plot signals
czsc/cli/bench.py bench
czsc/cli/schema.py schema
tests/cli/ 各命令 CliRunner 测试

设计偏差说明(相对飞书 spec): --json 改为每条命令的选项(而非全局 callback),因为 typer 全局 callback 选项必须置于子命令前(czsc --json signals list),对 LLM 不友好;改为 czsc signals list --json 更顺手。error_boundary 统一捕获异常。


Task 0: 依赖与入口骨架

Files:

  • Modify: pyproject.toml(dependencies 加 typer;新增 [project.scripts])

  • Create: czsc/cli/__init__.py

  • Test: tests/cli/__init__.py、tests/cli/test_cli_skeleton.py

  • Step 1: 安装 typer 依赖

编辑 pyproject.toml,在 [project].dependencies 列表末尾("tenacity", 之后)加一行:

toml
"typer>=0.12.0",

在 [project.optional-dependencies] 之前(约 line 73 附近,dependencies 数组闭合 ] 之后)新增:

toml
[project.scripts]
czsc = "czsc.cli:app"

Run: uv sync --extra dev
Expected: 成功安装 typer。

  • Step 2: 写失败的测试

Create tests/cli/__init__.py(空文件)。
Create tests/cli/test_cli_skeleton.py:

python
from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def test_app_help_lists_subcommands():
    result = runner.invoke(app, ["--help"])
    assert result.exit_code == 0
    for name in ["signals", "analyze", "backtest", "research", "data", "plot", "bench", "schema"]:
        assert name in result.output
  • Step 3: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_cli_skeleton.py -v
Expected: FAIL(ModuleNotFoundError: czsc.cli 或子命令未注册)。

  • Step 4: 写最小实现

Create czsc/cli/__init__.py:

python
"""CZSC 命令行工具入口。

通过 `czsc <子命令>` 调用;所有命令支持 `--json` 输出(LLM 友好)。
"""

from __future__ import annotations

import typer

from czsc.cli import analyze, backtest, bench, data, plot, research, schema, signals

app = typer.Typer(
    help="CZSC 缠论技术分析命令行工具",
    no_args_is_help=True,
    add_completion=False,
)

app.add_typer(signals.app, name="signals", help="信号目录与文档")
app.add_typer(research.app, name="research", help="策略研究 / 回放 / 配置解析")
app.add_typer(data.app, name="data", help="造数与质量校验")
app.add_typer(plot.app, name="plot", help="缠论 / 信号 HTML 可视化")
app.command("analyze", help="对一段 K 线跑缠论,输出分型 + 笔")(analyze.analyze)
app.command("backtest", help="传入 Position 对象 + 数据源,产出回测结果")(backtest.backtest)
app.command("bench", help="CZSC / CzscTrader 吞吐量基准")(bench.bench)
app.command("schema", help="吐出全部命令/参数 schema 供 LLM 自发现")(schema.schema)


if __name__ == "__main__":
    app()

此 task 依赖后续 task 创建的子模块。为先让骨架测试通过,先创建各子模块的最小占位(后续 task 替换为真实实现)。Create 以下占位文件,每个含一个 typer app 或函数:

czsc/cli/signals.py、czsc/cli/research.py、czsc/cli/data.py、czsc/cli/plot.py:

python
from __future__ import annotations

import typer

app = typer.Typer(no_args_is_help=True)

czsc/cli/analyze.py:

python
from __future__ import annotations


def analyze() -> None:  # 占位,Task 4 替换
    ...

同理 czsc/cli/backtest.py(def backtest(): ...)、czsc/cli/bench.py(def bench(): ...)、czsc/cli/schema.py(def schema(): ...)。

  • Step 5: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_cli_skeleton.py -v
Expected: PASS。

  • Step 6: Commit
bash
git add pyproject.toml czsc/cli/ tests/cli/
git commit -m "feat(cli): typer 入口骨架 + 子命令占位"

Task 1: `_io.py` 共享层

Files:

  • Create: czsc/cli/_io.py

  • Test: tests/cli/test_io.py

  • Step 1: 写失败的测试

Create tests/cli/test_io.py:

python
import json

import pytest
import typer

from czsc.cli import _io
from czsc.mock import generate_symbol_kines


def test_load_bars_df_csv(tmp_path):
    df = generate_symbol_kines("000001", "30分钟", "20240101", "20240105")
    p = tmp_path / "k.csv"
    df.to_csv(p, index=False)
    out = _io.load_bars_df(str(p))
    for col in ["dt", "symbol", "open", "close", "high", "low", "vol", "amount"]:
        assert col in out.columns


def test_load_bars_df_missing_columns(tmp_path):
    p = tmp_path / "bad.csv"
    p.write_text("a,b\n1,2\n")
    with pytest.raises(ValueError, match="缺少必需列"):
        _io.load_bars_df(str(p))


def test_emit_json(capsys):
    _io.emit({"k": 1}, json_out=True, human=lambda d: None)
    out = capsys.readouterr().out
    assert json.loads(out) == {"k": 1}


def test_fail_json_raises_exit(capsys):
    with pytest.raises(typer.Exit):
        _io.fail("boom", json_out=True, err_type="ValueError")
    err = capsys.readouterr().err
    assert json.loads(err)["error"]["message"] == "boom"


def test_freq_to_cn():
    assert _io.freq_to_cn("F30") == "30分钟"
    assert _io.freq_to_cn("30分钟") == "30分钟"
    assert _io.freq_to_cn("D") == "日线"
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_io.py -v
Expected: FAIL(czsc.cli._io 不存在)。

  • Step 3: 写实现

Create czsc/cli/_io.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_io.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/_io.py tests/cli/test_io.py
git commit -m "feat(cli): _io 共享层(加载/渲染/错误边界/连接器分发)"

Task 2: `data` 命令组(mock / quality)

Files:

  • Modify: czsc/cli/data.py

  • Test: tests/cli/test_data.py

  • Step 1: 写失败的测试

Create tests/cli/test_data.py:

python
import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def test_data_mock_writes_csv(tmp_path):
    out = tmp_path / "k.csv"
    r = runner.invoke(app, ["data", "mock", "--symbol", "000001", "--freq", "30分钟",
                            "--sdt", "20240101", "--edt", "20240201", "-o", str(out)])
    assert r.exit_code == 0, r.output
    assert out.exists()
    import pandas as pd

    df = pd.read_csv(out)
    assert {"dt", "symbol", "open", "close", "high", "low", "vol", "amount"} <= set(df.columns)


def test_data_quality_json(tmp_path):
    csv = tmp_path / "k.csv"
    from czsc.mock import generate_symbol_kines

    generate_symbol_kines("000001", "30分钟", "20240101", "20240201").to_csv(csv, index=False)
    r = runner.invoke(app, ["data", "quality", str(csv), "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    assert "000001" in data
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_data.py -v
Expected: FAIL(占位 app 无 mock/quality 命令)。

  • Step 3: 写实现

Replace czsc/cli/data.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_data.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/data.py tests/cli/test_data.py
git commit -m "feat(cli): data mock / quality"

Task 3: `signals` 命令组(list / doc)

Files:

  • Modify: czsc/cli/signals.py

  • Test: tests/cli/test_signals.py

  • Step 1: 写失败的测试

Create tests/cli/test_signals.py:

python
import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def test_signals_list_json_has_entries():
    r = runner.invoke(app, ["signals", "list", "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    assert isinstance(data, list) and len(data) > 100
    assert {"name", "param_template", "category", "namespace"} <= set(data[0])


def test_signals_doc_known_name():
    # 先取一个真实名字
    listing = json.loads(runner.invoke(app, ["signals", "list", "--json"]).stdout)
    name = listing[0]["name"]
    r = runner.invoke(app, ["signals", "doc", name, "--json"])
    assert r.exit_code == 0, r.output
    doc = json.loads(r.stdout)
    assert doc["name"] == name
    assert "param_template" in doc


def test_signals_doc_unknown_name_fails():
    r = runner.invoke(app, ["signals", "doc", "不存在的信号xyz", "--json"])
    assert r.exit_code != 0
    assert json.loads(r.stderr)["error"]["message"]
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_signals.py -v
Expected: FAIL。

  • Step 3: 写实现

Replace czsc/cli/signals.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_signals.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/signals.py tests/cli/test_signals.py
git commit -m "feat(cli): signals list / doc"

Task 4: `analyze` 命令

Files:

  • Modify: czsc/cli/analyze.py

  • Test: tests/cli/test_analyze.py

  • Step 1: 写失败的测试

Create tests/cli/test_analyze.py:

python
import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def _mock_csv(tmp_path):
    from czsc.mock import generate_symbol_kines

    p = tmp_path / "k.csv"
    generate_symbol_kines("000001", "30分钟", "20230101", "20240101").to_csv(p, index=False)
    return p


def test_analyze_json_has_fx_and_bi(tmp_path):
    p = _mock_csv(tmp_path)
    r = runner.invoke(app, ["analyze", str(p), "--freq", "30分钟", "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    assert data["symbol"] == "000001"
    assert len(data["fx_list"]) > 0
    assert len(data["bi_list"]) > 0
    assert {"dt", "mark", "fx"} <= set(data["fx_list"][0])
    assert {"sdt", "edt", "direction", "high", "low"} <= set(data["bi_list"][0])
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_analyze.py -v
Expected: FAIL。

  • Step 3: 写实现

Replace czsc/cli/analyze.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_analyze.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/analyze.py tests/cli/test_analyze.py
git commit -m "feat(cli): analyze(分型+笔)"

Task 5: `backtest` 命令(核心)

Files:

  • Modify: czsc/cli/backtest.py

  • Test: tests/cli/test_backtest.py、tests/cli/conftest.py(Position fixture)

  • Step 1: 写 Position fixture(测试基础设施)

Create tests/cli/conftest.py:

python
import pytest

from czsc import Event, Position


@pytest.fixture
def position_file(tmp_path):
    """构造一个 unique_signals 非空的真实 Position,落盘为 JSON 文件,返回路径。"""
    oe = Event.load({"operate": "开多", "signals_all": ["30分钟_D1_表里关系V230101_向上_任意_任意_0"]})
    xe = Event.load({"operate": "平多", "signals_all": ["30分钟_D1_表里关系V230101_向下_任意_任意_0"]})
    pos = Position(symbol="000001", name="表里多头", opens=[oe], exits=[xe],
                   interval=0, timeout=20, stop_loss=300)
    p = tmp_path / "pos.json"
    p.write_text(pos.to_json())
    return p
  • Step 2: 写失败的测试

Create tests/cli/test_backtest.py:

python
import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def _bars_csv(tmp_path, symbol="000001"):
    from czsc.mock import generate_symbol_kines

    p = tmp_path / f"{symbol}.csv"
    generate_symbol_kines(symbol, "30分钟", "20200101", "20240101").to_csv(p, index=False)
    return p


def test_backtest_with_bars_file_json(tmp_path, position_file):
    bars = _bars_csv(tmp_path)
    r = runner.invoke(app, ["backtest", str(position_file), "--bars", str(bars), "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    assert "symbols" in data and "portfolio" in data
    assert "000001" in data["symbols"]
    assert "年化收益" in data["portfolio"]


def test_backtest_html_report(tmp_path, position_file):
    bars = _bars_csv(tmp_path)
    out = tmp_path / "report.html"
    r = runner.invoke(app, ["backtest", str(position_file), "--bars", str(bars),
                            "--html", str(out), "--json"])
    assert r.exit_code == 0, r.output
    assert out.exists() and out.stat().st_size > 0


def test_backtest_requires_data_source(position_file):
    r = runner.invoke(app, ["backtest", str(position_file), "--json"])
    assert r.exit_code != 0
    assert json.loads(r.stderr)["error"]["message"]
  • Step 3: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_backtest.py -v
Expected: FAIL。

  • Step 4: 写实现

Replace czsc/cli/backtest.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 5: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_backtest.py -v
Expected: PASS(三个用例)。

  • Step 6: Commit
bash
git add czsc/cli/backtest.py tests/cli/test_backtest.py tests/cli/conftest.py
git commit -m "feat(cli): backtest(positions+数据源→逐symbol回测+组合+HTML)"

Task 6: `research` 命令组(run / replay / config)

Files:

  • Modify: czsc/cli/research.py

  • Test: tests/cli/test_research.py

  • Step 1: 写失败的测试

Create tests/cli/test_research.py:

python
import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def test_research_config_derives(tmp_path):
    r = runner.invoke(app, ["research", "config",
                            "30分钟_D1_表里关系V230101_向上_任意_任意_0", "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    assert "signals_config" in data and "freqs" in data
    assert data["freqs"] == ["30分钟"]
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_research.py -v
Expected: FAIL。

  • Step 3: 写实现

Replace czsc/cli/research.py:

text
/* Detailed source-code truncated for AI context efficiency. */

注:run_replay 的精确签名在 Task 实施时用 uv run --no-sync python -c "import inspect,czsc;print(inspect.signature(czsc.run_replay))" 确认;若 res_path 形参名不同,相应调整关键字。

  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_research.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/research.py tests/cli/test_research.py
git commit -m "feat(cli): research run / replay / config"

Task 7: `plot` 命令组(czsc / trader / signals)

Files:

  • Modify: czsc/cli/plot.py

  • Test: tests/cli/test_plot.py

  • Step 1: 写失败的测试

Create tests/cli/test_plot.py:

python
from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def _bars_csv(tmp_path):
    from czsc.mock import generate_symbol_kines

    p = tmp_path / "k.csv"
    generate_symbol_kines("000001", "30分钟", "20230101", "20240101").to_csv(p, index=False)
    return p


def test_plot_czsc_writes_html(tmp_path):
    p = _bars_csv(tmp_path)
    out = tmp_path / "c.html"
    r = runner.invoke(app, ["plot", "czsc", str(p), "--freq", "30分钟", "-o", str(out)])
    assert r.exit_code == 0, r.output
    assert out.exists() and out.stat().st_size > 0


def test_plot_signals_writes_html(tmp_path):
    p = _bars_csv(tmp_path)
    cfg = tmp_path / "cfg.json"
    cfg.write_text('[{"name": "30分钟_D1_表里关系V230101_向上_任意_任意_0"}]')
    out = tmp_path / "s.html"
    r = runner.invoke(app, ["plot", "signals", str(p), "--signals-config", str(cfg),
                            "--freq", "30分钟", "-o", str(out)])
    assert r.exit_code == 0, r.output
    assert out.exists() and out.stat().st_size > 0
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_plot.py -v
Expected: FAIL。

  • Step 3: 写实现

Replace czsc/cli/plot.py:

text
/* Detailed source-code truncated for AI context efficiency. */

注:plot trader 需要构造 CzscTrader 对象(依赖 strategy + bars),实现复杂度高于本计划其它命令,v1 先实现 plot czsc 与 plot signals(覆盖案例 2 单周期 + 案例 3);plot trader(案例 2 多周期)作为后续增量,实现时参考 docs/examples/13_lightweight_charts_html.py 构造 trader 后调 plot_czsc_trader。

  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_plot.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/plot.py tests/cli/test_plot.py
git commit -m "feat(cli): plot czsc / signals(案例 2、3)"

Task 8: `bench` 命令

Files:

  • Modify: czsc/cli/bench.py

  • Test: tests/cli/test_bench.py

  • Step 1: 先确认 examples/17 的可复用入口

Run: uv run --no-sync python -c "import ast,sys; print(open('docs/examples/17_perf_benchmark.py').read()[:2000])"
目标:确认其有无可直接 import 的函数;若无(纯 __main__ 脚本),则在 bench 命令内内联最小版:mock 生成 N 年 K 线 → 计时 CZSC(bars) 构造 + 逐根 c.update(bar) → 输出 bars/s。

  • Step 2: 写失败的测试

Create tests/cli/test_bench.py:

python
import json

import pytest
from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


@pytest.mark.slow
def test_bench_json_reports_throughput():
    r = runner.invoke(app, ["bench", "--years", "1", "--freq", "30分钟", "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    assert data["czsc_construct"]["bars_per_sec"] > 0
  • Step 3: 跑测试,确认 fail(带 --run-slow)

Run: uv run --no-sync pytest tests/cli/test_bench.py -v --run-slow
Expected: FAIL。

  • Step 4: 写实现

Replace czsc/cli/bench.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 5: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_bench.py -v --run-slow
Expected: PASS。

  • Step 6: Commit
bash
git add czsc/cli/bench.py tests/cli/test_bench.py
git commit -m "feat(cli): bench(CZSC 吞吐量基准,案例 4)"

Task 9: `schema` 元命令

Files:

  • Modify: czsc/cli/schema.py

  • Test: tests/cli/test_schema.py

  • Step 1: 写失败的测试

Create tests/cli/test_schema.py:

python
import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def test_schema_json_lists_commands():
    r = runner.invoke(app, ["schema", "--json"])
    assert r.exit_code == 0, r.output
    data = json.loads(r.stdout)
    cmds = {c["command"] for c in data}
    assert "czsc signals list" in cmds
    assert "czsc backtest" in cmds
    entry = next(c for c in data if c["command"] == "czsc signals list")
    assert any(p["name"] == "json_out" or p.get("opts") for p in entry["params"]) or entry["params"]
  • Step 2: 跑测试,确认 fail

Run: uv run --no-sync pytest tests/cli/test_schema.py -v
Expected: FAIL。

  • Step 3: 写实现

Replace czsc/cli/schema.py:

text
/* Detailed source-code truncated for AI context efficiency. */
  • Step 4: 跑测试,确认 pass

Run: uv run --no-sync pytest tests/cli/test_schema.py -v
Expected: PASS。

  • Step 5: Commit
bash
git add czsc/cli/schema.py tests/cli/test_schema.py
git commit -m "feat(cli): schema 元命令(LLM 自发现)"

Task 10: 全量验收(acceptance)

Files:

  • Create: tests/cli/test_acceptance.py

  • Step 1: 写端到端验收测试

Create tests/cli/test_acceptance.py:

python
"""端到端验收:模拟 LLM 用 schema 发现 → 调用核心命令全链路。"""

import json

from typer.testing import CliRunner

from czsc.cli import app

runner = CliRunner()


def test_schema_then_call_chain(tmp_path, position_file):
    # 1. schema 自发现
    schema = json.loads(runner.invoke(app, ["schema", "--json"]).stdout)
    cmds = {c["command"] for c in schema}
    assert {"czsc signals list", "czsc analyze", "czsc backtest", "czsc data mock"} <= cmds

    # 2. 造数
    csv = tmp_path / "k.csv"
    r = runner.invoke(app, ["data", "mock", "--symbol", "000001", "--freq", "30分钟",
                            "--sdt", "20200101", "--edt", "20240101", "-o", str(csv)])
    assert r.exit_code == 0

    # 3. analyze
    a = json.loads(runner.invoke(app, ["analyze", str(csv), "--freq", "30分钟", "--json"]).stdout)
    assert a["fx_list"] and a["bi_list"]

    # 4. backtest
    b = json.loads(runner.invoke(app, ["backtest", str(position_file), "--bars", str(csv), "--json"]).stdout)
    assert "年化收益" in b["portfolio"]
  • Step 2: 跑全套 CLI 测试

Run: uv run --no-sync pytest tests/cli/ -v
Expected: 全部 PASS(bench 默认跳过)。

  • Step 3: 跑全仓回归 + ruff

Run:

bash
uv run --no-sync pytest -q
uv run --no-sync ruff check czsc/cli/ tests/cli/
uv run --no-sync ruff format --check czsc/cli/ tests/cli/

Expected: 测试无回归;ruff 无报错(有则 ruff format czsc/cli/ tests/cli/ 修复后重跑)。

  • Step 4: 验证 console_scripts 真入口

Run:

bash
uv run --no-sync python -c "from czsc.cli import app; import typer; typer.main.get_command(app)"
uv run --no-sync czsc schema --json | python -c "import sys,json; print(len(json.load(sys.stdin)), 'commands')"

Expected: czsc 命令可用,schema 输出 ≥ 9 条命令。

  • Step 5: 慢测试全量

Run: uv run --no-sync pytest tests/cli/ --run-slow -q
Expected: 含 bench 全 PASS。

  • Step 6: Commit
bash
git add tests/cli/test_acceptance.py
git commit -m "test(cli): 端到端验收(schema 发现 → 全链路调用)"

自检(plan reviewer 已内联修正)

  • spec 覆盖:signals list/doc(T3)、analyze(T4)、backtest+案例1(T5)、research run/replay/config(T6)、data mock/quality(T2)、plot czsc/signals+案例2/3(T7,plot trader 标注为后续增量)、bench+案例4(T8)、schema(T9)、打包入口(T0)、I/O 约定(T1)、验收(T10)。✅
  • placeholder 扫描:每步均有真实代码与精确命令;run_replay 签名、examples/17 入口标注为「实施时用一行命令确认」,非 placeholder(给了确认命令与兜底方案)。
  • 类型一致性:_io.emit/fail/error_boundary/load_bars_df/freq_to_cn/resolve_bars_for_symbol 在 T1 定义,后续 task 一致引用;_PositionsStrategy 仅 T5 内部使用。

已知后续增量(非 v1 阻塞)

  • plot trader(案例 2 多周期联立):需构造 CzscTrader,参考 docs/examples/13_lightweight_charts_html.py。
  • backtest 多 position 权重聚合口径目前为等权 mean;如需市值/自定义加权,后续加 --weight-agg 选项。
  • --symbol 真实连接器(tushare/ccxt)需各自 token/网络,CI 用 --bars 文件路径覆盖;连接器路径用 @pytest.mark.slow + 真实凭证在本地验证。

--- METRICS ---