Skip to main content

TuAlpha

TuAlpha 是基于 Tushare 数据、面向中国 A 股股票与 ETF 的日频事件驱动回测框架。框架借鉴 Zipline 的 Asset → DataPortal → Blotter → Ledger → Metrics 分层,但使用自有 Reader/Writer,不依赖 zipline-reloaded

当前版本为 0.7.1,只支持多头现金账户、股票和 ETF;不支持期货、期权、融资融券或 ETF 申赎。

功能

  • TuAlpha 自有 Reader/Writer 生成并读取 assets-7.sqlite + daily_equities.bcolz + adjustments.sqlite 固定 Bundle
  • Tushare trade_cal 开放日固化到 calendar.sqlite,不依赖第三方交易日历
  • 股票、ETF 日线回测,Bundle 默认根目录:~/.tualpha
  • D 日收盘决策,D+1 开盘或收盘成交,避免未来函数
  • 涨停禁止买入、跌停禁止卖出的保守成交模型
  • 停牌、无行情、零成交量限制
  • 主板/创业板/ETF:买入 100 股(份)整数倍
  • 科创板:200 股起、之后按 1 股递增
  • 北交所:100 股起、之后按 1 股递增
  • 所有股票和 ETF 统一 T+1
  • 股票印花税、佣金、经手费与过户费;避免 all-in 佣金重复计费
  • 前复权、后复权或不复权策略数据
  • 五个默认宽基指数的 PIT 历史成分与权重
  • 向量化横截面读取、受控列缓存和批量撮合/估值
  • 中文 Plotly HTML 报告和每日持仓 CSV
  • tualpha update 增量更新显式指定的 Tushare CSV 缓存并替换固定目录 Bundle

安装

推荐 64 位 CPython 3.12:

uv add tualpha

从源码开发:

uv sync --dev
uv run pytest

数据目录

Bundle 与原始 CSV 已完全分离。Bundle 根目录默认是 ~/.tualpha,可通过 --bundle-root、Python 的 bundle_root= 或环境变量 TUALPHA_BUNDLE_ROOT 显式覆盖:

~/.tualpha/                         # Bundle 根目录;可用 --bundle-root 修改
├── bundles/
│   └── tualpha/                    # 固定目录,不再使用更新时间命名
│       ├── assets-7.sqlite
│       ├── daily_equities.bcolz/    # OHLCV + 全部扩展日线列
│       ├── index_daily.bcolz/       # 仅用于报告基准的指数日线
│       ├── minute_equities.bcolz/
│       ├── adjustments.sqlite
│       ├── calendar.sqlite          # Tushare SSE 交易日历
│       ├── finance.sqlite           # 公告时点财务宽表
│       ├── index_constituents.sqlite # PIT 指数成分与权重
│       ├── manifest.json
│       └── READY
├── cache/
│   └── tualpha/
│       ├── normalized.duckdb
│       └── sid-map.json
├── update-status.json              # 当前更新状态及最后一次成功更新
├── .locks/                          # 进程锁
└── .staging/                        # 构建临时区;成功后自动清理

Bundle schema v6 不生成或读取 tualpha.duckdbnormalized.duckdb 只存在于 cache/,用于更新和构建,不属于可发布 Bundle。新构建必须包含 calendar.sqlite;旧 schema v4/v5 可从日线 Bcolz 的 day + calendar_offset 只读恢复交易日,无需立即重建,下一次正常更新会整体发布为 v6。

CSV 缓存目录没有默认值,执行更新时必须显式传入,并且不能与 Bundle 根目录互为父子目录。例如:

E:\data\tushare_data\
├── daily\YYYYMMDD.csv
├── fund_daily\YYYYMMDD.csv
├── adj_factor\YYYYMMDD.csv
├── fund_adj\YYYYMMDD.csv
├── stk_limit\YYYYMMDD.csv
├── suspend_d\YYYYMMDD.csv
├── daily_basic\YYYYMMDD.csv
├── moneyflow\YYYYMMDD.csv
├── industry\YYYYMMDD.csv
├── stock_st\YYYYMMDD.csv
├── index_daily\YYYYMMDD.csv
├── index_weight\YYYYMMDD.csv
├── balancesheet\YYYYMMDD.csv
├── income\YYYYMMDD.csv
├── cashflow\YYYYMMDD.csv
├── fina_indicator\YYYYMMDD.csv
├── stock_basic.csv
├── etf_basic.csv
├── index_basic.csv
└── trade_cal.csv

