pmxt-core 2.6.0 → 2.7.0
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 +3 -0
- package/dist/BaseExchange.js +37 -0
- package/dist/exchanges/kalshi/fetchEvents.d.ts +2 -1
- package/dist/exchanges/kalshi/fetchEvents.js +6 -6
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +2 -1
- package/dist/exchanges/kalshi/fetchMarkets.js +19 -21
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +2 -1
- package/dist/exchanges/kalshi/fetchOHLCV.js +2 -2
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -1
- package/dist/exchanges/kalshi/fetchOrderBook.js +2 -2
- package/dist/exchanges/kalshi/fetchTrades.d.ts +2 -1
- package/dist/exchanges/kalshi/fetchTrades.js +2 -2
- package/dist/exchanges/kalshi/index.js +11 -15
- package/dist/exchanges/kalshi/kalshi.test.js +47 -18
- package/dist/exchanges/limitless/fetchEvents.d.ts +2 -1
- package/dist/exchanges/limitless/fetchEvents.js +6 -4
- package/dist/exchanges/limitless/fetchMarkets.d.ts +2 -1
- package/dist/exchanges/limitless/fetchMarkets.js +4 -4
- package/dist/exchanges/limitless/fetchOHLCV.d.ts +2 -1
- package/dist/exchanges/limitless/fetchOHLCV.js +2 -2
- package/dist/exchanges/limitless/fetchOrderBook.d.ts +2 -1
- package/dist/exchanges/limitless/fetchOrderBook.js +2 -2
- package/dist/exchanges/limitless/fetchTrades.d.ts +2 -1
- package/dist/exchanges/limitless/fetchTrades.js +5 -1
- package/dist/exchanges/limitless/index.js +5 -5
- package/dist/exchanges/myriad/fetchEvents.d.ts +2 -1
- package/dist/exchanges/myriad/fetchEvents.js +6 -6
- package/dist/exchanges/myriad/fetchMarkets.d.ts +2 -1
- package/dist/exchanges/myriad/fetchMarkets.js +10 -10
- package/dist/exchanges/myriad/fetchOHLCV.d.ts +2 -1
- package/dist/exchanges/myriad/fetchOHLCV.js +2 -2
- package/dist/exchanges/myriad/fetchOrderBook.d.ts +2 -1
- package/dist/exchanges/myriad/fetchOrderBook.js +2 -2
- package/dist/exchanges/myriad/fetchTrades.d.ts +2 -1
- package/dist/exchanges/myriad/fetchTrades.js +2 -2
- package/dist/exchanges/myriad/index.js +8 -12
- package/dist/exchanges/polymarket/fetchEvents.d.ts +2 -1
- package/dist/exchanges/polymarket/fetchEvents.js +8 -8
- package/dist/exchanges/polymarket/fetchMarkets.d.ts +2 -1
- package/dist/exchanges/polymarket/fetchMarkets.js +17 -17
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +2 -1
- package/dist/exchanges/polymarket/fetchOHLCV.js +2 -2
- package/dist/exchanges/polymarket/fetchOrderBook.d.ts +2 -1
- package/dist/exchanges/polymarket/fetchOrderBook.js +2 -2
- package/dist/exchanges/polymarket/fetchTrades.d.ts +2 -1
- package/dist/exchanges/polymarket/fetchTrades.js +2 -2
- package/dist/exchanges/polymarket/index.js +5 -5
- package/dist/exchanges/polymarket/utils.d.ts +2 -2
- package/dist/exchanges/polymarket/utils.js +7 -42
- package/dist/exchanges/probable/fetchEvents.d.ts +4 -3
- package/dist/exchanges/probable/fetchEvents.js +13 -13
- package/dist/exchanges/probable/fetchMarkets.d.ts +2 -1
- package/dist/exchanges/probable/fetchMarkets.js +16 -16
- package/dist/exchanges/probable/fetchOHLCV.d.ts +2 -1
- package/dist/exchanges/probable/fetchOHLCV.js +2 -2
- package/dist/exchanges/probable/fetchOrderBook.d.ts +2 -1
- package/dist/exchanges/probable/fetchOrderBook.js +2 -2
- package/dist/exchanges/probable/fetchTrades.d.ts +2 -2
- package/dist/exchanges/probable/fetchTrades.js +5 -1
- package/dist/exchanges/probable/index.js +7 -7
- package/dist/server/app.js +9 -0
- package/package.json +4 -4
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UnifiedMarket, UnifiedEvent, PriceCandle, CandleInterval, OrderBook, Trade, Order, Position, Balance, CreateOrderParams } from './types';
|
|
2
2
|
import { ExecutionPriceResult } from './utils/math';
|
|
3
|
+
import { AxiosInstance } from 'axios';
|
|
3
4
|
export interface MarketFilterParams {
|
|
4
5
|
limit?: number;
|
|
5
6
|
offset?: number;
|
|
@@ -120,6 +121,8 @@ export interface ExchangeCredentials {
|
|
|
120
121
|
}
|
|
121
122
|
export declare abstract class PredictionMarketExchange {
|
|
122
123
|
protected credentials?: ExchangeCredentials;
|
|
124
|
+
verbose: boolean;
|
|
125
|
+
http: AxiosInstance;
|
|
123
126
|
readonly has: ExchangeHas;
|
|
124
127
|
constructor(credentials?: ExchangeCredentials);
|
|
125
128
|
abstract get name(): string;
|
package/dist/BaseExchange.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.PredictionMarketExchange = void 0;
|
|
4
7
|
const math_1 = require("./utils/math");
|
|
5
8
|
const errors_1 = require("./errors");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
6
10
|
// ----------------------------------------------------------------------------
|
|
7
11
|
// Base Exchange Class
|
|
8
12
|
// ----------------------------------------------------------------------------
|
|
9
13
|
class PredictionMarketExchange {
|
|
10
14
|
credentials;
|
|
15
|
+
verbose = false;
|
|
16
|
+
http;
|
|
11
17
|
has = {
|
|
12
18
|
fetchMarkets: false,
|
|
13
19
|
fetchEvents: false,
|
|
@@ -25,6 +31,37 @@ class PredictionMarketExchange {
|
|
|
25
31
|
};
|
|
26
32
|
constructor(credentials) {
|
|
27
33
|
this.credentials = credentials;
|
|
34
|
+
this.http = axios_1.default.create();
|
|
35
|
+
// Request Interceptor
|
|
36
|
+
this.http.interceptors.request.use((config) => {
|
|
37
|
+
if (this.verbose) {
|
|
38
|
+
console.log(`\n[pmxt] → ${config.method?.toUpperCase()} ${config.url}`);
|
|
39
|
+
if (config.params)
|
|
40
|
+
console.log('[pmxt] params:', config.params);
|
|
41
|
+
if (config.data)
|
|
42
|
+
console.log('[pmxt] body:', JSON.stringify(config.data, null, 2));
|
|
43
|
+
}
|
|
44
|
+
return config;
|
|
45
|
+
});
|
|
46
|
+
// Response Interceptor
|
|
47
|
+
this.http.interceptors.response.use((response) => {
|
|
48
|
+
if (this.verbose) {
|
|
49
|
+
console.log(`\n[pmxt] ← ${response.status} ${response.statusText} ${response.config.url}`);
|
|
50
|
+
// console.log('[pmxt] response:', JSON.stringify(response.data, null, 2));
|
|
51
|
+
// Commented out full body log to avoid spam, but headers might be useful
|
|
52
|
+
}
|
|
53
|
+
return response;
|
|
54
|
+
}, (error) => {
|
|
55
|
+
if (this.verbose) {
|
|
56
|
+
console.log(`\n[pmxt] ✖ REQUEST FAILED: ${error.config?.url}`);
|
|
57
|
+
console.log('[pmxt] error:', error.message);
|
|
58
|
+
if (error.response) {
|
|
59
|
+
console.log('[pmxt] status:', error.response.status);
|
|
60
|
+
console.log('[pmxt] data:', JSON.stringify(error.response.data, null, 2));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return Promise.reject(error);
|
|
64
|
+
});
|
|
28
65
|
}
|
|
29
66
|
/**
|
|
30
67
|
* Fetch markets with optional filtering, search, or slug lookup.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { EventFetchParams } from '../../BaseExchange';
|
|
2
2
|
import { UnifiedEvent } from '../../types';
|
|
3
|
-
|
|
3
|
+
import { AxiosInstance } from 'axios';
|
|
4
|
+
export declare function fetchEvents(params: EventFetchParams, http?: AxiosInstance): Promise<UnifiedEvent[]>;
|
|
@@ -7,10 +7,10 @@ exports.fetchEvents = fetchEvents;
|
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const utils_1 = require("./utils");
|
|
9
9
|
const errors_1 = require("./errors");
|
|
10
|
-
async function fetchEventByTicker(eventTicker) {
|
|
10
|
+
async function fetchEventByTicker(eventTicker, http) {
|
|
11
11
|
const normalizedTicker = eventTicker.toUpperCase();
|
|
12
12
|
const url = `https://api.elections.kalshi.com/trade-api/v2/events/${normalizedTicker}`;
|
|
13
|
-
const response = await
|
|
13
|
+
const response = await http.get(url, {
|
|
14
14
|
params: { with_nested_markets: true }
|
|
15
15
|
});
|
|
16
16
|
const event = response.data.event;
|
|
@@ -38,15 +38,15 @@ async function fetchEventByTicker(eventTicker) {
|
|
|
38
38
|
};
|
|
39
39
|
return [unifiedEvent];
|
|
40
40
|
}
|
|
41
|
-
async function fetchEvents(params) {
|
|
41
|
+
async function fetchEvents(params, http = axios_1.default) {
|
|
42
42
|
try {
|
|
43
43
|
// Handle eventId lookup (direct API call)
|
|
44
44
|
if (params.eventId) {
|
|
45
|
-
return await fetchEventByTicker(params.eventId);
|
|
45
|
+
return await fetchEventByTicker(params.eventId, http);
|
|
46
46
|
}
|
|
47
47
|
// Handle slug lookup (slug IS the event ticker on Kalshi)
|
|
48
48
|
if (params.slug) {
|
|
49
|
-
return await fetchEventByTicker(params.slug);
|
|
49
|
+
return await fetchEventByTicker(params.slug, http);
|
|
50
50
|
}
|
|
51
51
|
const status = params?.status || 'active';
|
|
52
52
|
const limit = params?.limit || 10000;
|
|
@@ -65,7 +65,7 @@ async function fetchEvents(params) {
|
|
|
65
65
|
};
|
|
66
66
|
if (cursor)
|
|
67
67
|
queryParams.cursor = cursor;
|
|
68
|
-
const response = await
|
|
68
|
+
const response = await http.get(utils_1.KALSHI_API_URL, { params: queryParams });
|
|
69
69
|
const events = response.data.events || [];
|
|
70
70
|
if (events.length === 0)
|
|
71
71
|
break;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
1
2
|
import { MarketFetchParams } from '../../BaseExchange';
|
|
2
3
|
import { UnifiedMarket } from '../../types';
|
|
3
4
|
export declare function resetCache(): void;
|
|
4
|
-
export declare function fetchMarkets(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
|
|
5
|
+
export declare function fetchMarkets(params?: MarketFetchParams, http?: AxiosInstance): Promise<UnifiedMarket[]>;
|
|
@@ -8,7 +8,7 @@ exports.fetchMarkets = fetchMarkets;
|
|
|
8
8
|
const axios_1 = __importDefault(require("axios"));
|
|
9
9
|
const utils_1 = require("./utils");
|
|
10
10
|
const errors_1 = require("./errors");
|
|
11
|
-
async function fetchActiveEvents(targetMarketCount, status = 'open') {
|
|
11
|
+
async function fetchActiveEvents(http, targetMarketCount, status = 'open') {
|
|
12
12
|
let allEvents = [];
|
|
13
13
|
let totalMarketCount = 0;
|
|
14
14
|
let cursor = null;
|
|
@@ -27,7 +27,7 @@ async function fetchActiveEvents(targetMarketCount, status = 'open') {
|
|
|
27
27
|
};
|
|
28
28
|
if (cursor)
|
|
29
29
|
queryParams.cursor = cursor;
|
|
30
|
-
const response = await
|
|
30
|
+
const response = await http.get(utils_1.KALSHI_API_URL, { params: queryParams });
|
|
31
31
|
const events = response.data.events || [];
|
|
32
32
|
if (events.length === 0)
|
|
33
33
|
break;
|
|
@@ -56,9 +56,9 @@ async function fetchActiveEvents(targetMarketCount, status = 'open') {
|
|
|
56
56
|
} while (cursor && page < MAX_PAGES);
|
|
57
57
|
return allEvents;
|
|
58
58
|
}
|
|
59
|
-
async function fetchSeriesMap() {
|
|
59
|
+
async function fetchSeriesMap(http) {
|
|
60
60
|
try {
|
|
61
|
-
const response = await
|
|
61
|
+
const response = await http.get(utils_1.KALSHI_SERIES_URL);
|
|
62
62
|
const seriesList = response.data.series || [];
|
|
63
63
|
const map = new Map();
|
|
64
64
|
for (const series of seriesList) {
|
|
@@ -83,41 +83,41 @@ function resetCache() {
|
|
|
83
83
|
cachedSeriesMap = null;
|
|
84
84
|
lastCacheTime = 0;
|
|
85
85
|
}
|
|
86
|
-
async function fetchMarkets(params) {
|
|
86
|
+
async function fetchMarkets(params, http = axios_1.default) {
|
|
87
87
|
try {
|
|
88
88
|
// Handle marketId lookup (Kalshi marketId is the ticker)
|
|
89
89
|
if (params?.marketId) {
|
|
90
|
-
return await fetchMarketsBySlug(params.marketId);
|
|
90
|
+
return await fetchMarketsBySlug(params.marketId, http);
|
|
91
91
|
}
|
|
92
92
|
// Handle slug-based lookup (event ticker)
|
|
93
93
|
if (params?.slug) {
|
|
94
|
-
return await fetchMarketsBySlug(params.slug);
|
|
94
|
+
return await fetchMarketsBySlug(params.slug, http);
|
|
95
95
|
}
|
|
96
96
|
// Handle outcomeId lookup (strip -NO suffix, use as ticker)
|
|
97
97
|
if (params?.outcomeId) {
|
|
98
98
|
const ticker = params.outcomeId.replace(/-NO$/, '');
|
|
99
|
-
return await fetchMarketsBySlug(ticker);
|
|
99
|
+
return await fetchMarketsBySlug(ticker, http);
|
|
100
100
|
}
|
|
101
101
|
// Handle eventId lookup (event ticker works the same way)
|
|
102
102
|
if (params?.eventId) {
|
|
103
|
-
return await fetchMarketsBySlug(params.eventId);
|
|
103
|
+
return await fetchMarketsBySlug(params.eventId, http);
|
|
104
104
|
}
|
|
105
105
|
// Handle query-based search
|
|
106
106
|
if (params?.query) {
|
|
107
|
-
return await searchMarkets(params.query, params);
|
|
107
|
+
return await searchMarkets(params.query, params, http);
|
|
108
108
|
}
|
|
109
109
|
// Default: fetch markets
|
|
110
|
-
return await fetchMarketsDefault(params);
|
|
110
|
+
return await fetchMarketsDefault(params, http);
|
|
111
111
|
}
|
|
112
112
|
catch (error) {
|
|
113
113
|
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
async function fetchMarketsBySlug(eventTicker) {
|
|
116
|
+
async function fetchMarketsBySlug(eventTicker, http) {
|
|
117
117
|
// Kalshi API expects uppercase tickers, but URLs use lowercase
|
|
118
118
|
const normalizedTicker = eventTicker.toUpperCase();
|
|
119
119
|
const url = `https://api.elections.kalshi.com/trade-api/v2/events/${normalizedTicker}`;
|
|
120
|
-
const response = await
|
|
120
|
+
const response = await http.get(url, {
|
|
121
121
|
params: { with_nested_markets: true }
|
|
122
122
|
});
|
|
123
123
|
const event = response.data.event;
|
|
@@ -127,7 +127,7 @@ async function fetchMarketsBySlug(eventTicker) {
|
|
|
127
127
|
if (event.series_ticker) {
|
|
128
128
|
try {
|
|
129
129
|
const seriesUrl = `${utils_1.KALSHI_SERIES_URL}/${event.series_ticker}`;
|
|
130
|
-
const seriesResponse = await
|
|
130
|
+
const seriesResponse = await http.get(seriesUrl);
|
|
131
131
|
const series = seriesResponse.data.series;
|
|
132
132
|
if (series && series.tags && series.tags.length > 0) {
|
|
133
133
|
if (!event.tags || event.tags.length === 0) {
|
|
@@ -149,10 +149,10 @@ async function fetchMarketsBySlug(eventTicker) {
|
|
|
149
149
|
}
|
|
150
150
|
return unifiedMarkets;
|
|
151
151
|
}
|
|
152
|
-
async function searchMarkets(query, params) {
|
|
152
|
+
async function searchMarkets(query, params, http) {
|
|
153
153
|
// We must fetch ALL markets to search them locally since we don't have server-side search
|
|
154
154
|
const searchLimit = 10000;
|
|
155
|
-
const markets = await fetchMarketsDefault({ ...params, limit: searchLimit });
|
|
155
|
+
const markets = await fetchMarketsDefault({ ...params, limit: searchLimit }, http);
|
|
156
156
|
const lowerQuery = query.toLowerCase();
|
|
157
157
|
const searchIn = params?.searchIn || 'title'; // Default to title-only search
|
|
158
158
|
const filtered = markets.filter(market => {
|
|
@@ -167,7 +167,7 @@ async function searchMarkets(query, params) {
|
|
|
167
167
|
const limit = params?.limit || 10000;
|
|
168
168
|
return filtered.slice(0, limit);
|
|
169
169
|
}
|
|
170
|
-
async function fetchMarketsDefault(params) {
|
|
170
|
+
async function fetchMarketsDefault(params, http) {
|
|
171
171
|
const limit = params?.limit || 10000;
|
|
172
172
|
const offset = params?.offset || 0;
|
|
173
173
|
const now = Date.now();
|
|
@@ -196,13 +196,11 @@ async function fetchMarketsDefault(params) {
|
|
|
196
196
|
const isSorted = params?.sort && (params.sort === 'volume' || params.sort === 'liquidity');
|
|
197
197
|
const fetchLimit = isSorted ? 1000 : limit;
|
|
198
198
|
const [allEvents, fetchedSeriesMap] = await Promise.all([
|
|
199
|
-
fetchActiveEvents(fetchLimit, apiStatus),
|
|
200
|
-
fetchSeriesMap()
|
|
199
|
+
fetchActiveEvents(http, fetchLimit, apiStatus),
|
|
200
|
+
fetchSeriesMap(http)
|
|
201
201
|
]);
|
|
202
202
|
events = allEvents;
|
|
203
203
|
seriesMap = fetchedSeriesMap;
|
|
204
|
-
events = allEvents;
|
|
205
|
-
seriesMap = fetchedSeriesMap;
|
|
206
204
|
// Cache the dataset ONLY if:
|
|
207
205
|
// 1. We fetched a comprehensive set (>= 1000)
|
|
208
206
|
// 2. It's the standard 'open' status query
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
1
2
|
import { HistoryFilterParams, OHLCVParams } from '../../BaseExchange';
|
|
2
3
|
import { PriceCandle } from '../../types';
|
|
3
|
-
export declare function fetchOHLCV(id: string, params: OHLCVParams | HistoryFilterParams): Promise<PriceCandle[]>;
|
|
4
|
+
export declare function fetchOHLCV(id: string, params: OHLCVParams | HistoryFilterParams, http?: AxiosInstance): Promise<PriceCandle[]>;
|
|
@@ -8,7 +8,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
8
8
|
const utils_1 = require("./utils");
|
|
9
9
|
const validation_1 = require("../../utils/validation");
|
|
10
10
|
const errors_1 = require("./errors");
|
|
11
|
-
async function fetchOHLCV(id, params) {
|
|
11
|
+
async function fetchOHLCV(id, params, http = axios_1.default) {
|
|
12
12
|
(0, validation_1.validateIdFormat)(id, 'OHLCV');
|
|
13
13
|
// Validate resolution is provided
|
|
14
14
|
if (!params.resolution) {
|
|
@@ -57,7 +57,7 @@ async function fetchOHLCV(id, params) {
|
|
|
57
57
|
}
|
|
58
58
|
queryParams.start_ts = startTs;
|
|
59
59
|
queryParams.end_ts = endTs;
|
|
60
|
-
const response = await
|
|
60
|
+
const response = await http.get(url, { params: queryParams });
|
|
61
61
|
const candles = response.data.candlesticks || [];
|
|
62
62
|
const mappedCandles = candles.map((c) => {
|
|
63
63
|
// Priority:
|
|
@@ -7,14 +7,14 @@ exports.fetchOrderBook = fetchOrderBook;
|
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const validation_1 = require("../../utils/validation");
|
|
9
9
|
const errors_1 = require("./errors");
|
|
10
|
-
async function fetchOrderBook(id) {
|
|
10
|
+
async function fetchOrderBook(id, http = axios_1.default) {
|
|
11
11
|
(0, validation_1.validateIdFormat)(id, 'OrderBook');
|
|
12
12
|
try {
|
|
13
13
|
// Check if this is a NO outcome request
|
|
14
14
|
const isNoOutcome = id.endsWith('-NO');
|
|
15
15
|
const ticker = id.replace(/-NO$/, '');
|
|
16
16
|
const url = `https://api.elections.kalshi.com/trade-api/v2/markets/${ticker}/orderbook`;
|
|
17
|
-
const response = await
|
|
17
|
+
const response = await http.get(url);
|
|
18
18
|
const data = response.data.orderbook;
|
|
19
19
|
// Structure: { yes: [[price, qty], ...], no: [[price, qty], ...] }
|
|
20
20
|
// Kalshi returns bids at their actual prices (not inverted)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
1
2
|
import { HistoryFilterParams, TradesParams } from '../../BaseExchange';
|
|
2
3
|
import { Trade } from '../../types';
|
|
3
|
-
export declare function fetchTrades(id: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
|
|
4
|
+
export declare function fetchTrades(id: string, params: TradesParams | HistoryFilterParams, http?: AxiosInstance): Promise<Trade[]>;
|
|
@@ -6,11 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.fetchTrades = fetchTrades;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const errors_1 = require("./errors");
|
|
9
|
-
async function fetchTrades(id, params) {
|
|
9
|
+
async function fetchTrades(id, params, http = axios_1.default) {
|
|
10
10
|
try {
|
|
11
11
|
const ticker = id.replace(/-NO$/, '');
|
|
12
12
|
const url = `https://api.elections.kalshi.com/trade-api/v2/markets/trades`;
|
|
13
|
-
const response = await
|
|
13
|
+
const response = await http.get(url, {
|
|
14
14
|
params: {
|
|
15
15
|
ticker: ticker,
|
|
16
16
|
limit: params.limit || 100
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.KalshiExchange = void 0;
|
|
7
|
-
const axios_1 = __importDefault(require("axios"));
|
|
8
4
|
const BaseExchange_1 = require("../../BaseExchange");
|
|
9
5
|
const fetchMarkets_1 = require("./fetchMarkets");
|
|
10
6
|
const fetchEvents_1 = require("./fetchEvents");
|
|
@@ -72,16 +68,16 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
72
68
|
// Market Data Methods - Implementation for CCXT-style API
|
|
73
69
|
// ----------------------------------------------------------------------------
|
|
74
70
|
async fetchMarketsImpl(params) {
|
|
75
|
-
return (0, fetchMarkets_1.fetchMarkets)(params);
|
|
71
|
+
return (0, fetchMarkets_1.fetchMarkets)(params, this.http);
|
|
76
72
|
}
|
|
77
73
|
async fetchEventsImpl(params) {
|
|
78
|
-
return (0, fetchEvents_1.fetchEvents)(params);
|
|
74
|
+
return (0, fetchEvents_1.fetchEvents)(params, this.http);
|
|
79
75
|
}
|
|
80
76
|
async fetchOHLCV(id, params) {
|
|
81
|
-
return (0, fetchOHLCV_1.fetchOHLCV)(id, params);
|
|
77
|
+
return (0, fetchOHLCV_1.fetchOHLCV)(id, params, this.http);
|
|
82
78
|
}
|
|
83
79
|
async fetchOrderBook(id) {
|
|
84
|
-
return (0, fetchOrderBook_1.fetchOrderBook)(id);
|
|
80
|
+
return (0, fetchOrderBook_1.fetchOrderBook)(id, this.http);
|
|
85
81
|
}
|
|
86
82
|
async fetchTrades(id, params) {
|
|
87
83
|
// Deprecation warning
|
|
@@ -89,7 +85,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
89
85
|
console.warn('[pmxt] Warning: The "resolution" parameter is deprecated for fetchTrades() and will be ignored. ' +
|
|
90
86
|
'It will be removed in v3.0.0. Please remove it from your code.');
|
|
91
87
|
}
|
|
92
|
-
return (0, fetchTrades_1.fetchTrades)(id, params);
|
|
88
|
+
return (0, fetchTrades_1.fetchTrades)(id, params, this.http);
|
|
93
89
|
}
|
|
94
90
|
// ----------------------------------------------------------------------------
|
|
95
91
|
// User Data Methods
|
|
@@ -103,7 +99,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
103
99
|
// TODO: Make base URL configurable in credentials
|
|
104
100
|
const baseUrl = this.getBaseUrl();
|
|
105
101
|
const headers = auth.getHeaders('GET', path);
|
|
106
|
-
const response = await
|
|
102
|
+
const response = await this.http.get(`${baseUrl}${path}`, { headers });
|
|
107
103
|
// Kalshi response structure:
|
|
108
104
|
// - balance: Available balance in cents (for trading)
|
|
109
105
|
// - portfolio_value: Total portfolio value in cents (includes positions)
|
|
@@ -154,7 +150,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
154
150
|
kalshiOrder.no_price = priceInCents;
|
|
155
151
|
}
|
|
156
152
|
}
|
|
157
|
-
const response = await
|
|
153
|
+
const response = await this.http.post(`${baseUrl}${path}`, kalshiOrder, { headers });
|
|
158
154
|
const order = response.data.order;
|
|
159
155
|
return {
|
|
160
156
|
id: order.order_id,
|
|
@@ -180,7 +176,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
180
176
|
const path = `/trade-api/v2/portfolio/orders/${orderId}`;
|
|
181
177
|
const baseUrl = this.getBaseUrl();
|
|
182
178
|
const headers = auth.getHeaders('DELETE', path);
|
|
183
|
-
const response = await
|
|
179
|
+
const response = await this.http.delete(`${baseUrl}${path}`, { headers });
|
|
184
180
|
const order = response.data.order;
|
|
185
181
|
return {
|
|
186
182
|
id: order.order_id,
|
|
@@ -205,7 +201,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
205
201
|
const path = `/trade-api/v2/portfolio/orders/${orderId}`;
|
|
206
202
|
const baseUrl = this.getBaseUrl();
|
|
207
203
|
const headers = auth.getHeaders('GET', path);
|
|
208
|
-
const response = await
|
|
204
|
+
const response = await this.http.get(`${baseUrl}${path}`, { headers });
|
|
209
205
|
const order = response.data.order;
|
|
210
206
|
return {
|
|
211
207
|
id: order.order_id,
|
|
@@ -237,7 +233,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
237
233
|
const baseUrl = this.getBaseUrl();
|
|
238
234
|
// Sign only the base path, not the query parameters
|
|
239
235
|
const headers = auth.getHeaders('GET', basePath);
|
|
240
|
-
const response = await
|
|
236
|
+
const response = await this.http.get(`${baseUrl}${basePath}${queryParams}`, { headers });
|
|
241
237
|
const orders = response.data.orders || [];
|
|
242
238
|
return orders.map((order) => ({
|
|
243
239
|
id: order.order_id,
|
|
@@ -263,7 +259,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
263
259
|
const path = '/trade-api/v2/portfolio/positions';
|
|
264
260
|
const baseUrl = this.getBaseUrl();
|
|
265
261
|
const headers = auth.getHeaders('GET', path);
|
|
266
|
-
const response = await
|
|
262
|
+
const response = await this.http.get(`${baseUrl}${path}`, { headers });
|
|
267
263
|
const positions = response.data.market_positions || [];
|
|
268
264
|
return positions.map((pos) => {
|
|
269
265
|
const absPosition = Math.abs(pos.position);
|
|
@@ -6,8 +6,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const kalshi_1 = require("../kalshi");
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const auth_1 = require("./auth");
|
|
9
|
-
//
|
|
10
|
-
|
|
9
|
+
// Jest hoisting means we can't use outer variables in jest.mock factory
|
|
10
|
+
// unless they start with 'mock'. However, let's just define it inline to be safe and simple.
|
|
11
|
+
// To access the inner methods, we'll grab the instance returned by axios.create().
|
|
12
|
+
jest.mock('axios', () => {
|
|
13
|
+
const mockInstance = {
|
|
14
|
+
interceptors: {
|
|
15
|
+
request: { use: jest.fn() },
|
|
16
|
+
response: { use: jest.fn() },
|
|
17
|
+
},
|
|
18
|
+
get: jest.fn(),
|
|
19
|
+
post: jest.fn(),
|
|
20
|
+
delete: jest.fn(),
|
|
21
|
+
defaults: { headers: { common: {} } },
|
|
22
|
+
};
|
|
23
|
+
const mockAxios = {
|
|
24
|
+
create: jest.fn(() => mockInstance),
|
|
25
|
+
};
|
|
26
|
+
// Support both default and named exports
|
|
27
|
+
return {
|
|
28
|
+
__esModule: true,
|
|
29
|
+
...mockAxios,
|
|
30
|
+
default: mockAxios,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
// Access the mocked instance for assertions
|
|
34
|
+
// Since our factory returns the same object reference, this works.
|
|
35
|
+
const mockAxiosInstance = axios_1.default.create();
|
|
11
36
|
const mockedAxios = axios_1.default;
|
|
12
37
|
// Mock KalshiAuth
|
|
13
38
|
jest.mock('./auth');
|
|
@@ -20,6 +45,10 @@ describe('KalshiExchange', () => {
|
|
|
20
45
|
};
|
|
21
46
|
beforeEach(() => {
|
|
22
47
|
jest.clearAllMocks();
|
|
48
|
+
// Reset the mock instance methods to ensure clean state
|
|
49
|
+
mockAxiosInstance.get.mockReset();
|
|
50
|
+
mockAxiosInstance.post.mockReset();
|
|
51
|
+
mockAxiosInstance.delete.mockReset();
|
|
23
52
|
// Mock the getHeaders method
|
|
24
53
|
MockedKalshiAuth.prototype.getHeaders = jest.fn().mockReturnValue({
|
|
25
54
|
'KALSHI-ACCESS-KEY': 'test-api-key',
|
|
@@ -56,7 +85,7 @@ describe('KalshiExchange', () => {
|
|
|
56
85
|
]
|
|
57
86
|
}
|
|
58
87
|
};
|
|
59
|
-
|
|
88
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
60
89
|
const markets = await exchange.fetchMarkets();
|
|
61
90
|
expect(markets).toBeDefined();
|
|
62
91
|
});
|
|
@@ -88,9 +117,9 @@ describe('KalshiExchange', () => {
|
|
|
88
117
|
}
|
|
89
118
|
}
|
|
90
119
|
};
|
|
91
|
-
|
|
120
|
+
mockAxiosInstance.post.mockResolvedValue(mockResponse);
|
|
92
121
|
const order = await exchange.createOrder(orderParams);
|
|
93
|
-
expect(
|
|
122
|
+
expect(mockAxiosInstance.post).toHaveBeenCalledWith('https://api.elections.kalshi.com/trade-api/v2/portfolio/orders', expect.objectContaining({
|
|
94
123
|
ticker: 'TEST-MARKET',
|
|
95
124
|
side: 'yes',
|
|
96
125
|
action: 'buy',
|
|
@@ -123,9 +152,9 @@ describe('KalshiExchange', () => {
|
|
|
123
152
|
}
|
|
124
153
|
}
|
|
125
154
|
};
|
|
126
|
-
|
|
155
|
+
mockAxiosInstance.post.mockResolvedValue(mockResponse);
|
|
127
156
|
await exchange.createOrder(orderParams);
|
|
128
|
-
expect(
|
|
157
|
+
expect(mockAxiosInstance.post).toHaveBeenCalledWith('https://api.elections.kalshi.com/trade-api/v2/portfolio/orders', expect.objectContaining({
|
|
129
158
|
ticker: 'TEST-MARKET',
|
|
130
159
|
side: 'no',
|
|
131
160
|
action: 'sell',
|
|
@@ -153,18 +182,18 @@ describe('KalshiExchange', () => {
|
|
|
153
182
|
]
|
|
154
183
|
}
|
|
155
184
|
};
|
|
156
|
-
|
|
185
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
157
186
|
await exchange.fetchOpenOrders();
|
|
158
187
|
// Verify the request URL includes query params
|
|
159
|
-
expect(
|
|
188
|
+
expect(mockAxiosInstance.get).toHaveBeenCalledWith('https://api.elections.kalshi.com/trade-api/v2/portfolio/orders?status=resting', expect.any(Object));
|
|
160
189
|
// Verify getHeaders was called with base path only (no query params)
|
|
161
190
|
expect(MockedKalshiAuth.prototype.getHeaders).toHaveBeenCalledWith('GET', '/trade-api/v2/portfolio/orders');
|
|
162
191
|
});
|
|
163
192
|
it('should include ticker in query params when marketId provided', async () => {
|
|
164
193
|
const mockResponse = { data: { orders: [] } };
|
|
165
|
-
|
|
194
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
166
195
|
await exchange.fetchOpenOrders('TEST-MARKET');
|
|
167
|
-
expect(
|
|
196
|
+
expect(mockAxiosInstance.get).toHaveBeenCalledWith('https://api.elections.kalshi.com/trade-api/v2/portfolio/orders?status=resting&ticker=TEST-MARKET', expect.any(Object));
|
|
168
197
|
});
|
|
169
198
|
});
|
|
170
199
|
describe('fetchPositions', () => {
|
|
@@ -182,7 +211,7 @@ describe('KalshiExchange', () => {
|
|
|
182
211
|
]
|
|
183
212
|
}
|
|
184
213
|
};
|
|
185
|
-
|
|
214
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
186
215
|
const positions = await exchange.fetchPositions();
|
|
187
216
|
expect(positions).toHaveLength(1);
|
|
188
217
|
expect(positions[0].size).toBe(0);
|
|
@@ -202,7 +231,7 @@ describe('KalshiExchange', () => {
|
|
|
202
231
|
]
|
|
203
232
|
}
|
|
204
233
|
};
|
|
205
|
-
|
|
234
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
206
235
|
const positions = await exchange.fetchPositions();
|
|
207
236
|
expect(positions).toHaveLength(1);
|
|
208
237
|
expect(positions[0].size).toBe(10);
|
|
@@ -224,7 +253,7 @@ describe('KalshiExchange', () => {
|
|
|
224
253
|
]
|
|
225
254
|
}
|
|
226
255
|
};
|
|
227
|
-
|
|
256
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
228
257
|
const positions = await exchange.fetchPositions();
|
|
229
258
|
expect(positions[0].size).toBe(-5); // Negative for short
|
|
230
259
|
expect(Math.abs(positions[0].size)).toBe(5); // Absolute value
|
|
@@ -238,7 +267,7 @@ describe('KalshiExchange', () => {
|
|
|
238
267
|
portfolio_value: 15000 // $150.00 total
|
|
239
268
|
}
|
|
240
269
|
};
|
|
241
|
-
|
|
270
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
242
271
|
const balances = await exchange.fetchBalance();
|
|
243
272
|
expect(balances).toHaveLength(1);
|
|
244
273
|
expect(balances[0].currency).toBe('USD');
|
|
@@ -261,7 +290,7 @@ describe('KalshiExchange', () => {
|
|
|
261
290
|
}
|
|
262
291
|
}
|
|
263
292
|
};
|
|
264
|
-
|
|
293
|
+
mockAxiosInstance.delete.mockResolvedValue(mockResponse);
|
|
265
294
|
const order = await exchange.cancelOrder('order-123');
|
|
266
295
|
expect(order.status).toBe('cancelled');
|
|
267
296
|
expect(order.filled).toBe(5); // count - remaining_count
|
|
@@ -285,7 +314,7 @@ describe('KalshiExchange', () => {
|
|
|
285
314
|
}
|
|
286
315
|
}
|
|
287
316
|
};
|
|
288
|
-
|
|
317
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
289
318
|
const order = await exchange.fetchOrder('order-123');
|
|
290
319
|
expect(order.status).toBe('open');
|
|
291
320
|
});
|
|
@@ -301,7 +330,7 @@ describe('KalshiExchange', () => {
|
|
|
301
330
|
}
|
|
302
331
|
}
|
|
303
332
|
};
|
|
304
|
-
|
|
333
|
+
mockAxiosInstance.get.mockResolvedValue(mockResponse);
|
|
305
334
|
const order = await exchange.fetchOrder('order-123');
|
|
306
335
|
expect(order.status).toBe('filled');
|
|
307
336
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { EventFetchParams } from '../../BaseExchange';
|
|
2
2
|
import { UnifiedEvent } from '../../types';
|
|
3
|
-
|
|
3
|
+
import { AxiosInstance } from 'axios';
|
|
4
|
+
export declare function fetchEvents(params: EventFetchParams, http?: AxiosInstance): Promise<UnifiedEvent[]>;
|
|
@@ -40,9 +40,11 @@ exports.fetchEvents = fetchEvents;
|
|
|
40
40
|
const axios_1 = __importDefault(require("axios"));
|
|
41
41
|
const utils_1 = require("./utils");
|
|
42
42
|
const errors_1 = require("./errors");
|
|
43
|
-
async function fetchEventBySlug(slug) {
|
|
43
|
+
async function fetchEventBySlug(slug, http) {
|
|
44
44
|
const { HttpClient, MarketFetcher } = await Promise.resolve().then(() => __importStar(require('@limitless-exchange/sdk')));
|
|
45
45
|
const httpClient = new HttpClient({ baseURL: utils_1.LIMITLESS_API_URL });
|
|
46
|
+
// TODO: Ideally inject 'http' into HttpClient if supported, but SDK abstracts it.
|
|
47
|
+
// For now, single market fetch uses SDK's internal client.
|
|
46
48
|
const marketFetcher = new MarketFetcher(httpClient);
|
|
47
49
|
const market = await marketFetcher.getMarket(slug);
|
|
48
50
|
if (!market)
|
|
@@ -70,18 +72,18 @@ async function fetchEventBySlug(slug) {
|
|
|
70
72
|
tags: market.tags || []
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
|
-
async function fetchEvents(params) {
|
|
75
|
+
async function fetchEvents(params, http = axios_1.default) {
|
|
74
76
|
try {
|
|
75
77
|
// Handle eventId/slug lookup (same thing for Limitless)
|
|
76
78
|
if (params.eventId || params.slug) {
|
|
77
79
|
const slug = params.eventId || params.slug;
|
|
78
|
-
const event = await fetchEventBySlug(slug);
|
|
80
|
+
const event = await fetchEventBySlug(slug, http);
|
|
79
81
|
return event ? [event] : [];
|
|
80
82
|
}
|
|
81
83
|
// NOTE: The Limitless /markets/search endpoint currently only returns active/funded markets.
|
|
82
84
|
// It does not include expired or resolved markets in search results.
|
|
83
85
|
// Consequently, status 'inactive' will likely return 0 results and 'all' will only show active markets.
|
|
84
|
-
const response = await
|
|
86
|
+
const response = await http.get(`${utils_1.LIMITLESS_API_URL}/markets/search`, {
|
|
85
87
|
params: {
|
|
86
88
|
query: params.query,
|
|
87
89
|
limit: params?.limit || 10000,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { MarketFetchParams } from '../../BaseExchange';
|
|
2
2
|
import { UnifiedMarket } from '../../types';
|
|
3
|
-
|
|
3
|
+
import { AxiosInstance } from 'axios';
|
|
4
|
+
export declare function fetchMarkets(params?: MarketFetchParams, apiKey?: string, http?: AxiosInstance): Promise<UnifiedMarket[]>;
|