pmxt-core 2.39.1 → 2.40.1

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.
Files changed (82) hide show
  1. package/dist/exchanges/baozi/fetcher.js +28 -8
  2. package/dist/exchanges/baozi/index.js +6 -4
  3. package/dist/exchanges/gemini-titan/auth.d.ts +34 -0
  4. package/dist/exchanges/gemini-titan/auth.js +80 -0
  5. package/dist/exchanges/gemini-titan/config.d.ts +15 -0
  6. package/dist/exchanges/gemini-titan/config.js +24 -0
  7. package/dist/exchanges/gemini-titan/errors.d.ts +20 -0
  8. package/dist/exchanges/gemini-titan/errors.js +75 -0
  9. package/dist/exchanges/gemini-titan/fetcher.d.ts +26 -0
  10. package/dist/exchanges/gemini-titan/fetcher.js +148 -0
  11. package/dist/exchanges/gemini-titan/index.d.ts +31 -0
  12. package/dist/exchanges/gemini-titan/index.js +188 -0
  13. package/dist/exchanges/gemini-titan/normalizer.d.ts +13 -0
  14. package/dist/exchanges/gemini-titan/normalizer.js +229 -0
  15. package/dist/exchanges/gemini-titan/types.d.ts +220 -0
  16. package/dist/exchanges/gemini-titan/types.js +6 -0
  17. package/dist/exchanges/gemini-titan/utils.d.ts +30 -0
  18. package/dist/exchanges/gemini-titan/utils.js +57 -0
  19. package/dist/exchanges/gemini-titan/websocket.d.ts +46 -0
  20. package/dist/exchanges/gemini-titan/websocket.js +295 -0
  21. package/dist/exchanges/kalshi/api.d.ts +1 -1
  22. package/dist/exchanges/kalshi/api.js +1 -1
  23. package/dist/exchanges/kalshi/fetcher.js +6 -2
  24. package/dist/exchanges/limitless/api.d.ts +1 -1
  25. package/dist/exchanges/limitless/api.js +1 -1
  26. package/dist/exchanges/limitless/index.js +3 -6
  27. package/dist/exchanges/limitless/utils.js +9 -1
  28. package/dist/exchanges/metaculus/fetchEvents.js +7 -2
  29. package/dist/exchanges/mock/index.d.ts +55 -0
  30. package/dist/exchanges/mock/index.js +603 -0
  31. package/dist/exchanges/mock/seededRng.d.ts +10 -0
  32. package/dist/exchanges/mock/seededRng.js +48 -0
  33. package/dist/exchanges/myriad/api.d.ts +1 -1
  34. package/dist/exchanges/myriad/api.js +1 -1
  35. package/dist/exchanges/myriad/websocket.d.ts +4 -0
  36. package/dist/exchanges/myriad/websocket.js +51 -6
  37. package/dist/exchanges/opinion/api.d.ts +1 -1
  38. package/dist/exchanges/opinion/api.js +1 -1
  39. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  40. package/dist/exchanges/polymarket/api-clob.js +1 -1
  41. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  42. package/dist/exchanges/polymarket/api-data.js +1 -1
  43. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  44. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  45. package/dist/exchanges/polymarket/auth.js +5 -2
  46. package/dist/exchanges/polymarket/index.js +2 -1
  47. package/dist/exchanges/polymarket_us/normalizer.js +5 -1
  48. package/dist/exchanges/probable/api.d.ts +1 -1
  49. package/dist/exchanges/probable/api.js +1 -1
  50. package/dist/exchanges/probable/index.js +9 -6
  51. package/dist/exchanges/smarkets/fetcher.js +6 -2
  52. package/dist/index.d.ts +8 -0
  53. package/dist/index.js +9 -1
  54. package/dist/router/Router.js +55 -21
  55. package/dist/server/exchange-factory.js +9 -0
  56. package/dist/server/openapi.yaml +22 -0
  57. package/dist/server/ws-handler.js +13 -3
  58. package/package.json +3 -3
  59. package/dist/exchanges/baozi/price.test.d.ts +0 -1
  60. package/dist/exchanges/baozi/price.test.js +0 -33
  61. package/dist/exchanges/kalshi/kalshi.test.d.ts +0 -1
  62. package/dist/exchanges/kalshi/kalshi.test.js +0 -641
  63. package/dist/exchanges/kalshi/price.test.d.ts +0 -1
  64. package/dist/exchanges/kalshi/price.test.js +0 -24
  65. package/dist/exchanges/myriad/price.test.d.ts +0 -1
  66. package/dist/exchanges/myriad/price.test.js +0 -17
  67. package/dist/exchanges/polymarket_us/errors.test.d.ts +0 -1
  68. package/dist/exchanges/polymarket_us/errors.test.js +0 -54
  69. package/dist/exchanges/polymarket_us/index.test.d.ts +0 -8
  70. package/dist/exchanges/polymarket_us/index.test.js +0 -237
  71. package/dist/exchanges/polymarket_us/normalizer.test.d.ts +0 -1
  72. package/dist/exchanges/polymarket_us/normalizer.test.js +0 -224
  73. package/dist/exchanges/polymarket_us/price.test.d.ts +0 -1
  74. package/dist/exchanges/polymarket_us/price.test.js +0 -131
  75. package/dist/exchanges/polymarket_us/websocket.test.d.ts +0 -8
  76. package/dist/exchanges/polymarket_us/websocket.test.js +0 -162
  77. package/dist/exchanges/smarkets/price.test.d.ts +0 -1
  78. package/dist/exchanges/smarkets/price.test.js +0 -50
  79. package/dist/router/Router.test.d.ts +0 -1
  80. package/dist/router/Router.test.js +0 -328
  81. package/dist/router/client.test.d.ts +0 -1
  82. package/dist/router/client.test.js +0 -177
