prediction-market-agent-tooling 0.48.14__tar.gz → 0.48.15__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 (92) hide show
  1. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/PKG-INFO +1 -1
  2. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/agent.py +9 -0
  3. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/betting_strategy.py +23 -6
  4. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/gtypes.py +1 -1
  5. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/jobs/jobs.py +45 -0
  6. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/jobs/jobs_models.py +53 -0
  7. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/jobs/omen/omen_jobs.py +113 -0
  8. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/omen/data_models.py +3 -0
  9. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/omen/omen.py +90 -4
  10. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +17 -8
  11. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/monitor.py +5 -1
  12. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/py.typed +0 -0
  13. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +142 -0
  14. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +132 -0
  15. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/tools/betting_strategies/utils.py +6 -0
  16. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/is_predictable.py +3 -0
  17. prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/tools/langfuse_client_utils.py +159 -0
  18. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/pyproject.toml +1 -1
  19. prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +0 -60
  20. prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -116
  21. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/LICENSE +0 -0
  22. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/README.md +0 -0
  23. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
  24. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
  25. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
  26. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
  27. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
  28. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
  29. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
  30. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
  31. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
  32. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
  33. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
  34. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
  35. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
  36. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/benchmark/agents.py +0 -0
  37. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
  38. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/benchmark/utils.py +0 -0
  39. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/config.py +0 -0
  40. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/agent_example.py +0 -0
  41. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/constants.py +0 -0
  42. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
  43. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
  44. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
  45. {prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/markets/manifold → prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/jobs}/__init__.py +0 -0
  46. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/loggers.py +0 -0
  47. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/agent_market.py +0 -0
  48. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/categorize.py +0 -0
  49. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/data_models.py +0 -0
  50. {prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/markets/omen → prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/markets/manifold}/__init__.py +0 -0
  51. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
  52. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
  53. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/manifold/manifold.py +0 -0
  54. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
  55. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/markets.py +0 -0
  56. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
  57. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/metaculus/data_models.py +0 -0
  58. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +0 -0
  59. /prediction_market_agent_tooling-0.48.14/prediction_market_agent_tooling/py.typed → /prediction_market_agent_tooling-0.48.15/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
  60. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +0 -0
  61. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +0 -0
  62. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
  63. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
  64. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
  65. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
  66. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
  67. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
  68. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
  69. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
  70. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
  71. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/monitor_app.py +0 -0
  72. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
  73. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/balances.py +0 -0
  74. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
  75. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
  76. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/cache.py +0 -0
  77. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/contract.py +0 -0
  78. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/costs.py +0 -0
  79. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -0
  80. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/google.py +0 -0
  81. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
  82. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
  83. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
  84. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
  85. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
  86. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/safe.py +0 -0
  87. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/singleton.py +0 -0
  88. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
  89. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +0 -0
  90. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py +0 -0
  91. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/utils.py +0 -0
  92. {prediction_market_agent_tooling-0.48.14 → prediction_market_agent_tooling-0.48.15}/prediction_market_agent_tooling/tools/web3_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.14
3
+ Version: 0.48.15
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -426,6 +426,8 @@ class DeployableTraderAgent(DeployableAgent):
426
426
  market: AgentMarket,
427
427
  verify_market: bool = True,
428
428
  ) -> ProcessedMarket | None:
429
+ logger.info(f"Processing market {market.question=} from {market.url=}.")
430
+
429
431
  self.before_process_market(market_type, market)
430
432
 
431
433
  if verify_market and not self.verify_market(market_type, market):
@@ -462,6 +464,7 @@ class DeployableTraderAgent(DeployableAgent):
462
464
  processed_market = ProcessedMarket(answer=answer, trades=trades)
463
465
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
464
466
 
467
+ logger.info(f"Processed market {market.question=} from {market.url=}.")
465
468
  return processed_market
466
469
 
