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)side—0= aggressive sell (bid hit),1= aggressive buy (ask lift),2= unknownratePct(FundingMessage) — annualized percentage, e.g.10.95= 10.95%/yrimpliedApy(RateMarketMessage) — decimal fraction, e.g.0.058= 5.8%ptPrice— Pendle: USD price; Spectra: fraction of IBTtvl— 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
ErrorMessageto 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2009feade1d103a840edf8a02c58e842e7bc2dd3f95aaca684bdf7527f5ec6c0
|
|
| MD5 |
09cd4a289452cf67639b1ddacf54c192
|
|
| BLAKE2b-256 |
81c5e8944caa503b3b8bb4a059a33f672e1ea278ed72dfcdaa6ec07e8e250090
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16af41fb3b767b99f3f0df532c8411136562545547107647a1e7cb4fd23fb5db
|
|
| MD5 |
651793b27ce311856b944b75d23d91dc
|
|
| BLAKE2b-256 |
2cc3d29103496fb52a970f533e53576138084d86f8822def53b59f7dff9e3e49
|