Skip to main content

Historical tick-by-tick L2 orderbook reconstruction and backtesting primitives for Polymarket. Spread, impact, imbalance, and every trade, delivered as a DataFrame.

Project description

MarketLens Python SDK

Historical and real-time prediction market data — full L2 orderbook reconstruction, microstructure analytics, and backtesting primitives for Polymarket.

pip install marketlens
from marketlens import MarketLens

client = MarketLens(api_key="mk_...")  # or set MARKETLENS_API_KEY env var

Order Book Replay

Replay full L2 book state for any market. Each tick yields (Market, OrderBook) — one line to go from market ID to book-level analysis.

from datetime import datetime, timezone
from marketlens import MarketLens

client = MarketLens()

for market, book in client.orderbook.walk(market_id, after=start, before=end):
    print(f"mid={book.midpoint}  spread={book.spread_bps():.0f}bps")

# Or as a DataFrame
df = client.orderbook.walk(market_id, after=start, before=end).to_dataframe()
# Columns: midpoint, spread, spread_bps, imbalance, weighted_midpoint,
#          bid_depth, ask_depth, market_id, winning_outcome

Series Backtesting

Walk every market in a rolling series chronologically — same orderbook.walk() interface, just pass a series slug instead of a market ID.

for market, book in client.orderbook.walk(
    "btc-up-or-down-5m", status="resolved",
    after=datetime(2026, 3, 5, 8, 40, tzinfo=timezone.utc),
    before=datetime(2026, 3, 5, 8, 45, tzinfo=timezone.utc),
):
    if (spread := book.spread_bps()) and spread < 200:
        entry = book.impact("BUY", "100")
        # ...

Backtesting

Define a strategy by subclassing Strategy and implementing event hooks. Run it against any market or rolling series — the engine replays L2 book data tick-by-tick with realistic execution simulation.

from marketlens import MarketLens
from marketlens.backtest import Strategy

class BuyOnTightSpread(Strategy):
    def on_book(self, ctx, market, book):
        if ctx.position().side == "FLAT" and book.spread_bps() and book.spread_bps() < 200:
            ctx.buy_yes(size="100")

client = MarketLens()
result = client.backtest(BuyOnTightSpread(), "btc-up-or-down-5m",
                         initial_cash="10000.0000",
                         after="2026-03-05T10:00Z", before="2026-03-05T10:05Z")
print(result)
result.trades_df()       # per-fill DataFrame
result.settlements_df()  # per-market settlement P&L
result.equity_df()       # equity curve over time

Strategy hooks

Hook Called when
on_book(ctx, market, book) Every book state change (snapshot or delta)
on_trade(ctx, market, book, trade) Every historical trade
on_fill(ctx, market, fill) Your order is filled
on_market_start(ctx, market, book) A new market begins in the walk
on_market_end(ctx, market) A market's data is exhausted, before settlement

Execution realism

The engine simulates realistic execution by default:

Parameter Default Description
initial_cash required Starting capital (e.g. "10000.0000") — buy orders exceeding cash are cancelled
latency_ms 50 Order-to-fill delay — orders fill against the book state N ms after submission
limit_fill_rate 0.1 Fraction of historical trade size that fills your limit order (queue position)
slippage_bps 0 Extra price penalty on market order fills (on top of L2 book walk)
fees "polymarket" Fee model — auto-detects per category (crypto vs sports). Set to None for zero fees
max_fill_fraction 1.0 Max fraction of each book level consumed per order
include_trades True Fetch trade data (required for limit order fills and on_trade)
# Conservative simulation
result = client.backtest(strategy, "btc-up-or-down-5m",
                         initial_cash="10000.0000",
                         latency_ms=100, slippage_bps=5,
                         limit_fill_rate=0.1)

# Optimistic (instant fills, no queue, no fees)
result = client.backtest(strategy, "btc-up-or-down-5m",
                         initial_cash="10000.0000",
                         latency_ms=0, limit_fill_rate=1.0, fees=None)

For full control, use BacktestEngine with BacktestConfig directly.

Implied Probability Surfaces

Implied distributions extracted from multi-strike prediction markets — survival curves, density functions, and barrier probabilities, fitted via isotonic regression. Updated every 5 minutes.

