pmxt 1.5.7__py3-none-any.whl → 1.7.0__py3-none-any.whl

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.
pmxt/__init__.py CHANGED
@@ -33,7 +33,23 @@ from .models import (
33
33
  CreateOrderParams,
34
34
  )
35
35
 
36
- __version__ = "1.5.7"
36
+
37
+ # Global server management functions
38
+ _default_manager = ServerManager()
39
+
40
+ def stop_server():
41
+ """
42
+ Stop the background PMXT sidecar server.
43
+ """
44
+ _default_manager.stop()
45
+
46
+ def restart_server():
47
+ """
48
+ Restart the background PMXT sidecar server.
49
+ """
50
+ _default_manager.restart()
51
+
52
+ __version__ = "1.7.0"
37
53
  __all__ = [
38
54
  # Exchanges
39
55
  "Polymarket",
@@ -42,6 +58,8 @@ __all__ = [
42
58
  "Exchange",
43
59
  # Server Management
44
60
  "ServerManager",
61
+ "stop_server",
62
+ "restart_server",
45
63
  # Data Models
46
64
  "UnifiedMarket",
47
65
  "MarketOutcome",
@@ -24419,13 +24419,118 @@ var require_BaseExchange = __commonJS({
24419
24419
  this.credentials = credentials;
24420
24420
  }
24421
24421
  /**
24422
+ * Fetch markets with optional filtering, search, or slug lookup.
24423
+ * This is the primary method for retrieving markets in CCXT-style.
24424
+ *
24425
+ * @param params - Optional parameters for filtering and search
24426
+ * @param params.query - Search keyword to filter markets
24427
+ * @param params.slug - Market slug/ticker for direct lookup
24428
+ * @param params.limit - Maximum number of results
24429
+ * @param params.offset - Pagination offset
24430
+ * @param params.sort - Sort order
24431
+ * @param params.searchIn - Where to search (title, description, or both)
24432
+ * @returns Array of unified markets
24433
+ *
24434
+ * @example Fetch all markets
24435
+ * await exchange.fetchMarkets()
24436
+ *
24437
+ * @example Search markets
24438
+ * await exchange.fetchMarkets({ query: 'Trump' })
24439
+ *
24440
+ * @example Get market by slug
24441
+ * await exchange.fetchMarkets({ slug: 'will-trump-win' })
24442
+ */
24443
+ async fetchMarkets(params) {
24444
+ if (params?.slug) {
24445
+ return this.fetchMarketsBySlugImpl(params.slug);
24446
+ }
24447
+ if (params?.query) {
24448
+ return this.searchMarketsImpl(params.query, params);
24449
+ }
24450
+ return this.fetchMarketsImpl(params);
24451
+ }
24452
+ /**
24453
+ * Fetch events with optional keyword search.
24454
+ * Events group related markets together.
24455
+ *
24456
+ * @param params - Optional parameters for search and filtering
24457
+ * @param params.query - Search keyword to filter events
24458
+ * @param params.limit - Maximum number of results
24459
+ * @param params.offset - Pagination offset
24460
+ * @param params.searchIn - Where to search (title, description, or both)
24461
+ * @returns Array of unified events
24462
+ *
24463
+ * @example Search events
24464
+ * await exchange.fetchEvents({ query: 'Election' })
24465
+ */
24466
+ async fetchEvents(params) {
24467
+ if (params?.query) {
24468
+ return this.searchEventsImpl(params.query, params);
24469
+ }
24470
+ throw new Error("fetchEvents() requires a query parameter");
24471
+ }
24472
+ // ----------------------------------------------------------------------------
24473
+ // Implementation methods (to be overridden by exchanges)
24474
+ // ----------------------------------------------------------------------------
24475
+ /**
24476
+ * @internal
24477
+ * Implementation for fetching all markets.
24478
+ */
24479
+ async fetchMarketsImpl(params) {
24480
+ throw new Error("Method fetchMarketsImpl not implemented.");
24481
+ }
24482
+ /**
24483
+ * @internal
24484
+ * Implementation for searching markets by keyword.
24485
+ */
24486
+ async searchMarketsImpl(query, params) {
24487
+ throw new Error("Method searchMarketsImpl not implemented.");
24488
+ }
24489
+ /**
24490
+ * @internal
24491
+ * Implementation for fetching markets by slug/ticker.
24492
+ */
24493
+ async fetchMarketsBySlugImpl(slug) {
24494
+ throw new Error("Method fetchMarketsBySlugImpl not implemented.");
24495
+ }
24496
+ /**
24497
+ * @internal
24498
+ * Implementation for searching events by keyword.
24499
+ */
24500
+ async searchEventsImpl(query, params) {
24501
+ throw new Error("Method searchEventsImpl not implemented.");
24502
+ }
24503
+ // ----------------------------------------------------------------------------
24504
+ // Deprecated methods (kept for backward compatibility)
24505
+ // ----------------------------------------------------------------------------
24506
+ /**
24507
+ * @deprecated Use fetchMarkets({ query: '...' }) instead. Will be removed in v2.0
24508
+ * Search for markets matching a keyword query.
24509
+ * By default, searches only in market titles. Use params.searchIn to search descriptions or both.
24510
+ */
24511
+ async searchMarkets(query, params) {
24512
+ console.warn('\u26A0\uFE0F searchMarkets() is deprecated. Use fetchMarkets({ query: "..." }) instead.');
24513
+ return this.fetchMarkets({ ...params, query });
24514
+ }
24515
+ /**
24516
+ * @deprecated Use fetchMarkets({ slug: '...' }) instead. Will be removed in v2.0
24517
+ * Fetch markets by URL slug (Polymarket) or ticker (Kalshi).
24518
+ * @param slug - Market slug or ticker
24519
+ */
24520
+ async getMarketsBySlug(slug) {
24521
+ console.warn('\u26A0\uFE0F getMarketsBySlug() is deprecated. Use fetchMarkets({ slug: "..." }) instead.');
24522
+ return this.fetchMarkets({ slug });
24523
+ }
24524
+ /**
24525
+ * @deprecated Use fetchEvents({ query: '...' }) instead. Will be removed in v2.0
24422
24526
  * Search for events matching a keyword query.
24423
24527
  * Returns grouped events, each containing related markets.
24424
24528
  * @param query - Search term
24425
24529
  * @param params - Optional filter parameters
24426
24530
  */
24427
24531
  async searchEvents(query, params) {
24428
- throw new Error("Method searchEvents not implemented.");
24532
+ console.warn('\u26A0\uFE0F searchEvents() is deprecated. Use fetchEvents({ query: "..." }) instead.');
24533
+ return this.fetchEvents({ ...params, query });
24429
24534
  }
24430
24535
  /**
24431
24536
  * Fetch historical price data for a specific market outcome.
@@ -24494,6 +24599,227 @@ var require_BaseExchange = __commonJS({
24494
24599
  return (0, math_1.getExecutionPriceDetailed)(orderBook, side, amount);
24495
24600
  }
24496
24601
  // ----------------------------------------------------------------------------
24602
+ // Filtering Methods
24603
+ // ----------------------------------------------------------------------------
24604
+ /**
24605
+ * Filter markets based on criteria or custom function.
24606
+ *
24607
+ * @param markets - Array of markets to filter
24608
+ * @param criteria - Filter criteria object, string (simple text search), or predicate function
24609
+ * @returns Filtered array of markets
24610
+ *
24611
+ * @example Simple text search
24612
+ * api.filterMarkets(markets, 'Trump')
24613
+ *
24614
+ * @example Advanced filtering
24615
+ * api.filterMarkets(markets, {
24616
+ * text: 'Trump',
24617
+ * searchIn: ['title', 'tags'],
24618
+ * volume24h: { min: 10000 },
24619
+ * category: 'Politics',
24620
+ * price: { outcome: 'yes', max: 0.5 }
24621
+ * })
24622
+ *
24623
+ * @example Custom predicate
24624
+ * api.filterMarkets(markets, m => m.liquidity > 5000 && m.yes?.price < 0.3)
24625
+ */
24626
+ filterMarkets(markets, criteria) {
24627
+ if (typeof criteria === "function") {
24628
+ return markets.filter(criteria);
24629
+ }
24630
+ if (typeof criteria === "string") {
24631
+ const lowerQuery = criteria.toLowerCase();
24632
+ return markets.filter((m) => m.title.toLowerCase().includes(lowerQuery));
24633
+ }
24634
+ return markets.filter((market) => {
24635
+ if (criteria.text) {
24636
+ const lowerQuery = criteria.text.toLowerCase();
24637
+ const searchIn = criteria.searchIn || ["title"];
24638
+ let textMatch = false;
24639
+ for (const field of searchIn) {
24640
+ if (field === "title" && market.title?.toLowerCase().includes(lowerQuery)) {
24641
+ textMatch = true;
24642
+ break;
24643
+ }
24644
+ if (field === "description" && market.description?.toLowerCase().includes(lowerQuery)) {
24645
+ textMatch = true;
24646
+ break;
24647
+ }
24648
+ if (field === "category" && market.category?.toLowerCase().includes(lowerQuery)) {
24649
+ textMatch = true;
24650
+ break;
24651
+ }
24652
+ if (field === "tags" && market.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery))) {
24653
+ textMatch = true;
24654
+ break;
24655
+ }
24656
+ if (field === "outcomes" && market.outcomes?.some((o) => o.label.toLowerCase().includes(lowerQuery))) {
24657
+ textMatch = true;
24658
+ break;
24659
+ }
24660
+ }
24661
+ if (!textMatch)
24662
+ return false;
24663
+ }
24664
+ if (criteria.category && market.category !== criteria.category) {
24665
+ return false;
24666
+ }
24667
+ if (criteria.tags && criteria.tags.length > 0) {
24668
+ const hasMatchingTag = criteria.tags.some((tag) => market.tags?.some((marketTag) => marketTag.toLowerCase() === tag.toLowerCase()));
24669
+ if (!hasMatchingTag)
24670
+ return false;
24671
+ }
24672
+ if (criteria.volume24h) {
24673
+ if (criteria.volume24h.min !== void 0 && market.volume24h < criteria.volume24h.min) {
24674
+ return false;
24675
+ }
24676
+ if (criteria.volume24h.max !== void 0 && market.volume24h > criteria.volume24h.max) {
24677
+ return false;
24678
+ }
24679
+ }
24680
+ if (criteria.volume) {
24681
+ if (criteria.volume.min !== void 0 && (market.volume || 0) < criteria.volume.min) {
24682
+ return false;
24683
+ }
24684
+ if (criteria.volume.max !== void 0 && (market.volume || 0) > criteria.volume.max) {
24685
+ return false;
24686
+ }
24687
+ }
24688
+ if (criteria.liquidity) {
24689
+ if (criteria.liquidity.min !== void 0 && market.liquidity < criteria.liquidity.min) {
24690
+ return false;
24691
+ }
24692
+ if (criteria.liquidity.max !== void 0 && market.liquidity > criteria.liquidity.max) {
24693
+ return false;
24694
+ }
24695
+ }
24696
+ if (criteria.openInterest) {
24697
+ if (criteria.openInterest.min !== void 0 && (market.openInterest || 0) < criteria.openInterest.min) {
24698
+ return false;
24699
+ }
24700
+ if (criteria.openInterest.max !== void 0 && (market.openInterest || 0) > criteria.openInterest.max) {
24701
+ return false;
24702
+ }
24703
+ }
24704
+ if (criteria.resolutionDate) {
24705
+ const resDate = market.resolutionDate;
24706
+ if (criteria.resolutionDate.before && resDate >= criteria.resolutionDate.before) {
24707
+ return false;
24708
+ }
24709
+ if (criteria.resolutionDate.after && resDate <= criteria.resolutionDate.after) {
24710
+ return false;
24711
+ }
24712
+ }
24713
+ if (criteria.price) {
24714
+ const outcome = market[criteria.price.outcome];
24715
+ if (!outcome)
24716
+ return false;
24717
+ if (criteria.price.min !== void 0 && outcome.price < criteria.price.min) {
24718
+ return false;
24719
+ }
24720
+ if (criteria.price.max !== void 0 && outcome.price > criteria.price.max) {
24721
+ return false;
24722
+ }
24723
+ }
24724
+ if (criteria.priceChange24h) {
24725
+ const outcome = market[criteria.priceChange24h.outcome];
24726
+ if (!outcome || outcome.priceChange24h === void 0)
24727
+ return false;
24728
+ if (criteria.priceChange24h.min !== void 0 && outcome.priceChange24h < criteria.priceChange24h.min) {
24729
+ return false;
24730
+ }
24731
+ if (criteria.priceChange24h.max !== void 0 && outcome.priceChange24h > criteria.priceChange24h.max) {
24732
+ return false;
24733
+ }
24734
+ }
24735
+ return true;
24736
+ });
24737
+ }
24738
+ /**
24739
+ * Filter events based on criteria or custom function.
24740
+ *
24741
+ * @param events - Array of events to filter
24742
+ * @param criteria - Filter criteria object, string (simple text search), or predicate function
24743
+ * @returns Filtered array of events
24744
+ *
24745
+ * @example Simple text search
24746
+ * api.filterEvents(events, 'Trump')
24747
+ *
24748
+ * @example Advanced filtering
24749
+ * api.filterEvents(events, {
24750
+ * text: 'Election',
24751
+ * searchIn: ['title', 'tags'],
24752
+ * category: 'Politics',
24753
+ * marketCount: { min: 5 }
24754
+ * })
24755
+ *
24756
+ * @example Custom predicate
24757
+ * api.filterEvents(events, e => e.markets.length > 10)
24758
+ */
24759
+ filterEvents(events, criteria) {
24760
+ if (typeof criteria === "function") {
24761
+ return events.filter(criteria);
24762
+ }
24763
+ if (typeof criteria === "string") {
24764
+ const lowerQuery = criteria.toLowerCase();
24765
+ return events.filter((e) => e.title.toLowerCase().includes(lowerQuery));
24766
+ }
24767
+ return events.filter((event) => {
24768
+ if (criteria.text) {
24769
+ const lowerQuery = criteria.text.toLowerCase();
24770
+ const searchIn = criteria.searchIn || ["title"];
24771
+ let textMatch = false;
24772
+ for (const field of searchIn) {
24773
+ if (field === "title" && event.title?.toLowerCase().includes(lowerQuery)) {
24774
+ textMatch = true;
24775
+ break;
24776
+ }
24777
+ if (field === "description" && event.description?.toLowerCase().includes(lowerQuery)) {
24778
+ textMatch = true;
24779
+ break;
24780
+ }
24781
+ if (field === "category" && event.category?.toLowerCase().includes(lowerQuery)) {
24782
+ textMatch = true;
24783
+ break;
24784
+ }
24785
+ if (field === "tags" && event.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery))) {
24786
+ textMatch = true;
24787
+ break;
24788
+ }
24789
+ }
24790
+ if (!textMatch)
24791
+ return false;
24792
+ }
24793
+ if (criteria.category && event.category !== criteria.category) {
24794
+ return false;
24795
+ }
24796
+ if (criteria.tags && criteria.tags.length > 0) {
24797
+ const hasMatchingTag = criteria.tags.some((tag) => event.tags?.some((eventTag) => eventTag.toLowerCase() === tag.toLowerCase()));
24798
+ if (!hasMatchingTag)
24799
+ return false;
24800
+ }
24801
+ if (criteria.marketCount) {
24802
+ const count = event.markets.length;
24803
+ if (criteria.marketCount.min !== void 0 && count < criteria.marketCount.min) {
24804
+ return false;
24805
+ }
24806
+ if (criteria.marketCount.max !== void 0 && count > criteria.marketCount.max) {
24807
+ return false;
24808
+ }
24809
+ }
24810
+ if (criteria.totalVolume) {
24811
+ const totalVolume = event.markets.reduce((sum, m) => sum + m.volume24h, 0);
24812
+ if (criteria.totalVolume.min !== void 0 && totalVolume < criteria.totalVolume.min) {
24813
+ return false;
24814
+ }
24815
+ if (criteria.totalVolume.max !== void 0 && totalVolume > criteria.totalVolume.max) {
24816
+ return false;
24817
+ }
24818
+ }
24819
+ return true;
24820
+ });
24821
+ }
24822
+ // ----------------------------------------------------------------------------
24497
24823
  // WebSocket Streaming Methods