回测只读取 ~/.tualpha/bundles/tualpha,不会读取 CSV。fund_daily 中的 LOF、分级基金和异常代码不会成为可交易资产;Bundle 使用 etf_basic.csv 作为 ETF 白名单。

更新时先在 .staging 中使用 TuAlpha 自有 Writer 完整生成并加载验证所有文件,再替换固定的 bundles/tualpha 目录。回测和底层 load_bundle_data() 会持有读锁,更新将在现有读者关闭后发布,避免 Windows 文件句柄阻断目录替换。现有 schema v5 Bundle 无需重建;底层访问统一使用 tualpha.bundle.load_bundle_data() 返回的 TuAlpha Reader。详细文件、表和字段说明见 docs/bundle-format.md

更新数据

必须通过环境变量提供 Token;没有 Token 时命令立即中止:

export TUSHARE_TOKEN="your-token"
tualpha update --csv-dir /e/data/tushare_data

常用参数:

tualpha update --csv-dir /e/data/tushare_data --from 20260101 --to 20260821
tualpha update --csv-dir /e/data/tushare_data --repair-from 20250101
tualpha update --csv-dir /e/data/tushare_data --lookback 20
tualpha update --csv-dir /e/data/tushare_data --index-weight 000016.SH
tualpha update --csv-dir /e/data/tushare_data --bundle-root /d/tualpha-bundle
tualpha update --csv-dir /e/data/tushare_data --dry-run --json

更新流程:

  1. 增量下载并保留全部现有数据目录,包括行情、资金流、每日指标、行业和财务报表;普通更新会逐数据集检测并补齐缺失的交易日分区。
  2. 对超过单次行数上限的接口自动分页,并检测接口忽略分页造成的重复页;财务数据除刷新最近报告期外,还按公告日期增量抓取旧报告期修订并与历史版本合并。
  3. 数据写入 ~/.tualpha/.staging,校验字段、日期、唯一键和内容哈希。
  4. 成功后原子替换 CSV,并增量同步 ~/.tualpha/cache/tualpha/normalized.duckdb
  5. 在临时目录生成新的 Bundle,加载验证成功后替换固定的 ~/.tualpha/bundles/tualpha
  6. 原子写入 ~/.tualpha/update-status.json,记录 running / succeeded / failed / dry_run_succeeded 状态、CSV 路径、更新时间、更新日期和错误信息。

index_weight 官方接口要求逐个指数代码查询。TuAlpha 默认维护 000300.SH000852.SH000905.SH000906.SH899050.BJ;首次更新从本地最早日线之前一个月开始回补,之后重抓最近两个快照月。重复传入 --index-weight 可追加其他指数。原始权重单位为百分比。

快速开始

from tualpha import order_target_percent, record, run_algorithm, symbol


def initialize(context):
    context.asset = symbol("510300.SH")


def handle_data(context, data):
    closes = data.history(context.asset, "close", 20)
    if len(closes) < 20:
        return

    target = 0.95 if closes.iloc[-1] > closes.mean() else 0.0
    order_target_percent(context.asset, target)
    record(close=closes.iloc[-1], ma20=closes.mean())


result = run_algorithm(
    start="2020-01-01",
    end="2025-12-31",
    initialize=initialize,
    handle_data=handle_data,
    capital_base=1_000_000,
    # bundle_root="~/.tualpha",  # 默认值;仅自定义时传入
    adjustment="qfq",  # qfq / hfq / raw
    execution_time="open",  # open / close
    benchmark="000300.SH",
    output_dir="outputs/demo",
    strategy_name="沪深300 ETF 趋势策略",
    show_progress=True,  # 默认使用 tqdm 显示交易日进度
)

print(result.summary())

回测默认使用 tqdm 显示已处理交易日、完成比例、运行速度、预计剩余时间和当前日期。批处理、测试或嵌套运行时可传入 show_progress=False 关闭。

从 0.7.1 起,构建器严格校验 Tushare SSE trade_cal.csv,并把 Bundle 范围内的开放日写入 schema v6 calendar.sqlite;项目不再依赖 exchange-calendars。公司行动调整持仓时会同步隔夜全仓卖单,避免复权后订单与持仓不一致而误报“交易单位不合法”或“持仓不足”;普通买卖订单不改写。

