Files
cryptoHermes/ai/adr/0001-architecture-foundations.md
dela cc7f5a4f32 feat: 初始化 cryptoHermes 行情网关 v1 MVP + harness 工程文档
Binance USDⓈ-M Futures 行情网关,给上游 Hermes 量化分析引擎提供单一聚合
接口 /v1/market/context(K 线 + funding + OI + 多空比 + taker volume)。
只读公共行情,不下单、不接私钥、不查账户。

## v1 实现范围(Milestone 1-5)

- Clean Architecture 4 层(controller/usecase/repo/entity),接口边界在
  internal/usecase/ports.go
- Binance Futures REST client(K 线 / ticker24h / funding / OI / 多空比
  / taker volume 共 9 个接口),全链路 string 价格避免 float64 精度问题
- TimescaleDB 5 张 hypertable(market_klines / funding_rates /
  open_interest / long_short_ratio / taker_buy_sell_volume),主键含
  时间维度,UpsertMany 幂等
- robfig/cron 定时采集(15m/1h/4h/1d/1w 多周期 K 线 + 衍生品 15 分钟
  落库),未收线 K 线 (close_time > now) 由 mapper/repo 双重过滤
- pkg/httpclient 统一限流(默认 20 req/s, burst 40)+ 重试,避免触发
  Binance 2400 weight/min IP 上限
- /v1/market/context 聚合接口:errgroup 并发拉 snapshot/funding/OI,DB
  K 线不足 200 根回源 Binance 异步补
- cmd/backfill CLI 支持指定 from/to 大段回填(Binance 历史 OI / 多空比
  官方只保留 30 天,必须自己存)
- Docker Compose + Makefile + golang-migrate,本地一键启

技术指标(support/resistance/Vegas/箱体)留待 v2,技术段返回空对象 +
warning 占位。

## Harness 工程文档

- AGENTS.md — AI agent 工作速查(10 个章节)
- ai/project-map.md — 仓库结构、扩展点、控制流
- ai/risk-guardrails.md — G1-G10 守卫规则(每条带可机械验证命令)
- ai/adr/0001-architecture-foundations.md — 9 条架构基础决策
- ai/task-templates.md — 6 种任务契约模板
- ai/harness-health.md — 当前 harness 健康度评估

3 个 grep 守卫已验证通过:controller / usecase 无具体实现依赖,全项目
无私钥/签名字段。
2026-05-24 17:20:51 +08:00

7.0 KiB
Raw Permalink Blame History

ADR 0001 — 架构基础决策

状态Accepted 日期2026-05-24 决策者:项目维护者 适用范围cryptoHermes v1 全部代码


背景

cryptoHermes 是给上游 Hermes 量化分析引擎用的 Binance Futures 行情网关。在 MVP 启动前需要锁定一组基础技术决策,避免后续 PR 反复重新评估。

需求边界(来自 docs/dev.md 第 1 章):

  • 只读公共行情,不下单、不接私钥
  • BTCUSDT / ETHUSDT分钟到周级 K 线 + 衍生品funding / OI / 多空比 / taker volume
  • 历史数据必须自存Binance 历史 OI / 多空比官方只保留 30 天)
  • Hermes 只调用 /v1/market/context 一个聚合接口
  • 部署形态:单体服务 + Postgres国内开发机通过 HTTPS_PROXY 访问 Binance

决策

D1. Web 框架Fiber v2

选了 Fiber v2(而不是 net/http 标准库、Gin、Echo、Chi

理由

  • 性能足够fasthttp 底层),单体网关不需要再调优
  • API 与 Express/Gin 相近,团队上手快
  • 内置 recover/logger/req-id 中间件
  • v2 稳定,社区活跃

代价

  • 基于 fasthttp不兼容 net/http 中间件生态
  • 错误类型用 *fiber.Error,需要在 controller 包一层

何时重新评估:如果需要 HTTP/2 server push、需要复用 net/http 生态中间件、或者 v2 长期不维护——届时考虑迁回标准库。


D2. 数据库TimescaleDB on PG 16

选了 TimescaleDB(而不是纯 Postgres、InfluxDB、ClickHouse

理由

  • 时序数据天然适合 hypertable5 张表全部按时间分片,查询近期数据走最新 chunk旧数据自动归档
  • SQL 接口和普通 PG 完全兼容pgx 直连,无需新驱动
  • 没有引入新的运维栈(开发机用 Docker 起 timescale/timescaledb:latest-pg16 即可)
  • Chunk 间隔可按表调K 线 1 周、funding 30 天)

代价

  • 必须跑 SELECT create_hypertable(...) 才能享受时序优化,新表容易忘
  • 升级 PG 大版本需要看 Timescale 兼容矩阵

何时重新评估:如果数据量进入 TB 级、需要列存压缩或者 OLAP 复杂聚合——考虑 ClickHouse。


D3. ArchitectureClean Architecture4 层 + ports.go 接口边界)

4 层controller / usecase / repo / entity接口边界internal/usecase/ports.go 是唯一定义对外接口的地方。

