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, yield rates, and lending markets across 16 DeFi venues including Hyperliquid, dYdX V4, Uniswap V3/V4, Pendle, Spectra, GMX, Vertex, Ostium, Aave V3, Compound V3, Morpho Blue, 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
dYdX V4 perp dydx:<BASE>-USD dydx:ETH-USD
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
GMX perp gmx:<SYMBOL> gmx:BTC
Vertex perp vertex:<SYMBOL> vertex:SOL
Ostium (RWA) ostium:<BASE>/<QUOTE> ostium:XAU/USD
Aave V3 lending aave:<ASSET> or aave:all aave:USDC
Compound V3 lending compound:<ASSET> or compound:all compound:USDC
Morpho Blue lending morpho:<ASSET> or morpho:all morpho:USDC
Lending actions (all venues) lending:actions lending:actions
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, dYdX, GMX, Vertex, Ostium
DepthMessage Concentrated-liquidity tick snapshot + market impact AMM
LiquidityMessage LP Mint/Burn event AMM
RateMarketMessage Yield-market or lending-market snapshot Pendle, Spectra, Aave, Compound, Morpho
RateDepthMessage Depth-at-size APY quote Pendle only
LendingActionMessage On-chain lending event (Supply/Borrow/Liquidate/...) Aave, Compound, Morpho
RateModelParamsMessage IRM curve parameters Aave, Compound, Morpho
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

Examples with both API flavours use a _sync / _async filename suffix. Older single-flavour examples keep their original names.

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 Cross-venue ETH basis across CLOB, AMM, and oracle venues
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
examples/lending_rates_sync.py Aave + Compound + Morpho supply/borrow APY table (sync Mackinac)
examples/lending_rates_async.py Same lending-rate table using AsyncClient
examples/liquidation_watch_sync.py Live liquidation feed across all lending venues (sync)
examples/liquidation_watch_async.py Same liquidation feed using AsyncClient

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.2.0.tar.gz (51.6 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.2.0-py3-none-any.whl (35.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: mackinac_client-0.2.0.tar.gz
  • Upload date:
  • Size: 51.6 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.2.0.tar.gz
Algorithm Hash digest
SHA256 7d715bb12615021818db851f57bd706216070910b60035356f4d878254f8c072
MD5 b3040c3f60bd6187b8d6995c0bf24e6d
BLAKE2b-256 9812478bc792b7786eed9ecc1e41a75aed9fe43a1f6ed7829519d7a690bd2157

See more details on using hashes here.

File details

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

File metadata

  • Download URL: mackinac_client-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 35.8 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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bc702144b6ef534e274db6259782ce38ea14bc021dcc8fcb308f86fce57b4930
MD5 9786b8d1a9176f38980a56c4db7e5d00
BLAKE2b-256 ec84d15e2ce95ff319fdc0f60c46dc2c0e2d03f504fb65112942a3e4c264abd1

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