pmxt-core 2.41.7 → 2.42.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/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/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/base-feed.d.ts +31 -0
- package/dist/feeds/base-feed.js +76 -0
- package/dist/feeds/binance/binance-feed.d.ts +29 -0
- package/dist/feeds/binance/binance-feed.js +189 -0
- package/dist/feeds/binance/index.d.ts +4 -0
- package/dist/feeds/binance/index.js +10 -0
- package/dist/feeds/binance/normalizer.d.ts +7 -0
- package/dist/feeds/binance/normalizer.js +51 -0
- package/dist/feeds/binance/types.d.ts +32 -0
- package/dist/feeds/binance/types.js +11 -0
- package/dist/feeds/chainlink/chainlink-feed.d.ts +38 -0
- package/dist/feeds/chainlink/chainlink-feed.js +241 -0
- package/dist/feeds/chainlink/index.d.ts +4 -0
- package/dist/feeds/chainlink/index.js +17 -0
- package/dist/feeds/chainlink/normalizer.d.ts +9 -0
- package/dist/feeds/chainlink/normalizer.js +99 -0
- package/dist/feeds/chainlink/types.d.ts +75 -0
- package/dist/feeds/chainlink/types.js +51 -0
- package/dist/feeds/index.d.ts +6 -0
- package/dist/feeds/index.js +21 -0
- package/dist/feeds/interfaces.d.ts +23 -0
- package/dist/feeds/interfaces.js +2 -0
- package/dist/feeds/types.d.ts +123 -0
- package/dist/feeds/types.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server/feed-factory.d.ts +6 -0
- package/dist/server/feed-factory.js +34 -0
- package/dist/server/feed-routes.d.ts +6 -0
- package/dist/server/feed-routes.js +166 -0
- package/dist/server/openapi.yaml +513 -0
- package/package.json +3 -3
|
@@ -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-19T21:06:06.710Z
|
|
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-19T21:06:06.710Z
|
|
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-19T21:06:06.754Z
|
|
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-19T21:06:06.754Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -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-19T21:06:06.766Z
|
|
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-19T21:06:06.766Z
|
|
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-19T21:06:06.769Z
|
|
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-19T21:06:06.769Z
|
|
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-19T21:06:06.717Z
|
|
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-19T21:06:06.717Z
|
|
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-19T21:06:06.733Z
|
|
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-19T21:06:06.733Z
|
|
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-19T21:06:06.730Z
|
|
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-19T21:06:06.730Z
|
|
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-19T21:06:06.761Z
|
|
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-19T21:06:06.761Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.probableApiSpec = {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IDataFeed } from './interfaces';
|
|
2
|
+
import { Ticker, Tickers, OHLCV, OrderBook, Market, Dictionary } from './types';
|
|
3
|
+
export interface DataFeedOptions {
|
|
4
|
+
rateLimit?: number;
|
|
5
|
+
enableCache?: boolean;
|
|
6
|
+
cacheTtlMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare abstract class BaseDataFeed implements IDataFeed {
|
|
9
|
+
abstract readonly name: string;
|
|
10
|
+
abstract readonly description: string;
|
|
11
|
+
protected readonly options: Required<DataFeedOptions>;
|
|
12
|
+
private readonly cache;
|
|
13
|
+
private lastRequestTime;
|
|
14
|
+
constructor(options?: DataFeedOptions);
|
|
15
|
+
fetchTicker(symbol: string): Promise<Ticker>;
|
|
16
|
+
fetchTickers(symbols?: string[]): Promise<Tickers>;
|
|
17
|
+
watchTicker(symbol: string, callback: (ticker: Ticker) => void): () => void;
|
|
18
|
+
fetchOHLCV(symbol: string, timeframe?: string, since?: number, limit?: number): Promise<OHLCV[]>;
|
|
19
|
+
fetchOrderBook(symbol: string, limit?: number): Promise<OrderBook>;
|
|
20
|
+
abstract loadMarkets(reload?: boolean): Promise<Dictionary<Market>>;
|
|
21
|
+
protected abstract fetchTickerImpl(symbol: string): Promise<Ticker>;
|
|
22
|
+
protected abstract fetchTickersImpl(symbols?: string[]): Promise<Tickers>;
|
|
23
|
+
protected abstract watchTickerImpl(symbol: string, callback: (ticker: Ticker) => void): () => void;
|
|
24
|
+
protected abstract fetchOHLCVImpl(symbol: string, timeframe?: string, since?: number, limit?: number): Promise<OHLCV[]>;
|
|
25
|
+
protected abstract fetchOrderBookImpl(symbol: string, limit?: number): Promise<OrderBook>;
|
|
26
|
+
connect(): Promise<void>;
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
private getCached;
|
|
29
|
+
private setCache;
|
|
30
|
+
private throttle;
|
|
31
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseDataFeed = void 0;
|
|
4
|
+
class BaseDataFeed {
|
|
5
|
+
options;
|
|
6
|
+
cache = new Map();
|
|
7
|
+
lastRequestTime = 0;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.options = {
|
|
10
|
+
rateLimit: options.rateLimit ?? 10,
|
|
11
|
+
enableCache: options.enableCache ?? true,
|
|
12
|
+
cacheTtlMs: options.cacheTtlMs ?? 1000,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// -- Public API (CCXT-compatible names) --
|
|
16
|
+
async fetchTicker(symbol) {
|
|
17
|
+
const cached = this.getCached(symbol);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
await this.throttle();
|
|
21
|
+
const ticker = await this.fetchTickerImpl(symbol);
|
|
22
|
+
this.setCache(symbol, ticker);
|
|
23
|
+
return ticker;
|
|
24
|
+
}
|
|
25
|
+
async fetchTickers(symbols) {
|
|
26
|
+
await this.throttle();
|
|
27
|
+
return this.fetchTickersImpl(symbols);
|
|
28
|
+
}
|
|
29
|
+
watchTicker(symbol, callback) {
|
|
30
|
+
return this.watchTickerImpl(symbol, (ticker) => {
|
|
31
|
+
this.setCache(symbol, ticker);
|
|
32
|
+
callback(ticker);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async fetchOHLCV(symbol, timeframe, since, limit) {
|
|
36
|
+
await this.throttle();
|
|
37
|
+
return this.fetchOHLCVImpl(symbol, timeframe, since, limit);
|
|
38
|
+
}
|
|
39
|
+
async fetchOrderBook(symbol, limit) {
|
|
40
|
+
await this.throttle();
|
|
41
|
+
return this.fetchOrderBookImpl(symbol, limit);
|
|
42
|
+
}
|
|
43
|
+
// -- Lifecycle --
|
|
44
|
+
async connect() { }
|
|
45
|
+
async close() { }
|
|
46
|
+
// -- Internal --
|
|
47
|
+
getCached(symbol) {
|
|
48
|
+
if (!this.options.enableCache)
|
|
49
|
+
return null;
|
|
50
|
+
const entry = this.cache.get(symbol);
|
|
51
|
+
if (!entry)
|
|
52
|
+
return null;
|
|
53
|
+
if (Date.now() > entry.expiresAt) {
|
|
54
|
+
this.cache.delete(symbol);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return entry.ticker;
|
|
58
|
+
}
|
|
59
|
+
setCache(symbol, ticker) {
|
|
60
|
+
if (!this.options.enableCache)
|
|
61
|
+
return;
|
|
62
|
+
this.cache.set(symbol, {
|
|
63
|
+
ticker,
|
|
64
|
+
expiresAt: Date.now() + this.options.cacheTtlMs,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async throttle() {
|
|
68
|
+
const minInterval = 1000 / this.options.rateLimit;
|
|
69
|
+
const elapsed = Date.now() - this.lastRequestTime;
|
|
70
|
+
if (elapsed < minInterval) {
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, minInterval - elapsed));
|
|
72
|
+
}
|
|
73
|
+
this.lastRequestTime = Date.now();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.BaseDataFeed = BaseDataFeed;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BaseDataFeed, DataFeedOptions } from '../base-feed';
|
|
2
|
+
import { Ticker, Tickers, OHLCV, OrderBook, Market, Dictionary } from '../types';
|
|
3
|
+
import { BinanceFeedConfig } from './types';
|
|
4
|
+
export declare class BinanceFeed extends BaseDataFeed {
|
|
5
|
+
readonly name = "binance";
|
|
6
|
+
readonly description = "Binance spot trade firehose via obdata relay";
|
|
7
|
+
private readonly wsUrl;
|
|
8
|
+
private readonly apiKey;
|
|
9
|
+
private readonly reconnectIntervalMs;
|
|
10
|
+
private ws;
|
|
11
|
+
private subscriptions;
|
|
12
|
+
private latestTickers;
|
|
13
|
+
private isTerminated;
|
|
14
|
+
private reconnectTimer;
|
|
15
|
+
private connectionPromise;
|
|
16
|
+
constructor(config?: BinanceFeedConfig, options?: DataFeedOptions);
|
|
17
|
+
connect(): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
loadMarkets(): Promise<Dictionary<Market>>;
|
|
20
|
+
protected fetchTickerImpl(symbol: string): Promise<Ticker>;
|
|
21
|
+
protected fetchTickersImpl(symbols?: string[]): Promise<Tickers>;
|
|
22
|
+
protected watchTickerImpl(symbol: string, callback: (ticker: Ticker) => void): () => void;
|
|
23
|
+
protected fetchOHLCVImpl(_symbol: string, _timeframe?: string, _since?: number, _limit?: number): Promise<OHLCV[]>;
|
|
24
|
+
protected fetchOrderBookImpl(_symbol: string, _limit?: number): Promise<OrderBook>;
|
|
25
|
+
private ensureConnected;
|
|
26
|
+
private establishConnection;
|
|
27
|
+
private handleMessage;
|
|
28
|
+
private scheduleReconnect;
|
|
29
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
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.BinanceFeed = void 0;
|
|
7
|
+
const ws_1 = __importDefault(require("ws"));
|
|
8
|
+
const base_feed_1 = require("../base-feed");
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
const normalizer_1 = require("./normalizer");
|
|
11
|
+
class BinanceFeed extends base_feed_1.BaseDataFeed {
|
|
12
|
+
name = 'binance';
|
|
13
|
+
description = 'Binance spot trade firehose via obdata relay';
|
|
14
|
+
wsUrl;
|
|
15
|
+
apiKey;
|
|
16
|
+
reconnectIntervalMs;
|
|
17
|
+
ws = null;
|
|
18
|
+
subscriptions = [];
|
|
19
|
+
latestTickers = new Map();
|
|
20
|
+
isTerminated = false;
|
|
21
|
+
reconnectTimer = null;
|
|
22
|
+
connectionPromise = null;
|
|
23
|
+
constructor(config = {}, options) {
|
|
24
|
+
super(options);
|
|
25
|
+
this.wsUrl = config.wsUrl ?? types_1.BINANCE_RELAY_DEFAULTS.wsUrl;
|
|
26
|
+
this.apiKey = config.apiKey ?? process.env.OBDATA_API_KEY ?? '';
|
|
27
|
+
this.reconnectIntervalMs = config.reconnectIntervalMs ?? types_1.BINANCE_RELAY_DEFAULTS.reconnectIntervalMs;
|
|
28
|
+
}
|
|
29
|
+
// -- Lifecycle --
|
|
30
|
+
async connect() {
|
|
31
|
+
if (this.ws)
|
|
32
|
+
return;
|
|
33
|
+
if (this.connectionPromise)
|
|
34
|
+
return this.connectionPromise;
|
|
35
|
+
this.isTerminated = false;
|
|
36
|
+
this.connectionPromise = this.establishConnection();
|
|
37
|
+
return this.connectionPromise;
|
|
38
|
+
}
|
|
39
|
+
async close() {
|
|
40
|
+
this.isTerminated = true;
|
|
41
|
+
if (this.reconnectTimer) {
|
|
42
|
+
clearTimeout(this.reconnectTimer);
|
|
43
|
+
this.reconnectTimer = null;
|
|
44
|
+
}
|
|
45
|
+
if (this.ws) {
|
|
46
|
+
this.ws.close(1000, 'client_close');
|
|
47
|
+
this.ws = null;
|
|
48
|
+
}
|
|
49
|
+
this.connectionPromise = null;
|
|
50
|
+
this.subscriptions = [];
|
|
51
|
+
}
|
|
52
|
+
// -- CCXT-compatible implementations --
|
|
53
|
+
async loadMarkets() {
|
|
54
|
+
const symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'XRPUSDT'];
|
|
55
|
+
const markets = {};
|
|
56
|
+
for (const sym of symbols) {
|
|
57
|
+
const pair = (0, normalizer_1.symbolToPair)(sym);
|
|
58
|
+
const [base, quote] = pair.split('/');
|
|
59
|
+
markets[pair] = {
|
|
60
|
+
id: sym,
|
|
61
|
+
symbol: pair,
|
|
62
|
+
base,
|
|
63
|
+
quote,
|
|
64
|
+
active: true,
|
|
65
|
+
type: 'spot',
|
|
66
|
+
spot: true,
|
|
67
|
+
margin: false,
|
|
68
|
+
swap: false,
|
|
69
|
+
future: false,
|
|
70
|
+
option: false,
|
|
71
|
+
contract: false,
|
|
72
|
+
precision: { amount: undefined, price: undefined },
|
|
73
|
+
limits: {},
|
|
74
|
+
info: { symbol: sym, streamable: true },
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return markets;
|
|
78
|
+
}
|
|
79
|
+
async fetchTickerImpl(symbol) {
|
|
80
|
+
const cached = this.latestTickers.get(symbol);
|
|
81
|
+
if (cached)
|
|
82
|
+
return cached;
|
|
83
|
+
await this.ensureConnected();
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const timeout = setTimeout(() => {
|
|
86
|
+
cleanup();
|
|
87
|
+
reject(new Error(`BinanceFeed: timed out waiting for trade on ${symbol} (10s)`));
|
|
88
|
+
}, 10_000);
|
|
89
|
+
const cleanup = this.watchTickerImpl(symbol, (ticker) => {
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
cleanup();
|
|
92
|
+
resolve(ticker);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async fetchTickersImpl(symbols) {
|
|
97
|
+
await this.ensureConnected();
|
|
98
|
+
const pairs = symbols ?? ['BTC/USDT', 'ETH/USDT', 'SOL/USDT', 'XRP/USDT'];
|
|
99
|
+
const tickers = await Promise.all(pairs.map((s) => this.fetchTickerImpl(s)));
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const ticker of tickers) {
|
|
102
|
+
result[ticker.symbol] = ticker;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
watchTickerImpl(symbol, callback) {
|
|
107
|
+
const sub = { symbol, callback };
|
|
108
|
+
this.subscriptions = [...this.subscriptions, sub];
|
|
109
|
+
this.ensureConnected();
|
|
110
|
+
return () => {
|
|
111
|
+
this.subscriptions = this.subscriptions.filter((s) => s !== sub);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async fetchOHLCVImpl(_symbol, _timeframe, _since, _limit) {
|
|
115
|
+
throw new Error('BinanceFeed: OHLCV not available via trade relay');
|
|
116
|
+
}
|
|
117
|
+
async fetchOrderBookImpl(_symbol, _limit) {
|
|
118
|
+
throw new Error('BinanceFeed: Order book not available via trade relay');
|
|
119
|
+
}
|
|
120
|
+
// -- Internal --
|
|
121
|
+
async ensureConnected() {
|
|
122
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN)
|
|
123
|
+
return;
|
|
124
|
+
await this.connect();
|
|
125
|
+
}
|
|
126
|
+
establishConnection() {
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
const url = this.apiKey
|
|
129
|
+
? `${this.wsUrl}?key=${this.apiKey}`
|
|
130
|
+
: this.wsUrl;
|
|
131
|
+
const ws = new ws_1.default(url);
|
|
132
|
+
ws.on('open', () => {
|
|
133
|
+
this.ws = ws;
|
|
134
|
+
this.connectionPromise = null;
|
|
135
|
+
ws.send(JSON.stringify({ op: 'subscribe_all' }));
|
|
136
|
+
resolve();
|
|
137
|
+
});
|
|
138
|
+
ws.on('message', (data) => {
|
|
139
|
+
this.handleMessage(data);
|
|
140
|
+
});
|
|
141
|
+
ws.on('close', () => {
|
|
142
|
+
this.ws = null;
|
|
143
|
+
this.connectionPromise = null;
|
|
144
|
+
if (!this.isTerminated) {
|
|
145
|
+
this.scheduleReconnect();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
ws.on('error', (err) => {
|
|
149
|
+
this.ws = null;
|
|
150
|
+
this.connectionPromise = null;
|
|
151
|
+
if (!this.isTerminated) {
|
|
152
|
+
this.scheduleReconnect();
|
|
153
|
+
}
|
|
154
|
+
reject(err);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
handleMessage(data) {
|
|
159
|
+
const text = typeof data === 'string' ? data : data.toString();
|
|
160
|
+
let msg;
|
|
161
|
+
try {
|
|
162
|
+
msg = JSON.parse(text);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (msg.op !== 'event')
|
|
168
|
+
return;
|
|
169
|
+
const event = msg;
|
|
170
|
+
const ticker = (0, normalizer_1.normalizeTradeToTicker)(event);
|
|
171
|
+
this.latestTickers = new Map(this.latestTickers).set(ticker.symbol, ticker);
|
|
172
|
+
for (const sub of this.subscriptions) {
|
|
173
|
+
if (sub.symbol === ticker.symbol) {
|
|
174
|
+
sub.callback(ticker);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
scheduleReconnect() {
|
|
179
|
+
if (this.reconnectTimer)
|
|
180
|
+
return;
|
|
181
|
+
this.reconnectTimer = setTimeout(() => {
|
|
182
|
+
this.reconnectTimer = null;
|
|
183
|
+
if (!this.isTerminated) {
|
|
184
|
+
this.connect();
|
|
185
|
+
}
|
|
186
|
+
}, this.reconnectIntervalMs);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.BinanceFeed = BinanceFeed;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BINANCE_RELAY_DEFAULTS = exports.symbolToPair = exports.normalizeTradeToTicker = exports.BinanceFeed = void 0;
|
|
4
|
+
var binance_feed_1 = require("./binance-feed");
|
|
5
|
+
Object.defineProperty(exports, "BinanceFeed", { enumerable: true, get: function () { return binance_feed_1.BinanceFeed; } });
|
|
6
|
+
var normalizer_1 = require("./normalizer");
|
|
7
|
+
Object.defineProperty(exports, "normalizeTradeToTicker", { enumerable: true, get: function () { return normalizer_1.normalizeTradeToTicker; } });
|
|
8
|
+
Object.defineProperty(exports, "symbolToPair", { enumerable: true, get: function () { return normalizer_1.symbolToPair; } });
|
|
9
|
+
var types_1 = require("./types");
|
|
10
|
+
Object.defineProperty(exports, "BINANCE_RELAY_DEFAULTS", { enumerable: true, get: function () { return types_1.BINANCE_RELAY_DEFAULTS; } });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Ticker } from '../types';
|
|
2
|
+
import { BinanceRelayTradeEvent } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Convert a Binance symbol (e.g. "BTCUSDT") to CCXT pair format (e.g. "BTC/USDT").
|
|
5
|
+
*/
|
|
6
|
+
export declare function symbolToPair(symbol: string): string;
|
|
7
|
+
export declare function normalizeTradeToTicker(event: BinanceRelayTradeEvent): Ticker;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.symbolToPair = symbolToPair;
|
|
4
|
+
exports.normalizeTradeToTicker = normalizeTradeToTicker;
|
|
5
|
+
// ----------------------------------------------------------------------------
|
|
6
|
+
// Normalizer — pure functions mapping raw relay events to CCXT Ticker.
|
|
7
|
+
// ----------------------------------------------------------------------------
|
|
8
|
+
/**
|
|
9
|
+
* Convert a Binance symbol (e.g. "BTCUSDT") to CCXT pair format (e.g. "BTC/USDT").
|
|
10
|
+
*/
|
|
11
|
+
function symbolToPair(symbol) {
|
|
12
|
+
const quotes = ['USDT', 'USDC', 'BUSD', 'BTC', 'ETH', 'BNB'];
|
|
13
|
+
for (const quote of quotes) {
|
|
14
|
+
if (symbol.endsWith(quote)) {
|
|
15
|
+
const base = symbol.slice(0, -quote.length);
|
|
16
|
+
if (base.length > 0) {
|
|
17
|
+
return `${base}/${quote}`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return symbol;
|
|
22
|
+
}
|
|
23
|
+
function normalizeTradeToTicker(event) {
|
|
24
|
+
const pair = symbolToPair(event.symbol);
|
|
25
|
+
const price = parseFloat(event.price);
|
|
26
|
+
const ts = event.trade_time_ms;
|
|
27
|
+
return {
|
|
28
|
+
symbol: pair,
|
|
29
|
+
info: event,
|
|
30
|
+
timestamp: ts,
|
|
31
|
+
datetime: new Date(ts).toISOString(),
|
|
32
|
+
high: undefined,
|
|
33
|
+
low: undefined,
|
|
34
|
+
bid: undefined,
|
|
35
|
+
bidVolume: undefined,
|
|
36
|
+
ask: undefined,
|
|
37
|
+
askVolume: undefined,
|
|
38
|
+
vwap: undefined,
|
|
39
|
+
open: undefined,
|
|
40
|
+
close: undefined,
|
|
41
|
+
last: price,
|
|
42
|
+
previousClose: undefined,
|
|
43
|
+
change: undefined,
|
|
44
|
+
percentage: undefined,
|
|
45
|
+
average: undefined,
|
|
46
|
+
quoteVolume: undefined,
|
|
47
|
+
baseVolume: undefined,
|
|
48
|
+
indexPrice: undefined,
|
|
49
|
+
markPrice: undefined,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface BinanceRelayTradeEvent {
|
|
2
|
+
readonly op: 'event';
|
|
3
|
+
readonly source: 'binance';
|
|
4
|
+
readonly symbol: string;
|
|
5
|
+
readonly trade_id: number;
|
|
6
|
+
readonly price: string;
|
|
7
|
+
readonly quantity: string;
|
|
8
|
+
readonly event_time_ms: number;
|
|
9
|
+
readonly trade_time_ms: number;
|
|
10
|
+
readonly is_buyer_maker: boolean;
|
|
11
|
+
readonly timestamp_received_ms: number;
|
|
12
|
+
}
|
|
13
|
+
export interface BinanceRelayAck {
|
|
14
|
+
readonly op: 'ack';
|
|
15
|
+
readonly subscribed_all: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface BinanceRelayPong {
|
|
18
|
+
readonly op: 'pong';
|
|
19
|
+
}
|
|
20
|
+
export type BinanceRelayMessage = BinanceRelayTradeEvent | BinanceRelayAck | BinanceRelayPong;
|
|
21
|
+
export interface BinanceFeedConfig {
|
|
22
|
+
/** WebSocket URL for the Binance trade relay. */
|
|
23
|
+
readonly wsUrl?: string;
|
|
24
|
+
/** API key for authentication. */
|
|
25
|
+
readonly apiKey?: string;
|
|
26
|
+
/** Reconnect interval in ms (default: 5000). */
|
|
27
|
+
readonly reconnectIntervalMs?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const BINANCE_RELAY_DEFAULTS: {
|
|
30
|
+
readonly wsUrl: string;
|
|
31
|
+
readonly reconnectIntervalMs: 5000;
|
|
32
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ----------------------------------------------------------------------------
|
|
3
|
+
// Raw message types from the Binance trade relay (obdata)
|
|
4
|
+
// See: https://github.com/pmxt-dev/obdata/blob/main/docs/apis/BINANCE_WS.md
|
|
5
|
+
// ----------------------------------------------------------------------------
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.BINANCE_RELAY_DEFAULTS = void 0;
|
|
8
|
+
exports.BINANCE_RELAY_DEFAULTS = {
|
|
9
|
+
wsUrl: process.env.BINANCE_RELAY_WS_URL || '',
|
|
10
|
+
reconnectIntervalMs: 5000,
|
|
11
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BaseDataFeed, DataFeedOptions } from '../base-feed';
|
|
2
|
+
import { Ticker, Tickers, OHLCV, OrderBook, Market, OracleRound, OracleParams, Dictionary } from '../types';
|
|
3
|
+
import { ChainlinkFeedConfig } from './types';
|
|
4
|
+
export declare class ChainlinkFeed extends BaseDataFeed {
|
|
5
|
+
readonly name = "chainlink";
|
|
6
|
+
readonly description = "Chainlink price feeds (ETH, BTC, XRP, SOL) on Polygon via pmxt-ohlc";
|
|
7
|
+
private readonly client;
|
|
8
|
+
private readonly wsUrl;
|
|
9
|
+
private readonly wsApiKey;
|
|
10
|
+
private readonly reconnectIntervalMs;
|
|
11
|
+
private ws;
|
|
12
|
+
private subscriptions;
|
|
13
|
+
private latestTickers;
|
|
14
|
+
private isTerminated;
|
|
15
|
+
private reconnectTimer;
|
|
16
|
+
private connectionPromise;
|
|
17
|
+
constructor(config: ChainlinkFeedConfig, options?: DataFeedOptions);
|
|
18
|
+
connect(): Promise<void>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
loadMarkets(): Promise<Dictionary<Market>>;
|
|
21
|
+
protected fetchTickerImpl(symbol: string): Promise<Ticker>;
|
|
22
|
+
protected fetchTickersImpl(symbols?: string[]): Promise<Tickers>;
|
|
23
|
+
protected watchTickerImpl(symbol: string, callback: (ticker: Ticker) => void): () => void;
|
|
24
|
+
protected fetchOHLCVImpl(_symbol: string, _timeframe?: string, _since?: number, _limit?: number): Promise<OHLCV[]>;
|
|
25
|
+
protected fetchOrderBookImpl(_symbol: string, _limit?: number): Promise<OrderBook>;
|
|
26
|
+
fetchOracleRound(params: OracleParams): Promise<OracleRound>;
|
|
27
|
+
fetchOracleHistory(params: OracleParams): Promise<OracleRound[]>;
|
|
28
|
+
fetchHistoricalPrices(symbol: string, opts?: {
|
|
29
|
+
fromTimestamp?: number;
|
|
30
|
+
untilTimestamp?: number;
|
|
31
|
+
maxSize?: number;
|
|
32
|
+
order?: 'asc' | 'desc';
|
|
33
|
+
}): Promise<Ticker[]>;
|
|
34
|
+
private ensureConnected;
|
|
35
|
+
private establishConnection;
|
|
36
|
+
private handleMessage;
|
|
37
|
+
private scheduleReconnect;
|
|
38
|
+
}
|