for surface in client.signals.surfaces(underlying="BTC"):
    print(f"{surface.series_title}  [{surface.surface_type}]")
    print(f"  mean={surface.implied_mean}  cv={surface.implied_cv}%  skew={surface.implied_skew}")

    if surface.surface_type == "survival":
        for s in surface.survival_strikes():
            print(f"  K={s.strike:>10,.0f}  P(above)={s.fitted_prob:.3f}")

    elif surface.surface_type == "density":
        for b in surface.density_buckets():
            lo = f"${b.lower:,.0f}" if b.lower else "<tail"
            hi = f"${b.upper:,.0f}" if b.upper else "tail>"
            print(f"  {lo}-{hi}  p={b.normalized_prob:.3f}")

# Historical surface snapshots
df = client.signals.history("btc-multi-strikes-weekly", event_id).to_dataframe()

Three surface types are available:

Type Source Fitting Output
survival Multi-strike "above $X" markets PAVA monotone decreasing survival_strikes()
density Neg-risk range + tail markets Normalized probabilities density_buckets()
barrier Hit-price reach/dip markets PAVA per direction barrier_strikes()

Browse Series Events

Non-rolling series (e.g. weekly strike groups) are browsed by event:

for event in client.series.events("bitcoin-hit-price-weekly"):
    markets = client.events.markets(event.id).to_list()
    print(f"{event.title}{len(markets)} strikes")

OrderBook Analytics

Every OrderBook — live snapshot or replayed — carries the same analytical methods:

book = client.orderbook.get(market_id)

book.microprice()              # size-weighted mid from best level
book.weighted_midpoint(n=3)    # n-level weighted mid
book.spread_bps()              # spread in basis points
book.imbalance()               # full-book bid/ask imbalance [-1, 1]
book.imbalance(levels=3)       # top-of-book imbalance
book.impact("BUY", "1000")     # VWAP execution price for $1k market buy
book.slippage("BUY", "1000")   # slippage from mid for $1k order
book.depth_within("0.02")      # (bid_depth, ask_depth) within 2c of mid

Resources

Namespace Methods
client.markets list() get() trades() candles()
client.events list() get() markets()
client.series list() get() markets() walk() events()
client.orderbook get() history() metrics() walk()
client.signals surfaces() surface() history()

All list methods return auto-paginating iterators with .to_list() and .to_dataframe().

df = client.markets.candles(market_id, resolution="1h").to_dataframe()
trades = client.markets.trades(market_id, after=start, before=end).to_list()
top = client.markets.list(status="active", sort="-liquidity", limit=5).first_page()

Async

Every resource, iterator, and replay helper has an async counterpart.

from marketlens import AsyncMarketLens

async with AsyncMarketLens() as client:
    async for market, book in await client.orderbook.walk(market_id, after=start, before=end):
        print(book.microprice(), book.imbalance(levels=3))

Examples

Example Description
backtest_basic.py Buy YES on tight spread — minimal backtesting example
backtest_imbalance.py Imbalance signal with exit before settlement, fees and slippage
backtest_limit_orders.py Market-making with limit orders and fill rate simulation
single_market_replay.py Replay a single market's order book tick by tick
microstructure.py Feature matrix from L2 replay — imbalance vs outcome signal
series_backtest.py Spread-timing strategy with per-trade P&L across a rolling series
event_strikes.py Browse strike-level markets in a non-rolling series
execution_cost.py Live book depth, spread, impact/slippage across order sizes
implied_surfaces.py Implied probability surfaces — survival, density, barrier

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

marketlens-0.4.0.tar.gz (43.5 kB view details)

Uploaded Source

Built Distribution

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

marketlens-0.4.0-py3-none-any.whl (39.6 kB view details)

Uploaded Python 3

File details

Details for the file marketlens-0.4.0.tar.gz.

File metadata

  • Download URL: marketlens-0.4.0.tar.gz
  • Upload date:
  • Size: 43.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for marketlens-0.4.0.tar.gz
Algorithm Hash digest
SHA256 51f91ef93478e546032f6679b63d12ba1d4144d38f5fa314068222d1299312c7
MD5 fb7480a3d7529d33f3a21cbbee7855b1
BLAKE2b-256 495b9809af45463a194ae1f7c1c772854753aba1888ed4bdfb9825af1836083f

See more details on using hashes here.

File details

Details for the file marketlens-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: marketlens-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 39.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for marketlens-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c6da3c115ffce5bbcc0aa393b8011bae29a8bc4e90de7bb74b7fc751eda726c8
MD5 e364c2def3489e195239d080448c4b5c
BLAKE2b-256 8a7c47fa7cf9a40245bcd1132f7f53ea0d6b8826ea9997a59ea26b1fe0f8aa82

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