Broker / Portfolio / Reports 实施方案
本文档用于明确 zelqor 下一条主线能力的落地顺序:先做真实 portfolio,再做真实 broker,最后做完整 reports。
这三层不应该并行发散设计,而应按“账户真相源 -> 成交真相源 -> 结果汇总”的顺序推进。
当前落地状态:
portfolio第一阶段已开始落地engine.py已改为通过 portfolio 更新账户状态- summary 已开始基于 portfolio 真实状态输出
broker第一阶段已开始落地,买卖成交与拒单已具备结构化执行结果reports仍处于下一阶段
1. 为什么现在做这条线
当前仓库已经具备:
- 策略加载与统一运行入口
selector与stateful step两类策略接线run_backtest(...)/ CLI / safe API / task envelope- request / result / runtime / metadata 的基础宿主契约
但回测执行内核仍然偏“最小可运行”:
- 成交逻辑主要写在
engine.py Position还是最小模型reports仍以基础 summary 为主
这意味着当前系统已经能“结构化地跑”,但还没有形成真正可扩展的:
- 账户状态模型
- 成交流程模型
- 绩效分析模型
如果要继续向更真实的回测和未来模拟盘靠拢,收益最大的路径就是把这三层真正立起来。
2. 总体实施顺序
推荐顺序:
portfoliobrokerreports
不要反过来先做 reports,因为报表只是消费前两层结果。如果先做报表,后面 broker 或 portfolio 一旦调整,报表语义也要被迫重做。
3. 第一阶段:Portfolio 真实化
3.1 目标
把当前“cash + positions 的最小状态”升级成真正的组合状态中心。
3.2 当前问题
当前回测状态的核心问题是:
- 持仓对象过薄
- 已实现盈亏没有标准归属位置
- 未实现盈亏主要靠日末估值临时计算
- 现金、权益、持仓市值与交易结果之间还没有形成统一账户语义
- 后续如果要支持更复杂 broker 规则,会缺少稳定账户边界
3.3 推荐新增模型
建议至少补这些对象:
HoldingLotPortfolioStatePortfolioSnapshotClosedPositionRecord
建议职责如下:
HoldingLot- 表示一笔打开中的持仓批次
-
持有:
codeentry_dateplanned_exit_datequantityentry_priceentry_feecost_basis
-
PortfolioState - 作为账户真相源
-
持有:
cashholdingsrealized_pnlunrealized_pnlmarket_valueequity
-
PortfolioSnapshot - 表示某一交易日收盘后的账户快照
-
用于结果输出和报表计算
-
ClosedPositionRecord - 表示已完整平仓的一笔交易生命周期
- 记录:
- 买入日期
- 卖出日期
- 买入金额
- 卖出金额
- 总费用
- realized pnl
- 持有天数
3.4 Portfolio 阶段验收标准
完成这一阶段后,应满足:
- 账户资金与持仓价值有明确单一真相源
- 买入、卖出、日末估值都通过 portfolio 更新
BacktestResult可稳定拿到逐日账户快照- 报表层可以不直接读引擎内部临时变量
4. 第二阶段:Broker 真实化
4.1 目标
把“从池子直接变成交”的逻辑从 engine.py 中拆出来,形成独立执行层。
4.2 当前问题
当前成交逻辑虽然能跑,但还属于最小实现:
- 下单与撮合没有独立建模
- 成交失败原因没有统一表达
- 费用、滑点、可成交约束仍是过程式散落逻辑
- 未来如果支持更复杂规则,会继续堆在
engine.py
4.3 推荐新增模型
建议补这些对象:
OrderRequestOrderFillRejectedOrderBrokerExecutionResultBrokerConfig
建议职责如下:
OrderRequest- 表示策略或引擎发出的买卖请求
-
持有:
trade_datecodesidetarget_budget或requested_quantitytimingreason
-
OrderFill - 表示实际成交
-
持有:
- 成交价格
- 成交数量
- 金额
- 手续费
- 印花税
- 成交原因 / 成交路径
-
RejectedOrder - 表示未成交
-
原因例如:
- 现金不足
- 缺失 bar
- 数量不足一手
- 无法解析卖出日
- 不可成交约束
-
BrokerExecutionResult - 汇总某一交易日 broker 输出
-
包含:
- fills
- rejects
- warnings
-
BrokerConfig - 承载 broker 层执行规则
- 包括:
- timing
- slippage
- taxes
- board lot 规则
- limit up / limit down 可成交约束
4.4 优先支持的 broker 规则
第一版不需要追求完整撮合,但建议至少明确:
- 买入 / 卖出执行时点
- 滑点
- 手续费
- 印花税
- A 股按手取整
- 现金不足拒单
- 缺失 bar 拒单
- 涨跌停约束的最小版本
4.5 Broker 阶段验收标准
完成这一阶段后,应满足:
engine.py不再直接写大段成交细节- 每笔成交或拒单都有结构化结果
- portfolio 只接受 broker 的标准执行结果做账户更新
- future 模拟盘可以复用同一层 broker 语义
5. 第三阶段:Reports 完整化
5.1 目标
让报表从“最小 summary”升级成基于真实账户与交易生命周期的分析层。
5.2 推荐输出层次
建议分三层:
summary-
面向宿主和 CLI 的快速摘要
-
trade analytics -
面向单笔交易和已平仓交易的统计
-
daily analytics - 面向逐日权益与风险指标
5.3 推荐补充指标
建议优先补这些:
- 总收益率
- 最大回撤
- 胜率
- 盈亏比
- 平均单笔盈亏
- 平均持有天数
- 已实现收益
- 未实现收益
- 手续费总额
- 印花税总额
- 交易次数
- 买入次数
- 卖出次数
5.4 推荐新增结果对象
可以逐步新增:
TradeAnalyticsDailyAnalyticsReportBundle
第一版不一定都要暴露到公共 schema,但内部要先有明确 builder。
5.5 Reports 阶段验收标准
完成这一阶段后,应满足:
summary不再只依赖“equity curve + trades 粗算”- 交易统计来自标准 closed positions / fills
- 宿主与 CLI 可以稳定消费更完整的绩效指标
6. 推荐实施拆分
建议拆成三个连续迭代,而不是一次性重写:
6.1 Iteration A:Portfolio 重构
目标:
- 把账户状态抽出来
- 保持现有外部 API 尽量不变
范围:
backtest/portfolio.pybacktest/scheduler.pybacktest/engine.py- 相关模型与测试
6.2 Iteration B:Broker 重构
目标:
- 把执行过程从引擎里拆出来
- 给成交和拒单建立结构化结果
范围:
backtest/broker.pybacktest/engine.py- 相关模型与测试
6.3 Iteration C:Reports 升级
目标:
- 补更完整 summary 与 analytics
- 让结果基于 portfolio / broker 真实对象计算
范围:
backtest/reports.pymodels.py- 文档与 CLI 展示
7. 设计原则
推进这条线时,建议坚持:
- 先收口账户真相源,再扩执行层
- 先做结构化对象,再扩公开字段
- 保持 direct API / CLI / task envelope 的结果契约稳定
- 允许内部对象演进,但谨慎修改公共 schema
- 每一轮只推进一层主责任,不要三层一起重写
8. 风险与边界
主要风险:
- 过早把 broker 做得过于复杂,拖慢整体演进
- 在 portfolio 未稳定前就暴露太多新结果字段
- 直接重写
engine.py导致已有 selector / stateful step 链路回归
本阶段不建议立即做:
- 日内撮合
- 多资产支持
- 保证金 / 融资融券
- 公司行为复权重算
- 多账户并行
9. 推荐的第一步
真正开始写代码时,推荐从 portfolio 开始,先完成:
- 扩充持仓模型
- 建立账户级快照
- 把日末权益计算收敛到 portfolio
- 再让
engine.py改成通过 portfolio 更新状态
一句话总结:
portfolio是账户真相源,broker是成交真相源,reports是结果汇总层。
下一阶段应按这个顺序推进,而不是从报表或更复杂撮合开始。