24498
24824
  // ----------------------------------------------------------------------------
24499
24825
  /**
@@ -38170,9 +38496,11 @@ var require_utils4 = __commonJS({
38170
38496
  if (index === 0 || label.toLowerCase() === "yes" || candidateName && label === candidateName) {
38171
38497
  priceChange = Number(market.oneDayPriceChange || 0);
38172
38498
  }
38499
+ const outcomeIdValue = clobTokenIds[index] || String(index);
38173
38500
  outcomes.push({
38174
- id: clobTokenIds[index] || String(index),
38501
+ id: outcomeIdValue,
38175
38502
  // Use CLOB Token ID as the primary ID
38503
+ outcomeId: outcomeIdValue,
38176
38504
  label: outcomeLabel,
38177
38505
  price: parseFloat(rawPrice) || 0,
38178
38506
  priceChange24h: priceChange,
@@ -38185,6 +38513,7 @@ var require_utils4 = __commonJS({
38185
38513
  }
38186
38514
  const um = {
38187
38515
  id: market.id,
38516
+ marketId: market.id,
38188
38517
  title: market.question ? `${event.title} - ${market.question}` : event.title,
38189
38518
  description: market.description || event.description,
38190
38519
  outcomes,
@@ -38421,6 +38750,26 @@ var require_getMarketsBySlug = __commonJS({
38421
38750
  }
38422
38751
  });
38423
38752
 
38753
+ // dist/utils/validation.js
38754
+ var require_validation = __commonJS({
38755
+ "dist/utils/validation.js"(exports2) {
38756
+ "use strict";
38757
+ Object.defineProperty(exports2, "__esModule", { value: true });
38758
+ exports2.validateOutcomeId = validateOutcomeId;
38759
+ exports2.validateIdFormat = validateIdFormat;
38760
+ function validateOutcomeId(id, context) {
38761
+ if (id.length < 10 && /^\d+$/.test(id)) {
38762
+ throw new Error(`Invalid ID for ${context}: "${id}". This appears to be a market ID (deprecated: market.id, use: market.marketId). Please use outcome ID (preferred: outcome.outcomeId, deprecated: outcome.id).`);
38763
+ }
38764
+ }
38765
+ function validateIdFormat(id, context) {
38766
+ if (!id || id.trim().length === 0) {
38767
+ throw new Error(`Invalid ID for ${context}: ID cannot be empty`);
38768
+ }
38769
+ }
38770
+ }
38771
+ });
38772
+
38424
38773
  // dist/exchanges/polymarket/fetchOHLCV.js
38425
38774
  var require_fetchOHLCV = __commonJS({
38426
38775
  "dist/exchanges/polymarket/fetchOHLCV.js"(exports2) {
@@ -38432,10 +38781,10 @@ var require_fetchOHLCV = __commonJS({
38432
38781
  exports2.fetchOHLCV = fetchOHLCV;
38433
38782
  var axios_1 = __importDefault(require_axios());
38434
38783
  var utils_1 = require_utils4();
38784
+ var validation_1 = require_validation();
38435
38785
  async function fetchOHLCV(id, params) {
38436
- if (id.length < 10 && /^\d+$/.test(id)) {
38437
- throw new Error(`Invalid ID for Polymarket history: "${id}". You provided a Market ID, but Polymarket's CLOB API requires a Token ID. Ensure you are using 'outcome.id'.`);
38438
- }
38786
+ (0, validation_1.validateIdFormat)(id, "OHLCV");
38787
+ (0, validation_1.validateOutcomeId)(id, "OHLCV");
38439
38788
  try {
38440
38789
  const fidelity = (0, utils_1.mapIntervalToFidelity)(params.resolution);
38441
38790
  const nowTs = Math.floor(Date.now() / 1e3);
@@ -38519,7 +38868,10 @@ var require_fetchOrderBook = __commonJS({
38519
38868
  exports2.fetchOrderBook = fetchOrderBook;
38520
38869
  var axios_1 = __importDefault(require_axios());
38521
38870
  var utils_1 = require_utils4();
38871
+ var validation_1 = require_validation();
38522
38872
  async function fetchOrderBook(id) {
38873
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
38874
+ (0, validation_1.validateOutcomeId)(id, "OrderBook");
38523
38875
  try {
38524
38876
  const response = await axios_1.default.get(`${utils_1.CLOB_API_URL}/book`, {
38525
38877
  params: { token_id: id }
@@ -38557,10 +38909,10 @@ var require_fetchTrades = __commonJS({
38557
38909
  exports2.fetchTrades = fetchTrades;
38558
38910
  var axios_1 = __importDefault(require_axios());
38559
38911
  var utils_1 = require_utils4();
38912
+ var validation_1 = require_validation();
38560
38913
  async function fetchTrades(id, params) {
38561
- if (id.length < 10 && /^\d+$/.test(id)) {
38562
- throw new Error(`Invalid ID for Polymarket trades: "${id}". You provided a Market ID, but Polymarket's CLOB API requires a Token ID.`);
38563
- }
38914
+ (0, validation_1.validateIdFormat)(id, "Trades");
38915
+ (0, validation_1.validateOutcomeId)(id, "Trades");
38564
38916
  try {
38565
38917
  const queryParams = {
38566
38918
  asset_id: id
@@ -76055,7 +76407,7 @@ var require_permessage_deflate = __commonJS({
76055
76407
  });
76056
76408
 
76057
76409
  // ../node_modules/ws/lib/validation.js
76058
- var require_validation = __commonJS({
76410
+ var require_validation2 = __commonJS({
76059
76411
  "../node_modules/ws/lib/validation.js"(exports2, module2) {
76060
76412
  "use strict";
76061
76413
  var { isUtf8 } = require("buffer");
@@ -76268,7 +76620,7 @@ var require_receiver = __commonJS({
76268
76620
  kWebSocket
76269
76621
  } = require_constants();
76270
76622
  var { concat: concat2, toArrayBuffer, unmask } = require_buffer_util();
76271
- var { isValidStatusCode, isValidUTF8 } = require_validation();
76623
+ var { isValidStatusCode, isValidUTF8 } = require_validation2();
76272
76624
  var FastBuffer = Buffer[Symbol.species];
76273
76625
  var GET_INFO = 0;
76274
76626
  var GET_PAYLOAD_LENGTH_16 = 1;
@@ -76855,7 +77207,7 @@ var require_sender = __commonJS({
76855
77207
  var { randomFillSync } = require("crypto");
76856
77208
  var PerMessageDeflate = require_permessage_deflate();
76857
77209
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
76858
- var { isBlob: isBlob2, isValidStatusCode } = require_validation();
77210
+ var { isBlob: isBlob2, isValidStatusCode } = require_validation2();
76859
77211
  var { mask: applyMask, toBuffer } = require_buffer_util();
76860
77212
  var kByteLength = Symbol("kByteLength");
76861
77213
  var maskBuffer = Buffer.alloc(4);
@@ -77568,7 +77920,7 @@ var require_event_target = __commonJS({
77568
77920
  var require_extension = __commonJS({
77569
77921
  "../node_modules/ws/lib/extension.js"(exports2, module2) {
77570
77922
  "use strict";
77571
- var { tokenChars } = require_validation();
77923
+ var { tokenChars } = require_validation2();
77572
77924
  function push(dest, name, elem) {
77573
77925
  if (dest[name] === void 0) dest[name] = [elem];
77574
77926
  else dest[name].push(elem);
@@ -77732,7 +78084,7 @@ var require_websocket = __commonJS({
77732
78084
  var PerMessageDeflate = require_permessage_deflate();
77733
78085
  var Receiver = require_receiver();
77734
78086
  var Sender = require_sender();
77735
- var { isBlob: isBlob2 } = require_validation();
78087
+ var { isBlob: isBlob2 } = require_validation2();
77736
78088
  var {
77737
78089
  BINARY_TYPES,
77738
78090
  EMPTY_BUFFER,
@@ -78701,7 +79053,7 @@ var require_stream = __commonJS({
78701
79053
  var require_subprotocol = __commonJS({
78702
79054
  "../node_modules/ws/lib/subprotocol.js"(exports2, module2) {
78703
79055
  "use strict";
78704
- var { tokenChars } = require_validation();
79056
+ var { tokenChars } = require_validation2();
78705
79057
  function parse(header) {
78706
79058
  const protocols = /* @__PURE__ */ new Set();
