Skip to main content

Python client for the Mackinac market-data API — live quotes, trades, funding, and yield rates across 13 DeFi venues

Project description

mackinac-client

Python client for the Mackinac market-data API — live quotes, trades, funding rates, and yield rates across 13 DeFi venues including Hyperliquid, Uniswap V3/V4, Pendle, Spectra, GMX, Vertex, Ostium, and more.

pip install mackinac-client

Quick start

Synchronous (no asyncio required)

No credentials required for free-tier access (3 concurrent symbols):

from mackinac import Mackinac, QuoteMessage, PrintMessage

with Mackinac() as m:
    # What symbols are on HL?
    print(m.live_symbols("hl"))

    # Stream only quotes — types= filters the feed, no isinstance needed
    with m.subscribe("hl:ETH", types=QuoteMessage) as feed:
        for quote in feed:
            bid = quote.bids[0].price if quote.bids else None
            ask = quote.asks[0].price if quote.asks else None
            print(f"ETH  bid {bid}  ask {ask}")
            break  # just the first one

    # Stream only trades
    with m.subscribe("hl:ETH", types=PrintMessage) as feed:
        for trade in feed:
            side = "buy" if trade.side == 1 else "sell"
            print(f"ETH  {side}  {trade.price}  ×  {trade.size}")
            break

Async (for existing asyncio code)

import asyncio
from mackinac import AsyncClient, QuoteMessage

async def main():
    async with AsyncClient() as client:
        print(await client.live_symbols("hl"))

        async with client.subscribe("hl:ETH", types=QuoteMessage) as feed:
            async for quote in feed:
                print(f"ETH  bid {quote.bids[0].price}  ask {quote.asks[0].price}")
                break

asyncio.run(main())

Authentication

Both Mackinac (sync) and AsyncClient (async) share the same constructors:

API key (recommended for servers)

m      = Mackinac.from_api_key("mk_live_...")       # sync
client = AsyncClient.from_api_key("mk_live_...")    # async

API keys are long-lived and authenticate WebSocket connections only. Obtain them from the dashboard (requires tier api).

JWT (browser / short-lived sessions)

m      = Mackinac.from_jwt("eyJhbGciOi...")
client = AsyncClient.from_jwt("eyJhbGciOi...")

JWTs expire after 7 days. Refresh via client.refresh_token() / await client.refresh_token().

Wallet sign-in (EIP-191)

pip install 'mackinac-client[wallet]'
m      = Mackinac.from_wallet(private_key="0xdeadbeef...", address="0xYourWalletAddress")
client = await AsyncClient.from_wallet(
    private_key="0xdeadbeef...",
    address="0xYourWalletAddress",
)

Subscribing to live data

async with client.subscribe("hl:ETH", "uni:WETH/USDC", "rates:all") as feed:
    async for msg in feed:
        match msg.type:
            case "quote":
                print(msg.exchange, msg.symbol, msg.bids[0].price)
            case "print":
                print(msg.price, msg.size, msg.side)  # side: 0=sell 1=buy
            case "rate_market":
                print(msg.symbol, f"{msg.impliedApy * 100:.2f}% APY")
            case "funding":
                print(msg.symbol, f"{msg.ratePct:.4f}% ann.")

All messages are typed Pydantic v2 models. Use isinstance() or match on msg.type to branch per message type.

Subscribe key format

Venue Key format Example
Hyperliquid perp hl:<SYMBOL> hl:ETH
Uniswap V3 (Arbitrum) uni:<BASE>/<QUOTE> uni:WETH/USDC
Uniswap V4 (Arbitrum) univ4:<BASE>/<QUOTE> univ4:WETH/USDC
SushiSwap V3 sushi:<BASE>/<QUOTE> sushi:WETH/USDC
PancakeSwap V3 pancake:<BASE>/<QUOTE> pancake:WBTC/WETH
Lighter perp lighter:<SYMBOL> lighter:ETH
GMX perp gmx:<SYMBOL> gmx:BTC
Vertex perp vertex:<SYMBOL> vertex:SOL
Ostium (RWA) ostium:<BASE>/<QUOTE> ostium:XAU/USD
Pendle/Spectra (all) rates:all
Pendle/Spectra (trades) rates:swaps
Specific yield market rates:<symbol-or-address> rates:PT-weETH-25JUN2026
AMM consolidated NBBO ammbook:<BASE>/<QUOTE> ammbook:WETH/USDC (professional+)

