AArtery
On this page

Event Correlation

Artery's biggest value-add over per-provider SDKs is a single API that treats the same real-world question as one event — even though Polymarket, Kalshi, and Hyperliquid HIP-4 use completely different identifiers and metadata schemas.

The problem

The same question shows up everywhere differently:

ProviderIdentifierWhere threshold lives
Polymarket0xa8aceb0fb04828602057be289979652104e8f7b7ebda4489c53a97c60b9063d9 (256-bit hash)Inside question text
KalshiKXBTCD-26MAY0917-T69999.99 (hierarchical ticker)Suffix after T + floor_strike field
Hyperliquid HIP-4outcome: 10 (integer)description KV string targetPrice:79583

There is no external authority that maps these. Artery builds the mapping itself.

Structured parsers (current)

For crypto price-binary markets, all three providers expose enough structure to parse without NLP:

textHIP-4 description:  class:priceBinary|underlying:BTC|expiry:20260509-0600|targetPrice:79583
                    ↓ parse
                    { underlying: 'BTC', threshold: 79583, op: 'gte', expiresAt: '2026-05-09T06:00:00Z' }
 
Kalshi ticker:      KXBTCD-26MAY0917-T69999.99
                    ↓ parse
                    { underlying: 'BTC', threshold: 69999.99, op: 'gte', expiresAt: '2026-05-09T17:00:00Z' }
 
Polymarket question: "Will the price of Bitcoin be above $70,000 on May 9?"
                     ↓ regex + endDateIso
                    { underlying: 'BTC', threshold: 70000, op: 'gte', expiresAt: '2026-05-09T00:00:00Z' }

Canonical clustering key

Each parsed market is hashed into a canonical key; markets sharing a key join the same ArteryEvent:

textcanonical_key = `${underlying}|${op}|${round(threshold)}|${YYYY-MM-DD}`
            = "BTC|gte|70000|2026-05-09"

Day-level (rather than full-timestamp) precision lets Polymarket "by May 9" cluster with Kalshi "26MAY0917" — they resolve on the same trading day.

What gets clustered today

Live numbers from a recent run:

Provider sourceMarkets parsedSource
Polymarket80regex_title
Kalshi577structured_ticker
Hyperliquid HIP-41structured_description
Universal events created504
Multi-provider clusters17 (all BTC ≥ $70K-$88K on 2026-05-09 across Polymarket × Kalshi)

The Polymarket vs Kalshi pair is the most active cluster because both venues list dense daily price-binary grids. HIP-4 is sparse (1 market today) and its strikes (e.g. $79,583) rarely match other venues' round-number strikes.

Endpoints

EndpointWhat
GET /v1/eventsList + filter universal events
GET /v1/events/:idOne event with all linked markets + metadata
GET /v1/events/:id/spreadCross-platform price snapshot — yesBid/yesAsk/yesMid per leg
GET /v1/events/:id/arbitrageBest buy-leg / sell-leg + net edge after fees (no threshold)
GET /v1/events/:id/settlementPer-provider resolution history + consensus score
POST /v1/events/reconcileForce a reconcile pass (admin scope)
POST /v1/events/spread-stream/tickForce a spread-stream tick (admin scope)
bashcurl 'https://api.artery.questflow.ai/v1/events?underlying=BTC&min_providers=2&limit=5' \
  -H "Authorization: Bearer $TOKEN"
json{
  "events": [
    {
      "universalId": "evt_94c9a200-8152-43b1-8b10-46214bea101a",
      "canonicalName": "BTC ≥ $70,000 by 2026-05-09",
      "kind": "price_binary",
      "underlying": "BTC",
      "threshold": 70000,
      "op": "gte",
      "resolutionDate": "2026-05-09T00:00:00Z",
      "links": [
        {
          "provider": "polymarket",
          "providerMarketId": "0xa8aceb0fb04828602057be...",
          "source": "regex_title",
          "metadata": {
            "yesTokenId": "55007456209846753968...",
            "noTokenId":  "10093425111459431137..."
          }
        },
        {
          "provider": "kalshi",
          "providerMarketId": "KXBTCD-26MAY0917-T69999.99",
          "source": "structured_ticker"
        }
      ]
    }
  ]
}

Filtering

GET /v1/events supports the following query params:

ParamTypeEffect
underlyingstringBTC, ETH, SOL, etc. — case-insensitive
resolution_afterISO 8601Only events resolving after this time
resolution_beforeISO 8601Only events resolving before this time
min_providersintOnly multi-provider clusters when ≥2
limitintCap result size

Recent additions

FeatureStatusNotes
price_range event kindKalshi range markets with both cap_strike + floor_strike produce kind: 'price_range' events
Spread streaming artery:stream:event:<id>5s tick, change-debounced 0.5¢
Arbitrage signal stream artery:stream:arbitrage:<id>Net-edge after fees ≥ 50 bps default
GET /v1/events/:id/arbitrageOne-shot computation with no threshold
GET /v1/events/:id/settlementPer-provider resolution history + consensus score
Per-provider fee scheduleART_FEES_<PROVIDER>_TAKER_BPS env overrides
Settlement consensus scoreIn-memory; Postgres persistence on roadmap

What this is not (yet)

  • Election / outcome / sports markets are not clustered yet — they need entity-NER + temporal matching (roadmap)
  • Approximate threshold matching — Polymarket "above $70k" and Kalshi T69999.99 happen to round to the same $70K bucket, but T69500 would NOT cluster with $70K (roadmap)
  • Embedding-based clustering for long-tail markets (roadmap)
  • Postgres persistence of registry + settlement history (roadmap)

How parsers fail (and that's OK)

Parsers return null for anything they can't handle. The reconcile loop silently drops nulls and only registers what it understands. So:

  • Sports markets, custom event markets, jokes, and anything non-crypto → not in /v1/events
  • Range markets (Kalshi has cap_strike + floor_strike) → not yet parsed
  • class:other HIP-4 outcomes → not parsed

This keeps the registry tight and high-confidence. Recent versions progressively relaxes parsers as we add NER + embedding fallbacks.

Reconcile schedule

The reconcile job runs:

  • Once on app bootstrap (1.5s after listen)
  • Every 5 minutes thereafter
  • On-demand via POST /v1/events/reconcile (admin scope)

Disable with ART_RECONCILE_OFF=true (useful for tests where upstream calls are noisy).

See also

Edit this page on GitHubLast updated
Event Correlation · Artery API Docs