从 0.7.0 起,Bundle 的资产库、Bcolz 日线、复权库和空分钟元数据均由 TuAlpha 自有兼容层读写,项目不再依赖 zipline-reloaded;现有 schema v5 Bundle 无需重建。HTML 报告同时移除了“交易分析 (Trade Analysis)”及其两张逐笔图,其他指标、收益、基准、费用限制和组合归因模块保持不变。

从 0.6.1 起,多资产 data.current() 会一次定位整组资产并批量读取字段,不再退化为逐资产、逐字段 Bcolz 标量访问。大于等于 256 个资产的横截面读取会按需缓存只读物理列,Portal 生命周期内采用 1 GiB LRU 上限;普通单资产策略不会预加载这些列。Broker 同时只遍历未完成订单,并在撮合、盯市和日终持仓记录之间复用同一交易日行情。

真实 Bundle 上运行一个每日扫描全市场、最多持有 400 只股票的 2016-10-01 至 2026-08-21 策略,共 2400 个交易日、105610 笔订单;包含内联 Plotly 报告和 157991 行持仓 CSV 的总耗时为约 60 秒。该数字来自开发机基准,仅用于说明优化量级,实际时间取决于硬件、资产池和策略逻辑。

运行后生成:

outputs/demo/
├── report.html
└── daily_positions.csv

daily_positions.csv 使用 UTF-8-SIG 和长表结构。每个交易日都有一条 CASH 记录,持仓记录包含数量、可卖数量、成本、原始/复权收盘价、市值、权重和未实现盈亏。HTML 报告的组合归因表按标的展示成交次数、持有总天数、已实现盈亏、盈亏贡献占比、几何贡献收益和总费用;持有总天数按日终持仓大于零的交易日去重统计。几何贡献收益将每日组合收益归属于当日日终市值最大的实际持仓,再按 ∏(1+r)-1 链接,例如先涨 5%、再跌 3% 的结果为 1.05 × 0.97 - 1 = 1.85%

核心 API

策略回调内可使用:

  • symbol(code)
  • order(asset, amount)
  • order_value(asset, value)
  • order_target(asset, target)
  • order_target_value(asset, target)
  • order_percent(asset, percent)
  • order_target_percent(asset, target)
  • cancel_order(order) / get_open_orders()
  • record(**values)
  • data.current(asset, field)
  • data.history(asset, field, bar_count)
  • data.fundamental(asset, field, period="latest")
  • data.fundamentals(asset, fields, periods=4)
  • data.index_constituents(index_code)
  • data.available_fields(namespace=None)
  • data.can_trade(asset)

所有新订单默认只在下一交易日尝试一次。未成交订单会记录 limit_uplimit_downsuspendedt_plus_oneinvalid_lot 等原因,并在 HTML 报告中汇总。

日频因子、行业和 ST 数据

扩展日频字段使用 <数据集>.<字段> 命名,并与价格一样支持 current()history()

pe_ttm = data.current(context.asset, "daily_basic.pe_ttm")
net_flow = data.history(context.asset, "moneyflow.net_mf_amount", 20)
industry = data.current(context.asset, "industry.l1_name")
is_st = data.current(context.asset, "stock_st.is_st")

支持的数据集:

  • daily_basic:估值、换手率、股本、市值等;单位保持 Tushare 原始口径;
  • moneyflow:大小单量和金额,量为手、金额为万元;
  • industry:申万一至三级行业代码及名称;
  • stock_st:ST 名称、类型及虚拟字段 is_st,非 ST 日返回 0

可通过 data.available_fields("daily_basic") 查看当前 Bundle 实际包含的字段。所有扩展日线字段都是 daily_equities.bcolz 的物理 CTable 列,例如 ta_daily_basic__pe_ttm;行业和 ST 字符串采用字典编码列,并由根属性中的字段注册表透明还原。回测不会读取 CSV 或 normalized.duckdb

PIT 历史指数成分与权重

members = data.index_constituents("000300.SH")
# 索引:ts_code
# 列:asset、weight、snapshot_date

查询返回当前回调日前最新的月度快照,weight 保持 Tushare 百分比口径。为避免把快照日收盘后才能确定的数据提前使用,规则严格为 snapshot_date < 当前回测日:D 日快照从 D+1 首个回调开始可见;首个可见快照前返回空 DataFrame,快照之间沿用最近历史快照,绝不反向填充未来成分。无法映射到资产库的成分仍保留代码和权重,assetNone

正式数据位于 Bundle 的 index_constituents.sqlite。Tushare 未提供公告时间和历史修订时间,因此该能力保证“快照日期 PIT”,不能还原供应商后续修订前的数据版本。

