Files
cryptoHermes/ai/adr/0002-indicator-numeric-boundary.md
dela fa769331c2 feat(market): Phase 1 扩展 — 更多 symbol + Bollinger/Vegas + 衍生品信号
- collector.symbols 与 supportedSymbols 加 SOL/BNB/DOGE,env-default 与 README 同步
- entity 追加 TechnicalStructure.Intervals(每周期 Bollinger + Vegas,omitempty 不破坏既有字段)与 DerivativesBundle.Signal
- 新增纯解析 usecase derivatives_signal.go:fundingBias 绝对阈值、oiSignal 用 P20 baseline + 价格方向(OI up + 价格 up/down → long/short_building,funding 不参与方向)、lsrRegime 绝对阈值;signal 数据不足走 warnings 进 DataQuality
- indicator.go 加 sma/stddev/ema/bollinger/vegas + computeIntervalTechnicals;IndicatorComputer 签名收 klines map
- harness 同步:G12 扩入 derivatives_signal.go(含 Makefile G12.5/6 + ADR-0002 适用范围 + project-map / harness-health 更新)
2026-05-24 23:19:57 +08:00

4.6 KiB
Raw Permalink Blame History

ADR 0002 — 指标计算的数值边界

状态Accepted 日期2026-05-24 决策者:项目维护者 适用范围internal/usecase/indicator.gointernal/usecase/derivatives_signal.go 及其后续同形态的纯解析 usecase 实现


背景

Milestone 6 引入技术指标计算pivot S/R、percentile range、LSR 穿越中位数)。这些算法需要:

  • 浮点排序percentile、median
  • 浮点比较cluster threshold 0.3%
  • 线性插值percentile interpolation、LSR 穿越价位)

entity.Kline.High / Low / Closeentity.LongShortRatio.Ratio 都是 string(见 ADR-0001 D5 / 守卫 G2——全链路 string 保留 Binance 原始精度。

问题:指标内部要做数学运算,但项目硬规则禁止 float64 表示价格。如何调和?

候选方案:

  1. shopspring/decimal — 任意精度十进制Go 量化生态事实标准
  2. math/big.Float — stdlib任意精度二进制
  3. strconv.ParseFloat + transient float64 — stdlibIEEE-754 binary64

决策

方案 3strconv + transient float64输出前 FormatFloat 转回 string

边界规则:

  • float64 出现在 internal/usecase/indicator.gointernal/usecase/derivatives_signal.go 等"纯解析 usecase 文件"内部
  • 进入任何 entity 字段TechnicalLevel / TechnicalStructure / DerivativesSignal 等)前必须 strconv.FormatFloat(f, 'f', -1, 64) 转回 string类别型字段crowded_long)用 string 字面量
  • entity / DTO / persistence / transport 全部保持 string不变
  • 任何其它 usecase 文件出现 float64 视为违规
  • 新增同形态文件时:必须同步扩 Makefile 的 G12.1 白名单与 G12.5/6 子规则

这是 ai/risk-guardrails.md G2 例外条款"纯统计/分析(如指标计算的中间过程)可以用 float64但结果落库或返回 API 时必须转回 string")的具体落地。守卫 G12 把这条例外细化成可机械验证的命令。


理由

精度足够

BTC/ETH USDⓈ-M Futures 报价上限 ≈ 1e6 USD。

IEEE-754 binary64 在该量级下:

  • 显著位数 ~15.95 decimal digits
  • 相对误差 < 2.22e-16
  • 绝对误差 < 1e-9 USD

而 Binance 价格步长tickSizeBTCUSDT 0.10、ETHUSDT 0.01。float64 累积误差比 tickSize 小至少 7 个数量级。指标输出support price、percentile的有效位数远不到这个程度。

不引入 direct dep

shopspring/decimal 是稳定的好库,但:

  • 当前 go.mod 没有它,引入 = direct dep + 间接依赖链增长
  • 排序 / percentile / median 在 decimal 上写起来要构造 sort.Interface、手写比较啰嗦
  • benchmark 上 decimal 加减乘除比 float64 慢 ~10-30x排序整段 N=300 K 线大概累计差几十微秒——非热路径,但累积可见

math/big.Float 比 decimal 更难写(没有 Less / Cmp 的舒适接口),且十进制⇄二进制反复转换本身就有"看起来对但实际错位"的风险。

G2 已预留例外

守卫 G2 原文risk-guardrails.md:43

例外:纯统计/分析(如指标计算的中间过程)可以用 float64结果落库或返回 API 时必须转回 string

本 ADR 并非破坏既有规则,而是把这条已写好的例外具体化、可验证化


代价

  • 指标内部计算精度严格依赖 IEEE-754 行为;如果未来要做"百万级订单簿累加成交价"这类对绝对精度敏感的统计,本决策需重新评估。
  • 任何走出 indicator.go 的 float 值都是 bug依赖 G12 grep + code review 双重把关。
  • transient float64 → string 转回时 FormatFloat(_, 'f', -1, 64) 会给出最短可往返表示。对于 0.300000 这种 Binance 原样字符串经一次解析+格式化后可能变成 0.3——这点要在 indicator 测试中明确容忍,并在 ADR-0001 D5全链路 string之外形成补充indicator 输出的 string 是"计算结果 string",不一定字符等于任何输入 string。

何时重新评估

  • 指标扩展到累加型VWAP 总和、OI 加权累计)且数据点 ≥ 1e8 级别 → 评估 Kahan 求和或上 decimal
  • 出现"测试断言失败但人眼看着正确"的浮点尾差 case → 升级前先尝试调整测试容差,无法解决再换 decimal
  • 任何对外公布的"指标精度承诺"高于 1e-6 → 当前方案不够,必须换 decimal

与其它决策的关系

  • ADR-0001 D5全链路 string:本 ADR 是其在指标计算场景的具体例外说明,不冲突
  • 守卫 G2:本 ADR 是 G2 例外条款的落地
  • 守卫 G12本次新增:本 ADR 的可机械验证形式