Files
cryptoHermes/README.md
2026-05-24 21:18:19 +08:00

438 lines
13 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'
```
---
## 一键部署docker compose
适合生产或测试环境快速起服务。**3 步上线**
```bash
# 1. 准备环境变量
cp .env.example .env
# 编辑 .env必须填写 POSTGRES_PASSWORD留空时 compose 直接拒绝启动)
# 国内机器还要填 HTTP_PROXY / HTTPS_PROXY
# 2. 启动(自动建表)
docker compose up -d
# 3. 验证
curl -s http://localhost:8080/v1/health | jq .
docker compose ps # 应看到 3 行postgres healthy / migrate exited(0) / app running
```
### 启动顺序
`docker compose up` 会按如下依赖链推进,**任何一环失败都会阻断**
```
postgres 起 → pg_isready healthcheck 通过
→ migrate 容器跑 migrations/ up → 退出 0
→ app 起,连 DB启 cron
```
migrate 是一次性 init containerimage `migrate/migrate:v4.18.1`跑完立即退出。app 用 `depends_on: condition: service_completed_successfully` 等它结束,保证 app 启动时表一定存在。
### 容器 healthcheck
- **postgres**`pg_isready`5s 间隔
- **app**`wget --spider /v1/health`15s 间隔start_period 20s
`docker compose ps` 会显示 `(healthy)``(unhealthy)`。k8s / 反向代理 / LB 可直接根据 healthcheck 状态做流量切换。
### 优雅停机
`docker compose stop` 默认 10s本项目设 `stop_grace_period: 30s`
- app 收到 SIGTERM
- fiber `ShutdownWithContext(10s)` 等当前 HTTP 请求结束
- cron Scheduler.Stop() 等当前 collector 任务结束
- 30s 超时后 docker 才发 SIGKILL
### 日志
容器 stdout 走 docker json-file driver
- 单文件最大 50 MB
- 保留 5 个滚动文件
- 应用业务日志额外挂载到宿主机 `./logs/`
查实时日志:
```bash
docker compose logs -f app
docker compose logs -f postgres
```
### 回填历史
启动后 DB 是空的;调用 `/v1/market/context` 会 fallback 到 Binance带 warnings。要立刻获得满载历史进容器跑 backfill
```bash
docker compose exec app /app/backfill --symbol BTCUSDT --interval 1h --limit 500
docker compose exec app /app/backfill --symbol ETHUSDT --interval 1h --limit 500
# 其它 interval 同理
```
或等 cron 自然落库15m K 线每 15 分钟一次1h 每整点一次)。
### 生产 checklist
合并前再过一遍:
- [ ] `.env``POSTGRES_PASSWORD` 已改为强密码
- [ ] 国内机器:`.env``HTTP_PROXY` / `HTTPS_PROXY` 已配
- [ ] `docker compose config` 渲染后无 `${VAR:?}` 报错
- [ ] `docker compose up -d``docker compose ps` 全部 healthy / exited(0)
- [ ] `curl /v1/health` 200`curl /v1/market/context?symbol=BTCUSDT` 返回非空 `technical`
- [ ] 宿主机 `./logs/` 目录有 app 写入权限(默认 alpine root 用户写没问题)
- [ ] `make guard` 在仓库根目录全绿(即使是 deploy 修改,守卫规则也必须不破)
---
## 国内网络
`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 端口compose 部署中容器内固定为 8080 |
| `APP_HOST_PORT` | docker compose 暴露到宿主机的 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.