Use mackinac.symbols helpers to construct keys safely:

from mackinac import symbols

symbols.amm_pair("WETH", "USDC")        # "WETH/USDC"
symbols.ostium_pair("XAU")              # "XAU/USD"
symbols.pendle_address("0xC62D...")     # "0xc62d..."  (lowercased)
symbols.rates_all()                     # "rates:all"

Unsubscribing mid-session

await feed.unsubscribe("hl:ETH")

Auto-reconnect

The library reconnects automatically on drops (1s → 2s → 4s → … → 30s cap) and re-subscribes all active symbols. The async for loop continues seamlessly — no user-side retry logic needed.

Raw websockets note

If you connect with the websockets library directly (rather than via AsyncClient), set max_size to at least 10 MB. The initial trade snapshot sent on subscribe can exceed the library's 1 MB default:

async with websockets.connect(url, max_size=10 * 1024 * 1024) as ws:
    ...

AsyncClient already sets this internally.


REST methods

Symbol discovery

# Which exchanges are live?
await client.markets()                        # dict[exchange, MarketStatus]

# All subscribable symbols on one venue
await client.live_symbols("hl")              # list[str]
await client.live_symbols("pendle")          # list[str]

# All venues at once
await client.live_symbols()                  # dict[exchange, list[str]]

# Symbols with persisted history
await client.historical_symbols("hl")        # list[str]

# Which venues quote ETH?
await client.instrument("ETH")               # InstrumentVenues

Historical data

History iterators paginate automatically — no cursor handling needed:

# Trades
async for trade in client.history_trades("hl", "ETH",
                                          start="2026-04-01",
                                          end="2026-04-08"):
    print(trade.time, trade.price, trade.size, trade.side)

# 5-second quote snapshots
async for snap in client.history_quotes("uni", "WETH/USDC",
                                         start="2026-04-01"):
    print(snap.time, snap.bids[0].price if snap.bids else None)

# Funding rates
async for fr in client.history_funding("hl", "ETH"):
    print(fr.ratePct, fr.intervalHrs)

# Yield-market snapshots
async for rate in client.history_rates("0xc62d...", start="2026-03-01"):
    print(rate.impliedApy, rate.tvl)

All methods accept start/end as ISO 8601 strings or epoch milliseconds. Default range: last 24 hours. Maximum lookback: 1 day (free tier) or 90 days (professional/api).

Account

await client.me()                  # JWT claims
await client.subscription_status() # current tier + expiry (re-reads on-chain)
await client.refresh_token()       # new 7-day JWT with updated tier claims

Message types

Type Description Venues
QuoteMessage Top-of-book snapshot All
PrintMessage Trade execution CLOB, AMM, Oracle*, Yield
FundingMessage Perpetual funding rate HL, Lighter, GMX, Vertex, Ostium
DepthMessage Concentrated-liquidity tick snapshot + market impact AMM
LiquidityMessage LP Mint/Burn event AMM
RateMarketMessage Yield-market snapshot Pendle, Spectra
RateDepthMessage Depth-at-size APY quote Pendle only
AmmBookMessage Consolidated AMM NBBO (professional+) Virtual
AmmLiquiditySnapshotMessage LP event backfill on subscribe (professional+) Virtual
ArbFlagMessage Cross-venue arb gap signal (super_admin) Virtual
SpreadMessage Same-venue cross-tier spread (professional+) Uni, Univ4
FeedStaleMessage Feed health: stale Broadcast
FeedLiveMessage Feed health: recovered Broadcast
ServerClosingMessage Graceful shutdown notice Broadcast
ErrorMessage Error frame Server response

