prediction-market-agent-tooling 0.61.1.dev489__py3-none-any.whl → 0.62.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.
Files changed (50) hide show
  1. prediction_market_agent_tooling/deploy/agent.py +4 -5
  2. prediction_market_agent_tooling/deploy/betting_strategy.py +53 -69
  3. prediction_market_agent_tooling/gtypes.py +105 -27
  4. prediction_market_agent_tooling/jobs/jobs_models.py +5 -7
  5. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +13 -17
  6. prediction_market_agent_tooling/markets/agent_market.py +96 -55
  7. prediction_market_agent_tooling/markets/blockchain_utils.py +2 -32
  8. prediction_market_agent_tooling/markets/data_models.py +40 -44
  9. prediction_market_agent_tooling/markets/manifold/api.py +2 -6
  10. prediction_market_agent_tooling/markets/manifold/data_models.py +33 -25
  11. prediction_market_agent_tooling/markets/manifold/manifold.py +13 -11
  12. prediction_market_agent_tooling/markets/market_fees.py +6 -2
  13. prediction_market_agent_tooling/markets/omen/data_models.py +66 -57
  14. prediction_market_agent_tooling/markets/omen/omen.py +222 -250
  15. prediction_market_agent_tooling/markets/omen/omen_contracts.py +32 -33
  16. prediction_market_agent_tooling/markets/omen/omen_resolving.py +7 -14
  17. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +20 -14
  18. prediction_market_agent_tooling/markets/polymarket/data_models.py +3 -3
  19. prediction_market_agent_tooling/markets/polymarket/data_models_web.py +4 -4
  20. prediction_market_agent_tooling/markets/polymarket/polymarket.py +3 -5
  21. prediction_market_agent_tooling/markets/seer/data_models.py +92 -5
  22. prediction_market_agent_tooling/markets/seer/seer.py +93 -115
  23. prediction_market_agent_tooling/markets/seer/seer_contracts.py +11 -6
  24. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +18 -26
  25. prediction_market_agent_tooling/monitor/monitor.py +2 -2
  26. prediction_market_agent_tooling/tools/_generic_value.py +261 -0
  27. prediction_market_agent_tooling/tools/balances.py +14 -11
  28. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +12 -10
  29. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +31 -24
  30. prediction_market_agent_tooling/tools/betting_strategies/utils.py +3 -1
  31. prediction_market_agent_tooling/tools/contract.py +14 -10
  32. prediction_market_agent_tooling/tools/cow/cow_manager.py +3 -4
  33. prediction_market_agent_tooling/tools/cow/cow_order.py +51 -7
  34. prediction_market_agent_tooling/tools/langfuse_client_utils.py +13 -1
  35. prediction_market_agent_tooling/tools/omen/sell_positions.py +6 -3
  36. prediction_market_agent_tooling/tools/safe.py +5 -6
  37. prediction_market_agent_tooling/tools/tokens/auto_deposit.py +36 -27
  38. prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +4 -25
  39. prediction_market_agent_tooling/tools/tokens/main_token.py +2 -2
  40. prediction_market_agent_tooling/tools/tokens/token_utils.py +46 -0
  41. prediction_market_agent_tooling/tools/tokens/usd.py +79 -0
  42. prediction_market_agent_tooling/tools/utils.py +14 -8
  43. prediction_market_agent_tooling/tools/web3_utils.py +24 -41
  44. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/METADATA +2 -1
  45. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/RECORD +48 -47
  46. prediction_market_agent_tooling/markets/seer/price_manager.py +0 -111
  47. prediction_market_agent_tooling/markets/seer/subgraph_data_models.py +0 -57
  48. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/LICENSE +0 -0
  49. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/WHEEL +0 -0
  50. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/entry_points.txt +0 -0
@@ -15,12 +15,12 @@ from prediction_market_agent_tooling.gtypes import (
15
15
  HexBytes,
16
16
  HexStr,
17
17
  IPFSCIDVersion0,
18
- OmenOutcomeToken,
18
+ OutcomeWei,
19
19
  TxParams,
20
20
  TxReceipt,
21
21
  Wei,
22
22
  int_to_hexbytes,
23
- wei_type,
23
+ xDaiWei,
24
24
  )