467
470
  def after_process_market(
@@ -495,7 +498,11 @@ class DeployableTraderAgent(DeployableAgent):
495
498
  """
496
499
  Processes bets placed by agents on a given market.
497
500
  """
501
+ logger.info("Start processing of markets.")
498
502
  available_markets = self.get_markets(market_type)
503
+ logger.info(
504
+ f"Fetched {len(available_markets)=} markets to process, going to process {self.bet_on_n_markets_per_run=}."
505
+ )
499
506
  processed = 0
500
507
 
501
508
  for market in available_markets:
@@ -510,6 +517,8 @@ class DeployableTraderAgent(DeployableAgent):
510
517
  if processed == self.bet_on_n_markets_per_run:
511
518
  break
512
519
 
520
+ logger.info("All markets processed.")
521
+
513
522
  def after_process_markets(self, market_type: MarketType) -> None:
514
523
  pass
515
524
 
@@ -11,8 +11,10 @@ from prediction_market_agent_tooling.markets.data_models import (
11
11
  )
12
12
  from prediction_market_agent_tooling.markets.omen.data_models import get_boolean_outcome
13
13
  from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
14
- get_kelly_bet,
14
+ get_kelly_bet_full,
15
+ get_kelly_bet_simplified,
15
16
  )
17
+ from prediction_market_agent_tooling.tools.utils import check_not_none
16
18
 
17
19
 
18
20
  class BettingStrategy(ABC):
@@ -168,11 +170,26 @@ class KellyBettingStrategy(BettingStrategy):
168
170
  market: AgentMarket,
169
171
  ) -> list[Trade]:
170
172
  adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