*GMX and Vertex do not emit PrintMessage (oracle settle on net positions).

Key field conventions

  • time — always epoch milliseconds (int)
  • side0 = aggressive sell (bid hit), 1 = aggressive buy (ask lift), 2 = unknown
  • ratePct (FundingMessage) — annualized percentage, e.g. 10.95 = 10.95%/yr
  • impliedApy (RateMarketMessage) — decimal fraction, e.g. 0.058 = 5.8%
  • ptPrice — Pendle: USD price; Spectra: fraction of IBT
  • tvl — Pendle: USD; Spectra v1: underlying-asset units (no USD oracle)
  • amount0/amount1/amount (LiquidityMessage) — decimal strings in raw ERC-20 units

Tiers & limits

Tier WS symbol cap ammbook / spread Historical data
Free (anonymous) 3 1 day
Professional 50 90 days
API 100 90 days

Error handling

from mackinac.exceptions import (
    AuthError,      # 401 / WS auth_error
    TierError,      # 403 / WS subscription_required
    RateLimitError, # 429 / WS rate_limited — has .retry_after
    SymbolLimitError,
    InvalidSymbolError,
    ServerError,    # 5xx / WS internal_error
)

try:
    async with client.subscribe("ammbook:WETH/USDC") as feed:
        ...
except TierError:
    print("Upgrade to professional tier for ammbook access")
  • REST 4xx → typed exception immediately (no retry)
  • REST 5xx → retried 3× with exponential backoff, then ServerError
  • WS auth_error / subscription_required → raised, reconnect stops
  • Other WS errors → delivered as ErrorMessage to the iterator

Installing extras

pip install 'mackinac-client[wallet]'   # EIP-191 wallet sign-in
pip install 'mackinac-client[tables]'   # pandas, for amm_fee_table + rate_table examples
pip install 'mackinac-client[dev]'      # testing + model codegen

Examples

File What it shows
examples/subscribe_quotes.py HL ETH live top-of-book
examples/historical_trades.py 7-day Ostium XAU/USD → CSV
examples/yield_rates.py Pendle + Spectra live APY table
examples/reconnect_pattern.py Iterator survives forced disconnect
examples/multi_venue_basis.py HL / Uni / Lighter cross-venue basis
examples/amm_fee_table.py AMM depth → pandas fee-tier impact table
examples/rate_table.py rate_market stream → pandas DataFrame
examples/funding_carry.py CME vs HL vs Ostium carry cost comparison

Versioning

mackinac.__version__ follows the library release. mackinac.__protocol_version__ matches the API schema version; a major bump means a breaking wire-format change.


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mackinac_client-0.1.0.tar.gz (41.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mackinac_client-0.1.0-py3-none-any.whl (30.7 kB view details)

Uploaded Python 3

File details

Details for the file mackinac_client-0.1.0.tar.gz.

File metadata

  • Download URL: mackinac_client-0.1.0.tar.gz
  • Upload date:
  • Size: 41.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.12.9 HTTPX/0.28.1

File hashes

Hashes for mackinac_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2009feade1d103a840edf8a02c58e842e7bc2dd3f95aaca684bdf7527f5ec6c0
MD5 09cd4a289452cf67639b1ddacf54c192
BLAKE2b-256 81c5e8944caa503b3b8bb4a059a33f672e1ea278ed72dfcdaa6ec07e8e250090

See more details on using hashes here.

File details

Details for the file mackinac_client-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mackinac_client-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 30.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.12.9 HTTPX/0.28.1

File hashes

Hashes for mackinac_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 16af41fb3b767b99f3f0df532c8411136562545547107647a1e7cb4fd23fb5db
MD5 651793b27ce311856b944b75d23d91dc
BLAKE2b-256 2cc3d29103496fb52a970f533e53576138084d86f8822def53b59f7dff9e3e49

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page