RiskManager `peak_equity` 文件持久化简化设计

RiskManager peak_equity 文件持久化简化设计

1. 背景与现状

1.1 业务用途

  • peak_equity:用于 RiskManager最大回撤检查
  • 回撤公式:drawdown = (peak_equity - account_value) / peak_equity,并与 config.max_drawdown_pct 比较。
  • 只有当 account_value 低于 peak_equity 时才会触发回撤超限检查。

1.2 现有实现(与本次优化相关)

文件:src/trading/risk_manager.py

  • 文件级常量:
    • _PEAK_EQUITY_FILE = Path(__file__).parent.parent.parent / ".peak_equity"
  • 初始化:
    • self._peak_equity: float = self._load_peak_equity()
  • 启动时恢复:
    • _load_peak_equity():如 .peak_equity 存在,则读取 JSON,并解析 peak_equity 字段。
    • 成功时日志:"从文件恢复 peak_equity: $xxx.xx"
    • 失败时记录 warning,并回退到默认值 0.0
  • 运行时持久化:
    • _save_peak_equity(self, value)
      • 使用 tempfile.mkstemp_PEAK_EQUITY_FILE.parent 创建临时文件。
      • 写入 {"peak_equity": value} 的 JSON。
      • 通过 os.replace 原子替换 _PEAK_EQUITY_FILE
      • 出错时尝试删除临时文件并记录 warning。
    • update_peak_equity(self, account_value)
      • account_value > self._peak_equity
        • 更新内存中的 self._peak_equity
        • 调用 _save_peak_equity(account_value) 写入文件。
  • 最大回撤检查:
    • _check_max_drawdown(self, account_value)
      • self._peak_equity <= 0
        • account_value <= 0:记录错误并拒绝交易。
        • 否则:self._peak_equity = account_value,将当前权益视为初始峰值。
      • account_value > self._peak_equity:更新内存中的 self._peak_equity
      • 计算回撤比例 (self._peak_equity - account_value) / self._peak_equity,与 self._config.max_drawdown_pct 比较。

关键点:回撤风控本身只依赖进程内存中的 _peak_equity,文件持久化只是为其提供“跨重启初始化值”的额外路径。


2. 设计目标与原则

2.1 设计目标

  • peak_equity 实现收敛为单一内存状态,不再依赖 .peak_equity 文件。
  • 明确语义:最大回撤约束基于“本次进程生命周期内的最大账户权益”。
  • 移除与该持久化相关的 IO、异常处理和隐含状态,降低理解与排错成本。

2.2 设计原则(复杂度优先)

  • 只做减法:删除不必要的路径和状态来源,不增加新抽象层/配置项。
  • 无历史兼容层:不保留“若旧文件存在则……”之类的兼容逻辑。
  • 单一事实来源_peak_equity 仅由当前进程的业务流程维护,不从外部文件反向注入。
  • 行为语义显性化:文档中明确说明“最大回撤只针对本次进程生命周期”,避免未来隐式依赖“跨重启历史峰值”。

3. 目标行为(优化后的语义)

3.1 启动阶段

  • RiskManager 初始化时:
    • self._peak_equity 设为 0.0
  • 首次调用 _check_max_drawdown(account_value) 时:
    • self._peak_equity <= 0account_value > 0
      • self._peak_equity 设置为 account_value,作为本次进程的“初始峰值”。
    • account_value <= 0:视为异常数据,保持现有拒绝交易与日志行为。

3.2 运行阶段

  • 每次开仓前:
    • pre_trade_check 仍调用 _check_max_drawdown(account_value)
    • account_value > self._peak_equity
      • 更新内存中的 self._peak_equity 为新的高点。
    • 否则:
      • (self._peak_equity - account_value) / self._peak_equity 计算回撤比例,并与配置上限比较。
  • 不再有任何对 .peak_equity 的读写或相关日志。

3.3 重启后

  • 上一进程的峰值不再被恢复。
  • 新进程会在首次有效 account_value 时建立自己的起点峰值。
  • 最大回撤含义变为:“自本次进程启动以来的最大回撤”

4. 详细代码变更设计

4.1 删除文件持久化相关逻辑

文件:src/trading/risk_manager.py

