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,185 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi getMarketHistory() Test
6
- *
7
- * What: Tests fetching historical OHLCV candlestick data.
8
- * Why: Historical data is essential for charting and analysis.
9
- * How: Mocks Kalshi candlestick API responses and verifies data transformation.
10
- */
11
-
12
- jest.mock('axios');
13
- const mockedAxios = axios as jest.Mocked<typeof axios>;
14
-
15
- describe('KalshiExchange - getMarketHistory', () => {
16
- let exchange: KalshiExchange;
17
-
18
- beforeEach(() => {
19
- exchange = new KalshiExchange();
20
- jest.clearAllMocks();
21
- });
22
-
23
- it('should fetch and parse candlestick data', async () => {
24
- mockedAxios.get.mockResolvedValue({
25
- data: {
26
- candlesticks: [
27
- {
28
- end_period_ts: 1704067200,
29
- price: { open: 5200, high: 5300, low: 5100, close: 5250 },
30
- volume: 1000
31
- },
32
- {
33
- end_period_ts: 1704070800,
34
- price: { open: 5250, high: 5400, low: 5200, close: 5350 },
35
- volume: 1500
36
- }
37
- ]
38
- }
39
- });
40
-
41
- const history = await exchange.getMarketHistory('FED-25JAN-B4.75', { resolution: '1h' });
42
-
43
- expect(history.length).toBe(2);
44
- expect(history[0].open).toBe(52); // 5200 / 100 = 52
45
- expect(history[0].high).toBe(53);
46
- expect(history[0].low).toBe(51);
47
- expect(history[0].close).toBe(52.5);
48
- expect(history[0].volume).toBe(1000);
49
- });
50
-
51
- it('should convert timestamps to milliseconds', async () => {
52
- mockedAxios.get.mockResolvedValue({
53
- data: {
54
- candlesticks: [{
55
- end_period_ts: 1704067200,
56
- price: { open: 5000, high: 5000, low: 5000, close: 5000 },
57
- volume: 100
58
- }]
59
- }
60
- });
61
-
62
- const history = await exchange.getMarketHistory('TEST-MARKET-B5', { resolution: '1h' });
63
-
64
- expect(history[0].timestamp).toBe(1704067200 * 1000);
65
- });
66
-
67
- it('should respect limit parameter', async () => {
68
- const mockCandles = Array.from({ length: 100 }, (_, i) => ({
69
- end_period_ts: 1704067200 + (i * 3600),
70
- price: { open: 5000, high: 5000, low: 5000, close: 5000 },
71
- volume: 100
72
- }));
73
-
74
- mockedAxios.get.mockResolvedValue({
75
- data: { candlesticks: mockCandles }
76
- });
77
-
78
- const history = await exchange.getMarketHistory('TEST-MARKET-B5', {
79
- resolution: '1h',
80
- limit: 20
81
- });
82
-
83
- expect(history.length).toBe(20);
84
- });
85
-
86
- it('should handle different intervals', async () => {
87
- mockedAxios.get.mockResolvedValue({
88
- data: {
89
- candlesticks: [{
90
- end_period_ts: 1704067200,
91
- price: { open: 5000, high: 5000, low: 5000, close: 5000 },
92
- volume: 100
93
- }]
94
- }
95
- });
96
-
97
- await exchange.getMarketHistory('TEST-MARKET-B5', { resolution: '1m' });
98
- expect(mockedAxios.get).toHaveBeenCalledWith(
99
- expect.any(String),
100
- expect.objectContaining({
101
- params: expect.objectContaining({ period_interval: 1 })
102
- })
103
- );
104
-
105
- jest.clearAllMocks();
106
- mockedAxios.get.mockResolvedValue({
107
- data: { candlesticks: [] }
108
- });
109
-
110
- await exchange.getMarketHistory('TEST-MARKET-B5', { resolution: '1d' });
111
- expect(mockedAxios.get).toHaveBeenCalledWith(
112
- expect.any(String),
113
- expect.objectContaining({
114
- params: expect.objectContaining({ period_interval: 1440 })
115
- })
116
- );
117
- });
118
-
119
- it('should handle start and end timestamps', async () => {
120
- mockedAxios.get.mockResolvedValue({
121
- data: { candlesticks: [] }
122
- });
123
-
124
- const start = new Date('2025-01-01T00:00:00Z');
125
- const end = new Date('2025-01-02T00:00:00Z');
126
-
127
- await exchange.getMarketHistory('TEST-MARKET-B5', {
128
- resolution: '1h',
129
- start,
130
- end
131
- });
132
-
133
- expect(mockedAxios.get).toHaveBeenCalledWith(
134
- expect.any(String),
135
- expect.objectContaining({
136
- params: expect.objectContaining({
137
- start_ts: Math.floor(start.getTime() / 1000),
138
- end_ts: Math.floor(end.getTime() / 1000)
139
- })
140
- })
141
- );
142
- });
143
-
144
- it('should handle empty candlesticks array', async () => {
145
- mockedAxios.get.mockResolvedValue({
146
- data: { candlesticks: [] }
147
- });
148
-
149
- const history = await exchange.getMarketHistory('TEST-MARKET-B5', { resolution: '1h' });
150
-
151
- expect(history).toEqual([]);
152
- });
153
-
154
- it('should handle missing price fields gracefully', async () => {
155
- mockedAxios.get.mockResolvedValue({
156
- data: {
157
- candlesticks: [{
158
- end_period_ts: 1704067200,
159
- price: {}, // Missing OHLC data
160
- volume: 100
161
- }]
162
- }
163
- });
164
-
165
- const history = await exchange.getMarketHistory('TEST-MARKET-B5', { resolution: '1h' });
166
-
167
- expect(history[0].open).toBe(0);
168
- expect(history[0].high).toBe(0);
169
- expect(history[0].low).toBe(0);
170
- expect(history[0].close).toBe(0);
171
- });
172
-
173
- it('should extract series ticker correctly', async () => {
174
- mockedAxios.get.mockResolvedValue({
175
- data: { candlesticks: [] }
176
- });
177
-
178
- await exchange.getMarketHistory('FED-25JAN-B4.75', { resolution: '1h' });
179
-
180
- expect(mockedAxios.get).toHaveBeenCalledWith(
181
- expect.stringContaining('/series/FED-25JAN/markets/FED-25JAN-B4.75/'),
182
- expect.any(Object)
183
- );
184
- });
185
- });
@@ -1,149 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi getOrderBook() Test
6
- *
7
- * What: Tests fetching and parsing of order book data (bids/asks).
8
- * Why: Order book data is critical for trading and price discovery.
9
- * How: Mocks Kalshi orderbook API responses and verifies correct parsing and sorting.
10
- */
11
-
12
- jest.mock('axios');
13
- const mockedAxios = axios as jest.Mocked<typeof axios>;
14
-
15
- describe('KalshiExchange - getOrderBook', () => {
16
- let exchange: KalshiExchange;
17
-
18
- beforeEach(() => {
19
- exchange = new KalshiExchange();
20
- jest.clearAllMocks();
21
- });
22
-
23
- it('should parse orderbook data correctly', async () => {
24
- mockedAxios.get.mockResolvedValue({
25
- data: {
26
- orderbook: {
27
- yes: [
28
- [5200, 100], // price in cents, quantity
29
- [5100, 250],
30
- [5000, 500]
31
- ],
32
- no: [
33
- [4800, 150],
34
- [4900, 200],
35
- [5000, 300]
36
- ]
37
- }
38
- }
39
- });
40
-
41
- const orderbook = await exchange.getOrderBook('FED-25JAN-B4.75');
42
-
43
- expect(orderbook.bids).toBeDefined();
44
- expect(orderbook.asks).toBeDefined();
45
- expect(orderbook.bids.length).toBe(3);
46
- expect(orderbook.asks.length).toBe(3);
47
- });
48
-
49
- it('should convert prices from cents to decimals', async () => {
50
- mockedAxios.get.mockResolvedValue({
51
- data: {
52
- orderbook: {
53
- yes: [[5200, 100]],
54
- no: [[4800, 100]]
55
- }
56
- }
57
- });
58
-
59
- const orderbook = await exchange.getOrderBook('TEST-MARKET');
60
-
61
- expect(orderbook.bids[0].price).toBe(52); // 5200 / 100 = 52
62
- expect(orderbook.asks[0].price).toBe(-47); // (100 - 4800) / 100 = -47
63
- });
64
-
65
- it('should sort bids in descending order', async () => {
66
- mockedAxios.get.mockResolvedValue({
67
- data: {
68
- orderbook: {
69
- yes: [
70
- [5000, 100],
71
- [5200, 200],
72
- [5100, 150]
73
- ],
74
- no: []
75
- }
76
- }
77
- });
78
-
79
- const orderbook = await exchange.getOrderBook('TEST-MARKET');
80
-
81
- expect(orderbook.bids[0].price).toBeGreaterThanOrEqual(orderbook.bids[1].price);
82
- expect(orderbook.bids[1].price).toBeGreaterThanOrEqual(orderbook.bids[2].price);
83
- });
84
-
85
- it('should sort asks in ascending order', async () => {
86
- mockedAxios.get.mockResolvedValue({
87
- data: {
88
- orderbook: {
89
- yes: [],
90
- no: [
91
- [5000, 100],
92
- [4800, 200],
93
- [4900, 150]
94
- ]
95
- }
96
- }
97
- });
98
-
99
- const orderbook = await exchange.getOrderBook('TEST-MARKET');
100
-
101
- expect(orderbook.asks[0].price).toBeLessThanOrEqual(orderbook.asks[1].price);
102
- expect(orderbook.asks[1].price).toBeLessThanOrEqual(orderbook.asks[2].price);
103
- });
104
-
105
- it('should handle empty orderbook', async () => {
106
- mockedAxios.get.mockResolvedValue({
107
- data: {
108
- orderbook: {
109
- yes: [],
110
- no: []
111
- }
112
- }
113
- });
114
-
115
- const orderbook = await exchange.getOrderBook('TEST-MARKET');
116
-
117
- expect(orderbook.bids).toEqual([]);
118
- expect(orderbook.asks).toEqual([]);
119
- });
120
-
121
- it('should include timestamp', async () => {
122
- mockedAxios.get.mockResolvedValue({
123
- data: {
124
- orderbook: {
125
- yes: [[5200, 100]],
126
- no: [[4800, 100]]
127
- }
128
- }
129
- });
130
-
131
- const orderbook = await exchange.getOrderBook('TEST-MARKET');
132
-
133
- expect(orderbook.timestamp).toBeDefined();
134
- expect(typeof orderbook.timestamp).toBe('number');
135
- });
136
-
137
- it('should handle missing yes/no fields', async () => {
138
- mockedAxios.get.mockResolvedValue({
139
- data: {
140
- orderbook: {}
141
- }
142
- });
143
-
144
- const orderbook = await exchange.getOrderBook('TEST-MARKET');
145
-
146
- expect(orderbook.bids).toEqual([]);
147
- expect(orderbook.asks).toEqual([]);
148
- });
149
- });
@@ -1,174 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi searchMarkets() Test
6
- *
7
- * What: Tests the search functionality for finding markets by query string.
8
- * Why: Search is a critical user-facing feature that must work reliably.
9
- * How: Mocks API responses with various market data and verifies filtering logic.
10
- */
11
-
12
- jest.mock('axios');
13
- const mockedAxios = axios as jest.Mocked<typeof axios>;
14
-
15
- describe('KalshiExchange - searchMarkets', () => {
16
- let exchange: KalshiExchange;
17
-
18
- beforeEach(() => {
19
- exchange = new KalshiExchange();
20
- jest.clearAllMocks();
21
- });
22
-
23
- it('should filter markets by title', async () => {
24
- mockedAxios.get.mockResolvedValue({
25
- data: {
26
- events: [
27
- {
28
- event_ticker: 'FED-2025',
29
- title: 'Federal Reserve Interest Rate Decision',
30
- markets: [{
31
- ticker: 'FED-25JAN-B4.75',
32
- expiration_time: '2025-01-29T00:00:00Z',
33
- last_price: 5200
34
- }]
35
- },
36
- {
37
- event_ticker: 'PRES-2024',
38
- title: 'Presidential Election 2024',
39
- markets: [{
40
- ticker: 'PRES-2024-TRUMP',
41
- expiration_time: '2024-11-05T00:00:00Z',
42
- last_price: 4800
43
- }]
44
- }
45
- ],
46
- cursor: null
47
- }
48
- });
49
-
50
- const results = await exchange.searchMarkets('federal');
51
-
52
- expect(results.length).toBeGreaterThan(0);
53
- expect(results[0].title.toLowerCase()).toContain('federal');
54
- });
55
-
56
- it('should filter markets by description', async () => {
57
- mockedAxios.get.mockResolvedValue({
58
- data: {
59
- events: [
60
- {
61
- event_ticker: 'TEST-EVENT',
62
- title: 'Test Event',
63
- sub_title: 'This is about climate change policy',
64
- markets: [{
65
- ticker: 'TEST-MARKET',
66
- subtitle: 'Climate policy details',
67
- expiration_time: '2025-12-31T00:00:00Z',
68
- last_price: 5000
69
- }]
70
- }
71
- ],
72
- cursor: null
73
- }
74
- });
75
-
76
- const results = await exchange.searchMarkets('climate');
77
-
78
- expect(results.length).toBeGreaterThan(0);
79
- });
80
-
81
- it('should respect limit parameter', async () => {
82
- const mockEvents = Array.from({ length: 50 }, (_, i) => ({
83
- event_ticker: `EVENT-${i}`,
84
- title: `Test Market ${i}`,
85
- markets: [{
86
- ticker: `MARKET-${i}`,
87
- expiration_time: '2025-12-31T00:00:00Z',
88
- last_price: 5000
89
- }]
90
- }));
91
-
92
- mockedAxios.get.mockResolvedValue({
93
- data: {
94
- events: mockEvents,
95
- cursor: null
96
- }
97
- });
98
-
99
- const results = await exchange.searchMarkets('test', { limit: 5 });
100
-
101
- expect(results.length).toBeLessThanOrEqual(5);
102
- });
103
-
104
- it('should return empty array when no matches found', async () => {
105
- mockedAxios.get.mockResolvedValue({
106
- data: {
107
- events: [{
108
- event_ticker: 'UNRELATED',
109
- title: 'Completely Different Topic',
110
- markets: [{
111
- ticker: 'UNRELATED-MARKET',
112
- expiration_time: '2025-12-31T00:00:00Z',
113
- last_price: 5000
114
- }]
115
- }],
116
- cursor: null
117
- }
118
- });
119
-
120
- const results = await exchange.searchMarkets('nonexistent query string');
121
-
122
- expect(results).toEqual([]);
123
- });
124
-
125
- it('should handle search errors gracefully', async () => {
126
- mockedAxios.get.mockRejectedValue(new Error('Search failed'));
127
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
128
-
129
- const results = await exchange.searchMarkets('test');
130
-
131
- expect(results).toEqual([]);
132
- expect(consoleSpy).toHaveBeenCalled();
133
- consoleSpy.mockRestore();
134
- });
135
-
136
- it('should be case-insensitive', async () => {
137
- mockedAxios.get.mockResolvedValue({
138
- data: {
139
- events: [{
140
- event_ticker: 'TEST',
141
- title: 'FEDERAL RESERVE',
142
- markets: [{
143
- ticker: 'TEST-MARKET',
144
- expiration_time: '2025-12-31T00:00:00Z',
145
- last_price: 5000
146
- }]
147
- }],
148
- cursor: null
149
- }
150
- });
151
-
152
- const resultsLower = await exchange.searchMarkets('federal');
153
-
154
- jest.clearAllMocks();
155
- mockedAxios.get.mockResolvedValue({
156
- data: {
157
- events: [{
158
- event_ticker: 'TEST',
159
- title: 'FEDERAL RESERVE',
160
- markets: [{
161
- ticker: 'TEST-MARKET',
162
- expiration_time: '2025-12-31T00:00:00Z',
163
- last_price: 5000
164
- }]
165
- }],
166
- cursor: null
167
- }
168
- });
169
-
170
- const resultsUpper = await exchange.searchMarkets('FEDERAL');
171
-
172
- expect(resultsLower.length).toBe(resultsUpper.length);
173
- });
174
- });
@@ -1,44 +0,0 @@
1
- import axios from 'axios';
2
- import { KalshiExchange } from '../../../src/exchanges/Kalshi';
3
-
4
- /**
5
- * Kalshi Volume Fallback Test
6
- *
7
- * What: Tests the fallback logic for volume reporting.
8
- * Why: Kalshi sometimes omits volume_24h for certain markets. In these cases,
9
- * we fall back to total volume to ensure we don't display 0 volume for active markets.
10
- * How: Mocks a market with 0 volume_24h but non-zero total volume.
11
- */
12
-
13
- jest.mock('axios');
14
- const mockedAxios = axios as jest.Mocked<typeof axios>;
15
-
16
- describe('KalshiExchange - Volume Fallback Mechanism', () => {
17
- let exchange: KalshiExchange;
18
-
19
- beforeEach(() => {
20
- exchange = new KalshiExchange();
21
- jest.clearAllMocks();
22
- });
23
-
24
- it('should fallback to total volume if 24h volume is missing or zero', async () => {
25
- mockedAxios.get.mockResolvedValue({
26
- data: {
27
- events: [{
28
- event_ticker: 'KXTEST',
29
- title: 'Test Event',
30
- markets: [{
31
- ticker: 'KXTEST-001',
32
- volume: 5000,
33
- volume_24h: 0,
34
- expiration_time: "2024-12-31T00:00:00Z"
35
- }]
36
- }],
37
- cursor: ''
38
- }
39
- });
40
-
41
- const markets = await exchange.fetchMarkets();
42
- expect(markets[0].volume24h).toBe(5000);
43
- });
44
- });