add skills
This commit is contained in:
+228
@@ -0,0 +1,228 @@
|
||||
# WebSocket
|
||||
|
||||
Three channels for real-time data. Market and sports channels are public; user channel requires API credentials.
|
||||
|
||||
## Channels
|
||||
|
||||
| Channel | Endpoint | Auth |
|
||||
|---------|----------|------|
|
||||
| Market | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | No |
|
||||
| User | `wss://ws-subscriptions-clob.polymarket.com/ws/user` | Yes |
|
||||
| Sports | `wss://sports-api.polymarket.com/ws` | No |
|
||||
|
||||
## Market Channel
|
||||
|
||||
Public. Subscribes by **asset IDs** (token IDs).
|
||||
|
||||
### Subscribe
|
||||
|
||||
```json
|
||||
{
|
||||
"assets_ids": ["TOKEN_ID_1", "TOKEN_ID_2"],
|
||||
"type": "market",
|
||||
"custom_feature_enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
Set `custom_feature_enabled: true` to enable `best_bid_ask`, `new_market`, and `market_resolved` events.
|
||||
|
||||
### Event Types
|
||||
|
||||
| Event | Trigger | Key Fields |
|
||||
|-------|---------|------------|
|
||||
| `book` | On subscribe + when trade affects book | `bids[]`, `asks[]`, `hash`, `timestamp` |
|
||||
| `price_change` | Order placed or cancelled | `price_changes[]` with `price`, `size`, `side`, `best_bid`, `best_ask` |
|
||||
| `last_trade_price` | Trade executed | `price`, `side`, `size`, `fee_rate_bps` |
|
||||
| `tick_size_change` | Price hits >0.96 or <0.04 | `old_tick_size`, `new_tick_size` |
|
||||
| `best_bid_ask` | Top-of-book changes | `best_bid`, `best_ask`, `spread` |
|
||||
| `new_market` | Market created | `question`, `assets_ids`, `outcomes` |
|
||||
| `market_resolved` | Market resolved | `winning_asset_id`, `winning_outcome` |
|
||||
|
||||
Events requiring `custom_feature_enabled: true`: `best_bid_ask`, `new_market`, `market_resolved`.
|
||||
|
||||
**`tick_size_change` is critical for bots** — if tick size changes and you use the old one, orders are rejected.
|
||||
|
||||
A `price_change` with `size: "0"` means the price level was removed from the book.
|
||||
|
||||
### Example Messages
|
||||
|
||||
```json
|
||||
// book
|
||||
{
|
||||
"event_type": "book",
|
||||
"asset_id": "TOKEN_ID",
|
||||
"market": "0xCONDITION_ID",
|
||||
"bids": [{"price": ".48", "size": "30"}],
|
||||
"asks": [{"price": ".52", "size": "25"}],
|
||||
"timestamp": "123456789000",
|
||||
"hash": "0x..."
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// price_change
|
||||
{
|
||||
"event_type": "price_change",
|
||||
"market": "0xCONDITION_ID",
|
||||
"price_changes": [{
|
||||
"asset_id": "TOKEN_ID",
|
||||
"price": "0.5",
|
||||
"size": "200",
|
||||
"side": "BUY",
|
||||
"hash": "...",
|
||||
"best_bid": "0.5",
|
||||
"best_ask": "1"
|
||||
}],
|
||||
"timestamp": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## User Channel
|
||||
|
||||
Authenticated. Subscribes by **condition IDs** (market IDs), not asset IDs. The `markets` field is optional — omit it to receive events for all markets.
|
||||
|
||||
### Subscribe
|
||||
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"apiKey": "your-api-key",
|
||||
"secret": "your-api-secret",
|
||||
"passphrase": "your-passphrase"
|
||||
},
|
||||
"markets": ["0xCONDITION_ID"],
|
||||
"type": "user"
|
||||
}
|
||||
```
|
||||
|
||||
### Event Types
|
||||
|
||||
| Event | Trigger |
|
||||
|-------|---------|
|
||||
| `trade` | Trade lifecycle: MATCHED, MINED, CONFIRMED, RETRYING, FAILED |
|
||||
| `order` | Order lifecycle: PLACEMENT, UPDATE, CANCELLATION |
|
||||
|
||||
### Trade Message
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "trade",
|
||||
"id": "trade-uuid",
|
||||
"market": "0xCONDITION_ID",
|
||||
"asset_id": "TOKEN_ID",
|
||||
"side": "BUY",
|
||||
"size": "10",
|
||||
"price": "0.57",
|
||||
"status": "MATCHED",
|
||||
"maker_orders": [{ "order_id": "0x...", "matched_amount": "10", "price": "0.57" }],
|
||||
"type": "TRADE"
|
||||
}
|
||||
```
|
||||
|
||||
### Order Message
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "order",
|
||||
"id": "0xORDER_ID",
|
||||
"market": "0xCONDITION_ID",
|
||||
"asset_id": "TOKEN_ID",
|
||||
"side": "SELL",
|
||||
"price": "0.57",
|
||||
"original_size": "10",
|
||||
"size_matched": "0",
|
||||
"type": "PLACEMENT"
|
||||
}
|
||||
```
|
||||
|
||||
Order types: `PLACEMENT`, `UPDATE` (partial fill), `CANCELLATION`.
|
||||
|
||||
## Sports Channel
|
||||
|
||||
No subscription message needed. Connect and receive all active sports data.
|
||||
|
||||
```json
|
||||
// sport_result
|
||||
{ "type": "sport_result", ... } // Live scores, periods, status
|
||||
```
|
||||
|
||||
## Dynamic Subscribe / Unsubscribe
|
||||
|
||||
Modify subscriptions without reconnecting:
|
||||
|
||||
```json
|
||||
// Market channel — subscribe to more
|
||||
{ "assets_ids": ["NEW_TOKEN_ID"], "operation": "subscribe", "custom_feature_enabled": true }
|
||||
|
||||
// Market channel — unsubscribe
|
||||
{ "assets_ids": ["OLD_TOKEN_ID"], "operation": "unsubscribe" }
|
||||
|
||||
// User channel — subscribe to more markets
|
||||
{ "markets": ["0xNEW_CONDITION_ID"], "operation": "subscribe" }
|
||||
```
|
||||
|
||||
## Heartbeat
|
||||
|
||||
### Market & User Channels
|
||||
Send `PING` every **10 seconds**. Server responds with `PONG`.
|
||||
|
||||
```typescript
|
||||
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
|
||||
|
||||
ws.onopen = () => {
|
||||
// Subscribe...
|
||||
setInterval(() => ws.send("PING"), 10_000);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
if (event.data === "PONG") return;
|
||||
const msg = JSON.parse(event.data);
|
||||
// handle msg.event_type
|
||||
};
|
||||
```
|
||||
|
||||
### Sports Channel
|
||||
Server sends `ping` every 5 seconds. Respond with `pong` within 10 seconds or connection closes.
|
||||
|
||||
## Full TypeScript Example
|
||||
|
||||
```typescript
|
||||
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
|
||||
|
||||
ws.onopen = () => {
|
||||
ws.send(JSON.stringify({
|
||||
type: "market",
|
||||
assets_ids: ["TOKEN_ID"],
|
||||
custom_feature_enabled: true,
|
||||
}));
|
||||
setInterval(() => ws.send("PING"), 10_000);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
if (event.data === "PONG") return;
|
||||
const msg = JSON.parse(event.data);
|
||||
switch (msg.event_type) {
|
||||
case "book":
|
||||
console.log("Book snapshot:", msg.bids.length, "bids", msg.asks.length, "asks");
|
||||
break;
|
||||
case "price_change":
|
||||
for (const pc of msg.price_changes) {
|
||||
console.log(`${pc.side} ${pc.size}@${pc.price} (best: ${pc.best_bid}/${pc.best_ask})`);
|
||||
}
|
||||
break;
|
||||
case "last_trade_price":
|
||||
console.log(`Trade: ${msg.side} ${msg.size}@${msg.price}`);
|
||||
break;
|
||||
case "tick_size_change":
|
||||
console.log(`Tick: ${msg.old_tick_size} → ${msg.new_tick_size}`);
|
||||
break;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Connection closes immediately**: send subscription message right after open
|
||||
- **Drops after ~10s**: you're not sending PING heartbeats
|
||||
- **No messages**: verify asset IDs are correct and markets are active
|
||||
- **Auth failed (user channel)**: check API credentials haven't expired
|
||||
Reference in New Issue
Block a user