pmxt 1.5.7__py3-none-any.whl → 1.6.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.6.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",
@@ -24494,6 +24494,227 @@ var require_BaseExchange = __commonJS({
24494
24494
  return (0, math_1.getExecutionPriceDetailed)(orderBook, side, amount);
24495
24495
  }
24496
24496
  // ----------------------------------------------------------------------------
24497
+ // Filtering Methods
24498
+ // ----------------------------------------------------------------------------
24499
+ /**
24500
+ * Filter markets based on criteria or custom function.
24501
+ *
24502
+ * @param markets - Array of markets to filter
24503
+ * @param criteria - Filter criteria object, string (simple text search), or predicate function
24504
+ * @returns Filtered array of markets
24505
+ *
24506
+ * @example Simple text search
24507
+ * api.filterMarkets(markets, 'Trump')
24508
+ *
24509
+ * @example Advanced filtering
24510
+ * api.filterMarkets(markets, {
24511
+ * text: 'Trump',
24512
+ * searchIn: ['title', 'tags'],
24513
+ * volume24h: { min: 10000 },
24514
+ * category: 'Politics',
24515
+ * price: { outcome: 'yes', max: 0.5 }
24516
+ * })
24517
+ *
24518
+ * @example Custom predicate
24519
+ * api.filterMarkets(markets, m => m.liquidity > 5000 && m.yes?.price < 0.3)
24520
+ */
24521
+ filterMarkets(markets, criteria) {
24522
+ if (typeof criteria === "function") {
24523
+ return markets.filter(criteria);
24524
+ }
24525
+ if (typeof criteria === "string") {
24526
+ const lowerQuery = criteria.toLowerCase();
24527
+ return markets.filter((m) => m.title.toLowerCase().includes(lowerQuery));
24528
+ }
24529
+ return markets.filter((market) => {
24530
+ if (criteria.text) {
24531
+ const lowerQuery = criteria.text.toLowerCase();
24532
+ const searchIn = criteria.searchIn || ["title"];
24533
+ let textMatch = false;
24534
+ for (const field of searchIn) {
24535
+ if (field === "title" && market.title?.toLowerCase().includes(lowerQuery)) {
24536
+ textMatch = true;
24537
+ break;
24538
+ }
24539
+ if (field === "description" && market.description?.toLowerCase().includes(lowerQuery)) {
24540
+ textMatch = true;
24541
+ break;
24542
+ }
24543
+ if (field === "category" && market.category?.toLowerCase().includes(lowerQuery)) {
24544
+ textMatch = true;
24545
+ break;
24546
+ }
24547
+ if (field === "tags" && market.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery))) {
24548
+ textMatch = true;
24549
+ break;
24550
+ }
24551
+ if (field === "outcomes" && market.outcomes?.some((o) => o.label.toLowerCase().includes(lowerQuery))) {
24552
+ textMatch = true;
24553
+ break;
24554
+ }
24555
+ }
24556
+ if (!textMatch)
24557
+ return false;
24558
+ }
24559
+ if (criteria.category && market.category !== criteria.category) {
24560
+ return false;
24561
+ }
24562
+ if (criteria.tags && criteria.tags.length > 0) {
24563
+ const hasMatchingTag = criteria.tags.some((tag) => market.tags?.some((marketTag) => marketTag.toLowerCase() === tag.toLowerCase()));
24564
+ if (!hasMatchingTag)
24565
+ return false;
24566
+ }
24567
+ if (criteria.volume24h) {
24568
+ if (criteria.volume24h.min !== void 0 && market.volume24h < criteria.volume24h.min) {
24569
+ return false;
24570
+ }
24571
+ if (criteria.volume24h.max !== void 0 && market.volume24h > criteria.volume24h.max) {
24572
+ return false;
24573
+ }
24574
+ }
24575
+ if (criteria.volume) {
24576
+ if (criteria.volume.min !== void 0 && (market.volume || 0) < criteria.volume.min) {
24577
+ return false;
24578
+ }
24579
+ if (criteria.volume.max !== void 0 && (market.volume || 0) > criteria.volume.max) {
24580
+ return false;
24581
+ }
24582
+ }
24583
+ if (criteria.liquidity) {
24584
+ if (criteria.liquidity.min !== void 0 && market.liquidity < criteria.liquidity.min) {
24585
+ return false;
24586
+ }
24587
+ if (criteria.liquidity.max !== void 0 && market.liquidity > criteria.liquidity.max) {
24588
+ return false;
24589
+ }
24590
+ }
24591
+ if (criteria.openInterest) {
24592
+ if (criteria.openInterest.min !== void 0 && (market.openInterest || 0) < criteria.openInterest.min) {
24593
+ return false;
24594
+ }
24595
+ if (criteria.openInterest.max !== void 0 && (market.openInterest || 0) > criteria.openInterest.max) {
24596
+ return false;
24597
+ }
24598
+ }
24599
+ if (criteria.resolutionDate) {
24600
+ const resDate = market.resolutionDate;
24601
+ if (criteria.resolutionDate.before && resDate >= criteria.resolutionDate.before) {
24602
+ return false;
24603
+ }
24604
+ if (criteria.resolutionDate.after && resDate <= criteria.resolutionDate.after) {
24605
+ return false;
24606
+ }
24607
+ }
24608
+ if (criteria.price) {
24609
+ const outcome = market[criteria.price.outcome];
24610
+ if (!outcome)
24611
+ return false;
24612
+ if (criteria.price.min !== void 0 && outcome.price < criteria.price.min) {
24613
+ return false;
24614
+ }
24615
+ if (criteria.price.max !== void 0 && outcome.price > criteria.price.max) {
24616
+ return false;
24617
+ }
24618
+ }
24619
+ if (criteria.priceChange24h) {
24620
+ const outcome = market[criteria.priceChange24h.outcome];
24621
+ if (!outcome || outcome.priceChange24h === void 0)
24622
+ return false;
24623
+ if (criteria.priceChange24h.min !== void 0 && outcome.priceChange24h < criteria.priceChange24h.min) {
24624
+ return false;
24625
+ }
24626
+ if (criteria.priceChange24h.max !== void 0 && outcome.priceChange24h > criteria.priceChange24h.max) {
24627
+ return false;
24628
+ }
24629
+ }
24630
+ return true;
24631
+ });
24632
+ }
24633
+ /**
24634
+ * Filter events based on criteria or custom function.
24635
+ *
24636
+ * @param events - Array of events to filter
24637
+ * @param criteria - Filter criteria object, string (simple text search), or predicate function
24638
+ * @returns Filtered array of events
24639
+ *
24640
+ * @example Simple text search
24641
+ * api.filterEvents(events, 'Trump')
24642
+ *
24643
+ * @example Advanced filtering
24644
+ * api.filterEvents(events, {
24645
+ * text: 'Election',
24646
+ * searchIn: ['title', 'tags'],
24647
+ * category: 'Politics',
24648
+ * marketCount: { min: 5 }
24649
+ * })
24650
+ *
24651
+ * @example Custom predicate
24652
+ * api.filterEvents(events, e => e.markets.length > 10)
24653
+ */
24654
+ filterEvents(events, criteria) {
24655
+ if (typeof criteria === "function") {
24656
+ return events.filter(criteria);
24657
+ }
24658
+ if (typeof criteria === "string") {
24659
+ const lowerQuery = criteria.toLowerCase();
24660
+ return events.filter((e) => e.title.toLowerCase().includes(lowerQuery));
24661
+ }
24662
+ return events.filter((event) => {
24663
+ if (criteria.text) {
24664
+ const lowerQuery = criteria.text.toLowerCase();
24665
+ const searchIn = criteria.searchIn || ["title"];
24666
+ let textMatch = false;
24667
+ for (const field of searchIn) {
24668
+ if (field === "title" && event.title?.toLowerCase().includes(lowerQuery)) {
24669
+ textMatch = true;
24670
+ break;
24671
+ }
24672
+ if (field === "description" && event.description?.toLowerCase().includes(lowerQuery)) {
24673
+ textMatch = true;
24674
+ break;
24675
+ }
24676
+ if (field === "category" && event.category?.toLowerCase().includes(lowerQuery)) {
24677
+ textMatch = true;
24678
+ break;
24679
+ }
24680
+ if (field === "tags" && event.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery))) {
24681
+ textMatch = true;
24682
+ break;
24683
+ }
24684
+ }
24685
+ if (!textMatch)
24686
+ return false;
24687
+ }
24688
+ if (criteria.category && event.category !== criteria.category) {
24689
+ return false;
24690
+ }
24691
+ if (criteria.tags && criteria.tags.length > 0) {
24692
+ const hasMatchingTag = criteria.tags.some((tag) => event.tags?.some((eventTag) => eventTag.toLowerCase() === tag.toLowerCase()));
24693
+ if (!hasMatchingTag)
24694
+ return false;
24695
+ }
24696
+ if (criteria.marketCount) {
24697
+ const count = event.markets.length;
24698
+ if (criteria.marketCount.min !== void 0 && count < criteria.marketCount.min) {
24699
+ return false;
24700
+ }
24701
+ if (criteria.marketCount.max !== void 0 && count > criteria.marketCount.max) {
24702
+ return false;
24703
+ }
24704
+ }
24705
+ if (criteria.totalVolume) {
24706
+ const totalVolume = event.markets.reduce((sum, m) => sum + m.volume24h, 0);
24707
+ if (criteria.totalVolume.min !== void 0 && totalVolume < criteria.totalVolume.min) {
24708
+ return false;
24709
+ }
24710
+ if (criteria.totalVolume.max !== void 0 && totalVolume > criteria.totalVolume.max) {
24711
+ return false;
24712
+ }
24713
+ }
24714
+ return true;
24715
+ });
24716
+ }
24717
+ // ----------------------------------------------------------------------------
24497
24718
  // WebSocket Streaming Methods
