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

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. 依赖

# Go 1.23+
go version

# golang-migrate CLI用于跑 migration
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

2. 启动数据库

make docker-up

会用 timescale/timescaledb:latest-pg16 启一个 Postgres监听本地 5432。

3. 执行 migration

export DATABASE_URL="postgres://postgres:postgres@localhost:5432/hermes_market?sslmode=disable"
make migrate-up

会建 5 张 hypertablemarket_klines / funding_rates / open_interest / long_short_ratio / taker_buy_sell_volume

4. (可选)回填历史 K 线

第一次启动时 DB 是空的,直接调 /v1/market/context 会触发回源 Binance。建议先回填

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 同上

也可以指定起止时间做大段回填:

make backfill ARGS="--symbol BTCUSDT --interval 1h --from 1704067200000 --to 1735689600000"

5. 启动服务

make run

服务会:

  • 监听 :8080
  • 立刻启动 cron每 15 分钟拉一次 funding / OI / 多空比 / taker volume / 15m K 线,整点拉 1h每 4h 拉 4h每日 00:05 拉 1d每周一 00:10 拉 1w。

6. 验证

curl -s localhost:8080/v1/health | jq .
curl -s 'localhost:8080/v1/market/context?symbol=BTCUSDT' | jq 'keys'

国内网络

fapi.binance.com 国内直连不稳定通过环境变量挂代理即可Go 默认尊重 HTTPS_PROXY

HTTPS_PROXY=http://127.0.0.1:7890 make run

配置

config/config.yml(所有字段都可被环境变量覆盖,参见 config/config.goenv: tag

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

{ "status": "ok", "time": 1717000000000 }

GET /v1/market/context?symbol=BTCUSDT

Hermes 主接口。聚合返回 K 线 + 衍生品 + 数据质量信息:

{
  "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


常用命令

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) parseKlineRowtoInt64、6 个 map* 函数 9 个函数全部 100%
internal/repo/persistent/postgres 5 个 repo 的 UpsertMany + FindRecent 正反例pgxmock 包级 87.6%

跑全部测试 + 覆盖率:

make test-race
make test-cover

测试选型说明:

  • 算法(indicator.go)采用 table-driven + testify/require
  • Repo 层用 pgxmock/v4 锁 SQL 字符串与参数顺序——schema 漂移立刻可见。
  • *pgxpool.Pool 不能直接 mock因此 5 个 repo 的字段类型被改为内部 pgxPool 接口(internal/repo/persistent/postgres/pool.go),生产代码继续传 *pgxpool.Pool,调用方零改动。
  • 集成测试testcontainers + 真 Postgres + 真 migration留 v2.1 做 nightly。

Clean Architecture 边界(验收用)

# 一次跑全部 G1 / G6 / G11 / G12 守卫
make guard

或单独:

# 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
# 期望: 无输出

路线图

  • v1Binance 行情 + 衍生品 + 落库 + /v1/market/context 聚合接口。
  • v2.0(当前)技术结构计算第一波——pivot 支撑/压力 + P95/P5 区间 + LSR 穿越价位中位数primary interval 1h)。算法 100% 测试 + repo / mapper 关键路径测试到位。
  • v2.xMA / Bollinger / Vegas / 箱体per-interval 指标(map[interval]TechnicalStructuretestcontainers 集成测试 + CI。
  • v3CoinGlass 清算数据、ETF Flow、CME gap、CVD、链上稳定币流动性、宏观日历、多交易所聚合。

完整设计参见 docs/dev.md


License

Internal project. Not for redistribution.

Description
No description provided
Readme 357 KiB
Languages
Go 97.9%
Makefile 1.6%
Dockerfile 0.5%