理由

  • 上游 Hermes 只通过 REST 调用本服务,但 Binance 这个数据源大概率会扩展到 CoinGlass / ETF / CME。把数据源抽象成 MarketDataProvider / DerivativesProvider 接口,新增数据源只需要新建 internal/repo/webapi/<source>/,不动业务编排。
  • 单测可以 mock 接口,不需要起真 DB / 真 Binance。
  • internal/app/app.go 是唯一的组合根DI 集中可见。

强制约束(见 ai/risk-guardrails.md G6

controller 不可直接 import binance/postgres
usecase 不可直接 import binance/postgres

代价

  • 多一层 ports.go,每个新接口要写两次(接口 + 实现)
  • 对小型项目可能略 overkill但本项目已经规划到 v3多数据源早做减少返工

何时重新评估:如果 v2 之后接口数量爆炸(> 30 个)→ 考虑按子域拆分 ports.go 为多个文件。


D4. 传输层REST-onlyv1 不上 gRPC / 消息队列 / WebSocket

选了 REST(而不是 gRPC、NATS、WS 推送)。

理由

  • Hermes 是同语言 Go 服务,但跨服务调用频率是分钟级(拿一次 contextREST + JSON 完全够
  • gRPC 多一份 proto 维护负担,团队/工具收益不明显
  • WebSocket 推送是独立工程量重连、消息丢失、订阅状态机v1 不进入
  • Cron + REST 拉取模型对运维友好(任何 HTTP 监控就够)

代价

  • 如果未来 Hermes 需要 sub-second 行情REST 拉模型撑不住

何时重新评估Hermes 接入实盘策略后,如果发现"分钟级"延迟成为瓶颈 → 写新 ADR 引入 WS。


D5. 价格 / 成交量:全链路 string

所有金额字段端到端用 stringBinance DTO 是 string、entity 是 string、JSON 响应是 string。落库时由 PG 转 NUMERIC(36,18)

理由

  • Binance 原样返回字符串,避免 float64 转换丢精度
  • 108000.12 这种数 float64 表示就会丢尾数;累积统计后偏差更大
  • 上游量化分析对价格精度敏感,宁可让 Hermes 自己决定怎么解析

代价

  • 加减乘除需要 PG 端做或者引入 decimal 库
  • JSON 响应里 price 是字符串,前端/客户端要适配

强制约束:见 ai/risk-guardrails.md G2。


D6. 数据源边界:只用 Binance USDⓈ-M Futures 公共 API

v1 不引入 CoinGlass、Glassnode、ETF Flow、CME、链上数据

理由

  • Binance 一家覆盖 80% 的需求K 线 / 衍生品 / 多空比)
  • 引入新数据源等于引入新的鉴权、限流、错误模式、SLA——v1 优先把一条链路打通
  • 路线图 v2 / v3 才扩展数据源README "路线图" 段)

强制约束

  • 新增数据源前必须先写 ai/specs/<source>.md(见 AGENTS.md §10
  • 不可使用 Binance 的私有签名接口(见 G1

D7. 配置层cleanenvyaml + env override

选了 cleanenv(而不是 viper、koanf、env-only

理由

  • 一份 yaml 默认值 + 环境变量覆盖,开发和部署都方便
  • 比 viper 轻,没有多余抽象
  • env-default: tag 让默认值跟字段定义放在一起,可读性好

代价:无明显缺点。如果后续需要热重载(不需要)才会考虑换。


D8. 限流:golang.org/x/time/rate token bucket

挂在 pkg/httpclient/client.go 里,默认 RPS=20 / Burst=40从配置读。

理由

  • 标准库附属,零外部依赖
  • token bucket 适合突发场景
  • Binance IP 限 2400 weight/min20 req/s × 60s = 1200 req/min留出一半余量给重试和 backfill

强制约束:所有外部 HTTP 调用走 pkg/httpclient,见 G5。


D9. Schedulerrobfig/cron v3

选了 robfig/cron v3(而不是自己写 ticker、go-co-op/gocron

理由

  • cron 表达式所有人都看得懂schedule 修改是配置而非代码
  • 稳定老牌3.0 之后无破坏性变化
  • 和 Fiber 在同一进程内运行graceful shutdown 容易做

代价

  • 没有分布式锁——但本服务 v1 是单实例,不需要

不在本 ADR 范围

  • 下单 / 账户 / 私钥永久禁止G1。要做必须新仓库。
  • 多交易所聚合v3 才考虑,届时再写 ADR。
  • WS / 实时推送:见 D4需要单独写 ADR。
  • 观测性metrics / tracingMVP 阶段只用 slog 结构化日志Prometheus / OTel 留待生产部署前评估。

后续 ADR 的触发条件(来自 AGENTS.md §10

  • 换 HTTP 框架 → 新 ADR
  • 引入消息队列 / WS → 新 ADR
  • 跨服务通信改 gRPC → 新 ADR
  • 数据库换型PG → ClickHouse→ 新 ADR
  • 引入新数据源 → ai/specs/<source>.md(不一定要 ADR除非影响整体架构