24498
24719
  // ----------------------------------------------------------------------------
24499
24720
  /**
@@ -38170,9 +38391,11 @@ var require_utils4 = __commonJS({
38170
38391
  if (index === 0 || label.toLowerCase() === "yes" || candidateName && label === candidateName) {
38171
38392
  priceChange = Number(market.oneDayPriceChange || 0);
38172
38393
  }
38394
+ const outcomeIdValue = clobTokenIds[index] || String(index);
38173
38395
  outcomes.push({
38174
- id: clobTokenIds[index] || String(index),
38396
+ id: outcomeIdValue,
38175
38397
  // Use CLOB Token ID as the primary ID
38398
+ outcomeId: outcomeIdValue,
38176
38399
  label: outcomeLabel,
38177
38400
  price: parseFloat(rawPrice) || 0,
38178
38401
  priceChange24h: priceChange,
@@ -38185,6 +38408,7 @@ var require_utils4 = __commonJS({
38185
38408
  }
38186
38409
  const um = {
38187
38410
  id: market.id,
38411
+ marketId: market.id,
38188
38412
  title: market.question ? `${event.title} - ${market.question}` : event.title,
38189
38413
  description: market.description || event.description,
38190
38414
  outcomes,
@@ -38421,6 +38645,26 @@ var require_getMarketsBySlug = __commonJS({
38421
38645
  }
38422
38646
  });
38423
38647
 
38648
+ // dist/utils/validation.js
38649
+ var require_validation = __commonJS({
38650
+ "dist/utils/validation.js"(exports2) {
38651
+ "use strict";
38652
+ Object.defineProperty(exports2, "__esModule", { value: true });
38653
+ exports2.validateOutcomeId = validateOutcomeId;
38654
+ exports2.validateIdFormat = validateIdFormat;
38655
+ function validateOutcomeId(id, context) {
38656
+ if (id.length < 10 && /^\d+$/.test(id)) {
38657
+ 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).`);
38658
+ }
38659
+ }
38660
+ function validateIdFormat(id, context) {
38661
+ if (!id || id.trim().length === 0) {
38662
+ throw new Error(`Invalid ID for ${context}: ID cannot be empty`);
38663
+ }
38664
+ }
38665
+ }
38666
+ });
38667
+
38424
38668
  // dist/exchanges/polymarket/fetchOHLCV.js
38425
38669
  var require_fetchOHLCV = __commonJS({
38426
38670
  "dist/exchanges/polymarket/fetchOHLCV.js"(exports2) {
@@ -38432,10 +38676,10 @@ var require_fetchOHLCV = __commonJS({
38432
38676
  exports2.fetchOHLCV = fetchOHLCV;
38433
38677
  var axios_1 = __importDefault(require_axios());
38434
38678
  var utils_1 = require_utils4();
38679
+ var validation_1 = require_validation();
38435
38680
  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
- }
38681
+ (0, validation_1.validateIdFormat)(id, "OHLCV");
38682
+ (0, validation_1.validateOutcomeId)(id, "OHLCV");
38439
38683
  try {
38440
38684
  const fidelity = (0, utils_1.mapIntervalToFidelity)(params.resolution);
38441
38685
  const nowTs = Math.floor(Date.now() / 1e3);
@@ -38519,7 +38763,10 @@ var require_fetchOrderBook = __commonJS({
38519
38763
  exports2.fetchOrderBook = fetchOrderBook;
38520
38764
  var axios_1 = __importDefault(require_axios());
38521
38765
  var utils_1 = require_utils4();
38766
+ var validation_1 = require_validation();
38522
38767
  async function fetchOrderBook(id) {
38768
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
38769
+ (0, validation_1.validateOutcomeId)(id, "OrderBook");
38523
38770
  try {
38524
38771
  const response = await axios_1.default.get(`${utils_1.CLOB_API_URL}/book`, {
38525
38772
  params: { token_id: id }
@@ -38557,10 +38804,10 @@ var require_fetchTrades = __commonJS({
38557
38804
  exports2.fetchTrades = fetchTrades;
38558
38805
  var axios_1 = __importDefault(require_axios());
38559
38806
  var utils_1 = require_utils4();
38807
+ var validation_1 = require_validation();
38560
38808
  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
- }
38809
+ (0, validation_1.validateIdFormat)(id, "Trades");
38810
+ (0, validation_1.validateOutcomeId)(id, "Trades");
38564
38811
  try {
38565
38812
  const queryParams = {
38566
38813
  asset_id: id
@@ -76055,7 +76302,7 @@ var require_permessage_deflate = __commonJS({
76055
76302
  });
76056
76303
 
76057
76304
  // ../node_modules/ws/lib/validation.js
76058
- var require_validation = __commonJS({
76305
+ var require_validation2 = __commonJS({
76059
76306
  "../node_modules/ws/lib/validation.js"(exports2, module2) {
76060
76307
  "use strict";
76061
76308
  var { isUtf8 } = require("buffer");
@@ -76268,7 +76515,7 @@ var require_receiver = __commonJS({
76268
76515
  kWebSocket
76269
76516
  } = require_constants();
76270
76517
  var { concat: concat2, toArrayBuffer, unmask } = require_buffer_util();
76271
- var { isValidStatusCode, isValidUTF8 } = require_validation();
76518
+ var { isValidStatusCode, isValidUTF8 } = require_validation2();
76272
76519
  var FastBuffer = Buffer[Symbol.species];
76273
76520
  var GET_INFO = 0;
76274
76521
  var GET_PAYLOAD_LENGTH_16 = 1;
@@ -76855,7 +77102,7 @@ var require_sender = __commonJS({
76855
77102
  var { randomFillSync } = require("crypto");
76856
77103
  var PerMessageDeflate = require_permessage_deflate();
76857
77104
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
76858
- var { isBlob: isBlob2, isValidStatusCode } = require_validation();
77105
+ var { isBlob: isBlob2, isValidStatusCode } = require_validation2();
76859
77106
  var { mask: applyMask, toBuffer } = require_buffer_util();
76860
77107
  var kByteLength = Symbol("kByteLength");
76861
77108
  var maskBuffer = Buffer.alloc(4);
@@ -77568,7 +77815,7 @@ var require_event_target = __commonJS({
77568
77815
  var require_extension = __commonJS({
77569
77816
  "../node_modules/ws/lib/extension.js"(exports2, module2) {
77570
77817
  "use strict";
77571
- var { tokenChars } = require_validation();
77818
+ var { tokenChars } = require_validation2();
77572
77819
  function push(dest, name, elem) {
77573
77820
  if (dest[name] === void 0) dest[name] = [elem];
77574
77821
  else dest[name].push(elem);
@@ -77732,7 +77979,7 @@ var require_websocket = __commonJS({
77732
77979
  var PerMessageDeflate = require_permessage_deflate();
77733
77980
  var Receiver = require_receiver();
77734
77981
  var Sender = require_sender();
77735
- var { isBlob: isBlob2 } = require_validation();
77982
+ var { isBlob: isBlob2 } = require_validation2();
77736
77983
  var {
77737
77984
  BINARY_TYPES,
77738
77985
  EMPTY_BUFFER,
@@ -78701,7 +78948,7 @@ var require_stream = __commonJS({
78701
78948
  var require_subprotocol = __commonJS({
78702
78949
  "../node_modules/ws/lib/subprotocol.js"(exports2, module2) {
78703
78950
  "use strict";
78704
- var { tokenChars } = require_validation();
78951
+ var { tokenChars } = require_validation2();
78705
78952
  function parse(header) {
78706
78953
  const protocols = /* @__PURE__ */ new Set();