删除内容:

  • 常量:
    • _PEAK_EQUITY_FILE = Path(__file__).parent.parent.parent / ".peak_equity"
  • 方法:
    • _load_peak_equity()
    • _save_peak_equity(self, value: float)
  • 其他:
    • Pathostempfile 等 import 仅被上述逻辑使用,则一并删除。
    • 删除所有包含 "从文件恢复 peak_equity""加载 peak_equity 文件失败""保存 peak_equity 文件失败" 的日志调用。

效果:

  • RiskManager 不再产生或依赖 .peak_equity 文件。
  • 启动路径和运行路径中均不再包含磁盘 IO 与相关异常分支。

4.2 简化 _peak_equity 初始化

  • 原实现:
    • self._peak_equity: float = self._load_peak_equity()
  • 新实现:
    • self._peak_equity: float = 0.0

理由:

  • _check_max_drawdown 已经在 self._peak_equity <= 0 时,用当前 account_value 建立初始峰值。
  • 将初始化设为 0.0 即可触发该逻辑,无需额外分支。

4.3 收敛 update_peak_equity 为纯内存更新

  • 原实现(逻辑):
    • account_value > self._peak_equity
      • 更新内存中的 self._peak_equity
      • 调用 _save_peak_equity(account_value) 写文件。
  • 新实现:
    • 保留加锁与“创新高则更新”的判断;
    • 删除对 _save_peak_equity 的调用,仅更新内存。

语义:

  • update_peak_equity 成为维护内存峰值的纯函数式副作用(只改内存,不做 IO)。

4.4 保持 _check_max_drawdown 逻辑不变

  • 保持以下行为:
    • self._peak_equity <= 0
      • account_value <= 0:视为无效数据,记录错误并拒绝交易。
      • account_value > 0:使用当前账户权益初始化峰值。
    • account_value > self._peak_equity
      • 更新内存中的峰值。
    • 否则:
      • 按当前峰值计算回撤比例并与 max_drawdown_pct 比较。

原因:

  • 该逻辑是业务规则本身,与持久化方式无关,优化目标仅是移除文件持久化带来的额外复杂度。

5. 行为变化与兼容性分析

5.1 行为差异

  • 旧行为
    • .peak_equity 存在且有效,重启后 _peak_equity 从文件中恢复。
    • 最大回撤始终相对于“历史最高权益”(可能跨多个进程)。
  • 新行为
    • _peak_equity 在每个进程生命周期内独立维护。
    • 重启后重新从首次有效 account_value 建立峰值。

结论:

  • 新版本的回撤约束强度:对单次进程运行严格,对跨重启放宽。
  • 换取的是:实现和运维心智明显简化。

5.2 兼容性与风险

  • 若现在的监控、报警或人工流程隐式依赖“跨重启回撤连续性”,新行为可能与其预期不符。
  • 本次改动不增加任何配置开关、迁移逻辑或回退逻辑,保证代码路径单一、易于理解。
  • .peak_equity 文件如仍存在:
    • 运行时不会再被访问。
    • 可在人工确认后,由运维手工删除。

6. 测试方案

6.1 单元级测试

建议覆盖以下场景:

  1. 初始峰值建立

    • 给定:_peak_equity = 0.0account_value = 1000.0
    • 调用 _check_max_drawdown(1000.0)
    • 期望:
      • _peak_equity 更新为 1000.0
      • 返回 True(未超限)。
  2. 创新高更新

    • 初始:_peak_equity = 1000.0
    • 调用 update_peak_equity(1200.0)
    • 期望:
      • _peak_equity 更新为 1200.0
      • 无任何文件 IO 发生。
  3. 回撤在限制内

    • 初始:_peak_equity = 1000.0max_drawdown_pct = 0.2
    • 调用 _check_max_drawdown(900.0)
    • 回撤 10%,期望返回 True
  4. 回撤超限

    • 初始:_peak_equity = 1000.0max_drawdown_pct = 0.2
    • 调用 _check_max_drawdown(750.0)
    • 回撤 25%,期望返回 False,并由上层风控拒绝开仓。
  5. 重启后的行为

    • 模拟“历史上一度峰值 2000”这一事实不再从文件注入
    • 新进程第一次 _check_max_drawdown(1500.0)
      • 期望 _peak_equity 直接初始化为 1500.0,而不是 2000。

6.2 集成级测试

