pmxt-core 2.9.2 → 2.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseExchange.d.ts +118 -4
- package/dist/BaseExchange.js +160 -7
- package/dist/exchanges/baozi/fetchEvents.js +16 -11
- package/dist/exchanges/baozi/index.d.ts +5 -0
- package/dist/exchanges/baozi/index.js +6 -0
- package/dist/exchanges/kalshi/api.d.ts +7 -1
- package/dist/exchanges/kalshi/api.js +11 -2
- package/dist/exchanges/kalshi/config.d.ts +103 -0
- package/dist/exchanges/kalshi/config.js +144 -0
- package/dist/exchanges/kalshi/fetchEvents.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchEvents.js +138 -67
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchMarkets.js +36 -25
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +3 -3
- package/dist/exchanges/kalshi/fetchOHLCV.js +20 -17
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
- package/dist/exchanges/kalshi/fetchOrderBook.js +60 -0
- package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchTrades.js +32 -0
- package/dist/exchanges/kalshi/index.d.ts +20 -4
- package/dist/exchanges/kalshi/index.js +171 -90
- package/dist/exchanges/kalshi/kalshi.test.js +440 -157
- package/dist/exchanges/kalshi/utils.d.ts +1 -3
- package/dist/exchanges/kalshi/utils.js +15 -16
- package/dist/exchanges/kalshi/websocket.d.ts +4 -3
- package/dist/exchanges/kalshi/websocket.js +87 -61
- package/dist/exchanges/kalshi-demo/index.d.ts +10 -0
- package/dist/exchanges/kalshi-demo/index.js +23 -0
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/fetchEvents.d.ts +2 -1
- package/dist/exchanges/limitless/fetchEvents.js +95 -49
- package/dist/exchanges/limitless/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/limitless/index.d.ts +11 -3
- package/dist/exchanges/limitless/index.js +69 -1
- package/dist/exchanges/limitless/utils.js +1 -0
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/myriad/index.d.ts +9 -3
- package/dist/exchanges/myriad/index.js +34 -0
- package/dist/exchanges/myriad/utils.js +5 -1
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/polymarket/auth.js +3 -1
- package/dist/exchanges/polymarket/fetchEvents.js +116 -80
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/polymarket/index.d.ts +30 -6
- package/dist/exchanges/polymarket/index.js +101 -31
- package/dist/exchanges/polymarket/utils.js +1 -0
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.d.ts +45 -3
- package/dist/exchanges/probable/index.js +61 -0
- package/dist/exchanges/probable/utils.js +5 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/server/app.js +56 -48
- package/dist/server/utils/port-manager.js +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/utils/throttler.d.ts +17 -0
- package/dist/utils/throttler.js +50 -0
- package/package.json +7 -4
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchOrderBook = fetchOrderBook;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const validation_1 = require("../../utils/validation");
|
|
9
|
+
const errors_1 = require("./errors");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
async function fetchOrderBook(baseUrl, id) {
|
|
12
|
+
(0, validation_1.validateIdFormat)(id, "OrderBook");
|
|
13
|
+
try {
|
|
14
|
+
// Check if this is a NO outcome request
|
|
15
|
+
const isNoOutcome = id.endsWith("-NO");
|
|
16
|
+
const ticker = id.replace(/-NO$/, "");
|
|
17
|
+
const url = (0, config_1.getMarketsUrl)(baseUrl, ticker, ["orderbook"]);
|
|
18
|
+
const response = await axios_1.default.get(url);
|
|
19
|
+
const data = response.data.orderbook;
|
|
20
|
+
// Structure: { yes: [[price, qty], ...], no: [[price, qty], ...] }
|
|
21
|
+
// Kalshi returns bids at their actual prices (not inverted)
|
|
22
|
+
// - yes: bids for buying YES at price X
|
|
23
|
+
// - no: bids for buying NO at price X
|
|
24
|
+
let bids;
|
|
25
|
+
let asks;
|
|
26
|
+
if (isNoOutcome) {
|
|
27
|
+
// NO outcome order book:
|
|
28
|
+
// - Bids: people buying NO (use data.no directly)
|
|
29
|
+
// - Asks: people selling NO = people buying YES (invert data.yes)
|
|
30
|
+
bids = (data.no || []).map((level) => ({
|
|
31
|
+
price: level[0] / 100,
|
|
32
|
+
size: level[1],
|
|
33
|
+
}));
|
|
34
|
+
asks = (data.yes || []).map((level) => ({
|
|
35
|
+
price: 1 - level[0] / 100, // Invert YES price to get NO ask price
|
|
36
|
+
size: level[1],
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// YES outcome order book:
|
|
41
|
+
// - Bids: people buying YES (use data.yes directly)
|
|
42
|
+
// - Asks: people selling YES = people buying NO (invert data.no)
|
|
43
|
+
bids = (data.yes || []).map((level) => ({
|
|
44
|
+
price: level[0] / 100,
|
|
45
|
+
size: level[1],
|
|
46
|
+
}));
|
|
47
|
+
asks = (data.no || []).map((level) => ({
|
|
48
|
+
price: 1 - level[0] / 100, // Invert NO price to get YES ask price
|
|
49
|
+
size: level[1],
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
// Sort bids desc, asks asc
|
|
53
|
+
bids.sort((a, b) => b.price - a.price);
|
|
54
|
+
asks.sort((a, b) => a.price - b.price);
|
|
55
|
+
return { bids, asks, timestamp: Date.now() };
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchTrades = fetchTrades;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
async function fetchTrades(baseUrl, id, params) {
|
|
11
|
+
try {
|
|
12
|
+
const ticker = id.replace(/-NO$/, "");
|
|
13
|
+
const url = (0, config_1.getMarketsUrl)(baseUrl, undefined, ["trades"]);
|
|
14
|
+
const response = await axios_1.default.get(url, {
|
|
15
|
+
params: {
|
|
16
|
+
ticker: ticker,
|
|
17
|
+
limit: params.limit || 100,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
const trades = response.data.trades || [];
|
|
21
|
+
return trades.map((t) => ({
|
|
22
|
+
id: t.trade_id,
|
|
23
|
+
timestamp: new Date(t.created_time).getTime(),
|
|
24
|
+
price: t.yes_price / 100,
|
|
25
|
+
amount: t.count,
|
|
26
|
+
side: t.taker_side === "yes" ? "buy" : "sell",
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams } from
|
|
2
|
-
import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, Balance, Order, Position, CreateOrderParams } from
|
|
3
|
-
import { KalshiWebSocketConfig } from
|
|
1
|
+
import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams, OrderHistoryParams } from "../../BaseExchange";
|
|
2
|
+
import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Balance, Order, Position, CreateOrderParams, BuiltOrder } from "../../types";
|
|
3
|
+
import { KalshiWebSocketConfig } from "./websocket";
|
|
4
4
|
export type { KalshiWebSocketConfig };
|
|
5
5
|
export interface KalshiExchangeOptions {
|
|
6
6
|
credentials?: ExchangeCredentials;
|
|
7
7
|
websocket?: KalshiWebSocketConfig;
|
|
8
8
|
}
|
|
9
|
+
/** @internal */
|
|
10
|
+
export interface KalshiInternalOptions extends KalshiExchangeOptions {
|
|
11
|
+
demoMode?: boolean;
|
|
12
|
+
}
|
|
9
13
|
export declare class KalshiExchange extends PredictionMarketExchange {
|
|
10
14
|
readonly has: {
|
|
11
15
|
fetchMarkets: true;
|
|
@@ -21,9 +25,15 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
21
25
|
fetchBalance: true;
|
|
22
26
|
watchOrderBook: true;
|
|
23
27
|
watchTrades: true;
|
|
28
|
+
fetchMyTrades: true;
|
|
29
|
+
fetchClosedOrders: true;
|
|
30
|
+
fetchAllOrders: true;
|
|
31
|
+
buildOrder: true;
|
|
32
|
+
submitOrder: true;
|
|
24
33
|
};
|
|
25
34
|
private auth?;
|
|
26
35
|
private wsConfig?;
|
|
36
|
+
private config;
|
|
27
37
|
constructor(options?: ExchangeCredentials | KalshiExchangeOptions);
|
|
28
38
|
get name(): string;
|
|
29
39
|
protected sign(method: string, path: string, _params: Record<string, any>): Record<string, string>;
|
|
@@ -31,15 +41,21 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
31
41
|
private ensureAuth;
|
|
32
42
|
protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
|
|
33
43
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
34
|
-
fetchOHLCV(id: string, params: OHLCVParams
|
|
44
|
+
fetchOHLCV(id: string, params: OHLCVParams): Promise<PriceCandle[]>;
|
|
35
45
|
fetchOrderBook(id: string): Promise<OrderBook>;
|
|
36
46
|
fetchTrades(id: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
|
|
37
47
|
fetchBalance(): Promise<Balance[]>;
|
|
48
|
+
buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
|
|
49
|
+
submitOrder(built: BuiltOrder): Promise<Order>;
|
|
38
50
|
createOrder(params: CreateOrderParams): Promise<Order>;
|
|
39
51
|
cancelOrder(orderId: string): Promise<Order>;
|
|
40
52
|
fetchOrder(orderId: string): Promise<Order>;
|
|
41
53
|
fetchOpenOrders(marketId?: string): Promise<Order[]>;
|
|
54
|
+
fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
|
|
55
|
+
fetchClosedOrders(params?: OrderHistoryParams): Promise<Order[]>;
|
|
56
|
+
fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]>;
|
|
42
57
|
fetchPositions(): Promise<Position[]>;
|
|
58
|
+
private mapKalshiOrder;
|
|
43
59
|
private mapKalshiOrderStatus;
|
|
44
60
|
private ws?;
|
|
45
61
|
watchOrderBook(id: string, limit?: number): Promise<OrderBook>;
|
|
@@ -12,6 +12,7 @@ const errors_1 = require("./errors");
|
|
|
12
12
|
const errors_2 = require("../../errors");
|
|
13
13
|
const openapi_1 = require("../../utils/openapi");
|
|
14
14
|
const api_1 = require("./api");
|
|
15
|
+
const config_1 = require("./config");
|
|
15
16
|
class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
16
17
|
has = {
|
|
17
18
|
fetchMarkets: true,
|
|
@@ -27,28 +28,38 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
27
28
|
fetchBalance: true,
|
|
28
29
|
watchOrderBook: true,
|
|
29
30
|
watchTrades: true,
|
|
31
|
+
fetchMyTrades: true,
|
|
32
|
+
fetchClosedOrders: true,
|
|
33
|
+
fetchAllOrders: true,
|
|
34
|
+
buildOrder: true,
|
|
35
|
+
submitOrder: true,
|
|
30
36
|
};
|
|
31
37
|
auth;
|
|
32
38
|
wsConfig;
|
|
39
|
+
config;
|
|
33
40
|
constructor(options) {
|
|
34
41
|
// Support both old signature (credentials only) and new signature (options object)
|
|
35
42
|
let credentials;
|
|
36
43
|
let wsConfig;
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
let demoMode = false;
|
|
45
|
+
if (options && "credentials" in options) {
|
|
46
|
+
// New signature: KalshiExchangeOptions / KalshiInternalOptions
|
|
39
47
|
credentials = options.credentials;
|
|
40
48
|
wsConfig = options.websocket;
|
|
49
|
+
demoMode = options.demoMode || false;
|
|
41
50
|
}
|
|
42
51
|
else {
|
|
43
52
|
// Old signature: ExchangeCredentials directly
|
|
44
53
|
credentials = options;
|
|
45
54
|
}
|
|
46
55
|
super(credentials);
|
|
56
|
+
this.rateLimit = 100;
|
|
47
57
|
this.wsConfig = wsConfig;
|
|
58
|
+
this.config = (0, config_1.getKalshiConfig)(demoMode);
|
|
48
59
|
if (credentials?.apiKey && credentials?.privateKey) {
|
|
49
60
|
this.auth = new auth_1.KalshiAuth(credentials);
|
|
50
61
|
}
|
|
51
|
-
const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.kalshiApiSpec);
|
|
62
|
+
const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.kalshiApiSpec, this.config.apiUrl + config_1.KALSHI_PATHS.TRADE_API);
|
|
52
63
|
this.defineImplicitApi(descriptor);
|
|
53
64
|
}
|
|
54
65
|
get name() {
|
|
@@ -61,7 +72,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
61
72
|
const auth = this.ensureAuth();
|
|
62
73
|
// The implicit API passes just the spec path (e.g. /portfolio/balance),
|
|
63
74
|
// but Kalshi's signature requires the full path including /trade-api/v2.
|
|
64
|
-
return auth.getHeaders(method,
|
|
75
|
+
return auth.getHeaders(method, "/trade-api/v2" + path);
|
|
65
76
|
}
|
|
66
77
|
mapImplicitApiError(error) {
|
|
67
78
|
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
@@ -71,8 +82,8 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
71
82
|
// ----------------------------------------------------------------------------
|
|
72
83
|
ensureAuth() {
|
|
73
84
|
if (!this.auth) {
|
|
74
|
-
throw new errors_2.AuthenticationError(
|
|
75
|
-
|
|
85
|
+
throw new errors_2.AuthenticationError("Trading operations require authentication. " +
|
|
86
|
+
"Initialize KalshiExchange with credentials (apiKey and privateKey).", "Kalshi");
|
|
76
87
|
}
|
|
77
88
|
return this.auth;
|
|
78
89
|
}
|
|
@@ -89,10 +100,11 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
89
100
|
return (0, fetchOHLCV_1.fetchOHLCV)(id, params, this.callApi.bind(this));
|
|
90
101
|
}
|
|
91
102
|
async fetchOrderBook(id) {
|
|
92
|
-
(0, validation_1.validateIdFormat)(id,
|
|
93
|
-
const isNoOutcome = id.endsWith(
|
|
94
|
-
const ticker = id.replace(/-NO$/,
|
|
95
|
-
const data = (await this.callApi(
|
|
103
|
+
(0, validation_1.validateIdFormat)(id, "OrderBook");
|
|
104
|
+
const isNoOutcome = id.endsWith("-NO");
|
|
105
|
+
const ticker = id.replace(/-NO$/, "");
|
|
106
|
+
const data = (await this.callApi("GetMarketOrderbook", { ticker }))
|
|
107
|
+
.orderbook;
|
|
96
108
|
let bids;
|
|
97
109
|
let asks;
|
|
98
110
|
if (isNoOutcome) {
|
|
@@ -101,7 +113,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
101
113
|
size: level[1],
|
|
102
114
|
}));
|
|
103
115
|
asks = (data.yes || []).map((level) => ({
|
|
104
|
-
price: 1 -
|
|
116
|
+
price: 1 - level[0] / 100,
|
|
105
117
|
size: level[1],
|
|
106
118
|
}));
|
|
107
119
|
}
|
|
@@ -111,7 +123,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
111
123
|
size: level[1],
|
|
112
124
|
}));
|
|
113
125
|
asks = (data.no || []).map((level) => ({
|
|
114
|
-
price: 1 -
|
|
126
|
+
price: 1 - level[0] / 100,
|
|
115
127
|
size: level[1],
|
|
116
128
|
}));
|
|
117
129
|
}
|
|
@@ -120,129 +132,172 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
120
132
|
return { bids, asks, timestamp: Date.now() };
|
|
121
133
|
}
|
|
122
134
|
async fetchTrades(id, params) {
|
|
123
|
-
if (
|
|
135
|
+
if ("resolution" in params && params.resolution !== undefined) {
|
|
124
136
|
console.warn('[pmxt] Warning: The "resolution" parameter is deprecated for fetchTrades() and will be ignored. ' +
|
|
125
|
-
|
|
137
|
+
"It will be removed in v3.0.0. Please remove it from your code.");
|
|
126
138
|
}
|
|
127
|
-
const ticker = id.replace(/-NO$/,
|
|
128
|
-
const data = await this.callApi(
|
|
139
|
+
const ticker = id.replace(/-NO$/, "");
|
|
140
|
+
const data = await this.callApi("GetTrades", {
|
|
141
|
+
ticker,
|
|
142
|
+
limit: params.limit || 100,
|
|
143
|
+
});
|
|
129
144
|
const trades = data.trades || [];
|
|
130
145
|
return trades.map((t) => ({
|
|
131
146
|
id: t.trade_id,
|
|
132
147
|
timestamp: new Date(t.created_time).getTime(),
|
|
133
148
|
price: t.yes_price / 100,
|
|
134
149
|
amount: t.count,
|
|
135
|
-
side: t.taker_side ===
|
|
150
|
+
side: t.taker_side === "yes" ? "buy" : "sell",
|
|
136
151
|
}));
|
|
137
152
|
}
|
|
138
153
|
// ----------------------------------------------------------------------------
|
|
139
154
|
// User Data Methods
|
|
140
155
|
// ----------------------------------------------------------------------------
|
|
141
156
|
async fetchBalance() {
|
|
142
|
-
const data = await this.callApi(
|
|
157
|
+
const data = await this.callApi("GetBalance");
|
|
143
158
|
const available = data.balance / 100;
|
|
144
159
|
const total = data.portfolio_value / 100;
|
|
145
|
-
return [
|
|
146
|
-
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
162
|
+
currency: "USD",
|
|
147
163
|
total,
|
|
148
164
|
available,
|
|
149
165
|
locked: total - available,
|
|
150
|
-
}
|
|
166
|
+
},
|
|
167
|
+
];
|
|
151
168
|
}
|
|
152
169
|
// ----------------------------------------------------------------------------
|
|
153
170
|
// Trading Methods
|
|
154
171
|
// ----------------------------------------------------------------------------
|
|
155
|
-
async
|
|
156
|
-
const isYesSide = params.side ===
|
|
157
|
-
const
|
|
172
|
+
async buildOrder(params) {
|
|
173
|
+
const isYesSide = params.side === "buy";
|
|
174
|
+
const body = {
|
|
158
175
|
ticker: params.marketId,
|
|
159
176
|
client_order_id: `pmxt-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
160
|
-
side: isYesSide ?
|
|
161
|
-
action: params.side ===
|
|
177
|
+
side: isYesSide ? "yes" : "no",
|
|
178
|
+
action: params.side === "buy" ? "buy" : "sell",
|
|
162
179
|
count: params.amount,
|
|
163
|
-
type: params.type ===
|
|
180
|
+
type: params.type === "limit" ? "limit" : "market",
|
|
164
181
|
};
|
|
165
182
|
if (params.price) {
|
|
166
183
|
const priceInCents = Math.round(params.price * 100);
|
|
167
184
|
if (isYesSide) {
|
|
168
|
-
|
|
185
|
+
body.yes_price = priceInCents;
|
|
169
186
|
}
|
|
170
187
|
else {
|
|
171
|
-
|
|
188
|
+
body.no_price = priceInCents;
|
|
172
189
|
}
|
|
173
190
|
}
|
|
174
|
-
const data = await this.callApi('CreateOrder', kalshiOrder);
|
|
175
|
-
const order = data.order;
|
|
176
191
|
return {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
side: params.side,
|
|
181
|
-
type: params.type,
|
|
182
|
-
price: params.price,
|
|
183
|
-
amount: params.amount,
|
|
184
|
-
status: this.mapKalshiOrderStatus(order.status),
|
|
185
|
-
filled: order.queue_position === 0 ? params.amount : 0,
|
|
186
|
-
remaining: order.remaining_count || params.amount,
|
|
187
|
-
timestamp: new Date(order.created_time).getTime(),
|
|
192
|
+
exchange: this.name,
|
|
193
|
+
params,
|
|
194
|
+
raw: body,
|
|
188
195
|
};
|
|
189
196
|
}
|
|
197
|
+
async submitOrder(built) {
|
|
198
|
+
const data = await this.callApi("CreateOrder", built.raw);
|
|
199
|
+
return this.mapKalshiOrder(data.order);
|
|
200
|
+
}
|
|
201
|
+
async createOrder(params) {
|
|
202
|
+
const built = await this.buildOrder(params);
|
|
203
|
+
return this.submitOrder(built);
|
|
204
|
+
}
|
|
190
205
|
async cancelOrder(orderId) {
|
|
191
|
-
const data = await this.callApi(
|
|
206
|
+
const data = await this.callApi("CancelOrder", { order_id: orderId });
|
|
192
207
|
const order = data.order;
|
|
193
208
|
return {
|
|
194
209
|
id: order.order_id,
|
|
195
210
|
marketId: order.ticker,
|
|
196
211
|
outcomeId: order.ticker,
|
|
197
|
-
side: order.side ===
|
|
198
|
-
type:
|
|
212
|
+
side: order.side === "yes" ? "buy" : "sell",
|
|
213
|
+
type: "limit",
|
|
199
214
|
amount: order.count,
|
|
200
|
-
status:
|
|
215
|
+
status: "cancelled",
|
|
201
216
|
filled: order.count - (order.remaining_count || 0),
|
|
202
217
|
remaining: 0,
|
|
203
218
|
timestamp: new Date(order.created_time).getTime(),
|
|
204
219
|
};
|
|
205
220
|
}
|
|
206
221
|
async fetchOrder(orderId) {
|
|
207
|
-
const data = await this.callApi(
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
id: order.order_id,
|
|
211
|
-
marketId: order.ticker,
|
|
212
|
-
outcomeId: order.ticker,
|
|
213
|
-
side: order.side === 'yes' ? 'buy' : 'sell',
|
|
214
|
-
type: order.type === 'limit' ? 'limit' : 'market',
|
|
215
|
-
price: order.yes_price ? order.yes_price / 100 : undefined,
|
|
216
|
-
amount: order.count,
|
|
217
|
-
status: this.mapKalshiOrderStatus(order.status),
|
|
218
|
-
filled: order.count - (order.remaining_count || 0),
|
|
219
|
-
remaining: order.remaining_count || 0,
|
|
220
|
-
timestamp: new Date(order.created_time).getTime(),
|
|
221
|
-
};
|
|
222
|
+
const data = await this.callApi("GetOrder", { order_id: orderId });
|
|
223
|
+
return this.mapKalshiOrder(data.order);
|
|
222
224
|
}
|
|
223
225
|
async fetchOpenOrders(marketId) {
|
|
224
|
-
const queryParams = { status:
|
|
226
|
+
const queryParams = { status: "resting" };
|
|
225
227
|
if (marketId) {
|
|
226
228
|
queryParams.ticker = marketId;
|
|
227
229
|
}
|
|
228
|
-
const data = await this.callApi(
|
|
230
|
+
const data = await this.callApi("GetOrders", queryParams);
|
|
229
231
|
const orders = data.orders || [];
|
|
230
|
-
return orders.map((order) => (
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
232
|
+
return orders.map((order) => this.mapKalshiOrder(order));
|
|
233
|
+
}
|
|
234
|
+
async fetchMyTrades(params) {
|
|
235
|
+
const queryParams = {};
|
|
236
|
+
if (params?.outcomeId || params?.marketId) {
|
|
237
|
+
queryParams.ticker = (params.outcomeId || params.marketId).replace(/-NO$/, "");
|
|
238
|
+
}
|
|
239
|
+
if (params?.since)
|
|
240
|
+
queryParams.min_ts = Math.floor(params.since.getTime() / 1000);
|
|
241
|
+
if (params?.until)
|
|
242
|
+
queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
|
|
243
|
+
if (params?.limit)
|
|
244
|
+
queryParams.limit = params.limit;
|
|
245
|
+
if (params?.cursor)
|
|
246
|
+
queryParams.cursor = params.cursor;
|
|
247
|
+
const data = await this.callApi("GetFills", queryParams);
|
|
248
|
+
return (data.fills || []).map((f) => ({
|
|
249
|
+
id: f.fill_id,
|
|
250
|
+
timestamp: new Date(f.created_time).getTime(),
|
|
251
|
+
price: f.yes_price / 100,
|
|
252
|
+
amount: f.count,
|
|
253
|
+
side: f.side === "yes" ? "buy" : "sell",
|
|
254
|
+
orderId: f.order_id,
|
|
242
255
|
}));
|
|
243
256
|
}
|
|
257
|
+
async fetchClosedOrders(params) {
|
|
258
|
+
const queryParams = {};
|
|
259
|
+
if (params?.marketId)
|
|
260
|
+
queryParams.ticker = params.marketId;
|
|
261
|
+
if (params?.until)
|
|
262
|
+
queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
|
|
263
|
+
if (params?.limit)
|
|
264
|
+
queryParams.limit = params.limit;
|
|
265
|
+
if (params?.cursor)
|
|
266
|
+
queryParams.cursor = params.cursor;
|
|
267
|
+
const data = await this.callApi("GetHistoricalOrders", queryParams);
|
|
268
|
+
return (data.orders || []).map((o) => this.mapKalshiOrder(o));
|
|
269
|
+
}
|
|
270
|
+
async fetchAllOrders(params) {
|
|
271
|
+
const queryParams = {};
|
|
272
|
+
if (params?.marketId)
|
|
273
|
+
queryParams.ticker = params.marketId;
|
|
274
|
+
if (params?.since)
|
|
275
|
+
queryParams.min_ts = Math.floor(params.since.getTime() / 1000);
|
|
276
|
+
if (params?.until)
|
|
277
|
+
queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
|
|
278
|
+
if (params?.limit)
|
|
279
|
+
queryParams.limit = params.limit;
|
|
280
|
+
const historicalParams = { ...queryParams };
|
|
281
|
+
delete historicalParams.min_ts; // GetHistoricalOrders only supports max_ts
|
|
282
|
+
const [liveData, historicalData] = await Promise.all([
|
|
283
|
+
this.callApi("GetOrders", queryParams),
|
|
284
|
+
this.callApi("GetHistoricalOrders", historicalParams),
|
|
285
|
+
]);
|
|
286
|
+
const seen = new Set();
|
|
287
|
+
const all = [];
|
|
288
|
+
for (const o of [
|
|
289
|
+
...(liveData.orders || []),
|
|
290
|
+
...(historicalData.orders || []),
|
|
291
|
+
]) {
|
|
292
|
+
if (!seen.has(o.order_id)) {
|
|
293
|
+
seen.add(o.order_id);
|
|
294
|
+
all.push(this.mapKalshiOrder(o));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return all.sort((a, b) => b.timestamp - a.timestamp);
|
|
298
|
+
}
|
|
244
299
|
async fetchPositions() {
|
|
245
|
-
const data = await this.callApi(
|
|
300
|
+
const data = await this.callApi("GetPositions");
|
|
246
301
|
const positions = data.market_positions || [];
|
|
247
302
|
return positions.map((pos) => {
|
|
248
303
|
const absPosition = Math.abs(pos.position);
|
|
@@ -259,19 +314,35 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
259
314
|
};
|
|
260
315
|
});
|
|
261
316
|
}
|
|
317
|
+
// Helper to map a raw Kalshi order object to a unified Order
|
|
318
|
+
mapKalshiOrder(order) {
|
|
319
|
+
return {
|
|
320
|
+
id: order.order_id,
|
|
321
|
+
marketId: order.ticker,
|
|
322
|
+
outcomeId: order.ticker,
|
|
323
|
+
side: order.side === "yes" ? "buy" : "sell",
|
|
324
|
+
type: order.type === "limit" ? "limit" : "market",
|
|
325
|
+
price: order.yes_price ? order.yes_price / 100 : undefined,
|
|
326
|
+
amount: order.count,
|
|
327
|
+
status: this.mapKalshiOrderStatus(order.status),
|
|
328
|
+
filled: order.count - (order.remaining_count || 0),
|
|
329
|
+
remaining: order.remaining_count || 0,
|
|
330
|
+
timestamp: new Date(order.created_time).getTime(),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
262
333
|
// Helper to map Kalshi order status to unified status
|
|
263
334
|
mapKalshiOrderStatus(status) {
|
|
264
|
-
switch (status.toLowerCase()) {
|
|
265
|
-
case
|
|
266
|
-
return
|
|
267
|
-
case
|
|
268
|
-
case
|
|
269
|
-
return
|
|
270
|
-
case
|
|
271
|
-
case
|
|
272
|
-
return
|
|
335
|
+
switch ((status ?? "").toLowerCase()) {
|
|
336
|
+
case "resting":
|
|
337
|
+
return "open";
|
|
338
|
+
case "canceled":
|
|
339
|
+
case "cancelled":
|
|
340
|
+
return "cancelled";
|
|
341
|
+
case "executed":
|
|
342
|
+
case "filled":
|
|
343
|
+
return "filled";
|
|
273
344
|
default:
|
|
274
|
-
return
|
|
345
|
+
return "open";
|
|
275
346
|
}
|
|
276
347
|
}
|
|
277
348
|
// ----------------------------------------------------------------------------
|
|
@@ -281,19 +352,29 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
281
352
|
async watchOrderBook(id, limit) {
|
|
282
353
|
const auth = this.ensureAuth();
|
|
283
354
|
if (!this.ws) {
|
|
284
|
-
|
|
355
|
+
// Merge wsConfig with wsUrl from config
|
|
356
|
+
const wsConfigWithUrl = {
|
|
357
|
+
...this.wsConfig,
|
|
358
|
+
wsUrl: this.wsConfig?.wsUrl || this.config.wsUrl,
|
|
359
|
+
};
|
|
360
|
+
this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
|
|
285
361
|
}
|
|
286
362
|
// Normalize ticker (strip -NO suffix if present)
|
|
287
|
-
const marketTicker = id.replace(/-NO$/,
|
|
363
|
+
const marketTicker = id.replace(/-NO$/, "");
|
|
288
364
|
return this.ws.watchOrderBook(marketTicker);
|
|
289
365
|
}
|
|
290
366
|
async watchTrades(id, since, limit) {
|
|
291
367
|
const auth = this.ensureAuth();
|
|
292
368
|
if (!this.ws) {
|
|
293
|
-
|
|
369
|
+
// Merge wsConfig with wsUrl from config
|
|
370
|
+
const wsConfigWithUrl = {
|
|
371
|
+
...this.wsConfig,
|
|
372
|
+
wsUrl: this.wsConfig?.wsUrl || this.config.wsUrl,
|
|
373
|
+
};
|
|
374
|
+
this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
|
|
294
375
|
}
|
|
295
376
|
// Normalize ticker (strip -NO suffix if present)
|
|
296
|
-
const marketTicker = id.replace(/-NO$/,
|
|
377
|
+
const marketTicker = id.replace(/-NO$/, "");
|
|
297
378
|
return this.ws.watchTrades(marketTicker);
|
|
298
379
|
}
|
|
299
380
|
async close() {
|