Skip to main content

glimpse-markets

The official Python client for Glimpse's Nmarket prediction-market API — for people building forecasting algorithms and trading bots on Glimpse without writing HTTP plumbing by hand.

pip install glimpse-markets

Status: every read endpoint, every trade endpoint, the glimpse CLI, an async client, real-time streaming, and a small bot-building layer are all implemented.

Contents

Features

  • Sync and async clients with an identical method surface — every market-data, portfolio, and trading endpoint the public API exposes.
  • Typed responses. Every call returns a Pydantic model, not a raw dict — autocomplete and validation instead of resp["message"]["..."].
  • Built-in paper trading. Glimpse has no sandbox environment — every API key is a live key. dry_run=True simulates trades client-side through the free /trades/estimate endpoint, so you can test a strategy against real live prices without risking real funds.
  • Real-time market data over Glimpse's WebSocket feed, with no polling loop to write yourself.
  • A minimal bot-building layer (Strategy / StrategyRunner) for wiring strategy logic up to the live feed without hand-rolling the connect/dispatch/reconnect plumbing.
  • A glimpse CLI for one-off calls from the terminal — check a balance, get a quote, place a trade — without writing any code.
  • Safety around the sharp edges. The API has no idempotency key, so a network failure mid-trade is surfaced as a distinct GlimpseAmbiguousTradeStateError instead of being silently retried (which could double-execute a real trade) or silently swallowed.

Quickstart

from glimpse_markets import Client

with Client(api_key="glp_live_...") as client:
    print(client.wallet_balance())

    batch = client.batches().batches[0]
    print(client.batch_active_markets(batch.batch_id))

# Market-data endpoints (batches, markets, quotes, stats) are public --
# no API key needed:
with Client() as client:
    print(client.market_quotes(topic_id=6674))

Or configure from the environment (GLIMPSE_API_KEY, GLIMPSE_BASE_URL), optionally via a .env file in your working directory:

from glimpse_markets import Client

with Client.from_env() as client:
    print(client.portfolio_summary())

Generate an API key from your Glimpse account under Settings → Developer API Keys.

Trading

from glimpse_markets import Client, TradeLeg, EnterMultiTopicLegGroup

with Client.from_env() as client:
    # Always check cost and price impact first -- free, no API key required.
    estimate = client.estimate_trade(6674, "buy", [TradeLeg(option_id=500, contracts=10)])
    print(estimate)

    topics = [EnterMultiTopicLegGroup(topic_id=6674, legs=[TradeLeg(option_id=500, contracts=10)])]
    result = client.enter_multi_topic_multi_leg(topics)
    print(result)

    client.exit_consolidated(topic_id=6674, option_id=500)  # exits the full position

Entering a position is buy-only, by design of the underlying API — exit an existing position to realize a "sell." exit_consolidated_multi, exit_multi_topic_multi_leg, and exit_batch cover multi-leg and whole-batch exits.

Paper trading (dry run)

Glimpse has no sandbox environment — every API key trades with real funds. Pass dry_run=True (client-wide, or per call via dry_run=... on any trade method) to paper-trade instead: every enter/exit call is priced through the side-effect-free /trades/estimate endpoint rather than placing a real order, and returns a DryRunTradeResult — a distinct type from a real response, so a simulated fill can never be mistaken for a real one:

with Client.from_env(dry_run=True) as client:
    result = client.enter_multi_topic_multi_leg(topics)
    assert result.simulated is True  # no order was placed

A network failure while an enter/exit call is genuinely in flight raises GlimpseAmbiguousTradeStateError rather than being retried — the API has no idempotency key, so the client can't safely guess whether the trade went through. Check client.portfolio_active() before resubmitting.

Async client

AsyncClient mirrors Client's entire method surface — same names, same signatures, await in front — built on httpx.AsyncClient, for bots already running an asyncio event loop:

import asyncio
from glimpse_markets import AsyncClient, TradeLeg

async def main():
    async with AsyncClient.from_env() as client:
        print(await client.wallet_balance())
        estimate = await client.estimate_trade(6674, "buy", [TradeLeg(option_id=500, contracts=10)])
        print(estimate)

asyncio.run(main())

Real-time streaming

Glimpse pushes live quote updates over a public WebSocket feed — no API key required, no polling loop to write:

import asyncio
from glimpse_markets import AsyncClient

async def main():
    async with AsyncClient() as client:
        async with client.stream_market_updates(topic_id=6674) as stream:
            async for update in stream:
                print(update.data.quotes or update.data.binary_quotes)

asyncio.run(main())

Pass topic_id and/or batch_id to filter the feed to one market or batch (call stream.subscribe(...) again later to change the filter without reconnecting); pass neither to receive every market's updates. There is no separate "market resolved" event on this feed — detect resolution by polling quote_mode via market_quotes() or batch_active_markets_page() instead.

MarketStream also works standalone: from glimpse_markets import MarketStream.

Building a bot

