pmxtjs 0.1.0 → 0.1.2

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 (60) hide show
  1. package/{src/BaseExchange.ts → dist/BaseExchange.d.ts} +4 -22
  2. package/dist/BaseExchange.js +30 -0
  3. package/dist/exchanges/Kalshi.d.ts +20 -0
  4. package/{src/exchanges/Kalshi.ts → dist/exchanges/Kalshi.js} +75 -114
  5. package/dist/exchanges/Polymarket.d.ts +38 -0
  6. package/{src/exchanges/Polymarket.ts → dist/exchanges/Polymarket.js} +105 -146
  7. package/{src/index.ts → dist/index.d.ts} +0 -1
  8. package/dist/index.js +20 -0
  9. package/{src/types.ts → dist/types.d.ts} +3 -17
  10. package/dist/types.js +5 -0
  11. package/package.json +9 -1
  12. package/readme.md +62 -0
  13. package/coverage/clover.xml +0 -334
  14. package/coverage/coverage-final.json +0 -4
  15. package/coverage/lcov-report/base.css +0 -224
  16. package/coverage/lcov-report/block-navigation.js +0 -87
  17. package/coverage/lcov-report/favicon.png +0 -0
  18. package/coverage/lcov-report/index.html +0 -131
  19. package/coverage/lcov-report/pmxt/BaseExchange.ts.html +0 -256
  20. package/coverage/lcov-report/pmxt/exchanges/Kalshi.ts.html +0 -1132
  21. package/coverage/lcov-report/pmxt/exchanges/Polymarket.ts.html +0 -1456
  22. package/coverage/lcov-report/pmxt/exchanges/index.html +0 -131
  23. package/coverage/lcov-report/pmxt/index.html +0 -116
  24. package/coverage/lcov-report/prettify.css +0 -1
  25. package/coverage/lcov-report/prettify.js +0 -2
  26. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  27. package/coverage/lcov-report/sorter.js +0 -210
  28. package/coverage/lcov-report/src/BaseExchange.ts.html +0 -256
  29. package/coverage/lcov-report/src/exchanges/Kalshi.ts.html +0 -1132
  30. package/coverage/lcov-report/src/exchanges/Polymarket.ts.html +0 -1456
  31. package/coverage/lcov-report/src/exchanges/index.html +0 -131
  32. package/coverage/lcov-report/src/index.html +0 -116
  33. package/coverage/lcov.info +0 -766
  34. package/examples/get_event_prices.ts +0 -37
  35. package/examples/historical_prices.ts +0 -117
  36. package/examples/orderbook.ts +0 -102
  37. package/examples/recent_trades.ts +0 -29
  38. package/examples/search_events.ts +0 -68
  39. package/examples/search_market.ts +0 -29
  40. package/jest.config.js +0 -11
  41. package/pmxt-0.1.0.tgz +0 -0
  42. package/test/exchanges/kalshi/ApiErrors.test.ts +0 -132
  43. package/test/exchanges/kalshi/EmptyResponse.test.ts +0 -44
  44. package/test/exchanges/kalshi/FetchAndNormalizeMarkets.test.ts +0 -56
  45. package/test/exchanges/kalshi/LiveApi.integration.test.ts +0 -40
  46. package/test/exchanges/kalshi/MarketHistory.test.ts +0 -185
  47. package/test/exchanges/kalshi/OrderBook.test.ts +0 -149
  48. package/test/exchanges/kalshi/SearchMarkets.test.ts +0 -174
  49. package/test/exchanges/kalshi/VolumeFallback.test.ts +0 -44
  50. package/test/exchanges/polymarket/DataValidation.test.ts +0 -271
  51. package/test/exchanges/polymarket/ErrorHandling.test.ts +0 -34
  52. package/test/exchanges/polymarket/FetchAndNormalizeMarkets.test.ts +0 -68
  53. package/test/exchanges/polymarket/GetMarketsBySlug.test.ts +0 -268
  54. package/test/exchanges/polymarket/LiveApi.integration.test.ts +0 -44
  55. package/test/exchanges/polymarket/MarketHistory.test.ts +0 -207
  56. package/test/exchanges/polymarket/OrderBook.test.ts +0 -167
  57. package/test/exchanges/polymarket/RequestParameters.test.ts +0 -39
  58. package/test/exchanges/polymarket/SearchMarkets.test.ts +0 -176
  59. package/test/exchanges/polymarket/TradeHistory.test.ts +0 -248
  60. package/tsconfig.json +0 -12
