Files
cryptoHermes/README.md
dela 319e73d91f docs: v2.x 状态对齐 — dev.md 标废弃 + AGENTS/harness-health 更新
dev.md 是 v1 设计稿但路线图已与 README 冲突;加废弃声明 + 优先级表,
指向 README / AGENTS / ai/adr 作为权威来源。AGENTS §2 与 harness-health
同步 v2.x 进度(Bollinger / Vegas / derivatives signal ;per-interval
完整化 + 箱体 + CI 未完)。README 路线图分段 v2.0/v2.x/v3,与 G12 守卫
扩白名单(derivatives_signal.go)保持一致。
2026-05-25 10:43:24 +08:00

446 lines
14 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
for s in BTCUSDT ETHUSDT SOLUSDT BNBUSDT DOGEUSDT; do
for i in 15m 1h 4h 1d 1w; do
make backfill ARGS="--symbol $s --interval $i --limit 500"
done
done
```
也可以指定起止时间做大段回填:
```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 直接拒绝启动)
# 国内机器还要确认 GO_MODULE_PROXY并按需填写 HTTP_PROXY / HTTPS_PROXY
# 如果 .env 是旧文件补一行GO_MODULE_PROXY=https://goproxy.cn,direct
# HTTP_PROXY / HTTPS_PROXY 不要填 127.0.0.1Docker 容器访问宿主机代理用 host.docker.internal
# 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
for s in BTCUSDT ETHUSDT SOLUSDT BNBUSDT DOGEUSDT; do
docker compose exec app /app/backfill --symbol $s --interval 1h --limit 500
done
# 其它 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, SOLUSDT, BNBUSDT, DOGEUSDT]
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 端口 |
| `GO_MODULE_PROXY` | Docker build 阶段下载 Go module 使用的 Go proxy不是 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.support/resistance/rangeHigh/rangeLow/longShortLine` 目前按 primary `1h` 计算;`technical.intervals` 已提供每周期 Bollinger / Vegas。完整 per-interval `TechnicalStructure` 与箱体突破信号仍在 v2.x 待办。
### `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` | pivot S/R、percentile、LSR 穿越、Bollinger、Vegas、derivatives signal | 算法路径有表驱动与边界用例覆盖 |
| `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` / `derivatives_signal.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 / derivatives_signal.go 内部
grep -rn --include="*.go" "float64\|float32" internal/usecase | grep -vE "indicator|derivatives_signal"
# 期望: 无输出
```
---
## 路线图
- **v1**Binance 行情 + 衍生品 + 落库 + `/v1/market/context` 聚合接口。✅
- **v2.0**技术结构计算第一波——pivot 支撑/压力 + P95/P5 区间 + LSR 穿越价位中位数primary interval `1h`)。算法测试 + repo / mapper 关键路径测试到位。✅
- **v2.x当前**:进行中。
- ✅ Bollinger / Vegas 已落地到 `technical.intervals`
- ✅ Derivatives signal 已落地到 `derivatives.signal`
- ⚠️ 完整 per-interval `TechnicalStructure` 尚未升级Support / Resistance / Range / LSLine 仍只按 primary `1h` 输出。
- ⚠️ 箱体与 per-interval 突破信号未实现。
- ❌ testcontainers 集成测试 + CI 未起。
- **v3**CoinGlass 清算数据、ETF Flow、CME gap、CVD、链上稳定币流动性、宏观日历、多交易所聚合。
完整设计参见 [`docs/dev.md`](docs/dev.md)。
---
## License
Internal project. Not for redistribution.