171
- kelly_bet = get_kelly_bet(
172
- adjusted_bet_amount,
173
- market.current_p_yes,
174
- answer.p_yes,
175
- answer.confidence,
173
+ outcome_token_pool = check_not_none(market.outcome_token_pool)
174
+ kelly_bet = (
175
+ get_kelly_bet_full(
176
+ yes_outcome_pool_size=outcome_token_pool[
177
+ market.get_outcome_str_from_bool(True)
178
+ ],
179
+ no_outcome_pool_size=outcome_token_pool[
180
+ market.get_outcome_str_from_bool(False)
181
+ ],
182
+ estimated_p_yes=answer.p_yes,
183
+ max_bet=adjusted_bet_amount,
184
+ confidence=answer.confidence,
185
+ )
186
+ if market.has_token_pool()
187
+ else get_kelly_bet_simplified(
188
+ adjusted_bet_amount,
189
+ market.current_p_yes,
190
+ answer.p_yes,
191
+ answer.confidence,
192
+ )
176
193
  )
177
194
 
178
195
  amounts = {
@@ -27,7 +27,7 @@ PrivateKey = NewType("PrivateKey", SecretStr)
27
27
  xDai = NewType("xDai", float)
28
28
  GNO = NewType("GNO", float)
29
29
  ABI = NewType("ABI", str)
30
- OmenOutcomeToken = NewType("OmenOutcomeToken", int)
30
+ OmenOutcomeToken = NewType("OmenOutcomeToken", Wei)
31
31
  OutcomeStr = NewType("OutcomeStr", str)
32
32
  Probability = NewType("Probability", float)
33
33
  Mana = NewType("Mana", float) # Manifold's "currency"
@@ -0,0 +1,45 @@
1
+ import typing as t
2
+
3
+ from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
4
+ from prediction_market_agent_tooling.jobs.omen.omen_jobs import OmenJobAgentMarket
5
+ from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
6
+ from prediction_market_agent_tooling.markets.markets import MarketType
7
+
8
+ JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET: dict[MarketType, type[JobAgentMarket]] = {
9
+ MarketType.OMEN: OmenJobAgentMarket,
10
+ }
11
+
12
+
13
+ @t.overload
14
+ def get_jobs(
15
+ market_type: t.Literal[MarketType.OMEN],
16
+ limit: int | None,
17
+ filter_by: FilterBy = FilterBy.OPEN,
18
+ sort_by: SortBy = SortBy.NONE,
19
+ ) -> t.Sequence[OmenJobAgentMarket]:
20
+ ...
21
+
22
+
23
+ @t.overload
24
+ def get_jobs(
25
+ market_type: MarketType,
26
+ limit: int | None,
27
+ filter_by: FilterBy = FilterBy.OPEN,
28
+ sort_by: SortBy = SortBy.NONE,
29
+ ) -> t.Sequence[JobAgentMarket]:
30
+ ...
31
+
32
+
33
+ def get_jobs(
34
+ market_type: MarketType,
35
+ limit: int | None,
36
+ filter_by: FilterBy = FilterBy.OPEN,
37
+ sort_by: SortBy = SortBy.NONE,
38
+ ) -> t.Sequence[JobAgentMarket]:
39
+ job_class = JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET[market_type]
40
+ markets = job_class.get_jobs(
41
+ limit=limit,
42
+ sort_by=sort_by,
43
+ filter_by=filter_by,
44
+ )
45
+ return markets
@@ -0,0 +1,53 @@
1
+ import typing as t
2
+ from abc import ABC, abstractmethod
3
+ from datetime import datetime
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from prediction_market_agent_tooling.markets.agent_market import AgentMarket
8
+ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
9
+ FilterBy,
10
+ SortBy,
11
+ )
12
+
13
+
14
+ class SimpleJob(BaseModel):
15
+ id: str
16
+ job: str
17
+ reward: float
18
+ currency: str
19
+ deadline: datetime
20
+
21
+
22
+ class JobAgentMarket(AgentMarket, ABC):
23
+ CATEGORY: t.ClassVar[str]
24
+
25
+ @property
26
+ @abstractmethod
27
+ def job(self) -> str:
28
+ """Holds description of the job that needs to be done."""
29
+
30
+ @property
31
+ @abstractmethod
32
+ def deadline(self) -> datetime:
33
+ """Deadline for the job completion."""
34
+
35
+ @abstractmethod
36
+ def get_reward(self, max_bond: float) -> float:
37
+ """Reward for completing this job."""
38
+
39
+ @abstractmethod
40
+ @classmethod
41
+ def get_jobs(
42
+ cls, limit: int | None, filter_by: FilterBy, sort_by: SortBy
43
+ ) -> t.Sequence["JobAgentMarket"]:
44
+ """Get all available jobs."""
45
+
46
+ def to_simple_job(self, max_bond: float) -> SimpleJob:
47
+ return SimpleJob(
48
+ id=self.id,
49
+ job=self.job,
50
+ reward=self.get_reward(max_bond),
51
+ currency=self.currency.value,
52
+ deadline=self.deadline,
53
+ )
@@ -0,0 +1,113 @@
1
+ import typing as t
2
+ from datetime import datetime
3
+
4
+ from web3 import Web3
5
+
6
+ from prediction_market_agent_tooling.deploy.betting_strategy import (
7
+ Currency,
8
+ KellyBettingStrategy,
9
+ ProbabilisticAnswer,
10
+ TradeType,
11
+ )
12
+ from prediction_market_agent_tooling.gtypes import Probability
13
+ from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
14
+ from prediction_market_agent_tooling.markets.omen.omen import (
15
+ BetAmount,
16
+ OmenAgentMarket,
17
+ OmenMarket,
18
+ )
19
+ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
20
+ FilterBy,
21
+ OmenSubgraphHandler,
22
+ SortBy,
23
+ )
24
+
25
+
26
+ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
27
+ CATEGORY = "jobs"
28
+
29
+ @property
30
+ def job(self) -> str:
31
+ """Omen market's have only question, so that's where the job description is."""
32
+ return self.question
33
+
34
+ @property
35
+ def deadline(self) -> datetime:
36
+ return self.close_time
37
+
38
+ def get_reward(self, max_bond: float) -> float:
39
+ return compute_job_reward(self, max_bond)
40
+
41
+ @classmethod
42
+ def get_jobs(
43
+ cls, limit: int | None, filter_by: FilterBy, sort_by: SortBy
44
+ ) -> t.Sequence["OmenJobAgentMarket"]:
45
+ markets = OmenSubgraphHandler().get_omen_binary_markets_simple(
46
+ limit=limit,
47
+ filter_by=filter_by,
48
+ sort_by=sort_by,
49
+ category=cls.CATEGORY,
50
+ )
51
+ return [OmenJobAgentMarket.from_omen_market(market) for market in markets]
52
+
53
+ @staticmethod
54
+ def from_omen_market(market: OmenMarket) -> "OmenJobAgentMarket":
55
+ return OmenJobAgentMarket.from_omen_agent_market(
56
+ OmenAgentMarket.from_data_model(market)
57
+ )
58
+
59
+ @staticmethod
60
+ def from_omen_agent_market(market: OmenAgentMarket) -> "OmenJobAgentMarket":
61
+ return OmenJobAgentMarket(
62
+ id=market.id,
63
+ question=market.question,
64
+ description=market.description,
65
+ outcomes=market.outcomes,
66
+ outcome_token_pool=market.outcome_token_pool,
67
+ resolution=market.resolution,
68
+ created_time=market.created_time,
69
+ close_time=market.close_time,
70
+ current_p_yes=market.current_p_yes,
71
+ url=market.url,
72
+ volume=market.volume,
73
+ creator=market.creator,
74
+ collateral_token_contract_address_checksummed=market.collateral_token_contract_address_checksummed,
75
+ market_maker_contract_address_checksummed=market.market_maker_contract_address_checksummed,
76
+ condition=market.condition,
77
+ finalized_time=market.finalized_time,
78
+ fee=market.fee,
79
+ )
80
+
81
+
82
+ def compute_job_reward(
83
+ market: OmenAgentMarket, max_bond: float, web3: Web3 | None = None
84
+ ) -> float:
85
+ # Because jobs are powered by prediction markets, potentional reward depends on job's liquidity and our will to bond (bet) our xDai into our job completion.
86
+ required_trades = KellyBettingStrategy(max_bet_amount=max_bond).calculate_trades(
87
+ existing_position=None,
88
+ # We assume that we finish the job and so the probability of the market happening will be 100%.
89
+ answer=ProbabilisticAnswer(p_yes=Probability(1.0), confidence=1.0),
90
+ market=market,
91
+ )
92
+
93
+ assert (
94
+ len(required_trades) == 1
95
+ ), f"Shouldn't process same job twice: {required_trades}"
96
+ trade = required_trades[0]
97
+ assert trade.trade_type == TradeType.BUY, "Should only buy on job markets."
98
+ assert trade.outcome, "Should buy only YES on job markets."
99
+ assert (
100
+ trade.amount.currency == Currency.xDai
101
+ ), "Should work only on real-money markets."
102
+
103
+ reward = (
104
+ market.get_buy_token_amount(
105
+ bet_amount=BetAmount(
106
+ amount=trade.amount.amount, currency=trade.amount.currency
107
+ ),
108
+ direction=trade.outcome,
109
+ ).amount
110
+ - trade.amount.amount
111
+ )
112
+
113
+ return reward
@@ -9,6 +9,7 @@ from prediction_market_agent_tooling.gtypes import (
9
9
  ChecksumAddress,
10
10
  HexAddress,
11
11
  HexBytes,
12
+ HexStr,
12
13
  OmenOutcomeToken,
13
14
  Probability,
14
15
  Wei,
@@ -30,8 +31,10 @@ from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
30
31
 
31
32
  OMEN_TRUE_OUTCOME = "Yes"
32
33
  OMEN_FALSE_OUTCOME = "No"
34
+ OMEN_BINARY_MARKET_OUTCOMES = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME]
33
35
  INVALID_ANSWER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
