pmxtjs 0.1.0 → 0.1.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 (58) hide show
  1. package/dist/BaseExchange.js +30 -0
  2. package/{src/exchanges/Kalshi.ts → dist/exchanges/Kalshi.js} +75 -106
  3. package/{src/exchanges/Polymarket.ts → dist/exchanges/Polymarket.js} +107 -144
  4. package/dist/index.js +20 -0
  5. package/dist/types.js +5 -0
  6. package/package.json +4 -1
  7. package/API_REFERENCE.md +0 -88
  8. package/coverage/clover.xml +0 -334
  9. package/coverage/coverage-final.json +0 -4
  10. package/coverage/lcov-report/base.css +0 -224
  11. package/coverage/lcov-report/block-navigation.js +0 -87
  12. package/coverage/lcov-report/favicon.png +0 -0
  13. package/coverage/lcov-report/index.html +0 -131
  14. package/coverage/lcov-report/pmxt/BaseExchange.ts.html +0 -256
  15. package/coverage/lcov-report/pmxt/exchanges/Kalshi.ts.html +0 -1132
  16. package/coverage/lcov-report/pmxt/exchanges/Polymarket.ts.html +0 -1456
  17. package/coverage/lcov-report/pmxt/exchanges/index.html +0 -131
  18. package/coverage/lcov-report/pmxt/index.html +0 -116
  19. package/coverage/lcov-report/prettify.css +0 -1
  20. package/coverage/lcov-report/prettify.js +0 -2
  21. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  22. package/coverage/lcov-report/sorter.js +0 -210
  23. package/coverage/lcov-report/src/BaseExchange.ts.html +0 -256
  24. package/coverage/lcov-report/src/exchanges/Kalshi.ts.html +0 -1132
  25. package/coverage/lcov-report/src/exchanges/Polymarket.ts.html +0 -1456
  26. package/coverage/lcov-report/src/exchanges/index.html +0 -131
  27. package/coverage/lcov-report/src/index.html +0 -116
  28. package/coverage/lcov.info +0 -766
  29. package/examples/get_event_prices.ts +0 -37
  30. package/examples/historical_prices.ts +0 -117
  31. package/examples/orderbook.ts +0 -102
  32. package/examples/recent_trades.ts +0 -29
  33. package/examples/search_events.ts +0 -68
  34. package/examples/search_market.ts +0 -29
  35. package/jest.config.js +0 -11
  36. package/pmxt-0.1.0.tgz +0 -0
  37. package/src/BaseExchange.ts +0 -57
  38. package/src/index.ts +0 -5
  39. package/src/types.ts +0 -61
  40. package/test/exchanges/kalshi/ApiErrors.test.ts +0 -132
  41. package/test/exchanges/kalshi/EmptyResponse.test.ts +0 -44
  42. package/test/exchanges/kalshi/FetchAndNormalizeMarkets.test.ts +0 -56
  43. package/test/exchanges/kalshi/LiveApi.integration.test.ts +0 -40
  44. package/test/exchanges/kalshi/MarketHistory.test.ts +0 -185
  45. package/test/exchanges/kalshi/OrderBook.test.ts +0 -149
  46. package/test/exchanges/kalshi/SearchMarkets.test.ts +0 -174
  47. package/test/exchanges/kalshi/VolumeFallback.test.ts +0 -44
  48. package/test/exchanges/polymarket/DataValidation.test.ts +0 -271
  49. package/test/exchanges/polymarket/ErrorHandling.test.ts +0 -34
  50. package/test/exchanges/polymarket/FetchAndNormalizeMarkets.test.ts +0 -68
  51. package/test/exchanges/polymarket/GetMarketsBySlug.test.ts +0 -268
  52. package/test/exchanges/polymarket/LiveApi.integration.test.ts +0 -44
  53. package/test/exchanges/polymarket/MarketHistory.test.ts +0 -207
  54. package/test/exchanges/polymarket/OrderBook.test.ts +0 -167
  55. package/test/exchanges/polymarket/RequestParameters.test.ts +0 -39
  56. package/test/exchanges/polymarket/SearchMarkets.test.ts +0 -176
  57. package/test/exchanges/polymarket/TradeHistory.test.ts +0 -248
  58. 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,57 +0,0 @@
