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
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const price_1 = require("./price");
4
- describe("resolveMyriadPrice", () => {
5
- test("divides value by shares", () => {
6
- expect((0, price_1.resolveMyriadPrice)({ value: 100, shares: 4 })).toBe(25);
7
- });
8
- test("treats missing shares as 1", () => {
9
- expect((0, price_1.resolveMyriadPrice)({ value: 50 })).toBe(50);
10
- });
11
- test("treats zero shares as 1 to avoid division by zero", () => {
12
- expect((0, price_1.resolveMyriadPrice)({ value: 80, shares: 0 })).toBe(80);
13
- });
14
- test("treats missing value as 0", () => {
15
- expect((0, price_1.resolveMyriadPrice)({ shares: 5 })).toBe(0);
16
- });
17
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_1 = require("./errors");
4
- const polymarket_us_1 = require("polymarket-us");
5
- const errors_2 = require("../../errors");
6
- describe('PolymarketUSErrorMapper', () => {
7
- const mapper = new errors_1.PolymarketUSErrorMapper();
8
- test('maps SDK AuthenticationError -> PMXT AuthenticationError', () => {
9
- const mapped = mapper.mapError(new polymarket_us_1.AuthenticationError('bad token'));
10
- expect(mapped).toBeInstanceOf(errors_2.AuthenticationError);
11
- expect(mapped.exchange).toBe('PolymarketUS');
12
- });
13
- test('maps BadRequestError("Insufficient buying power") -> InsufficientFunds', () => {
14
- const mapped = mapper.mapError(new polymarket_us_1.BadRequestError('Insufficient buying power'));
15
- expect(mapped).toBeInstanceOf(errors_2.InsufficientFunds);
16
- });
17
- test('maps BadRequestError("Invalid price tick") -> InvalidOrder', () => {
18
- const mapped = mapper.mapError(new polymarket_us_1.BadRequestError('Invalid price tick'));
19
- expect(mapped).toBeInstanceOf(errors_2.InvalidOrder);
20
- });
21
- test('maps BadRequestError("Self-match prevented") -> InvalidOrder', () => {
22
- const mapped = mapper.mapError(new polymarket_us_1.BadRequestError('Self-match prevented'));
23
- expect(mapped).toBeInstanceOf(errors_2.InvalidOrder);
24
- });
25
- test('maps NotFoundError("Order not found") -> OrderNotFound', () => {
26
- const mapped = mapper.mapError(new polymarket_us_1.NotFoundError('Order not found'));
27
- expect(mapped).toBeInstanceOf(errors_2.OrderNotFound);
28
- });
29
- test('maps NotFoundError("Market not found") -> MarketNotFound', () => {
30
- const mapped = mapper.mapError(new polymarket_us_1.NotFoundError('Market not found'));
31
- expect(mapped).toBeInstanceOf(errors_2.MarketNotFound);
32
- });
33
- test('maps RateLimitError -> RateLimitExceeded', () => {
34
- const mapped = mapper.mapError(new polymarket_us_1.RateLimitError('slow down'));
35
- expect(mapped).toBeInstanceOf(errors_2.RateLimitExceeded);
36
- });
37
- test('maps InternalServerError -> ExchangeNotAvailable', () => {
38
- const mapped = mapper.mapError(new polymarket_us_1.InternalServerError('boom'));
39
- expect(mapped).toBeInstanceOf(errors_2.ExchangeNotAvailable);
40
- });
41
- test('maps generic Error -> generic exchange BaseError', () => {
42
- const mapped = mapper.mapError(new Error('something failed'));
43
- expect(mapped).toBeInstanceOf(errors_2.BaseError);
44
- expect(mapped.exchange).toBe('PolymarketUS');
45
- expect(mapped.code).toBe('EXCHANGE_ERROR');
46
- expect(mapped.message).toBe('something failed');
47
- });
48
- test('maps plain object -> generic exchange BaseError', () => {
49
- const mapped = mapper.mapError({ message: 'plain object error' });
50
- expect(mapped).toBeInstanceOf(errors_2.BaseError);
51
- expect(mapped.exchange).toBe('PolymarketUS');
52
- expect(mapped.message).toBe('plain object error');
53
- });
54
- });
@@ -1,8 +0,0 @@
1
- /**
2
- * Integration tests for PolymarketUSExchange.
3
- *
4
- * The `polymarket-us` SDK is fully mocked here so the tests run without
5
- * touching the network. Each test instantiates a fresh exchange (and
6
- * therefore a fresh mock client) so per-test mock setup is independent.
7
- */
8
- export {};
@@ -1,237 +0,0 @@
1
- "use strict";
2
- /**
3
- * Integration tests for PolymarketUSExchange.
4
- *
5
- * The `polymarket-us` SDK is fully mocked here so the tests run without
6
- * touching the network. Each test instantiates a fresh exchange (and
7
- * therefore a fresh mock client) so per-test mock setup is independent.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- jest.mock('polymarket-us', () => {
11
- class PolymarketUSError extends Error {
12
- }
13
- class APIError extends PolymarketUSError {
14
- status;
15
- constructor(status, message) {
16
- super(message);
17
- this.status = status;
18
- }
19
- }
20
- class AuthenticationError extends APIError {
21
- constructor(m = 'auth') { super(401, m); }
22
- }
23
- class BadRequestError extends APIError {
24
- constructor(m = 'bad request') { super(400, m); }
25
- }
26
- class NotFoundError extends APIError {
27
- constructor(m = 'not found') { super(404, m); }
28
- }
29
- class RateLimitError extends APIError {
30
- constructor(m = 'rate limit') { super(429, m); }
31
- }
32
- class InternalServerError extends APIError {
33
- constructor(m = 'server') { super(500, m); }
34
- }
35
- const PolymarketUS = jest.fn().mockImplementation(() => ({
36
- markets: {
37
- list: jest.fn(),
38
- book: jest.fn(),
39
- retrieveBySlug: jest.fn(),
40
- },
41
- events: {
42
- list: jest.fn(),
43
- retrieveBySlug: jest.fn(),
44
- },
45
- orders: {
46
- list: jest.fn(),
47
- retrieve: jest.fn(),
48
- create: jest.fn(),
49
- cancel: jest.fn(),
50
- },
51
- portfolio: {
52
- positions: jest.fn(),
53
- activities: jest.fn(),
54
- },
55
- account: {
56
- balances: jest.fn(),
57
- },
58
- }));
59
- return {
60
- PolymarketUS,
61
- PolymarketUSError,
62
- APIError,
63
- AuthenticationError,
64
- BadRequestError,
65
- NotFoundError,
66
- RateLimitError,
67
- InternalServerError,
68
- };
69
- });
70
- const index_1 = require("./index");
71
- const errors_1 = require("../../errors");
72
- const polymarket_us_1 = require("polymarket-us");
73
- const CREDS = { apiKey: 'k1', privateKey: 's1' };
74
- function getClient(exchange) {
75
- return exchange.client;
76
- }
77
- function makeMarketDetail(slug = 'btc-100k') {
78
- return {
79
- id: 1,
80
- slug,
81
- title: `Market ${slug}`,
82
- outcome: 'binary',
83
- description: 'desc',
84
- active: true,
85
- closed: false,
86
- liquidity: 100,
87
- volume: 1000,
88
- eventSlug: 'evt-1',
89
- };
90
- }
91
- function makeSdkOrder(overrides = {}) {
92
- return {
93
- id: 'order-1',
94
- marketSlug: 'btc-100k',
95
- side: 'ORDER_SIDE_BUY',
96
- type: 'ORDER_TYPE_LIMIT',
97
- price: { value: '0.55', currency: 'USD' },
98
- quantity: 10,
99
- cumQuantity: 0,
100
- leavesQuantity: 10,
101
- tif: 'TIME_IN_FORCE_GOOD_TILL_CANCEL',
102
- intent: 'ORDER_INTENT_BUY_LONG',
103
- state: 'ORDER_STATE_NEW',
104
- ...overrides,
105
- };
106
- }
107
- describe('PolymarketUSExchange', () => {
108
- describe('construction', () => {
109
- it('constructs without credentials but rejects auth-required calls', async () => {
110
- const ex = new index_1.PolymarketUSExchange();
111
- await expect(ex.fetchBalance()).rejects.toBeInstanceOf(errors_1.AuthenticationError);
112
- });
113
- it('constructs with credentials and exposes the unified name', () => {
114
- const ex = new index_1.PolymarketUSExchange(CREDS);
115
- expect(ex.name).toBe('PolymarketUS');
116
- });
117
- });
118
- describe('fetchMarkets', () => {
119
- it('returns normalized markets from markets.list', async () => {
120
- const ex = new index_1.PolymarketUSExchange(CREDS);
121
- const client = getClient(ex);
122
- client.markets.list.mockResolvedValue({
123
- markets: [makeMarketDetail('btc-100k'), makeMarketDetail('eth-5k')],
124
- });
125
- const result = await ex.fetchMarkets();
126
- expect(client.markets.list).toHaveBeenCalledWith({
127
- active: true,
128
- limit: 250,
129
- offset: 0,
130
- });
131
- expect(result).toHaveLength(2);
132
- expect(result[0].marketId).toBe('btc-100k');
133
- expect(result[1].marketId).toBe('eth-5k');
134
- });
135
- it('uses retrieveBySlug for direct slug lookup', async () => {
136
- const ex = new index_1.PolymarketUSExchange(CREDS);
137
- const client = getClient(ex);
138
- client.markets.retrieveBySlug.mockResolvedValue({
139
- market: makeMarketDetail('btc-100k'),
140
- });
141
- const result = await ex.fetchMarkets({ slug: 'btc-100k' });
142
- expect(client.markets.retrieveBySlug).toHaveBeenCalledWith('btc-100k');
143
- expect(result).toHaveLength(1);
144
- expect(result[0].marketId).toBe('btc-100k');
145
- });
146
- });
147
- describe('fetchOrderBook', () => {
148
- it('normalizes a market book', async () => {
149
- const ex = new index_1.PolymarketUSExchange(CREDS);
150
- const client = getClient(ex);
151
- client.markets.book.mockResolvedValue({
152
- marketSlug: 'btc-100k',
153
- bids: [{ px: { value: '0.55', currency: 'USD' }, qty: '10' }],
154
- offers: [{ px: { value: '0.57', currency: 'USD' }, qty: '5' }],
155
- state: 'MARKET_STATE_OPEN',
156
- transactTime: '2024-01-01T00:00:00Z',
157
- });
158
- const book = await ex.fetchOrderBook('btc-100k');
159
- expect(client.markets.book).toHaveBeenCalledWith('btc-100k');
160
- expect(book.bids).toEqual([{ price: 0.55, size: 10 }]);
161
- expect(book.asks).toEqual([{ price: 0.57, size: 5 }]);
162
- });
163
- });
164
- describe('buildOrder', () => {
165
- it('builds a BUY LONG limit order at the user price', async () => {
166
- const ex = new index_1.PolymarketUSExchange(CREDS);
167
- const built = await ex.buildOrder({
168
- marketId: 'btc-100k',
169
- outcomeId: 'btc-100k:long',
170
- side: 'buy',
171
- type: 'limit',
172
- amount: 10,
173
- price: 0.55,
174
- });
175
- const raw = built.raw;
176
- expect(raw.intent).toBe('ORDER_INTENT_BUY_LONG');
177
- expect(raw.type).toBe('ORDER_TYPE_LIMIT');
178
- expect(raw.marketSlug).toBe('btc-100k');
179
- expect(raw.quantity).toBe(10);
180
- expect(raw.price.value).toBe('0.550');
181
- });
182
- it('builds a BUY SHORT limit order using long-side price conversion', async () => {
183
- const ex = new index_1.PolymarketUSExchange(CREDS);
184
- const built = await ex.buildOrder({
185
- marketId: 'btc-100k',
186
- outcomeId: 'btc-100k:short',
187
- side: 'buy',
188
- type: 'limit',
189
- amount: 10,
190
- price: 0.40,
191
- });
192
- const raw = built.raw;
193
- expect(raw.intent).toBe('ORDER_INTENT_BUY_SHORT');
194
- expect(raw.price.value).toBe('0.600');
195
- });
196
- });
197
- describe('cancelOrder', () => {
198
- it('uses cached marketSlug when available', async () => {
199
- const ex = new index_1.PolymarketUSExchange(CREDS);
200
- const client = getClient(ex);
201
- // Pre-populate cache by listing open orders
202
- client.orders.list.mockResolvedValue({
203
- orders: [makeSdkOrder({ id: 'order-123', marketSlug: 'btc-100k' })],
204
- });
205
- await ex.fetchOpenOrders();
206
- client.orders.cancel.mockResolvedValue(undefined);
207
- client.orders.retrieve.mockResolvedValue({
208
- order: makeSdkOrder({ id: 'order-123', state: 'ORDER_STATE_CANCELED' }),
209
- });
210
- await ex.cancelOrder('order-123');
211
- expect(client.orders.cancel).toHaveBeenCalledWith('order-123', {
212
- marketSlug: 'btc-100k',
213
- });
214
- });
215
- it('fetches the order first when slug is not cached', async () => {
216
- const ex = new index_1.PolymarketUSExchange(CREDS);
217
- const client = getClient(ex);
218
- client.orders.retrieve.mockResolvedValue({
219
- order: makeSdkOrder({ id: 'order-456', marketSlug: 'eth-5k' }),
220
- });
221
- client.orders.cancel.mockResolvedValue(undefined);
222
- await ex.cancelOrder('order-456');
223
- expect(client.orders.retrieve).toHaveBeenCalledWith('order-456');
224
- expect(client.orders.cancel).toHaveBeenCalledWith('order-456', {
225
- marketSlug: 'eth-5k',
226
- });
227
- });
228
- });
229
- describe('error mapping', () => {
230
- it('translates SDK AuthenticationError into PMXT AuthenticationError', async () => {
231
- const ex = new index_1.PolymarketUSExchange(CREDS);
232
- const client = getClient(ex);
233
- client.account.balances.mockRejectedValue(new polymarket_us_1.AuthenticationError('bad creds'));
234
- await expect(ex.fetchBalance()).rejects.toBeInstanceOf(errors_1.AuthenticationError);
235
- });
236
- });
237
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,224 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- // Mock the price helpers so this test does not depend on price.ts being
4
- // implemented yet (it is created in parallel by another agent).
5
- jest.mock('./price', () => ({
6
- fromAmount: (a) => a ? parseFloat(a.value) : 0,
7
- fromLongSidePrice: (intent, longPrice) => intent.endsWith('SHORT') ? 1 - longPrice : longPrice,
8
- toAmount: (p) => ({ value: p.toFixed(2), currency: 'USD' }),
9
- }));
10
- const normalizer_1 = require("./normalizer");
11
- function makeOrder(overrides = {}) {
12
- return {
13
- id: 'order-1',
14
- marketSlug: 'btc-100k',
15
- side: 'ORDER_SIDE_BUY',
16
- type: 'ORDER_TYPE_LIMIT',
17
- price: { value: '0.55', currency: 'USD' },
18
- quantity: 10,
19
- cumQuantity: 0,
20
- leavesQuantity: 10,
21
- tif: 'TIME_IN_FORCE_GOOD_TILL_CANCEL',
22
- intent: 'ORDER_INTENT_BUY_LONG',
23
- state: 'ORDER_STATE_NEW',
24
- createTime: '2026-01-01T00:00:00.000Z',
25
- ...overrides,
26
- };
27
- }
28
- describe('PolymarketUSNormalizer', () => {
29
- const normalizer = new normalizer_1.PolymarketUSNormalizer();
30
- describe('normalizeMarket', () => {
31
- it('uses the slug as marketId and synthesizes long/short outcomes', () => {
32
- const detail = {
33
- id: 1,
34
- slug: 'btc-100k',
35
- title: 'BTC reaches $100k',
36
- outcome: 'yes',
37
- description: 'Will BTC hit 100k by year end?',
38
- active: true,
39
- closed: false,
40
- liquidity: 5000,
41
- volume: 12345,
42
- eventSlug: 'btc-events',
43
- };
44
- const um = normalizer.normalizeMarket(detail);
45
- expect(um.marketId).toBe('btc-100k');
46
- expect(um.slug).toBe('btc-100k');
47
- expect(um.title).toBe('BTC reaches $100k');
48
- expect(um.description).toBe('Will BTC hit 100k by year end?');
49
- expect(um.outcomes).toHaveLength(2);
50
- expect(um.outcomes[0].outcomeId).toBe('btc-100k:long');
51
- expect(um.outcomes[0].label).toBe('long');
52
- expect(um.outcomes[1].outcomeId).toBe('btc-100k:short');
53
- expect(um.outcomes[1].label).toBe('short');
54
- expect(um.liquidity).toBe(5000);
55
- expect(um.volume).toBe(12345);
56
- });
57
- it('populates outcome prices from marketSides[].price and tickSize from orderPriceMinTickSize', () => {
58
- const detail = {
59
- slug: 'nfl-sf-at-kc',
60
- question: 'SF at KC',
61
- marketSides: [
62
- { description: 'Kansas City Chiefs', long: true, price: '0.864' },
63
- { description: 'San Francisco 49ers', long: false, price: '0.136' },
64
- ],
65
- orderPriceMinTickSize: 0.001,
66
- };
67
- const um = normalizer.normalizeMarket(detail);
68
- expect(um.outcomes[0].price).toBeCloseTo(0.864);
69
- expect(um.outcomes[0].metadata?.sideDescription).toBe('Kansas City Chiefs');
70
- expect(um.outcomes[1].price).toBeCloseTo(0.136);
71
- expect(um.outcomes[1].metadata?.sideDescription).toBe('San Francisco 49ers');
72
- expect(um.tickSize).toBe(0.001);
73
- });
74
- it('derives the short-side price from 1 - longPrice when only the long side is quoted', () => {
75
- const detail = {
76
- slug: 'btc-100k',
77
- question: 'BTC 100k',
78
- marketSides: [
79
- { long: true, price: '0.72' },
80
- { long: false },
81
- ],
82
- };
83
- const um = normalizer.normalizeMarket(detail);
84
- expect(um.outcomes[0].price).toBeCloseTo(0.72);
85
- expect(um.outcomes[1].price).toBeCloseTo(0.28);
86
- });
87
- it('falls back to outcomePrices[] when marketSides is missing', () => {
88
- const detail = {
89
- slug: 'eth-5k',
90
- question: 'ETH 5k',
91
- outcomePrices: ['0.30', '0.70'],
92
- };
93
- const um = normalizer.normalizeMarket(detail);
94
- expect(um.outcomes[0].price).toBeCloseTo(0.30);
95
- expect(um.outcomes[1].price).toBeCloseTo(0.70);
96
- });
97
- });
98
- describe('normalizeOrderBook', () => {
99
- it('preserves bid/offer levels with parsed numeric prices', () => {
100
- const book = {
101
- marketSlug: 'btc-100k',
102
- bids: [
103
- { px: { value: '0.55', currency: 'USD' }, qty: '100' },
104
- { px: { value: '0.54', currency: 'USD' }, qty: '50' },
105
- ],
106
- offers: [
107
- { px: { value: '0.56', currency: 'USD' }, qty: '75' },
108
- { px: { value: '0.57', currency: 'USD' }, qty: '25' },
109
- ],
110
- state: 'MARKET_STATE_OPEN',
111
- };
112
- const ob = normalizer.normalizeOrderBook(book, 'btc-100k');
113
- expect(ob.bids).toEqual([
114
- { price: 0.55, size: 100 },
115
- { price: 0.54, size: 50 },
116
- ]);
117
- expect(ob.asks).toEqual([
118
- { price: 0.56, size: 75 },
119
- { price: 0.57, size: 25 },
120
- ]);
121
- expect(typeof ob.timestamp).toBe('number');
122
- });
123
- });
124
- describe('normalizeOrder - intent mapping', () => {
125
- const cases = [
126
- { intent: 'ORDER_INTENT_BUY_LONG', expectedSide: 'buy', expectedOutcomeSuffix: ':long' },
127
- { intent: 'ORDER_INTENT_SELL_LONG', expectedSide: 'sell', expectedOutcomeSuffix: ':long' },
128
- { intent: 'ORDER_INTENT_BUY_SHORT', expectedSide: 'buy', expectedOutcomeSuffix: ':short' },
129
- { intent: 'ORDER_INTENT_SELL_SHORT', expectedSide: 'sell', expectedOutcomeSuffix: ':short' },
130
- ];
131
- for (const { intent, expectedSide, expectedOutcomeSuffix } of cases) {
132
- it(`maps ${intent} to side=${expectedSide} and outcomeId ending in ${expectedOutcomeSuffix}`, () => {
133
- const order = normalizer.normalizeOrder(makeOrder({ intent }));
134
- expect(order.side).toBe(expectedSide);
135
- expect(order.outcomeId.endsWith(expectedOutcomeSuffix)).toBe(true);
136
- expect(order.marketId).toBe('btc-100k');
137
- });
138
- }
139
- it('keeps long-side price unchanged for BUY_LONG', () => {
140
- const order = normalizer.normalizeOrder(makeOrder({
141
- intent: 'ORDER_INTENT_BUY_LONG',
142
- price: { value: '0.60', currency: 'USD' },
143
- }));
144
- expect(order.price).toBeCloseTo(0.60);
145
- });
146
- it('flips long-side price to short-side for BUY_SHORT', () => {
147
- const order = normalizer.normalizeOrder(makeOrder({
148
- intent: 'ORDER_INTENT_BUY_SHORT',
149
- price: { value: '0.60', currency: 'USD' },
150
- }));
151
- expect(order.price).toBeCloseTo(0.40);
152
- });
153
- });
154
- describe('normalizeOrder - state mapping', () => {
155
- const cases = [
156
- { state: 'ORDER_STATE_NEW', expected: 'open' },
157
- { state: 'ORDER_STATE_PARTIALLY_FILLED', expected: 'open' },
158
- { state: 'ORDER_STATE_FILLED', expected: 'filled' },
159
- { state: 'ORDER_STATE_CANCELED', expected: 'cancelled' },
160
- { state: 'ORDER_STATE_REJECTED', expected: 'rejected' },
161
- { state: 'ORDER_STATE_EXPIRED', expected: 'cancelled' },
162
- ];
163
- for (const { state, expected } of cases) {
164
- it(`maps ${state} to ${expected}`, () => {
165
- const order = normalizer.normalizeOrder(makeOrder({ state }));
166
- expect(order.status).toBe(expected);
167
- });
168
- }
169
- });
170
- describe('normalizePositions', () => {
171
- it('maps positive netPosition to long and negative to short with absolute sizes', () => {
172
- const positions = {
173
- 'btc-100k': {
174
- netPosition: '15',
175
- qtyBought: '20',
176
- qtySold: '5',
177
- cost: { value: '7.50', currency: 'USD' },
178
- realized: { value: '0.00', currency: 'USD' },
179
- bodPosition: '0',
180
- expired: false,
181
- },
182
- 'eth-5k': {
183
- netPosition: '-8',
184
- qtyBought: '2',
185
- qtySold: '10',
186
- cost: { value: '4.00', currency: 'USD' },
187
- realized: { value: '1.25', currency: 'USD' },
188
- bodPosition: '0',
189
- expired: false,
190
- },
191
- };
192
- const result = normalizer.normalizePositions(positions);
193
- expect(result).toHaveLength(2);
194
- const btc = result.find(p => p.marketId === 'btc-100k');
195
- expect(btc.outcomeId).toBe('btc-100k:long');
196
- expect(btc.outcomeLabel).toBe('long');
197
- expect(btc.size).toBe(15);
198
- expect(btc.entryPrice).toBeCloseTo(0.5);
199
- const eth = result.find(p => p.marketId === 'eth-5k');
200
- expect(eth.outcomeId).toBe('eth-5k:short');
201
- expect(eth.outcomeLabel).toBe('short');
202
- expect(eth.size).toBe(8);
203
- expect(eth.entryPrice).toBeCloseTo(0.5);
204
- expect(eth.realizedPnL).toBeCloseTo(1.25);
205
- });
206
- });
207
- describe('normalizeBalance', () => {
208
- it('maps current/buyingPower to total/available/locked', () => {
209
- const balance = {
210
- currentBalance: 1000,
211
- currency: 'USD',
212
- buyingPower: 800,
213
- };
214
- const result = normalizer.normalizeBalance(balance);
215
- expect(result).toHaveLength(1);
216
- expect(result[0]).toEqual({
217
- currency: 'USD',
218
- total: 1000,
219
- available: 800,
220
- locked: 200,
221
- });
222
- });
223
- });
224
- });
@@ -1 +0,0 @@
1
- export {};