25
25
  from prediction_market_agent_tooling.markets.omen.data_models import (
26
26
  INVALID_ANSWER_HEX_BYTES,
@@ -128,8 +128,8 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
128
128
 
129
129
  def balanceOf(
130
130
  self, from_address: ChecksumAddress, position_id: int, web3: Web3 | None = None
131
- ) -> Wei:
132
- balance = wei_type(
131
+ ) -> OutcomeWei:
132
+ balance = OutcomeWei(
133
133
  self.call("balanceOf", [from_address, position_id], web3=web3)
134
134
  )
135
135
  return balance
@@ -169,7 +169,7 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
169
169
  collateral_token_address: ChecksumAddress,
170
170
  conditionId: HexBytes,
171
171
  index_sets: t.List[int],
172
- amount: Wei,
172
+ amount: OutcomeWei,
173
173
  parent_collection_id: HexStr = build_parent_collection_id(),
174
174
  web3: Web3 | None = None,
175
175
  ) -> TxReceipt:
@@ -296,28 +296,28 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
296
296
  # Factory contract at https://gnosisscan.io/address/0x9083a2b699c0a4ad06f63580bde2635d26a3eef0.
297
297
 
298
298
  def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
299
- balance: Wei = self.call("balanceOf", [for_address], web3=web3)
299
+ balance = Wei(self.call("balanceOf", [for_address], web3=web3))
300
300
  return balance
301
301
 
302
302
  def calcBuyAmount(
303
303
  self, investment_amount: Wei, outcome_index: int, web3: Web3 | None = None
304
- ) -> OmenOutcomeToken:
304
+ ) -> OutcomeWei:
305
305
  """
306
306
  Returns amount of shares we will get for the given outcome_index for the given investment amount.
307
307
  """
308
- calculated_shares: OmenOutcomeToken = self.call(
309
- "calcBuyAmount", [investment_amount, outcome_index], web3=web3
308
+ calculated_shares = OutcomeWei(
309
+ self.call("calcBuyAmount", [investment_amount, outcome_index], web3=web3)
310
310
  )
311
311
  return calculated_shares
312
312
 
313
313
  def calcSellAmount(
314
314
  self, return_amount: Wei, outcome_index: int, web3: Web3 | None = None
315
- ) -> OmenOutcomeToken:
315
+ ) -> OutcomeWei:
316
316
  """
317
317
  Returns amount of shares we will sell for the requested wei.
318
318
  """
319
- calculated_shares: OmenOutcomeToken = self.call(
320
- "calcSellAmount", [return_amount, outcome_index], web3=web3
319
+ calculated_shares = OutcomeWei(
320
+ self.call("calcSellAmount", [return_amount, outcome_index], web3=web3)
321
321
  )
322
322
  return calculated_shares
323
323
 
@@ -334,7 +334,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
334
334
  api_keys: APIKeys,
335
335
  amount_wei: Wei,
336
336
  outcome_index: int,
337
- min_outcome_tokens_to_buy: OmenOutcomeToken,
337
+ min_outcome_tokens_to_buy: OutcomeWei,
338
338
  tx_params: t.Optional[TxParams] = None,
339
339
  web3: Web3 | None = None,
340
340
  ) -> TxReceipt:
@@ -355,7 +355,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
355
355
  api_keys: APIKeys,
356
356
  amount_wei: Wei,
357
357
  outcome_index: int,
358
- max_outcome_tokens_to_sell: OmenOutcomeToken,
358
+ max_outcome_tokens_to_sell: OutcomeWei,
359
359
  tx_params: t.Optional[TxParams] = None,
360
360
  web3: Web3 | None = None,
361
361
  ) -> TxReceipt:
@@ -411,7 +411,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
411
411
 
412
412
  def totalSupply(self, web3: Web3 | None = None) -> Wei:
413
413
  # This is the liquidity you seen on the Omen website (but in Wei).
414
- total_supply: Wei = self.call("totalSupply", web3=web3)
414
+ total_supply = Wei(self.call("totalSupply", web3=web3))
415
415
  return total_supply
416
416
 
