pmxt-core 1.1.3 → 1.2.0
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/exchanges/kalshi/fetchOHLCV.js +12 -1
- package/dist/exchanges/kalshi/utils.js +4 -1
- package/dist/exchanges/polymarket/fetchOHLCV.js +38 -10
- package/dist/exchanges/polymarket/utils.js +4 -1
- package/dist/types.d.ts +4 -0
- package/dist/utils/market-utils.d.ts +5 -0
- package/dist/utils/market-utils.js +42 -0
- package/package.json +3 -3
|
@@ -25,7 +25,18 @@ async function fetchOHLCV(id, params) {
|
|
|
25
25
|
let startTs = now - (24 * 60 * 60);
|
|
26
26
|
let endTs = now;
|
|
27
27
|
// Helper to handle string dates (from JSON)
|
|
28
|
-
|
|
28
|
+
// IMPORTANT: Python sends naive datetimes as ISO strings without 'Z' suffix.
|
|
29
|
+
// We must treat these as UTC, not local time.
|
|
30
|
+
const ensureDate = (d) => {
|
|
31
|
+
if (typeof d === 'string') {
|
|
32
|
+
// If string doesn't end with 'Z' and doesn't have timezone offset, append 'Z'
|
|
33
|
+
if (!d.endsWith('Z') && !d.match(/[+-]\d{2}:\d{2}$/)) {
|
|
34
|
+
return new Date(d + 'Z');
|
|
35
|
+
}
|
|
36
|
+
return new Date(d);
|
|
37
|
+
}
|
|
38
|
+
return d;
|
|
39
|
+
};
|
|
29
40
|
const pStart = params.start ? ensureDate(params.start) : undefined;
|
|
30
41
|
const pEnd = params.end ? ensureDate(params.end) : undefined;
|
|
31
42
|
if (pStart) {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.KALSHI_SERIES_URL = exports.KALSHI_API_URL = void 0;
|
|
4
4
|
exports.mapMarketToUnified = mapMarketToUnified;
|
|
5
5
|
exports.mapIntervalToKalshi = mapIntervalToKalshi;
|
|
6
|
+
const market_utils_1 = require("../../utils/market-utils");
|
|
6
7
|
exports.KALSHI_API_URL = "https://api.elections.kalshi.com/trade-api/v2/events";
|
|
7
8
|
exports.KALSHI_SERIES_URL = "https://api.elections.kalshi.com/trade-api/v2/series";
|
|
8
9
|
function mapMarketToUnified(event, market) {
|
|
@@ -57,7 +58,7 @@ function mapMarketToUnified(event, market) {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
const um = {
|
|
61
62
|
id: market.ticker,
|
|
62
63
|
title: event.title,
|
|
63
64
|
description: market.rules_primary || market.rules_secondary || "",
|
|
@@ -71,6 +72,8 @@ function mapMarketToUnified(event, market) {
|
|
|
71
72
|
category: event.category,
|
|
72
73
|
tags: unifiedTags
|
|
73
74
|
};
|
|
75
|
+
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
76
|
+
return um;
|
|
74
77
|
}
|
|
75
78
|
function mapIntervalToKalshi(interval) {
|
|
76
79
|
const mapping = {
|
|
@@ -21,7 +21,18 @@ async function fetchOHLCV(id, params) {
|
|
|
21
21
|
// 1. Smart Lookback Calculation
|
|
22
22
|
// If start/end not provided, calculate window based on limit * resolution
|
|
23
23
|
// Helper to handle string dates (from JSON)
|
|
24
|
-
|
|
24
|
+
// IMPORTANT: Python sends naive datetimes as ISO strings without 'Z' suffix.
|
|
25
|
+
// We must treat these as UTC, not local time.
|
|
26
|
+
const ensureDate = (d) => {
|
|
27
|
+
if (typeof d === 'string') {
|
|
28
|
+
// If string doesn't end with 'Z' and doesn't have timezone offset, append 'Z'
|
|
29
|
+
if (!d.endsWith('Z') && !d.match(/[+-]\d{2}:\d{2}$/)) {
|
|
30
|
+
return new Date(d + 'Z');
|
|
31
|
+
}
|
|
32
|
+
return new Date(d);
|
|
33
|
+
}
|
|
34
|
+
return d;
|
|
35
|
+
};
|
|
25
36
|
const pStart = params.start ? ensureDate(params.start) : undefined;
|
|
26
37
|
const pEnd = params.end ? ensureDate(params.end) : undefined;
|
|
27
38
|
let startTs = pStart ? Math.floor(pStart.getTime() / 1000) : 0;
|
|
@@ -48,18 +59,35 @@ async function fetchOHLCV(id, params) {
|
|
|
48
59
|
// Polymarket returns random tick timestamps (e.g. 1:00:21).
|
|
49
60
|
// We want to normalize this to the start of the bucket (1:00:00).
|
|
50
61
|
const resolutionMs = fidelity * 60 * 1000;
|
|
51
|
-
|
|
62
|
+
// 2. Client-side Aggregation
|
|
63
|
+
// Polymarket returns tick data. We must group by time bucket to create true candles.
|
|
64
|
+
const buckets = new Map();
|
|
65
|
+
history.forEach((item) => {
|
|
52
66
|
const rawMs = item.t * 1000;
|
|
53
67
|
const snappedMs = Math.floor(rawMs / resolutionMs) * resolutionMs;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
const price = Number(item.p);
|
|
69
|
+
const volume = Number(item.s || item.v || 0); // specific field depends on api, usually 's' for size
|
|
70
|
+
if (!buckets.has(snappedMs)) {
|
|
71
|
+
buckets.set(snappedMs, {
|
|
72
|
+
timestamp: snappedMs,
|
|
73
|
+
open: price,
|
|
74
|
+
high: price,
|
|
75
|
+
low: price,
|
|
76
|
+
close: price,
|
|
77
|
+
volume: volume
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const candle = buckets.get(snappedMs);
|
|
82
|
+
candle.high = Math.max(candle.high, price);
|
|
83
|
+
candle.low = Math.min(candle.low, price);
|
|
84
|
+
candle.close = price; // Assuming history is sorted by time. If not, we need to track timestamps.
|
|
85
|
+
candle.volume = (candle.volume || 0) + volume;
|
|
86
|
+
// If history is not guaranteed sorted, we should track first/last timestamps per bucket.
|
|
87
|
+
// But usually /prices-history is sorted. We'll assume sorted for efficiency.
|
|
88
|
+
}
|
|
62
89
|
});
|
|
90
|
+
const candles = Array.from(buckets.values()).sort((a, b) => a.timestamp - b.timestamp);
|
|
63
91
|
// Apply limit if specified
|
|
64
92
|
if (params.limit && candles.length > params.limit) {
|
|
65
93
|
return candles.slice(-params.limit);
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CLOB_API_URL = exports.GAMMA_API_URL = void 0;
|
|
4
4
|
exports.mapMarketToUnified = mapMarketToUnified;
|
|
5
5
|
exports.mapIntervalToFidelity = mapIntervalToFidelity;
|
|
6
|
+
const market_utils_1 = require("../../utils/market-utils");
|
|
6
7
|
exports.GAMMA_API_URL = 'https://gamma-api.polymarket.com/events';
|
|
7
8
|
exports.CLOB_API_URL = 'https://clob.polymarket.com';
|
|
8
9
|
function mapMarketToUnified(event, market, options = {}) {
|
|
@@ -66,7 +67,7 @@ function mapMarketToUnified(event, market, options = {}) {
|
|
|
66
67
|
});
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
+
const um = {
|
|
70
71
|
id: market.id,
|
|
71
72
|
title: market.question ? `${event.title} - ${market.question}` : event.title,
|
|
72
73
|
description: market.description || event.description,
|
|
@@ -81,6 +82,8 @@ function mapMarketToUnified(event, market, options = {}) {
|
|
|
81
82
|
category: event.category || event.tags?.[0]?.label,
|
|
82
83
|
tags: event.tags?.map((t) => t.label) || []
|
|
83
84
|
};
|
|
85
|
+
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
86
|
+
return um;
|
|
84
87
|
}
|
|
85
88
|
function mapIntervalToFidelity(interval) {
|
|
86
89
|
const mapping = {
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ export interface UnifiedMarket {
|
|
|
19
19
|
image?: string;
|
|
20
20
|
category?: string;
|
|
21
21
|
tags?: string[];
|
|
22
|
+
yes?: MarketOutcome;
|
|
23
|
+
no?: MarketOutcome;
|
|
24
|
+
up?: MarketOutcome;
|
|
25
|
+
down?: MarketOutcome;
|
|
22
26
|
}
|
|
23
27
|
export type CandleInterval = '1m' | '5m' | '15m' | '1h' | '6h' | '1d';
|
|
24
28
|
export interface PriceCandle {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addBinaryOutcomes = addBinaryOutcomes;
|
|
4
|
+
/**
|
|
5
|
+
* Standardizes binary market outcomes into yes/no/up/down properties.
|
|
6
|
+
*/
|
|
7
|
+
function addBinaryOutcomes(market) {
|
|
8
|
+
const outcomes = market.outcomes;
|
|
9
|
+
if (outcomes.length !== 2)
|
|
10
|
+
return;
|
|
11
|
+
const o1 = outcomes[0];
|
|
12
|
+
const o2 = outcomes[1];
|
|
13
|
+
const l1 = o1.label.toLowerCase();
|
|
14
|
+
const l2 = o2.label.toLowerCase();
|
|
15
|
+
// 1. Check for explicit opposites
|
|
16
|
+
const isYes = (l) => l === 'yes' || l === 'up' || l === 'over';
|
|
17
|
+
const isNo = (l) => l === 'no' || l === 'down' || l === 'under';
|
|
18
|
+
if (isYes(l1) || isNo(l2)) {
|
|
19
|
+
market.yes = o1;
|
|
20
|
+
market.no = o2;
|
|
21
|
+
}
|
|
22
|
+
else if (isYes(l2) || isNo(l1)) {
|
|
23
|
+
market.yes = o2;
|
|
24
|
+
market.no = o1;
|
|
25
|
+
}
|
|
26
|
+
// 2. Check for "Not" pattern
|
|
27
|
+
else if (l2.startsWith('not ')) {
|
|
28
|
+
market.yes = o1;
|
|
29
|
+
market.no = o2;
|
|
30
|
+
}
|
|
31
|
+
else if (l1.startsWith('not ')) {
|
|
32
|
+
market.yes = o2;
|
|
33
|
+
market.no = o1;
|
|
34
|
+
}
|
|
35
|
+
// 3. Fallback to indexing
|
|
36
|
+
else {
|
|
37
|
+
market.yes = o1;
|
|
38
|
+
market.no = o2;
|
|
39
|
+
}
|
|
40
|
+
market.up = market.yes;
|
|
41
|
+
market.down = market.no;
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"test": "jest -c jest.config.js",
|
|
30
30
|
"server": "tsx watch src/server/index.ts",
|
|
31
31
|
"server:prod": "node dist/server/index.js",
|
|
32
|
-
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=1.
|
|
33
|
-
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=1.
|
|
32
|
+
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=1.2.0,library=urllib3",
|
|
33
|
+
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=1.2.0,supportsES6=true,typescriptThreePlus=true",
|
|
34
34
|
"generate:docs": "node ../scripts/generate-api-docs.js",
|
|
35
35
|
"generate:sdk:all": "npm run generate:sdk:python && npm run generate:sdk:typescript && npm run generate:docs"
|
|
36
36
|
},
|