From superior-skills
Discover Polymarket prediction markets, place single immediate market orders, run v3 backtests, and manage live Nautilus deployments via Superior Trade's API.
How this skill is triggered — by the user, by Claude, or both
Slash command
/superior-skills:polymarketThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Trade prediction markets on Polymarket through Superior Trade. Discover markets, place single immediate market orders, write NautilusTrader strategies, backtest against historical trade data, and deploy live — all through one API.
Trade prediction markets on Polymarket through Superior Trade. Discover markets, place single immediate market orders, write NautilusTrader strategies, backtest against historical trade data, and deploy live — all through one API.
Base URL: Use the environment-configured Superior Trade API base URL. Production is https://api.superior.trade/v3; UAT may use https://api-uat.superior.trade/v3.
Auth: Prefer x-api-key: <api_key> for Superior Trade product API keys. Browser/session callers may use Authorization: Bearer <token>.
Docs: GET /v3/docs (interactive reference), GET /v3/openapi.json (OpenAPI spec)
This skill uses an existing Superior Trade API key:
x-api-key: $SUPERIOR_TRADE_PM_API_KEY
If SUPERIOR_TRADE_PM_API_KEY is not set, ask the user to provide or configure their Superior Trade API key through their normal credential flow. Do not call POST /v3/account/onboard; that path is not part of the current main API.
Trading-account wallets are managed through /v3/account. Polymarket readiness and balances are checked through /v3/account/{address}/status/polymarket. Deposits are not "send-to-address instructions"; the v3 API wraps Polygon USDC/USDC.e from an owned Superior wallet into Polymarket pUSD through /v3/portfolio/polymarket/deposit.
Before live trading:
/v3/account.POST /v3/account/{address}/polymarket.GET /v3/account/{address}/status/polymarket; it reports onboarding, approvals, credentials, and balances.POST /v3/portfolio/polymarket/deposit.POST /v3/deployment/{id}/credentials using only the owned wallet_address.The v3 credentials endpoint accepts wallet-address metadata only:
{ "wallet_address": "0x1234567890123456789012345678901234567890" }
It rejects private_key and rejects wallet addresses outside the authenticated account list.
Current account/funding endpoints:
GET /v3/accountPOST /v3/accountPATCH /v3/account/{address}POST /v3/account/{address}/polymarketGET /v3/account/{address}/status/polymarketGET /v3/account/{address}/deposit-linkPOST /v3/portfolio/polymarket/depositPOST /v3/portfolio/polymarket/exitPOST /v3/authorize-and-send/polymarketUse POST /v3/authorize-and-send/polymarket when the user asks to place one immediate Polymarket bet/order. This is a fast path for a whitelisted placeMarketOrder action, not a strategy deployment. Do not create a deployment plan or run a backtest for a one-off order unless the user asks for a strategy or bot.
Required sequence:
SUPERIOR_TRADE_PM_API_KEY.GET /v3/account.action.from only to a wallet_address returned in items[]. Never trust or invent an arbitrary from address; if the user supplied one, verify it appears in the account list before using it.GET /v3/account/{address}/status/polymarket for that wallet. If onboarding, approvals, credentials, or available pUSD balance are not ready for the requested order, stop and report the blocker.POST /v3/authorize-and-send/polymarket and report the real API result. Never fabricate fills, order IDs, balances, or status.Supported action:
{
"action": {
"type": "placeMarketOrder",
"from": "0x1234567890123456789012345678901234567890",
"tokenID": "1234567890",
"side": "BUY",
"amount": "10",
"price": 0.55,
"orderType": "FOK"
}
}
Fast confirmation format:
Order Summary:
• Venue: Polymarket
• Wallet address: [wallet_address from GET /v3/account] — readiness: [ready/blockers from status endpoint]
• Token ID: [tokenID]
• Side: [BUY/SELL]
• Amount: [amount] pUSD
• Limit price: [price]
• Order type: [orderType]
This will submit one REAL Polymarket market order immediately. Proceed? (yes/no)
Current Polymarket state checks:
GET https://data-api.polymarket.com/positions — public current positions by wallet addressGET https://clob.polymarket.com/data/orders — authenticated open orders through Polymarket CLOB credentials/clientGET https://clob.polymarket.com/data/order/{orderId} — authenticated single-order lookupGET https://clob.polymarket.com/data/trades — authenticated fills/trade historyDo not advertise old/stale paths: /v3/account/onboard, /v3/account/wallets, /v3/account/deposit/polymarket, /v3/account/withdraw/polymarket, or /v3/deployment/{id}/exit.
This skill requires exactly one credential: a Superior Trade API key. The only secret the agent uses is SUPERIOR_TRADE_PM_API_KEY.
Security rules (non-negotiable):
| Can do | Cannot do |
|---|---|
| List/create/rename trading accounts | Access other users' data |
| Bootstrap Polymarket setup, check readiness, and deposit Polygon USDC/USDC.e to pUSD | Export, accept, or view private keys |
| Search markets and run filled-data backtests | Withdraw to an external address |
| Read current Polymarket positions and open orders | Guess holdings from deployment state |
| Plan deployments, store wallet-address credential metadata, start/stop/delete v3 runs | Invent balances or trade results |
Close all Polymarket positions/orders with /v3/portfolio/polymarket/exit after confirmation | Transfer or bridge arbitrary funds |
volume_24h and liquidity.end_date. Never deploy a strategy on a market that resolves before the strategy has time to work.Before any live deployment start, the agent MUST present this summary and wait for an explicit affirmative response:
Deployment Summary:
• Deployment: [deployment_id]
• Venue: Polymarket
• Market(s): [market question(s)]
• Wallet address: [0x...] — readiness: [ready/blockers from status endpoint]
• Trade size/risk settings: [from deployment.config]
• Backtest reviewed: [backtest_id / result summary]
⚠️ This will trade with REAL funds. Proceed? (yes/no)
Do NOT start a live deployment without an explicit affirmative response.
If the same task fails 3+ times (e.g. strategy source/config keeps failing, backtest keeps erroring), stop and:
1. Confirm auth → Use existing `SUPERIOR_TRADE_PM_API_KEY`
2. Account setup → GET/POST /v3/account, then POST /v3/account/{address}/polymarket
3. Check readiness → GET /v3/account/{address}/status/polymarket; deposit if needed
4. Read live state → For holdings/order questions, fetch positions and open orders for the owned wallet
5. Deposit if needed → POST /v3/portfolio/polymarket/deposit using Polygon USDC/USDC.e
6. Discover markets → POST /v3/markets/search — find candidate market slugs matching the user's interest; pass exact Polymarket event URLs directly when the user provides one
7. Write strategy → Author NautilusTrader Python strategy code from the closest archetype
8. Backtest → POST /v3/backtest with `strategyId`, `strategySource`, and `strategyConfig`
9. Review results → Poll/read backtest status/result/logs; analyze performance; iterate or proceed
10. Plan deployment → POST /v3/deployment with `{ deployment: { code, config } }`
11. Store credentials → POST /v3/deployment/{id}/credentials with an owned `wallet_address`
12. Start → Confirm with user → PATCH/PUT /v3/deployment/{id}/status `{ "action": "start" }`
13. Monitor/stop → Status/logs plus live positions/orders; stop with PATCH/PUT `/v3/deployment/{id}/status` `{ "action": "stop" }`
Deployment start requires both credential metadata and explicit user confirmation.
When a user asks for a Polymarket strategy, pick an archetype first and then generate strategy code from it. This keeps backtest assumptions explicit and reduces silent drift.
probability-momentum skill) — momentum, breakout, fast reaction in active marketsprobability-mean-reversion skill) — overreaction fade and range-like behaviordeadline-drift skill) — time-to-resolution behavior, especially before-date marketsrelated-market-spread skill) — relative-value checks across linked marketslarge-fill-pressure skill) — repeated oversized fills with directional follow-throughcatalyst-confirmation skill) — event thesis with market confirmation firstOperational rules:
POST /v3/backtest via strategySource with matching strategyConfig.TradeTick) in backtests; do not promise queue/maker behavior without matching evidence.Filled-data rule: Polymarket strategy logic should be driven by historical TradeTick replay in backtesting. If logic is quote-only, flag it as likely non-tradable in current backtest mode.
Before PATCH /v3/deployment/{id}/status or PUT /v3/deployment/{id}/status → {"action":"start"}:
GET /v3/account/{address}/status/polymarket returns onboarding.ready: true. If not ready, resolve the blockers first; common blockers are no wallet, not onboarded, approvals missing, or balance below 5 USDC.POST /v3/deployment/{id}/credentials with an owned wallet_address; do not send private keys.POST /v3/markets/search → confirm the exact slug to use. If the user gives a Polymarket event URL, pass that URL as the search query so child markets can be expanded. If search returns multiple plausible candidates, show the candidate questions/slugs and ask the user to choose. For backtests, require backtestSupported: true, coverageStatus: "available", and a requested timerange inside the candidate coverage.instrument_id formatted as <clobTokenId>.POLYMARKET.Do NOT skip any step or assume it passed without the API call.
/v3/account — List Trading Accounts{
"items": [
{
"name": "Trading Account 1",
"account_index": 1,
"wallet_address": "0x1234567890123456789012345678901234567890"
}
]
}
/v3/account — Create Trading AccountCreates a Privy server wallet-backed trading account. Limit: 3 active trading accounts per user.
// Request
{ "name": "btc-carry" }
// Response
{
"account": {
"id": "acct_...",
"label": "btc-carry",
"account_index": 2,
"wallet_address": "0x5678901234567890123456789012345678901234",
"wallet_id": "privy-wallet-id",
"status": "active"
}
}
Error 409 trading_account_limit_reached means the user already has 3 active trading accounts.
/v3/account/{address} — Rename Trading Account// Request
{ "name": "fed-spread" }
// Response
{
"account": {
"name": "fed-spread",
"account_index": 2,
"wallet_address": "0x5678901234567890123456789012345678901234"
}
}
/v3/account/{address}/polymarket — Bootstrap PolymarketBootstraps Polymarket setup for an owned trading-account wallet. This returns onboarding steps and accepted assets; it does not return a deposit address.
{
"wallet_id": "privy-wallet-id",
"chain": "polygon",
"chain_id": 137,
"account": {
"type": "trading_account",
"name": "fed-spread",
"account_index": 2,
"wallet_address": "0x5678901234567890123456789012345678901234"
},
"onboarding": {
"target": "polymarket",
"account_type": "trading_account",
"next_step": "deposit_polygon_assets",
"steps": [
{ "id": "verify_trading_account_wallet", "status": "complete" },
{ "id": "deposit_polygon_assets", "status": "ready" },
{ "id": "convert_to_pusd", "status": "available_after_deposit" }
]
},
"accepted_assets": [
{
"symbol": "USDC",
"address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
"decimals": 6
}
],
"gasless": true
}
/v3/account/{address}/status/polymarket — Readiness and BalancesUse this before deployment start. The v3 route reconciles approvals and CLOB credentials when possible and checks Polygon balances.
{
"chain": "polygon",
"chain_id": 137,
"wallet": {
"address": "0x5678901234567890123456789012345678901234",
"name": "fed-spread",
"index": 2
},
"balance": {
"minimum": "5",
"usdc": "10.25",
"usdcNative": "10.25",
"usdcE": "0",
"pusd": "8.00",
"pol": "0.01",
"meetsMinimum": true
},
"onboarding": {
"ready": true,
"onboarded": true,
"approvalsSet": true
},
"polymarketApi": {
"credentialsStored": true
}
}
/v3/account/{address}/deposit-link — Generate Deposit LinksGenerates wallet deep links for depositing native USDC to an owned trading-account wallet. Query params: chain (polygon or arbitrum, default arbitrum), amount (default 10), and wallet (all, metamask, trust, coinbase, etc.).
{
"chain": "polygon",
"chain_id": 137,
"token": {
"symbol": "USDC",
"address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
"decimals": 6
},
"amount": "10",
"amount_atoms": "10000000",
"wallet_address": "0x5678901234567890123456789012345678901234",
"selected_wallet": "all",
"links": {
"metamask": "https://...",
"eip681": "ethereum:pay-..."
}
}
/v3/portfolio/polymarket/deposit — Deposit Polygon USDC to PolymarketWraps Polygon USDC or USDC.e held by an owned Superior wallet into Polymarket pUSD. This is an on-chain action using the platform-managed wallet; present the details and get confirmation when user intent is not already explicit.
// Request
{
"chain": "polygon",
"asset_address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
"amount": "10",
"from": "0x5678901234567890123456789012345678901234"
}
// Response
{
"txHash": "0x...",
"deposit": {
"id": "dep_...",
"venue": "polymarket",
"amount": "10",
"depositedAmount": "10",
"status": "completed",
"superiorWalletAddress": "0x5678901234567890123456789012345678901234",
"polygonBalance": "0",
"pusdBalance": "10",
"bridgeTxHash": "0x..."
}
}
Requirements: chain, asset_address, amount, and from are required strings. chain must be polygon; asset_address must be Polygon USDC or USDC.e; minimum amount is 5 USDC.
/v3/portfolio/polymarket/exit — Close Polymarket PortfolioCloses all Polymarket positions and cancels orders for the selected wallet. This is portfolio-level, not deployment-level; /v3/deployment/{id}/exit does not exist.
// Request
{ "tradingAccountId": "acct_..." }
// Response
{
"orders_cancelled": 3,
"positions_closed": 2,
"wallet_address": "0x5678901234567890123456789012345678901234"
}
Before calling this, tell the user it closes all Polymarket positions/orders for that wallet and get explicit confirmation.
/v3/authorize-and-send/polymarket — Place One Immediate Market OrderSigns and sends one whitelisted Polymarket action through an owned wallet. Use this for a single user-confirmed bet/order, not for strategy deployments.
Before calling this endpoint:
GET /v3/account.wallet_address as action.from.GET /v3/account/{address}/status/polymarket.// Request
{
"action": {
"type": "placeMarketOrder",
"from": "0x5678901234567890123456789012345678901234",
"tokenID": "1234567890",
"side": "BUY",
"amount": "10",
"price": 0.55,
"orderType": "FOK"
}
}
action.type must be placeMarketOrder. action.from must be an owned wallet from GET /v3/account; do not pass user-provided addresses that are not in the account list.
Use these checks whenever the user asks about current holdings, open bets, pending orders, whether a deployment traded, or whether it is safe to stop/exit. Do not infer live exposure from deployment status alone.
Read-only checks do not require user confirmation. Order placement, cancellation, portfolio exit, and deployment start still require explicit confirmation.
https://data-api.polymarket.com/positions — Current PositionsPositions are public by wallet address. First get the owned wallet from GET /v3/account or the selected deployment credentials, then call:
GET https://data-api.polymarket.com/positions?user=0x5678901234567890123456789012345678901234&sizeThreshold=0&limit=500&sortBy=TOKENS&sortDirection=DESC
Summarize:
If the response is empty, say the wallet has no current Polymarket positions verified by the position API.
Open orders require authenticated Polymarket CLOB credentials/client context. Use the authenticated client when available; never ask the user to paste a CLOB secret/passphrase into chat.
const orders = await clobClient.getOpenOrders({ market, asset_id }, true)
const order = await clobClient.getOrder(orderId)
Underlying endpoints:
GET https://clob.polymarket.com/data/ordersGET https://clob.polymarket.com/data/order/{orderId}Filter params are optional:
{
"market": "0x...",
"asset_id": "1234567890",
"id": "order-id"
}
Summarize each meaningful order with:
Unmatched size is original_size - size_matched. If authenticated order reads are unavailable in the current context, say that positions were checked but authenticated open orders could not be fetched from this environment.
Use this when the user asks "did it trade?", "what filled?", or wants debugging for a deployment that appears idle.
const trades = await clobClient.getTrades({ maker_address: walletAddress, market, asset_id }, true)
Underlying endpoint:
GET https://clob.polymarket.com/data/tradesPresent fills as market/outcome, side, price, size, timestamp, and related order ID when available. Do not use trade history as proof of current exposure; check current positions too.
For "what do I have open?" use this order:
GET /v3/account to identify the selected owned wallet if needed.GET /v3/account/{address}/status/polymarket for readiness and balances.GET https://data-api.polymarket.com/positions?... for current positions.getOpenOrders() for pending orders, if client credentials are available.getTrades() only when the user asks about fills/history or debugging.For "exit everything", first show the verified positions/orders summary, then ask for explicit confirmation before calling POST /v3/portfolio/polymarket/exit.
/v3/markets/search — Search Polymarket MarketsUse this before any Polymarket backtest when the user describes a market in natural language ("BTC 120k before July", "Trump Greenland before 2027", "Fed 50 bps cut") or provides a Polymarket event URL such as https://polymarket.com/event/world-cup-winner. The endpoint returns candidates, not a single guaranteed resolution.
// Request
{
"query": "https://polymarket.com/event/world-cup-winner",
"limit": 10
}
Fields: query is required. It can be natural language, an exact market slug, or a Polymarket event URL. limit is optional, defaults to 10, and must be between 1 and 50.
Response:
{
"status": "candidates_found",
"candidates": [
{
"slug": "will-bitcoin-hit-120000-before-july-1",
"question": "Will Bitcoin hit $120,000 before July 1?",
"tradeCount": 12345,
"liquidity": 240000.5,
"volume": 1800000.25,
"volume24h": 95000.1,
"coverage": {
"start": "2026-05-01",
"end": "2026-06-17"
},
"dataMode": "fills_only",
"coverageStatus": "available",
"backtestSupported": true,
"deploymentSupported": false,
"matchedKeywords": ["bitcoin", "120000", "july"]
}
]
}
status: "not_found" with an empty candidates array means no matching market was found. Do not invent a slug.
Search rules:
query; do not manually strip it or guess the child market.https://polymarket.com/event/world-cup-winner can return country-specific markets such as will-portugal-win-the-2026-fifa-world-cup-912.slug exactly as returned into marketSlugs[] for POST /v3/backtest.coverageStatus: "available" and backtestSupported: true.coverageStatus is "metadata_only", the market is known but historical filled data is not hydrated yet; explain that it cannot be backtested until data coverage is available.deploymentSupported is intentionally separate from backtestSupported; do not assume a market is deployable just because filled-data backtesting is available.instrument_ids.Each outcome (Yes/No) is a separate tradeable instrument:
{clobTokenId}.POLYMARKET
Do not derive or guess token IDs from search candidates. Market search is for finding the exact slug and data coverage; it intentionally does not return outcome token IDs. Backtests can resolve market dataset instruments from marketSlugs[]; live deployment config must include a validated instrument_id such as "1234567890.POLYMARKET".
There is no /v3/strategy create/list/detail endpoint in the current apps/api main surface. Strategies are supplied inline:
POST /v3/backtest with strategyId, optional strategySource, and optional strategyConfig.POST /v3/deployment with deployment.code and deployment.config.Keep generated strategy code self-contained and NautilusTrader-compatible. Avoid filesystem/network operations, private-key handling, and hidden side effects.
/v3/backtest — Create and Run// Request
{
"tenantId": "tenant_a",
"backtestId": "bt_xyz789",
"strategyId": "strat_abc123",
"strategySource": "from nautilus_trader.config import StrategyConfig\nfrom nautilus_trader.model.data import TradeTick\n...",
"strategyConfig": {
"order_size": 10,
"lookback_ticks": 20
},
"marketSlugs": ["will-donald-trump-win-the-2024-us-presidential-election"],
"startingBalance": 1000,
"timerange": { "start": "2024-10-01", "end": "2024-11-06" },
"venueProfile": "polymarket-london"
}
// Response — planned/queued
{
"tenantId": "tenant_a",
"backtestId": "bt_xyz789",
"strategyId": "strat_abc123",
"status": "queued",
"executionCluster": "london-poc",
"namespace": "superior-trade",
"resultUri": "gs://.../backtest-results/polymarket/tenant=tenant_a/date=2026-06-17/bt_xyz789.json",
"plannedResources": ["ConfigMap", "Job", "NautilusBacktestRun"],
"resources": []
}
tenantId, backtestId, strategyId, marketSlugs, timerange, and venueProfile are required.strategyId as the strategy/run identifier and include strategySource + strategyConfig. If strategySource is omitted, the API tries the known template matching strategyId.instrument_id, the runner injects the primary market outcome instrument when strategyConfig.instrument_id is omitted. Only provide explicit instrument IDs when the user or a market-detail source gives exact values.slug values returned by POST /v3/markets/search as marketSlugs[].startingBalance defaults to 1000 pUSD.timerange must fit inside the candidate coverage returned by market search. If data coverage is missing, the API returns 409 with a blocked reason instead of pretending the backtest ran.Backtests replay historical trade ticks. Strategies that only subscribe to quote ticks (
subscribe_quote_ticks) may receive no callbacks in a backtest. For backtestable strategies, drive logic fromon_trade_tick(or subscribe to both and let live trading benefit from quotes).
/v3/backtest/{id}/status — Poll StatusPoll until status is completed or failed. Typical run takes 1–30 seconds depending on data size.
Statuses: pending → running → completed | failed
{
"id": "bt_xyz789",
"status": "completed",
"resultUrl": "gs://.../backtest-results/polymarket/tenant=tenant_a/date=2026-06-17/bt_xyz789.json",
"k8sJobName": "polymarket-backtest-bt-xyz789"
}
/v3/backtest/{id} — Persisted RecordReturns the persisted backtest record and current status.
/v3/backtest/{id}/result — Uploaded ResultReturns 202 while the result is not ready, then 200 when the runner has uploaded JSON to GCS.
// Response (completed)
{
"backtest_id": "bt_xyz789",
"status": "completed",
"resultUri": "gs://.../backtest-results/polymarket/tenant=tenant_a/date=2026-06-17/bt_xyz789.json",
"result": {
"starting_balance": 1000,
"ending_balance": 1087.5,
"total_pnl": 87.5,
"total_return_pct": 8.75,
"total_orders": 24,
"total_fills": 24,
"dataset_provenance": {
"dataset_version": "20260617T033000Z-backtest-ready-trade-ticks-compatible",
"source": "persisted_dataset",
"total_ticks": 63626
},
"trades": [
{
"instrument": "0xdd22472e...-21742633....POLYMARKET",
"side": "BUY",
"quantity": 19.23,
"price": 0.52,
"timestamp": "2024-10-15T14:22:00Z"
}
],
"positions": [
{
"instrument": "0xdd22472e...-21742633....POLYMARKET",
"side": "LONG",
"entry_price": 0.52,
"exit_price": 1.00,
"pnl": 48.0
}
]
},
"message": null
}
// Response (failed)
{ "backtest_id": "bt_xyz789", "status": "failed", "message": "No data loaded for slugs: invalid-market-slug" }
Balances are in pUSD. The trades array is capped at the first 50 fills. Win rate, Sharpe, and drawdown are not computed server-side — derive them from trades/positions if the user asks.
/v3/backtest/{id}/logs — Runtime LogsUse this when status is failed, stuck, or the user asks what happened. Logs come from GCP Logging for the Kubernetes Job.
Before suggesting deployment, always run a backtest first. If the backtest produced zero fills over a period that should have generated signals, do not offer deployment — the strategy, market slug, or instrument_id likely has an issue. If PnL is negative, note the timerange may be unsuitable but don't dismiss the strategy outright. If PnL is positive, present results without overpromising — strong backtest fit on a single resolved market is weak evidence. Stay neutral and let the user decide.
/v3/deployment — CreatePlans and persists a single v3 Polymarket deployment. Alias: POST /v3/deployments.
// Request
{
"region": "london",
"deployment": {
"code": "from nautilus_trader.trading import Strategy\n...",
"config": {
"venue": "polymarket",
"market": "btc-120k",
"instrument_id": "1234567890.POLYMARKET",
"trade_size_pusd": 10
}
}
}
// Response — 202
{
"deployment": {
"id": "01k...",
"status": "starting"
},
"executionCluster": "london-poc",
"namespace": "superior-trade",
"region": "london",
"activeDeploymentIds": ["01k..."],
"plannedResources": ["NautilusTenantRuntime"],
"resources": []
}
deployment.code and deployment.config are required. For Polymarket deployments, deployment.config.instrument_id must be formatted as <clobTokenId>.POLYMARKET. If runtime images or artifacts are not ready, the response is 409 with deployment.status: "blocked" and a reason.
/v3/deployment/{id}/credentials — Store Wallet Metadata// Request
{ "wallet_address": "0x1234567890123456789012345678901234567890" }
// Response
{
"id": "01k...",
"credentials_status": "stored",
"exchange": "polymarket",
"wallet_address": "0x1234567890123456789012345678901234567890"
}
The endpoint rejects private_key and rejects wallet addresses not owned by the authenticated user.
/v3/deployment/{id}/status — Start or Stop// Request
{ "action": "start" } // or "stop"
// Response (start)
{
"id": "01k...",
"status": "running",
"previous_status": "pending",
"k8s_deployment_name": "tenant-auth-user-runtime",
"namespace": "superior-trade",
"executionCluster": "london-poc",
"activeDeploymentIds": ["01k..."],
"plannedResources": ["NautilusTenantRuntime"],
"resources": []
}
// Response (stop)
{
"id": "01k...",
"status": "stopped",
"previous_status": "running",
"activeDeploymentIds": []
}
Start requires:
pending or stopped<clobTokenId>.POLYMARKET instrument IDs/v3/deployment/{id}/logs — Runtime LogsReturns Cloud Logging entries for the tenant runtime. Alias: GET /v3/deployments/{id}/logs.
{
"deployment_id": "01k...",
"items": [
{ "timestamp": "2026-06-18T10:00:00.000Z", "message": "started", "severity": "INFO" }
],
"nextCursor": "..."
}
/v3/deployment/{id} — Delete Stopped DeploymentSoft-deletes an owned v3 deployment. Running deployments must be stopped first.
{ "id": "01k...", "message": "Deployment deleted" }
{ "error": "unauthorized", "message": "Missing or invalid authentication" }{ "error": "not_found", "message": "Route not found" } or resource-specific not_foundWrite a NautilusTrader Strategy subclass in Python. The strategy receives real-time market data and submits orders. Keep the code self-contained and avoid filesystem, network, private-key, or environment access.
from nautilus_trader.config import StrategyConfig
from nautilus_trader.model.data import QuoteTick, TradeTick, Bar, BarType
from nautilus_trader.model.enums import OrderSide, TimeInForce
from nautilus_trader.model.events import OrderFilled, PositionOpened, PositionClosed
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.trading import Strategy
class MyStrategyConfig(StrategyConfig, frozen=True):
instrument_id: str # Polymarket instrument ID
# Add strategy-specific parameters here
trade_size: float = 10.0 # pUSD per trade
class MyStrategy(Strategy):
def __init__(self, config: MyStrategyConfig):
super().__init__(config)
self.instrument_id = InstrumentId.from_str(config.instrument_id)
def on_start(self):
"""Called when strategy starts. Subscribe to data here."""
self.subscribe_trade_ticks(self.instrument_id)
# also available: self.subscribe_quote_ticks(...) — live only, see note below
# or: self.subscribe_bars(BarType.from_str("..."))
def on_quote_tick(self, tick: QuoteTick):
"""Called on every bid/ask update (live trading)."""
bid = float(tick.bid_price)
ask = float(tick.ask_price)
def on_trade_tick(self, tick: TradeTick):
"""Called on every trade execution in the market."""
price = float(tick.price)
size = float(tick.size)
def on_bar(self, bar: Bar):
"""Called on every bar close (if subscribed to bars)."""
close = float(bar.close)
def on_order_filled(self, event: OrderFilled):
"""Called when your order is filled."""
pass
def on_position_opened(self, event: PositionOpened):
pass
def on_position_closed(self, event: PositionClosed):
pass
def on_order_rejected(self, event):
"""Called when an order is rejected by venue or risk engine."""
self.log.warning(f"Order rejected: {event.reason}")
def on_stop(self):
"""Called when strategy stops. Clean up here."""
self.cancel_all_orders(self.instrument_id)
Backtest compatibility: backtests replay historical trade ticks. A strategy whose logic lives entirely in
on_quote_tickwill do nothing in a backtest. Drive backtestable logic fromon_trade_tick, or subscribe to both.
Market order (BUY — quote quantity in pUSD):
instrument = self.cache.instrument(self.instrument_id)
order = self.order_factory.market(
instrument_id=self.instrument_id,
order_side=OrderSide.BUY,
quantity=instrument.make_qty(10.0), # 10 pUSD
time_in_force=TimeInForce.IOC,
quote_quantity=True, # BUY uses pUSD amount
)
self.submit_order(order)
Market order (SELL — base quantity in shares):
order = self.order_factory.market(
instrument_id=self.instrument_id,
order_side=OrderSide.SELL,
quantity=instrument.make_qty(25.0), # 25 shares
time_in_force=TimeInForce.IOC,
)
self.submit_order(order)
Limit order (GTC):
order = self.order_factory.limit(
instrument_id=self.instrument_id,
order_side=OrderSide.BUY,
quantity=instrument.make_qty(10.0),
price=instrument.make_price(0.45),
time_in_force=TimeInForce.GTC,
post_only=True, # maker only
)
self.submit_order(order)
quote_quantity=True — quantity is the pUSD amount to spendquote_quantityTimeInForce.IOC (maps to Polymarket FAK)GTC, GTD, FOK, IOCself.cache.instrument(id) to get the instrument for make_qty() and make_price()Position.quantity is always positive (unsigned size)on_order_rejected(self, event) — orders can be rejected by the venue or risk engineLimit orders use share quantities, not pUSD:
shares = dollar_amount / price
# e.g., $50 at price 0.25 = 200 shares
A single strategy can subscribe to and trade multiple instruments:
class MultiOutcomeConfig(StrategyConfig, frozen=True):
instrument_ids: list[str]
trade_size: float = 10.0
class MultiOutcome(Strategy):
def __init__(self, config: MultiOutcomeConfig):
super().__init__(config)
self.ids = [InstrumentId.from_str(i) for i in config.instrument_ids]
def on_start(self):
for iid in self.ids:
self.subscribe_trade_ticks(iid)
def on_trade_tick(self, tick: TradeTick):
# tick.instrument_id tells you which instrument this tick is for
if tick.instrument_id == self.ids[0]:
pass # handle first outcome
elif tick.instrument_id == self.ids[1]:
pass # handle second outcome
For multi-outcome markets (e.g. Fed rate: hold / 25bp cut / 50bp cut), pass all outcome instrument_ids and trade across them in one strategy. Each deployment runs one strategy instance — no need for separate deployments per outcome.
self.cache.quote_tick(instrument_id) — latest bid/askself.cache.instrument(instrument_id) — instrument detailsself.cache.positions(instrument_id=id) — open positions for an instrumentself.clock.utc_now() — current UTC timestampself.portfolio — account balances and positionsBuy high-probability outcomes near resolution. The "T-Bill" of prediction markets. Resolution risk applies: a 0.95 outcome that resolves NO loses everything staked.
from nautilus_trader.config import StrategyConfig
from nautilus_trader.model.data import TradeTick
from nautilus_trader.model.enums import OrderSide, TimeInForce
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.trading import Strategy
class CarryConfig(StrategyConfig, frozen=True):
instrument_id: str
min_probability: float = 0.90
max_probability: float = 0.99
trade_size_pusd: float = 50.0
class Carry(Strategy):
def __init__(self, config: CarryConfig):
super().__init__(config)
self.instrument_id = InstrumentId.from_str(config.instrument_id)
self._has_position = False
def on_start(self):
self.subscribe_trade_ticks(self.instrument_id)
def on_trade_tick(self, tick: TradeTick):
if self._has_position:
return
price = float(tick.price)
if self.config.min_probability <= price <= self.config.max_probability:
instrument = self.cache.instrument(self.instrument_id)
order = self.order_factory.market(
instrument_id=self.instrument_id,
order_side=OrderSide.BUY,
quantity=instrument.make_qty(self.config.trade_size_pusd),
time_in_force=TimeInForce.IOC,
quote_quantity=True,
)
self.submit_order(order)
def on_position_opened(self, event):
self._has_position = True
def on_position_closed(self, event):
self._has_position = False
def on_stop(self):
self.cancel_all_orders(self.instrument_id)
Buy when probability drops sharply (overreaction), sell when it recovers.
from nautilus_trader.config import StrategyConfig
from nautilus_trader.model.data import TradeTick
from nautilus_trader.model.enums import OrderSide, TimeInForce
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.trading import Strategy
class MomentumFadeConfig(StrategyConfig, frozen=True):
instrument_id: str
lookback_ticks: int = 20
drop_threshold: float = -0.05
recovery_threshold: float = 0.02
trade_size_pusd: float = 25.0
class MomentumFade(Strategy):
def __init__(self, config: MomentumFadeConfig):
super().__init__(config)
self.instrument_id = InstrumentId.from_str(config.instrument_id)
self._prices = []
self._has_position = False
def on_start(self):
self.subscribe_trade_ticks(self.instrument_id)
def on_trade_tick(self, tick: TradeTick):
price = float(tick.price)
self._prices.append(price)
if len(self._prices) > self.config.lookback_ticks:
self._prices.pop(0)
if len(self._prices) < self.config.lookback_ticks:
return
recent_return = (price - self._prices[0]) / self._prices[0]
instrument = self.cache.instrument(self.instrument_id)
if not self._has_position and recent_return < self.config.drop_threshold:
order = self.order_factory.market(
instrument_id=self.instrument_id,
order_side=OrderSide.BUY,
quantity=instrument.make_qty(self.config.trade_size_pusd),
time_in_force=TimeInForce.IOC,
quote_quantity=True,
)
self.submit_order(order)
elif self._has_position and recent_return > self.config.recovery_threshold:
positions = self.cache.positions(instrument_id=self.instrument_id)
for pos in positions:
if pos.is_open:
order = self.order_factory.market(
instrument_id=self.instrument_id,
order_side=OrderSide.SELL,
quantity=instrument.make_qty(float(pos.quantity)),
time_in_force=TimeInForce.IOC,
)
self.submit_order(order)
def on_position_opened(self, event):
self._has_position = True
def on_position_closed(self, event):
self._has_position = False
def on_stop(self):
self.cancel_all_orders(self.instrument_id)
Live-only reference. Do not use this as a filled-data backtest template: it needs quote/order-book state, queue assumptions, and spread realism that current Polymarket backtests do not replay. Watch the rate limit: cancelling and re-quoting on every tick can exceed 30 orders/minute on active markets.
from nautilus_trader.config import StrategyConfig
from nautilus_trader.model.data import QuoteTick
from nautilus_trader.model.enums import OrderSide, TimeInForce
from nautilus_trader.model.events import OrderFilled
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.trading import Strategy
class SpreadCaptureConfig(StrategyConfig, frozen=True):
instrument_id: str
spread_width: float = 0.02
order_size: float = 10.0
max_inventory: float = 100.0
class SpreadCapture(Strategy):
def __init__(self, config: SpreadCaptureConfig):
super().__init__(config)
self.instrument_id = InstrumentId.from_str(config.instrument_id)
self._net_qty = 0.0
def on_start(self):
self.subscribe_quote_ticks(self.instrument_id)
def on_quote_tick(self, tick: QuoteTick):
self.cancel_all_orders(self.instrument_id)
if abs(self._net_qty) >= self.config.max_inventory:
return
bid = float(tick.bid_price)
ask = float(tick.ask_price)
mid = (bid + ask) / 2
half = self.config.spread_width / 2
our_bid = mid - half
our_ask = mid + half
if our_bid <= 0 or our_ask >= 1:
return
instrument = self.cache.instrument(self.instrument_id)
bid_order = self.order_factory.limit(
instrument_id=self.instrument_id,
order_side=OrderSide.BUY,
quantity=instrument.make_qty(self.config.order_size),
price=instrument.make_price(our_bid),
time_in_force=TimeInForce.GTC,
post_only=True,
)
self.submit_order(bid_order)
ask_order = self.order_factory.limit(
instrument_id=self.instrument_id,
order_side=OrderSide.SELL,
quantity=instrument.make_qty(self.config.order_size),
price=instrument.make_price(our_ask),
time_in_force=TimeInForce.GTC,
post_only=True,
)
self.submit_order(ask_order)
def on_order_filled(self, event: OrderFilled):
qty = float(event.last_qty)
if event.order_side == OrderSide.BUY:
self._net_qty += qty
else:
self._net_qty -= qty
def on_stop(self):
self.cancel_all_orders(self.instrument_id)
strategySource or deployment.code; there is no separate /v3/strategy validation endpoint.strategyConfig or deployment.config keys match the StrategyConfig class fields.instrument_id formatted as <clobTokenId>.POLYMARKET; do not use condition IDs or market slugs as instrument IDs.status: "failed" with message: "No data loaded for slugs: ..." — the market slug is wrong or has no hydrated fill history. Re-check the exact slug from POST /v3/markets/search and require coverageStatus: "available".message: "Process exited with code N" — the strategy crashed at runtime. Common causes: config kwargs that don't match the config class fields, or referencing an instrument the engine didn't load.credentials_required — call POST /v3/deployment/{id}/credentials with an owned wallet address before starting.deployment_not_ready — call GET /v3/account/{address}/status/polymarket and resolve blockers: not onboarded, approvals missing, or balance below 5 USDC.GET /v3/deployment/{id}/logs if a runtime exists, otherwise retry after the platform issue is resolved.Check in order:
GET /v3/account/{address}/status/polymarket shows onboarding.ready: true and sufficient balanceGET /v3/deployment/{id}/logs for startup errors, order rejections, or strategy exceptionsend_date in the futuremin_probability: 0.90 does nothing while the market trades at 0.60)npx claudepluginhub superior-trade/superior-skills --plugin superior-skillsPlaces bets on Polymarket prediction markets via the Polygon Agent CLI. Browse markets, check prices, buy/sell YES/NO positions, manage orders. Dry-run by default.
Trade on Polymarket prediction markets on Polygon: browse markets, check wallet/CLOB balances, buy/sell YES/NO shares with safety gates. Uses Node.js scripts, WDK vault, and auto-setup.
Trade on prediction markets (Polymarket, Kalshi). Search markets, buy/sell positions, track PnL, and view trade history.