pmxt-core 2.44.3 → 2.44.5
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/errors.d.ts +6 -0
- package/dist/errors.js +10 -1
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/mock/index.d.ts +3 -2
- package/dist/exchanges/mock/index.js +14 -5
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -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/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/feeds/binance/binance-feed.d.ts +9 -0
- package/dist/feeds/binance/binance-feed.js +34 -7
- package/dist/feeds/chainlink/chainlink-feed.d.ts +14 -0
- package/dist/feeds/chainlink/chainlink-feed.js +62 -7
- package/dist/feeds/interfaces.d.ts +10 -0
- package/dist/router/Router.d.ts +9 -0
- package/dist/router/Router.js +153 -2
- package/dist/router/types.d.ts +5 -0
- package/dist/server/app.d.ts +26 -2
- package/dist/server/app.js +50 -9
- package/dist/server/feed-routes.js +34 -12
- package/dist/server/sql-route.d.ts +2 -0
- package/dist/server/sql-route.js +277 -0
- package/package.json +3 -3
package/dist/errors.d.ts
CHANGED
|
@@ -84,6 +84,12 @@ export declare class ValidationError extends BaseError {
|
|
|
84
84
|
readonly field?: string;
|
|
85
85
|
constructor(message: string, field?: string, exchange?: string);
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* 501 Not Implemented - The requested operation is not supported
|
|
89
|
+
*/
|
|
90
|
+
export declare class NotSupported extends BaseError {
|
|
91
|
+
constructor(message: string, exchange?: string);
|
|
92
|
+
}
|
|
87
93
|
/**
|
|
88
94
|
* 503 Service Unavailable - Network connectivity issues (retryable)
|
|
89
95
|
*/
|
package/dist/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ExchangeNotAvailable = exports.NetworkError = exports.ValidationError = exports.InsufficientFunds = exports.InvalidOrder = exports.RateLimitExceeded = exports.EventNotFound = exports.MarketNotFound = exports.OrderNotFound = exports.NotFound = exports.PermissionDenied = exports.AuthenticationError = exports.BadRequest = exports.BaseError = void 0;
|
|
3
|
+
exports.ExchangeNotAvailable = exports.NetworkError = exports.NotSupported = exports.ValidationError = exports.InsufficientFunds = exports.InvalidOrder = exports.RateLimitExceeded = exports.EventNotFound = exports.MarketNotFound = exports.OrderNotFound = exports.NotFound = exports.PermissionDenied = exports.AuthenticationError = exports.BadRequest = exports.BaseError = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Base error class for all PMXT errors
|
|
6
6
|
*
|
|
@@ -140,6 +140,15 @@ exports.ValidationError = ValidationError;
|
|
|
140
140
|
// ============================================================================
|
|
141
141
|
// 5xx Server/Network Errors
|
|
142
142
|
// ============================================================================
|
|
143
|
+
/**
|
|
144
|
+
* 501 Not Implemented - The requested operation is not supported
|
|
145
|
+
*/
|
|
146
|
+
class NotSupported extends BaseError {
|
|
147
|
+
constructor(message, exchange) {
|
|
148
|
+
super(message, 501, 'NOT_SUPPORTED', false, exchange);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.NotSupported = NotSupported;
|
|
143
152
|
/**
|
|
144
153
|
* 503 Service Unavailable - Network connectivity issues (retryable)
|
|
145
154
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.681Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const kalshiApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.kalshiApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.681Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.722Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const limitlessApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.limitlessApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.722Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -28,9 +28,10 @@ export declare class MockExchange extends PredictionMarketExchange {
|
|
|
28
28
|
private _buildEvents;
|
|
29
29
|
protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
|
|
30
30
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
31
|
-
fetchOrderBook(id: string,
|
|
31
|
+
fetchOrderBook(id: string, limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
|
|
32
|
+
fetchOrderBooks(outcomeIds: string[]): Promise<Record<string, OrderBook>>;
|
|
32
33
|
fetchOHLCV(id: string, params: OHLCVParams): Promise<PriceCandle[]>;
|
|
33
|
-
fetchTrades(id: string,
|
|
34
|
+
fetchTrades(id: string, params?: TradesParams): Promise<Trade[]>;
|
|
34
35
|
fetchBalance(_address?: string): Promise<Balance[]>;
|
|
35
36
|
fetchPositions(_address?: string): Promise<Position[]>;
|
|
36
37
|
private _nextOrderId;
|
|
@@ -289,12 +289,13 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
289
289
|
const limit = params?.limit;
|
|
290
290
|
return limit !== undefined ? events.slice(offset, offset + limit) : events.slice(offset);
|
|
291
291
|
}
|
|
292
|
-
async fetchOrderBook(id,
|
|
292
|
+
async fetchOrderBook(id, limit, _params) {
|
|
293
293
|
const resolved = await this.resolveOutcomeAlias(id, _params);
|
|
294
294
|
id = resolved.outcomeId;
|
|
295
295
|
const f = new seededRng_1.SeededRng(id);
|
|
296
296
|
const midPrice = round(f.float(0.1, 0.9), 3);
|
|
297
297
|
const spread = round(f.float(0.005, 0.03), 3);
|
|
298
|
+
const depth = limit === undefined ? 8 : Math.max(0, Math.floor(limit));
|
|
298
299
|
const buildLevels = (startPrice, direction, count) => {
|
|
299
300
|
const levels = [];
|
|
300
301
|
let price = startPrice;
|
|
@@ -308,11 +309,18 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
308
309
|
const askStart = clamp(round(midPrice + spread / 2, 3), 0.01, 0.99);
|
|
309
310
|
const bidStart = clamp(round(midPrice - spread / 2, 3), 0.01, 0.99);
|
|
310
311
|
return {
|
|
311
|
-
bids: buildLevels(bidStart, -1,
|
|
312
|
-
asks: buildLevels(askStart, 1,
|
|
312
|
+
bids: buildLevels(bidStart, -1, depth).sort((a, b) => b.price - a.price),
|
|
313
|
+
asks: buildLevels(askStart, 1, depth).sort((a, b) => a.price - b.price),
|
|
313
314
|
timestamp: Date.now(),
|
|
314
315
|
};
|
|
315
316
|
}
|
|
317
|
+
async fetchOrderBooks(outcomeIds) {
|
|
318
|
+
const books = {};
|
|
319
|
+
for (const outcomeId of outcomeIds) {
|
|
320
|
+
books[outcomeId] = await this.fetchOrderBook(outcomeId);
|
|
321
|
+
}
|
|
322
|
+
return books;
|
|
323
|
+
}
|
|
316
324
|
async fetchOHLCV(id, params) {
|
|
317
325
|
const f = new seededRng_1.SeededRng(id);
|
|
318
326
|
const resolutionMs = {
|
|
@@ -343,7 +351,7 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
343
351
|
}
|
|
344
352
|
return candles;
|
|
345
353
|
}
|
|
346
|
-
async fetchTrades(id,
|
|
354
|
+
async fetchTrades(id, params = {}) {
|
|
347
355
|
const f = new seededRng_1.SeededRng(id);
|
|
348
356
|
const count = f.int(5, 30);
|
|
349
357
|
const trades = [];
|
|
@@ -359,7 +367,8 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
359
367
|
outcomeId: id,
|
|
360
368
|
});
|
|
361
369
|
}
|
|
362
|
-
|
|
370
|
+
const sorted = trades.sort((a, b) => b.timestamp - a.timestamp);
|
|
371
|
+
return params.limit === undefined ? sorted : sorted.slice(0, Math.max(0, Math.floor(params.limit)));
|
|
363
372
|
}
|
|
364
373
|
async fetchBalance(_address) {
|
|
365
374
|
const locked = this._locked();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.733Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const myriadApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.myriadApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.733Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.myriadApiSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.737Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const opinionApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.opinionApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.737Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.opinionApiSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.689Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const polymarketClobSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.polymarketClobSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.689Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketClobSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.704Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const polymarketDataSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.polymarketDataSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.704Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketDataSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.701Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const polymarketGammaSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.polymarketGammaSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.701Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketGammaSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-25T10:52:11.728Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const probableApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.probableApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-25T10:52:11.728Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.probableApiSpec = {
|
|
@@ -4,6 +4,14 @@ import { BinanceFeedConfig } from './types';
|
|
|
4
4
|
export declare class BinanceFeed extends BaseDataFeed {
|
|
5
5
|
readonly name = "binance";
|
|
6
6
|
readonly description = "Binance spot trade firehose via obdata relay";
|
|
7
|
+
readonly has: {
|
|
8
|
+
readonly loadMarkets: true;
|
|
9
|
+
readonly fetchTicker: true;
|
|
10
|
+
readonly fetchTickers: true;
|
|
11
|
+
readonly watchTicker: true;
|
|
12
|
+
readonly fetchOHLCV: false;
|
|
13
|
+
readonly fetchOrderBook: false;
|
|
14
|
+
};
|
|
7
15
|
private readonly wsUrl;
|
|
8
16
|
private readonly apiKey;
|
|
9
17
|
private readonly reconnectIntervalMs;
|
|
@@ -26,4 +34,5 @@ export declare class BinanceFeed extends BaseDataFeed {
|
|
|
26
34
|
private establishConnection;
|
|
27
35
|
private handleMessage;
|
|
28
36
|
private scheduleReconnect;
|
|
37
|
+
private validateRelayWsUrl;
|
|
29
38
|
}
|
|
@@ -6,12 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.BinanceFeed = void 0;
|
|
7
7
|
const ws_1 = __importDefault(require("ws"));
|
|
8
8
|
const logger_1 = require("../../utils/logger");
|
|
9
|
+
const errors_1 = require("../../errors");
|
|
9
10
|
const base_feed_1 = require("../base-feed");
|
|
10
11
|
const types_1 = require("./types");
|
|
11
12
|
const normalizer_1 = require("./normalizer");
|
|
12
13
|
class BinanceFeed extends base_feed_1.BaseDataFeed {
|
|
13
14
|
name = 'binance';
|
|
14
15
|
description = 'Binance spot trade firehose via obdata relay';
|
|
16
|
+
has = {
|
|
17
|
+
loadMarkets: true,
|
|
18
|
+
fetchTicker: true,
|
|
19
|
+
fetchTickers: true,
|
|
20
|
+
watchTicker: true,
|
|
21
|
+
fetchOHLCV: false,
|
|
22
|
+
fetchOrderBook: false,
|
|
23
|
+
};
|
|
15
24
|
wsUrl;
|
|
16
25
|
apiKey;
|
|
17
26
|
reconnectIntervalMs;
|
|
@@ -23,7 +32,7 @@ class BinanceFeed extends base_feed_1.BaseDataFeed {
|
|
|
23
32
|
connectionPromise = null;
|
|
24
33
|
constructor(config = {}, options) {
|
|
25
34
|
super(options);
|
|
26
|
-
this.wsUrl = config.wsUrl ?? types_1.BINANCE_RELAY_DEFAULTS.wsUrl;
|
|
35
|
+
this.wsUrl = config.wsUrl ?? process.env.BINANCE_RELAY_WS_URL ?? types_1.BINANCE_RELAY_DEFAULTS.wsUrl;
|
|
27
36
|
this.apiKey = config.apiKey ?? process.env.OBDATA_API_KEY ?? '';
|
|
28
37
|
this.reconnectIntervalMs = config.reconnectIntervalMs ?? types_1.BINANCE_RELAY_DEFAULTS.reconnectIntervalMs;
|
|
29
38
|
}
|
|
@@ -117,10 +126,10 @@ class BinanceFeed extends base_feed_1.BaseDataFeed {
|
|
|
117
126
|
};
|
|
118
127
|
}
|
|
119
128
|
async fetchOHLCVImpl(_symbol, _timeframe, _since, _limit) {
|
|
120
|
-
throw new
|
|
129
|
+
throw new errors_1.NotSupported('BinanceFeed does not support fetchOHLCV via the configured trade relay.', this.name);
|
|
121
130
|
}
|
|
122
131
|
async fetchOrderBookImpl(_symbol, _limit) {
|
|
123
|
-
throw new
|
|
132
|
+
throw new errors_1.NotSupported('BinanceFeed does not support fetchOrderBook via the configured trade relay.', this.name);
|
|
124
133
|
}
|
|
125
134
|
// -- Internal --
|
|
126
135
|
async ensureConnected() {
|
|
@@ -130,10 +139,11 @@ class BinanceFeed extends base_feed_1.BaseDataFeed {
|
|
|
130
139
|
}
|
|
131
140
|
establishConnection() {
|
|
132
141
|
return new Promise((resolve, reject) => {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
const relayUrl = this.validateRelayWsUrl();
|
|
143
|
+
if (this.apiKey) {
|
|
144
|
+
relayUrl.searchParams.set('key', this.apiKey);
|
|
145
|
+
}
|
|
146
|
+
const ws = new ws_1.default(relayUrl.toString());
|
|
137
147
|
const connectionTimeout = setTimeout(() => {
|
|
138
148
|
ws.close();
|
|
139
149
|
this.ws = null;
|
|
@@ -203,5 +213,22 @@ class BinanceFeed extends base_feed_1.BaseDataFeed {
|
|
|
203
213
|
}
|
|
204
214
|
}, this.reconnectIntervalMs);
|
|
205
215
|
}
|
|
216
|
+
validateRelayWsUrl() {
|
|
217
|
+
const rawUrl = this.wsUrl.trim();
|
|
218
|
+
if (!rawUrl) {
|
|
219
|
+
throw new errors_1.ExchangeNotAvailable('BinanceFeed requires BINANCE_RELAY_WS_URL to fetch live ticker data.', this.name);
|
|
220
|
+
}
|
|
221
|
+
let url;
|
|
222
|
+
try {
|
|
223
|
+
url = new URL(rawUrl);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
throw new errors_1.ExchangeNotAvailable('BinanceFeed requires BINANCE_RELAY_WS_URL to be a valid WebSocket URL.', this.name);
|
|
227
|
+
}
|
|
228
|
+
if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
|
|
229
|
+
throw new errors_1.ExchangeNotAvailable('BinanceFeed requires BINANCE_RELAY_WS_URL to use ws:// or wss://.', this.name);
|
|
230
|
+
}
|
|
231
|
+
return url;
|
|
232
|
+
}
|
|
206
233
|
}
|
|
207
234
|
exports.BinanceFeed = BinanceFeed;
|
|
@@ -4,6 +4,18 @@ import { ChainlinkFeedConfig } from './types';
|
|
|
4
4
|
export declare class ChainlinkFeed extends BaseDataFeed {
|
|
5
5
|
readonly name = "chainlink";
|
|
6
6
|
readonly description = "Chainlink price feeds (ETH, BTC, XRP, SOL) on Polygon via pmxt-ohlc";
|
|
7
|
+
readonly has: {
|
|
8
|
+
readonly loadMarkets: true;
|
|
9
|
+
readonly fetchTicker: true;
|
|
10
|
+
readonly fetchTickers: true;
|
|
11
|
+
readonly watchTicker: true;
|
|
12
|
+
readonly fetchOHLCV: false;
|
|
13
|
+
readonly fetchOrderBook: false;
|
|
14
|
+
readonly fetchOracleRound: true;
|
|
15
|
+
readonly fetchOracleHistory: true;
|
|
16
|
+
readonly fetchHistoricalPrices: true;
|
|
17
|
+
};
|
|
18
|
+
private readonly baseUrl;
|
|
7
19
|
private readonly client;
|
|
8
20
|
private readonly wsUrl;
|
|
9
21
|
private readonly wsApiKey;
|
|
@@ -32,7 +44,9 @@ export declare class ChainlinkFeed extends BaseDataFeed {
|
|
|
32
44
|
order?: 'asc' | 'desc';
|
|
33
45
|
}): Promise<Ticker[]>;
|
|
34
46
|
private ensureConnected;
|
|
47
|
+
private ensureRestConfigured;
|
|
35
48
|
private establishConnection;
|
|
49
|
+
private validateWsUrl;
|
|
36
50
|
private handleMessage;
|
|
37
51
|
private scheduleReconnect;
|
|
38
52
|
}
|
|
@@ -8,11 +8,24 @@ const ws_1 = __importDefault(require("ws"));
|
|
|
8
8
|
const axios_1 = __importDefault(require("axios"));
|
|
9
9
|
const base_feed_1 = require("../base-feed");
|
|
10
10
|
const logger_1 = require("../../utils/logger");
|
|
11
|
+
const errors_1 = require("../../errors");
|
|
11
12
|
const types_1 = require("./types");
|
|
12
13
|
const normalizer_1 = require("./normalizer");
|
|
13
14
|
class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
14
15
|
name = 'chainlink';
|
|
15
16
|
description = 'Chainlink price feeds (ETH, BTC, XRP, SOL) on Polygon via pmxt-ohlc';
|
|
17
|
+
has = {
|
|
18
|
+
loadMarkets: true,
|
|
19
|
+
fetchTicker: true,
|
|
20
|
+
fetchTickers: true,
|
|
21
|
+
watchTicker: true,
|
|
22
|
+
fetchOHLCV: false,
|
|
23
|
+
fetchOrderBook: false,
|
|
24
|
+
fetchOracleRound: true,
|
|
25
|
+
fetchOracleHistory: true,
|
|
26
|
+
fetchHistoricalPrices: true,
|
|
27
|
+
};
|
|
28
|
+
baseUrl;
|
|
16
29
|
client;
|
|
17
30
|
wsUrl;
|
|
18
31
|
wsApiKey;
|
|
@@ -25,13 +38,14 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
25
38
|
connectionPromise = null;
|
|
26
39
|
constructor(config, options) {
|
|
27
40
|
super(options);
|
|
28
|
-
const baseURL = config.baseUrl ?? types_1.CHAINLINK_DEFAULTS.baseUrl;
|
|
41
|
+
const baseURL = config.baseUrl ?? process.env.CHAINLINK_API_URL ?? types_1.CHAINLINK_DEFAULTS.baseUrl;
|
|
42
|
+
this.baseUrl = baseURL;
|
|
29
43
|
this.client = axios_1.default.create({
|
|
30
44
|
baseURL,
|
|
31
45
|
headers: { 'X-API-Key': config.apiKey },
|
|
32
46
|
timeout: 10_000,
|
|
33
47
|
});
|
|
34
|
-
this.wsUrl = config.wsUrl ?? types_1.CHAINLINK_DEFAULTS.wsUrl;
|
|
48
|
+
this.wsUrl = config.wsUrl ?? process.env.CHAINLINK_WS_URL ?? types_1.CHAINLINK_DEFAULTS.wsUrl;
|
|
35
49
|
this.wsApiKey = config.wsApiKey ?? config.apiKey;
|
|
36
50
|
this.reconnectIntervalMs = config.reconnectIntervalMs ?? types_1.CHAINLINK_DEFAULTS.reconnectIntervalMs;
|
|
37
51
|
}
|
|
@@ -87,6 +101,7 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
87
101
|
const cached = this.latestTickers.get(symbol.toUpperCase());
|
|
88
102
|
if (cached)
|
|
89
103
|
return cached;
|
|
104
|
+
this.ensureRestConfigured();
|
|
90
105
|
const token = types_1.TOKEN_BY_PAIR.get(symbol.toUpperCase());
|
|
91
106
|
if (!token) {
|
|
92
107
|
throw new Error(`Unsupported Chainlink symbol: ${symbol}. Supported: ${types_1.SUPPORTED_TOKENS.map((t) => t.pair).join(', ')}`);
|
|
@@ -101,6 +116,7 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
101
116
|
}
|
|
102
117
|
// -- CCXT: fetchTickers --
|
|
103
118
|
async fetchTickersImpl(symbols) {
|
|
119
|
+
this.ensureRestConfigured();
|
|
104
120
|
const { data } = await this.client.get('/v1/chainlink/latest-prices');
|
|
105
121
|
const now = Date.now();
|
|
106
122
|
const requested = symbols
|
|
@@ -133,15 +149,16 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
133
149
|
}
|
|
134
150
|
// -- CCXT: fetchOHLCV (not supported) --
|
|
135
151
|
async fetchOHLCVImpl(_symbol, _timeframe, _since, _limit) {
|
|
136
|
-
throw new
|
|
137
|
-
'Use fetchOracleHistory() for raw AnswerUpdated records.');
|
|
152
|
+
throw new errors_1.NotSupported('Chainlink feed does not provide OHLCV candles. ' +
|
|
153
|
+
'Use fetchOracleHistory() for raw AnswerUpdated records.', this.name);
|
|
138
154
|
}
|
|
139
155
|
// -- CCXT: fetchOrderBook (not applicable) --
|
|
140
156
|
async fetchOrderBookImpl(_symbol, _limit) {
|
|
141
|
-
throw new
|
|
157
|
+
throw new errors_1.NotSupported('Chainlink oracle feeds do not have order books.', this.name);
|
|
142
158
|
}
|
|
143
159
|
// -- pmxt extensions: Oracle --
|
|
144
160
|
async fetchOracleRound(params) {
|
|
161
|
+
this.ensureRestConfigured();
|
|
145
162
|
const token = types_1.TOKEN_BY_PAIR.get(params.feed.toUpperCase());
|
|
146
163
|
if (!token) {
|
|
147
164
|
throw new Error(`Unsupported Chainlink feed: ${params.feed}. Supported: ${types_1.SUPPORTED_TOKENS.map((t) => t.pair).join(', ')}`);
|
|
@@ -153,6 +170,7 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
153
170
|
return (0, normalizer_1.normalizePriceRecordToOracleRound)(data.data[0]);
|
|
154
171
|
}
|
|
155
172
|
async fetchOracleHistory(params) {
|
|
173
|
+
this.ensureRestConfigured();
|
|
156
174
|
const token = types_1.TOKEN_BY_PAIR.get(params.feed.toUpperCase());
|
|
157
175
|
if (!token) {
|
|
158
176
|
throw new Error(`Unsupported Chainlink feed: ${params.feed}. Supported: ${types_1.SUPPORTED_TOKENS.map((t) => t.pair).join(', ')}`);
|
|
@@ -161,6 +179,7 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
161
179
|
return data.data.map(normalizer_1.normalizePriceRecordToOracleRound);
|
|
162
180
|
}
|
|
163
181
|
async fetchHistoricalPrices(symbol, opts) {
|
|
182
|
+
this.ensureRestConfigured();
|
|
164
183
|
const token = types_1.TOKEN_BY_PAIR.get(symbol.toUpperCase());
|
|
165
184
|
if (!token) {
|
|
166
185
|
throw new Error(`Unsupported Chainlink symbol: ${symbol}. Supported: ${types_1.SUPPORTED_TOKENS.map((t) => t.pair).join(', ')}`);
|
|
@@ -183,10 +202,29 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
183
202
|
return;
|
|
184
203
|
await this.connect();
|
|
185
204
|
}
|
|
205
|
+
ensureRestConfigured() {
|
|
206
|
+
const rawUrl = this.baseUrl.trim();
|
|
207
|
+
if (!rawUrl) {
|
|
208
|
+
throw new errors_1.ExchangeNotAvailable('ChainlinkFeed requires CHAINLINK_API_URL to fetch oracle data.', this.name);
|
|
209
|
+
}
|
|
210
|
+
let url;
|
|
211
|
+
try {
|
|
212
|
+
url = new URL(rawUrl);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
throw new errors_1.ExchangeNotAvailable('ChainlinkFeed requires CHAINLINK_API_URL to be a valid HTTP URL.', this.name);
|
|
216
|
+
}
|
|
217
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
218
|
+
throw new errors_1.ExchangeNotAvailable('ChainlinkFeed requires CHAINLINK_API_URL to use http:// or https://.', this.name);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
186
221
|
establishConnection() {
|
|
187
222
|
return new Promise((resolve, reject) => {
|
|
188
|
-
const
|
|
189
|
-
|
|
223
|
+
const wsUrl = this.validateWsUrl();
|
|
224
|
+
if (this.wsApiKey) {
|
|
225
|
+
wsUrl.searchParams.set('key', this.wsApiKey);
|
|
226
|
+
}
|
|
227
|
+
const ws = new ws_1.default(wsUrl.toString());
|
|
190
228
|
const connectionTimeout = setTimeout(() => {
|
|
191
229
|
ws.close();
|
|
192
230
|
this.ws = null;
|
|
@@ -220,6 +258,23 @@ class ChainlinkFeed extends base_feed_1.BaseDataFeed {
|
|
|
220
258
|
});
|
|
221
259
|
});
|
|
222
260
|
}
|
|
261
|
+
validateWsUrl() {
|
|
262
|
+
const rawUrl = this.wsUrl.trim();
|
|
263
|
+
if (!rawUrl) {
|
|
264
|
+
throw new errors_1.ExchangeNotAvailable('ChainlinkFeed requires CHAINLINK_WS_URL to stream live oracle data.', this.name);
|
|
265
|
+
}
|
|
266
|
+
let url;
|
|
267
|
+
try {
|
|
268
|
+
url = new URL(rawUrl);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
throw new errors_1.ExchangeNotAvailable('ChainlinkFeed requires CHAINLINK_WS_URL to be a valid WebSocket URL.', this.name);
|
|
272
|
+
}
|
|
273
|
+
if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
|
|
274
|
+
throw new errors_1.ExchangeNotAvailable('ChainlinkFeed requires CHAINLINK_WS_URL to use ws:// or wss://.', this.name);
|
|
275
|
+
}
|
|
276
|
+
return url;
|
|
277
|
+
}
|
|
223
278
|
handleMessage(data) {
|
|
224
279
|
const text = typeof data === 'string' ? data : data.toString();
|
|
225
280
|
let msg;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Ticker, Tickers, OHLCV, OrderBook, Market, FundingRate, FundingRates, OracleRound, OracleParams, Dictionary } from './types';
|
|
2
|
+
export type DataFeedCapability = 'loadMarkets' | 'fetchTicker' | 'fetchTickers' | 'watchTicker' | 'fetchOHLCV' | 'fetchOrderBook' | 'watchOrderBook' | 'fetchFundingRate' | 'fetchFundingRates' | 'fetchOracleRound' | 'fetchOracleHistory' | 'fetchHistoricalPrices';
|
|
3
|
+
export type DataFeedCapabilityValue = true | false | 'emulated';
|
|
4
|
+
export type DataFeedCapabilities = Readonly<Partial<Record<DataFeedCapability, DataFeedCapabilityValue>>>;
|
|
2
5
|
export interface IDataFeed {
|
|
3
6
|
readonly name: string;
|
|
4
7
|
readonly description: string;
|
|
8
|
+
readonly has?: DataFeedCapabilities;
|
|
5
9
|
loadMarkets(reload?: boolean): Promise<Dictionary<Market>>;
|
|
6
10
|
fetchTicker(symbol: string): Promise<Ticker>;
|
|
7
11
|
fetchTickers(symbols?: string[]): Promise<Tickers>;
|
|
@@ -13,6 +17,12 @@ export interface IDataFeed {
|
|
|
13
17
|
fetchFundingRates?(symbols?: string[]): Promise<FundingRates>;
|
|
14
18
|
fetchOracleRound?(params: OracleParams): Promise<OracleRound>;
|
|
15
19
|
fetchOracleHistory?(params: OracleParams): Promise<OracleRound[]>;
|
|
20
|
+
fetchHistoricalPrices?(symbol: string, opts?: {
|
|
21
|
+
fromTimestamp?: number;
|
|
22
|
+
untilTimestamp?: number;
|
|
23
|
+
maxSize?: number;
|
|
24
|
+
order?: 'asc' | 'desc';
|
|
25
|
+
}): Promise<Ticker[]>;
|
|
16
26
|
connect?(): Promise<void>;
|
|
17
27
|
close?(): Promise<void>;
|
|
18
28
|
}
|
package/dist/router/Router.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { RouterOptions, MatchResult, EventMatchResult, PriceComparison, Arb
|
|
|
4
4
|
export declare class Router extends PredictionMarketExchange {
|
|
5
5
|
private readonly client;
|
|
6
6
|
private readonly exchanges;
|
|
7
|
+
private readonly localExchanges;
|
|
7
8
|
constructor(options: RouterOptions);
|
|
8
9
|
get name(): string;
|
|
9
10
|
protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
|
|
@@ -28,6 +29,14 @@ export declare class Router extends PredictionMarketExchange {
|
|
|
28
29
|
fetchMatchedPrices(params?: FetchMatchedPricesParams): Promise<MatchedPricePair[]>;
|
|
29
30
|
/** @deprecated Use {@link fetchMatchedMarkets} instead. */
|
|
30
31
|
fetchArbitrage(params?: FetchArbitrageParams): Promise<ArbitrageOpportunity[]>;
|
|
32
|
+
private getLocalExchange;
|
|
33
|
+
/**
|
|
34
|
+
* Local mock IDs are sidecar-only fixtures. Hosted match endpoints do not
|
|
35
|
+
* know them, so resolve them locally when possible and avoid opaque hosted
|
|
36
|
+
* "not found" responses.
|
|
37
|
+
*/
|
|
38
|
+
private resolveLocalMockMarketLookup;
|
|
39
|
+
private resolveLocalMockEventLookup;
|
|
31
40
|
private fetchArbitrageInternal;
|
|
32
41
|
/**
|
|
33
42
|
* Bulk arbitrage via `GET /v0/arbitrage`. One round-trip.
|