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 无具体实现依赖,全项目 无私钥/签名字段。
This commit is contained in:
189
ai/adr/0001-architecture-foundations.md
Normal file
189
ai/adr/0001-architecture-foundations.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# 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)。
|
||||
|
||||
**理由**:
|
||||
- 时序数据天然适合 hypertable:5 张表全部按时间分片,查询近期数据走最新 chunk,旧数据自动归档
|
||||
- SQL 接口和普通 PG 完全兼容,pgx 直连,无需新驱动
|
||||
- 没有引入新的运维栈(开发机用 Docker 起 `timescale/timescaledb:latest-pg16` 即可)
|
||||
- Chunk 间隔可按表调(K 线 1 周、funding 30 天)
|
||||
|
||||
**代价**:
|
||||
- 必须跑 `SELECT create_hypertable(...)` 才能享受时序优化,新表容易忘
|
||||
- 升级 PG 大版本需要看 Timescale 兼容矩阵
|
||||
|
||||
**何时重新评估**:如果数据量进入 TB 级、需要列存压缩或者 OLAP 复杂聚合——考虑 ClickHouse。
|
||||
|
||||
---
|
||||
|
||||
### D3. Architecture:Clean Architecture(4 层 + 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-only(v1 不上 gRPC / 消息队列 / WebSocket)
|
||||
|
||||
**选了 REST**(而不是 gRPC、NATS、WS 推送)。
|
||||
|
||||
**理由**:
|
||||
- Hermes 是同语言 Go 服务,但跨服务调用频率是分钟级(拿一次 context),REST + JSON 完全够
|
||||
- gRPC 多一份 proto 维护负担,团队/工具收益不明显
|
||||
- WebSocket 推送是独立工程量(重连、消息丢失、订阅状态机),v1 不进入
|
||||
- Cron + REST 拉取模型对运维友好(任何 HTTP 监控就够)
|
||||
|
||||
**代价**:
|
||||
- 如果未来 Hermes 需要 sub-second 行情,REST 拉模型撑不住
|
||||
|
||||
**何时重新评估**:Hermes 接入实盘策略后,如果发现"分钟级"延迟成为瓶颈 → 写新 ADR 引入 WS。
|
||||
|
||||
---
|
||||
|
||||
### D5. 价格 / 成交量:全链路 `string`
|
||||
|
||||
**所有金额字段端到端用 `string`**:Binance 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. 配置层:cleanenv(yaml + 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/min,20 req/s × 60s = 1200 req/min,留出一半余量给重试和 backfill
|
||||
|
||||
**强制约束**:所有外部 HTTP 调用走 `pkg/httpclient`,见 G5。
|
||||
|
||||
---
|
||||
|
||||
### D9. Scheduler:robfig/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 / tracing)**:MVP 阶段只用 slog 结构化日志,Prometheus / OTel 留待生产部署前评估。
|
||||
|
||||
---
|
||||
|
||||
## 后续 ADR 的触发条件(来自 AGENTS.md §10)
|
||||
|
||||
- 换 HTTP 框架 → 新 ADR
|
||||
- 引入消息队列 / WS → 新 ADR
|
||||
- 跨服务通信改 gRPC → 新 ADR
|
||||
- 数据库换型(PG → ClickHouse)→ 新 ADR
|
||||
- 引入新数据源 → `ai/specs/<source>.md`(不一定要 ADR,除非影响整体架构)
|
||||
Reference in New Issue
Block a user