无未来函数的财务查询

财务字段必须使用 fundamental()fundamentals(),不能通过 current() 读取:

roe = data.fundamental(context.asset, "fina_indicator.roe")
revenue_2023 = data.fundamental(
    context.asset,
    "income.revenue",
    period="20231231",
)

reports = data.fundamentals(
    context.asset,
    [
        "fina_indicator.roe",
        "income.revenue",
        "balancesheet.total_assets",
        "cashflow.n_cashflow_act",
    ],
    periods=4,
)

四张财务宽表位于 Bundle 根目录的 finance.sqlite。为保守避免收盘后公告造成未来函数,财务记录从公告日后的首个交易日才可见,即查询要求 coalesce(f_ann_date, ann_date) < 当前回测日;同一报告期按当时可见的最新公告、update_flag 和修订顺序选择版本。利润表和现金流量表保留 Tushare 的年初至今累计口径,不自动伪造单季度或 TTM 数据。报表默认选择 report_type="1" 的合并报表。

复权与账户处理

  • 前复权历史窗口:原始价格 × 当日复权因子 / 当前回调日复权因子
  • 后复权:原始价格 × 当日复权因子

前复权窗口以策略当前可见的最后交易日为基准,延长回测结束日期不会改写过去回调看到的数据。成交、现金、费用和涨跌停判断始终使用原始价格。

持仓跨越复权因子变化时,框架按 Tushare 的“分红再投”总收益语义调整经济持仓数量和单位成本。这是只有复权因子、没有分红明细时的近似模型,并不等同于真实现金红利到账。

费用默认值

  • 券商佣金:成交额 0.03%,最低 5 元,默认视为已含交易所经手费
  • 股票卖出印花税:2023-08-28 前 0.1%,之后 0.05%
  • 股票过户费:2022-04-29 前 0.002%,之后 0.001%
  • ETF:不收股票印花税和股票过户费

可传入 ChinaFeeModel 修改费率、最低佣金和“佣金是否含经手费”的口径。

Agent 策略编写 Skill

项目提供 .agents/skills/tualpha-strategy/ Skill,使 Agent 能按照 TuAlpha 的 D/D+1 时序、A 股交易规则、扩展字段和财务 PIT 语义编写策略。Pi 项目配置会自动发现该目录,也可显式调用:

/skill:tualpha-strategy

详见 .agents/skills/README.md

已知边界

  • 触及涨跌停并非交易所法律意义上的绝对不能成交;框架采用“涨停不买、跌停不卖”的保守流动性假设。
  • 日内临时停牌在日频模型中按全天不可交易处理。
  • 不模拟涨跌停排队、盘口深度、部分成交和冲击成本。
  • etf_basic.csv 不提供退市日期;退市 ETF 在缺少额外清算数据时以最后交易日作为资产结束日。

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tualpha-0.7.1.tar.gz (87.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tualpha-0.7.1-py3-none-any.whl (96.7 kB view details)

Uploaded Python 3

File details

Details for the file tualpha-0.7.1.tar.gz.

File metadata

  • Download URL: tualpha-0.7.1.tar.gz
  • Upload date:
  • Size: 87.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for tualpha-0.7.1.tar.gz
Algorithm Hash digest
SHA256 55e86e891db4fb26e8cf6ea45e31bbab238d3273e72c44080de847ae985ec44c
MD5 1df7a6c5071ce49d999f1b6068133a3f
BLAKE2b-256 e06b8663ea4ca64df4945aabafee572b582bd9bcf1f3b9ba98d7be3dd33b8620

See more details on using hashes here.

File details

Details for the file tualpha-0.7.1-py3-none-any.whl.

File metadata

  • Download URL: tualpha-0.7.1-py3-none-any.whl
  • Upload date:
  • Size: 96.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for tualpha-0.7.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e28bb7387f27d2e89a6305555efb9ee48c199ecd3109881b31225495852f6976
MD5 094952ed8df2b563822cc479d423dfac
BLAKE2b-256 5110cdb02e95e1761159056795a5996fd3709a23f629ffcd724365819d4c8d42

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.0

2 files

1.4.1

2 files

1.4.0

2 files

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.3.0

2 files

1.0.0

2 files

0.8.1

2 files

0.8.0

2 files

This release

0.7.1 This release

2 files

0.7.0

2 files

0.6.1

2 files

0.6.0

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page