Files
gavindiazandClaude Code bd2253af01 docs: sync trading/research docs and add the risk-library mapping
Root README now points at the auto-trading shell and research pipeline;
AGENTS.md gains a keep-docs-in-sync rule; trading/README.md maps every method
from the two research docs to trading/risk and documents manage_position;
research/README.md documents the backtest hook and --backtest CLI.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-25 20:21:46 +08:00
..

trading/ —— 自动交易执行壳(你的代码)

⚠️ 这是你的执行壳骨架,不是策略,也不构成投资建议。它包装 mtdata-cli 作为子进程,不 import 任何 mtdata 内部模块,因此不会被上游同步覆盖。

本目录实现自动交易所需的四个核心机制:

┌──────────────────────────────────────────────────────────┐
│  signal_loop     信号循环:每个周期取行情 → 问策略 → 执行 │
│  state           状态机:IDLE→SIGNALED→PLACED→CLOSED      │
│  circuit_breaker 熔断器:连续失败 N 次后停止交易          │
│  dry-run 分层     preview → dry_run → demo → live         │
└──────────────────────────────────────────────────────────┘

快速上手

# 默认 dry-run,安全。单跑一个周期看看会发生什么:
py -3.14 -m trading --once -v

# 持续轮询(默认不会发任何真实订单):
py -3.14 -m trading

# 强制预览层,验证当前策略/参数组合:
py -3.14 -m trading --once --tier preview

第一个周期会:查询账户 → 查持仓 → 拉 K 线 → 问策略 → 记录到 journal.jsonl。默认策略(strategies/example_strategy.py)被禁用, 不会产生任何信号。

安全层级(dry-run 分层)

TRADING_TIER 控制能触达的账户层级,只升不降:

层级 行为 何时用
preview 连 CLI 都不发送真实请求语义,仅记录"将要发生什么" 开发
dry_run(默认) 所有交易命令带 --dry-run true 演练 / CI
demo 允许真实发送,但仅当登录的是模拟账户(自动校验) 模拟盘
live 允许真实发送,且要求每次调用显式 send_live=True 实盘

即使到 live,执行壳默认仍然 send_live=False(只预览);真实下单必须 同时满足:层级够 + 显式开启。这是最后的防呆。

环境变量

变量 默认 说明
TRADING_TIER dry_run preview / dry_run / demo / live
TRADING_SYMBOL EURUSD 交易品种
TRADING_TIMEFRAME H1 K 线周期
TRADING_POLL_SECONDS 60 轮询间隔(秒)
TRADING_MAX_LOTS 0.01 回退手数:风险计算不可用时使用(也是历史上单一的上限)
TRADING_RISK_PCT 0.5 每笔风险占权益百分比;策略不显式给 volume 时据此计算手数
TRADING_MAX_OPEN_POSITIONS 1 单品种最大持仓数
TRADING_MAGIC 0 订单 magic,用于与策略分组
TRADING_COMMENT mtdata-shell 订单注释
TRADING_STRATEGY strategies.example_strategy 策略模块路径
TRADING_ENABLE_EXAMPLE_SIGNALS 0 设为 1 才允许示例策略发信号
TRADING_INDICATORS rsi,ema 拉 K 线时附带的指标
TRADING_GUARD_DAILY_LOSS_PCT (关) 当日亏损占日初余额百分比阈值
TRADING_GUARD_DRAWDOWN_PCT (关) 相对当日权益峰值回撤阈值
TRADING_GUARD_MAX_POSITIONS (关) 全局最大持仓数上限
TRADING_GUARD_MAX_DAILY_TRADES (关) 每日最多开仓笔数
TRADING_GUARD_COOLDOWN_SECONDS (关) 平仓后禁止新开的冷却秒数
TRADING_GUARD_SESSION (关) 允许交易时段,如 09:30-16:00
TRADING_GUARD_MAX_SPREAD_PCT (关) 点差超过价格百分比时拒绝入场
MTDATA_CLI mtdata-cli CLI 路径(或可执行文件全路径)
TRADING_STATE_FILE state.json 状态机持久化文件
TRADING_JOURNAL_FILE journal.jsonl 追加式审计日志

文件地图

文件 职责
main.py 入口:--once / 轮询 / --tier 覆盖
config.py 环境变量 → ExecutionConfig(frozen dataclass)
signal_loop.py 主循环 + 策略加载 + 持仓管理 pass + 执行编排
state.py 交易周期状态机,崩溃后可恢复
circuit_breaker.py 连续失败熔断(CLOSED→OPEN→HALF_OPEN)
executor.py mtdata-cli 子进程包装 + dry-run 分层 + 护栏解析
journal.py 追加式 JSONL 审计日志
types.py TradeSignal / SignalContext 共享类型
risk/broker.py 品种规格 SymbolSpec:pip/point、手数取整、止损距离规范
risk/sizing.py 风险百分比手数计算 + 回退入口 suggest_volume
risk/exits.py 出场规则库:保本、经典/ATR/吊灯/均线移动止损、时间止损、分批平仓
risk/guards.py 账户级入场闸门(日亏损/回撤/持仓数/冷却/时段/点差),默认全关
risk/indicators.py 均线/RSI/ATR 等纯 numpy 指标(策略内部共用)
strategies/ 策略模块(signal(ctx) 接口)