417
417
  def get_collateral_token_contract(
@@ -466,7 +466,7 @@ class OmenFixedProductMarketMakerFactoryContract(ContractOnGnosisChain):
466
466
  initial_funds_wei: Wei,
467
467
  collateral_token_address: ChecksumAddress,
468
468
  fee: Wei, # This is actually fee in %, 'where 100% == 1 xDai'.
469
- distribution_hint: list[OmenOutcomeToken] | None = None,
469
+ distribution_hint: list[OutcomeWei] | None = None,
470
470
  tx_params: t.Optional[TxParams] = None,
471
471
  web3: Web3 | None = None,
472
472
  ) -> tuple[
@@ -585,7 +585,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
585
585
  api_keys: APIKeys,
586
586
  question: str,
587
587
  category: str,
588
- outcomes: list[str],
588
+ outcomes: t.Sequence[str],
589
589
  language: str,
590
590
  arbitrator: Arbitrator,
591
591
  opening: DatetimeUTC,
@@ -640,14 +640,14 @@ class OmenRealitioContract(ContractOnGnosisChain):
640
640
  api_keys: APIKeys,
641
641
  question_id: HexBytes,
642
642
  answer: HexBytes,
643
- bond: Wei,
644
- max_previous: Wei | None = None,
643
+ bond: xDaiWei,
644
+ max_previous: xDaiWei | None = None,
645
645
  web3: Web3 | None = None,
646
646
  ) -> TxReceipt:
647
647
  if max_previous is None:
648
648
  # If not provided, defaults to 0, which means no checking,
649
649
  # same as on Omen website: https://github.com/protofire/omen-exchange/blob/763d9c9d05ebf9edacbc1dbaa561aa5d08813c0f/app/src/services/realitio.ts#L363.
650
- max_previous = Wei(0)
650
+ max_previous = xDaiWei(0)
651
651
 
652
652
  return self.send_with_value(
653
653
  api_keys=api_keys,
@@ -657,7 +657,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
657
657
  answer=answer,
658
658
  max_previous=max_previous,
659
659
  ),
660
- amount_wei=bond,
660
+ amount_wei=bond.as_wei,
661
661
  web3=web3,
662
662
  )
663
663
 
@@ -666,9 +666,9 @@ class OmenRealitioContract(ContractOnGnosisChain):
666
666
  api_keys: APIKeys,
667
667
  question_id: HexBytes,
668
668
  answer: str,
669
- outcomes: list[str],
670
- bond: Wei,
671
- max_previous: Wei | None = None,
669
+ outcomes: t.Sequence[str],
670
+ bond: xDaiWei,
671
+ max_previous: xDaiWei | None = None,
672
672
  web3: Web3 | None = None,
673
673
  ) -> TxReceipt:
674
674
  # Normalise the answer to lowercase, to match Enum values as [YES, NO] against outcomes as ["Yes", "No"].
@@ -690,8 +690,8 @@ class OmenRealitioContract(ContractOnGnosisChain):
690
690
  self,
691
691
  api_keys: APIKeys,
692
692
  question_id: HexBytes,
693
- bond: Wei,
694
- max_previous: Wei | None = None,
693
+ bond: xDaiWei,
694
+ max_previous: xDaiWei | None = None,
695
695
  web3: Web3 | None = None,
696
696
  ) -> TxReceipt:
697
697
  return self.submitAnswer(
@@ -709,7 +709,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
709
709
  question_id: HexBytes,
710
710
  history_hashes: list[HexBytes],
711
711
  addresses: list[ChecksumAddress],
712
- bonds: list[Wei],
712
+ bonds: list[xDaiWei],
713
713
  answers: list[HexBytes],
714
714
  tx_params: t.Optional[TxParams] = None,
715
715
  web3: Web3 | None = None,
@@ -732,8 +732,8 @@ class OmenRealitioContract(ContractOnGnosisChain):
732
732
  self,
733
733
  from_address: ChecksumAddress,
734
734
  web3: Web3 | None = None,
735
- ) -> Wei:
736
- balance = wei_type(self.call("balanceOf", [from_address], web3=web3))
735
+ ) -> xDaiWei:
736
+ balance = xDaiWei(self.call("balanceOf", [from_address], web3=web3))
737
737
  return balance
738
738
 