@@ -36,24 +36,34 @@ class BaoziFetcher {
36
36
  }),
37
37
  ]);
38
38
  const markets = [];
39
+ let booleanSkipped = 0;
39
40
  for (const account of booleanAccounts) {
40
41
  try {
41
42
  const parsed = (0, utils_1.parseMarket)(account.account.data);
42
43
  markets.push({ pubkey: account.pubkey.toString(), parsed });
43
44
  }
44
- catch {
45
- // Skip malformed accounts
45
+ catch (parseError) {
46
+ booleanSkipped++;
47
+ console.warn(`[Baozi] fetchRawMarkets: failed to parse boolean market account pubkey=${account.pubkey.toString()}:`, parseError);
46
48
  }
47
49
  }
50
+ if (booleanSkipped > 0) {
51
+ console.warn(`[Baozi] fetchRawMarkets: skipped ${booleanSkipped} malformed boolean market account(s) out of ${booleanAccounts.length}`);
52
+ }
53
+ let raceSkipped = 0;
48
54
  for (const account of raceAccounts) {
49
55
  try {
50
56
  const parsed = (0, utils_1.parseRaceMarket)(account.account.data);
51
57
  markets.push({ pubkey: account.pubkey.toString(), parsed });
52
58
  }
53
- catch {
54
- // Skip malformed accounts
59
+ catch (parseError) {
60
+ raceSkipped++;
61
+ console.warn(`[Baozi] fetchRawMarkets: failed to parse race market account pubkey=${account.pubkey.toString()}:`, parseError);
55
62
  }
56
63
  }
64
+ if (raceSkipped > 0) {
65
+ console.warn(`[Baozi] fetchRawMarkets: skipped ${raceSkipped} malformed race market account(s) out of ${raceAccounts.length}`);
66
+ }
57
67
  marketsCache.set(markets);
58
68
  return markets;
59
69
  }
@@ -114,25 +124,35 @@ class BaoziFetcher {
114
124
  }),
115
125
  ]);
116
126
  const booleanPositions = [];
