pmxt-core 2.19.5 → 2.20.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 +69 -30
- package/dist/BaseExchange.js +124 -82
- package/dist/exchanges/baozi/index.d.ts +2 -0
- package/dist/exchanges/baozi/index.js +2 -0
- package/dist/exchanges/baozi/price.d.ts +3 -0
- package/dist/exchanges/baozi/price.js +16 -0
- package/dist/exchanges/baozi/price.test.d.ts +1 -0
- package/dist/exchanges/baozi/price.test.js +33 -0
- package/dist/exchanges/baozi/utils.js +5 -9
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/fetchOHLCV.js +5 -4
- package/dist/exchanges/kalshi/fetchOrderBook.js +21 -21
- package/dist/exchanges/kalshi/fetchTrades.js +2 -1
- package/dist/exchanges/kalshi/index.d.ts +3 -1
- package/dist/exchanges/kalshi/index.js +19 -16
- package/dist/exchanges/kalshi/price.d.ts +3 -0
- package/dist/exchanges/kalshi/price.js +14 -0
- package/dist/exchanges/kalshi/price.test.d.ts +1 -0
- package/dist/exchanges/kalshi/price.test.js +24 -0
- package/dist/exchanges/kalshi/utils.js +5 -4
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/index.d.ts +58 -19
- package/dist/exchanges/limitless/index.js +169 -101
- package/dist/exchanges/limitless/websocket.d.ts +10 -3
- package/dist/exchanges/limitless/websocket.js +71 -52
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/index.d.ts +3 -1
- package/dist/exchanges/myriad/index.js +7 -4
- package/dist/exchanges/myriad/price.d.ts +1 -0
- package/dist/exchanges/myriad/price.js +7 -0
- package/dist/exchanges/myriad/price.test.d.ts +1 -0
- package/dist/exchanges/myriad/price.test.js +17 -0
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/polymarket/index.d.ts +28 -15
- package/dist/exchanges/polymarket/index.js +217 -137
- package/dist/exchanges/polymarket/websocket.d.ts +11 -4
- package/dist/exchanges/polymarket/websocket.js +58 -36
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.d.ts +2 -0
- package/dist/exchanges/probable/index.js +2 -0
- package/dist/subscriber/base.d.ts +82 -0
- package/dist/subscriber/base.js +2 -0
- package/dist/subscriber/external/goldsky.d.ts +96 -0
- package/dist/subscriber/external/goldsky.js +412 -0
- package/dist/subscriber/watcher.d.ts +85 -0
- package/dist/subscriber/watcher.js +178 -0
- package/dist/types.d.ts +5 -0
- package/package.json +3 -3
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Trade } from '../types';
|
|
2
|
+
import { BaseSubscriber, SubscribedActivityBuilder, SubscribedAddressSnapshot, SubscriberConfig, SubscriptionOption } from './base';
|
|
3
|
+
export type FetchFn = (address: string, types: SubscriptionOption[]) => Promise<SubscribedAddressSnapshot>;
|
|
4
|
+
export interface AddressWatcherConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Subscriber. Responsible for polling or pushing on-chain events
|
|
7
|
+
* for each watched address. When an event arrives, the watcher fetches only
|
|
8
|
+
* the activity types that could not be derived from the event data.
|
|
9
|
+
*/
|
|
10
|
+
subscriber: BaseSubscriber;
|
|
11
|
+
/**
|
|
12
|
+
* Optional function to extract partial activity directly from the raw
|
|
13
|
+
* subscription event data, avoiding REST/RPC calls for the types
|
|
14
|
+
* it can populate.
|
|
15
|
+
*
|
|
16
|
+
* Pair with the matching subscription builder in GoldSkySubscriber:
|
|
17
|
+
* - `POLYMARKET_TRADES_SUBSCRIPTION` + `buildPolymarketTradesActivity`
|
|
18
|
+
* - `LIMITLESS_DEFAULT_SUBSCRIPTION` + `buildLimitlessBalanceActivity`
|
|
19
|
+
*
|
|
20
|
+
* Return `null` to fall back to a full REST/RPC fetch for every event.
|
|
21
|
+
*/
|
|
22
|
+
buildActivity?: SubscribedActivityBuilder;
|
|
23
|
+
}
|
|
24
|
+
export interface WatcherConfig extends Omit<SubscriberConfig, 'buildSubscription'> {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolves waiting promises whenever a subscriber reports an on-chain change.
|
|
28
|
+
*
|
|
29
|
+
* The watcher is purely reactive — it does not poll on its own. The supplied
|
|
30
|
+
* `BaseSubscriber` is responsible for all polling or push delivery. When the
|
|
31
|
+
* subscriber fires an event, the watcher:
|
|
32
|
+
* - Calls `buildActivity` to derive what it can from the raw event data.
|
|
33
|
+
* - Fetches only the missing types via `fetchFn` (REST/RPC).
|
|
34
|
+
* - Dispatches to any waiting `watch()` promises if the snapshot changed.
|
|
35
|
+
*
|
|
36
|
+
* CCXT Pro streaming pattern:
|
|
37
|
+
* - First `watch()` call → initial snapshot returned immediately via `fetchFn`.
|
|
38
|
+
* - Subsequent calls → block until the next subscriber-driven update.
|
|
39
|
+
*/
|
|
40
|
+
export declare class AddressWatcher {
|
|
41
|
+
private lastState;
|
|
42
|
+
private watchedTypes;
|
|
43
|
+
private assetIdResolvers;
|
|
44
|
+
private resolvers;
|
|
45
|
+
private readonly fetchFn;
|
|
46
|
+
private readonly subscriber;
|
|
47
|
+
private readonly buildActivity?;
|
|
48
|
+
constructor(fetchFn: FetchFn, config: AddressWatcherConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Watch an address for activity changes (CCXT Pro pattern).
|
|
51
|
+
*
|
|
52
|
+
* @param address - Public wallet address to watch
|
|
53
|
+
* @param types - Subset of activity to watch
|
|
54
|
+
* @param assetId - Optional asset id to filter activity changes.
|
|
55
|
+
* @returns Promise that resolves with the latest SubscribedAddressSnapshot snapshot
|
|
56
|
+
*/
|
|
57
|
+
watch(address: string, types: SubscriptionOption[], assetId: string): Promise<Trade[]>;
|
|
58
|
+
watch(address: string, types: SubscriptionOption[]): Promise<SubscribedAddressSnapshot>;
|
|
59
|
+
/**
|
|
60
|
+
* Stop watching an address, cancel its poll timer, unsubscribe from the
|
|
61
|
+
* subscriber, and reject any pending callers.
|
|
62
|
+
*
|
|
63
|
+
* @param address - Public wallet address to unwatch
|
|
64
|
+
*/
|
|
65
|
+
unwatch(address: string): void;
|
|
66
|
+
/** Stop all active watchers and close the underlying trigger. */
|
|
67
|
+
close(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Handle raw event data from the subscriber.
|
|
70
|
+
*
|
|
71
|
+
* @param address - Public wallet address to watch
|
|
72
|
+
* @param data - Raw event payload from the subscriber
|
|
73
|
+
*
|
|
74
|
+
* Calls `buildActivity` to attempt constructing a partial result from the
|
|
75
|
+
* event payload. Fetches only the types that are missing from the partial,
|
|
76
|
+
* then resolves any waiting promises if a change is detected.
|
|
77
|
+
*
|
|
78
|
+
* Falls back to a full `poll()` when no `buildActivity` is configured or
|
|
79
|
+
* when it returns `null`.
|
|
80
|
+
*/
|
|
81
|
+
private handleSubscriptionData;
|
|
82
|
+
private dispatchAssetResolvers;
|
|
83
|
+
private getChanged;
|
|
84
|
+
private hasChanges;
|
|
85
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AddressWatcher = void 0;
|
|
4
|
+
// ----------------------------------------------------------------------------
|
|
5
|
+
// AddressWatcher Class
|
|
6
|
+
// ----------------------------------------------------------------------------
|
|
7
|
+
/**
|
|
8
|
+
* Resolves waiting promises whenever a subscriber reports an on-chain change.
|
|
9
|
+
*
|
|
10
|
+
* The watcher is purely reactive — it does not poll on its own. The supplied
|
|
11
|
+
* `BaseSubscriber` is responsible for all polling or push delivery. When the
|
|
12
|
+
* subscriber fires an event, the watcher:
|
|
13
|
+
* - Calls `buildActivity` to derive what it can from the raw event data.
|
|
14
|
+
* - Fetches only the missing types via `fetchFn` (REST/RPC).
|
|
15
|
+
* - Dispatches to any waiting `watch()` promises if the snapshot changed.
|
|
16
|
+
*
|
|
17
|
+
* CCXT Pro streaming pattern:
|
|
18
|
+
* - First `watch()` call → initial snapshot returned immediately via `fetchFn`.
|
|
19
|
+
* - Subsequent calls → block until the next subscriber-driven update.
|
|
20
|
+
*/
|
|
21
|
+
class AddressWatcher {
|
|
22
|
+
lastState = new Map();
|
|
23
|
+
watchedTypes = new Map();
|
|
24
|
+
assetIdResolvers = new Map();
|
|
25
|
+
resolvers = new Map();
|
|
26
|
+
fetchFn;
|
|
27
|
+
subscriber;
|
|
28
|
+
buildActivity;
|
|
29
|
+
constructor(fetchFn, config) {
|
|
30
|
+
this.fetchFn = fetchFn;
|
|
31
|
+
this.subscriber = config.subscriber;
|
|
32
|
+
this.buildActivity = config.buildActivity;
|
|
33
|
+
}
|
|
34
|
+
async watch(address, types, assetId) {
|
|
35
|
+
const key = address.toLowerCase();
|
|
36
|
+
const currTypes = this.watchedTypes.get(key) ?? [];
|
|
37
|
+
const newTypes = [...new Set([...currTypes, ...types])];
|
|
38
|
+
this.watchedTypes.set(key, newTypes);
|
|
39
|
+
const diff = newTypes.filter(x => !currTypes.includes(x));
|
|
40
|
+
if (diff.length > 0) {
|
|
41
|
+
await this.subscriber.subscribe(address, newTypes, (data) => this.handleSubscriptionData(address, data));
|
|
42
|
+
}
|
|
43
|
+
if (assetId) {
|
|
44
|
+
const assetKey = `${key} ${assetId}`;
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
if (!this.assetIdResolvers.has(assetKey)) {
|
|
47
|
+
this.assetIdResolvers.set(assetKey, []);
|
|
48
|
+
}
|
|
49
|
+
this.assetIdResolvers.get(assetKey).push({ resolve, reject });
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
if (!this.resolvers.has(key)) {
|
|
54
|
+
this.resolvers.set(key, []);
|
|
55
|
+
}
|
|
56
|
+
this.resolvers.get(key).push({ resolve, reject });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stop watching an address, cancel its poll timer, unsubscribe from the
|
|
61
|
+
* subscriber, and reject any pending callers.
|
|
62
|
+
*
|
|
63
|
+
* @param address - Public wallet address to unwatch
|
|
64
|
+
*/
|
|
65
|
+
unwatch(address) {
|
|
66
|
+
const key = address.toLowerCase();
|
|
67
|
+
this.subscriber?.unsubscribe(address);
|
|
68
|
+
const resolvers = this.resolvers.get(key);
|
|
69
|
+
if (resolvers) {
|
|
70
|
+
resolvers.forEach(r => r.reject(new Error(`Stopped watching ${address}`)));
|
|
71
|
+
this.resolvers.delete(key);
|
|
72
|
+
}
|
|
73
|
+
this.lastState.delete(key);
|
|
74
|
+
this.watchedTypes.delete(key);
|
|
75
|
+
for (const [k, v] of this.assetIdResolvers.entries()) {
|
|
76
|
+
if (k.startsWith(`${key} `)) {
|
|
77
|
+
v.forEach((r) => r.reject(new Error(`Stopped watching ${address}`)));
|
|
78
|
+
this.assetIdResolvers.delete(k);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Stop all active watchers and close the underlying trigger. */
|
|
83
|
+
close() {
|
|
84
|
+
for (const address of [...this.watchedTypes.keys()]) {
|
|
85
|
+
this.unwatch(address);
|
|
86
|
+
}
|
|
87
|
+
this.subscriber?.close();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Handle raw event data from the subscriber.
|
|
91
|
+
*
|
|
92
|
+
* @param address - Public wallet address to watch
|
|
93
|
+
* @param data - Raw event payload from the subscriber
|
|
94
|
+
*
|
|
95
|
+
* Calls `buildActivity` to attempt constructing a partial result from the
|
|
96
|
+
* event payload. Fetches only the types that are missing from the partial,
|
|
97
|
+
* then resolves any waiting promises if a change is detected.
|
|
98
|
+
*
|
|
99
|
+
* Falls back to a full `poll()` when no `buildActivity` is configured or
|
|
100
|
+
* when it returns `null`.
|
|
101
|
+
*/
|
|
102
|
+
async handleSubscriptionData(address, data) {
|
|
103
|
+
const key = address.toLowerCase();
|
|
104
|
+
const types = this.watchedTypes.get(key);
|
|
105
|
+
if (!types)
|
|
106
|
+
return;
|
|
107
|
+
try {
|
|
108
|
+
const lastActivity = this.lastState.get(key);
|
|
109
|
+
const partial = this.buildActivity
|
|
110
|
+
? this.buildActivity(data, address, types, lastActivity)
|
|
111
|
+
: null;
|
|
112
|
+
let merged;
|
|
113
|
+
if (partial === null) {
|
|
114
|
+
merged = await this.fetchFn(address, types);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const missingTypes = types.filter(t => !(t in partial));
|
|
118
|
+
if (missingTypes.length > 0) {
|
|
119
|
+
const fetched = await this.fetchFn(address, missingTypes);
|
|
120
|
+
merged = { ...fetched, ...partial, address, timestamp: Date.now() };
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
merged = { address, timestamp: Date.now(), ...partial };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const last = this.lastState.get(key);
|
|
127
|
+
const value = last ? this.getChanged(last, merged) : merged;
|
|
128
|
+
this.lastState.set(key, merged);
|
|
129
|
+
// Ignore the fist snapshot and only deliver the changed fields
|
|
130
|
+
if (last && this.hasChanges(value)) {
|
|
131
|
+
const resolvers = this.resolvers.get(key);
|
|
132
|
+
if (resolvers?.length) {
|
|
133
|
+
resolvers.forEach(r => r.resolve(value));
|
|
134
|
+
this.resolvers.set(key, []);
|
|
135
|
+
}
|
|
136
|
+
this.dispatchAssetResolvers(key, value);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
dispatchAssetResolvers(addrKey, activity) {
|
|
143
|
+
for (const [assetKey, resolvers] of this.assetIdResolvers) {
|
|
144
|
+
if (!assetKey.startsWith(`${addrKey} `) || !resolvers.length)
|
|
145
|
+
continue;
|
|
146
|
+
const assetId = assetKey.slice(addrKey.length + 1);
|
|
147
|
+
const matching = (activity.trades ?? []).filter(t => t.outcomeId === assetId);
|
|
148
|
+
if (matching.length > 0) {
|
|
149
|
+
resolvers.forEach(r => r.resolve(matching));
|
|
150
|
+
this.assetIdResolvers.set(assetKey, []);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
getChanged(prev, curr) {
|
|
155
|
+
const changed = { address: curr.address, timestamp: curr.timestamp };
|
|
156
|
+
if (curr.trades !== undefined) {
|
|
157
|
+
const prevIds = new Set(prev.trades?.map(t => t.id) ?? []);
|
|
158
|
+
changed.trades = curr.trades.filter(t => !prevIds.has(t.id));
|
|
159
|
+
}
|
|
160
|
+
if (curr.positions !== undefined) {
|
|
161
|
+
const prevSizeByOutcome = new Map(prev.positions?.map(p => [p.outcomeId, p.size]) ?? []);
|
|
162
|
+
changed.positions = curr.positions.filter(p => !prevSizeByOutcome.has(p.outcomeId) || prevSizeByOutcome.get(p.outcomeId) !== p.size);
|
|
163
|
+
}
|
|
164
|
+
if (curr.balances !== undefined) {
|
|
165
|
+
const prevTotalByCurrency = new Map(prev.balances?.map(b => [b.currency, b.total]) ?? []);
|
|
166
|
+
changed.balances = curr.balances.filter(b => !prevTotalByCurrency.has(b.currency) || prevTotalByCurrency.get(b.currency) !== b.total);
|
|
167
|
+
}
|
|
168
|
+
return changed;
|
|
169
|
+
}
|
|
170
|
+
hasChanges(value) {
|
|
171
|
+
if (value.trades !== undefined && value.trades.length > 0)
|
|
172
|
+
return true;
|
|
173
|
+
if (value.positions !== undefined && value.positions.length > 0)
|
|
174
|
+
return true;
|
|
175
|
+
return value.balances !== undefined && value.balances.length > 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.AddressWatcher = AddressWatcher;
|
package/dist/types.d.ts
CHANGED
|
@@ -68,10 +68,15 @@ export interface Trade {
|
|
|
68
68
|
price: number;
|
|
69
69
|
amount: number;
|
|
70
70
|
side: 'buy' | 'sell' | 'unknown';
|
|
71
|
+
outcomeId?: string;
|
|
71
72
|
}
|
|
72
73
|
export interface UserTrade extends Trade {
|
|
73
74
|
orderId?: string;
|
|
74
75
|
}
|
|
76
|
+
export interface QueuedPromise<T> {
|
|
77
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
78
|
+
reject: (reason?: any) => void;
|
|
79
|
+
}
|
|
75
80
|
export interface Order {
|
|
76
81
|
id: string;
|
|
77
82
|
marketId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"test": "jest -c jest.config.js",
|
|
30
30
|
"server": "tsx watch src/server/index.ts",
|
|
31
31
|
"server:prod": "node dist/server/index.js",
|
|
32
|
-
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.
|
|
33
|
-
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.
|
|
32
|
+
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.20.0,library=urllib3",
|
|
33
|
+
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.20.0,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
|
|
34
34
|
"fetch:openapi": "node scripts/fetch-openapi-specs.js",
|
|
35
35
|
"extract:jsdoc": "node ../scripts/extract-jsdoc.js",
|
|
36
36
|
"generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",
|