prediction-market-agent-tooling 0.48.4__tar.gz → 0.48.6__tar.gz

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 (84) hide show
  1. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/PKG-INFO +1 -1
  2. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/benchmark/agents.py +0 -2
  3. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/benchmark/utils.py +5 -3
  4. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/deploy/agent.py +35 -27
  5. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/deploy/agent_example.py +5 -6
  6. prediction_market_agent_tooling-0.48.6/prediction_market_agent_tooling/deploy/betting_strategy.py +62 -0
  7. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/data_models.py +40 -3
  8. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/omen/data_models.py +43 -3
  9. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/monitor_app.py +9 -8
  10. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +6 -13
  11. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/is_predictable.py +1 -1
  12. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py +1 -3
  13. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/web3_utils.py +5 -10
  14. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/pyproject.toml +1 -1
  15. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/LICENSE +0 -0
  16. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/README.md +0 -0
  17. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
  18. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
  19. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
  20. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
  21. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
  22. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
  23. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
  24. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
  25. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
  26. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
  27. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
  28. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
  29. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
  30. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
  31. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/config.py +0 -0
  32. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/deploy/constants.py +0 -0
  33. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
  34. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
  35. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
  36. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/gtypes.py +0 -0
  37. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/loggers.py +0 -0
  38. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/agent_market.py +0 -0
  39. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/categorize.py +0 -0
  40. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/manifold/__init__.py +0 -0
  41. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
  42. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
  43. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/manifold/manifold.py +0 -0
  44. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
  45. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/markets.py +0 -0
  46. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
  47. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/metaculus/data_models.py +0 -0
  48. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +0 -0
  49. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
  50. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/omen/omen.py +0 -0
  51. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +0 -0
  52. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +0 -0
  53. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +0 -0
  54. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
  55. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
  56. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
  57. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
  58. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
  59. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
  60. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
  61. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
  62. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
  63. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/monitor.py +0 -0
  64. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
  65. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/py.typed +0 -0
  66. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/balances.py +0 -0
  67. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -0
  68. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
  69. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
  70. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/cache.py +0 -0
  71. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/contract.py +0 -0
  72. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/costs.py +0 -0
  73. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -0
  74. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/google.py +0 -0
  75. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
  76. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
  77. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
  78. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
  79. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
  80. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/safe.py +0 -0
  81. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/singleton.py +0 -0
  82. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
  83. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +0 -0
  84. {prediction_market_agent_tooling-0.48.4 → prediction_market_agent_tooling-0.48.6}/prediction_market_agent_tooling/tools/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.48.4
3
+ Version: 0.48.6
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -87,7 +87,6 @@ class RandomAgent(AbstractBenchmarkedAgent):
87
87
  p_yes, confidence = random.random(), random.random()