739
739
  def withdraw(
@@ -826,10 +826,7 @@ class OmenAgentResultMappingContract(ContractOnGnosisChain):
826
826
  return self.send(
827
827
  api_keys=api_keys,
828
828
  function_name="addPrediction",
829
- function_params=[
830
- market_address,
831
- prediction.model_dump(by_alias=True, exclude="publisher_checksummed"),
832
- ],
829
+ function_params=[market_address, prediction.model_dump(by_alias=True)],
833
830
  web3=web3,
834
831
  )
835
832
 
@@ -899,9 +896,11 @@ class OmenThumbnailMapping(ContractOnGnosisChain):
899
896
  class CollateralTokenChoice(str, Enum):
900
897
  wxdai = "wxdai"
901
898
  sdai = "sdai"
899
+ gno = "gno"
902
900
 
903
901
 
904
902
  COLLATERAL_TOKEN_CHOICE_TO_ADDRESS = {
905
903
  CollateralTokenChoice.wxdai: WrappedxDaiContract().address,
906
904
  CollateralTokenChoice.sdai: sDaiContract().address,
905
+ CollateralTokenChoice.gno: GNOContract().address,
907
906
  }
@@ -5,8 +5,8 @@ from prediction_market_agent_tooling.gtypes import (
5
5
  ChecksumAddress,
6
6
  HexAddress,
7
7
  HexBytes,
8
- Wei,
9
8
  xDai,
9
+ xDaiWei,
10
10
  )
11
11
  from prediction_market_agent_tooling.loggers import logger
12
12
  from prediction_market_agent_tooling.markets.data_models import Resolution
@@ -32,12 +32,7 @@ from prediction_market_agent_tooling.tools.tokens.main_token import (
32
32
  MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
33
33
  )
34
34
  from prediction_market_agent_tooling.tools.utils import utcnow
35
- from prediction_market_agent_tooling.tools.web3_utils import (
36
- ZERO_BYTES,
37
- wei_to_xdai,
38
- xdai_to_wei,
39
- xdai_type,
40
- )
35
+ from prediction_market_agent_tooling.tools.web3_utils import ZERO_BYTES
41
36
 
42
37
 
43
38
  def claim_bonds_on_realitio_questions(
@@ -93,7 +88,7 @@ def claim_bonds_on_realitio_question(
93
88
 
94
89
  history_hashes: list[HexBytes] = []
95
90
  addresses: list[ChecksumAddress] = []
96
- bonds: list[Wei] = []
91
+ bonds: list[xDaiWei] = []
97
92
  answers: list[HexBytes] = []
98
93
 
99
94
  # Caller must provide the answer history, in reverse order.
@@ -128,7 +123,7 @@ def claim_bonds_on_realitio_question(
128
123
  # Keeping balance on Realitio is not useful, so it's recommended to just withdraw it.
129
124
  if current_balance > 0 and auto_withdraw:
130
125
  logger.info(
131
- f"Withdrawing remaining balance {wei_to_xdai(current_balance)} xDai from Realitio."
126
+ f"Withdrawing remaining balance {current_balance.as_xdai} xDai from Realitio."
132
127
  )
133
128
  realitio_contract.withdraw(api_keys, web3=web3)
134
129
 
@@ -150,9 +145,7 @@ def finalize_markets(
150
145
  # If we don't have enough of xDai for bond, try to get it from the keeping token.
151
146
  send_keeping_token_to_eoa_xdai(
152
147
  api_keys=api_keys,
153
- min_required_balance=xdai_type(
154
- realitio_bond + MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES
155
- ),
148
+ min_required_balance=realitio_bond + MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
156
149
  web3=web3,
157
150
  )
158
151
 
@@ -235,7 +228,7 @@ def omen_submit_answer_market_tx(
235
228
  question_id=market.question.id,
236
229
  answer=resolution.value,
237
230
  outcomes=market.question.outcomes,
238
- bond=xdai_to_wei(bond),
231
+ bond=bond.as_xdai_wei,
239
232
  web3=web3,
240
233
  )
241
234
 
@@ -254,7 +247,7 @@ def omen_submit_invalid_answer_market_tx(
254
247
  realitio_contract.submit_answer_invalid(
255
248
  api_keys=api_keys,
256
249
  question_id=market.question.id,
257
- bond=xdai_to_wei(bond),
250
+ bond=bond.as_xdai_wei,
258
251
  web3=web3,
259
252
  )
260
253
 
@@ -11,7 +11,6 @@ from prediction_market_agent_tooling.gtypes import (
11
11
  HexAddress,
12
12
  HexBytes,
13
13
  Wei,
14
- wei_type,
15
14
  )
16
15
  from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
17
16
  from prediction_market_agent_tooling.markets.base_subgraph_handler import (
@@ -24,6 +23,8 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
24
23
  OmenMarket,
25
24
  OmenPosition,
26
25
  OmenUserPosition,
26
+ OutcomeStr,
27
+ OutcomeWei,
27
28
  RealityAnswer,
28
29
  RealityQuestion,
29
30
  RealityResponse,
@@ -44,6 +45,7 @@ from prediction_market_agent_tooling.tools.utils import (
44
45
  from prediction_market_agent_tooling.tools.web3_utils import (
45
46
  ZERO_BYTES,
46
47
  byte32_to_ipfscidv0,
48
+ unwrap_generic_value,
47
49
  )
48
50
 
49
51
  SAFE_COLLATERAL_TOKENS = (
@@ -209,7 +211,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
209
211
  self,
210
212
  creator: HexAddress | None,
211
213
  creator_in: t.Sequence[HexAddress] | None,
212
- outcomes: list[str],
214
+ outcomes: t.Sequence[OutcomeStr],
213
215
  created_after: DatetimeUTC | None,
214
216
  question_opened_before: DatetimeUTC | None,
215
217
  question_opened_after: DatetimeUTC | None,
@@ -347,7 +349,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
347
349
  # because even closed markets don't need to be resolved yet (e.g. if someone forgot to finalize the question on reality).
348
350
  opened_after = utcnow()
349
351
  # Even if the market isn't closed yet, liquidity can be withdrawn to 0, which essentially closes the market.
350
- liquidity_bigger_than = wei_type(0)
352
+ liquidity_bigger_than = Wei(0)
351
353
  elif filter_by == FilterBy.NONE:
352
354
  pass
353
355
  else:
@@ -391,7 +393,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
391
393
  id_in: list[str] | None = None,
392
394
  sort_by_field: FieldPath | None = None,
393
395
  sort_direction: str | None = None,
394
- outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
396
+ outcomes: t.Sequence[OutcomeStr] = OMEN_BINARY_MARKET_OUTCOMES,
395
397
  collateral_token_address_in: (
396
398
  tuple[ChecksumAddress, ...] | None
397
399
  ) = SAFE_COLLATERAL_TOKENS_ADDRESSES,
@@ -434,7 +436,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
434
436
  first=(
435
437
  limit if limit else sys.maxsize
436
438
  ), # if not limit, we fetch all possible markets
437
- where=where_stms,
439
+ where=unwrap_generic_value(where_stms),
438
440
  **optional_params,
439
441
  )
440
442
 
@@ -489,7 +491,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
489
491
  where_stms["conditionIds_contains"] = [condition_id.hex()]
490
492
 
491
493
  positions = self.conditional_tokens_subgraph.Query.positions(
492
- first=sys.maxsize, where=where_stms
494
+ first=sys.maxsize, where=unwrap_generic_value(where_stms)
493
495
  )
494
496
  fields = self._get_fields_for_positions(positions)
495
497
  result = self.sg.query_json(fields)
@@ -500,7 +502,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
500
502
  self,
501
503
  better_address: ChecksumAddress,
502
504
  position_id_in: list[HexBytes] | None = None,
503
- total_balance_bigger_than: Wei | None = None,
505
+ total_balance_bigger_than: OutcomeWei | None = None,
504
506
  ) -> list[OmenUserPosition]:
505
507
  where_stms: dict[str, t.Any] = {
506
508
  "user": better_address.lower(),
@@ -514,7 +516,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
514
516
  where_stms["position_"]["positionId_in"] = [x.hex() for x in position_id_in]
515
517
 
516
518
  positions = self.conditional_tokens_subgraph.Query.userPositions(
517
- first=sys.maxsize, where=where_stms
519
+ first=sys.maxsize, where=unwrap_generic_value(where_stms)
518
520
  )
519
521
  fields = self._get_fields_for_user_positions(positions)
520
522
  result = self.sg.query_json(fields)
@@ -568,7 +570,9 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
568
570
  < to_int_timestamp(market_resolved_before)
569
571
  )
570
572
  if collateral_amount_more_than is not None:
571
- where_stms.append(trade.collateralAmount > collateral_amount_more_than)
573
+ where_stms.append(
574
+ trade.collateralAmount > collateral_amount_more_than.value
575
+ )
572
576
 
573
577
  # These values can not be set to `None`, but they can be omitted.
574
578
  optional_params = {}
@@ -579,7 +583,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
579
583
 
580
584
  trades = self.trades_subgraph.Query.fpmmTrades(
581
585
  first=limit if limit else sys.maxsize,
582
- where=where_stms,
586
+ where=unwrap_generic_value(where_stms),
583
587
  **optional_params,
584
588
  )
585
589
  fields = self._get_fields_for_bets(trades)
@@ -818,7 +822,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
818
822
  first=(
819
823
  limit if limit else sys.maxsize
820
824
  ), # if not limit, we fetch all possible
821
- where=where_stms,
825
+ where=unwrap_generic_value(where_stms),
822
826
  )
823
827
  fields = self._get_fields_for_reality_questions(questions)
824
828
  result = self.sg.query_json(fields)
@@ -832,7 +836,9 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
832
836
  answer.question.questionId == question_id.hex(),
833
837
  ]
834
838
 
835
- answers = self.realityeth_subgraph.Query.answers(where=where_stms)
839
+ answers = self.realityeth_subgraph.Query.answers(
840
+ where=unwrap_generic_value(where_stms)
841
+ )
836
842
  fields = self._get_fields_for_answers(answers)
837
843
  result = self.sg.query_json(fields)
838
844
  items = self._parse_items_from_json(result)
@@ -879,7 +885,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
879
885
  first=(
880
886
  limit if limit else sys.maxsize
881
887
  ), # if not limit, we fetch all possible
882
- where=where_stms,
888
+ where=unwrap_generic_value(where_stms),
883
889
  )
884
890
  fields = self._get_fields_for_responses(responses)
885
891
  result = self.sg.query_json(fields)
@@ -938,7 +944,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
938
944
 
939
945
  prediction_added = (
940
946
  self.omen_agent_result_mapping_subgraph.Query.predictionAddeds(
941
- where=where_stms,
947
+ where=unwrap_generic_value(where_stms),
942
948
  orderBy="blockNumber",
943
949
  orderDirection="asc",
944
950
  )
@@ -1,6 +1,6 @@
1
1
  from pydantic import BaseModel
2
2
 
3
- from prediction_market_agent_tooling.gtypes import USDC, Probability, usdc_type
3
+ from prediction_market_agent_tooling.gtypes import USDC, OutcomeStr, Probability
4
4
  from prediction_market_agent_tooling.markets.data_models import Resolution
5
5
  from prediction_market_agent_tooling.markets.polymarket.data_models_web import (
6
6
  POLYMARKET_FALSE_OUTCOME,
@@ -22,7 +22,7 @@ class PolymarketRewards(BaseModel):
22
22
 
23
23
  class PolymarketToken(BaseModel):
24
24
  token_id: str
25
- outcome: str
25
+ outcome: OutcomeStr
26
26
  winner: bool
27
27
 
28
28
 
@@ -111,7 +111,7 @@ class PolymarketPriceResponse(BaseModel):
111
111
 
112
112
  @property
113
113
  def price_dec(self) -> USDC:
114
- return usdc_type(self.price)
114
+ return USDC(self.price)
115
115
 
116
116
 
117
117
  class Prices(BaseModel):
@@ -1,5 +1,5 @@
1
1
  """
2
- Autogenerated using `datamodel-codegen` from a single Polymarket response. Then adjusted, fixed.
2
+ Autogenerated using `datamodel-codegen` from a single Polymarket response. Then adjusted, fixed.
3
3
  Keep in mind that not all fields were used so far, so there might be some bugs.
4
4
 
5
5
  These models are based on what Polymarket's website returns in the response, and `prediction_market_agent_tooling/markets/polymarket/data_models.py` are based on what their API returns.
@@ -11,7 +11,7 @@ import typing as t
11
11
  import requests
12
12
  from pydantic import BaseModel, field_validator
13
13
 
14
- from prediction_market_agent_tooling.gtypes import USDC, HexAddress
14
+ from prediction_market_agent_tooling.gtypes import USDC, HexAddress, OutcomeStr
15
15
  from prediction_market_agent_tooling.loggers import logger
16
16
  from prediction_market_agent_tooling.markets.data_models import Resolution
17
17
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC
@@ -47,7 +47,7 @@ class Market1(BaseModel):
47
47
  question: str
48
48
  image: str
49
49
  volume: USDC | None = None
50
- outcomes: list[str]
50
+ outcomes: t.Sequence[OutcomeStr]
51
51
  outcomePrices: list[USDC]
52
52
  active: bool
53
53
  archived: bool
@@ -92,7 +92,7 @@ class Market(BaseModel):
92
92
  fee: str | None = None
93
93
  lowerBound: t.Any | None = None
94
94
  upperBound: t.Any | None = None
95
- outcomes: list[str]
95
+ outcomes: t.Sequence[OutcomeStr]
96
96
  image: str | None = None
97
97
  icon: str | None = None
98
98
  imageOptimized: t.Any | None = None
@@ -1,12 +1,12 @@
1
1
  import typing as t
2
2
 
3
+ from prediction_market_agent_tooling.gtypes import USD, CollateralToken
3
4
  from prediction_market_agent_tooling.markets.agent_market import (
4
5
  AgentMarket,
5
6
  FilterBy,
6
7
  MarketFees,
7
8
  SortBy,
8
9
  )
9
- from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency
10
10
  from prediction_market_agent_tooling.markets.polymarket.api import (
11
11
  get_polymarket_binary_markets,
12
12
  )
@@ -24,7 +24,6 @@ class PolymarketAgentMarket(AgentMarket):
24
24
  Polymarket's market class that can be used by agents to make predictions.
25
25
  """
26
26
 
27
- currency: t.ClassVar[Currency] = Currency.USDC
28
27
  base_url: t.ClassVar[str] = POLYMARKET_BASE_URL
29
28
 
30
29
  # Based on https://docs.polymarket.com/#fees, there are currently no fees, except for transactions fees.
@@ -49,11 +48,10 @@ class PolymarketAgentMarket(AgentMarket):
49
48
  outcome_token_pool=None,
50
49
  )
51
50
 
52
- @classmethod
53
- def get_tiny_bet_amount(cls) -> BetAmount:
51
+ def get_tiny_bet_amount(self) -> CollateralToken:
54
52
  raise NotImplementedError("TODO: Implement to allow betting on Polymarket.")
55
53
 
56
- def place_bet(self, outcome: bool, amount: BetAmount) -> str:
54
+ def place_bet(self, outcome: bool, amount: USD) -> str:
57
55
  raise NotImplementedError("TODO: Implement to allow betting on Polymarket.")
58
56
 
59
57
  @staticmethod
@@ -5,20 +5,47 @@ from urllib.parse import urljoin
5
5
 
6
6
  from pydantic import BaseModel, ConfigDict, Field
7
7
  from web3 import Web3
8
+ from web3.constants import ADDRESS_ZERO
8
9
 
9
10
  from prediction_market_agent_tooling.config import RPCConfig
10
11
  from prediction_market_agent_tooling.gtypes import (
11
12
  ChecksumAddress,
13
+ CollateralToken,
12
14
  HexAddress,
13
15
  HexBytes,
16
+ OutcomeStr,
17
+ Probability,
18
+ Web3Wei,
14
19
  )
20
+ from prediction_market_agent_tooling.loggers import logger
15
21
  from prediction_market_agent_tooling.markets.data_models import Resolution
16
- from prediction_market_agent_tooling.markets.seer.subgraph_data_models import (
17
- SeerParentMarket,
18
- )
22
+ from prediction_market_agent_tooling.tools.cow.cow_manager import CowManager
19
23
  from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
20
24
 
21
25
 
26
+ class CreateCategoricalMarketsParams(BaseModel):
27
+ model_config = ConfigDict(populate_by_name=True)
28
+
29
+ market_name: str = Field(..., alias="marketName")
30
+ outcomes: t.Sequence[OutcomeStr]
31
+ # Only relevant for scalar markets
32
+ question_start: str = Field(alias="questionStart", default="")
33
+ question_end: str = Field(alias="questionEnd", default="")
34
+ outcome_type: str = Field(alias="outcomeType", default="")
35
+
36
+ # Not needed for non-conditional markets.
37
+ parent_outcome: int = Field(alias="parentOutcome", default=0)
38
+ parent_market: HexAddress = Field(alias="parentMarket", default=ADDRESS_ZERO)
39
+
40
+ category: str
41
+ lang: str
42
+ lower_bound: int = Field(alias="lowerBound", default=0)
43
+ upper_bound: int = Field(alias="upperBound", default=0)
44
+ min_bond: Web3Wei = Field(..., alias="minBond")
45
+ opening_time: int = Field(..., alias="openingTime")
46
+ token_names: list[str] = Field(..., alias="tokenNames")
47
+
48
+
22
49
  class SeerOutcomeEnum(str, Enum):
23
50
  YES = "yes"
24
51
  NO = "no"
@@ -57,6 +84,10 @@ class SeerOutcomeEnum(str, Enum):
57
84
  raise ValueError(f"Unknown outcome: {self}")
58
85
 
59
86
 
87
+ class SeerParentMarket(BaseModel):
88
+ id: HexBytes
89
+
90
+
60
91
  SEER_BASE_URL = "https://app.seer.pm"
61
92
 
62
93
 
@@ -66,7 +97,7 @@ class SeerMarket(BaseModel):
66
97
  id: HexBytes
67
98
  creator: HexAddress
68
99
  title: str = Field(alias="marketName")
69
- outcomes: list[str]
100
+ outcomes: t.Sequence[OutcomeStr]
70
101
  wrapped_tokens: list[HexAddress] = Field(alias="wrappedTokens")
71
102
  parent_outcome: int = Field(alias="parentOutcome")
72
103
  parent_market: t.Optional[SeerParentMarket] = Field(
@@ -79,7 +110,6 @@ class SeerMarket(BaseModel):
79
110
  has_answers: bool | None = Field(alias="hasAnswers")
80
111
  payout_reported: bool = Field(alias="payoutReported")
81
112
  payout_numerators: list[int] = Field(alias="payoutNumerators")
82
- outcomes_supply: int = Field(alias="outcomesSupply")
83
113
 
84
114
  @property
85
115
  def has_valid_answer(self) -> bool:
@@ -156,7 +186,64 @@ class SeerMarket(BaseModel):
156
186
  def created_time(self) -> DatetimeUTC:
157
187
  return DatetimeUTC.to_datetime_utc(self.block_timestamp)
158
188
 
189
+ @property
190
+ def current_p_yes(self) -> Probability:
191
+ price_data = {}
192
+ for idx in range(len(self.outcomes)):
193
+ wrapped_token = self.wrapped_tokens[idx]
194
+ price = self._get_price_for_token(
195
+ token=Web3.to_checksum_address(wrapped_token)
196
+ )
197
+ price_data[idx] = price
198
+
199
+ if sum(price_data.values()) == 0:
200
+ logger.warning(
201
+ f"Could not get p_yes for market {self.id.hex()}, all price quotes are 0."
202
+ )
203
+ return Probability(0)
204
+
205
+ yes_idx = self.outcome_as_enums[SeerOutcomeEnum.YES]
206
+ price_yes = price_data[yes_idx] / sum(price_data.values())
207
+ return Probability(price_yes)
208
+
209
+ def _get_price_for_token(self, token: ChecksumAddress) -> float:
210
+ collateral_exchange_amount = CollateralToken(1).as_wei
211
+ try:
212
+ quote = CowManager().get_quote(
213
+ collateral_token=self.collateral_token_contract_address_checksummed,
214
+ buy_token=token,
215
+ sell_amount=collateral_exchange_amount,
216
+ )
217
+ except Exception as e:
218
+ logger.warning(f"Could not get quote for {token=}, returning price 0. {e=}")
219
+ return 0
220
+
221
+ return collateral_exchange_amount.value / float(quote.quote.buyAmount.root)
222
+
159
223
  @property
160
224
  def url(self) -> str:
161
225
  chain_id = RPCConfig().chain_id
162
226
  return urljoin(SEER_BASE_URL, f"markets/{chain_id}/{self.id.hex()}")
227
+
228
+
229
+ class SeerToken(BaseModel):
230
+ id: HexBytes
231
+ name: str
232
+ symbol: str
233
+
234
+
235
+ class SeerPool(BaseModel):
236
+ model_config = ConfigDict(populate_by_name=True)
237
+ id: HexBytes
238
+ liquidity: int
239
+ token0: SeerToken
240
+ token1: SeerToken
241
+
242
+
243
+ class NewMarketEvent(BaseModel):
244
+ market: HexAddress
245
+ marketName: str
246
+ parentMarket: HexAddress
247
+ conditionId: HexBytes
248
+ questionId: HexBytes
249
+ questionsIds: list[HexBytes]