127
+ let boolPosSkipped = 0;
117
128
  for (const account of booleanAccounts) {
118
129
  try {
119
130
  const parsed = (0, utils_1.parseUserPosition)(account.account.data);
120
131
  booleanPositions.push({ pubkey: account.pubkey.toString(), parsed });
121
132
  }
122
- catch {
123
- // Skip malformed
133
+ catch (parseError) {
134
+ boolPosSkipped++;
135
+ console.warn(`[Baozi] fetchRawUserPositions: failed to parse boolean position account pubkey=${account.pubkey.toString()}:`, parseError);
124
136
  }
125
137
  }
138
+ if (boolPosSkipped > 0) {
139
+ console.warn(`[Baozi] fetchRawUserPositions: skipped ${boolPosSkipped} malformed boolean position account(s) out of ${booleanAccounts.length}`);
140
+ }
126
141
  const racePositions = [];
142
+ let racePosSkipped = 0;
127
143
  for (const account of raceAccounts) {
128
144
  try {
129
145
  const parsed = (0, utils_1.parseRacePosition)(account.account.data);
130
146
  racePositions.push({ pubkey: account.pubkey.toString(), parsed });
131
147
  }
132
- catch {
133
- // Skip malformed
148
+ catch (parseError) {
149
+ racePosSkipped++;
150
+ console.warn(`[Baozi] fetchRawUserPositions: failed to parse race position account pubkey=${account.pubkey.toString()}:`, parseError);
134
151
  }
135
152
  }
153
+ if (racePosSkipped > 0) {
154
+ console.warn(`[Baozi] fetchRawUserPositions: skipped ${racePosSkipped} malformed race position account(s) out of ${raceAccounts.length}`);
155
+ }
136
156
  return { booleanPositions, racePositions };
137
157
  }
138
158
  catch (error) {
@@ -108,8 +108,9 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
108
108
  marketLookup.set(marketPdaStr, unified);
109
109
  }
110
110
  }
111
- catch {
112
- // Use defaults if market fetch fails
111
+ catch (error) {
112
+ console.warn(`[Baozi] fetchPositions: failed to fetch boolean market account for PDA=${marketPdaStr}:`, error);
113
+ throw error;
113
114
  }
114
115
  }
115
116
  }
@@ -127,8 +128,9 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
127
128
  marketLookup.set(racePdaStr, unified);
128
129
  }
129
130
  }
130
- catch {
131
- // Use defaults if market fetch fails
131
+ catch (error) {
132
+ console.warn(`[Baozi] fetchPositions: failed to fetch race market account for PDA=${racePdaStr}:`, error);
133
+ throw error;
132
134
  }
133
135
  }
134
136
  }
