# Polymarket BTC 5m 图表

Polymarket **BTC 5 分钟涨跌市场**的实时看板：逐笔成交聚合 K 线 + Chainlink TWAP 结算线 + submin_mom 动量信号。

独立项目，与 `bosona-research` / `updown-live` 解耦，只读取它们的策略判据（`app/pmframe/strategies/submin_mom.py`）。

## 数据源与缓存（按可变性分级，不是统一 TTL）

| 源 | 拿法 | 存哪 | 有效期 | 为什么 |
|:--|:--|:--|:--|:--|
| Chainlink TWAP（结算同源） | 服务端 WS 常驻 | `data/chainlink_btc_YYYYMMDD.ndjson` | 永久（追加型） | 秒级价格不可变，历史 TWAP 由 `/api/cl` 现算 |
| OKX 成交（动量信号源） | 服务端 WS 常驻 | `data/okx_btc_YYYYMMDD.ndjson` | 永久（追加型） | 页面刷新时用 `/api/okx` 回补，重算历史 bar 的信号 |
| Polymarket 逐笔成交 | 服务端代理 `data-api` | `cache/trades_final/` | **已定型窗口永久**；当前窗口直通 | 窗口 `end < now-120s` 后成交不会再变 |
| 市场元数据/结算 | 服务端代理 `gamma` | `cache/meta_final/`（已结算）<br>`cache/meta_live/`（未结算，20s） | 同上 | 结算后 `outcomePrices` 不变 |

- 页面不再直连 Polymarket：`/api/meta?slug=…`、`/api/trades?condition=…`，刷新时命中的都是本地缓存。
- `data/` 是**原始落盘数据（只增不改）**；`cache/` 是**派生缓存**，可清：
  `curl 'localhost:8900/api/cache/prune?days=7'`（启动时也会自动清一次超龄缓存）。
- 状态查询：`curl localhost:8900/api/cache/status`。

## 启动

```bash
cd I:/plot/polymarket-5m-chart
python serve.py --port 8900
```

打开 <http://127.0.0.1:8900/btc5m_live.html>

## 生产部署（爱尔兰 34.255.199.213 / chart.nowhub.org）

两个 systemd 单元，采集器独立监管（挂掉 3 秒内自动重拉，实测 kill -9 后 `NRestarts=1` 恢复、数据滞后 2.4s）：

| 单元 | 命令 | 备注 |
|:--|:--|:--|
| `polymarket-chart.service` | `serve.py --port 8900 --no-proxy --no-recorder` | 只跑 Web + API，不再自带子进程 |
| `polymarket-chart-recorder.service` | `node recorder.js` | `Restart=always`，日志 `recorder.log` |

nginx 在 80/443 反代到 8900（域名 80→301 https，443 用 Let's Encrypt；IP 访问走自签）。

`serve.py` 会一并拉起 `recorder.js`（Node 采集器）。需要能访问：
- `gamma-api.polymarket.com` / `data-api.polymarket.com`（成交数据）
- `ws-live-data.polymarket.com`（Chainlink TWAP）
- `ws.okx.com`（动量信号）

代理默认 `127.0.0.1:10808`，可用 `--proxy` 改、`--no-proxy` 关闭。

## 目录

```
polymarket-5m-chart/
├── btc5m_live.html                      # 主页面（动态加载 + 实时）
├── btc5m_tv.html                        # 静态快照版（离线可用）
├── btc5m_2h.html                        # 原生 Canvas 版
├── lightweight-charts.standalone.*.js   # 本地图表库（无 CDN 依赖）
├── serve.py                             # 静态服务 + /api/cl + 采集器管理
├── recorder.js                          # Node 常驻订阅 Chainlink RTDS，秒级落盘
├── data/                                # chainlink_btc_YYYYMMDD.ndjson
├── tools/                               # 一次性生成静态快照的脚本
└── reference/polyhedg-chart/            # 参考项目（10 种渲染方案对比）
```

## 页面能力

| 功能 | 说明 |
|:---|:---|
| 时间范围 | 15m / 30m / 1h / 2h / 6h（按需加载，可向左滚动回溯至 7 天） |
| K 线周期 | 5s / 10s / 30s / 1m / 5m（由逐笔成交聚合 OHLC） |
| 成交量 | 桶内 shares 求和 |
| Chainlink TWAP60 | 橙色曲线（左轴 USD），与结算同源 |
| price to beat | 蓝色虚线 = 当前市场起点 TWAP60 |
| 结算结果 | 绿 ↑UP / 红 ↓DN（`closed + umaResolutionStatus=resolved`） |
| 动量信号 | 复刻 `submin_mom`：OKX tick，窗口 bar+10~60s，阈值可调 |

## 关键数据源

```
成交： GET  data-api.polymarket.com/v2/trades?condition=0x...&start=&end=
市场： GET  gamma-api.polymarket.com/events?slug=btc-updown-5m-<ts>
结算： GET  data-api.polymarket.com/v2/resolutions?condition=0x...
TWAP： WSS  wss://ws-live-data.polymarket.com
            {"action":"subscribe","subscriptions":[{"topic":"crypto_prices_chainlink",
              "type":"update","filters":"{\"symbol\":\"btc/usd\"}"}]}
动量： WSS  wss://ws.okx.com:8443/ws/v5/public
            {"op":"subscribe","args":[{"channel":"trades","instId":"BTC-USDT"}]}
```

## 已知限制

- Chainlink RTDS 首包只回填约 12 秒，历史靠 `recorder.js` 落盘补齐
- Python `websocket-client` 收不到持续推送（只能用 Node 采集）
- 官方 1 分钟粒度数据只保留 7 天
- TWAP 曲线为 5 秒采样 + 线性连接（TWAP 是连续函数，不是离散 tick）
