Files
gavindiaz 9b0da0279f Add Telegram watcher, dashboard docs, and deployment config
- scripts/telegram-watcher.js: push ENTER signals from signals.csv to Telegram
- DASHBOARD_GUIDE.md: complete dashboard field-by-field documentation
- package.json: add watch:telegram npm script
- .env.example: deployment env template
- .gitignore: exclude ecosystem.config.cjs/start.sh (server-only with paths)

Watcher reads recommendation column and triggers on:
  - transition NO_TRADE -> ENTER:side
  - flip ENTER:UP <-> ENTER:DOWN
  - 30s cooldown prevents spam
2026-07-21 18:00:21 +08:00

16 KiB
Raw Permalink Blame History

仪表盘逐项解读手册(基于代码逻辑链)

本文严格按 src/index.jssrc/engines/*.js 的执行顺序,逐行推演仪表盘上每一行/每一个数字是如何生成的、它代表什么、如何判断信号方向。


一、整体渲染流程

主循环 main()src/index.js:398-731)每 CONFIG.pollIntervalMs(默认 ~1500ms)刷新一屏。每一帧的渲染数据来自六个数据源,按以下顺序组装:

Binance WS (实时成交)        →  spotPrice  ─┐
Polymarket Live WS (BTC/USD) →  polymarketWsPrice ─┐
Chainlink on-chain WSS/HTTP  →  chainlinkWsPrice  ─┼─→  currentPrice (PRICE TO BEAT 当前价)
                                             fetchChainlinkBtcUsd() (HTTP 回退)
Binance REST klines (1m×240, 5m×200)  ──→  1分钟K线序列,喂给所有指标
Polymarket REST (Gamma + CLOB)         ──→  市场/订单簿/UP-DOWN价
策略引擎                                ──→  scoreDirection → applyTimeAwareness → computeEdge → decide

随后调用 renderScreen()src/index.js:71-79)用 ANSI 重定位光标 + clearScreenDown 把整张表重写到终端,所以你看到的不是新增日志,而是"整帧覆写"


二、逐字段拆解

仪表盘上从上到下共有五个区段,每个区段的字段解释如下。

1. 顶部信息(市场识别)

Bitcoin Up or Down - July 21, 4:30AM-4:45AM ET

来源:poly.market?.questionsrc/index.js:648)。这是 Polymarket Gamma API 返回的 question 字段,等价于"市场标题",标识当前 15 分钟窗口的起止时间(美国东部时间)。

Market: btc-updown-15m-1784622600

来源:poly.market?.slugsrc/index.js:649)。是 Polymarket 的市场唯一 slug。其中 1784622600 是市场开始时的 Unix 时间戳秒数(用作市场 ID)。市场选取逻辑:

  • 如果你设置了 POLYMARKET_SLUG,则锁定该市场(src/index.js:285-287)。
  • 否则 pickLatestLiveMarket() 从 Gamma 系列 seriesId=10192btc-up-or-down-15m)的活跃事件中挑"最新正在进行的 15 分钟窗口"(src/index.js:296-302,并有 25s 缓存避免重复请求)。
  • 每 15 分钟滚动一次新窗口(Polymarket 自身的事件节奏)。

Time left: 04:49

来源:timeLeftMinsrc/index.js:452, 658)。计算方法:

settlementMs = poly.market.endDate  →  转毫秒
timeLeftMin  = (settlementMs - Date.now()) / 60_000

颜色编码规则(src/index.js:651-657):

  • 绿色:[10, 15] 分钟 — EARLY 阶段
  • 黄色:[5, 10) 分钟 — MID 阶段
  • 红色:[0, 5) 分钟 — LATE 阶段(最后冲刺)

⚠️ 关键解读:剩余时间是策略相位切换的唯一信号。它直接影响 decide() 的入场阈值(见下文 Decision Logic)。


2. 策略预测区

TA Predict: LONG 63% / SHORT 37%

这是整张表最关键的输出,由两条流水线生成:

第一步 — scoreDirection()src/engines/probability.js:3-53: 从六个原始信号累加 up/down 计数,起始都是 1

触发条件 加成
price > vwap up += 2
price < vwap down += 2
vwapSlope > 0 up += 2
vwapSlope < 0 down += 2
rsi > 55 && rsiSlope > 0 up += 2
rsi < 45 && rsiSlope < 0 down += 2
MACD histogram 扩张且为正 up += 2
MACD histogram 扩张且为负 down += 2
MACD 主线 > 0 up += 1
MACD 主线 < 0 down += 1
Heiken Ashi 连续 ≥2 根同色 该方向 += 1
跌破 VWAP 后未收回(failed reclaim down += 3 ⚠️

最终 rawUp = up / (up + down)src/engines/probability.js:51)。

第二步 — applyTimeAwareness()src/engines/probability.js:55-59: 随着剩余时间减少,模型预测会被"拉回"到 50/50

timeDecay   = remainingMinutes / 15  (clamp 0..1)
adjustedUp  = 0.5 + (rawUp - 0.5) * timeDecay   (clamp 0..1)
adjustedDown= 1 - adjustedUp

这意味着:越临近到期,TA 的偏向越被"折扣",因为只剩下几分钟,已发生的价格走势对最终结果的代表性越弱。极端情形:到 timeLeftMin=0adjustedUp = 0.5 完全无偏向。

仪表盘上 Predict 就是这个 adjustedUp/adjustedDownsrc/index.js:537-543)。

Heiken Ashi: green x8

来源:computeHeikenAshi() + countConsecutive()src/indicators/heikenAshi.js)。 最后 N 根 Heiken Ashi 同色(绿=收>开)的连击数。x8 表示当前已经连续 8 根绿色 K 线 → 短期明显多头。

⚠️ 注意:在 TA 评分里只触发 +1heikenCount >= 2 时单边 +1,计数本身没有单调加权,见 src/engines/probability.js:44-47)。它是确认信号,不是主导信号。

RSI: 52.6 ↑

来源:computeRsi(closes, period=14) + slopeLast(rsiSeries, 3)src/index.js:465-473)。 52.6 是当前 14 周期 RSI 表示最近 3 根的斜率 > 0。

⚠️ 关键解读 — RSI 在本系统里被硬阈值化src/engines/probability.js:29-32):

  • 只有 rsi > 55 且 rsiSlope > 0 才触发 up += 2
  • 只有 rsi < 45 且 rsiSlope < 0 才触发 down += 2
  • 52.6(中性区)→ 零贡献

所以"RSI 52.6 ↑"看起来像多头,但 TA 计分里完全没贡献。它只是显示给你看,不影响模型。

MACD: bullish (expanding)

来源:computeMacd(closes, 12, 26, 9)src/index.js:475)。 bullish (expanding) 表示 hist > 0histDelta > 0src/index.js:519-523)。

expanding 触发 up += 2src/engines/probability.js:34-38)。所以这个字段是 TA 模型里真实的加分项

Delta 1/3Min: -$4.40, -0.01% | -$1.03, -0.00%

来源:src/index.js:525-530

  • delta1m = lastClose - klines1m[-2].close
  • delta3m = lastClose - klines1m[-4].close

⚠️ 它只是展示,不参与 TA 计分。在 scoreDirection() 的输入里没有 delta1/delta3。所以这个字段是"信息参考",不是模型输入

VWAP: 65,923 (0.63%) | slope: UP

来源:computeSessionVwap() + computeVwapSeries() + slopeLast()src/index.js:457-462)。

  • 65,923 = 当日开盘至今的成交量加权均价
  • 0.63% = 当前价相对 VWAP 的偏离(price 在 VWAP 之上,正数)
  • slope: UP = 最近 vwapSlopeLookbackMinutes 分钟的 VWAP 单调变化方向为正

这两个字段在 TA 计分里贡献最大src/engines/probability.js:19-27):

  • price > vwap → up += 2
  • vwapSlope > 0 → up += 2

合计 +4,是单项最大的多头信号源。


3. Polymarket 实时市场区

POLYMARKET: ↑ UP 0.94¢ | ↓ DOWN 0.05¢

来源:fetchPolymarketSnapshot()src/index.js:305-396)。

价格优先级src/index.js:388-389):

upPrice   = CLOB buy 价   ??  Gamma outcomePrices[upIndex]
downPrice = CLOB buy 价   ??  Gamma outcomePrices[downIndex]

先取订单簿实时买价(=bestAsk,即你能立即以多大概率买到的成本),失败时回落到 Gamma 的 midpoint 价格。

如何解读

  • 0.94¢ 意味着押 UP 每份成本 $0.94,赢了收到 $1.00 → 潜在收益 ≈ 6.4%
  • 0.05¢ 意味着押 DOWN 每份成本 $0.05,赢了收到 $1.00 → 潜在收益 ≈ 20 倍

⚠️ 赔率反映 Polymarket 集体押注的预期概率(隐含 UP ≈ 94%DOWN ≈ 5%)。但这个"市场概率"和你的 TA 模型概率可以不一致 → 这就是 edge(见下文)。

Liquidity: 6,027

来源:poly.market.liquidityNum ?? poly.market.liquiditysrc/index.js:576-578)。 是 Polymarket 该市场的报价方总资金(USDC)。只显示,不参与任何计算

解读建议:低于 ~$1000 的市场深度很浅,挂单价差大(spread 大),下单时滑点可能吃掉所有 edge。

Time left: 04:49(第二个)

来源与顶部 Time left 不同:这里是 settlementLeftMin(基于 poly.market.endDate),而顶部是基于 timing.remainingMinutes(基于 15 分钟 K 线窗口)。多数情况下两者数值接近但不完全相等。

颜色用同样的 EARLY/MID/LATE 编码(src/index.js:660-668)。

PRICE TO BEAT: $66,274

这是整个仪表盘决定胜负的关键数字。

来源链路(src/index.js:585-597):

  1. 进入新市场(slug 变化)时清空 priceToBeatState
  2. marketStartMs <= Date.now() 且当前 currentPrice 可用时,锁存当前 Chainlink 价格作为本市场的 PRICE TO BEAT。
  3. 之后 PRICE TO BEAT 固定不变(直到切到下一个市场)。

锁定值来源优先级(src/index.js:435-439):

polymarket_ws (Polymarket Live WS BTC/USD)  >  chainlink_ws (Chainlink 链上 WSS)  >  fetchChainlinkBtcUsd() (HTTP)

⚠️ 也就是说:PRICE TO BEAT 取的是 Polymarket UI 同一个 Chainlink 喂价源,不是 Binance 现货!这能避免"Binance 与 Polymarket 数据源不同步"造成的胜负误判。

到期判定规则Polymarket 官方):到期时刻 BTC 价格(同样取 Chainlink> PRICE TO BEAT → UP 赢;否则 DOWN 赢。

CURRENT PRICE: $66,291.57 ↓ (+$17.27)

来源:

  • 价格:currentPrice = chainlink.pricesrc/index.js:581),来源与 PRICE TO BEAT 相同
  • 颜色箭头:与上一帧比较(colorPriceLinesrc/index.js:109-131
    • 绿色:上涨
    • 红色:下跌
  • +$17.27:当前价 PRICE TO BEATptbDeltasrc/index.js:606-618
    • 正数绿色:在 BEAT 之上 → 倾向 UP 赢
    • 负数红色:在 BEAT 之下 → 倾向 DOWN 赢

解读:当前比 BEAT 高 $17.27。要反转需要 BTC 在剩余时间内回撤至少 $17.27。


4. Binance 现货参考区

BTC (Binance): $66,338 (+$46.02, +0.07%)

来源:binanceStream.getLast()?.price ?? fetchLastPrice()src/index.js:580)。

  • +46.02:相对上一帧 WS tick 的变动
  • 括号外的 (diff)Binance 现货价 当前 Chainlink 价(src/index.js:636-644

⚠️ 只是参考,不参与 TA 计分。但可以用来判断:

  • 两个数据源是否脱钩(diff 持续 >$50 → 数据源不一致风险)
  • 现货是否比 Chainlink 先行(>0 → 现货领先,可能预示上行)

5. 时间与会话区

ET | Session: 04:40:10 | Europe

来源:fmtEtTime(new Date()) + getBtcSession(new Date())src/index.js:173-199)。

Session 是基于 UTC 小时划分的全球流动性时段(仅显示,不参与计算):

  • Asia: 07 UTC
  • Europe: 715 UTC
  • US: 1321 UTC
  • 重叠时段会有 "Europe/US overlap" / "Asia/Europe overlap"

实战含义:欧美重叠时段(13–16 UTC)波动率最大,亚盘(0–7 UTC)波动最小。短窗口(15 分钟)市场对流动性敏感,低流动性时段 edge 模型容易失效。


三、决策逻辑(用户最关心的"该怎么判断")

虽然仪表盘不直接显示入场信号,但主循环每帧都在调用:

rec = decide({ remainingMinutes, edgeUp, edgeDown, modelUp, modelDown })

代码在 src/engines/edge.js:23-48

Step 1 — 计算市场隐含概率

marketUp   = marketYes / (marketYes + marketNo)
marketDown = 1 - marketUp

⚠️ 因为订单簿买价 marketYes + marketNo ≠ 1(spread),所以要先归一化。本例:UP 0.94 + DOWN 0.05 = 0.99 → marketUp ≈ 94.9%。

Step 2 — 计算 edge

edgeUp   = modelUp   - marketUp    // TA 对 UP 的看法  市场对 UP 的隐含概率
edgeDown = modelDown - marketDown

edge 才是交易价值所在:模型和市场分歧越大,edge 越高,理论上"无风险套利空间"越大。

Step 3 — 三阶段阈值(剩余时间分桶)

Phase 剩余时间 edge 阈值 modelProb 阈值
EARLY > 10 min 5% 55%
MID 510 min 10% 60%
LATE ≤ 5 min 20% 65%

Step 4 — 入场判定

只有当:

  1. bestEdge ≥ 阶段阈值
  2. bestModelProb ≥ 阶段概率下限

才返回 action: ENTER,否则 NO_TRADE


四、回到你的示例数据

TA Predict:     LONG 63% / SHORT 37%   ← timeAware 模型概率
Heiken Ashi:    green x8               ← 确认(+1 已包含)
RSI:            52.6 ↑                 ← 中性区,零贡献
MACD:           bullish (expanding)    ← +2(关键多头信号)
Delta 1/3:      -$4.40, -0.01%         ← 展示用,不入模型
VWAP:           65,923 (0.63%) | UP    ← price>vwap(+2) + slope>0(+2) = +4

POLYMARKET:     ↑ UP 0.94¢  |  ↓ DOWN 0.05¢
PRICE TO BEAT:  $66,274
CURRENT PRICE:  $66,291.57 ↓ (+$17.27)   ← 当前比 BEAT 高 $17.27

逐项核对模型(你看到的 LONG 63% 是怎么来的):

timeLeftMin ≈ 4.84:49)→ timeDecay = 4.8/15 = 0.32

假设其他指标命中情况(基于字段):

  • VWAP +4price>vwap 且 slope up
  • MACD +2expanding bullish
  • Heiken Ashi +1green ×8count ≥ 2
  • RSI 52.6:中性,无加成
  • failedVwapReclaim:未触发

→ upScore ≈ 1+4+2+1 = 8 → downScore ≈ 1 → rawUp = 8/9 ≈ 0.889

→ adjustedUp = 0.5 + (0.889 - 0.5) × 0.32 = 0.624 ≈ 62%

(与面板的 63% 吻合,差距来自小数四舍五入或个别次要信号 0/1 边界)

市场隐含marketUp ≈ 0.94/0.99 ≈ 0.949 edgeUp = 0.62 0.949 = 0.33(负 edge

LATE 阶段(剩 <5 分钟)要求 edge ≥ 0.20 且 modelProb ≥ 0.65

  • edge 0.33 < 0.20 →
  • modelProb 0.62 < 0.65 →
  • 结论:NO_TRADE

也就是说:这个时刻模型和市场完全同向(都看 UP),没有可赚的 edge。


五、这套仪表盘的设计哲学

  1. 数据源优先级Polymarket Live WS(直接复用 Polymarket UI 的 BTC/USD Chainlink 喂价)→ 链上 Chainlink WSS → HTTP 回退。所有"涨跌判定"逻辑都用 Chainlink 价,避免与 Polymarket 的结算价脱钩。
  2. Binance 现货只作参考,不进入 TA 计分。
  3. TA 模型信号是堆叠式评分(不是加权平均),单指标上限 +4(VWAP),MACD 上限 +2Heiken 上限 +1,RSI 硬阈值化(中位区无效)。
  4. 时间感知是衰减机制:剩余越少,模型预测越往 50/50 拉回。LATE 阶段 edge 必须 ≥20%,几乎只在模型和市场严重分歧时才入场。
  5. 入场动作隐藏在 CSV log 中./logs/signals.csv),仪表盘只显示中间过程。命令行不会有"BUY UP"提示,所有决策要在 signals.csv 里看 recommendation 字段:ENTER:UP:LATE:STRONG 之类的格式。

六、常见误读警示

误读 正确认知
"RSI 52.6 ↑ = 多头信号" 中性区,零贡献
"Delta 1/3 显示跌 → 模型看空" Delta 只是展示,不入模型
"Predict 63% = 应该买 UP" Predict 是 modelProb,需要和 marketUp 比较才算 edge
"TIME LEFT 04:49 还有时间" LATE 阶段,入场门槛最严edge ≥20%、prob ≥65%
"UP 0.94¢ 稳赢要下" 押 UP 收益仅 ~6%DOWN 0.05¢ 才是 20 倍冷门
"VWAP 0.63% 很弱" 0.63% 在 15 分钟窗口是显著偏离,是核心多头证据
"BTC Binance 和 Chainlink 价差 = 信号" 价差是数据源同步度问题,非交易信号
"Heiken Ashi x8 决定方向" 仅触发 +1,影响远小于 VWAP/MACD

七、文件落点速查

你想了解的内容 文件:行
仪表盘渲染主循环 src/index.js:398-731
PRICE TO BEAT 锁存逻辑 src/index.js:585-597
Polymarket 价抓取(含回退) src/index.js:305-396
TA 信号评分细节 src/engines/probability.js:3-53
时间感知衰减 src/engines/probability.js:55-59
入场决策(edge/threshold src/engines/edge.js:23-48
市场状态分类(regime src/engines/regime.js
时间颜色与剩余阶段编码 src/index.js:651-668
CSV 决策日志格式 src/index.js:407-419, 709-722