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.
@@ -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
- const ensureDate = (d) => (typeof d === 'string' ? new Date(d) : d);
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
- return {
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
- const ensureDate = (d) => (typeof d === 'string' ? new Date(d) : d);
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
- const candles = history.map((item) => {
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
- return {
55
- timestamp: snappedMs, // Aligned timestamp
56
- open: item.p,
57
- high: item.p,
58
- low: item.p,
59
- close: item.p,
60
- volume: undefined
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
- return {
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,5 @@
1
+ import { UnifiedMarket } from '../types';
2
+ /**
3
+ * Standardizes binary market outcomes into yes/no/up/down properties.
4
+ */
5
+ export declare function addBinaryOutcomes(market: UnifiedMarket): void;
@@ -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.1.3",
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.1.3,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.1.3,supportsES6=true,typescriptThreePlus=true",
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
  },