Metadata-Version: 2.4
Name: polynode
Version: 0.11.0
Summary: Python SDK for the PolyNode real-time prediction market data platform
Project-URL: Homepage, https://polynode.dev
Project-URL: Documentation, https://docs.polynode.dev
Author-email: PolyNode <josh@quantish.live>
License: MIT
Keywords: polymarket,polynode,prediction-markets,trading,websocket
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Requires-Dist: websockets>=12.0
Provides-Extra: all
Requires-Dist: aiosqlite>=0.20; extra == 'all'
Requires-Dist: eth-account>=0.13; extra == 'all'
Requires-Dist: web3>=7.0; extra == 'all'
Provides-Extra: cache
Requires-Dist: aiosqlite>=0.20; extra == 'cache'
Provides-Extra: trading
Requires-Dist: eth-account>=0.13; extra == 'trading'
Requires-Dist: web3>=7.0; extra == 'trading'
Description-Content-Type: text/markdown

# polynode

Python SDK for the [PolyNode](https://polynode.dev) real-time prediction market data platform.

**New in v0.11.0:** Current-production parity. Trading now defaults to CLOB V2 on `clob.polymarket.com`, uses PolyNode's public builder attribution unless overridden, omits removed V1 wire fields, and supports V2 GTD expiration. Managed 5-minute, 15-minute, and 4-hour streams select the required 30/60-second Chainlink TWAP lookbacks on a dedicated connection and reconnect/resubscribe at every market rotation. WebSocket models, presets, and filters now cover current redemption, position-conversion, dome/fill, and PM2 combo events. REST position queries now include redeemable/condition filters, multi-wallet batches, and market-holder views; connection and status observability match the current public API.

**New in v0.10.8:** POLY_1271 V2 order signatures now normalize the ERC-7739 `TypedDataSign` recovery byte to Ethereum `v=27/28` for on-chain ERC-1271 validation.

**In v0.10.7:** Polymarket V2 deposit-wallet trading fixes. `ensure_ready()` detects deployed `POLY_1271` wallets correctly, V2 type-3 orders use the deposit wallet as both maker and signer, and existing local credentials can be repaired by rerunning `ensure_ready()`.

## Install

```bash
pip install polynode
```

For trading support:
```bash
pip install polynode[trading]
```

## Quick Start

### REST API

```python
from polynode import PolyNode

with PolyNode(api_key="pn_live_...") as pn:
    status = pn.status()
    connections = pn.connections()
    markets = pn.markets(count=10)
    settlements = pn.recent_settlements(count=5)
    wallet_positions = pn.wallet_positions(
        address, redeemable=True, condition_id=condition_id
    )
    batch_positions = pn.multi_wallet_positions([address, second_address], limit=100)
    market_positions = pn.market_positions(
        condition_id, sort_by="CURRENT_VALUE", min_size=0.01
    )
    onchain_positions = pn.wallet_onchain_positions(
        address, since=window_start, tag_slug="crypto"
    )
```

### Sports and Online Context

```python
from polynode import PolyNode

with PolyNode(api_key="pn_live_...") as pn:
    state = pn.sports_game_state(
        "nba-cle-nyk-2026-05-31",
        price_limit_tokens=20,
    )

    context = pn.sports_game_context(
        "nba-cle-nyk-2026-05-31",
        sources=["online"],
        query_set="injuries",
        max_queries=2,
        max_per_query=5,
        include_state=True,
    )

    web = pn.search_online(
        "Cavaliers Knicks injury news",
        max_results=5,
    )
```

### Async REST

```python
import asyncio
from polynode import AsyncPolyNode

async def main():
    async with AsyncPolyNode(api_key="pn_live_...") as pn:
        status = await pn.status()
        markets = await pn.markets(count=10)

asyncio.run(main())
```

Current presets include `dome`, `fills`, `combos`, `redemptions`, and `deposits`. Current filters include `since()`, `combo_condition_ids()`, `leg_position_ids()`, `event_ids()`, `module_ids()`, `action()`, and `direction()`.

`dome` and `fills` change settlement delivery into a flat, per-fill wire
format. Use one of those presets on a dedicated `PolyNodeWS` connection when
also consuming non-fill events; the server deduplicates delivery per
connection and cannot deliver both wire formats for the same settlement.

### Chainlink TWAP and short-form markets

The TWAP values are lookback windows, not update cadence: 5-minute markets use 30 seconds; 15-minute and 4-hour markets use 60 seconds.

```python
async def stream_short_markets(pn):
    prices = await (
        pn.ws.subscribe("chainlink")
        .feeds(["BTC/USD", "ETH/USD"])
        .twap_windows([30])
        .send()
    )
    print(prices.price_source, prices.twap_windows, prices.warnings)
    prices.on("price_feed", lambda event: print(event.feed, event.price))

    stream = pn.ws.short_form("5m", coins=["btc", "eth"])
    stream.on("rotation", lambda rotation: print([m.slug for m in rotation.markets]))
    stream.on("price_feed", lambda event: print(event.feed, event.price))
    stream.on("settlement", lambda event: print(event.market_slug, event.status))
```

A Chainlink selection is scoped to its WebSocket connection, so combine feeds and windows into one Chainlink subscription per connection. The resolved subscription exposes the server acknowledgement through `price_source`, `twap_windows`, and `warnings`. `short_form()` handles rotation safely with its own socket. At each market boundary it closes that socket, discovers the new slugs, reconnects, and subscribes to the exact settlement and TWAP filters again.

### WebSocket Streaming

```python
import asyncio
from polynode import AsyncPolyNode

async def main():
    async with AsyncPolyNode(api_key="pn_live_...") as pn:
        sub = await pn.ws.subscribe("settlements").min_size(1000).send()

        async for event in sub:
            print(event.event_type, event.market_title, event.taker_price)

asyncio.run(main())
```

### Orderbook

```python
import asyncio
from polynode import OrderbookEngine

async def main():
    engine = OrderbookEngine(api_key="pn_live_...")
    await engine.subscribe(["token_id_1", "token_id_2"])

    engine.on("ready", lambda: print(f"Tracking {engine.size} books"))
    engine.on("update", lambda u: print(f"{u.asset_id}: {engine.midpoint(u.asset_id)}"))

asyncio.run(main())
```

### Trading

```python
import asyncio
from polynode.trading import PolyNodeTrader, TraderConfig, OrderParams, ExchangeVersion

async def main():
    # CLOB V2 (pUSD collateral) is the current production default.
    trader = PolyNodeTrader(TraderConfig(
        polynode_key="pn_live_...",
        # exchange_version=ExchangeVersion.V2,
        # builder_code=None,  # disables default public PolyNode attribution
    ))
    status = await trader.ensure_ready("0xYourPrivateKey...")

    result = await trader.order(OrderParams(
        token_id="...",
        side="BUY",
        price=0.55,
        size=100,
        builder="0x<your_builder_code_bytes32>",  # V2 only; omit for V1
    ))
    print(result)

    trader.close()

asyncio.run(main())
```

For the V2 order flow — required approvals, EIP-712 struct, fee math, and common failure modes — see `polynode/trading/V2_ORDER_FLOW.md` in the installed package.

V2 fees are determined at match time and are not signed into an order, so V2 payloads omit `feeRateBps`, `nonce`, and `taker`. Explicit legacy V1 mode still signs `feeRateBps`; for that path the SDK fetches `/fee-rate` and fails closed if fee, tick-size, or neg-risk metadata is unavailable or malformed.

## Documentation

Full docs at [docs.polynode.dev](https://docs.polynode.dev)