78707
79059
  let start = -1;
@@ -83892,7 +84244,7 @@ var require_permessage_deflate2 = __commonJS({
83892
84244
  });
83893
84245
 
83894
84246
  // ../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/validation.js
83895
- var require_validation2 = __commonJS({
84247
+ var require_validation3 = __commonJS({
83896
84248
  "../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/validation.js"(exports2, module2) {
83897
84249
  "use strict";
83898
84250
  var { isUtf8 } = require("buffer");
@@ -84105,7 +84457,7 @@ var require_receiver2 = __commonJS({
84105
84457
  kWebSocket
84106
84458
  } = require_constants2();
84107
84459
  var { concat: concat2, toArrayBuffer, unmask } = require_buffer_util2();
84108
- var { isValidStatusCode, isValidUTF8 } = require_validation2();
84460
+ var { isValidStatusCode, isValidUTF8 } = require_validation3();
84109
84461
  var FastBuffer = Buffer[Symbol.species];
84110
84462
  var GET_INFO = 0;
84111
84463
  var GET_PAYLOAD_LENGTH_16 = 1;
@@ -84692,7 +85044,7 @@ var require_sender2 = __commonJS({
84692
85044
  var { randomFillSync } = require("crypto");
84693
85045
  var PerMessageDeflate = require_permessage_deflate2();
84694
85046
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants2();
84695
- var { isBlob: isBlob2, isValidStatusCode } = require_validation2();
85047
+ var { isBlob: isBlob2, isValidStatusCode } = require_validation3();
84696
85048
  var { mask: applyMask, toBuffer } = require_buffer_util2();
84697
85049
  var kByteLength = Symbol("kByteLength");
84698
85050
  var maskBuffer = Buffer.alloc(4);
@@ -85405,7 +85757,7 @@ var require_event_target2 = __commonJS({
85405
85757
  var require_extension2 = __commonJS({
85406
85758
  "../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/extension.js"(exports2, module2) {
85407
85759
  "use strict";
85408
- var { tokenChars } = require_validation2();
85760
+ var { tokenChars } = require_validation3();
85409
85761
  function push(dest, name, elem) {
85410
85762
  if (dest[name] === void 0) dest[name] = [elem];
85411
85763
  else dest[name].push(elem);
@@ -85569,7 +85921,7 @@ var require_websocket2 = __commonJS({
85569
85921
  var PerMessageDeflate = require_permessage_deflate2();
85570
85922
  var Receiver = require_receiver2();
85571
85923
  var Sender = require_sender2();
85572
- var { isBlob: isBlob2 } = require_validation2();
85924
+ var { isBlob: isBlob2 } = require_validation3();
85573
85925
  var {
85574
85926
  BINARY_TYPES,
85575
85927
  CLOSE_TIMEOUT,
@@ -86542,7 +86894,7 @@ var require_stream2 = __commonJS({
86542
86894
  var require_subprotocol2 = __commonJS({
86543
86895
  "../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/subprotocol.js"(exports2, module2) {
86544
86896
  "use strict";
86545
- var { tokenChars } = require_validation2();
86897
+ var { tokenChars } = require_validation3();
86546
86898
  function parse(header) {
86547
86899
  const protocols = /* @__PURE__ */ new Set();
86548
86900
  let start = -1;
@@ -104903,18 +105255,21 @@ var require_polymarket = __commonJS({
104903
105255
  get name() {
104904
105256
  return "Polymarket";
104905
105257
  }
104906
- async fetchMarkets(params) {
105258
+ // ----------------------------------------------------------------------------
105259
+ // Implementation methods for CCXT-style API
105260
+ // ----------------------------------------------------------------------------
105261
+ async fetchMarketsImpl(params) {
104907
105262
  return (0, fetchMarkets_1.fetchMarkets)(params);
104908
105263
  }
104909
- async searchMarkets(query, params) {
105264
+ async searchMarketsImpl(query, params) {
104910
105265
  return (0, searchMarkets_1.searchMarkets)(query, params);
104911
105266
  }
104912
- async searchEvents(query, params) {
104913
- return (0, searchEvents_1.searchEvents)(query, params);
104914
- }
104915
- async getMarketsBySlug(slug) {
105267
+ async fetchMarketsBySlugImpl(slug) {
104916
105268
  return (0, getMarketsBySlug_1.getMarketsBySlug)(slug);
104917
105269
  }
105270
+ async searchEventsImpl(query, params) {
105271
+ return (0, searchEvents_1.searchEvents)(query, params);
105272
+ }
104918
105273
  async fetchOHLCV(id, params) {
104919
105274
  return (0, fetchOHLCV_1.fetchOHLCV)(id, params);
104920
105275
  }
@@ -105192,8 +105547,10 @@ var require_utils10 = __commonJS({
105192
105547
  const tokenEntries = Object.entries(market.tokens);
105193
105548
  tokenEntries.forEach(([label, tokenId], index) => {
105194
105549
  const outcomePrice = market.prices[index] || 0;
105550
+ const outcomeIdValue = tokenId;
105195
105551
  outcomes.push({
105196
- id: tokenId,
105552
+ id: outcomeIdValue,
105553
+ outcomeId: outcomeIdValue,
105197
105554
  label: label.charAt(0).toUpperCase() + label.slice(1),
105198
105555
  // Capitalize 'yes'/'no'
105199
105556
  price: outcomePrice,
@@ -105207,6 +105564,7 @@ var require_utils10 = __commonJS({
105207
105564
  }
105208
105565
  const um = {
105209
105566
  id: market.slug,
105567
+ marketId: market.slug,
105210
105568
  title: market.title || market.question,
105211
105569
  description: market.description,
105212
105570
  outcomes,
@@ -105415,7 +105773,9 @@ var require_fetchOHLCV2 = __commonJS({
105415
105773
  exports2.fetchOHLCV = fetchOHLCV;
105416
105774
  var axios_1 = __importDefault(require_axios());
105417
105775
  var utils_1 = require_utils10();
105776
+ var validation_1 = require_validation();
105418
105777
  async function fetchOHLCV(id, params) {
105778
+ (0, validation_1.validateIdFormat)(id, "OHLCV");
105419
105779
  try {
105420
105780
  const fidelity = (0, utils_1.mapIntervalToFidelity)(params.resolution);
105421
105781
  const url2 = `${utils_1.LIMITLESS_API_URL}/markets/${id}/historical-price`;
@@ -105466,7 +105826,9 @@ var require_fetchOrderBook2 = __commonJS({
105466
105826
  exports2.fetchOrderBook = fetchOrderBook;
105467
105827
  var axios_1 = __importDefault(require_axios());
105468
105828
  var utils_1 = require_utils10();
105829
+ var validation_1 = require_validation();
105469
105830
  async function fetchOrderBook(id) {
105831
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
105470
105832
  try {
105471
105833
  const url2 = `${utils_1.LIMITLESS_API_URL}/markets/${id}/orderbook`;
105472
105834
  const response = await axios_1.default.get(url2);
@@ -105881,18 +106243,21 @@ var require_limitless = __commonJS({
105881
106243
  get name() {
105882
106244
  return "Limitless";
105883
106245
  }
105884
- async fetchMarkets(params) {
106246
+ // ----------------------------------------------------------------------------
106247
+ // Implementation methods for CCXT-style API
106248
+ // ----------------------------------------------------------------------------
106249
+ async fetchMarketsImpl(params) {
105885
106250
  return (0, fetchMarkets_1.fetchMarkets)(params);
105886
106251
  }
105887
- async searchMarkets(query, params) {
106252
+ async searchMarketsImpl(query, params) {
105888
106253
  return (0, searchMarkets_1.searchMarkets)(query, params);
105889
106254
  }
105890
- async searchEvents(query, params) {
105891
- return (0, searchEvents_1.searchEvents)(query, params);
105892
- }
105893
- async getMarketsBySlug(slug) {
106255
+ async fetchMarketsBySlugImpl(slug) {
105894
106256
  return (0, getMarketsBySlug_1.getMarketsBySlug)(slug);
105895
106257
  }
106258
+ async searchEventsImpl(query, params) {
106259
+ return (0, searchEvents_1.searchEvents)(query, params);
106260
+ }
105896
106261
  async fetchOHLCV(id, params) {
105897
106262
  return (0, fetchOHLCV_1.fetchOHLCV)(id, params);
105898
106263
  }
@@ -106092,6 +106457,7 @@ var require_utils11 = __commonJS({
106092
106457
  {
106093
106458
  id: market.ticker,
106094
106459
  // The actual market ticker (primary tradeable)
106460
+ outcomeId: market.ticker,
106095
106461
  label: candidateName || "Yes",
106096
106462
  price,
106097
106463
  priceChange24h: priceChange
@@ -106099,6 +106465,7 @@ var require_utils11 = __commonJS({
106099
106465
  {
106100
106466
  id: `${market.ticker}-NO`,
106101
106467
  // Virtual ID for the No outcome
106468
+ outcomeId: `${market.ticker}-NO`,
106102
106469
  label: candidateName ? `Not ${candidateName}` : "No",
106103
106470
  price: 1 - price,
106104
106471
  priceChange24h: -priceChange
@@ -106118,6 +106485,7 @@ var require_utils11 = __commonJS({
106118
106485
  }
106119
106486
  const um = {
106120
106487
  id: market.ticker,
106488
+ marketId: market.ticker,
106121
106489
  title: event.title,
106122
106490
  description: market.rules_primary || market.rules_secondary || "",
106123
106491
  outcomes,
@@ -106446,7 +106814,9 @@ var require_fetchOHLCV3 = __commonJS({
106446
106814
  exports2.fetchOHLCV = fetchOHLCV;
106447
106815
  var axios_1 = __importDefault(require_axios());
106448
106816
  var utils_1 = require_utils11();
106817
+ var validation_1 = require_validation();
106449
106818
  async function fetchOHLCV(id, params) {
106819
+ (0, validation_1.validateIdFormat)(id, "OHLCV");
106450
106820
  try {
106451
106821
  const cleanedId = id.replace(/-NO$/, "");
106452
106822
  const normalizedId = cleanedId.toUpperCase();
@@ -106532,7 +106902,9 @@ var require_fetchOrderBook3 = __commonJS({
106532
106902
  Object.defineProperty(exports2, "__esModule", { value: true });
106533
106903
  exports2.fetchOrderBook = fetchOrderBook;
106534
106904
  var axios_1 = __importDefault(require_axios());
106905
+ var validation_1 = require_validation();
106535
106906
  async function fetchOrderBook(id) {
106907
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
106536
106908
  try {
106537
106909
  const isNoOutcome = id.endsWith("-NO");
106538
106910
  const ticker = id.replace(/-NO$/, "");
@@ -107086,20 +107458,20 @@ var require_kalshi = __commonJS({
107086
107458
  return this.auth;
107087
107459
  }
107088
107460
  // ----------------------------------------------------------------------------
107089
- // Market Data Methods
107461
+ // Market Data Methods - Implementation for CCXT-style API
107090
107462
  // ----------------------------------------------------------------------------
107091
- async fetchMarkets(params) {
107463
+ async fetchMarketsImpl(params) {
107092
107464
  return (0, fetchMarkets_1.fetchMarkets)(params);
107093
107465
  }
107094
- async searchMarkets(query, params) {
107466
+ async searchMarketsImpl(query, params) {
107095
107467
  return (0, searchMarkets_1.searchMarkets)(query, params);
107096
107468
  }
107097
- async searchEvents(query, params) {
107098
- return (0, searchEvents_1.searchEvents)(query, params);
107099
- }
107100
- async getMarketsBySlug(slug) {
107469
+ async fetchMarketsBySlugImpl(slug) {
107101
107470
  return (0, getMarketsBySlug_1.getMarketsBySlug)(slug);
107102
107471
  }
107472
+ async searchEventsImpl(query, params) {
107473
+ return (0, searchEvents_1.searchEvents)(query, params);
107474
+ }
107103
107475
  async fetchOHLCV(id, params) {
107104
107476
  return (0, fetchOHLCV_1.fetchOHLCV)(id, params);
107105
107477
  }
pmxt/client.py CHANGED
@@ -7,7 +7,7 @@ OpenAPI client, matching the JavaScript API exactly.
7
7
 
8
8
  import os
9
9
  import sys
10
- from typing import List, Optional, Dict, Any, Literal
10
+ from typing import List, Optional, Dict, Any, Literal, Union
11
11
  from datetime import datetime
12
12
  from abc import ABC, abstractmethod
13
13
  import json
@@ -37,6 +37,10 @@ from .models import (
37
37
  HistoryFilterParams,
38
38
  CreateOrderParams,
39
39
  ExecutionPriceResult,
40
+ MarketFilterCriteria,
41
+ MarketFilterFunction,
42
+ EventFilterCriteria,
43
+ EventFilterFunction,
40
44
  )
41
45
  from .server_manager import ServerManager
42
46
 
@@ -432,7 +436,223 @@ class Exchange(ABC):
432
436
  return [_convert_market(m) for m in data]
433
437
  except ApiException as e:
434
438
  raise Exception(f"Failed to get markets by slug: {e}")
435
-
439
+
440
+ # ----------------------------------------------------------------------------
441
+ # Filtering Methods
442
+ # ----------------------------------------------------------------------------
443
+
444
+ def filter_markets(
445
+ self,
446
+ markets: List[UnifiedMarket],
447
+ criteria: Union[str, MarketFilterCriteria, MarketFilterFunction]
448
+ ) -> List[UnifiedMarket]:
449
+ """
450
+ Filter markets based on criteria or custom function.
451
+
452
+ Args:
453
+ markets: List of markets to filter
454
+ criteria: Filter criteria object, string (simple text search), or predicate function
455
+
456
+ Returns:
457
+ Filtered list of markets
458
+
459
+ Example:
460
+ >>> api.filter_markets(markets, "Trump")
461
+ >>> api.filter_markets(markets, {"volume_24h": {"min": 1000}})
462
+ >>> api.filter_markets(markets, lambda m: m.yes and m.yes.price > 0.5)
463
+ """
464
+ # Handle predicate function
465
+ if callable(criteria):
466
+ return list(filter(criteria, markets))
467
+
468
+ # Handle simple string search
469
+ if isinstance(criteria, str):
470
+ lower_query = criteria.lower()
471
+ return [m for m in markets if m.title and lower_query in m.title.lower()]
472
+
473
+ # Handle criteria object
474
+ params: MarketFilterCriteria = criteria # type: ignore
475
+ results = []
476
+
477
+ for market in markets:
478
+ # Text search
479
+ if "text" in params:
480
+ lower_query = params["text"].lower()
481
+ search_in = params.get("search_in", ["title"])
482
+ match = False
483
+
484
+ if "title" in search_in and market.title and lower_query in market.title.lower():
485
+ match = True
486
+ elif "description" in search_in and market.description and lower_query in market.description.lower():
487
+ match = True
488
+ elif "category" in search_in and market.category and lower_query in market.category.lower():
489
+ match = True
490
+ elif "tags" in search_in and market.tags and any(lower_query in t.lower() for t in market.tags):
491
+ match = True
492
+ elif "outcomes" in search_in and market.outcomes and any(lower_query in o.label.lower() for o in market.outcomes):
493
+ match = True
494
+
495
+ if not match:
496
+ continue
497
+
498
+ # Category filter
499
+ if "category" in params:
500
+ if market.category != params["category"]:
501
+ continue
502
+
503
+ # Tags filter (match ANY)
504
+ if "tags" in params and params["tags"]:
505
+ if not market.tags:
506
+ continue
507
+ query_tags = [t.lower() for t in params["tags"]]
508
+ market_tags = [t.lower() for t in market.tags]
509
+ if not any(t in market_tags for t in query_tags):
510
+ continue
511
+
512
+ # Volume 24h
513
+ if "volume_24h" in params:
514
+ f = params["volume_24h"]
515
+ val = market.volume_24h
516
+ if "min" in f and val < f["min"]: continue
517
+ if "max" in f and val > f["max"]: continue
518
+
519
+ # Volume
520
+ if "volume" in params:
521
+ f = params["volume"]
522
+ val = market.volume or 0
523
+ if "min" in f and val < f["min"]: continue
524
+ if "max" in f and val > f["max"]: continue
525
+
526
+ # Liquidity
527
+ if "liquidity" in params:
528
+ f = params["liquidity"]
529
+ val = market.liquidity
530
+ if "min" in f and val < f["min"]: continue
531
+ if "max" in f and val > f["max"]: continue
532
+
533
+ # Open Interest
534
+ if "open_interest" in params:
535
+ f = params["open_interest"]
536
+ val = market.open_interest or 0
537
+ if "min" in f and val < f["min"]: continue
538
+ if "max" in f and val > f["max"]: continue
539
+
540
+ # Resolution Date
541
+ if "resolution_date" in params:
542
+ f = params["resolution_date"]
543
+ val = market.resolution_date
544
+
545
+ if not val:
546
+ continue
547
+
548
+ # Ensure val is timezone-aware if the filter dates are, or naive if filter dates are.
549
+ # Assuming standard library comparison works (or both are TZ aware/naive).
550
+ if "before" in f and val >= f["before"]: continue
551
+ if "after" in f and val <= f["after"]: continue
552
+
553
+ # Price filter
554
+ if "price" in params:
555
+ f = params["price"]
556
+ outcome_key = f.get("outcome")
557
+ if outcome_key:
558
+ outcome = getattr(market, outcome_key, None)
559
+ if not outcome: continue
560
+ if "min" in f and outcome.price < f["min"]: continue
561
+ if "max" in f and outcome.price > f["max"]: continue
562
+
563
+ # Price Change 24h
564
+ if "price_change_24h" in params:
565
+ f = params["price_change_24h"]
566
+ outcome_key = f.get("outcome")
567
+ if outcome_key:
568
+ outcome = getattr(market, outcome_key, None)
569
+ if not outcome or outcome.price_change_24h is None: continue
570
+ if "min" in f and outcome.price_change_24h < f["min"]: continue
571
+ if "max" in f and outcome.price_change_24h > f["max"]: continue
572
+
573
+ results.append(market)
574
+
575
+ return results
576
+
577
+ def filter_events(
578
+ self,
579
+ events: List[UnifiedEvent],
580
+ criteria: Union[str, EventFilterCriteria, EventFilterFunction]
581
+ ) -> List[UnifiedEvent]:
582
+ """
583
+ Filter events based on criteria or custom function.
584
+
585
+ Args:
586
+ events: List of events to filter
587
+ criteria: Filter criteria object, string, or function
588
+
589
+ Returns:
590
+ Filtered list of events
591
+ """
592
+ # Handle predicate function
593
+ if callable(criteria):
594
+ return list(filter(criteria, events))
595
+
596
+ # Handle simple string search
597
+ if isinstance(criteria, str):
598
+ lower_query = criteria.lower()
599
+ return [e for e in events if e.title and lower_query in e.title.lower()]
600
+
601
+ # Handle criteria object
602
+ params: EventFilterCriteria = criteria # type: ignore
603
+ results = []
604
+
605
+ for event in events:
606
+ # Text search
607
+ if "text" in params:
608
+ lower_query = params["text"].lower()
609
+ search_in = params.get("search_in", ["title"])
610
+ match = False
611
+
612
+ if "title" in search_in and event.title and lower_query in event.title.lower():
613
+ match = True
614
+ elif "description" in search_in and event.description and lower_query in event.description.lower():
615
+ match = True
616
+ elif "category" in search_in and event.category and lower_query in event.category.lower():
617
+ match = True
618
+ elif "tags" in search_in and event.tags and any(lower_query in t.lower() for t in event.tags):
619
+ match = True
620
+
621
+ if not match:
622
+ continue
623
+
624
+ # Category
625
+ if "category" in params:
626
+ if event.category != params["category"]:
627
+ continue
628
+
629
+ # Tags
630
+ if "tags" in params and params["tags"]:
631
+ if not event.tags:
632
+ continue
633
+ query_tags = [t.lower() for t in params["tags"]]
634
+ event_tags = [t.lower() for t in event.tags]
635
+ if not any(t in event_tags for t in query_tags):
636
+ continue
637
+
638
+ # Market Count
639
+ if "market_count" in params:
640
+ f = params["market_count"]
641
+ count = len(event.markets)
642
+ if "min" in f and count < f["min"]: continue
643
+ if "max" in f and count > f["max"]: continue
644
+
645
+ # Total Volume
646
+ if "total_volume" in params:
647
+ f = params["total_volume"]
648
+ total_vol = sum(m.volume_24h for m in event.markets)
649
+ if "min" in f and total_vol < f["min"]: continue
650
+ if "max" in f and total_vol > f["max"]: continue
651
+
652
+ results.append(event)
653
+
654
+ return results
655
+
436
656
  def fetch_ohlcv(
437
657
  self,
438
658
  outcome_id: str,
pmxt/models.py CHANGED
@@ -15,6 +15,7 @@ SortOption = Literal["volume", "liquidity", "newest"]
15
15
  SearchIn = Literal["title", "description", "both"]
16
16
  OrderSide = Literal["buy", "sell"]
17
17
  OrderType = Literal["market", "limit"]
18
+ OutcomeType = Literal["yes", "no", "up", "down"]
18
19
 
19
20
 
20
21
  @dataclass
@@ -398,3 +399,70 @@ class CreateOrderParams:
398
399
 
399
400
  fee: Optional[int] = None
400
401
  """Optional fee rate (e.g., 1000 for 0.1%)"""
402
+
403
+
404
+ # ----------------------------------------------------------------------------
405
+ # Filtering Types
406
+ # ----------------------------------------------------------------------------
407
+
408
+ from typing import TypedDict, Callable, Union
409
+
410
+ class MinMax(TypedDict, total=False):
411
+ """Range filter."""
412
+ min: float
413
+ max: float
414
+
415
+ class DateRange(TypedDict, total=False):
416
+ """Date range filter."""
417
+ before: datetime
418
+ after: datetime
419
+
420
+ class PriceFilter(TypedDict, total=False):
421
+ """Price filter."""
422
+ outcome: OutcomeType
423
+ min: float
424
+ max: float
425
+
426
+ class MarketFilterCriteria(TypedDict, total=False):
427
+ """Criteria for filtering markets locally."""
428
+
429
+ # Text search
430
+ text: str
431
+ search_in: List[Literal["title", "description", "category", "tags", "outcomes"]]
432
+
433
+ # Numeric range filters
434
+ volume_24h: MinMax
435
+ volume: MinMax
436
+ liquidity: MinMax
437
+ open_interest: MinMax
438
+
439
+ # Date filters
440
+ resolution_date: DateRange
441
+
442
+ # Category/tag filters
443
+ category: str
444
+ tags: List[str]
445
+
446
+ # Price filters
447
+ price: PriceFilter
448
+ price_change_24h: PriceFilter
449
+
450
+ MarketFilterFunction = Callable[[UnifiedMarket], bool]
451
+
452
+ class EventFilterCriteria(TypedDict, total=False):
453
+ """Criteria for filtering events locally."""
454
+
455
+ # Text search
456
+ text: str
457
+ search_in: List[Literal["title", "description", "category", "tags"]]
458
+
459
+ # Category/tag filters
460
+ category: str
461
+ tags: List[str]
462
+
463
+ # Market metrics
464
+ market_count: MinMax
465
+ total_volume: MinMax
466
+
467
+ EventFilterFunction = Callable[[UnifiedEvent], bool]
468
+
pmxt/server_manager.py CHANGED
@@ -130,8 +130,25 @@ class ServerManager:
130
130
 
131
131
  return False
132
132
 
133
+ def stop(self) -> None:
134
+ """
135
+ Stop the currently running server.
136
+
137
+ This reads the lock file to find the process ID and sends a SIGTERM.
138
+ """
139
+ self._kill_old_server()
140
+
141
+ def restart(self) -> None:
142
+ """
143
+ Restart the server.
144
+
145
+ Stops the current server if running, and starts a fresh one.
146
+ """
147
+ self.stop()
148
+ self.ensure_server_running()
149
+
133
150
  def _kill_old_server(self) -> None:
134
- """Kill the currently running server."""
151
+ """Kill the currently running server (Internal)."""
135
152
  server_info = self.get_server_info()
136
153
  if server_info and 'pid' in server_info:
137
154
  import signal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pmxt
3
- Version: 1.5.7
3
+ Version: 1.7.0
4
4
  Summary: Unified prediction market data API - The ccxt for prediction markets
5
5
  Author: PMXT Contributors
6
6
  License: MIT
@@ -69,14 +69,14 @@ print(markets[0].title)
69
69
  outcome = markets[0].outcomes[0]
70
70
  print(f"{outcome.label}: {outcome.price * 100:.1f}%")
71
71
 
72
- # Fetch historical data (use outcome.id!)
72
+ # Fetch historical data (use outcome.outcome_id!)
73
73
  candles = poly.fetch_ohlcv(
74
- outcome.id,
74
+ outcome.outcome_id,
75
75
  pmxt.HistoryFilterParams(resolution="1d", limit=30)
76
76
  )
77
77
 
78
78
  # Get current order book
79
- order_book = poly.fetch_order_book(outcome.id)
79
+ order_book = poly.fetch_order_book(outcome.outcome_id)
80
80
  spread = order_book.asks[0].price - order_book.bids[0].price
81
81
  print(f"Spread: {spread * 100:.2f}%")
82
82
  ```
@@ -182,7 +182,7 @@ All methods return clean Python dataclasses:
182
182
  ```python
183
183
  @dataclass
184
184
  class UnifiedMarket:
185
- id: str
185
+ market_id: str # Use this for create_order
186
186
  title: str
187
187
  outcomes: List[MarketOutcome]
188
188
  volume_24h: float
@@ -192,7 +192,7 @@ class UnifiedMarket:
192
192
 
193
193
  @dataclass
194
194
  class MarketOutcome:
195
- id: str # Use this for fetch_ohlcv/fetch_order_book
195
+ outcome_id: str # Use this for fetch_ohlcv/fetch_order_book/fetch_trades
196
196
  label: str # "Trump", "Yes", etc.
197
197
  price: float # 0.0 to 1.0 (probability)
198
198
  # ... more fields
@@ -202,16 +202,16 @@ See the [full API reference](../../API_REFERENCE.md) for complete documentation.
202
202
 
203
203
  ## Important Notes
204
204
 
205
- ### Use `outcome.id`, not `market.id`
205
+ ### Use `outcome.outcome_id`, not `market.market_id`
206
206
 
207
207
  For deep-dive methods like `fetch_ohlcv()`, `fetch_order_book()`, and `fetch_trades()`, you must use the **outcome ID**, not the market ID:
208
208
 
209
209
  ```python
210
210
  markets = poly.search_markets("Trump")
211
- outcome_id = markets[0].outcomes[0].id # Correct
211
+ outcome_id = markets[0].outcomes[0].outcome_id # Correct
212
212
 
213
213
  candles = poly.fetch_ohlcv(outcome_id, ...) # Works
214
- candles = poly.fetch_ohlcv(markets[0].id, ...) # Wrong!
214
+ candles = poly.fetch_ohlcv(markets[0].market_id, ...) # Wrong!
215
215
  ```
216
216
 
217
217
  ### Prices are 0.0 to 1.0
@@ -1,15 +1,15 @@
1
- pmxt/__init__.py,sha256=b3gvQdSC1s_9caOe6sJj9imvUtv9m6WZxqTW6bfDBcY,1178
2
- pmxt/client.py,sha256=NnUD0h7BCbNFJv_oUkVD3YgpTHvRPrkFQbSmBGOWj5A,34798
3
- pmxt/models.py,sha256=zDbv2E8lZObwhUTYdIwK5MmX0QneNqb7nhGvkfsrQFc,8666
4
- pmxt/server_manager.py,sha256=6uS1LIZ2d5d_K-MtbMUAlCZvbvhZ_iyofKok55HEofc,11606
1
+ pmxt/__init__.py,sha256=mALOMPFV9BEkIwUqZ4_SAytlq9Lh3WVjSuAlTzLFFaY,1520
2
+ pmxt/client.py,sha256=lpDT0zDw1OXal_hbjcV69DidlgyyzKMmPMqvm4waNGg,43488
3
+ pmxt/models.py,sha256=LYfbd_SEtvqI9gYnVz6wAqlijJkSODrodE7v0owbyok,10291
4
+ pmxt/server_manager.py,sha256=kkbvQOgCBBDUbe0X4KGLZsWpuHxz3Yvb9ECDhjQxMOE,12050
5
5
  pmxt/_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  pmxt/_server/bin/pmxt-ensure-server,sha256=kXIond0UbxS52FAVQD7kHmSBaL_s6cbIyapLRr4KZJw,4544
7
7
  pmxt/_server/bin/pmxt-ensure-server.js,sha256=kXIond0UbxS52FAVQD7kHmSBaL_s6cbIyapLRr4KZJw,4544
8
- pmxt/_server/server/bundled.js,sha256=YxN4U7I4JoyxolYjBlBEOmdniHzOPuuIeBpwADjTkA8,4241376
9
- pmxt_internal/__init__.py,sha256=GLV0Tlq6W_-JZOYfGcpSpe0tYe7SipNsVQzQxI0L0wM,7756
10
- pmxt_internal/api_client.py,sha256=ctURaPvx0PiRRTWlAknPFkKzjS8abuWlFYpHIGkfTLo,27889
8
+ pmxt/_server/server/bundled.js,sha256=PoH6oBCeXSAFINx7AAPeVY89xbAoxjlWt3WWwbrZaLI,4256943
9
+ pmxt_internal/__init__.py,sha256=kB1112bowIVIz803gTxnoVVXdPoBOkZ7Hr3uUb4JTss,7756
10
+ pmxt_internal/api_client.py,sha256=Z4IcjvppvcZGKSah_BCFI8WZIyUX_rP05XLtSNIYklI,27889
11
11
  pmxt_internal/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
12
- pmxt_internal/configuration.py,sha256=O0KyVUlah0xHEgwb2pWZtL57eVrGaVNwzZvaeIAa2VM,18320
12
+ pmxt_internal/configuration.py,sha256=UxpBcHmAuuTBdl-1wAY8GVGyebJursuyndZjrFeCQk4,18320
13
13
  pmxt_internal/exceptions.py,sha256=txF8A7vlan57JS69kFPs-IZF-Qhp7IZobBTJVa4fOaM,6644
14
14
  pmxt_internal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  pmxt_internal/rest.py,sha256=FMj4yaV6XLr842u_ScWHSzQsTFdk0jaUeuWLJoRbogQ,9760
@@ -50,7 +50,7 @@ pmxt_internal/models/get_markets_by_slug_request.py,sha256=PtjXAw6hixHuYzCvSGH1a
50
50
  pmxt_internal/models/health_check200_response.py,sha256=yR_OkIlTPztO0zFmpyWllSwyaEbI48RpumfmVVCJAyc,2758
51
51
  pmxt_internal/models/history_filter_params.py,sha256=0i9oQLwJsRWRyzPZuW467y_0ccKpc2_I6T2lgZCkHd4,3239
52
52
  pmxt_internal/models/market_filter_params.py,sha256=Jun3HbQP05HEXlht1bYCrqrlgbO0QjP6vCOKCHkZn-E,3638
53
- pmxt_internal/models/market_outcome.py,sha256=STS17FU7VmS7bgOfUejJqfqNs_An3NJVqPQByBKkdgI,3200
53
+ pmxt_internal/models/market_outcome.py,sha256=W8-34Q-DQR-pVmpuAROSusu8BxwGfeUporTxh8BhKsQ,3532
54
54
  pmxt_internal/models/order.py,sha256=WniZ6JgnnhPBPyZje1wzgmsz4OjyOCIl5a-ksQFmUNM,4768
55
55
  pmxt_internal/models/order_book.py,sha256=FZQ5g81szBtpCun3vFFTDZb_xZkwUMVyEIF1ZZPzqhI,3615
56
56
  pmxt_internal/models/order_level.py,sha256=dMeuXlhBqe1kA-R1ysFjt77qJxx6ukrZHwO1y3hZ6EM,2735
@@ -62,11 +62,11 @@ pmxt_internal/models/search_markets_request.py,sha256=BARoy2GXgV7RQNIGck6UaOyQqf
62
62
  pmxt_internal/models/search_markets_request_args_inner.py,sha256=PkusFd_OxhUsItsBpluPJA11zg0sXXjbOK-lPmemvLs,5561
63
63
  pmxt_internal/models/trade.py,sha256=U6Fc18rbwILs9FmX8CSDYYL8dF6763l8QzeMQNRxQdo,3328
64
64
  pmxt_internal/models/unified_event.py,sha256=zJVokyA3_Ny9cFxI_kZaJShB_V-KMqr5rY-i6oMDXCU,3896
65
- pmxt_internal/models/unified_market.py,sha256=DoYhiH4HycYGlq858PEeB-CIA7haT6rxmJeYZJuulMA,5646
65
+ pmxt_internal/models/unified_market.py,sha256=0y917aPCUNtlToDhfJWwVh3lGT-1VRFh_pDF9f3C9b8,5919
66
66
  pmxt_internal/models/watch_order_book_request.py,sha256=kavGUI-SLz2-Kam_jcJ_h0GDe0-9UkxqCmVsAi6Uios,3726
67
67
  pmxt_internal/models/watch_order_book_request_args_inner.py,sha256=ZHrjmFDGxRG5MXbuz4mUp9KFfo3XS7zuXWTyMNgi4xI,5464
68
68
  pmxt_internal/models/watch_trades_request.py,sha256=brrg8JbEe-aeg7mIe_Y2HzRPogp-IfRhkXChrxzqoLU,3722
69
- pmxt-1.5.7.dist-info/METADATA,sha256=SifaoTjxEHuXt4tyhq8MjgC90Ec7fQvmXUGva_LMkac,6449
70
- pmxt-1.5.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
71
- pmxt-1.5.7.dist-info/top_level.txt,sha256=J_jrcouJ-x-5lpcXMxeW0GOSi1HsBVR5_PdSfvigVrw,19
72
- pmxt-1.5.7.dist-info/RECORD,,
69
+ pmxt-1.7.0.dist-info/METADATA,sha256=Q3N8stoYU3EiOESPB60W8cfG2D3TsRg0FKYLE1p6P38,6557
70
+ pmxt-1.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
71
+ pmxt-1.7.0.dist-info/top_level.txt,sha256=J_jrcouJ-x-5lpcXMxeW0GOSi1HsBVR5_PdSfvigVrw,19
72
+ pmxt-1.7.0.dist-info/RECORD,,
pmxt_internal/__init__.py CHANGED
@@ -14,7 +14,7 @@
14
14
  """ # noqa: E501
15
15
 
16
16
 
17
- __version__ = "1.5.7"
17
+ __version__ = "1.7.0"
18
18
 
19
19
  # Define package exports
20
20
  __all__ = [
@@ -91,7 +91,7 @@ class ApiClient:
91
91
  self.default_headers[header_name] = header_value
92
92
  self.cookie = cookie
93
93
  # Set default User-Agent.
94
- self.user_agent = 'OpenAPI-Generator/1.5.7/python'
94
+ self.user_agent = 'OpenAPI-Generator/1.7.0/python'
95
95
  self.client_side_validation = configuration.client_side_validation
96
96
 
97
97
  def __enter__(self):
@@ -506,7 +506,7 @@ class Configuration:
506
506
  "OS: {env}\n"\
507
507
  "Python Version: {pyversion}\n"\
508
508
  "Version of the API: 0.4.4\n"\
509
- "SDK Package Version: 1.5.7".\
509
+ "SDK Package Version: 1.7.0".\
510
510
  format(env=sys.platform, pyversion=sys.version)
511
511
 
512
512
  def get_host_settings(self) -> List[HostSetting]:
@@ -26,12 +26,13 @@ class MarketOutcome(BaseModel):
26
26
  """
27
27
  MarketOutcome
28
28
  """ # noqa: E501
29
- id: Optional[StrictStr] = None
29
+ id: Optional[StrictStr] = Field(default=None, description="DEPRECATED: Use outcomeId instead. Will be removed in v2.0")
30
+ outcome_id: Optional[StrictStr] = Field(default=None, description="Outcome ID for trading operations (CLOB Token ID for Polymarket, Market Ticker for Kalshi)", alias="outcomeId")
30
31
  label: Optional[StrictStr] = None
31
32
  price: Optional[Union[StrictFloat, StrictInt]] = None
32
33
  price_change24h: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, alias="priceChange24h")
33
34
  metadata: Optional[Dict[str, Any]] = Field(default=None, description="Exchange-specific metadata (e.g., clobTokenId for Polymarket)")
34
- __properties: ClassVar[List[str]] = ["id", "label", "price", "priceChange24h", "metadata"]
35
+ __properties: ClassVar[List[str]] = ["id", "outcomeId", "label", "price", "priceChange24h", "metadata"]
35
36
 
36
37
  model_config = ConfigDict(
37
38
  populate_by_name=True,
@@ -85,6 +86,7 @@ class MarketOutcome(BaseModel):
85
86
 
86
87
  _obj = cls.model_validate({
87
88
  "id": obj.get("id"),
89
+ "outcomeId": obj.get("outcomeId"),
88
90
  "label": obj.get("label"),
89
91
  "price": obj.get("price"),
90
92
  "priceChange24h": obj.get("priceChange24h"),
@@ -28,7 +28,8 @@ class UnifiedMarket(BaseModel):
28
28
  """
29
29
  UnifiedMarket
30
30
  """ # noqa: E501
31
- id: Optional[StrictStr] = None
31
+ id: Optional[StrictStr] = Field(default=None, description="DEPRECATED: Use marketId instead. Will be removed in v2.0")
32
+ market_id: Optional[StrictStr] = Field(default=None, description="The unique identifier for this market", alias="marketId")
32
33
  title: Optional[StrictStr] = None
33
34
  description: Optional[StrictStr] = None
34
35
  outcomes: Optional[List[MarketOutcome]] = None
@@ -45,7 +46,7 @@ class UnifiedMarket(BaseModel):
45
46
  no: Optional[MarketOutcome] = None
46
47
  up: Optional[MarketOutcome] = None
47
48
  down: Optional[MarketOutcome] = None
48
- __properties: ClassVar[List[str]] = ["id", "title", "description", "outcomes", "resolutionDate", "volume24h", "volume", "liquidity", "openInterest", "url", "image", "category", "tags", "yes", "no", "up", "down"]
49
+ __properties: ClassVar[List[str]] = ["id", "marketId", "title", "description", "outcomes", "resolutionDate", "volume24h", "volume", "liquidity", "openInterest", "url", "image", "category", "tags", "yes", "no", "up", "down"]
49
50
 
50
51
  model_config = ConfigDict(
51
52
  populate_by_name=True,
@@ -118,6 +119,7 @@ class UnifiedMarket(BaseModel):
118
119
 
119
120
  _obj = cls.model_validate({
120
121
  "id": obj.get("id"),
122
+ "marketId": obj.get("marketId"),
121
123
  "title": obj.get("title"),
122
124
  "description": obj.get("description"),
123
125
  "outcomes": [MarketOutcome.from_dict(_item) for _item in obj["outcomes"]] if obj.get("outcomes") is not None else None,
File without changes