@@ -1,37 +0,0 @@
1
- import { PolymarketExchange } from '../src/exchanges/Polymarket';
2
- import { KalshiExchange } from '../src/exchanges/Kalshi';
3
-
4
- const main = async () => {
5
- const polySlug = process.argv[2] || 'who-will-trump-nominate-as-fed-chair';
6
- const kalshiTicker = process.argv[3] || 'KXFEDCHAIRNOM-29';
7
-
8
- console.log(`Fetching prices for: ${polySlug} / ${kalshiTicker}\n`);
9
-
10
- // Polymarket
11
- const polymarket = new PolymarketExchange();
12
- const polyMarkets = await polymarket.getMarketsBySlug(polySlug);
13
-
14
- console.log('--- Polymarket ---');
15
- polyMarkets
16
- .sort((a, b) => b.outcomes[0].price - a.outcomes[0].price)
17
- .slice(0, 10)
18
- .forEach(m => {
19
- console.log(`${m.outcomes[0].label}: ${(m.outcomes[0].price * 100).toFixed(1)}% ` +
20
- `(Vol24h: $${m.volume24h.toLocaleString()})`);
21
- });
22
-
23
- // Kalshi
24
- const kalshi = new KalshiExchange();
25
- const kalshiMarkets = await kalshi.getMarketsBySlug(kalshiTicker);
26
-
27
- console.log('\n--- Kalshi ---');
28
- kalshiMarkets
29
- .sort((a, b) => b.outcomes[0].price - a.outcomes[0].price)
30
- .slice(0, 10)
31
- .forEach(m => {
32
- console.log(`${m.outcomes[0].label}: ${(m.outcomes[0].price * 100).toFixed(1)}% ` +
33
- `(Vol24h: $${m.volume24h.toLocaleString()})`);
34
- });
35
- };
36
-
37
- main();
@@ -1,117 +0,0 @@
1
-
2
- import { PolymarketExchange } from '../src/exchanges/Polymarket';
3
- import { KalshiExchange } from '../src/exchanges/Kalshi';
4
- import { CandleInterval } from '../src/types';
5
-
6
- /**
7
- * START HERE
8
- *
9
- * This example fetches historical price data for a specific EVENT.
10
- *
11
- * NOTE:
12
- * An "Event" (e.g. "Who will be Fed Chair?") contains multiple "Markets" or "Outcomes"
13
- * (e.g. "Kevin Warsh", "Marc Rowan", "Scott Bessent").
14
- *
15
- * Price history is tracked at the MARKET/OUTCOME level.
16
- */
17
-
18
- const main = async () => {
19
- // 1. Define the Event Slugs (Hardcoded for simplicity)
20
- const polySlug = 'who-will-trump-nominate-as-fed-chair';
21
- const kalshiTicker = 'KXFEDCHAIRNOM-29';
22
-
23
- console.log(`=== Historical Price Fetcher ===\n`);
24
- console.log(`Target Event: Fed Chair Nomination (Kevin Warsh - YES)`);
25
-
26
- // ---------------------------------------------------------
27
- // Polymarket
28
- // ---------------------------------------------------------
29
- const polymarket = new PolymarketExchange();
30
- console.log('\n--- Polymarket ---');
31
- try {
32
- const polyMarkets = await polymarket.getMarketsBySlug(polySlug);
33
-
34
- if (polyMarkets.length > 0) {
35
- const eventMarket = polyMarkets[0];
36
-
37
- // Find "Kevin Warsh" outcome
38
- const warshOutcome = eventMarket.outcomes.find(o =>
39
- o.label.toLowerCase().includes('warsh') &&
40
- !o.label.toLowerCase().includes('not') // Ensure it's YES
41
- );
42
-
43
- if (!warshOutcome) {
44
- console.log("Could not find 'Kevin Warsh' outcome in Polymarket event.");
45
- } else {
46
- console.log(`Target Outcome: ${warshOutcome.label}`);
47
- console.log(`Current Price: ${(warshOutcome.price * 100).toFixed(1)}%`);
48
-
49
- const clobTokenId = warshOutcome.metadata?.clobTokenId;
50
-
51
- if (clobTokenId) {
52
- console.log(`Fetching 1-hour history for "${warshOutcome.label}"...`);
53
- console.log(`Token ID: ${clobTokenId}`);
54
-
55
- const history = await polymarket.getMarketHistory(clobTokenId, {
56
- resolution: '1h',
57
- limit: 20,
58
- start: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // 7 days ago
59
- });
60
-
61
- console.log(`Received ${history.length} data points.`);
62
- history.forEach(point => {
63
- const date = new Date(point.timestamp).toLocaleString();
64
- console.log(` ${date} | Price: $${point.close.toFixed(3)}`);
65
- });
66
- } else {
67
- console.log('No Token ID found, cannot fetch history.');
68
- }
69
- }
70
- }
71
- } catch (e) {
72
- console.error("Polymarket Error:", e);
73
- }
74
-
75
- // ---------------------------------------------------------
76
- // Kalshi
77
- // ---------------------------------------------------------
78
- const kalshi = new KalshiExchange();
79
- console.log('\n--- Kalshi ---');
80
- try {
81
- // First get the event to find the markets
82
- const kalshiMarkets = await kalshi.getMarketsBySlug(kalshiTicker);
83
-
84
- if (kalshiMarkets.length > 0) {
85
- // Find the market where the subtitle/title includes Warsh
86
- // Kalshi often has subtitles like "Kevin Warsh"
87
- const warshMarket = kalshiMarkets.find(m =>
88
- m.description.toLowerCase().includes('warsh') ||
89
- m.outcomes[0].label.toLowerCase().includes('warsh')
90
- );
91
-
92
- if (!warshMarket) {
93
- console.log("Could not find 'Kevin Warsh' market in Kalshi event.");
94
- } else {
95
- console.log(`Target Market: ${warshMarket.title} (${warshMarket.description})`);
96
- console.log(`Ticker: ${warshMarket.id}`);
97
- console.log(`Current Price: ${(warshMarket.outcomes[0].price * 100).toFixed(1)}%`);
98
-
99
- console.log(`Fetching 1-hour history...`);
100
-
101
- const history = await kalshi.getMarketHistory(warshMarket.id, {
102
- resolution: '1h',
103
- limit: 10
104
- });
105
-
106
- history.forEach(candle => {
107
- const date = new Date(candle.timestamp).toLocaleString();
108
- console.log(` ${date} | Price: $${candle.close.toFixed(2)}`);
109
- });
110
- }
111
- }
112
- } catch (e) {
113
- console.error("Kalshi Error:", e);
114
- }
115
- };
116
-
117
- main();
@@ -1,102 +0,0 @@
1
-
2
- import { PolymarketExchange } from '../src/exchanges/Polymarket';
3
- import { KalshiExchange } from '../src/exchanges/Kalshi';
4
- import { OrderBook } from '../src/types';
5
-
6
- /**
7
- * Example: Get Order Book (Multi-Exchange)
8
- *
9
- * This example fetches and compares order books from both Polymarket and Kalshi
10
- * for the "Kevin Warsh" Fed Chair nomination prediction.
11
- */
12
-
13
- // Helper to display an order book
14
- const displayOrderBook = (exchangeName: string, book: OrderBook) => {
15
- console.log(`\n-----------------------------------------`);
16
- console.log(`ORDER BOOK: ${exchangeName}`);
17
- console.log(`-----------------------------------------`);
18
-
19
- if (book.bids.length === 0 && book.asks.length === 0) {
20
- console.log("No orders found or market is closed.");
21
- return;
22
- }
23
-
24
- // Show Asks (Sellers) - Reverse to show highest price (lowest/best ask) at bottom
25
- console.log(`\n --- ASKS (Sellers) ---`);
26
- const asks = book.asks.slice(0, 5).sort((a, b) => b.price - a.price); // Sort descending for display (high to low)
27
- asks.forEach(level => {
28
- console.log(` Price: $${level.price.toFixed(2)} | Size: ${level.size.toLocaleString().padStart(8)}`);
29
- });
30
-
31
- // Show Bids (Buyers) - Descending (Highest/best buy first)
32
- console.log(` --- BIDS (Buyers) ---`);
33
- const bids = book.bids.slice(0, 5).sort((a, b) => b.price - a.price);
34
- bids.forEach(level => {
35
- console.log(` Price: $${level.price.toFixed(2)} | Size: ${level.size.toLocaleString().padStart(8)}`);
36
- });
37
- };
38
-
39
- const main = async () => {
40
- console.log(`=== Multi-Exchange Order Book Fetcher ===`);
41
- console.log(`Target: "Kevin Warsh" -> Fed Chair Nomination\n`);
42
-
43
- // 1. Polymarket
44
- try {
45
- const polymarket = new PolymarketExchange();
46
- const polySlug = 'who-will-trump-nominate-as-fed-chair';
47
-
48
- console.log(`Fetching Polymarket data...`);
49
- const markets = await polymarket.getMarketsBySlug(polySlug);
50
-
51
- if (markets.length > 0) {
52
- const warshOutcome = markets[0].outcomes.find(o =>
53
- o.label.toLowerCase().includes('warsh') &&
54
- !o.label.toLowerCase().includes('not')
55
- );
56
-
57
- if (warshOutcome && warshOutcome.metadata?.clobTokenId) {
58
- const book = await polymarket.getOrderBook(warshOutcome.metadata.clobTokenId);
59
- displayOrderBook('Polymarket', book);
60
- } else {
61
- console.log("Polymarket: Outcome or Token ID not found.");
62
- }
63
- }
64
- } catch (e) {
65
- console.error("Polymarket Error:", e);
66
- }
67
-
68
- // 2. Kalshi
69
- try {
70
- const kalshi = new KalshiExchange();
71
- // Specifically for "Kevin Warsh" in the Fed Chair market.
72
- // Ticker derived from previous searches.
73
- const kalshiTicker = 'KXFEDCHAIRNOM-29';
74
-
75
- console.log(`\nFetching Kalshi data...`);
76
- // Verify event exists and find the specific market
77
- const markets = await kalshi.getMarketsBySlug(kalshiTicker);
78
-
79
- if (markets.length > 0) {
80
- // Find "Kevin Warsh" market within the event group
81
- const warshMarket = markets.find(m =>
82
- m.description.toLowerCase().includes('warsh') ||
83
- m.outcomes[0].label.toLowerCase().includes('warsh')
84
- );
85
-
86
- if (warshMarket) {
87
- console.log(`Found Market Ticker: ${warshMarket.id}`);
88
- const book = await kalshi.getOrderBook(warshMarket.id);
89
- displayOrderBook('Kalshi', book);
90
- } else {
91
- console.log("Kalshi: 'Kevin Warsh' market not found in event.");
92
- }
93
- } else {
94
- console.log("Kalshi: Event not found.");
95
- }
96
-
97
- } catch (e) {
98
- console.error("Kalshi Error:", e);
99
- }
100
- };
101
-
102
- main();
@@ -1,29 +0,0 @@
1
- import { KalshiExchange } from '../src/exchanges/Kalshi';
2
- import { PolymarketExchange } from '../src/exchanges/Polymarket';
3
-
4
- async function run() {
5
- const kalshi = new KalshiExchange();
6
- const poly = new PolymarketExchange();
7
-
8
- const kMarkets = await kalshi.getMarketsBySlug('KXFEDCHAIRNOM-29');
9
- // Filter for Kevin Warsh specifically
10
- const warshMarket = kMarkets.find(m => m.outcomes[0].label.includes('Kevin Warsh'));
11
-
12
- if (warshMarket) {
13
- console.log(`--- Kalshi Tape: ${warshMarket.outcomes[0].label} ---`);
14
- const trades = await kalshi.getTradeHistory(warshMarket.id, { limit: 10, resolution: '1m' });
15
- trades.forEach(t => console.log(`${new Date(t.timestamp).toLocaleTimeString()} | ${t.side.toUpperCase()} | ${(t.price * 100).toFixed(1)}c | ${t.amount}`));
16
- }
17
-
18
- const pMarkets = await poly.getMarketsBySlug('who-will-trump-nominate-as-fed-chair');
19
- const pWarsh = pMarkets.find(m => m.outcomes[0].label.includes('Kevin Warsh'));
20
-
21
- if (pWarsh) {
22
- console.log(`\n--- Polymarket Tape: Kevin Warsh ---`);
23
- const tokenId = pWarsh.outcomes[0].metadata?.clobTokenId;
24
- const trades = await poly.getTradeHistory(tokenId, { limit: 5, resolution: '1m' });
25
- trades.forEach(t => console.log(`${new Date(t.timestamp).toLocaleTimeString()} | ${t.side.toUpperCase()} | ${(t.price * 100).toFixed(1)}c | ${t.amount}`));
26
- }
27
- }
28
-
29
- run();
@@ -1,68 +0,0 @@
1
- import { PolymarketExchange } from '../src/exchanges/Polymarket';
2
- import { KalshiExchange } from '../src/exchanges/Kalshi';
3
- import { UnifiedMarket } from '../src/types';
4
-
5
- interface UnifiedEvent {
6
- title: string;
7
- url: string;
8
- volume24h: number; // Aggregated
9
- markets: UnifiedMarket[];
10
- }
11
-
12
- const groupMarketsByEvent = (markets: UnifiedMarket[]): UnifiedEvent[] => {
13
- const eventsMap = new Map<string, UnifiedEvent>();
14
-
15
- for (const m of markets) {
16
- if (!eventsMap.has(m.url)) {
17
- eventsMap.set(m.url, {
18
- title: m.title,
19
- url: m.url,
20
- volume24h: 0,
21
- markets: []
22
- });
23
- }
24
- const event = eventsMap.get(m.url)!;
25
- event.volume24h += m.volume24h;
26
- event.markets.push(m);
27
- }
28
-
29
- return Array.from(eventsMap.values());
30
- };
31
-
32
- const main = async () => {
33
- const query = process.argv[2] || 'Fed';
34
- console.log(`Searching for Events matching "${query}"...\n`);
35
-
36
- // 1. Polymarket
37
- const polymarket = new PolymarketExchange();
38
- // Polymarket returns many sub-markets per event
39
- const polyMarkets = await polymarket.searchMarkets(query, { sort: 'volume' });
40
- const polyEvents = groupMarketsByEvent(polyMarkets);
41
-
42
- console.log(`--- Polymarket Events (${polyEvents.length}) ---`);
43
- polyEvents
44
- .sort((a, b) => b.volume24h - a.volume24h)
45
- .slice(0, 5)
46
- .forEach(e => {
47
- console.log(`${e.title} (Vol24h: $${e.volume24h.toLocaleString()})`);
48
- console.log(` -> ${e.markets.length} sub-markets`);
49
- });
50
-
51
- console.log('');
52
-
53
- // 2. Kalshi
54
- const kalshi = new KalshiExchange();
55
- const kalshiMarkets = await kalshi.searchMarkets(query);
56
- const kalshiEvents = groupMarketsByEvent(kalshiMarkets);
57
-
58
- console.log(`--- Kalshi Events (${kalshiEvents.length}) ---`);
59
- kalshiEvents
60
- .sort((a, b) => b.volume24h - a.volume24h)
61
- .slice(0, 5)
62
- .forEach(e => {
63
- console.log(`${e.title} (Vol24h: $${e.volume24h.toLocaleString()})`);
64
- console.log(` -> ${e.markets.length} sub-markets`);
65
- });
66
- };
67
-
68
- main();
@@ -1,29 +0,0 @@
1
- import { PolymarketExchange } from '../src/exchanges/Polymarket';
2
- import { KalshiExchange } from '../src/exchanges/Kalshi';
3
-
4
- const main = async () => {
5
- const query = process.argv[2] || 'Fed';
6
- console.log(`Searching for "${query}"...\n`);
7
-
8
- // Polymarket
9
- const polymarket = new PolymarketExchange();
10
- const polyResults = await polymarket.searchMarkets(query, { sort: 'volume' });
11
-
12
- console.log(`--- Polymarket Found ${polyResults.length} ---`);
13
- polyResults.slice(0, 10).forEach(m => {
14
- const label = m.outcomes[0]?.label || 'Unknown';
15
- console.log(`[${m.id}] ${m.title} - ${label} (Vol24h: $${m.volume24h.toLocaleString()})`);
16
- });
17
-
18
- // Kalshi
19
- const kalshi = new KalshiExchange();
20
- const kalshiResults = await kalshi.searchMarkets(query);
21
-
22
- console.log(`\n--- Kalshi Found ${kalshiResults.length} ---`);
23
- kalshiResults.slice(0, 10).forEach(m => {
24
- const label = m.outcomes[0]?.label || 'Unknown';
25
- console.log(`[${m.id}] ${m.title} - ${label} (Vol24h: $${m.volume24h.toLocaleString()})`);
26
- });
27
- };
28
-
29
- main();
package/jest.config.js DELETED
@@ -1,11 +0,0 @@
1
- const { createDefaultPreset } = require("ts-jest");
2
-
3
- const tsJestTransformCfg = createDefaultPreset().transform;
4
-
5
- /** @type {import("jest").Config} **/
6
- module.exports = {
7
- testEnvironment: "node",
8
- transform: {
9
- ...tsJestTransformCfg,
10
- },
11
- };
package/pmxt-0.1.0.tgz DELETED
Binary file
@@ -1,132 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi API Error Handling Test
6
- *
7
- * What: Tests how the exchange handles various API error responses (4xx, 5xx).
8
- * Why: External APIs can fail with rate limits, authentication errors, or server issues.
9
- * The library must handle these gracefully without crashing.
10
- * How: Mocks different HTTP error responses and verifies proper error handling.
11
- */
12
-
13
- jest.mock('axios');
14
- const mockedAxios = axios as jest.Mocked<typeof axios>;
15
-
16
- describe('KalshiExchange - API Error Handling', () => {
17
- let exchange: KalshiExchange;
18
-
19
- beforeEach(() => {
20
- exchange = new KalshiExchange();
21
- jest.clearAllMocks();
22
- });
23
-
24
- it('should handle 404 errors in getMarketsBySlug', async () => {
25
- const error = {
26
- response: {
27
- status: 404,
28
- data: { error: 'Event not found' }
29
- },
30
- isAxiosError: true
31
- };
32
- mockedAxios.get.mockRejectedValue(error);
33
- // @ts-expect-error - Mock type mismatch is expected in tests
34
- mockedAxios.isAxiosError = jest.fn().mockReturnValue(true);
35
-
36
- await expect(exchange.getMarketsBySlug('INVALID-EVENT'))
37
- .rejects
38
- .toThrow(/not found/i);
39
- });
40
-
41
- it('should handle 500 errors in getMarketsBySlug', async () => {
42
- const error = {
43
- response: {
44
- status: 500,
45
- data: { error: 'Internal Server Error' }
46
- },
47
- isAxiosError: true
48
- };
49
- mockedAxios.get.mockRejectedValue(error);
50
- // @ts-expect-error - Mock type mismatch is expected in tests
51
- mockedAxios.isAxiosError = jest.fn().mockReturnValue(true);
52
-
53
- await expect(exchange.getMarketsBySlug('SOME-EVENT'))
54
- .rejects
55
- .toThrow(/API Error/i);
56
- });
57
-
58
- it('should handle network errors in fetchMarkets', async () => {
59
- mockedAxios.get.mockRejectedValue(new Error('Network timeout'));
60
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
61
-
62
- const markets = await exchange.fetchMarkets();
63
-
64
- expect(markets).toEqual([]);
65
- expect(consoleSpy).toHaveBeenCalled();
66
- consoleSpy.mockRestore();
67
- });
68
-
69
- it('should handle malformed response data', async () => {
70
- mockedAxios.get.mockResolvedValue({
71
- data: {
72
- events: [
73
- {
74
- // Missing required fields
75
- event_ticker: 'TEST',
76
- markets: [{ ticker: 'TEST-MARKET' }]
77
- }
78
- ]
79
- }
80
- });
81
-
82
- const markets = await exchange.fetchMarkets();
83
-
84
- // Should not crash, may return empty or partial data
85
- expect(Array.isArray(markets)).toBe(true);
86
- });
87
-
88
- it('should handle invalid ticker format in getMarketHistory', async () => {
89
- await expect(exchange.getMarketHistory('INVALID', { resolution: '1h' }))
90
- .rejects
91
- .toThrow(/Invalid Kalshi Ticker format/i);
92
- });
93
-
94
- it('should handle API errors in getMarketHistory', async () => {
95
- const error = {
96
- response: {
97
- status: 400,
98
- data: { error: 'Invalid parameters' }
99
- },
100
- isAxiosError: true
101
- };
102
- mockedAxios.get.mockRejectedValue(error);
103
- // @ts-expect-error - Mock type mismatch is expected in tests
104
- mockedAxios.isAxiosError = jest.fn().mockReturnValue(true);
105
-
106
- await expect(exchange.getMarketHistory('FED-25JAN-B4.75', { resolution: '1h' }))
107
- .rejects
108
- .toThrow(/History API Error/i);
109
- });
110
-
111
- it('should return empty orderbook on error', async () => {
112
- mockedAxios.get.mockRejectedValue(new Error('API Error'));
113
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
114
-
115
- const orderbook = await exchange.getOrderBook('TEST-TICKER');
116
-
117
- expect(orderbook).toEqual({ bids: [], asks: [] });
118
- expect(consoleSpy).toHaveBeenCalled();
119
- consoleSpy.mockRestore();
120
- });
121
-
122
- it('should return empty trades on error', async () => {
123
- mockedAxios.get.mockRejectedValue(new Error('API Error'));
124
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
125
-
126
- const trades = await exchange.getTradeHistory('TEST-TICKER', { resolution: '1h' });
127
-
128
- expect(trades).toEqual([]);
129
- expect(consoleSpy).toHaveBeenCalled();
130
- consoleSpy.mockRestore();
131
- });
132
- });
@@ -1,44 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi Empty Response Handling Test
6
- *
7
- * What: Tests how the exchange handles empty responses from the API.
8
- * Why: APIs can return empty lists during maintenance or based on filters.
9
- * The library must handle this gracefully without throwing errors.
10
- * How: Mocks an empty events array and verifies an empty market array is returned.
11
- */
12
-
13
- jest.mock('axios');
14
- const mockedAxios = axios as jest.Mocked<typeof axios>;
15
-
16
- describe('KalshiExchange - Empty Response Handling', () => {
17
- let exchange: KalshiExchange;
18
-
19
- beforeEach(() => {
20
- exchange = new KalshiExchange();
21
- jest.clearAllMocks();
22
- });
23
-
24
- it('should handle empty events array gracefully', async () => {
25
- mockedAxios.get.mockResolvedValue({
26
- data: {
27
- events: [],
28
- cursor: ''
29
- }
30
- });
31
-
32
- const markets = await exchange.fetchMarkets();
33
- expect(markets).toEqual([]);
34
- });
35
-
36
- it('should handle missing events field gracefully', async () => {
37
- mockedAxios.get.mockResolvedValue({
38
- data: {}
39
- });
40
-
41
- const markets = await exchange.fetchMarkets();
42
- expect(markets).toEqual([]);
43
- });
44
- });
@@ -1,56 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi Markets Normalization Test
6
- *
7
- * What: Tests if Kalshi markets are correctly normalized from the event-based structure.
8
- * Why: Kalshi uses a hierarchical Event > Market structure. We need to ensured nested markets
9
- * are correctly flattened and combined with their parent event metadata (title, category).
10
- * How: Mocks the Kalshi /events API response with nested markets and verifies the UnifiedMarket output.
11
- */
12
-
13
- jest.mock('axios');
14
- const mockedAxios = axios as jest.Mocked<typeof axios>;
15
-
16
- describe('KalshiExchange - Fetch and Normalize Markets', () => {
17
- let exchange: KalshiExchange;
18
-
19
- beforeEach(() => {
20
- exchange = new KalshiExchange();
21
- jest.clearAllMocks();
22
- });
23
-
24
- it('should correctly flatten nested markets from events', async () => {
25
- mockedAxios.get.mockResolvedValue({
26
- data: {
27
- events: [{
28
- event_ticker: 'KXINFLATION',
29
- title: 'Inflation > 3%',
30
- sub_title: 'CPI exceeds 3%',
31
- category: 'Economics',
32
- markets: [{
33
- ticker: 'KXINFLATION24',
34
- yes_ask: 45,
35
- yes_bid: 43,
36
- volume_24h: 1000,
37
- expiration_time: "2024-12-31T00:00:00Z",
38
- open_interest: 500,
39
- last_price: 44
40
- }]
41
- }],
42
- cursor: ''
43
- }
44
- });
45
-
46
- const markets = await exchange.fetchMarkets();
47
-
48
- expect(markets.length).toBe(1);
49
- const m = markets[0];
50
- expect(m.id).toBe('KXINFLATION24');
51
- expect(m.title).toBe('Inflation > 3%');
52
- expect(m.outcomes[0].price).toBe(0.44); // last_price / 100
53
- expect(m.volume24h).toBe(1000);
54
- expect(m.category).toBe('Economics');
55
- });
56
- });
@@ -1,40 +0,0 @@
1
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
2
-
3
- /**
4
- * Kalshi Integration Test (Live API)
5
- *
6
- * What: Verifies real-world connectivity and data structure from the live Kalshi API.
7
- * Why: To ensure our mapping logic matches the actual live data and to detect
8
- * breaking API changes early.
9
- * How: Makes actual HTTP requests to Kalshi's public endpoints and validates
10
- * the properties of the returned markets.
11
- */
12
-
13
- describe('KalshiExchange - Live API Integration', () => {
14
- const exchange = new KalshiExchange();
15
- jest.setTimeout(30000); // Kalshi fetches all events before applying limit (needs optimization)
16
-
17
- it('should fetch real markets with expected properties', async () => {
18
- const markets = await exchange.fetchMarkets({ limit: 5 });
19
-
20
- if (markets.length > 0) {
21
- const m = markets[0];
22
- expect(m.id).toBeDefined();
23
- expect(typeof m.title).toBe('string');
24
- expect(m.outcomes.length).toBeGreaterThan(0);
25
- expect(m.volume24h).toBeGreaterThanOrEqual(0);
26
-
27
- // Prices should be normalized to 0-1
28
- m.outcomes.forEach(outcome => {
29
- expect(outcome.price).toBeGreaterThanOrEqual(0);
30
- expect(outcome.price).toBeLessThanOrEqual(1);
31
- });
32
- }
33
- });
34
-
35
- it('should respect the limit parameter on live data', async () => {
36
- const limit = 3;
37
- const markets = await exchange.fetchMarkets({ limit });
38
- expect(markets.length).toBeLessThanOrEqual(limit);
39
- });
40
- });