pmxt-core 0.0.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/.env +5 -0
- package/dist/BaseExchange.d.ts +74 -0
- package/dist/BaseExchange.js +72 -0
- package/dist/exchanges/kalshi/auth.d.ts +23 -0
- package/dist/exchanges/kalshi/auth.js +99 -0
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchMarkets.js +110 -0
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchOHLCV.js +78 -0
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
- package/dist/exchanges/kalshi/fetchOrderBook.js +32 -0
- package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchTrades.js +31 -0
- package/dist/exchanges/kalshi/getMarketsBySlug.d.ts +7 -0
- package/dist/exchanges/kalshi/getMarketsBySlug.js +62 -0
- package/dist/exchanges/kalshi/index.d.ts +21 -0
- package/dist/exchanges/kalshi/index.js +273 -0
- package/dist/exchanges/kalshi/kalshi.test.d.ts +1 -0
- package/dist/exchanges/kalshi/kalshi.test.js +309 -0
- package/dist/exchanges/kalshi/searchMarkets.d.ts +3 -0
- package/dist/exchanges/kalshi/searchMarkets.js +28 -0
- package/dist/exchanges/kalshi/utils.d.ts +5 -0
- package/dist/exchanges/kalshi/utils.js +85 -0
- package/dist/exchanges/polymarket/auth.d.ts +32 -0
- package/dist/exchanges/polymarket/auth.js +98 -0
- package/dist/exchanges/polymarket/fetchMarkets.d.ts +3 -0
- package/dist/exchanges/polymarket/fetchMarkets.js +75 -0
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +7 -0
- package/dist/exchanges/polymarket/fetchOHLCV.js +73 -0
- package/dist/exchanges/polymarket/fetchOrderBook.d.ts +6 -0
- package/dist/exchanges/polymarket/fetchOrderBook.js +38 -0
- package/dist/exchanges/polymarket/fetchPositions.d.ts +2 -0
- package/dist/exchanges/polymarket/fetchPositions.js +27 -0
- package/dist/exchanges/polymarket/fetchTrades.d.ts +11 -0
- package/dist/exchanges/polymarket/fetchTrades.js +59 -0
- package/dist/exchanges/polymarket/getMarketsBySlug.d.ts +7 -0
- package/dist/exchanges/polymarket/getMarketsBySlug.js +39 -0
- package/dist/exchanges/polymarket/index.d.ts +23 -0
- package/dist/exchanges/polymarket/index.js +216 -0
- package/dist/exchanges/polymarket/searchMarkets.d.ts +3 -0
- package/dist/exchanges/polymarket/searchMarkets.js +35 -0
- package/dist/exchanges/polymarket/utils.d.ts +7 -0
- package/dist/exchanges/polymarket/utils.js +95 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +35 -0
- package/dist/server/app.d.ts +1 -0
- package/dist/server/app.js +79 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +29 -0
- package/dist/server/utils/lock-file.d.ts +12 -0
- package/dist/server/utils/lock-file.js +81 -0
- package/dist/server/utils/port-manager.d.ts +4 -0
- package/dist/server/utils/port-manager.js +68 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +5 -0
- package/index.js +1 -0
- package/package.json +11 -0
- package/pmxt-core-0.4.4.tgz +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PolymarketExchange = void 0;
|
|
4
|
+
const BaseExchange_1 = require("../../BaseExchange");
|
|
5
|
+
const fetchMarkets_1 = require("./fetchMarkets");
|
|
6
|
+
const searchMarkets_1 = require("./searchMarkets");
|
|
7
|
+
const getMarketsBySlug_1 = require("./getMarketsBySlug");
|
|
8
|
+
const fetchOHLCV_1 = require("./fetchOHLCV");
|
|
9
|
+
const fetchOrderBook_1 = require("./fetchOrderBook");
|
|
10
|
+
const fetchTrades_1 = require("./fetchTrades");
|
|
11
|
+
const fetchPositions_1 = require("./fetchPositions");
|
|
12
|
+
const auth_1 = require("./auth");
|
|
13
|
+
const clob_client_1 = require("@polymarket/clob-client");
|
|
14
|
+
class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
15
|
+
constructor(credentials) {
|
|
16
|
+
super(credentials);
|
|
17
|
+
// Initialize auth if credentials are provided
|
|
18
|
+
if (credentials?.privateKey) {
|
|
19
|
+
this.auth = new auth_1.PolymarketAuth(credentials);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
get name() {
|
|
23
|
+
return 'Polymarket';
|
|
24
|
+
}
|
|
25
|
+
async fetchMarkets(params) {
|
|
26
|
+
return (0, fetchMarkets_1.fetchMarkets)(params);
|
|
27
|
+
}
|
|
28
|
+
async searchMarkets(query, params) {
|
|
29
|
+
return (0, searchMarkets_1.searchMarkets)(query, params);
|
|
30
|
+
}
|
|
31
|
+
async getMarketsBySlug(slug) {
|
|
32
|
+
return (0, getMarketsBySlug_1.getMarketsBySlug)(slug);
|
|
33
|
+
}
|
|
34
|
+
async fetchOHLCV(id, params) {
|
|
35
|
+
return (0, fetchOHLCV_1.fetchOHLCV)(id, params);
|
|
36
|
+
}
|
|
37
|
+
async fetchOrderBook(id) {
|
|
38
|
+
return (0, fetchOrderBook_1.fetchOrderBook)(id);
|
|
39
|
+
}
|
|
40
|
+
async fetchTrades(id, params) {
|
|
41
|
+
return (0, fetchTrades_1.fetchTrades)(id, params);
|
|
42
|
+
}
|
|
43
|
+
// ----------------------------------------------------------------------------
|
|
44
|
+
// Trading Methods
|
|
45
|
+
// ----------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Ensure authentication is initialized before trading operations.
|
|
48
|
+
*/
|
|
49
|
+
ensureAuth() {
|
|
50
|
+
if (!this.auth) {
|
|
51
|
+
throw new Error('Trading operations require authentication. ' +
|
|
52
|
+
'Initialize PolymarketExchange with credentials: new PolymarketExchange({ privateKey: "0x..." })');
|
|
53
|
+
}
|
|
54
|
+
return this.auth;
|
|
55
|
+
}
|
|
56
|
+
async createOrder(params) {
|
|
57
|
+
const auth = this.ensureAuth();
|
|
58
|
+
const client = await auth.getClobClient();
|
|
59
|
+
// Map side to Polymarket enum
|
|
60
|
+
const side = params.side.toUpperCase() === 'BUY' ? clob_client_1.Side.BUY : clob_client_1.Side.SELL;
|
|
61
|
+
// For limit orders, price is required
|
|
62
|
+
if (params.type === 'limit' && !params.price) {
|
|
63
|
+
throw new Error('Price is required for limit orders');
|
|
64
|
+
}
|
|
65
|
+
// For market orders, use max slippage: 0.99 for BUY (willing to pay up to 99%), 0.01 for SELL (willing to accept down to 1%)
|
|
66
|
+
const price = params.price || (side === clob_client_1.Side.BUY ? 0.99 : 0.01);
|
|
67
|
+
try {
|
|
68
|
+
// We use createAndPostOrder which handles signing and posting
|
|
69
|
+
const response = await client.createAndPostOrder({
|
|
70
|
+
tokenID: params.outcomeId,
|
|
71
|
+
price: price,
|
|
72
|
+
side: side,
|
|
73
|
+
size: params.amount,
|
|
74
|
+
feeRateBps: 0,
|
|
75
|
+
}, {
|
|
76
|
+
tickSize: "0.01"
|
|
77
|
+
});
|
|
78
|
+
if (!response || !response.success) {
|
|
79
|
+
throw new Error(response?.errorMsg || 'Order placement failed');
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
id: response.orderID,
|
|
83
|
+
marketId: params.marketId,
|
|
84
|
+
outcomeId: params.outcomeId,
|
|
85
|
+
side: params.side,
|
|
86
|
+
type: params.type,
|
|
87
|
+
price: price,
|
|
88
|
+
amount: params.amount,
|
|
89
|
+
status: 'open',
|
|
90
|
+
filled: 0,
|
|
91
|
+
remaining: params.amount,
|
|
92
|
+
timestamp: Date.now()
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error("Polymarket createOrder error:", error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async cancelOrder(orderId) {
|
|
101
|
+
const auth = this.ensureAuth();
|
|
102
|
+
const client = await auth.getClobClient();
|
|
103
|
+
try {
|
|
104
|
+
await client.cancelOrder({ orderID: orderId });
|
|
105
|
+
return {
|
|
106
|
+
id: orderId,
|
|
107
|
+
marketId: 'unknown',
|
|
108
|
+
outcomeId: 'unknown',
|
|
109
|
+
side: 'buy',
|
|
110
|
+
type: 'limit',
|
|
111
|
+
amount: 0,
|
|
112
|
+
status: 'cancelled',
|
|
113
|
+
filled: 0,
|
|
114
|
+
remaining: 0,
|
|
115
|
+
timestamp: Date.now()
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error("Polymarket cancelOrder error:", error);
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async fetchOrder(orderId) {
|
|
124
|
+
const auth = this.ensureAuth();
|
|
125
|
+
const client = await auth.getClobClient();
|
|
126
|
+
try {
|
|
127
|
+
const order = await client.getOrder(orderId);
|
|
128
|
+
return {
|
|
129
|
+
id: order.id,
|
|
130
|
+
marketId: order.market || 'unknown',
|
|
131
|
+
outcomeId: order.asset_id,
|
|
132
|
+
side: order.side.toLowerCase(),
|
|
133
|
+
type: order.order_type === 'GTC' ? 'limit' : 'market',
|
|
134
|
+
price: parseFloat(order.price),
|
|
135
|
+
amount: parseFloat(order.original_size),
|
|
136
|
+
status: order.status, // Needs precise mapping
|
|
137
|
+
filled: parseFloat(order.size_matched),
|
|
138
|
+
remaining: parseFloat(order.original_size) - parseFloat(order.size_matched),
|
|
139
|
+
timestamp: order.created_at * 1000
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error("Polymarket fetchOrder error:", error);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async fetchOpenOrders(marketId) {
|
|
148
|
+
const auth = this.ensureAuth();
|
|
149
|
+
const client = await auth.getClobClient();
|
|
150
|
+
try {
|
|
151
|
+
const orders = await client.getOpenOrders({
|
|
152
|
+
market: marketId
|
|
153
|
+
});
|
|
154
|
+
return orders.map((o) => ({
|
|
155
|
+
id: o.id,
|
|
156
|
+
marketId: o.market || 'unknown',
|
|
157
|
+
outcomeId: o.asset_id,
|
|
158
|
+
side: o.side.toLowerCase(),
|
|
159
|
+
type: 'limit',
|
|
160
|
+
price: parseFloat(o.price),
|
|
161
|
+
amount: parseFloat(o.original_size),
|
|
162
|
+
status: 'open',
|
|
163
|
+
filled: parseFloat(o.size_matched),
|
|
164
|
+
remaining: parseFloat(o.size_left || (parseFloat(o.original_size) - parseFloat(o.size_matched))),
|
|
165
|
+
timestamp: o.created_at * 1000
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error("Polymarket fetchOpenOrders error:", error);
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async fetchPositions() {
|
|
174
|
+
const auth = this.ensureAuth();
|
|
175
|
+
const address = auth.getAddress();
|
|
176
|
+
return (0, fetchPositions_1.fetchPositions)(address);
|
|
177
|
+
}
|
|
178
|
+
async fetchBalance() {
|
|
179
|
+
const auth = this.ensureAuth();
|
|
180
|
+
const client = await auth.getClobClient();
|
|
181
|
+
try {
|
|
182
|
+
// 1. Fetch raw collateral balance (USDC)
|
|
183
|
+
// Polymarket relies strictly on USDC (Polygon) which has 6 decimals.
|
|
184
|
+
const USDC_DECIMALS = 6;
|
|
185
|
+
const balRes = await client.getBalanceAllowance({
|
|
186
|
+
asset_type: clob_client_1.AssetType.COLLATERAL
|
|
187
|
+
});
|
|
188
|
+
const rawBalance = parseFloat(balRes.balance);
|
|
189
|
+
const total = rawBalance / Math.pow(10, USDC_DECIMALS);
|
|
190
|
+
// 2. Fetch open orders to calculate locked funds
|
|
191
|
+
// We only care about BUY orders for USDC balance locking
|
|
192
|
+
const openOrders = await client.getOpenOrders({});
|
|
193
|
+
let locked = 0;
|
|
194
|
+
if (openOrders && Array.isArray(openOrders)) {
|
|
195
|
+
for (const order of openOrders) {
|
|
196
|
+
if (order.side === clob_client_1.Side.BUY) {
|
|
197
|
+
const remainingSize = parseFloat(order.original_size) - parseFloat(order.size_matched);
|
|
198
|
+
const price = parseFloat(order.price);
|
|
199
|
+
locked += remainingSize * price;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return [{
|
|
204
|
+
currency: 'USDC',
|
|
205
|
+
total: total,
|
|
206
|
+
available: total - locked, // Available for new trades
|
|
207
|
+
locked: locked
|
|
208
|
+
}];
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
console.error("Polymarket fetchBalance error:", error);
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
exports.PolymarketExchange = PolymarketExchange;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.searchMarkets = searchMarkets;
|
|
4
|
+
const fetchMarkets_1 = require("./fetchMarkets");
|
|
5
|
+
async function searchMarkets(query, params) {
|
|
6
|
+
// Polymarket Gamma API doesn't support native search
|
|
7
|
+
// Fetch all active markets and filter client-side
|
|
8
|
+
const searchLimit = 100000; // Fetch all markets for comprehensive search
|
|
9
|
+
try {
|
|
10
|
+
// Fetch markets with a higher limit
|
|
11
|
+
const markets = await (0, fetchMarkets_1.fetchMarkets)({
|
|
12
|
+
...params,
|
|
13
|
+
limit: searchLimit
|
|
14
|
+
});
|
|
15
|
+
// Client-side text filtering
|
|
16
|
+
const lowerQuery = query.toLowerCase();
|
|
17
|
+
const searchIn = params?.searchIn || 'title'; // Default to title-only search
|
|
18
|
+
const filtered = markets.filter(market => {
|
|
19
|
+
const titleMatch = (market.title || '').toLowerCase().includes(lowerQuery);
|
|
20
|
+
const descMatch = (market.description || '').toLowerCase().includes(lowerQuery);
|
|
21
|
+
if (searchIn === 'title')
|
|
22
|
+
return titleMatch;
|
|
23
|
+
if (searchIn === 'description')
|
|
24
|
+
return descMatch;
|
|
25
|
+
return titleMatch || descMatch; // 'both'
|
|
26
|
+
});
|
|
27
|
+
// Apply limit to filtered results
|
|
28
|
+
const limit = params?.limit || 20;
|
|
29
|
+
return filtered.slice(0, limit);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error("Error searching Polymarket data:", error);
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { UnifiedMarket, CandleInterval } from '../../types';
|
|
2
|
+
export declare const GAMMA_API_URL = "https://gamma-api.polymarket.com/events";
|
|
3
|
+
export declare const CLOB_API_URL = "https://clob.polymarket.com";
|
|
4
|
+
export declare function mapMarketToUnified(event: any, market: any, options?: {
|
|
5
|
+
useQuestionAsCandidateFallback?: boolean;
|
|
6
|
+
}): UnifiedMarket | null;
|
|
7
|
+
export declare function mapIntervalToFidelity(interval: CandleInterval): number;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLOB_API_URL = exports.GAMMA_API_URL = void 0;
|
|
4
|
+
exports.mapMarketToUnified = mapMarketToUnified;
|
|
5
|
+
exports.mapIntervalToFidelity = mapIntervalToFidelity;
|
|
6
|
+
exports.GAMMA_API_URL = 'https://gamma-api.polymarket.com/events';
|
|
7
|
+
exports.CLOB_API_URL = 'https://clob.polymarket.com';
|
|
8
|
+
function mapMarketToUnified(event, market, options = {}) {
|
|
9
|
+
if (!market)
|
|
10
|
+
return null;
|
|
11
|
+
const outcomes = [];
|
|
12
|
+
// Polymarket Gamma often returns 'outcomes' and 'outcomePrices' as stringified JSON keys.
|
|
13
|
+
let outcomeLabels = [];
|
|
14
|
+
let outcomePrices = [];
|
|
15
|
+
try {
|
|
16
|
+
outcomeLabels = typeof market.outcomes === 'string' ? JSON.parse(market.outcomes) : (market.outcomes || []);
|
|
17
|
+
outcomePrices = typeof market.outcomePrices === 'string' ? JSON.parse(market.outcomePrices) : (market.outcomePrices || []);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.warn(`Error parsing outcomes for market ${market.id}:`, e);
|
|
21
|
+
}
|
|
22
|
+
// Extract CLOB token IDs for granular operations
|
|
23
|
+
let clobTokenIds = [];
|
|
24
|
+
try {
|
|
25
|
+
clobTokenIds = typeof market.clobTokenIds === 'string' ? JSON.parse(market.clobTokenIds) : (market.clobTokenIds || []);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
// console.warn(`Error parsing clobTokenIds for market ${market.id}`, e);
|
|
29
|
+
}
|
|
30
|
+
// Extract candidate/option name from market question for better outcome labels
|
|
31
|
+
let candidateName = null;
|
|
32
|
+
if (market.groupItemTitle) {
|
|
33
|
+
candidateName = market.groupItemTitle;
|
|
34
|
+
}
|
|
35
|
+
else if (market.question && options.useQuestionAsCandidateFallback) {
|
|
36
|
+
// Fallback or sometimes question is the candidate name in nested structures
|
|
37
|
+
// Only used if explicitly requested (e.g. for getMarketsBySlug)
|
|
38
|
+
candidateName = market.question;
|
|
39
|
+
}
|
|
40
|
+
if (outcomeLabels.length > 0) {
|
|
41
|
+
outcomeLabels.forEach((label, index) => {
|
|
42
|
+
const rawPrice = outcomePrices[index] || "0";
|
|
43
|
+
// For Yes/No markets with specific candidates, use the candidate name
|
|
44
|
+
let outcomeLabel = label;
|
|
45
|
+
if (candidateName && label.toLowerCase() === 'yes') {
|
|
46
|
+
outcomeLabel = candidateName;
|
|
47
|
+
}
|
|
48
|
+
else if (candidateName && label.toLowerCase() === 'no') {
|
|
49
|
+
outcomeLabel = `Not ${candidateName}`;
|
|
50
|
+
}
|
|
51
|
+
// 24h Price Change
|
|
52
|
+
// Polymarket API provides 'oneDayPriceChange' on the market object
|
|
53
|
+
let priceChange = 0;
|
|
54
|
+
if (index === 0 || label.toLowerCase() === 'yes' || (candidateName && label === candidateName)) {
|
|
55
|
+
priceChange = Number(market.oneDayPriceChange || 0);
|
|
56
|
+
}
|
|
57
|
+
outcomes.push({
|
|
58
|
+
id: clobTokenIds[index] || String(index), // Use CLOB Token ID as the primary ID
|
|
59
|
+
label: outcomeLabel,
|
|
60
|
+
price: parseFloat(rawPrice) || 0,
|
|
61
|
+
priceChange24h: priceChange,
|
|
62
|
+
metadata: {
|
|
63
|
+
// clobTokenId is now the main ID, but keeping it in metadata for backward compat if needed
|
|
64
|
+
clobTokenId: clobTokenIds[index]
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
id: market.id,
|
|
71
|
+
title: market.question ? `${event.title} - ${market.question}` : event.title,
|
|
72
|
+
description: market.description || event.description,
|
|
73
|
+
outcomes: outcomes,
|
|
74
|
+
resolutionDate: market.endDate ? new Date(market.endDate) : (market.end_date_iso ? new Date(market.end_date_iso) : new Date()),
|
|
75
|
+
volume24h: Number(market.volume24hr || market.volume_24h || 0),
|
|
76
|
+
volume: Number(market.volume || 0),
|
|
77
|
+
liquidity: Number(market.liquidity || market.rewards?.liquidity || 0),
|
|
78
|
+
openInterest: Number(market.openInterest || market.open_interest || 0),
|
|
79
|
+
url: `https://polymarket.com/event/${event.slug}`,
|
|
80
|
+
image: event.image || market.image || `https://polymarket.com/api/og?slug=${event.slug}`,
|
|
81
|
+
category: event.category || event.tags?.[0]?.label,
|
|
82
|
+
tags: event.tags?.map((t) => t.label) || []
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function mapIntervalToFidelity(interval) {
|
|
86
|
+
const mapping = {
|
|
87
|
+
'1m': 1,
|
|
88
|
+
'5m': 5,
|
|
89
|
+
'15m': 15,
|
|
90
|
+
'1h': 60,
|
|
91
|
+
'6h': 360,
|
|
92
|
+
'1d': 1440
|
|
93
|
+
};
|
|
94
|
+
return mapping[interval];
|
|
95
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './BaseExchange';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export * from './exchanges/polymarket';
|
|
4
|
+
export * from './exchanges/kalshi';
|
|
5
|
+
export * from './server/app';
|
|
6
|
+
export * from './server/utils/port-manager';
|
|
7
|
+
export * from './server/utils/lock-file';
|
|
8
|
+
import { PolymarketExchange } from './exchanges/polymarket';
|
|
9
|
+
import { KalshiExchange } from './exchanges/kalshi';
|
|
10
|
+
declare const pmxt: {
|
|
11
|
+
polymarket: typeof PolymarketExchange;
|
|
12
|
+
kalshi: typeof KalshiExchange;
|
|
13
|
+
Polymarket: typeof PolymarketExchange;
|
|
14
|
+
Kalshi: typeof KalshiExchange;
|
|
15
|
+
};
|
|
16
|
+
export declare const polymarket: typeof PolymarketExchange;
|
|
17
|
+
export declare const kalshi: typeof KalshiExchange;
|
|
18
|
+
export default pmxt;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.kalshi = exports.polymarket = void 0;
|
|
18
|
+
__exportStar(require("./BaseExchange"), exports);
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
__exportStar(require("./exchanges/polymarket"), exports);
|
|
21
|
+
__exportStar(require("./exchanges/kalshi"), exports);
|
|
22
|
+
__exportStar(require("./server/app"), exports);
|
|
23
|
+
__exportStar(require("./server/utils/port-manager"), exports);
|
|
24
|
+
__exportStar(require("./server/utils/lock-file"), exports);
|
|
25
|
+
const polymarket_1 = require("./exchanges/polymarket");
|
|
26
|
+
const kalshi_1 = require("./exchanges/kalshi");
|
|
27
|
+
const pmxt = {
|
|
28
|
+
polymarket: polymarket_1.PolymarketExchange,
|
|
29
|
+
kalshi: kalshi_1.KalshiExchange,
|
|
30
|
+
Polymarket: polymarket_1.PolymarketExchange,
|
|
31
|
+
Kalshi: kalshi_1.KalshiExchange
|
|
32
|
+
};
|
|
33
|
+
exports.polymarket = polymarket_1.PolymarketExchange;
|
|
34
|
+
exports.kalshi = kalshi_1.KalshiExchange;
|
|
35
|
+
exports.default = pmxt;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startServer(port: number): Promise<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startServer = startServer;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const cors_1 = __importDefault(require("cors"));
|
|
9
|
+
const polymarket_1 = require("../exchanges/polymarket");
|
|
10
|
+
const kalshi_1 = require("../exchanges/kalshi");
|
|
11
|
+
// Singleton instances for local usage
|
|
12
|
+
const exchanges = {
|
|
13
|
+
polymarket: null,
|
|
14
|
+
kalshi: null
|
|
15
|
+
};
|
|
16
|
+
async function startServer(port) {
|
|
17
|
+
const app = (0, express_1.default)();
|
|
18
|
+
app.use((0, cors_1.default)());
|
|
19
|
+
app.use(express_1.default.json());
|
|
20
|
+
// Health check
|
|
21
|
+
app.get('/health', (req, res) => {
|
|
22
|
+
res.json({ status: 'ok', timestamp: Date.now() });
|
|
23
|
+
});
|
|
24
|
+
// API endpoint: POST /api/:exchange/:method
|
|
25
|
+
// Body: { args: any[] }
|
|
26
|
+
app.post('/api/:exchange/:method', async (req, res, next) => {
|
|
27
|
+
try {
|
|
28
|
+
const exchangeName = req.params.exchange.toLowerCase();
|
|
29
|
+
const methodName = req.params.method;
|
|
30
|
+
const args = Array.isArray(req.body.args) ? req.body.args : [];
|
|
31
|
+
// 1. Get or Initialize Exchange
|
|
32
|
+
if (!exchanges[exchangeName]) {
|
|
33
|
+
exchanges[exchangeName] = createExchange(exchangeName);
|
|
34
|
+
}
|
|
35
|
+
const exchange = exchanges[exchangeName];
|
|
36
|
+
// 2. Validate Method
|
|
37
|
+
if (typeof exchange[methodName] !== 'function') {
|
|
38
|
+
res.status(404).json({ success: false, error: `Method '${methodName}' not found on ${exchangeName}` });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// 3. Execute with direct argument spreading
|
|
42
|
+
const result = await exchange[methodName](...args);
|
|
43
|
+
res.json({ success: true, data: result });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
next(error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// Error handler
|
|
50
|
+
app.use((error, req, res, next) => {
|
|
51
|
+
console.error('Error:', error);
|
|
52
|
+
res.status(error.status || 500).json({
|
|
53
|
+
success: false,
|
|
54
|
+
error: {
|
|
55
|
+
message: error.message || 'Internal server error',
|
|
56
|
+
// stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
return app.listen(port);
|
|
61
|
+
}
|
|
62
|
+
function createExchange(name) {
|
|
63
|
+
switch (name) {
|
|
64
|
+
case 'polymarket':
|
|
65
|
+
return new polymarket_1.PolymarketExchange({
|
|
66
|
+
privateKey: process.env.POLYMARKET_PK || process.env.POLYMARKET_PRIVATE_KEY,
|
|
67
|
+
apiKey: process.env.POLYMARKET_API_KEY,
|
|
68
|
+
apiSecret: process.env.POLYMARKET_API_SECRET,
|
|
69
|
+
passphrase: process.env.POLYMARKET_PASSPHRASE
|
|
70
|
+
});
|
|
71
|
+
case 'kalshi':
|
|
72
|
+
return new kalshi_1.KalshiExchange({
|
|
73
|
+
apiKey: process.env.KALSHI_API_KEY,
|
|
74
|
+
privateKey: process.env.KALSHI_PRIVATE_KEY
|
|
75
|
+
});
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unknown exchange: ${name}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
require("dotenv/config");
|
|
5
|
+
const app_1 = require("./app");
|
|
6
|
+
const port_manager_1 = require("./utils/port-manager");
|
|
7
|
+
const lock_file_1 = require("./utils/lock-file");
|
|
8
|
+
async function main() {
|
|
9
|
+
const portManager = new port_manager_1.PortManager();
|
|
10
|
+
const port = await portManager.findAvailablePort(3847); // Default port
|
|
11
|
+
const lockFile = new lock_file_1.LockFile();
|
|
12
|
+
await lockFile.create(port, process.pid);
|
|
13
|
+
const server = await (0, app_1.startServer)(port);
|
|
14
|
+
console.log(`PMXT Sidecar Server running on http://localhost:${port}`);
|
|
15
|
+
console.log(`Lock file created at ${lockFile.lockPath}`);
|
|
16
|
+
// Graceful shutdown
|
|
17
|
+
const shutdown = async () => {
|
|
18
|
+
console.log('\nShutting down gracefully...');
|
|
19
|
+
server.close();
|
|
20
|
+
await lockFile.remove();
|
|
21
|
+
process.exit(0);
|
|
22
|
+
};
|
|
23
|
+
process.on('SIGTERM', shutdown);
|
|
24
|
+
process.on('SIGINT', shutdown);
|
|
25
|
+
}
|
|
26
|
+
main().catch((error) => {
|
|
27
|
+
console.error('Failed to start server:', error);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class LockFile {
|
|
2
|
+
lockPath: string;
|
|
3
|
+
constructor();
|
|
4
|
+
create(port: number, pid: number): Promise<void>;
|
|
5
|
+
read(): Promise<{
|
|
6
|
+
port: number;
|
|
7
|
+
pid: number;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
} | null>;
|
|
10
|
+
remove(): Promise<void>;
|
|
11
|
+
isServerRunning(): Promise<boolean>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LockFile = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
class LockFile {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.lockPath = path.join(os.homedir(), '.pmxt', 'server.lock');
|
|
43
|
+
}
|
|
44
|
+
async create(port, pid) {
|
|
45
|
+
await fs.mkdir(path.dirname(this.lockPath), { recursive: true });
|
|
46
|
+
await fs.writeFile(this.lockPath, JSON.stringify({ port, pid, timestamp: Date.now() }, null, 2));
|
|
47
|
+
}
|
|
48
|
+
async read() {
|
|
49
|
+
try {
|
|
50
|
+
const data = await fs.readFile(this.lockPath, 'utf-8');
|
|
51
|
+
return JSON.parse(data);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async remove() {
|
|
58
|
+
try {
|
|
59
|
+
await fs.unlink(this.lockPath);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Ignore errors if file doesn't exist
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async isServerRunning() {
|
|
66
|
+
const lock = await this.read();
|
|
67
|
+
if (!lock)
|
|
68
|
+
return false;
|
|
69
|
+
// Check if process is still alive
|
|
70
|
+
try {
|
|
71
|
+
process.kill(lock.pid, 0); // Signal 0 checks existence without killing
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Process doesn't exist, remove stale lock file
|
|
76
|
+
await this.remove();
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.LockFile = LockFile;
|