78707
78954
  let start = -1;
@@ -83892,7 +84139,7 @@ var require_permessage_deflate2 = __commonJS({
83892
84139
  });
83893
84140
 
83894
84141
  // ../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/validation.js
83895
- var require_validation2 = __commonJS({
84142
+ var require_validation3 = __commonJS({
83896
84143
  "../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/validation.js"(exports2, module2) {
83897
84144
  "use strict";
83898
84145
  var { isUtf8 } = require("buffer");
@@ -84105,7 +84352,7 @@ var require_receiver2 = __commonJS({
84105
84352
  kWebSocket
84106
84353
  } = require_constants2();
84107
84354
  var { concat: concat2, toArrayBuffer, unmask } = require_buffer_util2();
84108
- var { isValidStatusCode, isValidUTF8 } = require_validation2();
84355
+ var { isValidStatusCode, isValidUTF8 } = require_validation3();
84109
84356
  var FastBuffer = Buffer[Symbol.species];
84110
84357
  var GET_INFO = 0;
84111
84358
  var GET_PAYLOAD_LENGTH_16 = 1;
@@ -84692,7 +84939,7 @@ var require_sender2 = __commonJS({
84692
84939
  var { randomFillSync } = require("crypto");
84693
84940
  var PerMessageDeflate = require_permessage_deflate2();
84694
84941
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants2();
84695
- var { isBlob: isBlob2, isValidStatusCode } = require_validation2();
84942
+ var { isBlob: isBlob2, isValidStatusCode } = require_validation3();
84696
84943
  var { mask: applyMask, toBuffer } = require_buffer_util2();
84697
84944
  var kByteLength = Symbol("kByteLength");
84698
84945
  var maskBuffer = Buffer.alloc(4);
@@ -85405,7 +85652,7 @@ var require_event_target2 = __commonJS({
85405
85652
  var require_extension2 = __commonJS({
85406
85653
  "../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/extension.js"(exports2, module2) {
85407
85654
  "use strict";
85408
- var { tokenChars } = require_validation2();
85655
+ var { tokenChars } = require_validation3();
85409
85656
  function push(dest, name, elem) {
85410
85657
  if (dest[name] === void 0) dest[name] = [elem];
85411
85658
  else dest[name].push(elem);
@@ -85569,7 +85816,7 @@ var require_websocket2 = __commonJS({
85569
85816
  var PerMessageDeflate = require_permessage_deflate2();
85570
85817
  var Receiver = require_receiver2();
85571
85818
  var Sender = require_sender2();
85572
- var { isBlob: isBlob2 } = require_validation2();
85819
+ var { isBlob: isBlob2 } = require_validation3();
85573
85820
  var {
85574
85821
  BINARY_TYPES,
85575
85822
  CLOSE_TIMEOUT,
@@ -86542,7 +86789,7 @@ var require_stream2 = __commonJS({
86542
86789
  var require_subprotocol2 = __commonJS({
86543
86790
  "../node_modules/@nevuamarkets/poly-websockets/node_modules/ws/lib/subprotocol.js"(exports2, module2) {
86544
86791
  "use strict";
86545
- var { tokenChars } = require_validation2();
86792
+ var { tokenChars } = require_validation3();
86546
86793
  function parse(header) {
86547
86794
  const protocols = /* @__PURE__ */ new Set();
86548
86795
  let start = -1;
@@ -105192,8 +105439,10 @@ var require_utils10 = __commonJS({
105192
105439
  const tokenEntries = Object.entries(market.tokens);
105193
105440
  tokenEntries.forEach(([label, tokenId], index) => {
105194
105441
  const outcomePrice = market.prices[index] || 0;
105442
+ const outcomeIdValue = tokenId;
105195
105443
  outcomes.push({
105196
- id: tokenId,
105444
+ id: outcomeIdValue,
105445
+ outcomeId: outcomeIdValue,
105197
105446
  label: label.charAt(0).toUpperCase() + label.slice(1),
105198
105447
  // Capitalize 'yes'/'no'
105199
105448
  price: outcomePrice,
@@ -105207,6 +105456,7 @@ var require_utils10 = __commonJS({
105207
105456
  }
105208
105457
  const um = {
105209
105458
  id: market.slug,
105459
+ marketId: market.slug,
105210
105460
  title: market.title || market.question,
105211
105461
  description: market.description,
105212
105462
  outcomes,
@@ -105415,7 +105665,9 @@ var require_fetchOHLCV2 = __commonJS({
105415
105665
  exports2.fetchOHLCV = fetchOHLCV;
105416
105666
  var axios_1 = __importDefault(require_axios());
105417
105667
  var utils_1 = require_utils10();
105668
+ var validation_1 = require_validation();
105418
105669
  async function fetchOHLCV(id, params) {
105670
+ (0, validation_1.validateIdFormat)(id, "OHLCV");
105419
105671
  try {
105420
105672
  const fidelity = (0, utils_1.mapIntervalToFidelity)(params.resolution);
105421
105673
  const url2 = `${utils_1.LIMITLESS_API_URL}/markets/${id}/historical-price`;
@@ -105466,7 +105718,9 @@ var require_fetchOrderBook2 = __commonJS({
105466
105718
  exports2.fetchOrderBook = fetchOrderBook;
105467
105719
  var axios_1 = __importDefault(require_axios());
105468
105720
  var utils_1 = require_utils10();
105721
+ var validation_1 = require_validation();
105469
105722
  async function fetchOrderBook(id) {
105723
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
105470
105724
  try {
105471
105725
  const url2 = `${utils_1.LIMITLESS_API_URL}/markets/${id}/orderbook`;
105472
105726
  const response = await axios_1.default.get(url2);
@@ -106092,6 +106346,7 @@ var require_utils11 = __commonJS({
106092
106346
  {
106093
106347
  id: market.ticker,
106094
106348
  // The actual market ticker (primary tradeable)
106349
+ outcomeId: market.ticker,
106095
106350
  label: candidateName || "Yes",
106096
106351
  price,
106097
106352
  priceChange24h: priceChange
@@ -106099,6 +106354,7 @@ var require_utils11 = __commonJS({
106099
106354
  {
106100
106355
  id: `${market.ticker}-NO`,
106101
106356
  // Virtual ID for the No outcome
106357
+ outcomeId: `${market.ticker}-NO`,
106102
106358
  label: candidateName ? `Not ${candidateName}` : "No",
106103
106359
  price: 1 - price,
106104
106360
  priceChange24h: -priceChange
@@ -106118,6 +106374,7 @@ var require_utils11 = __commonJS({
106118
106374
  }
106119
106375
  const um = {
106120
106376
  id: market.ticker,
106377
+ marketId: market.ticker,
106121
106378
  title: event.title,
106122
106379
  description: market.rules_primary || market.rules_secondary || "",
106123
106380
  outcomes,
@@ -106446,7 +106703,9 @@ var require_fetchOHLCV3 = __commonJS({
106446
106703
  exports2.fetchOHLCV = fetchOHLCV;
106447
106704
  var axios_1 = __importDefault(require_axios());
106448
106705
  var utils_1 = require_utils11();
106706
+ var validation_1 = require_validation();
106449
106707
  async function fetchOHLCV(id, params) {
106708
+ (0, validation_1.validateIdFormat)(id, "OHLCV");
106450
106709
  try {
106451
106710
  const cleanedId = id.replace(/-NO$/, "");
106452
106711
  const normalizedId = cleanedId.toUpperCase();
@@ -106532,7 +106791,9 @@ var require_fetchOrderBook3 = __commonJS({
106532
106791
  Object.defineProperty(exports2, "__esModule", { value: true });
106533
106792
  exports2.fetchOrderBook = fetchOrderBook;
106534
106793
  var axios_1 = __importDefault(require_axios());
106794
+ var validation_1 = require_validation();
106535
106795
  async function fetchOrderBook(id) {
106796
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
106536
106797
  try {
106537
106798
  const isNoOutcome = id.endsWith("-NO");
106538
106799
  const ticker = id.replace(/-NO$/, "");
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.6.0
4
4
  Summary: Unified prediction market data API - The ccxt for prediction markets
5
5
  Author: PMXT Contributors
6
6
  License: MIT
@@ -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=kbtmNd7bfDO-SNmmsJ0D11RuTWmX1gYBrJiVyTTRdgo,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=ZfMpYAXX5UZECbkYlvCB5mGPvhorSrkw-h4m0ZeRdEk,4251928
9
+ pmxt_internal/__init__.py,sha256=CEpTnAT2C0rjNGAaRZbClTn4UvDWuHTe3AeC5n7AuBE,7756
10
+ pmxt_internal/api_client.py,sha256=nNG9x5sU703c-iI5McO1R21TNLikw9sHxn5osmiSmkk,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=QzLqWzVIo7r9XwfJKlfaHgWg7cbgNmERylbCO6Ja4b8,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.6.0.dist-info/METADATA,sha256=aglrK1EbEVfA2fdanlMDKDCmRdHd8ONF8mcUOuvf9h8,6449
70
+ pmxt-1.6.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
71
+ pmxt-1.6.0.dist-info/top_level.txt,sha256=J_jrcouJ-x-5lpcXMxeW0GOSi1HsBVR5_PdSfvigVrw,19
72
+ pmxt-1.6.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.6.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.6.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.6.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