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.
- package/dist/BaseExchange.js +30 -0
- package/{src/exchanges/Kalshi.ts → dist/exchanges/Kalshi.js} +75 -106
- package/{src/exchanges/Polymarket.ts → dist/exchanges/Polymarket.js} +107 -144
- package/dist/index.js +20 -0
- package/dist/types.js +5 -0
- package/package.json +4 -1
- package/API_REFERENCE.md +0 -88
- package/coverage/clover.xml +0 -334
- package/coverage/coverage-final.json +0 -4
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/pmxt/BaseExchange.ts.html +0 -256
- package/coverage/lcov-report/pmxt/exchanges/Kalshi.ts.html +0 -1132
- package/coverage/lcov-report/pmxt/exchanges/Polymarket.ts.html +0 -1456
- package/coverage/lcov-report/pmxt/exchanges/index.html +0 -131
- package/coverage/lcov-report/pmxt/index.html +0 -116
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/BaseExchange.ts.html +0 -256
- package/coverage/lcov-report/src/exchanges/Kalshi.ts.html +0 -1132
- package/coverage/lcov-report/src/exchanges/Polymarket.ts.html +0 -1456
- package/coverage/lcov-report/src/exchanges/index.html +0 -131
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov.info +0 -766
- package/examples/get_event_prices.ts +0 -37
- package/examples/historical_prices.ts +0 -117
- package/examples/orderbook.ts +0 -102
- package/examples/recent_trades.ts +0 -29
- package/examples/search_events.ts +0 -68
- package/examples/search_market.ts +0 -29
- package/jest.config.js +0 -11
- package/pmxt-0.1.0.tgz +0 -0
- package/src/BaseExchange.ts +0 -57
- package/src/index.ts +0 -5
- package/src/types.ts +0 -61
- package/test/exchanges/kalshi/ApiErrors.test.ts +0 -132
- package/test/exchanges/kalshi/EmptyResponse.test.ts +0 -44
- package/test/exchanges/kalshi/FetchAndNormalizeMarkets.test.ts +0 -56
- package/test/exchanges/kalshi/LiveApi.integration.test.ts +0 -40
- package/test/exchanges/kalshi/MarketHistory.test.ts +0 -185
- package/test/exchanges/kalshi/OrderBook.test.ts +0 -149
- package/test/exchanges/kalshi/SearchMarkets.test.ts +0 -174
- package/test/exchanges/kalshi/VolumeFallback.test.ts +0 -44
- package/test/exchanges/polymarket/DataValidation.test.ts +0 -271
- package/test/exchanges/polymarket/ErrorHandling.test.ts +0 -34
- package/test/exchanges/polymarket/FetchAndNormalizeMarkets.test.ts +0 -68
- package/test/exchanges/polymarket/GetMarketsBySlug.test.ts +0 -268
- package/test/exchanges/polymarket/LiveApi.integration.test.ts +0 -44
- package/test/exchanges/polymarket/MarketHistory.test.ts +0 -207
- package/test/exchanges/polymarket/OrderBook.test.ts +0 -167
- package/test/exchanges/polymarket/RequestParameters.test.ts +0 -39
- package/test/exchanges/polymarket/SearchMarkets.test.ts +0 -176
- package/test/exchanges/polymarket/TradeHistory.test.ts +0 -248
- 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();
|
package/examples/orderbook.ts
DELETED
|
@@ -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
package/pmxt-0.1.0.tgz
DELETED
|
Binary file
|
package/src/BaseExchange.ts
DELETED
|
@@ -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
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
|
-
});
|