Polymarket¶
Connector for Polymarket prediction market protocol.
almanak.connectors.polymarket
¶
Polymarket prediction market connector.
This module provides integration with Polymarket's hybrid CLOB + on-chain architecture for prediction market trading.
Architecture Overview
- CLOB (Central Limit Order Book): Off-chain order matching via API
- CTF (Conditional Token Framework): On-chain ERC-1155 outcome tokens
- Market Making: Specialized utilities for automated market making
- Signals: Integration with news, social sentiment, and prediction models
Export Organization
This connector exports 103 items across 14 categories due to Polymarket's unique hybrid architecture. See docs/internal/notes/tech-debt/polymarket-export-list.md for detailed analysis and justification.
Major categories: - Clients & SDK: Core client classes (ClobClient, CtfSDK, PolymarketSDK) - Adapter & Results: Adapter interface and transaction results - Constants: Contract addresses, EIP-712 domains, CTF SDK constants - Configuration: Credentials and config management - Enums: Order types, statuses, signature types - Market Data: Market info, order books, prices - Orders: Order parameters and state transitions - Positions & Trades: Position tracking and trade history - Filters: Query filters for markets, orders, trades - Historical Data: Price history and historical trades - Receipt Parser: Event parsing for on-chain transactions - Market Making: Quote generation and risk management - Signals: Prediction signal providers and aggregation - Exceptions: Comprehensive error hierarchy
Example
from almanak.connectors.polymarket import ( ClobClient, PolymarketConfig, MarketFilters, )
Initialize client¶
config = PolymarketConfig.from_env() client = ClobClient(config)
Fetch markets¶
markets = client.get_markets(MarketFilters(active=True, limit=10)) for market in markets: print(f"{market.question}: YES={market.yes_price}, NO={market.no_price}")
OrderResult
dataclass
¶
OrderResult(
success: bool,
order_id: str | None = None,
error: str | None = None,
token_id: str | None = None,
side: str | None = None,
price: Decimal | None = None,
size: Decimal | None = None,
)
Result of building and signing an order.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
success |
bool
|
Whether the order was built successfully |
order_id |
str | None
|
Order ID if submitted |
error |
str | None
|
Error message if failed |
PolymarketAdapter
¶
Adapter for compiling prediction market intents to ActionBundles.
This adapter integrates with the Almanak Strategy Framework to compile high-level prediction market intents into executable orders and transactions.
The compilation process: 1. Resolve market_id to a GammaMarket (supports ID or slug) 2. Determine token_id based on outcome (YES or NO) 3. Build order/transaction based on intent type 4. Return ActionBundle for execution
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
client |
Gateway-backed Polymarket client for order management |
|
clob |
Compatibility alias for the client |
|
ctf |
CTF SDK for on-chain operations |
|
web3 |
Optional Web3 instance for on-chain operations |
Example
adapter = PolymarketAdapter(pm_client, wallet_address="0x1234...")
intent = Intent.prediction_buy( ... market_id="btc-100k", ... outcome="YES", ... amount_usd=Decimal("100"), ... ) bundle = adapter.compile_intent(intent, market_snapshot)
Initialize the Polymarket adapter.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
client
|
Any
|
Gateway-backed Polymarket client |
必需 |
wallet_address
|
str | None
|
Wallet address used for on-chain redemption |
None
|
web3
|
Any | None
|
Optional Web3 instance for on-chain operations. Required for redeem intents. |
None
|
compile_intent
¶
compile_intent(
intent: PredictionBuyIntent
| PredictionSellIntent
| PredictionRedeemIntent,
market_snapshot: MarketSnapshot | None = None,
) -> ActionBundle
Compile a prediction market intent to an ActionBundle.
This is the main entry point for intent compilation. It dispatches to the appropriate handler based on intent type.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
intent
|
PredictionBuyIntent | PredictionSellIntent | PredictionRedeemIntent
|
The prediction intent to compile |
必需 |
market_snapshot
|
MarketSnapshot | None
|
Optional market snapshot for additional context |
None
|
返回:
| 类型 | 描述 |
|---|---|
ActionBundle
|
ActionBundle containing the compiled orders/transactions |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If intent type is not supported |
RedeemResult
dataclass
¶
RedeemResult(
success: bool,
transactions: list[dict[str, Any]] = list(),
error: str | None = None,
condition_id: str | None = None,
outcome: str | None = None,
amount: Decimal | None = None,
)
Result of building a redeem transaction.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
success |
bool
|
Whether the transaction was built successfully |
transactions |
list[dict[str, Any]]
|
List of transaction data dicts |
error |
str | None
|
Error message if failed |
ClobClient
¶
ClobClient(
config: PolymarketConfig,
http_client: Client | None = None,
rate_limiter: TokenBucketRateLimiter | None = None,
*,
signer: Signer | None = None,
)
Client for Polymarket CLOB API.
Handles both L1 (EIP-712) and L2 (HMAC) authentication and provides methods for market data and order management.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
config |
Polymarket configuration |
|
credentials |
API credentials (may be None until created) |
Thread Safety
This class is NOT thread-safe. Use separate instances per thread.
Initialize CLOB client.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
config
|
PolymarketConfig
|
Polymarket configuration (public/inert addresses + URLs). Carries no signing credentials — see issue #1961. |
必需 |
http_client
|
Client | None
|
Optional HTTP client for testing. |
None
|
rate_limiter
|
TokenBucketRateLimiter | None
|
Optional rate limiter for testing. If not provided, creates one based on config settings. |
None
|
signer
|
Signer | None
|
EIP-712 signer callable. |
None
|
rate_limiter
property
¶
Access the rate limiter for configuration or testing.
create_api_credentials
¶
Create new API credentials using L1 authentication.
This signs an EIP-712 message to prove wallet ownership and creates new API credentials for L2 authentication.
返回:
| 类型 | 描述 |
|---|---|
ApiCredentials
|
ApiCredentials with api_key, secret, and passphrase |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAuthenticationError
|
If credential creation fails |
derive_api_credentials
¶
Derive existing API credentials using L1 authentication.
If credentials were previously created, this retrieves them using wallet signature.
返回:
| 类型 | 描述 |
|---|---|
ApiCredentials
|
ApiCredentials with api_key, secret, and passphrase |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAuthenticationError
|
If credential derivation fails |
get_or_create_credentials
¶
Get existing credentials or create new ones.
First attempts to derive existing credentials. If that fails, creates new ones.
返回:
| 类型 | 描述 |
|---|---|
ApiCredentials
|
ApiCredentials |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAuthenticationError
|
If both operations fail |
health_check
¶
Check if CLOB API is healthy.
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if API is responding |
get_server_time
¶
Get server timestamp.
V2 returns /time as a raw integer (1777384897); historical V1
responses were {"time": <int>}. Accept both — a future API change
back to a dict shape, or a transitional period where one host serves
each, must not break the L1-auth timestamp-drift check.
返回:
| 类型 | 描述 |
|---|---|
int
|
Unix timestamp from server. |
get_markets
¶
Get list of markets from Gamma API.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
filters
|
MarketFilters | None
|
Optional filters for the query |
None
|
返回:
| 类型 | 描述 |
|---|---|
list[GammaMarket]
|
List of GammaMarket objects |
get_market
¶
Get single market by ID.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market_id
|
str
|
Market ID |
必需 |
返回:
| 类型 | 描述 |
|---|---|
GammaMarket
|
GammaMarket object |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAPIError
|
If market not found |
get_market_by_slug
¶
Get market by URL slug.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
slug
|
str
|
Market URL slug |
必需 |
返回:
| 类型 | 描述 |
|---|---|
GammaMarket | None
|
GammaMarket or None if not found |
get_market_by_condition_id
¶
Get market by CTF condition ID, with per-session caching.
Used by :meth:get_positions to backfill market_id/token_id
when the Data API returns them empty. Gamma's /markets endpoint
accepts a condition_ids filter and returns one entry per match.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str
|
CTF condition ID ( |
必需 |
返回:
| 类型 | 描述 |
|---|---|
GammaMarket | None
|
GammaMarket or None if no market with that condition_id exists |
GammaMarket | None
|
(e.g. resolved/delisted). |
get_orderbook
¶
Get orderbook for a token.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token ID (YES or NO) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
OrderBook
|
OrderBook with bids and asks |
get_price
¶
Get best bid/ask/mid for a token.
V2 split the V1 single-call /price endpoint into per-side queries:
/price?token_id=X&side=BUY returns {"price": "<best bid>"} and
/price?token_id=X&side=SELL returns {"price": "<best ask>"}.
We make both calls and derive mid = (bid + ask) / 2 rather than
adding a third round-trip to /midpoint — Polymarket's midpoint is
the same arithmetic mean.
A side that has no resting liquidity returns
{"error": "No orderbook exists ..."} with HTTP 404; we surface
that as a PolymarketAPIError (the caller decides whether to fall
back). Both sides are required for a meaningful TokenPrice — a
one-sided response would silently produce a zero on the other leg.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token ID. |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TokenPrice
|
TokenPrice with bid, ask, and mid. |
get_midpoint
¶
Get midpoint price for a token.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token ID |
必需 |
返回:
| 类型 | 描述 |
|---|---|
Decimal
|
Midpoint price |
get_tick_size
¶
Get minimum tick size for a token.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token ID |
必需 |
返回:
| 类型 | 描述 |
|---|---|
Decimal
|
Minimum tick size |
get_balance_allowance
¶
get_balance_allowance(
asset_type: str = "COLLATERAL",
token_id: str | None = None,
) -> BalanceAllowance
Get balance and allowance.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
asset_type
|
str
|
"COLLATERAL" for USDC or "CONDITIONAL" for position tokens |
'COLLATERAL'
|
token_id
|
str | None
|
Token ID (required for CONDITIONAL) |
None
|
返回:
| 类型 | 描述 |
|---|---|
BalanceAllowance
|
BalanceAllowance with current balance and allowance |
round_price_to_tick
¶
round_price_to_tick(
price: Decimal,
side: str,
market: GammaMarket | None = None,
tick_size: Decimal | None = None,
) -> Decimal
Round price to valid tick size for the market.
Public method for rounding prices before order submission. Use this when you want automatic rounding instead of validation errors.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
price
|
Decimal
|
Price to round |
必需 |
side
|
str
|
Order side ("BUY" or "SELL") |
必需 |
market
|
GammaMarket | None
|
Optional GammaMarket for market-specific tick size |
None
|
tick_size
|
Decimal | None
|
Optional explicit tick size (overrides market) |
None
|
返回:
| 类型 | 描述 |
|---|---|
Decimal
|
Price rounded to nearest valid tick |
Example
market = client.get_market(market_id) rounded = client.round_price_to_tick(Decimal("0.655"), "BUY", market=market)
build_limit_order
¶
Build an unsigned V2 limit order.
Limit orders specify exact price and size. They remain on the orderbook until filled, cancelled, or expired (GTD).
For BUY orders
- You spend pUSD (maker_amount = size * price)
- You receive shares (taker_amount = size)
For SELL orders
- You spend shares (maker_amount = size)
- You receive pUSD (taker_amount = size * price)
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
params
|
LimitOrderParams
|
Limit order parameters (V2: no fee_rate_bps; expiration is wire-only GTD timestamp, not signed). |
必需 |
market
|
GammaMarket
|
GammaMarket metadata. Required in V2 — drives both
validation (min size, tick size) and exchange routing
(regular CTF V2 vs NegRisk V2). Passing |
必需 |
返回:
| 类型 | 描述 |
|---|---|
UnsignedOrder
|
UnsignedOrder ready for signing. |
引发:
| 类型 | 描述 |
|---|---|
PolymarketInvalidPriceError
|
If price is out of range (0.01-0.99) |
PolymarketInvalidTickSizeError
|
If price is not a valid tick multiple |
PolymarketMinimumOrderError
|
If size is below market minimum |
ValueError
|
If |
build_market_order
¶
Build an unsigned market order.
Market orders execute immediately at the best available price. They should be submitted with IOC (Immediate or Cancel) order type.
For BUY orders
- You specify USDC amount to spend
- worst_price sets the maximum price per share
For SELL orders
- You specify number of shares to sell
- worst_price sets the minimum price per share
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
params
|
MarketOrderParams
|
Market order parameters |
必需 |
market
|
GammaMarket
|
Optional GammaMarket metadata for market-specific validation. If provided, uses market.order_min_size for size validation and market.order_price_min_tick_size for tick validation. |
必需 |
返回:
| 类型 | 描述 |
|---|---|
UnsignedOrder
|
UnsignedOrder ready for signing |
引发:
| 类型 | 描述 |
|---|---|
PolymarketInvalidPriceError
|
If worst_price is out of range (0.01-0.99) |
PolymarketInvalidTickSizeError
|
If worst_price is not a valid tick multiple |
PolymarketMinimumOrderError
|
If amount is below market minimum |
Example
params = MarketOrderParams( ... token_id="123...", ... side="BUY", ... amount=Decimal("100"), # USDC to spend ... worst_price=Decimal("0.70"), # Max price per share ... ) order = client.build_market_order(params)
With market metadata for proper minimum and tick validation¶
market = client.get_market(market_id) order = client.build_market_order(params, market=market)
sign_order
¶
Sign an order using EIP-712 typed data signing.
This creates a cryptographic signature that proves the order was created by the wallet owner.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
order
|
UnsignedOrder
|
Unsigned order to sign |
必需 |
返回:
| 类型 | 描述 |
|---|---|
SignedOrder
|
SignedOrder with signature attached |
Example
unsigned = client.build_limit_order(params) signed = client.sign_order(unsigned) response = client.submit_order(signed)
create_and_sign_limit_order
¶
Build and sign a V2 limit order in one call.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
params
|
LimitOrderParams
|
Limit order parameters |
必需 |
market
|
GammaMarket
|
GammaMarket metadata. Required in V2 — drives validation AND neg-risk exchange routing. |
必需 |
返回:
| 类型 | 描述 |
|---|---|
SignedOrder
|
SignedOrder ready for submission |
create_and_sign_market_order
¶
Build and sign a V2 market order in one call.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
params
|
MarketOrderParams
|
Market order parameters |
必需 |
market
|
GammaMarket
|
GammaMarket metadata. Required in V2 — drives validation AND neg-risk exchange routing. |
必需 |
返回:
| 类型 | 描述 |
|---|---|
SignedOrder
|
SignedOrder ready for submission |
submit_order
¶
Submit a signed order.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
order
|
SignedOrder
|
Signed order to submit |
必需 |
order_type
|
OrderType
|
Order type (GTC, IOC, FOK) |
GTC
|
返回:
| 类型 | 描述 |
|---|---|
OrderResponse
|
OrderResponse with order ID and status |
create_and_post_order
¶
create_and_post_order(
*,
token_id: str,
price: Decimal,
size: Decimal,
side: str,
market: GammaMarket,
time_in_force: str = "GTC",
expiration: int = 0,
) -> OrderResponse
Compatibility helper used by gateway-backed and legacy callers.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market
|
GammaMarket
|
GammaMarket — required in V2 to route neg-risk and validate tick / min-size. |
必需 |
expiration
|
int
|
API-level GTD timestamp (Unix seconds). 0 = no expiry. |
0
|
submit_order_payload
¶
Submit an order from a pre-built (already-signed) payload dict.
Direct API for callers that build the EIP-712 payload themselves
(e.g. external scripts or tests against a real Polymarket key).
The framework execution path no longer calls this — under V2 the
gateway signs server-side and ClobActionHandler routes through
create_and_post_order instead.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
payload
|
dict[str, Any]
|
Order payload dict containing 'order', 'signature', and 'orderType' |
必需 |
返回:
| 类型 | 描述 |
|---|---|
OrderResponse
|
OrderResponse with order ID and status |
get_order
¶
Get a single order by ID.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
order_id
|
str
|
Order ID to retrieve |
必需 |
返回:
| 类型 | 描述 |
|---|---|
OpenOrder | None
|
OpenOrder if found, None otherwise |
cancel_order
¶
Cancel an order by ID.
Polymarket's CLOB expects a DELETE with a JSON body, not query params — sending query params 401s with "Invalid api key" because the HMAC signature is computed over the body.
cancel_all_orders
¶
Cancel all open orders.
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if cancelled successfully |
get_open_orders
¶
Get open orders.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
filters
|
OrderFilters | None
|
Optional filters |
None
|
返回:
| 类型 | 描述 |
|---|---|
list[OpenOrder]
|
List of open orders |
get_trades
¶
Get trade history.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
filters
|
TradeFilters | None
|
Optional filters |
None
|
返回:
| 类型 | 描述 |
|---|---|
list[Trade]
|
List of trades |
get_positions
¶
get_positions(
wallet: str | None = None,
filters: PositionFilters | None = None,
) -> list[Position]
Get positions for a wallet.
Queries the Polymarket Data API to retrieve all open prediction market positions for the specified wallet.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
wallet
|
str | None
|
Wallet address to query. Defaults to config wallet if not specified. |
None
|
filters
|
PositionFilters | None
|
Optional filters for market or outcome |
None
|
返回:
| 类型 | 描述 |
|---|---|
list[Position]
|
List of Position objects with size, prices, and PnL data |
Example
positions = client.get_positions() for pos in positions: ... print(f"{pos.outcome}: {pos.size} shares at {pos.avg_price}")
get_price_history
¶
get_price_history(
token_id: str,
interval: str | PriceHistoryInterval | None = None,
start_ts: int | None = None,
end_ts: int | None = None,
fidelity: int | None = None,
) -> PriceHistory
Get historical price data for a token.
Fetches time-series price data from the CLOB API. Can query by predefined interval or custom time range.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token ID (YES or NO outcome) |
必需 |
interval
|
str | PriceHistoryInterval | None
|
Predefined interval (1m, 1h, 6h, 1d, 1w, max). Mutually exclusive with start_ts/end_ts. |
None
|
start_ts
|
int | None
|
Unix timestamp for start of range (UTC). Requires end_ts. Mutually exclusive with interval. |
None
|
end_ts
|
int | None
|
Unix timestamp for end of range (UTC). Requires start_ts. Mutually exclusive with interval. |
None
|
fidelity
|
int | None
|
Data resolution in minutes (e.g., 1, 5, 15, 60). Optional for both modes. |
None
|
返回:
| 类型 | 描述 |
|---|---|
PriceHistory
|
PriceHistory with list of timestamped prices |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAPIError
|
If request fails |
ValueError
|
If both interval and start_ts/end_ts are provided |
Example
Get last 24 hours¶
history = client.get_price_history(token_id, interval="1d") print(f"Open: {history.open_price}, Close: {history.close_price}")
Get custom range¶
history = client.get_price_history( ... token_id, ... start_ts=1700000000, ... end_ts=1700100000, ... fidelity=5, # 5-minute resolution ... )
get_trade_tape
¶
Get recent executed trades (trade tape).
Fetches the most recent trades for analysis. Can be filtered by token ID for market-specific trades.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str | None
|
Optional CLOB token ID to filter trades |
None
|
limit
|
int
|
Maximum number of trades to return (default 100, max 500) |
100
|
返回:
| 类型 | 描述 |
|---|---|
list[HistoricalTrade]
|
List of HistoricalTrade objects, newest first |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAPIError
|
If request fails |
Example
Get recent trades for YES token¶
trades = client.get_trade_tape(token_id="123...", limit=50) for trade in trades: ... print(f"{trade.side} {trade.size} @ {trade.price}")
TokenBucketRateLimiter
¶
Token bucket rate limiter for API calls.
Implements a token bucket algorithm that allows bursting while maintaining an average rate over time. Tokens are added to the bucket at a fixed rate, and each request consumes one token.
The algorithm: 1. Bucket starts full (capacity = rate_per_second tokens) 2. Tokens are added at rate_per_second tokens per second 3. Each request consumes 1 token 4. If bucket is empty, the caller waits until a token is available
Thread Safety
This implementation is NOT thread-safe. Use separate instances per thread or add locking if needed.
Example
limiter = TokenBucketRateLimiter(rate_per_second=30.0) limiter.acquire() # Blocks if rate limit exceeded
Make API call¶
Initialize the rate limiter.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
rate_per_second
|
float
|
Maximum average requests per second |
必需 |
enabled
|
bool
|
Whether rate limiting is active (can be disabled for testing) |
True
|
available_tokens
property
¶
Number of tokens currently available (after refill).
acquire
¶
Acquire a token, blocking if necessary.
Waits until a token is available, then consumes it. If the bucket is empty, sleeps until enough time has passed for a new token.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
timeout
|
float | None
|
Maximum time to wait in seconds. None means wait forever. If timeout expires before a token is available, returns False. |
None
|
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if token was acquired, False if timeout expired (only when timeout is set) |
Example
if limiter.acquire(timeout=5.0): ... make_api_call() ... else: ... print("Timeout waiting for rate limit")
try_acquire
¶
Try to acquire a token without blocking.
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if token was acquired, False if bucket is empty |
Example
if limiter.try_acquire(): ... make_api_call() ... else: ... print("Rate limit would be exceeded, skipping")
reset
¶
Reset the rate limiter to full capacity.
Useful for testing or after a period of inactivity.
AllowanceStatus
dataclass
¶
AllowanceStatus(
source_asset_balance: int,
pusd_balance: int,
source_asset_allowance_onramp: int,
pusd_allowance_ctf_exchange: int,
pusd_allowance_neg_risk_exchange: int,
pusd_allowance_neg_risk_adapter: int,
ctf_approved_for_ctf_exchange: bool,
ctf_approved_for_neg_risk_adapter: bool,
native_usdc_balance: int = 0,
native_usdc_allowance_onramp: int = 0,
)
Status of token allowances for Polymarket V2 trading.
V2 collateral pivot: spending collateral is pUSD (minted via the CollateralOnramp from a source asset — USDC.e or native USDC). The approval set is:
Source assets (only the wallet's funded source assets are approved
lazily — we don't burn gas approving an asset the wallet has never
held):
- configured source_asset (default USDC.e) → CollateralOnramp
- native USDC → CollateralOnramp (when wallet holds native USDC)
pUSD spend (the BUY-side leg — Polymarket V2 pulls pUSD from the maker via these contracts on order fill): - pUSD → CTF Exchange V2 (binary YES/NO market BUYs) - pUSD → NegRisk Exchange V2 (neg-risk order matching) - pUSD → NegRisk Adapter (neg-risk split/merge — the adapter is the actual spender on fills)
CTF (ERC-1155) operator (the SELL-side leg — V2 exchanges pull shares): - CTF.setApprovalForAll(CTF Exchange V2) - CTF.setApprovalForAll(NegRisk Adapter)
VIB-3770: native_usdc_balance / native_usdc_allowance_onramp were
added so a wallet funded with native Circle USDC instead of USDC.e (the
Polymarket UI now accepts both as deposit) can wrap to pUSD without the
user manually swapping first.
source_asset_approved_onramp
property
¶
Check if the source asset (USDC.e / USDC) is approved for the Onramp.
Sufficiency rather than non-zero — see SUFFICIENT_ALLOWANCE_THRESHOLD.
native_usdc_approved_onramp
property
¶
Whether native Circle USDC is approved for the Onramp (sufficiency).
pusd_approved_ctf_exchange
property
¶
Check if pUSD is approved for CTF Exchange V2 (sufficiency, not >0).
pusd_approved_neg_risk_exchange
property
¶
Check if pUSD is approved for NegRisk Exchange V2 (sufficiency, not >0).
pusd_approved_neg_risk_adapter
property
¶
Check if pUSD is approved for the NegRisk Adapter (sufficiency, not >0).
fully_approved
property
¶
Check if all V2 approvals are in place (6-tx set fully applied).
CollateralBreakdown
dataclass
¶
CollateralBreakdown(
pusd: int,
usdce: int,
usdc_native: int,
pusd_address: str,
usdce_address: str,
usdc_native_address: str,
)
Per-asset on-chain balance breakdown for Polymarket V2 collateral.
VIB-3770: surfaces every source the gateway will consider for wrap → pUSD plus the spendable pUSD. Used by:
CtfSDK.select_source_for_wrapto pick a wrap input that actually has the on-chain balance to cover a BUY's pUSD deficit.- The
almanak ax balance --protocol polymarketUX: shows pUSD, USDC.e, native USDC, and the total spendable collateral side-by-side so users see exactly what's usable.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
pusd |
int
|
pUSD balance (the spendable trading collateral) in 6-dp units. |
usdce |
int
|
Bridged USDC.e balance (legacy source asset) in 6-dp units. |
usdc_native |
int
|
Native Circle USDC balance in 6-dp units. |
pusd_address |
str
|
pUSD contract address (Polygon). |
usdce_address |
str
|
USDC.e contract address (Polygon). |
usdc_native_address |
str
|
Native USDC contract address (Polygon). |
total_spendable
property
¶
pUSD + every wrappable source asset, in 6-dp units.
This is the amount the wallet can ultimately bring to bear on a Polymarket BUY (pUSD spends directly; USDC.e / native USDC become pUSD via a single Onramp wrap). Treats the three at parity because the Onramp enforces 1:1.
CtfSDK
¶
CtfSDK(
chain_id: int = POLYGON_CHAIN_ID,
ctf_exchange: str = CTF_EXCHANGE_V2,
neg_risk_exchange: str = NEG_RISK_EXCHANGE_V2,
conditional_tokens: str = CONDITIONAL_TOKENS,
neg_risk_adapter: str = NEG_RISK_ADAPTER,
pusd: str = PUSD,
collateral_onramp: str = COLLATERAL_ONRAMP,
collateral_offramp: str = COLLATERAL_OFFRAMP,
source_asset: str = USDCE_POLYGON,
native_usdc: str = USDC_NATIVE_POLYGON,
)
Low-level SDK for Polymarket CTF on-chain operations.
This SDK provides methods to: - Check and set token approvals - Query token balances - Build split, merge, and redeem transactions - Check condition resolution status
All transaction building methods return TransactionData objects that can be signed and submitted using a signer.
Example
from almanak.framework.gateway_client import GatewayClient gateway_client = GatewayClient() gateway_client.connect()
sdk = CtfSDK()
Production: use GatewayWeb3Provider so RPC calls go through¶
the gateway sidecar, not directly to the chain.¶
web3 = Web3(GatewayWeb3Provider(gateway_client, chain="polygon"))
Check if wallet needs approvals¶
status = sdk.check_allowances("0x...", web3)
if not status.pusd_approved_ctf_exchange: tx = sdk.build_approve_collateral_tx(PUSD, CTF_EXCHANGE_V2, "0x...") # Sign and submit tx...
Build redeem transaction for resolved market¶
tx = sdk.build_redeem_tx( condition_id="0x...", index_sets=[1, 2], sender="0x...", )
Initialize the CTF SDK.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
chain_id
|
int
|
Chain ID (default: Polygon 137) |
POLYGON_CHAIN_ID
|
ctf_exchange
|
str
|
CTF Exchange V2 contract address |
CTF_EXCHANGE_V2
|
neg_risk_exchange
|
str
|
NegRisk CTF Exchange V2 contract address |
NEG_RISK_EXCHANGE_V2
|
conditional_tokens
|
str
|
Conditional Tokens contract address |
CONDITIONAL_TOKENS
|
neg_risk_adapter
|
str
|
NegRisk Adapter contract address |
NEG_RISK_ADAPTER
|
pusd
|
str
|
pUSD collateral token (V2). Approved to V2 exchanges. |
PUSD
|
collateral_onramp
|
str
|
CollateralOnramp contract for wrapping source asset → pUSD. |
COLLATERAL_ONRAMP
|
collateral_offramp
|
str
|
CollateralOfframp contract for unwrapping pUSD → source asset. |
COLLATERAL_OFFRAMP
|
source_asset
|
str
|
The user's primary source-of-funds token (USDC.e by default).
Approved to the Onramp eagerly during |
USDCE_POLYGON
|
native_usdc
|
str
|
Secondary source-of-funds token (native Circle USDC). VIB-3770: tracked so a wallet funded with native USDC instead of USDC.e can still wrap to pUSD. The native-USDC → Onramp approval is emitted only when the wallet actually holds native USDC, so we don't burn gas approving an asset the user has never funded. |
USDC_NATIVE_POLYGON
|
build_approve_collateral_tx
¶
build_approve_collateral_tx(
asset: str,
spender: str,
sender: str,
amount: int = MAX_UINT256,
) -> TransactionData
Build a generic ERC-20 approve transaction.
Used in V2 for: source-asset → Onramp, pUSD → CTF Exchange V2, pUSD → NegRisk Exchange V2.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
asset
|
str
|
ERC-20 asset address (e.g., pUSD, USDC.e, native USDC). |
必需 |
spender
|
str
|
Address to approve. |
必需 |
sender
|
str
|
Transaction sender address (informational; tx is built without a from-address since callers may rebroadcast under a different signer / via Zodiac wrapper). |
必需 |
amount
|
int
|
Approval amount (defaults to MAX_UINT256). |
MAX_UINT256
|
返回:
| 类型 | 描述 |
|---|---|
TransactionData
|
TransactionData for the approval. |
build_wrap_to_pusd_tx
¶
build_wrap_to_pusd_tx(
wallet: str,
amount: int,
source_asset: str | None = None,
) -> TransactionData
Build a CollateralOnramp.wrap call to mint pUSD from a source asset.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
wallet
|
str
|
Address that will receive the minted pUSD. |
必需 |
amount
|
int
|
Amount of source asset to wrap (token units; same decimals as pUSD). |
必需 |
source_asset
|
str | None
|
Source asset address (defaults to |
None
|
返回:
| 类型 | 描述 |
|---|---|
TransactionData
|
TransactionData targeting CollateralOnramp.wrap(asset, to, amount). |
build_unwrap_from_pusd_tx
¶
build_unwrap_from_pusd_tx(
wallet: str,
amount: int,
target_asset: str | None = None,
) -> TransactionData
Build a CollateralOfframp.unwrap call to redeem pUSD back to a source asset.
build_approve_conditional_tokens_tx
¶
build_approve_conditional_tokens_tx(
operator: str, approved: bool, sender: str
) -> TransactionData
Build ERC-1155 setApprovalForAll transaction.
Approves the operator (typically CTF Exchange or Neg Risk Adapter) to transfer conditional tokens on behalf of the sender.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
operator
|
str
|
Address to approve (e.g., CTF_EXCHANGE_V2 or NEG_RISK_ADAPTER) |
必需 |
approved
|
bool
|
True to approve, False to revoke |
必需 |
sender
|
str
|
Transaction sender address |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TransactionData
|
TransactionData for the approval |
check_allowances
¶
Check all relevant V2 token allowances.
Queries the source-asset → Onramp leg (both USDC.e and native USDC), the pUSD → exchange legs, and the CTF (ERC-1155) operator approvals needed to trade on Polymarket V2.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
wallet
|
str
|
Wallet address to check. |
必需 |
web3
|
Any
|
Web3 instance. |
必需 |
返回:
| 类型 | 描述 |
|---|---|
AllowanceStatus
|
AllowanceStatus with V2 allowance information, including the |
AllowanceStatus
|
secondary native-USDC source asset (VIB-3770). |
ensure_allowances
¶
Build the idempotent V2 approval set.
Emits only the approvals the wallet doesn't already have. Order: 1. source_asset → CollateralOnramp (so user can wrap to pUSD) 2. pUSD → CTF Exchange V2 (binary BUYs) 3. pUSD → NegRisk Exchange V2 (neg-risk order matching) 4. pUSD → NegRisk Adapter (neg-risk split/merge — the adapter is the actual spender on fill, not the exchange) 5. CTF.setApprovalForAll(CTF Exchange V2) (binary SELLs pull shares) 6. CTF.setApprovalForAll(NegRisk Adapter) (neg-risk SELLs / merge) 7. native USDC → CollateralOnramp (VIB-3770: ONLY when the wallet actually holds native USDC — otherwise we burn ~58k gas approving an asset the user has never funded)
返回:
| 类型 | 描述 |
|---|---|
list[TransactionData]
|
List of TransactionData for any approvals that aren't already in place. |
get_collateral_breakdown
¶
Read the wallet's full collateral breakdown (VIB-3770).
Three balanceOf calls — one each for pUSD, the canonical
bridged USDC.e bucket, and native Circle USDC — keyed off the
canonical token addresses (not self.source_asset). Pinning the
usdce slot to USDCE_POLYGON keeps the breakdown
self-consistent if a future deploy ever reconfigures
source_asset to the native USDC address: the CLI/UX
("how much USDC.e do I have? how much native USDC?") still maps
each on-chain pile to the right semantic bucket, instead of
silently lumping native USDC under usdce and zeroing
usdc_native.
Used by the gateway to decide whether a wrap is needed (and from
which source) and by almanak ax balance --protocol polymarket
for the user-facing breakdown.
select_source_for_wrap
¶
Pick the source asset to wrap into pUSD given a deficit.
VIB-3770: when a BUY needs more pUSD than the wallet holds, the gateway must wrap from one of the user's source assets. Earlier builds hardcoded USDC.e; users funding native USDC instead saw the wrap fail with "Insufficient source asset for wrap" even though their wallet was funded.
Selection rule:
- Prefer the configured
source_asset(USDC.e by default) when it covers the deficit on its own. This is the path Polymarket's Onramp has been live on the longest. - Otherwise pick native USDC if it covers the deficit.
- Otherwise pick whichever has the larger balance — the gateway's higher-level deficit check will surface the "insufficient" error with the more accurate per-asset numbers.
返回:
| 类型 | 描述 |
|---|---|
str
|
The chosen source asset address (checksummed). |
get_token_balance
¶
Get ERC-1155 token balance.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
wallet
|
str
|
Wallet address |
必需 |
token_id
|
int
|
Conditional token ID (position ID) |
必需 |
web3
|
Any
|
Web3 instance |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int
|
Token balance in base units |
get_token_balances
¶
Get multiple ERC-1155 token balances in a single call.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
wallet
|
str
|
Wallet address |
必需 |
token_ids
|
list[int]
|
List of conditional token IDs |
必需 |
web3
|
Any
|
Web3 instance |
必需 |
返回:
| 类型 | 描述 |
|---|---|
list[int]
|
List of token balances in base units |
get_pusd_balance
¶
Get pUSD balance — the spendable trading collateral in V2.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
wallet
|
str
|
Wallet address |
必需 |
web3
|
Any
|
Web3 instance |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int
|
pUSD balance in base units (6 decimals). |
get_source_asset_balance
¶
Get the source-asset (USDC.e or native USDC) balance — Onramp input.
get_collection_id
¶
get_collection_id(
condition_id: bytes,
index_set: int,
parent_collection_id: bytes = ZERO_BYTES32,
) -> bytes
Calculate collection ID for an outcome.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
bytes
|
Condition ID (32 bytes) |
必需 |
index_set
|
int
|
Outcome index set (1=YES, 2=NO for binary) |
必需 |
parent_collection_id
|
bytes
|
Parent collection (default: root) |
ZERO_BYTES32
|
返回:
| 类型 | 描述 |
|---|---|
bytes
|
Collection ID (32 bytes) |
get_position_id
¶
Calculate ERC-1155 position ID from collection ID.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
collateral
|
str
|
Collateral token address (pUSD in V2) |
必需 |
collection_id
|
bytes
|
Collection ID (32 bytes) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int
|
Position ID (uint256) |
get_token_ids_for_condition
¶
Get YES and NO token IDs for a binary condition.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str | bytes
|
Condition ID (hex string or bytes) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
tuple[int, int]
|
Tuple of (yes_token_id, no_token_id) |
build_split_tx
¶
Build split position transaction.
Splits pUSD into YES and NO conditional tokens. Requires pUSD approval for the Conditional Tokens contract.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str | bytes
|
Condition ID (hex string or bytes) |
必需 |
amount
|
int
|
Amount of pUSD to split (in base units) |
必需 |
sender
|
str
|
Transaction sender address |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TransactionData
|
TransactionData for the split operation |
build_merge_tx
¶
Build merge positions transaction.
Merges equal amounts of YES and NO tokens back into pUSD. Requires ERC-1155 approval for Conditional Tokens contract.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str | bytes
|
Condition ID (hex string or bytes) |
必需 |
amount
|
int
|
Amount of each outcome token to merge |
必需 |
sender
|
str
|
Transaction sender address |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TransactionData
|
TransactionData for the merge operation |
build_redeem_tx
¶
build_redeem_tx(
condition_id: str | bytes,
index_sets: list[int],
sender: str,
) -> TransactionData
Build redeem positions transaction.
Redeems winning positions after market resolution. Only works if the condition has been resolved.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str | bytes
|
Condition ID (hex string or bytes) |
必需 |
index_sets
|
list[int]
|
List of index sets to redeem (e.g., [1, 2] for both) |
必需 |
sender
|
str
|
Transaction sender address |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TransactionData
|
TransactionData for the redemption |
get_condition_resolution
¶
Get resolution status of a condition.
Checks if a condition has been resolved and returns payout information.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str | bytes
|
Condition ID (hex string or bytes) |
必需 |
web3
|
Any
|
Web3 instance |
必需 |
返回:
| 类型 | 描述 |
|---|---|
ResolutionStatus
|
ResolutionStatus with resolution information |
ResolutionStatus
dataclass
¶
ResolutionStatus(
condition_id: str,
is_resolved: bool,
payout_denominator: int,
payout_numerators: list[int],
winning_outcome: int | None = None,
)
Resolution status of a condition.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
condition_id |
str
|
The condition ID (bytes32 hex string) |
is_resolved |
bool
|
Whether the condition has been resolved |
payout_denominator |
int
|
Denominator for payout calculation |
payout_numerators |
list[int]
|
List of payout numerators for each outcome |
winning_outcome |
int | None
|
Index of winning outcome (0=YES, 1=NO) or None if not resolved |
TransactionData
dataclass
¶
TransactionData(
to: str,
data: str,
value: int = 0,
gas_estimate: int = 100000,
description: str = "",
)
Transaction data for on-chain operations.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
to |
str
|
Contract address to call |
data |
str
|
Encoded function call data |
value |
int
|
ETH value to send (usually 0) |
gas_estimate |
int
|
Estimated gas for the transaction |
description |
str
|
Human-readable description |
to_tx_params
¶
Convert to web3 transaction parameters.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
sender
|
str
|
Transaction sender address |
必需 |
返回:
| 类型 | 描述 |
|---|---|
dict[str, Any]
|
Dict with transaction parameters for web3 |
PolymarketAPIError
¶
PolymarketAuthenticationError
¶
Bases: PolymarketError
Authentication failed with Polymarket.
PolymarketCredentialsError
¶
Bases: PolymarketError
API credentials are invalid or missing.
PolymarketError
¶
Bases: Exception
Base exception for all Polymarket errors.
PolymarketInsufficientBalanceError
¶
PolymarketInvalidPriceError
¶
PolymarketMarketClosedError
¶
PolymarketMarketError
¶
Bases: PolymarketError
Error related to market operations.
PolymarketMarketNotFoundError
¶
PolymarketMarketNotResolvedError
¶
PolymarketMinimumOrderError
¶
Bases: PolymarketOrderError
Order size below minimum.
Two failure modes share this class, distinguished by the $ prefix on
size / minimum:
- Share-count floor failure: size="5", minimum="10" (market's
order_min_size).
- USD-value floor failure: size="$0.30", minimum="$1" — Polymarket
CLOB rejects BUY orders with makerAmount < $1. The live CLOB message is
invalid amount for a marketable BUY order ($X), min size: $1; we
embed that exact string in the exception message so strategy authors
can grep for it (VIB-3140, coordinated with VIB-3141's non_retryable
classification).
PolymarketOrderError
¶
Bases: PolymarketError
Error related to order operations.
PolymarketOrderNotFoundError
¶
PolymarketRateLimitError
¶
PolymarketRedemptionError
¶
Bases: PolymarketError
Error during position redemption.
PolymarketSignatureError
¶
Bases: PolymarketError
Error creating or verifying signature.
GatewayPolymarketClient
¶
Synchronous Polymarket client that talks only to the gateway.
get_positions
¶
get_positions(
wallet: str | None = None,
filters: PositionFilters | None = None,
) -> list[Position]
Return positions for the gateway-managed wallet.
The wallet parameter is accepted for API compatibility with
ClobClient.get_positions but is ignored — the gateway derives
the wallet from its own configuration.
get_price_history
¶
get_price_history(
token_id: str,
interval: str | PriceHistoryInterval | None = None,
start_ts: int | None = None,
end_ts: int | None = None,
fidelity: int | None = None,
) -> PriceHistory
Fetch historical price points for token_id via the gateway.
Signature mirrors :meth:ClobClient.get_price_history so
:class:PredictionMarketDataProvider (and any other caller still
using the V1 ClobClient signature) works unchanged. The local
ValueError validation lives server-side now (the underlying
ClobClient enforces interval vs start_ts/end_ts mutual
exclusion); the gateway re-raises it as a PolymarketAPIError
through response.error for parity.
get_trade_tape
¶
Fetch the recent trade tape via the gateway.
Signature mirrors :meth:ClobClient.get_trade_tape so
:class:PredictionMarketDataProvider works unchanged.
get_simplified_markets
¶
Fetch a paginated page of simplified market summaries.
Mirrors the gateway's GetSimplifiedMarkets RPC, which proxies the
public CLOB /simplified-markets endpoint. Returns (markets,
next_cursor) so callers can page through the catalogue without
baking pagination state into the client. An empty next_cursor
signals the final page.
get_midpoint
¶
Return the midpoint price for token_id as a Decimal probability.
get_price
¶
Return the best price for one side of token_id.
side must be "BUY" or "SELL" (case-sensitive on the wire,
normalized here). Validation runs locally BEFORE the RPC fires so a
typo never burns a network round-trip — V2 split the V1 single-call
/price into per-side queries, so an invalid side is unambiguously
a programming error.
get_spread
¶
Return the bid-ask spread for token_id as a Decimal.
get_tick_size
¶
Return the minimum tick size for token_id as a Decimal.
get_balance_allowance
¶
get_balance_allowance(
asset_type: str = "COLLATERAL",
token_id: str | None = None,
) -> BalanceAllowance
Return current balance and CLOB allowance for the gateway wallet.
asset_type is "COLLATERAL" (USDC/pUSD) or "CONDITIONAL"
(a position token — token_id required in that case). Mirrors
ClobClient.get_balance_allowance; the gateway derives the wallet
from its own configuration.
get_trades
¶
Return executed trade history for the gateway wallet.
Distinct from :meth:get_trade_tape, which returns the public
market-wide price tape. GetTradesHistory proxies the
authenticated /trades endpoint and reports the wallet's own
executed fills. Mirrors :meth:ClobClient.get_trades so callers can
adopt without shape changes.
cancel_orders
¶
Cancel multiple orders, returning per-id success/failure lists.
Returns {"canceled": [...], "not_canceled": [...]}. The gateway's
CancelOrders RPC iterates serverside (one CLOB call per id) and
returns partial-success — we surface that shape verbatim so callers
can retry the failures without re-attempting the successes.
cancel_all_orders
¶
Cancel all open orders, optionally scoped by market or asset.
market_id filters by market (CTF condition / market id) and
asset_id filters by token id. The gateway lists open orders,
applies the optional filters, then bulk-cancels — returning the list
of canceled order ids.
create_and_post_market_order
¶
create_and_post_market_order(
*,
token_id: str,
amount: Decimal,
side: str,
worst_price: Decimal | None = None,
expiration: int = 0,
market: Any | None = None,
) -> OrderResponse
Submit a market order via the gateway-owned signer.
amount semantics differ by side, matching the upstream V2
endpoint: for BUY it is the USDC notional to spend; for SELL
it is the token (share) size to sell. The gateway fetches the
current best price, derives the size for BUY (amount / price),
and submits the resulting order as Fill-or-Kill.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token id to trade. |
必需 |
amount
|
Decimal
|
USDC notional for BUY, share size for SELL. |
必需 |
side
|
str
|
|
必需 |
worst_price
|
Decimal | None
|
Optional limit price guard — refuses to fill at a worse price (BUY: above worst; SELL: below worst). |
None
|
expiration
|
int
|
API-level GTD timestamp (Unix seconds). 0 = none. |
0
|
market
|
Any | None
|
Accepted for ClobClient signature parity. Ignored — the
gateway re-fetches the market server-side from |
None
|
Quote
dataclass
¶
A single quote with price, size, and side.
Represents a single order that can be placed on the orderbook. Used in quote ladders for market making.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
price |
Decimal
|
Quote price (0.01 to 0.99 for prediction markets) |
size |
Decimal
|
Number of shares to quote |
side |
Literal['BUY', 'SELL']
|
"BUY" for bid, "SELL" for ask |
Example
quote = Quote(price=Decimal("0.50"), size=Decimal("100"), side="BUY") print(f"Bid {quote.size} @ {quote.price}") Bid 100 @ 0.50
RiskParameters
dataclass
¶
RiskParameters(
base_spread: Decimal = Decimal("0.02"),
skew_factor: Decimal = Decimal("0.5"),
max_position: Decimal = Decimal("1000"),
min_edge: Decimal = Decimal("0.001"),
volatility_multiplier: Decimal = Decimal("1.0"),
tick_size: Decimal = Decimal("0.01"),
)
Parameters for market making risk management.
These parameters control how the market maker adjusts spreads and quotes based on inventory position and market conditions.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
base_spread |
Decimal
|
Minimum spread to maintain (e.g., 0.02 = 2%) |
skew_factor |
Decimal
|
How much to skew quotes based on inventory (0-1). Higher values mean more aggressive skewing to reduce inventory. |
max_position |
Decimal
|
Maximum position size allowed. Orders beyond this size will not be quoted on the accumulating side. |
min_edge |
Decimal
|
Minimum expected edge (profit margin) per trade. Quotes will not be placed if edge falls below this threshold. |
volatility_multiplier |
Decimal
|
Multiplier for spread based on volatility. Higher volatility leads to wider spreads. |
tick_size |
Decimal
|
Minimum price increment (default 0.01). |
Example
params = RiskParameters( ... base_spread=Decimal("0.02"), ... skew_factor=Decimal("0.5"), ... max_position=Decimal("1000"), ... )
ApiCredentials
¶
Bases: BaseModel
Polymarket API credentials from L1 authentication.
These credentials are obtained by signing an EIP-712 message and calling the create/derive API key endpoint.
Credential Handling Limitations
The current implementation has the following limitations that users should be aware of:
-
Storage: Credentials are stored in environment variables only. There is no integration with secret management systems (e.g., AWS Secrets Manager, HashiCorp Vault). Users are responsible for secure credential storage in their deployment environment.
-
No Automatic Rotation: API credentials do not auto-rotate. The Polymarket CLOB API credentials have an expiration (typically 7 days from creation). Users must manually regenerate credentials before expiration by calling
ClobClient.create_api_credentials()orClobClient.derive_api_credentials()and updating environment variables. -
No Expiration Monitoring: The connector does not monitor credential expiration. If credentials expire, API calls will fail with authentication errors. Users should implement their own monitoring or credential refresh workflow.
-
Manual Renewal Process:
- Generate new credentials using L1 authentication (EIP-712 signature)
- Update environment variables: POLYMARKET_API_KEY, POLYMARKET_SECRET, POLYMARKET_PASSPHRASE
- Restart the application or reload configuration
Environment Variables
- POLYMARKET_API_KEY: API key for L2 HMAC authentication (required for trading)
- POLYMARKET_SECRET: Base64-encoded HMAC secret (required for trading)
- POLYMARKET_PASSPHRASE: Passphrase for API requests (required for trading)
Example
Load from environment¶
creds = ApiCredentials.from_env()
Or create manually¶
creds = ApiCredentials( api_key="your-api-key", secret=SecretStr("your-base64-secret"), passphrase=SecretStr("your-passphrase"), )
from_env
classmethod
¶
Load credentials from environment variables.
Phase 5b of the config-service migration: routes through the typed
:class:almanak.config.connectors.ConnectorsConfig. The legacy
api_key_env / secret_env / passphrase_env overrides are
gone — use ApiCredentials(...) directly to bypass env entirely.
The ladder mirrors the gateway-tier resolver exactly: bare
POLYMARKET_* env var first, then ALMANAK_POLYMARKET_*
alias. Both rungs are handled inside
:func:almanak.config.connectors.connectors_config_from_env.
BalanceAllowance
¶
Bases: BaseModel
Balance and allowance information.
GammaMarket
¶
Bases: BaseModel
Market data from Gamma Markets API.
from_api_response
classmethod
¶
Create from Gamma API response.
HistoricalPrice
dataclass
¶
Single historical price point.
Represents a price at a specific timestamp from the CLOB API. Price values represent probability (0.0 to 1.0).
from_api_response
classmethod
¶
Create from API response.
HistoricalTrade
dataclass
¶
HistoricalTrade(
id: str,
token_id: str,
side: Literal["BUY", "SELL"],
price: Decimal,
size: Decimal,
timestamp: datetime,
maker: str | None = None,
taker: str | None = None,
)
Historical trade from the market.
Represents a single executed trade from the trade tape.
from_api_response
classmethod
¶
Create from API response.
LimitOrderParams
dataclass
¶
LimitOrderParams(
token_id: str,
side: Literal["BUY", "SELL"],
price: Decimal,
size: Decimal,
expiration: int = 0,
)
Parameters for building a V2 limit order.
V2 changes vs V1:
- Drop on-chain expiration (V2 orders carry no on-chain expiration;
GTD time is API-level and lives on the order envelope, not the signed struct).
- Drop fee_rate_bps (operator-set in V2; never signed by the maker).
MarketFilters
¶
Bases: BaseModel
Filters for market queries.
MarketOrderParams
dataclass
¶
MarketOrderParams(
token_id: str,
side: Literal["BUY", "SELL"],
amount: Decimal,
worst_price: Decimal | None = None,
)
Parameters for building a market order.
OpenOrder
¶
Bases: BaseModel
Open order information.
OrderBook
¶
Bases: BaseModel
Orderbook for a token.
best_bid
property
¶
Best bid (highest price someone is willing to BUY at).
Polymarket's CLOB returns the orderbook in depth-walk order — bids
ascend from worst to best, so the best bid sits at the END of the
list, not the start. Cross-checked against GET /price?side=BUY
which returns the same value as bids[-1].price on a live market.
best_ask
property
¶
Best ask (lowest price someone is willing to SELL at).
Polymarket's CLOB returns the orderbook in depth-walk order — asks
descend from worst to best, so the best ask sits at the END of the
list. Cross-checked against GET /price?side=SELL.
from_api_response
classmethod
¶
Create from CLOB API response.
OrderFilters
¶
Bases: BaseModel
Filters for order queries.
OrderResponse
¶
Bases: BaseModel
Response from order submission.
The POST /order API returns status, filledSize (immediate fills at
submission time) and, when available, avgPrice (volume-weighted fill
price). These fields are the basis for distinguishing "order accepted"
from "order filled" in downstream execution (VIB-3218).
VIB-3710: also carries (a) setup_txs — the on-chain approval / wrap
transactions the gateway submitted before this order, populated only when
_ensure_wallet_ready actually had work to do; and (b) fee_pusd —
the operator-set fee charged at match time, which is NOT part of the
signed order payload and so cannot be derived strategy-side without the
gateway surfacing it explicitly.
from_api_response
classmethod
¶
Create from CLOB API response.
VIB-3218: Polymarket POST /order returns statuses the original enum
did not cover (delayed / unmatched); the OrderStatus enum
has been extended to include them. A truly-unknown status (new API
value, typo) falls back to FAILED with a warning -- the safest
default for a money-critical path is to force caller attention, not
to silently pretend the order is resting on the book.
OrderSide
¶
Bases: int, Enum
Order side.
OrderStatus
¶
Bases: StrEnum
Order status values.
VIB-3218: Polymarket's POST /order response can return delayed (matching
engine still processing) and unmatched (IOC / FOK failed to match any
liquidity) in addition to the historical four. FAILED / REJECTED
cover adapter-side and API-side rejections. Missing these from the enum
caused OrderResponse.from_api_response to silently coerce them to
LIVE, turning a rejected order into a "healthy resting order" and
defeating downstream failure detection.
OrderType
¶
Bases: StrEnum
Order time-in-force types.
PolymarketConfig
¶
Bases: BaseModel
Configuration for Polymarket connector.
Carries only public/inert addresses + URL overrides. Signing material
(private key, signer service URL/JWT) is not part of this config —
it lives in a :class:Signer callable injected into ClobClient.
See issue #1961 for the rationale (the gateway is the security boundary;
a strategy that grabs the connector directly must not be one mistake away
from holding signer credentials).
L2 (HMAC) API credentials remain on the config because they are derived
via L1 signing once and are wallet-scoped — the strategy container never
sees them today (only the gateway calls derive_api_credentials).
Credential Management
Polymarket uses a two-tier authentication system:
L1 Authentication (Wallet Signing):
- Used for: Creating/deriving API credentials, signing on-chain transactions
- Requires: a :class:Signer callable injected into ClobClient
- No expiration: Wallet keys don't expire
L2 Authentication (HMAC API): - Used for: Orderbook operations (place/cancel orders, fetch positions) - Requires: api_credentials (api_key, secret, passphrase) - Expiration: Credentials typically expire after 7 days - Storage: Environment variables only (no secret manager integration)
Current Limitations: 1. Credentials stored in environment variables only 2. No automatic credential rotation or renewal 3. No expiration monitoring - API calls will fail silently when expired 4. Manual renewal required before expiration
Renewal Process: When credentials expire, you must: 1. Use ClobClient with a valid Signer to call create_api_credentials() or derive_api_credentials() 2. Update environment variables with new credentials 3. Restart the application or reload configuration
Configurable URLs
All API URLs are configurable to support proxies, test environments, or alternative endpoints:
- clob_base_url: CLOB API for orderbook, prices, and trading (default: https://clob.polymarket.com)
- gamma_base_url: Gamma Markets API for market metadata (default: https://gamma-api.polymarket.com)
- data_api_base_url: Data API for positions and user data (default: https://data-api.polymarket.com)
Environment Variables
Required for wallet operations: - POLYMARKET_WALLET_ADDRESS: Wallet address for signing and transactions
Used by signer_from_env (NOT this config):
- POLYMARKET_PRIVATE_KEY: Private key for EIP-712 signing (0x prefixed hex)
- ALMANAK_SIGNER_SERVICE_URL: Almanak Signer Service base URL (platform mode)
- ALMANAK_SIGNER_SERVICE_JWT: JWT for the Almanak Signer Service (platform mode)
Required for trading operations (L2 auth): - POLYMARKET_API_KEY: API key from credential creation/derivation - POLYMARKET_SECRET: Base64-encoded HMAC secret - POLYMARKET_PASSPHRASE: Passphrase from credential creation/derivation
Optional: - POLYGON_RPC_URL: RPC endpoint for Polygon (for on-chain operations) - POLYMARKET_CLOB_URL: Override clob_base_url - POLYMARKET_GAMMA_URL: Override gamma_base_url - POLYMARKET_DATA_API_URL: Override data_api_base_url
Example
from almanak.connectors.polymarket import ( ClobClient, PolymarketConfig, ) from almanak.connectors.polymarket.signer import ( make_local_signer, signer_from_env, )
Basic configuration (config + signer composed separately)¶
config = PolymarketConfig(wallet_address="0x...") signer = make_local_signer("0x...") # private key never reaches config client = ClobClient(config, signer=signer)
Full configuration with API credentials (for trading)¶
config = PolymarketConfig( wallet_address="0x...", api_credentials=ApiCredentials( api_key="your-api-key", secret=SecretStr("your-base64-secret"), passphrase=SecretStr("your-passphrase"), ), ) client = ClobClient(config, signer=make_local_signer("0x..."))
Load from environment¶
config = PolymarketConfig.from_env() signer = signer_from_env() # may be None (read-only mode) creds = ApiCredentials.from_env() config.api_credentials = creds client = ClobClient(config, signer=signer)
Read-only client (no signer)¶
config = PolymarketConfig(wallet_address="0x...") client = ClobClient(config) # signing-required calls raise PolymarketSignatureError
With custom URLs (for proxies or testing)¶
config = PolymarketConfig( wallet_address="0x...", data_api_base_url="https://my-proxy.example.com/data", )
from_env
classmethod
¶
Load configuration from environment variables.
Builds a credentials-free PolymarketConfig. Pair with
:func:almanak.connectors.polymarket.signer.signer_from_env
to produce a :class:Signer for the same env, then::
config = PolymarketConfig.from_env()
signer = signer_from_env()
client = ClobClient(config, signer=signer)
Required Environment Variables
- POLYMARKET_WALLET_ADDRESS: Wallet address (signer EOA or trading EOA)
Optional Environment Variables
- POLYGON_RPC_URL: RPC endpoint for Polygon
- POLYMARKET_CLOB_URL: Override CLOB API base URL
- POLYMARKET_GAMMA_URL: Override Gamma Markets API base URL
- POLYMARKET_DATA_API_URL: Override Data API base URL
Note: POLYMARKET_PRIVATE_KEY / ALMANAK_SIGNER_SERVICE_URL /
ALMANAK_SIGNER_SERVICE_JWT are read by signer_from_env — never
by this method. Keeping the credential read out of the config builder
is what stops a strategy that grabs the connector directly from
having a path to signer material (issue #1961).
Phase 5b: routes through :func:almanak.config.connectors.connectors_config_from_env.
The legacy custom-env-name kwargs were removed — construct
PolymarketConfig(wallet_address=..., rpc_url=..., ...) directly
for non-default env-var names.
Position
¶
Bases: BaseModel
Position information.
PriceHistory
dataclass
¶
PriceHistory(
token_id: str,
interval: str,
prices: list[HistoricalPrice],
start_time: datetime | None = None,
end_time: datetime | None = None,
)
Historical price data for a token.
Contains a time series of prices for OHLC-style analysis.
PriceHistoryInterval
¶
Bases: StrEnum
Supported intervals for price history queries.
PriceLevel
¶
Bases: BaseModel
Single price level in orderbook.
SetupTxInfo
dataclass
¶
SetupTxInfo(
tx_hash: str,
description: str,
gas_used: int,
gas_price_wei: str,
total_cost_wei: str,
)
Per-tx record of a Polymarket V2 on-chain setup transaction (VIB-3710).
The gateway's _ensure_wallet_ready may submit up to 5 ERC-20 approvals
plus 1 source-asset → pUSD wrap on the first BUY for a wallet. These cost
real MATIC and structurally attribute to the position whose first BUY
triggered them. The strategy-side OrderResponse carries one
SetupTxInfo per setup tx so the downstream prediction handler can
fold the gas spend into the position's loaded cost basis.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
tx_hash |
str
|
0x-prefixed Polygon tx hash. |
description |
str
|
Human-readable label produced by the CTF SDK
(e.g. |
gas_used |
int
|
Receipt |
gas_price_wei |
str
|
Effective gas price in wei (post-London EIP-1559 takes
|
total_cost_wei |
str
|
|
SignatureType
¶
Bases: int, Enum
Signature types for Polymarket authentication.
SignedOrder
dataclass
¶
Signed V2 order ready for submission to /order.
to_api_payload
¶
Convert to Polymarket V2 CLOB /order submission payload.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
owner
|
str
|
Polymarket API key (UUID) that owns the credential used to authenticate the request. Required by the API matcher. |
必需 |
order_type
|
str
|
One of "GTC", "GTD", "FOK", "FAK". |
'GTC'
|
Wire shape matches py-clob-client-v2's order_to_json_v2: signature
is inside the order object with 0x prefix, side is a string
("BUY"/"SELL"), and timestamp / metadata / builder are
included alongside the API-level expiration.
TokenPrice
¶
Bases: BaseModel
Price information for a token.
from_api_response
classmethod
¶
Create from CLOB API response.
Trade
¶
Bases: BaseModel
Trade information.
TradeFilters
¶
Bases: BaseModel
Filters for trade queries.
TradeStatus
¶
Bases: StrEnum
Trade status values.
UnsignedOrder
dataclass
¶
UnsignedOrder(
salt: int,
maker: str,
signer: str,
token_id: int,
maker_amount: int,
taker_amount: int,
side: int,
signature_type: int,
timestamp: int,
metadata: str,
builder: str,
exchange_address: str,
api_expiration: int = 0,
)
Unsigned V2 order ready for EIP-712 signing.
Field set matches the V2 Order struct (11 fields signed). The
exchange_address is the V2 verifyingContract for this order — it
differs between regular CTF Exchange V2 and NegRisk CTF Exchange V2,
routed by GammaMarket.neg_risk.
api_expiration is the GTD timestamp included in the wire envelope but
NOT in the signed struct — V2's matcher enforces freshness off-chain.
to_struct
¶
Convert to struct for EIP-712 signing.
Returns the 11-field V2 Order struct. exchange_address and
api_expiration are intentionally excluded — they belong on the
domain / wire envelope, not the signed payload.
CtfEvent
dataclass
¶
CtfEvent(
event_type: PolymarketEventType,
event_name: str,
log_index: int,
transaction_hash: str,
block_number: int,
contract_address: str,
data: dict[str, Any],
raw_topics: list[str] = list(),
raw_data: str = "",
timestamp: datetime = (lambda: datetime.now(UTC))(),
)
Parsed CTF/ERC-1155 event.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
event_type |
PolymarketEventType
|
Type of event |
event_name |
str
|
Name of event |
log_index |
int
|
Index of log in transaction |
transaction_hash |
str
|
Transaction hash |
block_number |
int
|
Block number |
contract_address |
str
|
Contract that emitted event |
data |
dict[str, Any]
|
Parsed event data |
raw_topics |
list[str]
|
Raw event topics |
raw_data |
str
|
Raw event data |
timestamp |
datetime
|
Event timestamp |
CtfParseResult
dataclass
¶
CtfParseResult(
success: bool,
events: list[CtfEvent] = list(),
transfer_singles: list[TransferSingleData] = list(),
transfer_batches: list[TransferBatchData] = list(),
redemptions: list[PayoutRedemptionData] = list(),
erc20_transfers: list[Erc20TransferData] = list(),
redemption_result: RedemptionResult | None = None,
error: str | None = None,
transaction_hash: str = "",
block_number: int = 0,
transaction_success: bool = True,
)
Result of parsing a CTF transaction receipt.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
success |
bool
|
Whether parsing succeeded |
events |
list[CtfEvent]
|
List of all parsed events |
transfer_singles |
list[TransferSingleData]
|
TransferSingle events |
transfer_batches |
list[TransferBatchData]
|
TransferBatch events |
redemptions |
list[PayoutRedemptionData]
|
PayoutRedemption events |
erc20_transfers |
list[Erc20TransferData]
|
ERC-20 Transfer events |
redemption_result |
RedemptionResult | None
|
High-level redemption result |
error |
str | None
|
Error message if parsing failed |
transaction_hash |
str
|
Transaction hash |
block_number |
int
|
Block number |
transaction_success |
bool
|
Whether transaction succeeded |
Erc20TransferData
dataclass
¶
Parsed data from ERC-20 Transfer event.
Event: Transfer(from, to, value)
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
from_addr |
str
|
Sender address |
to_addr |
str
|
Recipient address |
value |
int
|
Amount transferred |
token_address |
str
|
Token contract address |
PayoutRedemptionData
dataclass
¶
PayoutRedemptionData(
redeemer: str,
collateral_token: str,
parent_collection_id: str,
condition_id: str,
index_sets: list[int],
payout: int,
contract_address: str,
)
Parsed data from PayoutRedemption event.
Event: PayoutRedemption(redeemer, collateralToken, parentCollectionId, conditionId, indexSets[], payout)
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
redeemer |
str
|
Address that received payout |
collateral_token |
str
|
Collateral token address (USDC) |
parent_collection_id |
str
|
Parent collection ID (usually 0x0) |
condition_id |
str
|
CTF condition ID |
index_sets |
list[int]
|
Which outcomes were redeemed |
payout |
int
|
Total payout amount |
contract_address |
str
|
Contract that emitted event |
PolymarketEventType
¶
Bases: Enum
Polymarket/CTF event types.
PolymarketReceiptParser
¶
Parser for Polymarket CLOB responses and CTF transaction receipts.
This parser handles two types of data: 1. CLOB API responses - order submissions, fills, status updates 2. CTF transaction receipts - on-chain token transfers and redemptions
Example
parser = PolymarketReceiptParser()
Parse CLOB order submission response¶
trade_result = parser.parse_order_response(api_response) if trade_result.success: print(f"Order {trade_result.order_id}: {trade_result.status}")
Parse CTF redemption transaction receipt¶
ctf_result = parser.parse_ctf_receipt(tx_receipt) if ctf_result.success and ctf_result.redemption_result: print(f"Redeemed: {ctf_result.redemption_result.amount_redeemed} USDC")
Initialize the receipt parser.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
**kwargs
|
Any
|
Additional arguments (ignored for compatibility) |
{}
|
parse_order_response
¶
Parse a CLOB order submission response.
The response contains order details and initial status after submission.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
response
|
dict[str, Any]
|
CLOB API order response dict |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TradeResult
|
TradeResult with order details |
Example response
{ "orderID": "0x1234...", "status": "LIVE", "owner": "0xYourAddress", "market": "19045189...", "side": "BUY", "price": "0.65", "size": "100", "filledSize": "0", "createdAt": "2025-01-15T10:30:00Z" }
parse_fill_notification
¶
Parse a CLOB fill notification.
Fill notifications are received when an order is matched.
.. note::
This method is parser-API surface for callers that subscribe to
Polymarket's CLOB fill stream (a separate websocket channel
from the order-submission response). The SDK does not currently
consume that stream — the runner attaches a
:class:PredictionFill from the order-submission response
instead, which the enricher routes through
:meth:parse_order_response. This method is kept on the
parser API so the parsing logic stays in one place when fill-
stream consumption is added (VIB-3708 follow-up).
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
notification
|
dict[str, Any]
|
Fill notification from CLOB |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TradeResult
|
TradeResult with fill details |
Example notification
{ "type": "fill", "orderId": "0x1234...", "matchId": "0x5678...", "fillSize": "50", "fillPrice": "0.65", "fee": "0", "timestamp": "2025-01-15T10:30:00Z" }
parse_order_status
¶
Parse a CLOB order status query response.
.. note::
Parser-API surface for status polling. The current SDK polling
path (:meth:PolymarketClobClient.get_order) builds an
:class:OpenOrder directly from the API response without going
through the parser. This method is kept available so future
polling consumers (VIB-3708 follow-up) can route through the
parser for consistent edge-case handling — it simply delegates
to :meth:parse_order_response because the response shape is
identical.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
status_response
|
dict[str, Any]
|
Order status response from CLOB |
必需 |
返回:
| 类型 | 描述 |
|---|---|
TradeResult
|
TradeResult with current order status |
parse_ctf_receipt
¶
Parse a CTF transaction receipt for on-chain events.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs', 'transactionHash', etc. |
必需 |
filter_by_contract
|
bool
|
If True (default), only parse logs from known Polymarket V2 contracts (CONDITIONAL_TOKENS, CTF_EXCHANGE_V2, NEG_RISK_EXCHANGE_V2, NEG_RISK_ADAPTER, PUSD, USDCE_POLYGON, COLLATERAL_ONRAMP, COLLATERAL_OFFRAMP). Set to False to parse all matching event signatures regardless of contract address (useful for testing or analyzing multi-protocol transactions). |
True
|
返回:
| 类型 | 描述 |
|---|---|
CtfParseResult
|
CtfParseResult with extracted events and redemption data |
is_polymarket_event
¶
Check if a topic is a known Polymarket/CTF event.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
topic
|
str | bytes
|
Event topic (supports bytes, hex string with/without 0x, any case) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if topic is a known event |
is_polymarket_contract
¶
Check if an address is a known Polymarket contract.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
address
|
str
|
Contract address |
必需 |
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if address is a known Polymarket contract |
get_event_type
¶
Get the event type for a topic.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
topic
|
str | bytes
|
Event topic (supports bytes, hex string with/without 0x, any case) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
PolymarketEventType
|
Event type or UNKNOWN |
extract_outcome_tokens_received
¶
Extract outcome tokens received from transaction receipt.
Note: Without the user's address, we cannot determine transfer direction. This method sums all TransferSingle values as a proxy for tokens involved. For precise directional filtering, pass the user address to your own logic.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int | None
|
Total outcome tokens from all TransferSingle events if found, None otherwise |
extract_cost_basis
¶
Extract cost basis (USDC involved) from transaction receipt.
Note: Without the user's address, we cannot determine transfer direction. This method sums all USDC transfer values as a proxy for the transaction size. For precise directional filtering, pass the user address to your own logic.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int | None
|
Total USDC in base units (6 decimals) from transfers if found, None otherwise |
extract_market_id
¶
Extract market ID (condition ID) from transaction receipt.
For redemptions, extracts the condition ID directly from PayoutRedemption event.
For trades, returns the raw token ID as hex. Note: CTF token IDs use a complex encoding where conditionId = keccak256(oracle, questionId, outcomeSlotCount) and tokenId = positionId derived from collateral + conditionId + indexSet. Decoding the actual conditionId from a tokenId requires reverse lookup via CTF contract. For simplified use cases, the raw tokenId serves as a unique market identifier.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
str | None
|
Condition ID hex string for redemptions, raw token ID hex for trades, |
str | None
|
or None if not found. |
extract_outcome_tokens_sold
¶
Extract outcome tokens sold from transaction receipt.
Note: Polymarket sells are transfers to the exchange, not burns. Without the exchange contract address, we cannot reliably distinguish sells from other transfers. This method sums all TransferSingle values as a proxy for tokens involved in the transaction.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int | None
|
Total outcome tokens from all TransferSingle events if found, None otherwise |
extract_proceeds
¶
Extract proceeds (USDC involved) from transaction receipt.
Note: Without the user's address, we cannot determine transfer direction. This method sums all USDC transfer values as a proxy for the transaction size. For precise directional filtering, pass the user address to your own logic.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int | None
|
Total USDC in base units (6 decimals) from transfers if found, None otherwise |
extract_redemption_amount
¶
Extract redemption amount (outcome tokens redeemed) from receipt.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int | None
|
Total tokens redeemed if found, None otherwise |
extract_payout
¶
Extract payout amount from redemption transaction.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict with 'logs' field |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int | None
|
Payout amount in USDC base units if found, None otherwise |
RedemptionResult
dataclass
¶
RedemptionResult(
success: bool,
tx_hash: str = "",
amount_redeemed: Decimal = (lambda: Decimal("0"))(),
condition_id: str | None = None,
index_sets: list[int] = list(),
payout_amounts: list[Decimal] = list(),
redeemer: str | None = None,
gas_used: int = 0,
error: str | None = None,
)
Result of parsing a CTF redemption transaction receipt.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
success |
bool
|
Whether parsing succeeded |
tx_hash |
str
|
Transaction hash |
amount_redeemed |
Decimal
|
Total USDC received from redemption |
condition_id |
str | None
|
CTF condition ID |
index_sets |
list[int]
|
Which index sets were redeemed |
payout_amounts |
list[Decimal]
|
Amount redeemed per index set |
redeemer |
str | None
|
Address that received the payout |
gas_used |
int
|
Gas used by the transaction |
error |
str | None
|
Error message if parsing failed |
TradeResult
dataclass
¶
TradeResult(
success: bool,
order_id: str | None = None,
status: str | None = None,
filled_size: Decimal = (lambda: Decimal("0"))(),
avg_price: Decimal = (lambda: Decimal("0"))(),
fee: Decimal = (lambda: Decimal("0"))(),
tx_hash: str | None = None,
side: str | None = None,
token_id: str | None = None,
timestamp: datetime | None = None,
error: str | None = None,
)
Result of parsing a CLOB order response or fill.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
success |
bool
|
Whether parsing succeeded |
order_id |
str | None
|
Order ID from CLOB |
status |
str | None
|
Order status (LIVE, MATCHED, CANCELLED, etc.) |
filled_size |
Decimal
|
Amount filled (in shares) |
avg_price |
Decimal
|
Average fill price |
fee |
Decimal
|
Trading fee (in USDC) |
tx_hash |
str | None
|
On-chain settlement tx hash (if settled) |
side |
str | None
|
Order side (BUY or SELL) |
token_id |
str | None
|
CLOB token ID |
timestamp |
datetime | None
|
Order/fill timestamp |
error |
str | None
|
Error message if parsing failed |
TransferBatchData
dataclass
¶
TransferBatchData(
operator: str,
from_addr: str,
to_addr: str,
token_ids: list[int],
values: list[int],
contract_address: str,
)
Parsed data from TransferBatch event.
Event: TransferBatch(operator, from, to, ids[], values[])
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
operator |
str
|
Address that triggered the transfer |
from_addr |
str
|
Sender address |
to_addr |
str
|
Recipient address |
token_ids |
list[int]
|
List of ERC-1155 token IDs |
values |
list[int]
|
List of amounts transferred |
contract_address |
str
|
Contract that emitted event |
TransferSingleData
dataclass
¶
TransferSingleData(
operator: str,
from_addr: str,
to_addr: str,
token_id: int,
value: int,
contract_address: str,
)
Parsed data from TransferSingle event.
Event: TransferSingle(operator, from, to, id, value)
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
operator |
str
|
Address that triggered the transfer |
from_addr |
str
|
Sender address |
to_addr |
str
|
Recipient address |
token_id |
int
|
ERC-1155 token ID |
value |
int
|
Amount transferred |
contract_address |
str
|
Contract that emitted event |
PolymarketSDK
¶
PolymarketSDK(
config: PolymarketConfig,
web3: Any | None = None,
*,
signer: Signer | None = None,
)
Unified SDK for Polymarket prediction market operations.
Orchestrates both CLOB API client (off-chain order management) and CTF SDK (on-chain token operations) to provide a seamless trading experience.
Key features: - Lazy credential creation: API credentials are created on first authenticated request - Unified market lookups: Find markets by ID, slug, or condition ID - Automatic approval checking: Ensure all token approvals are set up - Convenience methods: Common operations wrapped in simple methods
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
config |
Polymarket configuration |
|
clob |
CLOB API client for order management |
|
ctf |
CTF SDK for on-chain operations |
|
web3 |
Web3 instance for on-chain queries (optional) |
Thread Safety
This class is NOT thread-safe. Use separate instances per thread.
Example
config = PolymarketConfig.from_env() web3 = Web3(GatewayWeb3Provider(gateway_client, chain="polygon")) sdk = PolymarketSDK(config, web3)
Get market data¶
market = sdk.get_market_by_slug("btc-100k") print(f"YES: {market.yes_price}, NO: {market.no_price}")
Check and set up approvals¶
approval_txs = sdk.ensure_allowances() if approval_txs: ... print(f"Need {len(approval_txs)} approval(s)")
Initialize Polymarket SDK.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
config
|
PolymarketConfig
|
Polymarket configuration (public/inert addresses + URLs). |
必需 |
web3
|
Any | None
|
Optional Web3 instance for on-chain operations. Required for allowance checking and CTF operations. |
None
|
signer
|
Signer | None
|
Optional EIP-712 :class: |
None
|
get_or_create_credentials
¶
Get existing credentials or create new ones lazily.
This method is called automatically when making authenticated requests. It first attempts to derive existing credentials, and if that fails, creates new ones.
返回:
| 类型 | 描述 |
|---|---|
ApiCredentials
|
ApiCredentials with api_key, secret, and passphrase |
引发:
| 类型 | 描述 |
|---|---|
PolymarketAuthenticationError
|
If credential creation fails |
get_market_by_slug
¶
Get market by URL slug.
Convenience method that searches for a market by its URL slug and raises an error if not found.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
slug
|
str
|
Market URL slug (e.g., "will-bitcoin-exceed-100k-2025") |
必需 |
返回:
| 类型 | 描述 |
|---|---|
GammaMarket
|
GammaMarket object |
引发:
| 类型 | 描述 |
|---|---|
PolymarketMarketNotFoundError
|
If market not found |
Example
market = sdk.get_market_by_slug("btc-100k") print(market.question)
get_market_by_condition_id
¶
Get market by CTF condition ID.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
condition_id
|
str
|
CTF condition ID (0x...) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
GammaMarket
|
GammaMarket object |
引发:
| 类型 | 描述 |
|---|---|
PolymarketMarketNotFoundError
|
If market not found |
Example
market = sdk.get_market_by_condition_id("0x9915bea...")
get_market_by_token_id
¶
Get market by CLOB token ID.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
str
|
CLOB token ID (YES or NO token) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
GammaMarket
|
GammaMarket object |
引发:
| 类型 | 描述 |
|---|---|
PolymarketMarketNotFoundError
|
If market not found |
Example
market = sdk.get_market_by_token_id("19045189...")
get_yes_no_prices
¶
Get YES and NO prices for a market.
Fetches the market and returns the current prices for both YES and NO outcomes.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market_id
|
str
|
Market ID |
必需 |
返回:
| 类型 | 描述 |
|---|---|
tuple[Decimal, Decimal]
|
Tuple of (yes_price, no_price) |
Example
yes_price, no_price = sdk.get_yes_no_prices("12345") print(f"YES: ${yes_price}, NO: ${no_price}")
get_prices_by_slug
¶
Get YES and NO prices for a market by slug.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
slug
|
str
|
Market URL slug |
必需 |
返回:
| 类型 | 描述 |
|---|---|
tuple[Decimal, Decimal]
|
Tuple of (yes_price, no_price) |
Example
yes_price, no_price = sdk.get_prices_by_slug("btc-100k")
ensure_allowances
¶
Build transactions to ensure all necessary approvals.
Checks current allowance status and returns a list of transactions needed to set up all required approvals for trading on Polymarket.
This includes: - USDC approval for CTF Exchange - USDC approval for Neg Risk Exchange - ERC-1155 approval for CTF Exchange - ERC-1155 approval for Neg Risk Adapter
返回:
| 类型 | 描述 |
|---|---|
list[TransactionData]
|
List of TransactionData for any needed approvals. |
list[TransactionData]
|
Empty list if all approvals are already in place. |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If web3 instance is not configured |
Example
txs = sdk.ensure_allowances() if txs: ... print(f"Need {len(txs)} approval(s)") ... for tx in txs: ... # Sign and submit transaction ... pass
check_allowances
¶
Check all relevant token allowances.
Queries USDC allowances and ERC-1155 operator approvals needed for trading on Polymarket.
返回:
| 类型 | 描述 |
|---|---|
AllowanceStatus
|
AllowanceStatus with all allowance information |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If web3 instance is not configured |
Example
status = sdk.check_allowances() print(f"pUSD approved on V2 exchange: {status.pusd_approved_ctf_exchange}") print(f"Fully approved: {status.fully_approved}")
get_pusd_balance
¶
Get pUSD (V2 trading collateral) balance for configured wallet.
返回:
| 类型 | 描述 |
|---|---|
int
|
pUSD balance in base units (6 decimals). |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If web3 instance is not configured. |
get_source_asset_balance
¶
Get the V2 source-asset (USDC.e / native USDC) balance for configured wallet.
get_position_balance
¶
Get ERC-1155 position token balance.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
token_id
|
int
|
Conditional token ID (position ID) |
必需 |
返回:
| 类型 | 描述 |
|---|---|
int
|
Token balance in base units |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If web3 instance is not configured |
Example
balance = sdk.get_position_balance(token_id) print(f"Position: {balance / 1e6} shares")
ModelPredictionProvider
¶
ModelPredictionProvider(
model: Callable | None = None,
confidence_calibration: float = 1.0,
threshold_bullish: float = 0.6,
threshold_bearish: float = 0.4,
)
Signal provider that wraps a machine learning model.
This provider allows integration of custom ML models that predict market outcomes. The model should output a probability for the YES outcome, which is then converted to a SignalResult.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
model |
The prediction model (any callable returning float between 0 and 1) |
|
confidence_calibration |
Calibration factor for model confidence |
Example
from sklearn.linear_model import LogisticRegression
# Train your model
model = LogisticRegression()
model.fit(X_train, y_train)
# Wrap in provider
def predict_fn(market_id, features):
return model.predict_proba([features])[0][1]
provider = ModelPredictionProvider(model=predict_fn)
signal = provider.get_signal("market-123", features=[0.5, 0.3, 0.8])
Initialize the model prediction provider.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
model
|
Callable | None
|
Callable that takes (market_id, **kwargs) and returns probability between 0 and 1 |
None
|
confidence_calibration
|
float
|
Factor to scale model confidence |
1.0
|
threshold_bullish
|
float
|
Probability above which signal is BULLISH |
0.6
|
threshold_bearish
|
float
|
Probability below which signal is BEARISH |
0.4
|
get_signal
¶
Get signal from model prediction.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market_id
|
str
|
The Polymarket market ID |
必需 |
**kwargs
|
Any
|
Parameters to pass to the model (e.g., features) |
{}
|
返回:
| 类型 | 描述 |
|---|---|
SignalResult
|
SignalResult based on model prediction |
NewsAPISignalProvider
¶
NewsAPISignalProvider(
api_key: str | None = None,
base_url: str = "https://newsapi.org/v2",
sentiment_threshold: float = 0.3,
)
Example signal provider using news sentiment analysis.
This is a reference implementation showing how to build a news-based signal provider. In production, you would integrate with a real news API (e.g., NewsAPI, Aylien, or a custom NLP pipeline).
The provider analyzes news headlines and articles related to the market question, extracts sentiment, and converts it to a trading signal.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
api_key |
API key for the news service |
|
base_url |
Base URL for the news API |
|
sentiment_threshold |
Minimum sentiment score to generate a signal |
Example
provider = NewsAPISignalProvider(api_key="your-api-key") signal = provider.get_signal( "market-123", question="Will Bitcoin reach $100k by end of 2024?" )
Initialize the news signal provider.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
api_key
|
str | None
|
API key for NewsAPI (or similar service) |
None
|
base_url
|
str
|
Base URL for the news API |
'https://newsapi.org/v2'
|
sentiment_threshold
|
float
|
Minimum absolute sentiment to generate non-neutral signal |
0.3
|
get_signal
¶
Get signal based on news sentiment.
This example implementation demonstrates the pattern. In production, replace _analyze_sentiment with actual API calls and NLP analysis.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market_id
|
str
|
The Polymarket market ID |
必需 |
**kwargs
|
Any
|
Optional parameters: - question: Market question text for keyword extraction - keywords: Explicit keywords to search for - lookback_hours: How far back to search (default 24) |
{}
|
返回:
| 类型 | 描述 |
|---|---|
SignalResult
|
SignalResult with sentiment-based direction and confidence |
PredictionSignal
¶
Bases: Protocol
Protocol for prediction market signal providers.
Any class implementing this protocol can be used as a signal provider. The protocol is runtime-checkable, so isinstance() can be used.
Example
class MyProvider: def get_signal(self, market_id: str, **kwargs) -> SignalResult: return SignalResult(SignalDirection.BULLISH, 0.8)
provider = MyProvider() assert isinstance(provider, PredictionSignal) # True
get_signal
¶
Get signal for a specific market.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market_id
|
str
|
The Polymarket market ID to analyze |
必需 |
**kwargs
|
Any
|
Additional parameters (market question, end date, etc.) |
{}
|
返回:
| 类型 | 描述 |
|---|---|
SignalResult
|
SignalResult with direction and confidence |
SignalDirection
¶
Bases: StrEnum
Direction of a prediction signal.
BULLISH: Expect the YES outcome to increase in probability BEARISH: Expect the YES outcome to decrease in probability (NO increases) NEUTRAL: No clear directional signal
SignalResult
dataclass
¶
SignalResult(
direction: SignalDirection,
confidence: float,
timestamp: datetime = (lambda: datetime.now(UTC))(),
metadata: dict | None = None,
source: str | None = None,
raw_score: float | None = None,
)
Result from a signal provider.
Represents the output of a signal analysis with direction, confidence, and optional metadata for debugging and auditing.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
direction |
SignalDirection
|
The predicted direction (BULLISH, BEARISH, NEUTRAL) |
confidence |
float
|
Confidence level from 0.0 (no confidence) to 1.0 (certain) |
timestamp |
datetime
|
When the signal was generated (defaults to now) |
metadata |
dict | None
|
Additional data from the signal source (for debugging) |
source |
str | None
|
Identifier for the signal provider |
raw_score |
float | None
|
Raw numerical score before directional interpretation |
Example
result = SignalResult( direction=SignalDirection.BULLISH, confidence=0.85, source="news_sentiment", metadata={"headline": "Positive developments..."} )
neutral
classmethod
¶
Create a neutral signal with low confidence.
Useful for error cases or when no signal is available.
SocialSentimentProvider
¶
SocialSentimentProvider(
platforms: list[str] | None = None,
min_engagement: int = 10,
sentiment_model: str = "vader",
)
Example signal provider using social media sentiment.
This is a reference implementation showing how to build a social media signal provider. In production, you would integrate with social APIs (e.g., Twitter/X API, Reddit API) or social listening platforms.
The provider analyzes social media posts and discussions related to the market topic, extracts sentiment and engagement metrics, and converts them to a trading signal.
属性:
| 名称 | 类型 | 描述 |
|---|---|---|
platforms |
List of platforms to analyze (twitter, reddit, etc.) |
|
min_engagement |
Minimum engagement score to consider |
|
sentiment_model |
Sentiment analysis approach to use |
Example
provider = SocialSentimentProvider(platforms=["twitter", "reddit"]) signal = provider.get_signal( "market-123", topic="Bitcoin ETF approval", hashtags=["#Bitcoin", "#ETF"] )
Initialize the social sentiment provider.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
platforms
|
list[str] | None
|
Platforms to analyze (default: ["twitter", "reddit"]) |
None
|
min_engagement
|
int
|
Minimum engagement (likes, upvotes) to consider |
10
|
sentiment_model
|
str
|
Model for sentiment analysis ("vader", "textblob", "ml") |
'vader'
|
get_signal
¶
Get signal based on social media sentiment.
This example implementation demonstrates the pattern. In production, replace with actual social API integration.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
market_id
|
str
|
The Polymarket market ID |
必需 |
**kwargs
|
Any
|
Optional parameters: - topic: Topic to search for - hashtags: Hashtags to track - lookback_hours: How far back to search (default 6) |
{}
|
返回:
| 类型 | 描述 |
|---|---|
SignalResult
|
SignalResult with social-sentiment-based direction and confidence |
Signer
¶
Bases: Protocol
Sign an EIP-712 typed-data dict; return 0x-prefixed 65-byte r||s||v hex.
The single-method shape is what ClobClient actually needs at the call
sites — anything more (key material, URLs, JWTs) belongs in the factory
that builds the Signer, not on the connector. See issue #1961.
calculate_inventory_skew
¶
Calculate inventory skew factor based on current position.
The skew factor represents how much the position deviates from neutral as a proportion of the maximum allowed position. It's used to adjust bid/ask prices to encourage trades that reduce inventory.
返回:
| 类型 | 描述 |
|---|---|
Decimal
|
Skew factor from -1.0 (max short) to +1.0 (max long). |
Decimal
|
|
Decimal
|
|
Decimal
|
|
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
position
|
Decimal
|
Current position in shares. Positive = long, negative = short. |
必需 |
max_position
|
Decimal
|
Maximum allowed position size (absolute value). |
必需 |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If max_position is not positive |
Example
skew = calculate_inventory_skew(Decimal("500"), Decimal("1000")) print(f"Skew: {skew}") # 0.5 (50% of max long position) Skew: 0.5
skew = calculate_inventory_skew(Decimal("-250"), Decimal("1000")) print(f"Skew: {skew}") # -0.25 (25% of max short position) Skew: -0.25
calculate_optimal_spread
¶
calculate_optimal_spread(
orderbook: OrderBook,
inventory: Decimal,
risk_params: RiskParameters,
) -> tuple[Decimal, Decimal]
Calculate optimal bid and ask prices based on inventory and market state.
Uses the Avellaneda-Stoikov market making model adapted for prediction markets. The spread is adjusted based on: 1. Base spread requirement (minimum profit margin) 2. Inventory skew (encourage position reduction) 3. Market volatility (wider spreads in volatile markets)
The algorithm: 1. Calculate mid price from orderbook 2. Calculate base half-spread 3. Apply inventory skew adjustment 4. Apply volatility adjustment 5. Clamp to valid price range [0.01, 0.99]
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
orderbook
|
OrderBook
|
Current orderbook with bids and asks |
必需 |
inventory
|
Decimal
|
Current position (positive = long, negative = short) |
必需 |
risk_params
|
RiskParameters
|
Risk management parameters |
必需 |
返回:
| 类型 | 描述 |
|---|---|
Decimal
|
Tuple of (bid_price, ask_price) representing optimal quotes. |
Decimal
|
Returns (MIN_PRICE, MAX_PRICE) if orderbook is empty. |
Example
orderbook = client.get_orderbook(token_id) risk_params = RiskParameters(base_spread=Decimal("0.02")) bid, ask = calculate_optimal_spread(orderbook, Decimal("100"), risk_params) print(f"Quote: {bid} - {ask}")
generate_quote_ladder
¶
generate_quote_ladder(
mid_price: Decimal,
spread: Decimal,
num_levels: int,
size_per_level: Decimal,
tick_size: Decimal = Decimal("0.01"),
skew: Decimal = Decimal("0"),
) -> list[Quote]
Generate a quote ladder with multiple price levels.
Creates a symmetric ladder of bids and asks around the mid price, with optional skew adjustment for inventory management.
The ladder places quotes at increasing distances from mid: - Level 1: mid +/- spread/2 - Level 2: mid +/- spread - Level 3: mid +/- spread*1.5 - etc.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
mid_price
|
Decimal
|
Center price for the ladder |
必需 |
spread
|
Decimal
|
Base spread between best bid and ask |
必需 |
num_levels
|
int
|
Number of price levels on each side (1-10) |
必需 |
size_per_level
|
Decimal
|
Size to quote at each price level |
必需 |
tick_size
|
Decimal
|
Minimum price increment (default 0.01) |
Decimal('0.01')
|
skew
|
Decimal
|
Inventory skew factor (-1 to +1) to shift the ladder. Positive skew shifts asks closer (encourage selling). Negative skew shifts bids closer (encourage buying). |
Decimal('0')
|
返回:
| 类型 | 描述 |
|---|---|
list[Quote]
|
List of Quote objects, sorted by price (highest first). |
list[Quote]
|
Bids have side="BUY", asks have side="SELL". |
引发:
| 类型 | 描述 |
|---|---|
ValueError
|
If num_levels is not between 1 and 10, or if parameters produce invalid prices. |
Example
quotes = generate_quote_ladder( ... mid_price=Decimal("0.50"), ... spread=Decimal("0.02"), ... num_levels=3, ... size_per_level=Decimal("10"), ... ) for q in quotes: ... print(f"{q.side} {q.size} @ {q.price}") SELL 10 @ 0.53 SELL 10 @ 0.52 SELL 10 @ 0.51 BUY 10 @ 0.49 BUY 10 @ 0.48 BUY 10 @ 0.47
should_requote
¶
should_requote(
current_quotes: list[Quote],
orderbook: OrderBook,
threshold: Decimal = Decimal("0.01"),
) -> bool
Determine if quotes should be updated based on market changes.
Compares current quotes against the orderbook to decide if requoting is necessary. Requoting is recommended when:
- The market has moved significantly (mid price change > threshold)
- Our quotes are no longer at competitive prices
- Our quotes have been fully filled (detected by absence in orderbook)
This function helps avoid excessive requoting while ensuring quotes remain competitive.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
current_quotes
|
list[Quote]
|
List of currently active quotes |
必需 |
orderbook
|
OrderBook
|
Current orderbook state |
必需 |
threshold
|
Decimal
|
Price movement threshold that triggers requote (default 0.01). A threshold of 0.01 means requote if mid price moves by 1% or more. |
Decimal('0.01')
|
返回:
| 类型 | 描述 |
|---|---|
bool
|
True if quotes should be updated, False otherwise. |
Example
quotes = [Quote(Decimal("0.49"), Decimal("10"), "BUY"), ... Quote(Decimal("0.51"), Decimal("10"), "SELL")] orderbook = client.get_orderbook(token_id) if should_requote(quotes, orderbook, threshold=Decimal("0.01")): ... # Cancel and replace quotes ... pass
build_ctf_exchange_domain
¶
Build a per-order EIP-712 domain.
V2 orders may target either the regular CTF Exchange or the NegRisk CTF Exchange — the verifyingContract differs per market, so we build the domain dict per call rather than holding it as a module constant.
aggregate_signals
¶
aggregate_signals(
signals: list[SignalResult],
weights: list[float] | None = None,
min_agreement: float = 0.0,
) -> SignalResult
Aggregate multiple signals into a single signal.
Combines signals from multiple providers using weighted averaging. Each signal's contribution is weighted by both its explicit weight and its confidence level.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
signals
|
list[SignalResult]
|
List of SignalResult objects to aggregate |
必需 |
weights
|
list[float] | None
|
Optional weights for each signal (default: equal weights) |
None
|
min_agreement
|
float
|
Minimum fraction of signals that must agree on direction for non-neutral result (0.0 to 1.0) |
0.0
|
返回:
| 类型 | 描述 |
|---|---|
SignalResult
|
Aggregated SignalResult |
Example
signals = [ SignalResult(SignalDirection.BULLISH, 0.8, source="news"), SignalResult(SignalDirection.BULLISH, 0.6, source="social"), SignalResult(SignalDirection.NEUTRAL, 0.5, source="model"), ] result = aggregate_signals(signals)
result.direction = BULLISH (2/3 agreement)¶
result.confidence = weighted average of confidences¶
Note
- Empty signals list returns neutral with 0.5 confidence
- Signals with higher confidence have more influence
- Direction is determined by vote weighted by confidence
- Final confidence is the average confidence of the winning direction
combine_with_market_price
¶
combine_with_market_price(
signal: SignalResult,
current_price: Decimal,
edge_threshold: Decimal = Decimal("0.05"),
) -> SignalResult
Combine signal with market price to calculate expected edge.
Adjusts signal confidence based on the potential edge between the signal's implied probability and the current market price.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
signal
|
SignalResult
|
The signal to evaluate |
必需 |
current_price
|
Decimal
|
Current YES token price (between 0 and 1) |
必需 |
edge_threshold
|
Decimal
|
Minimum edge required for confident signal |
Decimal('0.05')
|
返回:
| 类型 | 描述 |
|---|---|
SignalResult
|
Adjusted SignalResult with edge metadata |
make_local_signer
¶
EOA-mode signer — wraps :func:sign_typed_data_local.
The private_key is captured in a closure so callers (gateway, tests)
can hand a Signer to ClobClient without leaking the key onto the
config object. See issue #1961.
make_remote_signer
¶
make_remote_signer(
eoa_address: str,
signer_service_url: str,
signer_service_jwt: str,
*,
http_client: Client | None = None,
) -> Signer
Platform-mode signer — wraps :func:sign_typed_data_remote.
Captures (eoa_address, signer_service_url, signer_service_jwt) plus an
optional reused http_client in a closure. The URL/JWT never reach
ClobClient itself — the Signer object is the only handle the connector
holds. See issue #1961.
signer_from_env
¶
Build a :class:Signer from environment variables, or return None.
Sibling of :meth:PolymarketConfig.from_env — kept here so the credentials
never have to round-trip through the config object. Callers compose
explicitly::
config = PolymarketConfig.from_env() # public/inert addresses
signer = signer_from_env() # may be None (read-only)
client = ClobClient(config, signer=signer)
Resolution order:
POLYMARKET_PRIVATE_KEYset → :func:make_local_signer.ALMANAK_SIGNER_SERVICE_URLANDALMANAK_SIGNER_SERVICE_JWTANDPOLYMARKET_WALLET_ADDRESSset → :func:make_remote_signer.- Neither path fully configured →
None(read-only mode; signing-required calls raise :class:PolymarketSignatureError).
Phase 5b: routes through :class:almanak.config.connectors.ConnectorsConfig.
The legacy custom-env-name kwargs were removed — to wire a non-default
signer in a test or harness, use :func:make_local_signer /
:func:make_remote_signer directly.