Files
cryptoHermes/ai/risk-guardrails.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

185 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Risk Guardrails — cryptoHermes
可机械验证 / 不可逾越的规则。每条规则给出**为什么**、**怎么验证**。
任何 PR 都应该满足这里全部规则。
---
## G1. 绝不接入交易能力Hard Stop
**规则**:本服务只读公共行情。**禁止**任何下单、撤单、查询账户/持仓/资金、签名请求、托管、转账。
**为什么**:项目定位是"上游 Hermes 量化分析引擎的行情网关"。一旦引入交易能力,安全边界、合规风险、密钥管理责任全部上升一个数量级。`docs/dev.md` 第 1 章原文:"本服务只提供市场数据与分析上下文,不做交易、不下单、不托管资金"。
**怎么验证**
```bash
# Go 代码不可出现私钥/签名相关字段
grep -ri --include="*.go" "apikey\|x-mbx-apikey\|hmac\|secret_key\|api_secret" .
# 期望:无输出(注释/字符串/文档中说明"不接入"不算)
# Binance 私有接口前缀不可出现
grep -r --include="*.go" "fapi/v1/order\|fapi/v1/account\|fapi/v2/account\|fapi/v2/positionRisk" .
# 期望:无输出
```
**例外**:无。如果上游需求变化要做交易,**必须先开新仓库**或新模块(独立鉴权 / 独立部署),不能在本仓库内直接放开。
---
## G2. 价格 / 成交量绝不用 float64Hard Stop
**规则**:所有金额、价格、成交量字段**全链路用 `string`**。DTO、entity、JSON 响应都是 `string`。落库时由 PG 转 `NUMERIC(36,18)`
**为什么**`108000.12` 这种价格 float64 表示就会丢精度。Binance 原样返回字符串,本服务保留字符串到 PG。这一点在所有 5 个 entity 文件里都已经做到了,新代码必须遵守。
**怎么验证**
```bash
# 任何新增 entity 字段如果叫 *Price/*Volume/*Quantity/*Amount类型必须是 string
grep -rn --include="*.go" "Price\s*float\|Volume\s*float\|Quantity\s*float\|Amount\s*float" internal/entity
# 期望:无输出
```
**例外**:纯统计/分析(如指标计算的中间过程)可以用 float64但**结果落库或返回 API 时必须转回 string**。
---
## G3. 未收线 K 线IsClosed=false不入库
**规则**:从 Binance 拉到的最新一根 K 线如果 `close_time > now()`**不能写入 `market_klines`**。
**为什么**:未收线 K 线的 close/high/low/volume 还会变。如果入库,后续不更新就是脏数据,更新又会触发 upsert 风暴。`internal/repo/persistent/postgres/kline_repo.go``UpsertMany` 已经过滤 `IsClosed=false``internal/repo/webapi/binance/mapper.go` 已经自动设置 `IsClosed = closeTime < now()`。新代码(含 backfill必须遵守。
**怎么验证**:手动跑一次 collector立刻 `SELECT count(*) FROM market_klines WHERE close_time > extract(epoch from now()) * 1000` 应为 0。
---
## G4. UpsertMany 必须幂等
**规则**:所有 repo 的 `UpsertMany``INSERT ... ON CONFLICT (<PK>) DO UPDATE SET ... updated_at = now()`。**禁止** `INSERT` 不带 ON CONFLICT 或用 `INSERT ... ON CONFLICT DO NOTHING`(后者错过了更新场景)。
**为什么**cron 会重复跑、backfill 会和 cron 重叠、重启会重拉。重复执行必须不能产生重复行也不能丢更新(例如 OI 后修正、close 值微调)。
**怎么验证**
```bash
make backfill ARGS="--symbol BTCUSDT --interval 1h --limit 100"
psql $DATABASE_URL -c "SELECT count(*) FROM market_klines WHERE symbol='BTCUSDT' AND interval='1h'"
make backfill ARGS="--symbol BTCUSDT --interval 1h --limit 100"
psql $DATABASE_URL -c "SELECT count(*) FROM market_klines WHERE symbol='BTCUSDT' AND interval='1h'"
# 期望:两次 count 一致
```
---
## G5. Rate Limit 不可绕开
**规则**:所有外部 HTTP 调用走 `pkg/httpclient`,该 client 已挂 `golang.org/x/time/rate`(默认 20 req/sburst 40。**禁止** 在业务代码里直接用 `net/http` 调 Binance。
**为什么**Binance USDⓈ-M Futures IP 级限额 2400 weight/min。一旦超限会被封 IP 几分钟到几小时。本地代理在国内是稀缺资源,封了影响整个开发环境。
**预算**:新增 collector 任务前估算 weight
- `GET /fapi/v1/klines`1limit ≤ 100→ 10limit ≤ 1000
- `GET /fapi/v1/ticker/24hr`1单 symbol/ 40全量
- `GET /fapi/v1/openInterest`1
- `GET /futures/data/*`1
**怎么验证**
```bash
grep -rn --include="*.go" "net/http\"" internal/repo/webapi
# 期望:只在 pkg/httpclient 里repo/webapi 不应直接 import net/http
```
---
## G6. Clean Architecture 边界
**规则**:见 `ai/project-map.md` "不可逾越的边界"段。3 个 grep 必须无输出。
**为什么**:一旦 controller / usecase 直接 import 具体实现,测试就需要起 DB 起 Binance后续替换数据源CoinGlass也变成大手术。
**怎么验证**
```bash
grep -r "repo/webapi/binance\|repo/persistent" internal/controller # 必须无输出
grep -r "repo/webapi/binance\|repo/persistent" internal/usecase # 必须无输出
```
---
## G7. Migration 必须 down/up 互逆
**规则**:任何 SQL migration 改动后,本地必须跑:
```bash
make migrate-down && make migrate-up
```
两步都成功(不报错、不丢表)才能提交。
**为什么**生产回滚是兜底手段down 脚本错了就回不去。
**额外规则**
- 新 migration **不可改已发布 migration 的内容**——只能加新文件(编号递增)。
- 改表结构如果会丢数据drop column / change type必须在 PR 里显式说明。
- 改 hypertable chunk 间隔需要先看 `docs/dev.md` 第 10 章。
---
## G8. K 线数组下标解析不可破
**规则**`internal/repo/webapi/binance/mapper.go``mapKline` 的下标解析对应 Binance 官方约定:
```
[0] openTime ms
[1] open string
[2] high string
[3] low string
[4] close string
[5] volume string
[6] closeTime ms
[7] quoteAssetVolume string
[8] numberOfTrades int
[9] takerBuyBaseVolume string
[10] takerBuyQuoteVolume string
[11] ignore
```
**禁止**调整顺序、跳过位置、改用对象解析Binance 这个接口就是数组)。
**为什么**:错一位整张表数据就废了,回滚成本极高。`docs/dev.md` 第 9.2 节有完整说明。
**怎么验证**:动了 `mapper.go` 之后跑一次回填,对比 Binance 网页 K 线图同一根的 OHLCV 是否一致。
---
## G9. 配置项不引入秘钥
**规则**`config/config.yml` 和环境变量列表里不允许出现 `api_key``secret``token``password`DB 密码除外,且必须从环境变量读、不能 hardcode
**为什么**:本服务不需要任何 Binance 私有鉴权,也不向外部传秘密。一旦引入会破坏 G1。
**怎么验证**
```bash
grep -i "api_key\|api_secret\|access_token" config/
# 期望:无输出
```
---
## G10. WebSocket / 实时推送不在 v1 范围
**规则**v1 只用 REST 拉取。不引入 gorilla/websocket、不订阅 `wss://fstream.binance.com`
**为什么**WS 长连接的可观测性、重连、消息丢失处理是独立工程量。v1 用 REST + cron 已经能满足 Hermes 的"分钟级上下文"需求。要做 WS 必须先写 ADR。
**怎么验证**
```bash
grep -rn --include="*.go" "websocket\|fstream.binance" .
# 期望:无输出
```
---
## 升级守护规则的时机
同一类错误第二次出现 → 写一条新规则进来。修改本文件 = 改动了项目契约,需要在 PR 描述里说明原因并 @ 维护者。
新增 Hard Stop 规则时,**必须给出可机械验证的命令**——只能靠 review 来执行的规则会被绕过。