Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

141 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MonitoBlitz

A read-only terminal dashboard that tracks crypto prediction markets across multiple venues, surfaces price disagreement between venues for the same market, and alerts when that disagreement crosses a configurable threshold.

MonitoBlitz is a personal trading-research tool — not a trading client. There are no order endpoints, no wallet integration, and no authenticated write paths. It only reads public market data.

Markets — group period — sort spread ↓ — filter ""
ASSET  TF    TTR   RESOLVES    STRIKE          POLYMARKET      HYPERLIQUID      KALSHI            SPREAD
── 15m ───────────────────────────────────────────────────────────────────────────────────────────────
BTC    15m   13m   11:30 UTC   UP / $67,210    33 · 68         —                48 · 53 @67,210   ⚠ 15.0%
ETH    15m   13m   11:30 UTC   UP / $1,884     30 · 71         —                44 · 56 @1,884    ⚠ 14.5%
── 1d ────────────────────────────────────────────────────────────────────────────────────────────────
BTC    1d    18h   06:00 UTC   UP / $67,297    —               48 · 52 @67,297  —                 —

Contents


What it does

  • Aggregates the same market across venues into one row, side by side.
  • Computes the spread — the gap between the highest and lowest YES odds for a matched market — and colours / alerts when it exceeds your threshold, with hysteresis to avoid flapping.
  • Shows each venue's "target to beat" for direction (up/down-from-open) markets — the reference price the market resolves against — so you can see what each venue is actually pricing.
  • Flags divergence (⚠ mixed) when two venues anchor a direction market to meaningfully different reference prices, so an apples-to-oranges spread doesn't read as a real edge.
  • Runs entirely in your terminal, in memory, with no database and no API keys.

Supported venues

Venue Transport Auth Notes
Polymarket Gamma REST API (polled) None Discovers active crypto strike & up/down markets.
Hyperliquid /info HTTP API (polled) None Direction markets; exposes a reference (target) price.
Kalshi Public market-data REST (polled) None Series-driven; configured via event_series (e.g. KXBTC15M).

All three use public, unauthenticated endpoints. Hyperliquid and Kalshi poll cadence is configurable (default 3s); Polymarket currently polls at a fixed 3s.

How markets are matched

Two markets from different venues are treated as the same market if and only if these fields are equal:

  • Asset (canonical upper-case ticker, e.g. BTC)
  • Resolution time (truncated to the minute, UTC)
  • Strike — the resolution condition:
    • above / below a fixed price (e.g. > $67,199.99), or
    • up / down from the window's open price (direction markets)
  • Timeframe label (5m, 15m, 1h, 1d, weekly, …)

For direction markets, the fixed strike price is zeroed so that the same up/down market merges across venues even though each venue anchors to its own reference/open price. Markets with no cross-venue counterpart (singletons) are still shown — they just have no spread.

Reading the table