34
36
  INVALID_ANSWER_HEX_BYTES = HexBytes(INVALID_ANSWER)
37
+ INVALID_ANSWER_STR = HexStr(INVALID_ANSWER_HEX_BYTES.hex())
35
38
  OMEN_BASE_URL = "https://aiomen.eth.limo"
36
39
  PRESAGIO_BASE_URL = "https://presagio.pages.dev"
37
40
 
@@ -97,10 +97,6 @@ class OmenAgentMarket(AgentMarket):
97
97
  close_time: datetime
98
98
  fee: float # proportion, from 0 to 1
99
99
 
100
- INVALID_MARKET_ANSWER: HexStr = HexStr(
101
- "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
102
- )
103
-
104
100
  _binary_market_p_yes_history: list[Probability] | None = None
105
101
  description: str | None = (
106
102
  None # Omen markets don't have a description, so just default to None.
@@ -577,6 +573,66 @@ class OmenAgentMarket(AgentMarket):
577
573
  def get_user_url(cls, keys: APIKeys) -> str:
578
574
  return get_omen_user_url(keys.bet_from_address)
579
575
 
576
+ def get_buy_token_amount(
577
+ self, bet_amount: BetAmount, direction: bool
578
+ ) -> TokenAmount:
579
+ """
580
+ Note: this is only valid if the market instance's token pool is
581
+ up-to-date with the smart contract.
582
+ """
583
+ outcome_token_pool = check_not_none(self.outcome_token_pool)
584
+ amount = get_buy_outcome_token_amount(
585
+ investment_amount=bet_amount.amount,
586
+ buy_direction=direction,
587
+ yes_outcome_pool_size=outcome_token_pool[OMEN_TRUE_OUTCOME],
588
+ no_outcome_pool_size=outcome_token_pool[OMEN_FALSE_OUTCOME],
589
+ fee=self.fee,
590
+ )
591
+ return TokenAmount(amount=amount, currency=self.currency)
592
+
593
+ def _get_buy_token_amount_from_smart_contract(
594
+ self, bet_amount: BetAmount, direction: bool
595
+ ) -> TokenAmount:
596
+ received_token_amount_wei = Wei(
597
+ self.get_contract().calcBuyAmount(
598
+ investment_amount=xdai_to_wei(xDai(bet_amount.amount)),
599
+ outcome_index=self.get_outcome_index(
600
+ self.get_outcome_str_from_bool(direction)
601
+ ),
602
+ )
603
+ )
604
+ received_token_amount = float(wei_to_xdai(received_token_amount_wei))
605
+ return TokenAmount(amount=received_token_amount, currency=self.currency)
606
+
607
+ def get_new_p_yes(self, bet_amount: BetAmount, direction: bool) -> Probability:
608
+ """
609
+ Calculate the new p_yes based on the bet amount and direction.
610
+ """
611
+ if not self.has_token_pool():
612
+ raise ValueError("Outcome token pool is required to calculate new p_yes.")
613
+
614
+ outcome_token_pool = check_not_none(self.outcome_token_pool)
615
+ yes_outcome_pool_size = outcome_token_pool[self.get_outcome_str_from_bool(True)]
616
+ no_outcome_pool_size = outcome_token_pool[self.get_outcome_str_from_bool(False)]
617
+
618
+ new_yes_outcome_pool_size = yes_outcome_pool_size + (
619
+ bet_amount.amount * (1 - self.fee)
620
+ )
621
+ new_no_outcome_pool_size = no_outcome_pool_size + (
622
+ bet_amount.amount * (1 - self.fee)
623
+ )
624
+
625
+ received_token_amount = self.get_buy_token_amount(bet_amount, direction).amount
626
+ if direction:
627
+ new_yes_outcome_pool_size -= received_token_amount
628
+ else:
629
+ new_no_outcome_pool_size -= received_token_amount
630
+
631
+ new_p_yes = new_no_outcome_pool_size / (
632
+ new_yes_outcome_pool_size + new_no_outcome_pool_size
633
+ )
634
+ return Probability(new_p_yes)
635
+
580
636
 
581
637
  def get_omen_user_url(address: ChecksumAddress) -> str:
582
638
  return f"https://gnosisscan.io/address/{address}"
@@ -1165,3 +1221,33 @@ def withdraw_wxdai_to_xdai_to_keep_balance(
1165
1221
  logger.info(
1166
1222
  f"Withdrew {need_to_withdraw} wxDai to keep the balance above the minimum required balance {min_required_balance}."
1167
1223
  )
1224
+
1225
+
1226
+ def get_buy_outcome_token_amount(
1227
+ investment_amount: float,
1228
+ buy_direction: bool,
1229
+ yes_outcome_pool_size: float,
1230
+ no_outcome_pool_size: float,
1231
+ fee: float,
1232
+ ) -> float:
1233
+ """
1234
+ Calculates the amount of outcome tokens received for a given investment
1235
+
1236
+ Taken from https://github.com/gnosis/conditional-tokens-market-makers/blob/6814c0247c745680bb13298d4f0dd7f5b574d0db/contracts/FixedProductMarketMaker.sol#L264
1237
+ """
1238
+ investment_amount_minus_fees = investment_amount * (1 - fee)
1239
+ buy_token_pool_balance = (
1240
+ yes_outcome_pool_size if buy_direction else no_outcome_pool_size
1241
+ )
1242
+
1243
+ pool_balance = no_outcome_pool_size if buy_direction else yes_outcome_pool_size
1244
+ denominator = pool_balance + investment_amount_minus_fees
1245
+ ending_outcome_balance = buy_token_pool_balance * pool_balance / denominator
1246
+
1247
+ if ending_outcome_balance <= 0:
1248
+ raise ValueError("must have non-zero balances")
1249
+
1250
+ result = (
1251
+ buy_token_pool_balance + investment_amount_minus_fees - ending_outcome_balance
1252
+ )
1253
+ return result
@@ -19,8 +19,7 @@ from prediction_market_agent_tooling.gtypes import (
19
19
  from prediction_market_agent_tooling.loggers import logger
20
20
  from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
21
21
  from prediction_market_agent_tooling.markets.omen.data_models import (
22
- OMEN_FALSE_OUTCOME,
23
- OMEN_TRUE_OUTCOME,
22
+ OMEN_BINARY_MARKET_OUTCOMES,
24
23
  OmenBet,
25
24
  OmenMarket,
26
25
  OmenPosition,
@@ -204,7 +203,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
204
203
  self,
205
204
  creator: t.Optional[HexAddress] = None,
206
205
  creator_in: t.Optional[t.Sequence[HexAddress]] = None,
207
- outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
206
+ outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
208
207
  created_after: t.Optional[datetime] = None,
209
208
  opened_before: t.Optional[datetime] = None,
210
209
  opened_after: t.Optional[datetime] = None,
@@ -217,6 +216,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
217
216
  id_in: list[str] | None = None,
218
217
  excluded_questions: set[str] | None = None,
219
218
  collateral_token_address_in: tuple[ChecksumAddress, ...] | None = None,
219
+ category: str | None = None,
220
220
  ) -> dict[str, t.Any]:
221
221
  where_stms: dict[str, t.Any] = {
222
222
  "isPendingArbitration": False,
@@ -282,6 +282,9 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
282
282
  finalized_after
283
283
  )
284
284
 
285
+ if category:
286
+ where_stms["category"] = category
287
+
285
288
  # `excluded_question_titles` can not be an empty list, otherwise the API bugs out and returns nothing.
286
289
  excluded_question_titles = [""]
287
290
  if excluded_questions:
@@ -324,8 +327,10 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
324
327
  # Additional filters, these can not be modified by the enums above.
325
328
  created_after: datetime | None = None,
326
329
  excluded_questions: set[str] | None = None, # question titles
327
- collateral_token_address_in: tuple[ChecksumAddress, ...]
328
- | None = SAFE_COLLATERAL_TOKEN_MARKETS,
330
+ collateral_token_address_in: (
331
+ tuple[ChecksumAddress, ...] | None
332
+ ) = SAFE_COLLATERAL_TOKEN_MARKETS,
333
+ category: str | None = None,
329
334
  ) -> t.List[OmenMarket]:
330
335
  """
331
336
  Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
@@ -363,6 +368,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
363
368
  created_after=created_after,
364
369
  excluded_questions=excluded_questions,
365
370
  collateral_token_address_in=collateral_token_address_in,
371
+ category=category,
366
372
  )
367
373
 
368
374
  def get_omen_binary_markets(
@@ -383,9 +389,11 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
383
389
  excluded_questions: set[str] | None = None, # question titles
384
390
  sort_by_field: FieldPath | None = None,
385
391
  sort_direction: str | None = None,
386
- outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
387
- collateral_token_address_in: tuple[ChecksumAddress, ...]
388
- | None = SAFE_COLLATERAL_TOKEN_MARKETS,
392
+ outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
393
+ collateral_token_address_in: (
394
+ tuple[ChecksumAddress, ...] | None
395
+ ) = SAFE_COLLATERAL_TOKEN_MARKETS,
396
+ category: str | None = None,
389
397
  ) -> t.List[OmenMarket]:
390
398
  """
391
399
  Complete method to fetch Omen binary markets with various filters, use `get_omen_binary_markets_simple` for simplified version that uses FilterBy and SortBy enums.
@@ -406,6 +414,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
406
414
  excluded_questions=excluded_questions,
407
415
  liquidity_bigger_than=liquidity_bigger_than,
408
416
  collateral_token_address_in=collateral_token_address_in,
417
+ category=category,
409
418
  )
410
419
 
411
420
  # These values can not be set to `None`, but they can be omitted.
@@ -211,7 +211,11 @@ def monitor_agent(agent: DeployedAgent) -> None:
211
211
  key=f"{agent.name}_x_axis_column",
212
212
  )
