Files
cryptoHermes/README.md
dela 12420c5db7 chore(milestone6): Makefile guard/test-cover/test-race + README 测试章节
- Makefile 新增 test-race / test-cover / guard 三个 target
- guard 一次性跑所有可机械验证守卫(G1/G6/G11/G12 共 7 条 grep),
  任一非零退出即失败;CI 友好,无需 jq/yq
- README 补'测试与覆盖率'段落:列覆盖率现状、解释 pgxmock 选型、
  解释 pgxPool 接口的存在;路线图把 v2.0 标记为完成

Production gate(合并 main 前必须全绿):
 go build ./...
 go vet ./...
 go test -race ./...
 indicator.go 12 函数平均 97.7%(≥90%)
 postgres 包 87.6%、mapper.go 100%(≥60%)
 make guard 全绿(G1/G6/G11/G12 7 条)
 go mod tidy 干净
 entity.TechnicalStructure 字段签名未改

剩余收尾留给真实环境:手动 smoke(make docker-up + migrate-up + run
+ GET /v1/market/context?symbol=BTCUSDT)在生产部署前由 ops 跑一次。
2026-05-24 20:51:20 +08:00

350 lines
10 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.
# cryptoHermes — Market Data Gateway
为上游 Hermes 量化分析引擎提供统一的币圈行情与衍生品上下文。Hermes 只需要调用本服务的 `/v1/market/context` 一个接口,就能拿到 BTC/ETH 多周期 K 线、当前价、funding、OI、多空比等用于策略分析的数据。
本服务只做行情采集与聚合,**不下单、不接私钥、不查账户、不托管任何资金**。
---
## 特性
- Binance USDⓈ-M Futures 公共接口,无需 API Key。
- 多周期 K 线 / 24h ticker / funding / Open Interest / 全局多空比 / 大户持仓多空比 / Taker 主动买卖量。
- 历史数据自动落库Binance 官方历史 OI / 多空比只保留 30 天,必须自己存)。
- Clean Architecturecontroller / usecase / repo 分层usecase 只依赖接口。
- Fiber v2 REST API + robfig/cron 定时采集 + TimescaleDB hypertable 存时序数据。
- Rate Limit 节流(默认 20 req/s可重复运行的 upsertK 线只入库已收线。
---
## 技术栈
| 类型 | 选型 |
|---|---|
| 语言 | Go 1.23 |
| HTTP | Fiber v2 |
| DB | TimescaleDB (PG 16) |
| DB Driver | pgx v5 (pgxpool) |
| Scheduler | robfig/cron v3 |
| Config | cleanenv (yaml + env) |
| 限流 | golang.org/x/time/rate |
| Migration | golang-migrate CLI |
---
## 快速开始
### 1. 依赖
```bash
# Go 1.23+
go version
# golang-migrate CLI用于跑 migration
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
```
### 2. 启动数据库
```bash
make docker-up
```
会用 `timescale/timescaledb:latest-pg16` 启一个 Postgres监听本地 5432。
### 3. 执行 migration
```bash
export DATABASE_URL="postgres://postgres:postgres@localhost:5432/hermes_market?sslmode=disable"
make migrate-up
```
会建 5 张 hypertable`market_klines` / `funding_rates` / `open_interest` / `long_short_ratio` / `taker_buy_sell_volume`
### 4. (可选)回填历史 K 线
第一次启动时 DB 是空的,直接调 `/v1/market/context` 会触发回源 Binance。建议先回填
```bash
make backfill ARGS="--symbol BTCUSDT --interval 15m --limit 500"
make backfill ARGS="--symbol BTCUSDT --interval 1h --limit 500"
make backfill ARGS="--symbol BTCUSDT --interval 4h --limit 500"
make backfill ARGS="--symbol BTCUSDT --interval 1d --limit 500"
make backfill ARGS="--symbol BTCUSDT --interval 1w --limit 500"
# ETHUSDT 同上
```
也可以指定起止时间做大段回填:
```bash
make backfill ARGS="--symbol BTCUSDT --interval 1h --from 1704067200000 --to 1735689600000"
```
### 5. 启动服务
```bash
make run
```
服务会:
- 监听 `:8080`
- 立刻启动 cron每 15 分钟拉一次 funding / OI / 多空比 / taker volume / 15m K 线,整点拉 1h每 4h 拉 4h每日 00:05 拉 1d每周一 00:10 拉 1w。
### 6. 验证
```bash
curl -s localhost:8080/v1/health | jq .
curl -s 'localhost:8080/v1/market/context?symbol=BTCUSDT' | jq 'keys'
```
---
## 国内网络
`fapi.binance.com` 国内直连不稳定通过环境变量挂代理即可Go 默认尊重 `HTTPS_PROXY`
```bash
HTTPS_PROXY=http://127.0.0.1:7890 make run
```
---
## 配置
`config/config.yml`(所有字段都可被环境变量覆盖,参见 `config/config.go``env:` tag
```yaml
app:
port: 8080
env: local
binance:
base_url: https://fapi.binance.com
timeout: 10s
retry_count: 2
rps: 20 # IP 级限速,超过会自动 sleep
burst: 40
postgres:
dsn: postgres://postgres:postgres@localhost:5432/hermes_market?sslmode=disable
max_conns: 10
min_conns: 2
collector:
enabled: true # 设为 false 可只跑只读 API不开 cron
symbols: [BTCUSDT, ETHUSDT]
intervals: [15m, 1h, 4h, 1d, 1w]
default_limit: 500
```
常用环境变量:
| 变量 | 说明 |
|---|---|
| `CONFIG_PATH` | 配置文件路径,默认 `config/config.yml` |
| `POSTGRES_DSN` | 覆盖 yaml 中的 DSNDocker Compose 里就是这么干的) |
| `APP_PORT` | HTTP 端口 |
| `HTTPS_PROXY` | 走代理访问 Binance |
---
## API
### `GET /v1/health`
```json
{ "status": "ok", "time": 1717000000000 }
```
### `GET /v1/market/context?symbol=BTCUSDT`
**Hermes 主接口**。聚合返回 K 线 + 衍生品 + 数据质量信息:
```jsonc
{
"symbol": "BTCUSDT",
"generatedAt": 1717000000000,
"snapshot": {
"lastPrice": "108000.12",
"priceChangePercent": "2.14",
"highPrice": "109200.00",
"lowPrice": "104800.00",
"volume": "...",
"quoteVolume": "..."
},
"klines": {
"15m": [ /* 300 */ ],
"1h": [ /* 300 */ ],
"4h": [ /* 300 */ ],
"1d": [ /* 300 */ ],
"1w": [ /* 300 */ ]
},
"derivatives": {
"funding": { "current": { ... }, "history": [ ... ] },
"openInterest": { "current": { ... }, "history": [ ... ] },
"longShortRatio": {
"global": [ ... ],
"topTraderPosition": [ ... ]
},
"takerBuySellVolume": []
},
"technical": {
"support": [], "resistance": [],
"rangeHigh": null, "rangeLow": null, "longShortLine": null
},
"dataQuality": {
"source": "binance",
"warnings": []
}
}
```
> `technical.*` 字段在 v1 留空。v2 会补支撑压力、箱体、多空线计算。
### `GET /v1/market/klines?symbol=BTCUSDT&interval=1h&limit=300`
直接读 DB 返回 K 线数组。`interval` 取值 `15m/1h/4h/1d/1w`
### `GET /v1/market/snapshot?symbol=BTCUSDT`
24h ticker 实时拉取(不走 DB
### `GET /v1/market/derivatives?symbol=BTCUSDT&period=1h`
衍生品聚合funding / OI / 多空比。`period` 默认 `1h`
---
## 目录结构
```
cryptoHermes/
├── cmd/
│ ├── app/ # 主服务入口
│ └── backfill/ # 历史 K 线回填 CLI
├── config/ # cleanenv 配置
├── internal/
│ ├── app/ # 装配 DI、cron scheduler、graceful shutdown
│ ├── controller/ # Fiber 路由 + middleware
│ ├── entity/ # 业务实体(不依赖 Fiber/DB/Binance
│ ├── usecase/ # 业务接口与编排
│ ├── repo/
│ │ ├── webapi/binance/ # Binance HTTP client
│ │ └── persistent/postgres/ # 5 个 repository
│ └── worker/ # 实际跑采集逻辑的 collector
├── migrations/ # SQL migration含 hypertable 转换)
├── pkg/
│ ├── httpclient/ # 通用 HTTP client + retry + rate limit
│ ├── logger/ # slog 包装
│ └── postgres/ # pgxpool 初始化
├── docs/dev.md # 完整设计文档
├── docker-compose.yml
├── Dockerfile
├── Makefile
└── go.mod
```
---
## 数据库
5 张 hypertable主键设计保证 upsert 幂等:
| 表 | 主键 | 时间列 |
|---|---|---|
| `market_klines` | `(source, symbol, interval, open_time)` | `open_time` |
| `funding_rates` | `(source, symbol, funding_time)` | `funding_time` |
| `open_interest` | `(source, symbol, period, timestamp)` | `timestamp` |
| `long_short_ratio` | `(source, symbol, period, ratio_type, timestamp)` | `timestamp` |
| `taker_buy_sell_volume` | `(source, symbol, period, timestamp)` | `timestamp` |
K 线时间列存毫秒 epochTimescale chunk 间隔默认 1 周(衍生品表)/ 30 天funding
---
## 常用命令
```bash
make run # 跑服务
make build # 编译两个二进制到 ./bin/
make test # 跑测试
make test-race # -race 模式跑测试
make test-cover # 跑测试 + 输出总覆盖率
make guard # 跑全部可机械验证的守卫规则G1/G6/G11/G12
make tidy # go mod tidy
make docker-up # 起 Timescale
make docker-down # 停 docker
make migrate-up # apply migration
make migrate-down # 回滚 1 步
make backfill ARGS="--symbol BTCUSDT --interval 1h --limit 500"
```
---
## 测试与覆盖率
v2 第一次把测试体系立住。当前覆盖:
| 包 | 测试范围 | 覆盖率 |
|---|---|---|
| `internal/usecase` (indicator.go) | pivot S/R、percentile、LSR 穿越、聚类 全分支 | 12 个函数平均 97.7% |
| `internal/repo/webapi/binance` (mapper.go) | `parseKlineRow``toInt64`、6 个 `map*` 函数 | 9 个函数全部 100% |
| `internal/repo/persistent/postgres` | 5 个 repo 的 `UpsertMany` + `FindRecent` 正反例pgxmock | 包级 87.6% |
跑全部测试 + 覆盖率:
```bash
make test-race
make test-cover
```
测试选型说明:
- 算法(`indicator.go`)采用 table-driven + `testify/require`
- Repo 层用 [`pgxmock/v4`](https://github.com/pashagolub/pgxmock) 锁 SQL 字符串与参数顺序——schema 漂移立刻可见。
- `*pgxpool.Pool` 不能直接 mock因此 5 个 repo 的字段类型被改为内部 `pgxPool` 接口(`internal/repo/persistent/postgres/pool.go`),生产代码继续传 `*pgxpool.Pool`,调用方零改动。
- 集成测试testcontainers + 真 Postgres + 真 migration留 v2.1 做 nightly。
---
## Clean Architecture 边界(验收用)
```bash
# 一次跑全部 G1 / G6 / G11 / G12 守卫
make guard
```
或单独:
```bash
# controller 不应直接依赖 binance client
grep -r "repo/webapi/binance" internal/controller # 期望: 无输出
# usecase 不应直接依赖具体 binance/postgres 实现
grep -r "repo/webapi/binance\|repo/persistent" internal/usecase # 期望: 无输出
# 全项目不应出现私钥/账户/签名相关字段
grep -ri "apikey\|x-mbx-apikey\|hmac" . # 期望: 无输出
# G12float64 仅允许在 indicator.go 内部
grep -rn --include="*.go" "float64\|float32" internal/usecase | grep -v indicator
# 期望: 无输出
```
---
## 路线图
- **v1**Binance 行情 + 衍生品 + 落库 + `/v1/market/context` 聚合接口。✅
- **v2.0(当前)**技术结构计算第一波——pivot 支撑/压力 + P95/P5 区间 + LSR 穿越价位中位数primary interval `1h`)。算法 100% 测试 + repo / mapper 关键路径测试到位。✅
- **v2.x**MA / Bollinger / Vegas / 箱体per-interval 指标(`map[interval]TechnicalStructure`testcontainers 集成测试 + CI。
- **v3**CoinGlass 清算数据、ETF Flow、CME gap、CVD、链上稳定币流动性、宏观日历、多交易所聚合。
完整设计参见 [`docs/dev.md`](docs/dev.md)。
---
## License
Internal project. Not for redistribution.