Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/api-doc-config.generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -1011,4 +1011,4 @@
"python": "import pmxt\nimport os\n\nexchange = pmxt.Polymarket(\n private_key=os.getenv('POLYMARKET_PRIVATE_KEY')\n)\n\n# 1. Check balance\nbalances = exchange.fetch_balance()\nif balances:\n balance = balances[0]\n print(f'Available: ${balance.available}')\n\n# 2. Search for a market\nmarkets = exchange.fetch_markets(query='Trump')\nmarket = markets[0]\noutcome = market.yes\n\nprint(f'{market.title}')\nprint(f'Price: {outcome.price * 100:.1f}%')\n\n# 3. Place a limit order\norder = exchange.create_order(\n market_id=market.market_id,\n outcome_id=outcome.outcome_id,\n side='buy',\n type='limit',\n amount=10,\n price=0.50\n)\n\nprint(f'Order placed: {order.id}')\n\n# 4. Check order status\nupdated_order = exchange.fetch_order(order.id)\nprint(f'Status: {updated_order.status}')\nprint(f'Filled: {updated_order.filled}/{updated_order.amount}')\n\n# 5. Cancel if needed\nif updated_order.status == 'open':\n exchange.cancel_order(order.id)\n print('Order cancelled')\n\n# 6. Check positions\npositions = exchange.fetch_positions()\nfor pos in positions:\n pnl_sign = '+' if pos.unrealized_pnl > 0 else ''\n print(f'{pos.outcome_label}: {pnl_sign}${pos.unrealized_pnl:.2f}')",
"typescript": "import pmxt from 'pmxtjs';\n\nconst exchange = new pmxt.Polymarket({\n privateKey: process.env.POLYMARKET_PRIVATE_KEY\n});\n\n// 1. Check balance\nconst [balance] = await exchange.fetchBalance();\nconsole.log(`Available: $${balance.available}`);\n\n// 2. Search for a market\nconst markets = await exchange.fetchMarkets({ query: 'Trump' });\nconst market = markets[0];\nconst outcome = market.yes;\n\nconsole.log(market.title);\nconsole.log(`Price: ${(outcome.price * 100).toFixed(1)}%`);\n\n// 3. Place a limit order\nconst order = await exchange.createOrder({\n marketId: market.marketId,\n outcomeId: outcome.outcomeId,\n side: 'buy',\n type: 'limit',\n amount: 10,\n price: 0.50\n});\n\nconsole.log(`Order placed: ${order.id}`);\n\n// 4. Check order status\nconst updatedOrder = await exchange.fetchOrder(order.id);\nconsole.log(`Status: ${updatedOrder.status}`);\nconsole.log(`Filled: ${updatedOrder.filled}/${updatedOrder.amount}`);\n\n// 5. Cancel if needed\nif (updatedOrder.status === 'open') {\n await exchange.cancelOrder(order.id);\n console.log('Order cancelled');\n}\n\n// 6. Check positions\nconst positions = await exchange.fetchPositions();\npositions.forEach(pos => {\n console.log(`${pos.outcomeLabel}: ${pos.unrealizedPnL > 0 ? '+' : ''}$${pos.unrealizedPnL.toFixed(2)}`);\n});"
}
}
}
25 changes: 25 additions & 0 deletions sdks/python/pmxt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from typing import Any, Dict, List