213
213
 
214
- bets_df = pd.DataFrame(bets_info).sort_values(by=x_axis_column, ascending=False)
214
+ bets_df = (
215
+ pd.DataFrame(bets_info)
216
+ .sort_values(by=x_axis_column, ascending=False)
217
+ .reset_index(drop=True)
218
+ )
215
219
  bets_df["x-axis-day"] = bets_df[x_axis_column].dt.date
216
220
 
217
221
  # Metrics
@@ -0,0 +1,142 @@
1
+ from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
2
+
3
+
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")
7
+
8
+
9
+ def get_kelly_bet_simplified(
10
+ max_bet: float,
11
+ market_p_yes: float,
12
+ estimated_p_yes: float,
13
+ confidence: float,
14
+ ) -> SimpleBet:
15
+ """
16
+ Calculate the optimal bet amount using the Kelly Criterion for a binary outcome market.
17
+
18
+ From https://en.wikipedia.org/wiki/Kelly_criterion:
19
+
20
+ f* = p - q / b
21
+
22
+ where:
23
+ - f* is the fraction of the current bankroll to wager
24
+ - p is the probability of a win
25
+ - q = 1-p is the probability of a loss
26
+ - b is the proportion of the bet gained with a win
27
+
28
+ Note: this calculation does not factor in that the bet changes the market
29
+ odds. This means the calculation is only accurate if the bet size is small
30
+ compared to the market volume. See discussion here for more detail:
31
+ https://github.com/gnosis/prediction-market-agent-tooling/pull/330#discussion_r1698269328
32
+ """
33
+ check_is_valid_probability(market_p_yes)
34
+ check_is_valid_probability(estimated_p_yes)
35
+ check_is_valid_probability(confidence)
36
+
37
+ if estimated_p_yes > market_p_yes:
38
+ bet_direction = True
39
+ market_prob = market_p_yes
40
+ else:
41
+ bet_direction = False
42
+ market_prob = 1 - market_p_yes
43
+
44
+ # Handle the case where market_prob is 0
45
+ if market_prob == 0:
46
+ market_prob = 1e-10
47
+
48
+ edge = abs(estimated_p_yes - market_p_yes) * confidence
49
+ odds = (1 / market_prob) - 1
50
+ kelly_fraction = edge / odds
51
+
52
+ # Ensure bet size is non-negative does not exceed the wallet balance
53
+ bet_size = min(kelly_fraction * max_bet, max_bet)
54
+
55
+ return SimpleBet(direction=bet_direction, size=bet_size)
56
+
57
+
58
+ def get_kelly_bet_full(
59
+ yes_outcome_pool_size: float,
60
+ no_outcome_pool_size: float,
61
+ estimated_p_yes: float,
62
+ confidence: float,
63
+ max_bet: float,
64
+ fee: float = 0.0, # proportion, 0 to 1
65
+ ) -> SimpleBet:
66
+ """
67
+ Calculate the optimal bet amount using the Kelly Criterion for a binary outcome market.
68
+
69
+ 'Full' as in it accounts for how the bet changes the market odds.
70
+
71
+ Taken from https://github.com/valory-xyz/trader/blob/main/strategies/kelly_criterion/kelly_criterion.py
72
+
73
+ with derivation in PR description: https://github.com/valory-xyz/trader/pull/119
74
+
75
+ ```
76
+ Licensed under the Apache License, Version 2.0 (the "License");
77
+ you may not use this file except in compliance with the License.
78
+ You may obtain a copy of the License at
79
+
80
+ http://www.apache.org/licenses/LICENSE-2.0
81
+
82
+ Unless required by applicable law or agreed to in writing, software
83
+ distributed under the License is distributed on an "AS IS" BASIS,
84
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
85
+ See the License for the specific language governing permissions and
86
+ limitations under the License.
87
+ ```
88
+ """
89
+ check_is_valid_probability(estimated_p_yes)
90
+ check_is_valid_probability(confidence)
91
+ check_is_valid_probability(fee)
92
+
93
+ if max_bet == 0:
94
+ return SimpleBet(direction=True, size=0)
95
+
96
+ x = yes_outcome_pool_size
97
+ y = no_outcome_pool_size
98
+ p = estimated_p_yes
99
+ c = confidence
100
+ b = max_bet
101
+ f = 1 - fee
102
+
103
+ if x == y:
104
+ # Add a delta to prevent division by zero
105
+ y += 1e-10
106
+
107
+ numerator = (
108
+ -4 * x**2 * y
109
+ + b * y**2 * p * c * f
110
+ + 2 * b * x * y * p * c * f
111
+ + b * x**2 * p * c * f
112
+ - 2 * b * y**2 * f
113
+ - 2 * b * x * y * f
114
+ + (
115
+ (
116
+ 4 * x**2 * y
117
+ - b * y**2 * p * c * f
118
+ - 2 * b * x * y * p * c * f
119
+ - b * x**2 * p * c * f
120
+ + 2 * b * y**2 * f
121
+ + 2 * b * x * y * f
122
+ )
123
+ ** 2
124
+ - (
125
+ 4
126
+ * (x**2 * f - y**2 * f)
127
+ * (
128
+ -4 * b * x * y**2 * p * c
129
+ - 4 * b * x**2 * y * p * c
130
+ + 4 * b * x * y**2
131
+ )
132
+ )
133
+ )
134
+ ** (1 / 2)
135
+ )
136
+ denominator = 2 * (x**2 * f - y**2 * f)
137
+ kelly_bet_amount = numerator / denominator
138
+
139
+ # Clip the bet size to max_bet to account for rounding errors.
140
+ return SimpleBet(
141
+ direction=kelly_bet_amount > 0, size=min(max_bet, abs(kelly_bet_amount))
142
+ )