@@ -0,0 +1,34 @@
1
+ import { ExchangeCredentials } from '../../BaseExchange';
2
+ /**
3
+ * Gemini HMAC-SHA384 authentication.
4
+ *
5
+ * Gemini's prediction market API uses header-based auth where the POST body
6
+ * is always empty — the actual payload is base64-encoded and placed in the
7
+ * X-GEMINI-PAYLOAD header, with an HMAC-SHA384 signature.
8
+ */
9
+ export declare class GeminiAuth {
10
+ private readonly apiKey;
11
+ private readonly apiSecret;
12
+ private lastNonce;
13
+ constructor(credentials: ExchangeCredentials);
14
+ /**
15
+ * Generate a strictly monotonic nonce.
16
+ * Uses Date.now() but ensures it never repeats even if called
17
+ * within the same millisecond.
18
+ */
19
+ nonce(): number;
20
+ /**
21
+ * Build the three authentication headers for a given payload object.
22
+ *
23
+ * The payload must include `request` (endpoint path) and `nonce`.
24
+ * Additional fields (symbol, side, etc.) are included for order requests.
25
+ */
26
+ buildHeaders(payload: Record<string, unknown>): Record<string, string>;
27
+ /**
28
+ * Build WebSocket handshake authentication headers.
29
+ *
30
+ * WebSocket auth uses a time-based nonce (seconds since epoch)
31
+ * as the payload, not a JSON object.
32
+ */
33
+ buildWsHeaders(): Record<string, string>;
34
+ }
@@ -0,0 +1,80 @@
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.GeminiAuth = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const errors_1 = require("../../errors");
9
+ /**
10
+ * Gemini HMAC-SHA384 authentication.
11
+ *
12
+ * Gemini's prediction market API uses header-based auth where the POST body
13
+ * is always empty — the actual payload is base64-encoded and placed in the
14
+ * X-GEMINI-PAYLOAD header, with an HMAC-SHA384 signature.
15
+ */
16
+ class GeminiAuth {
17
+ apiKey;
18
+ apiSecret;
19
+ lastNonce = 0;
20
+ constructor(credentials) {
21
+ if (!credentials.apiKey || !credentials.apiSecret) {
22
+ throw new errors_1.AuthenticationError('Gemini Titan trading requires both apiKey and apiSecret.', 'GeminiTitan');
23
+ }
24
+ this.apiKey = credentials.apiKey;
25
+ this.apiSecret = credentials.apiSecret;
26
+ }
27
+ /**
28
+ * Generate a strictly monotonic nonce.
29
+ * Uses Date.now() but ensures it never repeats even if called
30
+ * within the same millisecond.
31
+ */
32
+ nonce() {
33
+ const now = Date.now();
34
+ this.lastNonce = Math.max(now, this.lastNonce + 1);
35
+ return this.lastNonce;
36
+ }
37
+ /**
38
+ * Build the three authentication headers for a given payload object.
39
+ *
40
+ * The payload must include `request` (endpoint path) and `nonce`.
41
+ * Additional fields (symbol, side, etc.) are included for order requests.
42
+ */
43
+ buildHeaders(payload) {
44
+ const jsonPayload = JSON.stringify(payload);
45
+ const b64Payload = Buffer.from(jsonPayload).toString('base64');
46
+ const signature = crypto_1.default
47
+ .createHmac('sha384', this.apiSecret)
48
+ .update(b64Payload)
49
+ .digest('hex');
50
+ return {
51
+ 'Content-Type': 'text/plain',
52
+ 'Content-Length': '0',
53
+ 'X-GEMINI-APIKEY': this.apiKey,
54
+ 'X-GEMINI-PAYLOAD': b64Payload,
55
+ 'X-GEMINI-SIGNATURE': signature,
56
+ 'Cache-Control': 'no-cache',
57
+ };
58
+ }
59
+ /**
60
+ * Build WebSocket handshake authentication headers.
61
+ *
62
+ * WebSocket auth uses a time-based nonce (seconds since epoch)
63
+ * as the payload, not a JSON object.
64
+ */
65
+ buildWsHeaders() {
66
+ const nonce = Math.floor(Date.now() / 1000).toString();
67
+ const b64Payload = Buffer.from(nonce).toString('base64');
68
+ const signature = crypto_1.default
69
+ .createHmac('sha384', this.apiSecret)
70
+ .update(b64Payload)
71
+ .digest('hex');
72
+ return {
73
+ 'X-GEMINI-APIKEY': this.apiKey,
74
+ 'X-GEMINI-NONCE': nonce,
75
+ 'X-GEMINI-PAYLOAD': b64Payload,
76
+ 'X-GEMINI-SIGNATURE': signature,
77
+ };
78
+ }
79
+ }
80
+ exports.GeminiAuth = GeminiAuth;
@@ -0,0 +1,15 @@
1
+ export declare const DEFAULT_GEMINI_BASE_URL = "https://api.gemini.com";
2
+ export declare const GEMINI_SANDBOX_BASE_URL = "https://api.sandbox.gemini.com";
3
+ export declare const GEMINI_WS_URL = "wss://ws.gemini.com";
4
+ export declare const GEMINI_SANDBOX_WS_URL = "wss://ws.sandbox.gemini.com";
5
+ export declare const INSTRUMENT_PREFIX = "GEMI-";
6
+ export declare const MIN_PRICE = 0.01;
7
+ export declare const MAX_PRICE = 0.99;
8
+ export declare const TICK_SIZE = 0.01;
9
+ export declare const PAYOUT_PER_CONTRACT = 1;
10
+ export interface GeminiApiConfig {
11
+ baseUrl: string;
12
+ wsUrl: string;
13
+ sandbox: boolean;
14
+ }
15
+ export declare function getGeminiConfig(baseUrlOverride?: string, sandbox?: boolean): GeminiApiConfig;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PAYOUT_PER_CONTRACT = exports.TICK_SIZE = exports.MAX_PRICE = exports.MIN_PRICE = exports.INSTRUMENT_PREFIX = exports.GEMINI_SANDBOX_WS_URL = exports.GEMINI_WS_URL = exports.GEMINI_SANDBOX_BASE_URL = exports.DEFAULT_GEMINI_BASE_URL = void 0;
4
+ exports.getGeminiConfig = getGeminiConfig;
5
+ exports.DEFAULT_GEMINI_BASE_URL = 'https://api.gemini.com';
6
+ exports.GEMINI_SANDBOX_BASE_URL = 'https://api.sandbox.gemini.com';
7
+ exports.GEMINI_WS_URL = 'wss://ws.gemini.com';
8
+ exports.GEMINI_SANDBOX_WS_URL = 'wss://ws.sandbox.gemini.com';
9
+ // All Gemini prediction market instrument symbols use this prefix
10
+ exports.INSTRUMENT_PREFIX = 'GEMI-';
11
+ // Gemini prediction market prices are in the range [0.01, 0.99]
12
+ exports.MIN_PRICE = 0.01;
13
+ exports.MAX_PRICE = 0.99;
14
+ exports.TICK_SIZE = 0.01;
15
+ // Payout is always $1.00 per winning contract
16
+ exports.PAYOUT_PER_CONTRACT = 1.0;
17
+ function getGeminiConfig(baseUrlOverride, sandbox) {
18
+ const isSandbox = sandbox ?? false;
19
+ return {
20
+ baseUrl: baseUrlOverride ?? (isSandbox ? exports.GEMINI_SANDBOX_BASE_URL : exports.DEFAULT_GEMINI_BASE_URL),
21
+ wsUrl: isSandbox ? exports.GEMINI_SANDBOX_WS_URL : exports.GEMINI_WS_URL,
22
+ sandbox: isSandbox,
23
+ };
24
+ }
@@ -0,0 +1,20 @@
1
+ import { ErrorMapper } from '../../utils/error-mapper';
2
+ import { BadRequest } from '../../errors';
3
+ /**
4
+ * Maps Gemini Titan API errors to PMXT unified error classes.
5
+ *
6
+ * Gemini returns errors as JSON:
7
+ * { result: "error", reason: "InvalidSignature", message: "..." }
8
+ *
9
+ * Common reasons:
10
+ * - InvalidSignature -> AuthenticationError
11
+ * - InsufficientFunds -> InsufficientFunds
12
+ * - InvalidQuantity, InvalidPrice, MarketNotOpen -> InvalidOrder
13
+ */
14
+ export declare class GeminiErrorMapper extends ErrorMapper {
15
+ constructor();
16
+ protected extractErrorMessage(error: any): string;
17
+ protected mapBadRequestError(message: string, data: any): BadRequest;
18
+ mapError(error: any): ReturnType<ErrorMapper['mapError']>;
19
+ }
20
+ export declare const geminiErrorMapper: GeminiErrorMapper;
@@ -0,0 +1,75 @@
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.geminiErrorMapper = exports.GeminiErrorMapper = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const error_mapper_1 = require("../../utils/error-mapper");
9
+ const errors_1 = require("../../errors");
10
+ /**
11
+ * Maps Gemini Titan API errors to PMXT unified error classes.
12
+ *
13
+ * Gemini returns errors as JSON:
14
+ * { result: "error", reason: "InvalidSignature", message: "..." }
15
+ *
16
+ * Common reasons:
17
+ * - InvalidSignature -> AuthenticationError
18
+ * - InsufficientFunds -> InsufficientFunds
19
+ * - InvalidQuantity, InvalidPrice, MarketNotOpen -> InvalidOrder
20
+ */
21
+ class GeminiErrorMapper extends error_mapper_1.ErrorMapper {
22
+ constructor() {
23
+ super('GeminiTitan');
24
+ }
25
+ extractErrorMessage(error) {
26
+ if (axios_1.default.isAxiosError(error) && error.response?.data) {
27
+ const data = error.response.data;
28
+ if (typeof data === 'string') {
29
+ return `[${error.response.status}] ${data}`;
30
+ }
31
+ if (data.message) {
32
+ return `[${error.response.status}] ${data.message}`;
33
+ }
34
+ if (data.reason) {
35
+ return `[${error.response.status}] ${data.reason}`;
36
+ }
37
+ }
38
+ return super.extractErrorMessage(error);
39
+ }
40
+ mapBadRequestError(message, data) {
41
+ const reason = typeof data === 'object' && data?.reason
42
+ ? String(data.reason)
43
+ : '';
44
+ const lowerReason = reason.toLowerCase();
45
+ const lowerMessage = message.toLowerCase();
46
+ if (lowerReason.includes('insufficientfunds') || lowerMessage.includes('insufficient')) {
47
+ return new errors_1.InsufficientFunds(message, this.exchangeName);
48
+ }
49
+ if (lowerReason.includes('invalidquantity') ||
50
+ lowerReason.includes('invalidprice') ||
51
+ lowerReason.includes('limitpriceofftick') ||
52
+ lowerReason.includes('invalidstopprice') ||
53
+ lowerReason.includes('marketnotopen') ||
54
+ lowerReason.includes('unknowninstrument') ||
55
+ lowerReason.includes('duplicateorder')) {
56
+ return new errors_1.InvalidOrder(message, this.exchangeName);
57
+ }
58
+ if (lowerReason.includes('invalidsignature') ||
59
+ lowerReason.includes('invalidapikey') ||
60
+ lowerMessage.includes('terms_not_accepted')) {
61
+ return new errors_1.AuthenticationError(message, this.exchangeName);
62
+ }
63
+ return super.mapBadRequestError(message, data);
64
+ }
65
+ mapError(error) {
66
+ if (axios_1.default.isAxiosError(error) && error.response?.status === 429) {
67
+ const retryAfter = error.response.headers?.['retry-after'];
68
+ const retryAfterSeconds = retryAfter ? parseInt(retryAfter, 10) : undefined;
69
+ return new errors_1.RateLimitExceeded(this.extractErrorMessage(error), retryAfterSeconds, this.exchangeName);
70
+ }
71
+ return super.mapError(error);
72
+ }
73
+ }
74
+ exports.GeminiErrorMapper = GeminiErrorMapper;
75
+ exports.geminiErrorMapper = new GeminiErrorMapper();
@@ -0,0 +1,26 @@
1
+ import { MarketFilterParams, EventFetchParams } from '../../BaseExchange';
2
+ import { IExchangeFetcher, FetcherContext } from '../interfaces';
3
+ import { GeminiAuth } from './auth';
4
+ import { GeminiRawEvent, GeminiRawOrder, GeminiRawPosition, GeminiRawOrderBook } from './types';
5
+ export declare class GeminiFetcher implements IExchangeFetcher<GeminiRawEvent, GeminiRawEvent> {
6
+ private readonly ctx;
7
+ private readonly baseUrl;
8
+ private readonly auth;
9
+ private symbolToEventTicker;
10
+ constructor(ctx: FetcherContext, baseUrl: string, auth?: GeminiAuth);
11
+ fetchRawMarkets(params?: MarketFilterParams): Promise<GeminiRawEvent[]>;
12
+ fetchRawEvents(params: EventFetchParams): Promise<GeminiRawEvent[]>;
13
+ fetchRawSingleEvent(eventTicker: string): Promise<GeminiRawEvent>;
14
+ fetchRawOrderBook(instrumentSymbol: string): Promise<GeminiRawOrderBook | undefined>;
15
+ getEventTickerForSymbol(instrumentSymbol: string): string | undefined;
16
+ submitRawOrder(payload: Record<string, unknown>): Promise<GeminiRawOrder>;
17
+ cancelRawOrder(orderId: number): Promise<{
18
+ result: string;
19
+ message: string;
20
+ }>;
21
+ fetchRawActiveOrders(symbol?: string): Promise<GeminiRawOrder[]>;
22
+ fetchRawOrderHistory(): Promise<GeminiRawOrder[]>;
23
+ fetchRawPositions(): Promise<GeminiRawPosition[]>;
24
+ private get;
25
+ private postAuthenticated;
26
+ }
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GeminiFetcher = void 0;
4
+ const errors_1 = require("./errors");
5
+ // ----------------------------------------------------------------------------
6
+ // Fetcher
7
+ // ----------------------------------------------------------------------------
8
+ class GeminiFetcher {
9
+ ctx;
10
+ baseUrl;
11
+ auth;
12
+ // Index mapping instrumentSymbol -> eventTicker, built during fetchRawEvents
13
+ symbolToEventTicker = new Map();
14
+ constructor(ctx, baseUrl, auth) {
15
+ this.ctx = ctx;
16
+ this.baseUrl = baseUrl;
17
+ this.auth = auth;
18
+ }
19
+ // -- Public data -----------------------------------------------------------
20
+ async fetchRawMarkets(params) {
21
+ return this.fetchRawEvents(params ?? {});
22
+ }
23
+ async fetchRawEvents(params) {
24
+ const allEvents = [];
25
+ const pageSize = 500;
26
+ let offset = params.offset ?? 0;
27
+ const maxResults = params.limit ?? 250000;
28
+ while (allEvents.length < maxResults) {
29
+ const queryParams = {
30
+ limit: String(Math.min(pageSize, maxResults - allEvents.length)),
31
+ offset: String(offset),
32
+ };
33
+ if (params.status && params.status !== 'all') {
34
+ queryParams.status = params.status === 'active' ? 'active' : params.status;
35
+ }
36
+ else if (!params.status) {
37
+ queryParams.status = 'active';
38
+ }
39
+ if (params.category) {
40
+ queryParams.category = params.category;
41
+ }
42
+ if (params.query) {
43
+ queryParams.search = params.query;
44
+ }
45
+ const response = await this.get('/v1/prediction-markets/events', queryParams);
46
+ const events = response.data;
47
+ if (events.length === 0)
48
+ break;
49
+ // Build the instrumentSymbol -> eventTicker index
50
+ for (const event of events) {
51
+ for (const contract of event.contracts) {
52
+ this.symbolToEventTicker.set(contract.instrumentSymbol, event.ticker);
53
+ }
54
+ }
55
+ allEvents.push(...events);
56
+ // Check if we've fetched all available
57
+ if (allEvents.length >= response.pagination.total)
58
+ break;
59
+ offset += events.length;
60
+ }
61
+ return allEvents;
62
+ }
63
+ async fetchRawSingleEvent(eventTicker) {
64
+ return this.get(`/v1/prediction-markets/events/${encodeURIComponent(eventTicker)}`);
65
+ }
66
+ async fetchRawOrderBook(instrumentSymbol) {
67
+ const eventTicker = this.getEventTickerForSymbol(instrumentSymbol);
68
+ if (!eventTicker) {
69
+ throw new Error(`Cannot fetch order book: no event ticker found for ${instrumentSymbol}. ` +
70
+ 'Call fetchMarkets first to build the symbol index.');
71
+ }
72
+ const event = await this.fetchRawSingleEvent(eventTicker);
73
+ // The REST API does not expose a full order book — construct a
74
+ // single-level book from the contract's best bid/ask prices.
75
+ // Full depth is only available via the WebSocket @depth streams.
76
+ const contract = event.contracts.find(c => c.instrumentSymbol === instrumentSymbol);
77
+ if (!contract?.prices)
78
+ return undefined;
79
+ const { bestBid, bestAsk } = contract.prices;
80
+ const bids = bestBid
81
+ ? [{ price: bestBid, size: '0' }]
82
+ : [];
83
+ const asks = bestAsk
84
+ ? [{ price: bestAsk, size: '0' }]
85
+ : [];
86
+ return { bids, asks, timestamp: Date.now() };
87
+ }
88
+ getEventTickerForSymbol(instrumentSymbol) {
89
+ return this.symbolToEventTicker.get(instrumentSymbol);
90
+ }
91
+ // -- Authenticated endpoints -----------------------------------------------
92
+ async submitRawOrder(payload) {
93
+ return this.postAuthenticated('/v1/prediction-markets/order', payload);
94
+ }
95
+ async cancelRawOrder(orderId) {
96
+ return this.postAuthenticated('/v1/prediction-markets/order/cancel', { orderId });
97
+ }
98
+ async fetchRawActiveOrders(symbol) {
99
+ const extra = {};
100
+ if (symbol)
101
+ extra.symbol = symbol;
102
+ const response = await this.postAuthenticated('/v1/prediction-markets/orders/active', extra);
103
+ return response.orders;
104
+ }
105
+ async fetchRawOrderHistory() {
106
+ const response = await this.postAuthenticated('/v1/prediction-markets/orders/history', {});
107
+ return Array.isArray(response) ? response : [];
108
+ }
109
+ async fetchRawPositions() {
110
+ const response = await this.postAuthenticated('/v1/prediction-markets/positions', {});
111
+ return response.positions;
112
+ }
113
+ // -- HTTP helpers ----------------------------------------------------------
114
+ async get(path, params) {
115
+ try {
116
+ const url = new URL(path, this.baseUrl);
117
+ if (params) {
118
+ for (const [key, value] of Object.entries(params)) {
119
+ url.searchParams.set(key, value);
120
+ }
121
+ }
122
+ const response = await this.ctx.http.get(url.toString());
123
+ return response.data;
124
+ }
125
+ catch (error) {
126
+ throw errors_1.geminiErrorMapper.mapError(error);
127
+ }
128
+ }
129
+ async postAuthenticated(path, extraFields) {
130
+ if (!this.auth) {
131
+ throw new Error('Authentication required. Provide apiKey and apiSecret.');
132
+ }
133
+ const payload = {
134
+ request: path,
135
+ nonce: this.auth.nonce(),
136
+ ...extraFields,
137
+ };
138
+ const headers = this.auth.buildHeaders(payload);
139
+ try {
140
+ const response = await this.ctx.http.post(`${this.baseUrl}${path}`, {}, { headers });
141
+ return response.data;
142
+ }
143
+ catch (error) {
144
+ throw errors_1.geminiErrorMapper.mapError(error);
145
+ }
146
+ }
147
+ }
148
+ exports.GeminiFetcher = GeminiFetcher;
@@ -0,0 +1,31 @@
1
+ import { PredictionMarketExchange, MarketFilterParams, EventFetchParams, ExchangeCredentials } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, OrderBook, Trade, Order, Position, CreateOrderParams, BuiltOrder } from '../../types';
3
+ export interface GeminiTitanExchangeOptions {
4
+ credentials?: ExchangeCredentials;
5
+ sandbox?: boolean;
6
+ }
7
+ export declare class GeminiTitanExchange extends PredictionMarketExchange {
8
+ private readonly config;
9
+ private readonly fetcher;
10
+ private readonly normalizer;
11
+ private readonly geminiAuth?;
12
+ private geminiWs?;
13
+ constructor(credentials?: ExchangeCredentials | GeminiTitanExchangeOptions);
14
+ get name(): string;
15
+ private requireAuth;
16
+ protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
17
+ protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
18
+ fetchOrderBook(outcomeId: string): Promise<OrderBook>;
19
+ buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
20
+ submitOrder(built: BuiltOrder): Promise<Order>;
21
+ createOrder(params: CreateOrderParams): Promise<Order>;
22
+ cancelOrder(orderId: string): Promise<Order>;
23
+ fetchOpenOrders(marketId?: string): Promise<Order[]>;
24
+ fetchClosedOrders(): Promise<Order[]>;
25
+ fetchAllOrders(): Promise<Order[]>;
26
+ fetchPositions(): Promise<Position[]>;
27
+ private ensureWebSocket;
28
+ watchOrderBook(outcomeId: string): Promise<OrderBook>;
29
+ watchTrades(outcomeId: string): Promise<Trade[]>;
30
+ close(): Promise<void>;
31
+ }