Column Meaning
ASSET Crypto ticker.
TF Timeframe of the market window.
TTR Time-to-resolution (live countdown).
RESOLVES Resolution time (UTC).
STRIKE The resolution condition. > $X / < $X for fixed strikes; UP / $X / DOWN / $X for direction markets (consensus reference), or UP ⚠ mixed when venues disagree on the reference.
YES · NO odds (0–100). For direction markets, also shows that venue's own reference as @price when available.
SPREAD Highest − lowest YES odds across venues. Coloured by threshold; a trailing marks a row whose venue references diverge (spread may be misleading).
  • A bold/red ⚠ <spread>% means the spread is at/above your alert threshold.
  • means no data for that venue/row (e.g. a venue doesn't list that market).

Install & run

Requires Go 1.24+.

# clone, then from the repo root:
make run          # build & run against live public APIs

# or build a binary:
make build
./monitoblitz

On first run, MonitoBlitz writes a default config.toml to the working directory if none exists, then starts the TUI in the alternate screen. Quit with q (or Ctrl-C).

Optional flags:

./monitoblitz --config /path/to/config.toml   # use a specific config file
./monitoblitz --alert-pct 2.5                  # override the alert threshold
./monitoblitz --theme dark                     # auto | light | dark
./monitoblitz --log /tmp/monitoblitz.log       # write errors to a log file

Configuration

Configuration is resolved with this precedence (highest wins):

CLI flags → environment variables → config file → built-in defaults.

  • Config file: config.toml in the working directory by default. Override with --config <path> or the MONITOBLITZ_CONFIG env var.
  • Env overrides: MONITOBLITZ_ALERT_PCT, MONITOBLITZ_THEME.

Example config.toml (abridged from the defaults written on first run):

[app]
  assets = ["BTC", "ETH"]
  providers = ["polymarket", "hyperliquid", "kalshi"]
  ring_size = 200                      # in-memory history depth per (market, venue)

[ui]
  sort = "spread_desc"                 # spread_desc | asset | resolution_asc
  filter = ""                          # asset prefix filter
  show_alerts = true
  theme = "auto"                       # auto | light | dark

[alerts]
  alert_pct = "2"                      # spread % that triggers an alert
  clear_pct = "1.5"                    # spread % at which an active alert clears (hysteresis)
  bell = true                          # terminal bell on alert
  notify = "auto"                      # auto | terminal-notifier | notify-send | off
  target_mismatch_tolerance = "0.001"  # relative gap (0.001 = 0.1%) above which venue references are "mixed"

[providers]
  [providers.polymarket]
    rest_url = "https://gamma-api.polymarket.com"
    # Polymarket polls at a fixed 3s. The first-run file also writes ws_url
    # and discover_every fields, but they are not wired up yet.
  [providers.hyperliquid]
    poll_every = "3s"
  [providers.kalshi]
    rest_url = "https://api.elections.kalshi.com/trade-api/v2"
    poll_every = "3s"
    event_series = ["KXBTC", "KXBTCD", "KXBTC15M", "KXETH", "KXETHD", "KXETH15M",
                    "KXXRPD", "KXXRP15M", "KXDOGED", "KXDOGE15M",
                    "KXSOL15M", "KXBNB15M", "KXHYPE15M"]

Validation rejects: ring_size <= 0, alert_pct <= 0, clear_pct < 0 or clear_pct > alert_pct, negative target_mismatch_tolerance, a negative Hyperliquid poll_every, a non-positive Kalshi poll_every, and unknown theme / sort / notify values. When clear_pct is 0, it derives to 0.75 × alert_pct.

Key bindings

Key Action
q / Ctrl-C Quit
/ k, / j Navigate rows
enter Drill into the selected market
esc Close drill-down
s Cycle sort (spread ↓ / asset / resolves ↑)
g Toggle grouping (period ↔ venue)
a Toggle the alerts pane
c Clear the alerts log
/ Filter by asset prefix
r Retry providers
? Toggle help

Architecture

Approach: a pub/sub in-memory store with a Bubble Tea bridge. Providers push snapshots into a shared store; matchers subscribe and emit match updates; an alerter and the TUI each consume those updates independently.

provider PM  ─┐
provider HL  ─┼─► snapshots ─► store ─► subscribe ─► Matcher ─┬─► TUI bridge ─► tea.Msg
provider KAL ─┘   (ring buffer per          │                └─► Alerter ─► events ─► bell / OS notify
                   market+venue)            └─► (second Matcher for the alerter)
  • Providers normalise each venue's raw markets into a common Snapshot (domain.Snapshot) and push them onto a channel.
  • Store (internal/store) keeps a bounded ring buffer per (MarketKey, Venue) and fans out to subscribers. History feeds the drill-down view.
  • Matcher (internal/matcher) groups snapshots by MarketKey and emits a MatchUpdate (quotes per venue + computed spread). Two independent matchers are wired — one for the TUI, one for the alerter — so consumers don't contend.
  • Alerter (internal/alerter) applies the threshold with hysteresis, rings the terminal bell, and best-effort OS notifications (terminal-notifier on macOS / notify-send on Linux when present).
  • TUI (internal/tui) is the Bubble Tea model: table, grouping/sorting, filtering, drill-down, alerts pane, and live countdowns.

All identity types live in internal/domain and import nothing outside the standard library and shopspring/decimal, keeping the core decoupled.

Project layout

MonitoBlitz/
├── cmd/monitoblitz/main.go     # entrypoint — wires providers, store, matchers, alerter, TUI
├── internal/
│   ├── domain/                 # MarketKey, Snapshot, Strike, MatchUpdate (no internal imports)
│   ├── provider/               # Provider interface + retry helpers
│   │   ├── polymarket/         # Gamma REST client + normalisation
│   │   ├── hyperliquid/        # /info client + market description parsing
│   │   └── kalshi/             # public REST poll, series-driven discovery
│   ├── netutil/                # shared HTTP client + retry/backoff helpers
│   ├── store/                  # in-memory snapshot store, ring buffer per (market, venue)
│   ├── matcher/                # groups snapshots by MarketKey, emits MatchUpdate
│   ├── alerter/                # threshold + hysteresis; bell + OS notify
│   ├── config/                 # TOML load + env + flag overrides, with validation
│   ├── clock/                  # Clock interface (real + fake for tests)
│   ├── logging/                # optional file logger
│   └── tui/                    # Bubble Tea model, views, table, styles, bridge
├── docs/superpowers/           # design specs & implementation plans
├── Makefile
└── config.toml                 # written on first run if absent

Development

The Makefile is the entrypoint for all common tasks:

make build         # go build ./...
make test          # go test -race ./...
make lint          # golangci-lint run (skipped if not installed)
make run           # go run ./cmd/monitoblitz
make tui-snapshot  # regenerate TUI golden snapshots (-update)
make fmt           # gofmt -w .
make tidy          # go mod tidy

Conventions followed in this codebase:

  • TDD — tests are written before implementation.
  • DRY / KISS — small, focused, single-responsibility files.
  • Granular commits; feature work happens on dedicated branches.

Built with Bubble Tea / Lip Gloss, shopspring/decimal, and BurntSushi/toml.

Known limitations

  • In-memory only. No persistence — all state is lost on restart.
  • Polymarket has no published reference price. PM direction markets resolve on a Chainlink price stream whose open price isn't exposed in the market data, so PM rows don't show an @-target (deriving one from another source is under discussion). Kalshi (floor_strike) and Hyperliquid (market description) do publish theirs.
  • Read-only. No trading, no order placement, no authenticated endpoints.
  • No CI. make test must be green locally before each commit.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages