Python SDK
mackinac-client normalizes live and historical data from 13 venues — Hyperliquid, dYdX V4, Uniswap V3/V4, Pendle, Spectra, GMX, Vertex, Ostium, Sushi, PancakeSwap, Balancer, and the consolidated AMM book — into a single typed message schema. Every message comes back as a Pydantic v2 model with the same field names regardless of venue. The library ships both a synchronous Mackinac client and an AsyncClient for asyncio code, with automatic reconnect and re-subscribe built in.
- GitHub: mackinac/mackinac-python
- PyPI: mackinac-client
Install
pip install mackinac-clientOptional extras:
pip install 'mackinac-client[tables]' # pandas — needed for rate/AMM table examplespip install 'mackinac-client[wallet]' # eth-account — needed for from_wallet authRequires Python 3.10+. Pin pydantic>=2,<3, httpx>=0.27, websockets>=12.
Tiers and limits
Know your cap before you run anything:
| Tier | Symbol cap | History lookback |
|---|---|---|
Anonymous — Mackinac() | 3 per IP across all sessions | 24 hours |
api — from_api_key("mk_live_…") | high | full since inception |
Live trades — no credentials
The sync API requires no event loop. types=PrintMessage filters at the iterator level — no isinstance checks in your loop, no buffering other message types.
from mackinac import Mackinac, PrintMessage
with Mackinac() as m: with m.subscribe("hl:ETH", types=PrintMessage) as feed: for trade in feed: side = "buy " if trade.side == 1 else "sell" print(f"{side} {trade.price:>10,.2f} x {trade.size}")This runs on the anonymous tier — no API key needed, 3-symbol cap per IP.
Historical funding rates
history_funding is a generator that auto-paginates. ratePct is annualised percentage. Symbols with slashes (XAU/USD) are URL-encoded automatically on REST calls.
from datetime import datetime, timedelta, timezonefrom mackinac import Mackinac
NOTIONAL = 100_000HOURS = 12end = datetime.now(timezone.utc)start = end - timedelta(hours=HOURS)
with Mackinac() as m: hl = list(m.history_funding("hl", "ETH", start=start, end=end)) xau = list(m.history_funding("ostium", "XAU/USD", start=start, end=end))
def carry(rates): if not rates: return 0.0, 0.0 avg = sum(r.ratePct for r in rates) / len(rates) # annualised % cost = NOTIONAL * (avg / 100) * (HOURS / 8760) return avg, cost
print(f"HL ETH ann={carry(hl)[0]:>6.2f}% cost=${carry(hl)[1]:>7.2f}")print(f"Ostium XAU ann={carry(xau)[0]:>6.2f}% cost=${carry(xau)[1]:>7.2f}")See Funding Rates, Rollover Fees, and the Implicit Carry of Traditional Futures for the full carry-cost analysis this example is drawn from.
Cross-venue basis
The same QuoteMessage model works for a CLOB perpetual (hl), an AMM spot pool (uni), and an oracle perpetual (gmx). QuoteMessage.bids[0].price is the same field name on all three.
from mackinac import Mackinac, QuoteMessage
mids = {}with Mackinac.from_api_key("mk_live_...") as m: with m.subscribe("hl:ETH", "uni:WETH/USDC", "gmx:ETH", types=QuoteMessage) as feed: for q in feed: if not (q.bids and q.asks): continue mids[f"{q.exchange}:{q.symbol}"] = (q.bids[0].price + q.asks[0].price) / 2 if len(mids) == 3: hl, uni, gmx = mids["hl:ETH"], mids["uni:WETH/USDC"], mids["gmx:ETH"] print(f"HL {hl:.2f} Uni {uni:.2f} GMX {gmx:.2f} " f"HL–Uni {(hl-uni)/uni*1e4:+.1f}bps") breakfrom_api_key bypasses the 3-symbol anonymous cap and handles auth on both the WebSocket connection and REST history calls automatically.
Next steps
- Reference — auth methods, all REST generators, message types, symbol helpers, reconnect behavior, error handling
- Examples on GitHub — 11 runnable scripts (≤60 lines each)