1
- import { UnifiedMarket, PriceCandle, CandleInterval, OrderBook, Trade } from './types';
2
-
3
- export interface MarketFilterParams {
4
- limit?: number;
5
- offset?: number;
6
- sort?: 'volume' | 'liquidity' | 'newest';
7
- }
8
-
9
- export interface HistoryFilterParams {
10
- resolution: CandleInterval;
11
- start?: Date;
12
- end?: Date;
13
- limit?: number;
14
- }
15
-
16
- // ----------------------------------------------------------------------------
17
- // Base Exchange Class
18
- // ----------------------------------------------------------------------------
19
-
20
- export abstract class PredictionMarketExchange {
21
- abstract get name(): string;
22
-
23
- /**
24
- * Fetch all relevant markets from the source.
25
- */
26
- abstract fetchMarkets(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
27
-
28
- /**
29
- * Search for markets matching a keyword query.
30
- * Searches across title and description fields.
31
- */
32
- abstract searchMarkets(query: string, params?: MarketFilterParams): Promise<UnifiedMarket[]>;
33
-
34
- /**
35
- * Fetch historical price data for a specific market or outcome.
36
- * @param id - The market ID or specific outcome ID/Token ID depending on the exchange
37
- */
38
- async getMarketHistory(id: string, params: HistoryFilterParams): Promise<PriceCandle[]> {
39
- throw new Error("Method getMarketHistory not implemented.");
40
- }
41
-
42
- /**
43
- * Fetch the current order book (bids/asks) for a specific outcome.
44
- * Essential for calculating localized spread and depth.
45
- */
46
- async getOrderBook(id: string): Promise<OrderBook> {
47
- throw new Error("Method getOrderBook not implemented.");
48
- }
49
-
50
- /**
51
- * Fetch raw trade history.
52
- * Useful for generating synthetic OHLCV candles if the exchange doesn't provide them natively.
53
- */
54
- async getTradeHistory(id: string, params: HistoryFilterParams): Promise<Trade[]> {
55
- throw new Error("Method getTradeHistory not implemented.");
56
- }
57
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export * from './BaseExchange';
2
- export * from './types';
3
- export * from './exchanges/Polymarket';
4
- export * from './exchanges/Kalshi';
5
-
package/src/types.ts DELETED
@@ -1,61 +0,0 @@
1
-
2
- // ----------------------------------------------------------------------------
3
- // Core Data Models
4
- // ----------------------------------------------------------------------------
5
-
6
- export interface MarketOutcome {
7
- id: string;
8
- label: string;
9
- price: number;
10
- priceChange24h?: number;
11
- metadata?: Record<string, any>;
12
- }
13
-
14
- export interface UnifiedMarket {
15
- id: string;
16
- title: string;
17
- description: string;
18
- outcomes: MarketOutcome[];
19
-
20
- resolutionDate: Date;
21
- volume24h: number;
22
- volume?: number; // Total / Lifetime volume
23
- liquidity: number;
24
- openInterest?: number;
25
-
26
- url: string;
27
- image?: string;
28
-
29
- category?: string;
30
- tags?: string[];
31
- }
32
-
33
- export type CandleInterval = '1m' | '5m' | '15m' | '1h' | '6h' | '1d';
34
-
35
- export interface PriceCandle {
36
- timestamp: number;
37
- open: number;
38
- high: number;
39
- low: number;
40
- close: number;
41
- volume?: number;
42
- }
43
-
44
- export interface OrderLevel {
45
- price: number; // 0.0 to 1.0 (probability)
46
- size: number; // contracts/shares
47
- }
48
-
49
- export interface OrderBook {
50
- bids: OrderLevel[];
51
- asks: OrderLevel[];
52
- timestamp?: number;
53
- }
54
-
55
- export interface Trade {
56
- id: string;
57
- timestamp: number;
58
- price: number;
59
- amount: number;
60
- side: 'buy' | 'sell' | 'unknown';
61
- }
@@ -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
- });