写自己的策略

任何模块只要有 signal(ctx) -> TradeSignal | None 即可,然后:

TRADING_STRATEGY=my_package.my_strategy py -3.14 -m trading --once -v

研究侧的模型(见 research/)最终也是通过这个接口进入执行壳:训练好的 模型在 signal() 里对最新特征做推理,返回 TradeSignal。

ctx(SignalContext,见 types.py)提供一次决策需要的全部输入: symbol / timeframe / candles(OHLCV 列表,ctx.closes() 取收盘价)/ quote / account / positions(当前持仓)/ as_of。ctx.symbol_spec 是 symbols_describe 的 best-effort 载荷,策略可用 trading.risk.broker.SymbolSpec.from_payload 解析成规格(pip / 手数步长 / 最小止损距离等),再配合 risk/sizing.py 的 suggest_volume 自算带止损的手数。

TradeSignal.volume 留空时,循环按风险百分比定手数(见上表 TRADING_RISK_PCT);显式给 volume 则始终优先(权威顺序: 显式 volume > 风险百分比 > TRADING_MAX_LOTS 回退)。

持仓管理(可选)

有持仓时,循环每周期先调用策略的 manage_position(ctx, position) 再做 开仓决策;离线回测器在同一根 K 线上也调用它 —— 出场决策两侧共用同一份。 返回 None 或 PositionAction("hold") = 不动(持仓继续由服务器端挂单 SL/TP 管理)。要主动管理时返回(PositionAction 见 types.py):

  • PositionAction("modify", stop_loss=..., take_profit=...) → 移损 / 改止盈; trading/risk/exits.py 的 update_breakeven、update_classic_trailing、 evaluate_exits 等会直接产出这些参数;
  • PositionAction("close", reason=...) → 全部平仓;
  • PositionAction("partial_close", close_volume=..., reason=...) → 平掉 close_volume 手,剩余继续持有。

实盘由 Executor.modify / close(dry-run 分层)执行;回测按同一语义模拟 (research/backtest.py 的 apply_position_action)。策略不实现该方法 = 不做任何动态出场,行为与旧版完全一致。管理动作失败只会记日志、熔断记数, 不会中断循环。

风控库:两份梳理文档的抽象落点

把根目录的《止损方法梳理与伪代码》《仓位管理方法梳理》抽象进了 trading/risk/(不保证 100% 保留,重点是留下可复用、可组合的积木):

文档里的方法 落在哪里 说明
pip/point 规则(3/5 位小数) risk/broker.py SymbolSpec.pip 所有距离换算的单一来源
手数归一化(向下取整、低于最小手数归零、封顶) risk/broker.py normalize_volume 一次性收口佣金/点差外的手数规则
止损距离规范(stop level) risk/broker.py min_stop_distance 防"止损贴太近"被拒单
风险百分比 → 手数 risk/sizing.py size_lot 公式来自文档;不可算时明说原因
R 冻结在开仓价(|开仓−初始止损|) risk/exits.py initial_risk / r_multiple 后续移损不改变 R
保本止损 risk/exits.py update_breakeven 支持正向偏移,幂等
经典/ATR/吊灯/均线移动止损 risk/exits.py update_classic_trailing / update_atr_trailing / update_chandelier / update_ma_trailing 都遵守"只许收紧"的单调不变量
时间止损 risk/exits.py time_stop_exceeded 支持时长与K线数两种口径
分批平仓 risk/exits.py plan_partial_close 剩余仓位必须 ≥ 最小手数
组合优先级(硬出场 > 分批 > 移损) risk/exits.py evaluate_exits 一个出口函数折叠所有规则
日亏损/回撤/持仓数/冷却/时段/点差闸门 risk/guards.py 环境变量武装,默认全关
指标(均线/RSI/ATR) risk/indicators.py 策略侧 strategies/indicators.py 已改为转出这些名字

暂缓、未纳入(策略空间,留给你的研究决定何时接):网格 / DCA / 马丁格尔 / 回撤恢复区 / 对冲 / 新闻停损。它们需要独立的策略语义,不适合 在风控库层面预设行为。

实盘前必读

切换 TRADING_TIER=live 之前,请完整核对 docs/LIVE_TRADING_CHECKLIST.md (护栏、幂等、演练、首笔小额、运维清单),并阅读 docs/TRADING_SAFETY.md。