88
88
  return Prediction(
89
89
  outcome_prediction=OutcomePrediction(
90
- decision=p_yes > 0.5,
91
90
  p_yes=Probability(p_yes),
92
91
  confidence=confidence,
93
92
  info_utility=None,
@@ -111,7 +110,6 @@ class FixedAgent(AbstractBenchmarkedAgent):
111
110
  p_yes, confidence = 1.0 if self.fixed_answer else 0.0, 1.0
112
111
  return Prediction(
113
112
  outcome_prediction=OutcomePrediction(
114
- decision=self.fixed_answer,
115
113
  p_yes=Probability(p_yes),
116
114
  confidence=confidence,
117
115
  info_utility=None,
@@ -3,11 +3,13 @@ import typing as t
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
6
- from prediction_market_agent_tooling.deploy.agent import Answer
7
- from prediction_market_agent_tooling.markets.data_models import Resolution
6
+ from prediction_market_agent_tooling.markets.data_models import (
7
+ ProbabilisticAnswer,
8
+ Resolution,
9
+ )
8
10
 
9
11
 
10
- class OutcomePrediction(Answer):
12
+ class OutcomePrediction(ProbabilisticAnswer):
11
13
  info_utility: t.Optional[float]
12
14
 
13
15
  @property
@@ -12,6 +12,10 @@ from pydantic import BaseModel, BeforeValidator, computed_field
12
12
  from typing_extensions import Annotated
13
13
 
14
14
  from prediction_market_agent_tooling.config import APIKeys
15
+ from prediction_market_agent_tooling.deploy.betting_strategy import (
16
+ BettingStrategy,
17
+ MaxAccuracyBettingStrategy,
18
+ )
15
19
  from prediction_market_agent_tooling.deploy.constants import (
16
20
  MARKET_TYPE_KEY,
17
21
  REPOSITORY_KEY,
@@ -25,14 +29,19 @@ from prediction_market_agent_tooling.deploy.gcp.utils import (
25
29
  gcp_function_is_active,
26
30
  gcp_resolve_api_keys_secrets,
27
31
  )
28
- from prediction_market_agent_tooling.gtypes import Probability, xDai, xdai_type
32
+ from prediction_market_agent_tooling.gtypes import xDai, xdai_type
29
33
  from prediction_market_agent_tooling.loggers import logger
30
34
  from prediction_market_agent_tooling.markets.agent_market import (
31
35
  AgentMarket,
32
36
  FilterBy,
33
37
  SortBy,
34
38
  )
35
- from prediction_market_agent_tooling.markets.data_models import BetAmount
39
+ from prediction_market_agent_tooling.markets.data_models import (
40
+ BetAmount,
41
+ ProbabilisticAnswer,
42
+ TokenAmount,
43
+ TokenAmountAndDirection,
44
+ )
36
45
  from prediction_market_agent_tooling.markets.markets import (
37
46
  MarketType,
38
47
  have_bet_on_market_since,
@@ -95,19 +104,8 @@ class OutOfFundsError(ValueError):
95
104
  pass
96
105
 
97
106
 
98
- class Answer(BaseModel):
99
- decision: Decision # Warning: p_yes > 0.5 doesn't necessarily mean decision is True! For example, if our p_yes is 55%, but market's p_yes is 80%, then it might be profitable to bet on False.
100
- p_yes: Probability
101
- confidence: float
102
- reasoning: str | None = None
103
-
104
- @property
105
- def p_no(self) -> Probability:
106
- return Probability(1 - self.p_yes)
107
-
108
-
109
107
  class ProcessedMarket(BaseModel):
110
- answer: Answer
108
+ answer: ProbabilisticAnswer
111
109
  amount: BetAmount
112
110
 
113
111
 
@@ -280,6 +278,7 @@ class DeployableTraderAgent(DeployableAgent):
280
278
  bet_on_n_markets_per_run: int = 1
281
279
  min_required_balance_to_operate: xDai | None = xdai_type(1)
282
280
  min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
281
+ strategy: BettingStrategy = MaxAccuracyBettingStrategy()
283
282
 
284
283
  def __init__(
285
284
  self,
@@ -295,7 +294,7 @@ class DeployableTraderAgent(DeployableAgent):
295
294
  self.have_bet_on_market_since = observe()(self.have_bet_on_market_since) # type: ignore[method-assign]
296
295
  self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
297
296
  self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
298
- self.calculate_bet_amount = observe()(self.calculate_bet_amount) # type: ignore[method-assign]
297
+ self.calculate_bet_amount_and_direction = observe()(self.calculate_bet_amount_and_direction) # type: ignore[method-assign]
299
298
  self.process_market = observe()(self.process_market) # type: ignore[method-assign]
300
299
 
301
300
  def update_langfuse_trace_by_market(
@@ -311,6 +310,18 @@ class DeployableTraderAgent(DeployableAgent):
311
310
  },
312
311
  )
313
312
 
313
+ def calculate_bet_amount_and_direction(
314
+ self, answer: ProbabilisticAnswer, market: AgentMarket
315
+ ) -> TokenAmountAndDirection:
316
+ amount_and_direction = self.strategy.calculate_bet_amount_and_direction(
317
+ answer, market
318
+ )
319
+ if amount_and_direction.currency != market.currency:
320
+ raise ValueError(
321
+ f"Currency mismatch. Strategy yields {amount_and_direction.currency}, market has currency {market.currency}"
322
+ )
323
+ return amount_and_direction
324
+
314
325
  def update_langfuse_trace_by_processed_market(
315
326
  self, market_type: MarketType, processed_market: ProcessedMarket | None
316
327
  ) -> None:
@@ -360,18 +371,12 @@ class DeployableTraderAgent(DeployableAgent):
360
371
 
361
372
  return True
362
373
 
363
- def answer_binary_market(self, market: AgentMarket) -> Answer | None:
374
+ def answer_binary_market(self, market: AgentMarket) -> ProbabilisticAnswer | None:
364
375
  """
365
376
  Answer the binary market. This method must be implemented by the subclass.
366
377
  """
367
378
  raise NotImplementedError("This method must be implemented by the subclass")
368
379
 
369
- def calculate_bet_amount(self, answer: Answer, market: AgentMarket) -> BetAmount:
370
- """
371
- Calculate the bet amount. By default, it returns the minimum bet amount.
372
- """
373
- return market.get_tiny_bet_amount()
374
-
375
380
  def get_markets(
376
381
  self,
377
382
  market_type: MarketType,
@@ -408,22 +413,25 @@ class DeployableTraderAgent(DeployableAgent):
408
413
  self.update_langfuse_trace_by_processed_market(market_type, None)
409
414
  return None
410
415
 
411
- amount = self.calculate_bet_amount(answer, market)
416
+ amount_and_direction = self.calculate_bet_amount_and_direction(answer, market)
412
417
 
413
418
  if self.place_bet:
414
419
  logger.info(
415
- f"Placing bet on {market} with result {answer} and amount {amount}"
420
+ f"Placing bet on {market} with direction {amount_and_direction.direction} and amount {amount_and_direction.amount}"
416
421
  )
417
422
  market.place_bet(
418
- amount=amount,
419
- outcome=answer.decision,
423
+ amount=TokenAmount(
424
+ amount=amount_and_direction.amount,
425
+ currency=amount_and_direction.currency,
426
+ ),
427
+ outcome=amount_and_direction.direction,
420
428
  )
421
429
 
422
430
  self.after_process_market(market_type, market)
423
431
 
424
432
  processed_market = ProcessedMarket(
425
433
  answer=answer,
426
- amount=amount,
434
+ amount=amount_and_direction,
427
435
  )
428
436
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
429
437
 
@@ -1,10 +1,10 @@
1
1
  import random
2
2
 
3
3
  from prediction_market_agent_tooling.deploy.agent import (
4
- Answer,
5
4
  DeployableTraderAgent,
6
- Probability,
5
+ ProbabilisticAnswer,
7
6
  )
7
+ from prediction_market_agent_tooling.gtypes import Probability
8
8
  from prediction_market_agent_tooling.markets.agent_market import AgentMarket
9
9
  from prediction_market_agent_tooling.markets.markets import MarketType
10
10
 
@@ -13,10 +13,9 @@ class DeployableCoinFlipAgent(DeployableTraderAgent):
13
13
  def verify_market(self, market_type: MarketType, market: AgentMarket) -> bool:
14
14
  return True
15
15
 
16
- def answer_binary_market(self, market: AgentMarket) -> Answer | None:
16
+ def answer_binary_market(self, market: AgentMarket) -> ProbabilisticAnswer | None:
17
17
  decision = random.choice([True, False])
18
- return Answer(
19
- decision=decision,
18
+ return ProbabilisticAnswer(
20
19
  p_yes=Probability(float(decision)),
21
20
  confidence=0.5,
22
21
  reasoning="I flipped a coin to decide.",
@@ -24,5 +23,5 @@ class DeployableCoinFlipAgent(DeployableTraderAgent):
24
23
 
25
24
 
26
25
  class DeployableAlwaysRaiseAgent(DeployableTraderAgent):
27
- def answer_binary_market(self, market: AgentMarket) -> Answer | None:
26
+ def answer_binary_market(self, market: AgentMarket) -> ProbabilisticAnswer | None:
28
27
  raise RuntimeError("I always raise!")
@@ -0,0 +1,62 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from prediction_market_agent_tooling.markets.agent_market import AgentMarket
4
+ from prediction_market_agent_tooling.markets.data_models import (
5
+ ProbabilisticAnswer,
6
+ TokenAmountAndDirection,
7
+ )
8
+ from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
9
+ get_kelly_bet,
10
+ )
11
+
12
+
13
+ class BettingStrategy(ABC):
14
+ @abstractmethod
15
+ def calculate_bet_amount_and_direction(
16
+ self, answer: ProbabilisticAnswer, market: AgentMarket
17
+ ) -> TokenAmountAndDirection:
18
+ pass
19
+
20
+
21
+ class MaxAccuracyBettingStrategy(BettingStrategy):
22
+ def __init__(self, bet_amount: float | None = None):
23
+ self.bet_amount = bet_amount
24
+
25
+ @staticmethod
26
+ def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:
27
+ # If estimate_p_yes >= market.current_p_yes, then bet TRUE, else bet FALSE.
28
+ # This is equivalent to saying EXPECTED_VALUE = (estimate_p_yes * num_tokens_obtained_by_betting_yes) -
29
+ # ((1 - estimate_p_yes) * num_tokens_obtained_by_betting_no) >= 0
30
+ return estimate_p_yes >= market_p_yes
31
+
32
+ def calculate_bet_amount_and_direction(
33
+ self, answer: ProbabilisticAnswer, market: AgentMarket
34
+ ) -> TokenAmountAndDirection:
35
+ bet_amount = (
36
+ market.get_tiny_bet_amount().amount
37
+ if self.bet_amount is None
38
+ else self.bet_amount
39
+ )
40
+ direction = self.calculate_direction(market.current_p_yes, answer.p_yes)
41
+ return TokenAmountAndDirection(
42
+ amount=bet_amount,
43
+ currency=market.currency,
44
+ direction=direction,
45
+ )
46
+
47
+
48
+ class KellyBettingStrategy(BettingStrategy):
49
+ def __init__(self, max_bet_amount: float = 10):
50
+ self.max_bet_amount = max_bet_amount
51
+
52
+ def calculate_bet_amount_and_direction(
53
+ self, answer: ProbabilisticAnswer, market: AgentMarket
54
+ ) -> TokenAmountAndDirection:
55
+ kelly_bet = get_kelly_bet(
56
+ self.max_bet_amount, market.current_p_yes, answer.p_yes, answer.confidence
57
+ )
58
+ return TokenAmountAndDirection(
59
+ amount=kelly_bet.size,
60
+ currency=market.currency,
61
+ direction=kelly_bet.direction,
62
+ )
@@ -1,10 +1,10 @@
1
1
  from datetime import datetime
2
2
  from enum import Enum
3
- from typing import TypeAlias
3
+ from typing import Annotated, TypeAlias
4
4
 
5
- from pydantic import BaseModel, computed_field
5
+ from pydantic import BaseModel, BeforeValidator, computed_field
6
6
 
7
- from prediction_market_agent_tooling.gtypes import OutcomeStr
7
+ from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
8
8
 
9
9
 
10
10
  class Currency(str, Enum):
@@ -57,6 +57,43 @@ class ResolvedBet(Bet):
57
57
  return f"Resolved bet for market {self.market_id} for question {self.market_question} created at {self.created_time}: {self.amount} on {self.outcome}. Bet was resolved at {self.resolved_time} and was {'correct' if self.is_correct else 'incorrect'}. Profit was {self.profit}"
58
58
 
59
59
 
60
+ class TokenAmountAndDirection(TokenAmount):
61
+ direction: bool
62
+
63
+
64
+ def to_boolean_outcome(value: str | bool) -> bool:
65
+ if isinstance(value, bool):
66
+ return value
67
+
68
+ elif isinstance(value, str):
69
+ value = value.lower().strip()
70
+
71
+ if value in {"true", "yes", "y", "1"}:
72
+ return True
73
+
74
+ elif value in {"false", "no", "n", "0"}:
75
+ return False
76
+
77
+ else:
78
+ raise ValueError(f"Expected a boolean string, but got {value}")
79
+
80
+ else:
81
+ raise ValueError(f"Expected a boolean or a string, but got {value}")
82
+
83
+
84
+ Decision = Annotated[bool, BeforeValidator(to_boolean_outcome)]
85
+
86
+
87
+ class ProbabilisticAnswer(BaseModel):
88
+ p_yes: Probability
89
+ confidence: float
90
+ reasoning: str | None = None
91
+
92
+ @property
93
+ def p_no(self) -> Probability:
94
+ return Probability(1 - self.p_yes)
95
+
96
+
60
97
  class Position(BaseModel):
61
98
  market_id: str
62
99
  amounts: dict[OutcomeStr, TokenAmount]
@@ -77,6 +77,10 @@ class Question(BaseModel):
77
77
  def opening_datetime(self) -> datetime:
78
78
  return datetime.fromtimestamp(self.openingTimestamp)
79
79
 
80
+ @property
81
+ def has_answer(self) -> bool:
82
+ return self.currentAnswer is not None
83
+
80
84
  @property
81
85
  def outcome_index(self) -> int | None:
82
86
  return (
@@ -88,6 +92,34 @@ class Question(BaseModel):
88
92
  else None
89
93
  )
90
94
 
95
+ @property
96
+ def is_binary(self) -> bool:
97
+ return len(self.outcomes) == 2
98
+
99
+ @property
100
+ def has_valid_answer(self) -> bool:
101
+ return self.outcome_index is not None and self.outcome_index != INVALID_ANSWER
102
+
103
+ @property
104
+ def boolean_outcome(self) -> bool:
105
+ if not self.is_binary:
106
+ raise ValueError(
107
+ f"Question with title {self.title} is not binary, it has {len(self.outcomes)} outcomes."
108
+ )
109
+
110
+ if not self.has_answer:
111
+ raise ValueError(f"Question with title {self.title} is not answered.")
112
+
113
+ outcome_index = check_not_none(self.outcome_index)
114
+
115
+ if not self.has_valid_answer:
116
+ raise ValueError(
117
+ f"Question with title {self.title} has invalid answer {outcome_index}."
118
+ )
119
+
120
+ outcome: str = self.outcomes[outcome_index]
121
+ return get_boolean_outcome(outcome)
122
+
91
123
 
92
124
  class OmenPosition(BaseModel):
93
125
  id: HexBytes
@@ -132,7 +164,15 @@ class OmenUserPosition(BaseModel):
132
164
 
133
165
  class OmenMarket(BaseModel):
134
166
  """
135
- https://aiomen.eth.limo
167
+ https://presagio.pages.dev
168
+
169
+ An Omen market goes through the following stages:
170
+
171
+ 1. creation - can add liquidty immediately, and trade immediately if there is liquidity
172
+ 2. closing - market is closed, and a question is simultaneously opened for answers on Reality
173
+ 3. finalizing - the question is finalized on reality (including any disputes)
174
+ 4. resolving - a manual step required by calling the Omen oracle contract
175
+ 5. redeeming - a user withdraws collateral tokens from the market
136
176
  """
137
177
 
138
178
  BET_AMOUNT_CURRENCY: t.ClassVar[Currency] = Currency.xDai
@@ -347,7 +387,7 @@ class OmenBet(BaseModel):
347
387
  collateralAmountUSD: USD
348
388
  feeAmount: Wei
349
389
  outcomeIndex: int
350
- outcomeTokensTraded: int
390
+ outcomeTokensTraded: Wei
351
391
  transactionHash: HexAddress
352
392
  fpmm: OmenMarket
353
393
 
@@ -372,7 +412,7 @@ class OmenBet(BaseModel):
372
412
  def get_profit(self) -> ProfitAmount:
373
413
  bet_amount_xdai = wei_to_xdai(self.collateralAmount)
374
414
  profit = (
375
- wei_to_xdai(Wei(self.outcomeTokensTraded)) - bet_amount_xdai
415
+ wei_to_xdai(self.outcomeTokensTraded) - bet_amount_xdai
376
416
  if self.boolean_outcome == self.fpmm.boolean_outcome
377
417
  else -bet_amount_xdai
378
418
  )
@@ -135,15 +135,16 @@ def monitor_app(
135
135
  )
136
136
 
137
137
  st.header("Market Info")
138
- with st.spinner("Loading markets"):
139
- open_markets, resolved_markets = get_open_and_resolved_markets(
140
- start_time=oldest_start_time, market_type=market_type
138
+ if st.checkbox("Show Market Info"):
139
+ with st.spinner("Loading markets"):
140
+ open_markets, resolved_markets = get_open_and_resolved_markets(
141
+ start_time=oldest_start_time, market_type=market_type
142
+ )
143
+ (
144
+ monitor_market(open_markets=open_markets, resolved_markets=resolved_markets)
145
+ if open_markets and resolved_markets
146
+ else st.warning("No market data found.")
141
147
  )
142
- (
143
- monitor_market(open_markets=open_markets, resolved_markets=resolved_markets)
144
- if open_markets and resolved_markets
145
- else st.warning("No market data found.")
146
- )
147
148
 
148
149
  st.header("Agent Info")
149
150
  if st.button("Export agents"):
@@ -1,23 +1,16 @@
1
- from enum import Enum
2
-
3
1
  from pydantic import BaseModel
4
2
 
5
3
 
6
- class BetDirection(str, Enum):
7
- YES = "Yes"
8
- NO = "No"
4
+ def check_is_valid_probability(probability: float) -> None:
5
+ if not 0 <= probability <= 1:
6
+ raise ValueError("Probability must be between 0 and 1")
9
7
 
10
8
 
11
9
  class KellyBet(BaseModel):
12
- direction: BetDirection
10
+ direction: bool
13
11
  size: float
14
12
 
15
13
 
16
- def check_is_valid_probability(probability: float) -> None:
17
- if not 0 <= probability <= 1:
18
- raise ValueError("Probability must be between 0 and 1")
19
-
20
-
21
14
  def get_kelly_bet(
22
15
  max_bet: float,
23
16
  market_p_yes: float,
@@ -47,10 +40,10 @@ def get_kelly_bet(
47
40
  check_is_valid_probability(confidence)
48
41
 
49
42
  if estimated_p_yes > market_p_yes:
50
- bet_direction = BetDirection.YES
43
+ bet_direction = True
51
44
  market_prob = market_p_yes
52
45
  else:
53
- bet_direction = BetDirection.NO
46
+ bet_direction = False
54
47
  market_prob = 1 - market_p_yes
55
48
 
56
49
  # Handle the case where market_prob is 0
@@ -143,7 +143,7 @@ def is_predictable_without_description(
143
143
  description=description,
144
144
  )
145
145
  completion = str(
146
- llm(
146
+ llm.invoke(
147
147
  messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
148
148
  ).content
149
149
  )
@@ -60,9 +60,7 @@ def tavily_search(
60
60
  return response_parsed
61
61
 
62
62
 
63
- @tenacity.retry(
64
- stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1), reraise=True
65
- )
63
+ @tenacity.retry(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1))
66
64
  def _tavily_search(
67
65
  query: str,
68
66
  search_depth: t.Literal["basic", "advanced"],
@@ -28,6 +28,7 @@ from prediction_market_agent_tooling.loggers import logger
28
28
  ONE_NONCE = Nonce(1)
29
29
  ONE_XDAI = xdai_type(1)
30
30
  ZERO_BYTES = HexBytes(HASH_ZERO)
31
+ NOT_REVERTED_ICASE_REGEX_PATTERN = "(?i)(?!.*reverted.*)"
31
32
 
32
33
 
33
34
  def private_key_to_public_key(private_key: SecretStr) -> ChecksumAddress:
@@ -154,11 +155,8 @@ def _prepare_tx_params(
154
155
 
155
156
 
156
157
  @tenacity.retry(
157
- # Retry only for the transaction errors that match the given patterns,
158
- # add other retrieable errors gradually to be safe.
159
- retry=tenacity.retry_if_exception_message(
160
- match="(.*wrong transaction nonce.*)|(.*Invalid.*)|(.*OldNonce.*)"
161
- ),
158
+ # Don't retry on `reverted` messages, as they would always fail again.
159
+ retry=tenacity.retry_if_exception_message(match=NOT_REVERTED_ICASE_REGEX_PATTERN),
162
160
  wait=tenacity.wait_chain(*[tenacity.wait_fixed(n) for n in range(1, 10)]),
163
161
  stop=tenacity.stop_after_attempt(9),
164
162
  after=lambda x: logger.debug(
@@ -194,11 +192,8 @@ def send_function_on_contract_tx(
194
192
 
195
193
 
196
194
  @tenacity.retry(
197
- # Retry only for the transaction errors that match the given patterns,
198
- # add other retrieable errors gradually to be safe.
199
- retry=tenacity.retry_if_exception_message(
200
- match="(.*wrong transaction nonce.*)|(.*Invalid.*)|(.*OldNonce.*)"
201
- ),
195
+ # Don't retry on `reverted` messages, as they would always fail again.
196
+ retry=tenacity.retry_if_exception_message(match=NOT_REVERTED_ICASE_REGEX_PATTERN),
202
197
  wait=tenacity.wait_chain(*[tenacity.wait_fixed(n) for n in range(1, 10)]),
203
198
  stop=tenacity.stop_after_attempt(9),
204
199
  after=lambda x: logger.debug(
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "prediction-market-agent-tooling"
3
- version = "0.48.4"
3
+ version = "0.48.6"
4
4
  description = "Tools to benchmark, deploy and monitor prediction market agents."
5
5
  authors = ["Gnosis"]
6
6
  readme = "README.md"