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
6 changes: 3 additions & 3 deletions core/scripts/generate-python-exchanges.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ const OVERRIDES = {

function toClassName(name) {
return name
.split('-')
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
.split(/[-_]/)
.map(part => part.toLowerCase() === 'us' ? 'US' : part.charAt(0).toUpperCase() + part.slice(1))
.join('');
}

Expand Down Expand Up @@ -244,7 +244,7 @@ init = init.replace(
// Update the # Exchanges section in __all__
const allEntries = classNames.map(n => ` "${n}",`).join('\n');
init = init.replace(
/( # Exchanges\n)([\s\S]*?)( "Exchange",)/,
/( # Exchanges\r?\n)([\s\S]*?)( "Exchange",)/,
`$1${allEntries}\n$3`
);

Expand Down
16 changes: 12 additions & 4 deletions core/src/exchanges/baozi/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,7 @@ export class BaoziWebSocket {
}

const dataPromise = new Promise<OrderBook>((resolve, reject) => {
if (!this.orderBookResolvers.has(marketPubkey)) {
this.orderBookResolvers.set(marketPubkey, []);
}
this.orderBookResolvers.get(marketPubkey)!.push({ resolve, reject });
this.getOrderBookQueue(marketPubkey).push({ resolve, reject });
});

return withWatchTimeout(
Expand All @@ -105,6 +102,17 @@ export class BaoziWebSocket {
}
}

private getOrderBookQueue(marketPubkey: string): QueuedPromise<OrderBook>[] {
const resolvers = this.orderBookResolvers.get(marketPubkey);
if (resolvers) {
return resolvers;
}

const queue: QueuedPromise<OrderBook>[] = [];
this.orderBookResolvers.set(marketPubkey, queue);
return queue;
}

async close(connection: Connection): Promise<void> {
for (const [, subId] of this.subscriptions) {
await connection.removeAccountChangeListener(subId);
Expand Down
13 changes: 13 additions & 0 deletions core/test/pipeline/baozi-websocket-resolvers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fs from 'fs';
import path from 'path';

describe('Baozi WebSocket resolver queues', () => {
it('does not rely on a non-null assertion when queueing order book watchers', () => {
const source = fs.readFileSync(
path.resolve(__dirname, '../../src/exchanges/baozi/websocket.ts'),
'utf8',
);

expect(source).not.toMatch(/orderBookResolvers\.get\([^)]*\)!/);
});
});
34 changes: 24 additions & 10 deletions sdks/python/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,14 @@ Fetch the order book (bids/asks) for a specific outcome.
**Signature:**

```python
def fetch_order_book(outcome_id: str, limit: Optional[float] = None, params: Optional[Dict[str, Any]] = None) -> OrderBook:
def fetch_order_book(outcome_id: str, limit: Optional[float] = None, params: Optional[FetchOrderBookParams] = None) -> OrderBook:
```

**Parameters:**

- `outcome_id` (str): The Outcome ID (outcomeId) or market slug
- `limit` (float) - **Optional**: Max number of bid/ask levels to return (CCXT-style).
- `params` (Dict[str, Any]) - **Optional**: Optional parameters:
- `limit` (float) - **Optional**: Max number of bid/ask levels to return. For range
- `params` ([FetchOrderBookParams](#fetchorderbookparams)) - **Optional**: Optional parameters:

**Returns:** [OrderBook](#orderbook) - Order book with bids and asks. Returns OrderBook[] when

Expand Down Expand Up @@ -922,27 +922,27 @@ exchange.unwatch_address(address="0xabc...")


---
### `test_dummy_method`
### `close`

Close all WebSocket connections and clean up resources.


**Signature:**

```python
def test_dummy_method(param: Optional[str] = None) -> str:
def close() -> void:
```

**Parameters:**

- `param` (str) - **Optional**: param
- None

**Returns:** str - Result
**Returns:** void - Result

**Example:**

```python
exchange.test_dummy_method(param="...")
exchange.close()
```


Expand Down Expand Up @@ -1966,17 +1966,31 @@ end: str # End of the time range
limit: float # Maximum number of results to return
```

---
### `FetchOrderBookParams`



```python
@dataclass
class FetchOrderBookParams:
side: str # Outcome side: 'yes' or 'no'. Required for exchanges like Limitless where the API returns a single orderbook per market.
outcome: str # Outcome alias: 'yes' or 'no', or an outcome token ID. When set, the first argument is treated as a market ID and this value selects which outcome's order book to fetch. Accepts the literal strings 'yes'/'no' (resolved via a market lookup) or a raw outcome token ID.
since: float # Unix timestamp (ms) — fetch a historical snapshot at or before this time, or the start of a range when combined with `until` (hosted API only).
until: float # Unix timestamp (ms) — end of a historical range. When combined with `since`, returns an array of reconstructed L2 OrderBook snapshots between `since` and `until` (hosted API only).
```

---
### `TradesParams`

Parameters for fetching trade history. No resolution parameter - trades are discrete events.


```python
@dataclass
class TradesParams:
start: str # Start of the time range
end: str # End of the time range
limit: float # Maximum number of results to return
limit: float # Maximum number of results to return (max {@link MAX_TRADES_LIMIT})
```

---
Expand Down
4 changes: 2 additions & 2 deletions sdks/python/pmxt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"""

from .client import Exchange
from ._exchanges import Polymarket, Limitless, Kalshi, KalshiDemo, Probable, Baozi, Myriad, Opinion, Metaculus, Smarkets, Polymarket_us, Hyperliquid, GeminiTitan, Mock, Router
from ._exchanges import Polymarket, Limitless, Kalshi, KalshiDemo, Probable, Baozi, Myriad, Opinion, Metaculus, Smarkets, PolymarketUS, Hyperliquid, GeminiTitan, Mock, Router
from .router import Router
from .server_manager import ServerManager
from .errors import (
Expand Down Expand Up @@ -143,7 +143,7 @@ def restart_server():
"Opinion",
"Metaculus",
"Smarkets",
"Polymarket_us",
"PolymarketUS",
"Hyperliquid",
"GeminiTitan",
"Mock",
Expand Down
6 changes: 3 additions & 3 deletions sdks/python/pmxt/_exchanges.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,8 @@ def __init__(
)


class Polymarket_us(Exchange):
"""Polymarket_us exchange client."""
class PolymarketUS(Exchange):
"""PolymarketUS exchange client."""

def __init__(
self,
Expand All @@ -377,7 +377,7 @@ def __init__(
pmxt_api_key: Optional[str] = None,
):
"""
Initialize Polymarket_us client.
Initialize PolymarketUS client.

Args:
api_key: API key for authentication (optional)
Expand Down
35 changes: 35 additions & 0 deletions sdks/python/scripts/generate-client-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,41 @@ function buildPyReturnLines(config) {
}

function generatePyMethod(name, params, config, sf) {
if (name === 'fetchOrderBook') {
return [
` def fetch_order_book(self, outcome_id: Union[str, "MarketOutcome"] = _UNSET, limit: Optional[float] = None, params: Optional[dict] = None, **kwargs) -> Union[OrderBook, List[OrderBook]]:`,
` try:`,
` args = []`,
` if kwargs:`,
` params = {**(params or {}), **kwargs}`,
` outcome_id = _compat_id(outcome_id, kwargs)`,
` args.append(_resolve_outcome_id(outcome_id))`,
` if limit is not None:`,
` args.append(limit)`,
` if params is not None:`,
` if limit is None:`,
` args.append(None)`,
` args.append(params)`,
` body: dict = {"args": args}`,
` creds = self._get_credentials_dict()`,
` if creds:`,
` body["credentials"] = creds`,
` url = f"{self._resolve_sidecar_host()}/api/{self.exchange_name}/fetchOrderBook"`,
` headers = {"Content-Type": "application/json", "Accept": "application/json"}`,
` headers.update(self._get_auth_headers())`,
` response = self._fetch_with_retry(`,
` lambda: self._api_client.call_api(method="POST", url=url, body=body, header_params=headers)`,
` )`,
` response.read()`,
` data = self._handle_response(json.loads(response.data))`,
` if isinstance(data, list):`,
` return [_convert_order_book(d) for d in data]`,
` return _convert_order_book(data)`,
` except ApiException as e:`,
` raise self._parse_api_exception(e) from None`,
].join('\n');
}

const snakeName = camelToSnake(name);
const paramSig = buildPySignatureParams(params, sf);
const hasParams = params.some(p => camelToSnake(p.name.getText(sf)) === 'params');
Expand Down
34 changes: 24 additions & 10 deletions sdks/typescript/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,14 +389,14 @@ Fetch the order book (bids/asks) for a specific outcome.
**Signature:**

```typescript
async fetchOrderBook(outcomeId: string, limit?: number, params?: Record<string, any>): Promise<OrderBook>
async fetchOrderBook(outcomeId: string, limit?: number, params?: FetchOrderBookParams): Promise<OrderBook>
```

**Parameters:**

- `outcomeId` (string): The Outcome ID (outcomeId) or market slug
- `limit` (number) - **Optional**: Max number of bid/ask levels to return (CCXT-style).
- `params` (Record<string, any>) - **Optional**: Optional parameters:
- `limit` (number) - **Optional**: Max number of bid/ask levels to return. For range
- `params` ([FetchOrderBookParams](#fetchorderbookparams)) - **Optional**: Optional parameters:

**Returns:** Promise<[OrderBook](#orderbook)> - Order book with bids and asks. Returns OrderBook[] when

Expand Down Expand Up @@ -925,27 +925,27 @@ await exchange.unwatchAddress("0xabc...")


---
### `testDummyMethod`
### `close`

Close all WebSocket connections and clean up resources.


**Signature:**

```typescript
async testDummyMethod(param?: string): Promise<string>
async close(): Promise<void>
```

**Parameters:**

- `param` (string) - **Optional**: param
- None

**Returns:** Promise<string> - Result
**Returns:** Promise<void> - Result

**Example:**

```typescript
await exchange.testDummyMethod({ param: "..." })
await exchange.close()
```


Expand Down Expand Up @@ -1967,16 +1967,30 @@ limit?: number; // Maximum number of results to return
}
```

---
### `FetchOrderBookParams`



```typescript
interface FetchOrderBookParams {
side?: string; // Outcome side: 'yes' or 'no'. Required for exchanges like Limitless where the API returns a single orderbook per market.
outcome?: string; // Outcome alias: 'yes' or 'no', or an outcome token ID. When set, the first argument is treated as a market ID and this value selects which outcome's order book to fetch. Accepts the literal strings 'yes'/'no' (resolved via a market lookup) or a raw outcome token ID.
since?: number; // Unix timestamp (ms) — fetch a historical snapshot at or before this time, or the start of a range when combined with `until` (hosted API only).
until?: number; // Unix timestamp (ms) — end of a historical range. When combined with `since`, returns an array of reconstructed L2 OrderBook snapshots between `since` and `until` (hosted API only).
}
```

---
### `TradesParams`

Parameters for fetching trade history. No resolution parameter - trades are discrete events.


```typescript
interface TradesParams {
start?: string; // Start of the time range
end?: string; // End of the time range
limit?: number; // Maximum number of results to return
limit?: number; // Maximum number of results to return (max {@link MAX_TRADES_LIMIT})
}
```

Expand Down
38 changes: 38 additions & 0 deletions sdks/typescript/scripts/generate-client-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,44 @@ function buildReturnLines(config) {
}

function generateMethod(name, params, config, sf) {
if (name === 'fetchOrderBook') {
return [
` async fetchOrderBook(outcomeId: string | MarketOutcome, limit?: number, params?: FetchOrderBookParams): Promise<OrderBook | OrderBook[]> {`,
` await this.initPromise;`,
` try {`,
` const args: any[] = [];`,
` args.push(resolveOutcomeId(outcomeId));`,
` if (limit !== undefined) args.push(limit);`,
` if (params !== undefined) {`,
` if (limit === undefined) args.push(null);`,
` args.push(params);`,
` }`,
` const response = await this.fetchWithRetry(\`\${this.resolveBaseUrl()}/api/\${this.exchangeName}/fetchOrderBook\`, {`,
` method: 'POST',`,
` headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },`,
` body: JSON.stringify({ args, credentials: this.getCredentials() }),`,
` });`,
` if (!response.ok) {`,
` const body = await response.json().catch(() => ({}));`,
` if (body.error && typeof body.error === "object") {`,
` throw fromServerError(body.error);`,
` }`,
` throw new PmxtError(body.error?.message || response.statusText);`,
` }`,
` const json = await response.json();`,
` const data = this.handleResponse(json);`,
` if (Array.isArray(data)) {`,
` return data.map(convertOrderBook);`,
` }`,
` return convertOrderBook(data);`,
` } catch (error) {`,
` if (error instanceof PmxtError) throw error;`,
` throw new PmxtError(\`Failed to fetchOrderBook: \${error}\`);`,
` }`,
` }`,
].join('\n');
}

const sig = buildSignatureParams(params, sf);
const argsCode = buildArgsLines(params, sf);
const returnCode = buildReturnLines(config);
Expand Down
Loading