from .client import Exchange
from .constants import ENV, ENV_BASE_URL, ENV_API_KEY
from ._exchanges import Polymarket, Limitless, Kalshi, KalshiDemo, Probable, Baozi, Myriad, Opinion, Metaculus, Smarkets, PolymarketUS, Polymarket_us, Hyperliquid, GeminiTitan, SuiBets, Suibets, Mock, Router
from .router import Router
from .feed_client import FeedClient
Expand Down Expand Up @@ -61,6 +62,13 @@
EventFilterCriteria,
MarketFetchParams,
EventFetchParams,
SeriesFetchParams,
TradesParams,
FetchOrderBookParams,
ExchangeOptions,
PolymarketOptions,
RouterOptions,
FeedClientOptions,
MatchResult,
EventMatchResult,
MatchedMarketCluster,
Expand All @@ -73,6 +81,9 @@
ExecutionPriceResult,
MatchRelation,
ClusterSortOption,
MatchedClusterSort,
FetchMatchedMarketClustersParams,
FetchMatchedEventClustersParams,
SortOption,
SearchIn,
OrderSide,
Expand Down Expand Up @@ -170,6 +181,14 @@ def restart_server() -> None:
"Router",
"Exchange",
"FeedClient",
"ExchangeOptions",
"PolymarketOptions",
"RouterOptions",
"FeedClientOptions",
# Environment
"ENV",
"ENV_BASE_URL",
"ENV_API_KEY",
# Server Management
"ServerManager",
"server",
Expand Down Expand Up @@ -220,10 +239,16 @@ def restart_server() -> None:
"SubscribedAddressSnapshot",
"MatchRelation",
"ClusterSortOption",
"MatchedClusterSort",
"FetchMatchedMarketClustersParams",
"FetchMatchedEventClustersParams",
"MarketFilterCriteria",
"EventFilterCriteria",
"MarketFetchParams",
"EventFetchParams",
"SeriesFetchParams",
"TradesParams",
"FetchOrderBookParams",
"SortOption",
"SearchIn",
"OrderSide",
Expand Down
2 changes: 2 additions & 0 deletions sdks/python/pmxt/_exchanges.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,3 +547,5 @@ def __init__(
# Backwards-compatible aliases for exchange classes generated before underscore handling.
Polymarket_us = PolymarketUS
Suibets = SuiBets

from .router import Router as Router
73 changes: 30 additions & 43 deletions sdks/python/pmxt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
MarketFilterFunction,
EventFilterCriteria,
EventFilterFunction,
SeriesFetchParams,
TradesParams,
FetchOrderBookParams,
SubscribedAddressSnapshot,
FirehoseEvent,
MatchResult,
Expand Down Expand Up @@ -262,9 +265,9 @@ def _convert_subscription_snapshot(raw: Dict[str, Any]) -> SubscribedAddressSnap
raw_positions = raw.get("positions")
raw_balances = raw.get("balances")
return _auto_convert(SubscribedAddressSnapshot, raw,
trades=[_convert_trade(t) for t in raw_trades] if raw_trades else None,
positions=[_convert_position(p) for p in raw_positions] if raw_positions else None,
balances=[_convert_balance(b) for b in raw_balances] if raw_balances else None,
trades=[_convert_trade(t) for t in (raw_trades or [])],
positions=[_convert_position(p) for p in (raw_positions or [])],
balances=[_convert_balance(b) for b in (raw_balances or [])],
)


Expand Down Expand Up @@ -368,7 +371,7 @@ def __init__(
effective_base_url = f"http://localhost:{actual_port}"

except Exception as e:
raise Exception(
raise PmxtError(
f"Failed to start PMXT server: {e}\n\n"
f"Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n"
f"Or start the server manually: pmxt-server"
Expand Down Expand Up @@ -571,6 +574,7 @@ def _sidecar_read_request(
same ``_parse_api_exception`` path as the POST fallback.
"""
base_url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/{method_name}"
query = _convert_params_to_camel(query)
creds = self._get_credentials_dict()
has_credentials = creds is not None

Expand Down Expand Up @@ -772,7 +776,7 @@ def fetch_markets(self, params: Optional[dict] = None, **kwargs) -> List[Unified
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -795,7 +799,7 @@ def fetch_markets_paginated(self, params: Optional[dict] = None, **kwargs) -> Pa
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -810,7 +814,7 @@ def fetch_markets_paginated(self, params: Optional[dict] = None, **kwargs) -> Pa
data = self._handle_response(json.loads(response.data))
return PaginatedMarketsResult(
data=[_convert_market(m) for m in data.get("data", [])],
total=data.get("total", 0),
total=data.get("total"),
next_cursor=data.get("nextCursor"),
)
except ApiException as e:
Expand All @@ -822,7 +826,7 @@ def fetch_events(self, params: Optional[dict] = None, **kwargs) -> List[UnifiedE
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -845,7 +849,7 @@ def fetch_series(self, params: Optional[dict] = None, **kwargs) -> List[UnifiedS
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -868,7 +872,7 @@ def fetch_market(self, params: Optional[dict] = None, **kwargs) -> UnifiedMarket
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -891,7 +895,7 @@ def fetch_event(self, params: Optional[dict] = None, **kwargs) -> UnifiedEvent:
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand Down Expand Up @@ -920,7 +924,7 @@ def fetch_order_book(self, outcome_id: Union[str, "MarketOutcome"] = _UNSET, lim
if params is not None:
if limit is None:
args.append(None)
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand Down Expand Up @@ -1026,7 +1030,7 @@ def fetch_my_trades(self, params: Optional[dict] = None, **kwargs) -> List[UserT
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1049,7 +1053,7 @@ def fetch_closed_orders(self, params: Optional[dict] = None, **kwargs) -> List[O
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1072,7 +1076,7 @@ def fetch_all_orders(self, params: Optional[dict] = None, **kwargs) -> List[Orde
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand Down Expand Up @@ -1194,7 +1198,7 @@ def fetch_market_matches(self, params: Optional[dict] = None, **kwargs) -> List[
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1216,7 +1220,7 @@ def fetch_matches(self, params: dict, **kwargs) -> List[Any]:
args = []
if kwargs:
params = {**(params or {}), **kwargs}
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1239,7 +1243,7 @@ def fetch_event_matches(self, params: Optional[dict] = None, **kwargs) -> List[A
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1261,7 +1265,7 @@ def compare_market_prices(self, params: dict, **kwargs) -> List[Any]:
args = []
if kwargs:
params = {**(params or {}), **kwargs}
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1283,7 +1287,7 @@ def fetch_related_markets(self, params: dict, **kwargs) -> List[Any]:
args = []
if kwargs:
params = {**(params or {}), **kwargs}
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1306,7 +1310,7 @@ def fetch_matched_markets(self, params: Optional[dict] = None, **kwargs) -> List
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1329,7 +1333,7 @@ def fetch_matched_prices(self, params: Optional[dict] = None, **kwargs) -> List[
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1351,7 +1355,7 @@ def fetch_hedges(self, params: dict, **kwargs) -> List[Any]:
args = []
if kwargs:
params = {**(params or {}), **kwargs}
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand All @@ -1374,7 +1378,7 @@ def fetch_arbitrage(self, params: Optional[dict] = None, **kwargs) -> List[Any]:
if kwargs:
params = {**(params or {}), **kwargs}
if params is not None:
args.append(params)
args.append(_convert_params_to_camel(params))
body: dict = {"args": args}
creds = self._get_credentials_dict()
if creds:
Expand Down Expand Up @@ -1908,7 +1912,7 @@ def watch_order_book(
if params:
if limit is None:
args.append(None)
args.append(params)
args.append(_convert_params_to_camel(params))

ws_data = self._watch_required_via_ws(
"watch_order_book",
Expand All @@ -1917,23 +1921,6 @@ def watch_order_book(
)
return _convert_order_book(ws_data)

def unwatch_order_book(self, outcome_id: Union[str, "MarketOutcome"]) -> None:
"""
Unsubscribe from a previously watched order book stream.

Args:
outcome_id: Outcome ID to stop watching

Returns:
None
"""
outcome_id = _resolve_outcome_id(outcome_id)
self._unwatch_required_via_ws(
"unwatch_order_book",
"unwatchOrderBook",
[outcome_id],
)

def watch_order_books(
self,
outcome_ids: List[Union[str, "MarketOutcome"]] = _UNSET,
Expand Down Expand Up @@ -1985,7 +1972,7 @@ def watch_order_books(
if params:
if limit is None:
args.append(None)
args.append(params)
args.append(_convert_params_to_camel(params))

raw_result = self._watch_batch_required_via_ws(
"watch_order_books",
Expand Down
6 changes: 4 additions & 2 deletions sdks/python/pmxt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import os
from typing import Mapping, NamedTuple, Optional
from types import SimpleNamespace

#: The hosted pmxt production endpoint.
#:
Expand All @@ -28,8 +29,9 @@

#: Environment variable names. Centralised so tests and docs can reference
#: a single source of truth.
ENV_BASE_URL = "PMXT_BASE_URL"
ENV_API_KEY = "PMXT_API_KEY"
ENV = SimpleNamespace(BASE_URL="PMXT_BASE_URL", API_KEY="PMXT_API_KEY")
ENV_BASE_URL = ENV.BASE_URL
ENV_API_KEY = ENV.API_KEY


class ResolvedBaseUrl(NamedTuple):
Expand Down
Loading
Loading