与 orchestrator 联动,验证:

  • 正常链路仍然会在获取账户权益后调用 update_peak_equity
  • 最大回撤超限时,仍能在日志与风控结果中看到对应的拒绝原因。
  • 日志中不再出现与 .peak_equity 文件相关的任何信息。

7. 后续扩展建议(仅在有强需求时考虑)

  • 如后续有明确写入的产品需求要求“最大回撤必须跨重启严格生效”,可在那时评估重新引入持久化方案:
    • 优先复用现有集中式存储(如 DB)的一行记录/一个键,而不是重新启用本地文件。
    • 仍然保持“单一实现路径”,避免再添加“文件 vs DB vs 内存”多选逻辑。
  • 在没有这类书面需求之前,保持当前简化后的内存实现,避免过早增加抽象和兼容层。

Read more

跑步的技巧(滚动落地)

“滚动落地(rolling contact / rolling foot strike)”不是一种教条式的“脚法”,而是一种 让冲击沿着整只脚、整条后链逐级传递的落地机制。 它的核心不是“你先用哪儿着地”,而是: 你的脚落地之后,冲击是不是像轮子一样滚过去,而不是像锤子一样砸下去。 这就是滚动落地的本质。 一、什么叫“滚动落地”? 你可以把它理解成两种完全不同的落地方式: 1. 砸地(撞击式) 脚像锤子一样拍到地上: * 要么后跟先砸 * 要么前掌先戳 * 冲击集中在一个点 * 一个结构瞬间吃掉大部分载荷 结果就是: * 后跟砸 → 膝盖难受 * 前掌戳 → 前脚掌磨烂 * 都不是长跑友好模式 这叫 撞击式着地(impact strike)。 2. 滚地(滚动式) 脚像轮胎一样“滚”过地面: * 不是某一点硬砸 * 而是外侧中足先轻触 * 再向前滚到前掌 * 最后从大脚趾蹬离

By SHI XIAOLONG

AMI的优越性

世界模型(World Models)的具体例子 如下,我按类型分类,便于理解。每类都附带实际实现、演示效果和应用场景。 1. Yann LeCun / Meta 的 JEPA 系列(最直接对应“世界模型”概念) 这些是 LeCun 主张的非生成式抽象预测世界模型代表。 * I-JEPA(Image JEPA,2023) 输入一张图像,模型把不同区域(context 和 target)编码成抽象表示,然后预测 target 的表示(不在像素级别重建)。 例子:给定一张遮挡了部分物体的图片,模型能预测“被遮挡物体的大致位置和属性”,构建对物体持久性和空间关系的理解。 这是一个“原始世界模型”,能学习物理常识(如物体不会凭空消失)。 * V-JEPA / V-JEPA 2(Video JEPA,

By SHI XIAOLONG

什么是:“世界模型(World Models)”

世界模型(World Models) 是人工智能领域的一个核心概念,尤其在 Yann LeCun 等研究者推动的下一代 AI 架构中占据中心位置。它指的是 AI 系统在内部构建的对现实世界的抽象模拟或内部表示,让机器能够像人类或动物一样“理解”物理世界、预测未来、规划行动。 简单比喻 想象你闭上眼睛也能“看到”房间里的物体会如何移动、碰撞或掉落——这就是你大脑里的世界模型。AI 的世界模型就是类似的“数字孪生”(digital twin)或“内部模拟器”:它不是简单记住数据,而是学习世界的动态、因果关系和物理直觉(如重力、物体持久性、遮挡、因果等)。 为什么需要世界模型? 当前主流的大型语言模型(LLM) 擅长处理文本(统计模式预测),但存在根本局限: * 缺乏对物理世界的真正理解 → 容易“幻觉”、无法可靠规划。 * 样本效率低 → 人类/

By SHI XIAOLONG

K线周期可配置化设计方案

K线周期可配置化设计方案 1. 背景与目标 当前 Beta 套利策略的 K 线周期硬编码为 "1h",分散在多个文件中。需要: 1. 将 K 线周期从 1h 改为 2h 2. 提取为环境变量 BETA_ARB_KLINE_INTERVAL,使其可在 .env 中配置 2. 影响范围分析 2.1 需要修改的文件(共 6 个) 文件 硬编码位置 修改内容 src/trading/config.py BetaArbConfig dataclass 新增 kline_interval 字段,

By SHI XIAOLONG