Strategy and StrategyRunner wire a MarketStream up to your logic without you writing the connect/dispatch/reconnect plumbing yourself:

import asyncio
from glimpse_markets import AsyncClient, MarketUpdate, Strategy, StrategyRunner

class MyStrategy(Strategy):
    async def on_quote(self, update: MarketUpdate) -> None:
        # called for every market_update from the stream
        print(update.topic_id, update.data.quotes)

    async def on_tick(self) -> None:
        # called every `tick_interval` seconds, independent of quote events
        positions = await self.positions.get()  # cached portfolio_active()
        print(f"{len(positions)} open positions")

async def main():
    client = AsyncClient.from_env(dry_run=True)  # paper-trade by default
    runner = StrategyRunner(MyStrategy(), client=client, topic_id=6674, tick_interval=5.0)
    await runner.run()

asyncio.run(main())

self.client (the AsyncClient) and self.positions (a PositionTracker) are available inside any hook — place trades with the former, check current positions with the latter without re-fetching your whole portfolio on every quote tick. PositionTracker caches portfolio_active() and refreshes at most once every few seconds; call self.positions.invalidate() right after placing a trade to force a fresh read.

An exception raised from on_quote or on_tick stops the runner and propagates out of run() — a strategy bug fails loud instead of getting silently swallowed. See examples/dry_run_strategy.py for a complete, runnable example.

CLI

The glimpse command covers the same ground as the Python client, for one-off calls from the terminal. Config comes from GLIMPSE_API_KEY / GLIMPSE_BASE_URL, read from the environment or a .env file in the current directory.

glimpse balance

glimpse portfolio active
glimpse portfolio summary
glimpse portfolio ended [--limit N --offset N]
glimpse portfolio resolved [--limit N --offset N]

glimpse batches                       # list all batches
glimpse batches --batch-id <id>       # active markets in a batch
glimpse quotes --topic-id <id>

glimpse estimate --topic-id <id> --type buy --leg 500:10 [--leg 501:5]
glimpse execute --topic-id <id> --leg 500:10 [--dry-run]
glimpse exit --topic-id <id> --option-id 500 [--shares 5] [--dry-run]
glimpse exit-batch --batch-id <id> [--dry-run]

--dry-run works the same way it does in the Python client. execute and a partial exit --shares N never need an API key in dry-run mode, since both are priced entirely through the public estimate endpoint. A full-position exit (no --shares) or exit-batch still needs a key even in dry-run mode, since pricing them requires looking up your current positions first.

Units: millisats vs. price

The API mixes two numeric scales that are easy to confuse:

  • millisats (*_millisats fields, wallet balance) — real money. 1 satoshi = 1000 millisatoshis.
  • price (yes_price / no_price / odds from quotes) — a 0–100 LS-LMSR pricing scale, only converted to millisats at trade/settlement time.

glimpse_markets.money exposes Millisats and PriceUnits as distinct types, plus millisats_to_sats() / sats_to_millisats() helpers, so it's harder to accidentally treat a price of 62.1 as 62.1 millisats.

Error handling

Every non-2xx response raises a subclass of GlimpseAPIError (GlimpseAuthenticationError, GlimpseForbiddenError, GlimpseTradingNotEligibleError, GlimpseNotFoundError, GlimpseValidationError, GlimpseRateLimitError, GlimpseServerError), each carrying .status_code, .error, .reason, .message, and the raw response body:

from glimpse_markets import GlimpseAPIError

try:
    client.wallet_balance()
except GlimpseAPIError as e:
    print(e.status_code, e.error, e.reason)

Both clients also throttle themselves client-side to stay under Glimpse's 60-requests-per-60-seconds-per-key limit, so a naive loop doesn't immediately trip a 429.

Development

git clone <repo-url>
cd python-package

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

ruff check .
mypy src
pytest

License

MIT — see LICENSE.

Metadata

Release files for glimpse-markets 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for glimpse-markets 0.1.0
File Size Uploaded
glimpse_markets-0.1.0.tar.gz 33.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for glimpse-markets 0.1.0
File Interpreter ABI Platform
glimpse_markets-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 59.8 kB

Release files / glimpse_markets-0.1.0.tar.gz

Download URL glimpse_markets-0.1.0.tar.gz
Size 33.4 kB
Tags Source
SHA-256 checksum
How to use checksums
9b8e21025326b699aa8f98a5524945e776828190ca81d26a1b013e2d87287539
BLAKE2b-256 checksum
How to use checksums
2c6b8a2b92bc2217c1276e3eb88dd0687a809e3bba7410c7419e96d4120d3d93
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / glimpse_markets-0.1.0-py3-none-any.whl

Download URL glimpse_markets-0.1.0-py3-none-any.whl
Size 26.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
208d5c77de914c1d2f48df801a45eb4d48bfe1f24d96edf091c452fdc17f609f
BLAKE2b-256 checksum
How to use checksums
b9fa6f3bc6a0f98dcc64c9f854138db877c1940d96fce3765680d2e318c217e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page