prediction-market-agent-tooling 0.53.0__tar.gz → 0.55.0__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 (99) hide show
  1. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/PKG-INFO +2 -2
  2. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/agent.py +181 -183
  3. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/betting_strategy.py +23 -2
  4. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/agent_market.py +48 -0
  5. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/manifold/manifold.py +5 -0
  6. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/markets.py +4 -0
  7. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/metaculus/data_models.py +4 -1
  8. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +30 -8
  9. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/omen/omen.py +70 -4
  10. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/is_invalid.py +4 -3
  11. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/tavily/tavily_search.py +11 -1
  12. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/pyproject.toml +3 -3
  13. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/LICENSE +0 -0
  14. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/README.md +0 -0
  15. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/debuggingcontract.abi.json +0 -0
  16. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
  17. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
  18. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
  19. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_agentresultmapping.abi.json +0 -0
  20. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
  21. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
  22. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
  23. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
  24. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
  25. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
  26. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
  27. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
  28. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
  29. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
  30. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/benchmark/agents.py +0 -0
  31. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
  32. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/benchmark/utils.py +0 -0
  33. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/config.py +0 -0
  34. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/agent_example.py +0 -0
  35. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/constants.py +0 -0
  36. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
  37. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
  38. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
  39. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/gtypes.py +0 -0
  40. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/jobs/__init__.py +0 -0
  41. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/jobs/jobs.py +0 -0
  42. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/jobs/jobs_models.py +0 -0
  43. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/jobs/omen/omen_jobs.py +0 -0
  44. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/loggers.py +0 -0
  45. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/categorize.py +0 -0
  46. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/data_models.py +0 -0
  47. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/manifold/__init__.py +0 -0
  48. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
  49. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
  50. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
  51. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/market_fees.py +0 -0
  52. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
  53. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
  54. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/omen/data_models.py +0 -0
  55. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +0 -0
  56. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +0 -0
  57. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +0 -0
  58. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
  59. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
  60. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
  61. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
  62. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
  63. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
  64. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
  65. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
  66. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
  67. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/monitor.py +0 -0
  68. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/monitor_app.py +0 -0
  69. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
  70. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/py.typed +0 -0
  71. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/balances.py +0 -0
  72. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +0 -0
  73. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -0
  74. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
  75. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
  76. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/betting_strategies/utils.py +0 -0
  77. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/cache.py +0 -0
  78. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/contract.py +0 -0
  79. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/costs.py +0 -0
  80. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/datetime_utc.py +0 -0
  81. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -0
  82. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/google.py +0 -0
  83. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
  84. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/httpx_cached_client.py +0 -0
  85. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
  86. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
  87. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py +0 -0
  88. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/is_predictable.py +0 -0
  89. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
  90. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/langfuse_client_utils.py +0 -0
  91. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/omen/reality_accuracy.py +0 -0
  92. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
  93. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/safe.py +0 -0
  94. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/singleton.py +0 -0
  95. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
  96. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/tavily/tavily_models.py +0 -0
  97. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/tavily/tavily_storage.py +0 -0
  98. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/prediction_market_agent_tooling/tools/utils.py +0 -0
  99. {prediction_market_agent_tooling-0.53.0 → prediction_market_agent_tooling-0.55.0}/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.53.0
3
+ Version: 0.55.0
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -44,7 +44,7 @@ Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
44
44
  Requires-Dist: streamlit (>=1.31.0,<2.0.0)
45
45
  Requires-Dist: subgrounds (>=1.9.1,<2.0.0)
46
46
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
47
- Requires-Dist: tavily-python (>=0.3.9,<0.4.0)
47
+ Requires-Dist: tavily-python (>=0.5.0,<0.6.0)
48
48
  Requires-Dist: tqdm (>=4.66.2,<5.0.0)
49
49
  Requires-Dist: typer (>=0.9.0,<1.0.0)
50
50
  Requires-Dist: types-python-dateutil (>=2.9.0.20240906,<3.0.0.0)
@@ -8,10 +8,8 @@ from datetime import timedelta
8
8
  from enum import Enum
9
9
  from functools import cached_property
10
10
 
11
- from pydantic import BaseModel, BeforeValidator, computed_field
11
+ from pydantic import BeforeValidator, computed_field
12
12
  from typing_extensions import Annotated
13
- from web3 import Web3
14
- from web3.constants import HASH_ZERO
15
13
 
16
14
  from prediction_market_agent_tooling.config import APIKeys
17
15
  from prediction_market_agent_tooling.deploy.betting_strategy import (
@@ -32,11 +30,13 @@ from prediction_market_agent_tooling.deploy.gcp.utils import (
32
30
  gcp_function_is_active,
33
31
  gcp_resolve_api_keys_secrets,
34
32
  )
35
- from prediction_market_agent_tooling.gtypes import HexStr, xDai, xdai_type
33
+ from prediction_market_agent_tooling.gtypes import xDai, xdai_type
36
34
  from prediction_market_agent_tooling.loggers import logger
37
35
  from prediction_market_agent_tooling.markets.agent_market import (
38
36
  AgentMarket,
39
37
  FilterBy,
38
+ ProcessedMarket,
39
+ ProcessedTradedMarket,
40
40
  SortBy,
41
41
  )
42
42
  from prediction_market_agent_tooling.markets.data_models import (
@@ -49,28 +49,16 @@ from prediction_market_agent_tooling.markets.markets import (
49
49
  MarketType,
50
50
  have_bet_on_market_since,
51
51
  )
52
- from prediction_market_agent_tooling.markets.omen.data_models import (
53
- ContractPrediction,
54
- IPFSAgentResult,
55
- )
56
52
  from prediction_market_agent_tooling.markets.omen.omen import (
57
- is_minimum_required_balance,
58
- redeem_from_all_user_positions,
59
53
  withdraw_wxdai_to_xdai_to_keep_balance,
60
54
  )
61
- from prediction_market_agent_tooling.markets.omen.omen_contracts import (
62
- OmenAgentResultMappingContract,
63
- )
64
55
  from prediction_market_agent_tooling.monitor.monitor_app import (
65
56
  MARKET_TYPE_TO_DEPLOYED_AGENT,
66
57
  )
67
- from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
68
- from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
69
58
  from prediction_market_agent_tooling.tools.is_invalid import is_invalid
70
59
  from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
71
60
  from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
72
61
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC, utcnow
73
- from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
74
62
 
75
63
  MAX_AVAILABLE_MARKETS = 20
76
64
  TRADER_TAG = "trader"
@@ -122,11 +110,6 @@ class OutOfFundsError(ValueError):
122
110
  pass
123
111
 
124
112
 
125
- class ProcessedMarket(BaseModel):
126
- answer: ProbabilisticAnswer
127
- trades: list[PlacedTrade]
128
-
129
-
130
113
  class AnsweredEnum(str, Enum):
131
114
  ANSWERED = "answered"
132
115
  NOT_ANSWERED = "not_answered"
@@ -183,9 +166,11 @@ class DeployableAgent:
183
166
  return f"{self.__class__.__name__} - {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}"
184
167
 
185
168
  def __init_subclass__(cls, **kwargs: t.Any) -> None:
186
- if "DeployableAgent" not in str(
187
- cls.__init__
188
- ) and "DeployableTraderAgent" not in str(cls.__init__):
169
+ if (
170
+ "DeployableAgent" not in str(cls.__init__)
171
+ and "DeployableTraderAgent" not in str(cls.__init__)
172
+ and "DeployablePredictionAgent" not in str(cls.__init__)
173
+ ):
189
174
  raise TypeError(
190
175
  "Cannot override __init__ method of deployable agent class, please override the `load` method to set up the agent."
191
176
  )
@@ -292,29 +277,21 @@ def {entrypoint_function_name}(request) -> str:
292
277
  return f"{self.__class__.__name__.lower()}-{market_type}-{utcnow().strftime('%Y-%m-%d--%H-%M-%S')}"
293
278
 
294
279
 
295
- class DeployableTraderAgent(DeployableAgent):
280
+ class DeployablePredictionAgent(DeployableAgent):
296
281
  bet_on_n_markets_per_run: int = 1
297
- min_required_balance_to_operate: xDai | None = xdai_type(1)
298
282
  min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
299
283
  allow_invalid_questions: bool = False
300
284
  same_market_bet_interval: timedelta = timedelta(hours=24)
285
+ # Only Metaculus allows to post predictions without trading (buying/selling of outcome tokens).
286
+ supported_markets: t.Sequence[MarketType] = [MarketType.METACULUS]
301
287
 
302
288
  def __init__(
303
289
  self,
304
290
  enable_langfuse: bool = APIKeys().default_enable_langfuse,
305
- place_bet: bool = True,
291
+ store_prediction: bool = True,
306
292
  ) -> None:
307
293
  super().__init__(enable_langfuse=enable_langfuse)
308
- self.place_bet = place_bet
309
-
310
- def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
311
- user_id = market.get_user_id(api_keys=APIKeys())
312
-
313
- total_amount = market.get_tiny_bet_amount().amount
314
- if existing_position := market.get_position(user_id=user_id):
315
- total_amount += existing_position.total_amount.amount
316
-
317
- return MaxAccuracyBettingStrategy(bet_amount=total_amount)
294
+ self.store_prediction = store_prediction
318
295
 
319
296
  def initialize_langfuse(self) -> None:
320
297
  super().initialize_langfuse()
@@ -323,7 +300,6 @@ class DeployableTraderAgent(DeployableAgent):
323
300
  self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
324
301
  self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
325
302
  self.process_market = observe()(self.process_market) # type: ignore[method-assign]
326
- self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
327
303
 
328
304
  def update_langfuse_trace_by_market(
329
305
  self, market_type: MarketType, market: AgentMarket
@@ -353,38 +329,12 @@ class DeployableTraderAgent(DeployableAgent):
353
329
  ]
354
330
  )
355
331
 
356
- def check_min_required_balance_to_operate(
357
- self,
358
- market_type: MarketType,
359
- check_for_gas: bool = True,
360
- check_for_trades: bool = True,
361
- ) -> None:
332
+ def check_min_required_balance_to_operate(self, market_type: MarketType) -> None:
362
333
  api_keys = APIKeys()
363
- if (
364
- market_type == MarketType.OMEN
365
- and check_for_gas
366
- and not is_minimum_required_balance(
367
- api_keys.public_key,
368
- min_required_balance=xdai_type(0.001),
369
- sum_wxdai=False,
370
- )
371
- ):
334
+
335
+ if not market_type.market_class.verify_operational_balance(api_keys):
372
336
  raise CantPayForGasError(
373
- f"{api_keys.public_key=} doesn't have enough xDai to pay for gas."
374
- )
375
- if self.min_required_balance_to_operate is None:
376
- return
377
- if (
378
- market_type == MarketType.OMEN
379
- and check_for_trades
380
- and not is_minimum_required_balance(
381
- api_keys.bet_from_address,
382
- min_required_balance=self.min_required_balance_to_operate,
383
- )
384
- ):
385
- raise OutOfFundsError(
386
- f"Minimum required balance {self.min_required_balance_to_operate} "
387
- f"for agent with address {api_keys.bet_from_address=} is not met."
337
+ f"{api_keys=} doesn't have enough operational balance."
388
338
  )
389
339
 
390
340
  def have_bet_on_market_since(self, market: AgentMarket, since: timedelta) -> bool:
@@ -431,21 +381,19 @@ class DeployableTraderAgent(DeployableAgent):
431
381
  )
432
382
  return available_markets
433
383
 
434
- def build_trades(
435
- self,
436
- market: AgentMarket,
437
- answer: ProbabilisticAnswer,
438
- existing_position: Position | None,
439
- ) -> list[Trade]:
440
- strategy = self.get_betting_strategy(market=market)
441
- trades = strategy.calculate_trades(existing_position, answer, market)
442
- BettingStrategy.assert_trades_currency_match_markets(market, trades)
443
- return trades
444
-
445
384
  def before_process_market(
446
385
  self, market_type: MarketType, market: AgentMarket
447
386
  ) -> None:
448
- self.update_langfuse_trace_by_market(market_type, market)
387
+ api_keys = APIKeys()
388
+
389
+ if market_type.is_blockchain_market:
390
+ # Exchange wxdai back to xdai if the balance is getting low, so we can keep paying for fees.
391
+ if self.min_balance_to_keep_in_native_currency is not None:
392
+ withdraw_wxdai_to_xdai_to_keep_balance(
393
+ api_keys,
394
+ min_required_balance=self.min_balance_to_keep_in_native_currency,
395
+ withdraw_multiplier=2,
396
+ )
449
397
 
450
398
  def process_market(
451
399
  self,
@@ -453,129 +401,47 @@ class DeployableTraderAgent(DeployableAgent):
453
401
  market: AgentMarket,
454
402
  verify_market: bool = True,
455
403
  ) -> ProcessedMarket | None:
404
+ self.update_langfuse_trace_by_market(market_type, market)
456
405
  logger.info(f"Processing market {market.question=} from {market.url=}.")
457
406
 
458
- self.before_process_market(market_type, market)
459
-
407
+ answer: ProbabilisticAnswer | None
460
408
  if verify_market and not self.verify_market(market_type, market):
461
409
  logger.info(f"Market '{market.question}' doesn't meet the criteria.")
462
- self.update_langfuse_trace_by_processed_market(market_type, None)
463
- return None
464
-
465
- answer = self.answer_binary_market(market)
466
-
467
- if answer is None:
468
- logger.info(f"No answer for market '{market.question}'.")
469
- self.update_langfuse_trace_by_processed_market(market_type, None)
470
- return None
410
+ answer = None
411
+ else:
412
+ answer = self.answer_binary_market(market)
471
413
 
472
- existing_position = market.get_position(user_id=APIKeys().bet_from_address)
473
- trades = self.build_trades(
474
- market=market,
475
- answer=answer,
476
- existing_position=existing_position,
414
+ processed_market = (
415
+ ProcessedMarket(answer=answer) if answer is not None else None
477
416
  )
478
417
 
479
- placed_trades = []
480
- if self.place_bet:
481
- for trade in trades:
482
- logger.info(f"Executing trade {trade} on market {market.id}")
483
-
484
- match trade.trade_type:
485
- case TradeType.BUY:
486
- id = market.buy_tokens(
487
- outcome=trade.outcome, amount=trade.amount
488
- )
489
- case TradeType.SELL:
490
- id = market.sell_tokens(
491
- outcome=trade.outcome, amount=trade.amount
492
- )
493
- case _:
494
- raise ValueError(f"Unexpected trade type {trade.trade_type}.")
495
- placed_trades.append(PlacedTrade.from_trade(trade, id))
496
-
497
- processed_market = ProcessedMarket(answer=answer, trades=placed_trades)
498
418
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
499
-
500
- self.after_process_market(
501
- market_type, market, processed_market=processed_market
419
+ logger.info(
420
+ f"Processed market {market.question=} from {market.url=} with {answer=}."
502
421
  )
503
-
504
- logger.info(f"Processed market {market.question=} from {market.url=}.")
505
422
  return processed_market
506
423
 
507
424
  def after_process_market(
508
425
  self,
509
426
  market_type: MarketType,
510
427
  market: AgentMarket,
511
- processed_market: ProcessedMarket,
428
+ processed_market: ProcessedMarket | None,
512
429
  ) -> None:
513
- if market_type != MarketType.OMEN:
514
- logger.info(
515
- f"Skipping after_process_market since market_type {market_type} != OMEN"
516
- )
517
- return
518
430
  keys = APIKeys()
519
- self.store_prediction(
520
- market_id=market.id, processed_market=processed_market, keys=keys
521
- )
522
-
523
- def store_prediction(
524
- self, market_id: str, processed_market: ProcessedMarket, keys: APIKeys
525
- ) -> None:
526
- reasoning = (
527
- processed_market.answer.reasoning
528
- if processed_market.answer.reasoning
529
- else ""
530
- )
531
-
532
- ipfs_hash_decoded = HexBytes(HASH_ZERO)
533
- if keys.enable_ipfs_upload:
534
- logger.info("Storing prediction on IPFS.")
535
- ipfs_hash = IPFSHandler(keys).store_agent_result(
536
- IPFSAgentResult(reasoning=reasoning)
431
+ if self.store_prediction:
432
+ market.store_prediction(processed_market=processed_market, keys=keys)
433
+ else:
434
+ logger.info(
435
+ f"Prediction {processed_market} not stored because {self.store_prediction=}."
537
436
  )
538
- ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)
539
-
540
- tx_hashes = [
541
- HexBytes(HexStr(i.id)) for i in processed_market.trades if i.id is not None
542
- ]
543
- prediction = ContractPrediction(
544
- publisher=keys.public_key,
545
- ipfs_hash=ipfs_hash_decoded,
546
- tx_hashes=tx_hashes,
547
- estimated_probability_bps=int(processed_market.answer.p_yes * 10000),
548
- )
549
- tx_receipt = OmenAgentResultMappingContract().add_prediction(
550
- api_keys=keys,
551
- market_address=Web3.to_checksum_address(market_id),
552
- prediction=prediction,
553
- )
554
- logger.info(
555
- f"Added prediction to market {market_id}. - receipt {tx_receipt['transactionHash'].hex()}."
556
- )
557
437
 
558
438
  def before_process_markets(self, market_type: MarketType) -> None:
559
439
  """
560
440
  Executes actions that occur before bets are placed.
561
441
  """
562
442
  api_keys = APIKeys()
563
- if market_type == MarketType.OMEN:
564
- # First, check if we have enough xDai to pay for gas, there is no way of doing anything without it.
565
- self.check_min_required_balance_to_operate(
566
- market_type, check_for_trades=False
567
- )
568
- # Omen is specific, because the user (agent) needs to manually withdraw winnings from the market.
569
- redeem_from_all_user_positions(api_keys)
570
- # After redeeming, check if we have enough xDai to pay for gas and place bets.
571
- self.check_min_required_balance_to_operate(market_type)
572
- # Exchange wxdai back to xdai if the balance is getting low, so we can keep paying for fees.
573
- if self.min_balance_to_keep_in_native_currency is not None:
574
- withdraw_wxdai_to_xdai_to_keep_balance(
575
- api_keys,
576
- min_required_balance=self.min_balance_to_keep_in_native_currency,
577
- withdraw_multiplier=2,
578
- )
443
+ self.check_min_required_balance_to_operate(market_type)
444
+ market_type.market_class.redeem_winnings(api_keys)
579
445
 
580
446
  def process_markets(self, market_type: MarketType) -> None:
581
447
  """
@@ -589,10 +455,9 @@ class DeployableTraderAgent(DeployableAgent):
589
455
  processed = 0
590
456
 
591
457
  for market in available_markets:
592
- # We need to check it again before each market bet, as the balance might have changed.
593
- self.check_min_required_balance_to_operate(market_type)
594
-
458
+ self.before_process_market(market_type, market)
595
459
  processed_market = self.process_market(market_type, market)
460
+ self.after_process_market(market_type, market, processed_market)
596
461
 
597
462
  if processed_market is not None:
598
463
  processed += 1
@@ -603,9 +468,142 @@ class DeployableTraderAgent(DeployableAgent):
603
468
  logger.info("All markets processed.")
604
469
 
605
470
  def after_process_markets(self, market_type: MarketType) -> None:
606
- pass
471
+ "Executes actions that occur after bets are placed."
607
472
 
608
473
  def run(self, market_type: MarketType) -> None:
474
+ if market_type not in self.supported_markets:
475
+ raise ValueError(
476
+ f"Only {self.supported_markets} are supported by this agent."
477
+ )
609
478
  self.before_process_markets(market_type)
610
479
  self.process_markets(market_type)
611
480
  self.after_process_markets(market_type)
481
+
482
+
483
+ class DeployableTraderAgent(DeployablePredictionAgent):
484
+ # These markets require place of bet, not just predictions.
485
+ supported_markets: t.Sequence[MarketType] = [
486
+ MarketType.OMEN,
487
+ MarketType.MANIFOLD,
488
+ MarketType.POLYMARKET,
489
+ ]
490
+
491
+ def __init__(
492
+ self,
493
+ enable_langfuse: bool = APIKeys().default_enable_langfuse,
494
+ store_prediction: bool = True,
495
+ store_trades: bool = True,
496
+ place_trades: bool = True,
497
+ ) -> None:
498
+ super().__init__(
499
+ enable_langfuse=enable_langfuse, store_prediction=store_prediction
500
+ )
501
+ self.store_trades = store_trades
502
+ self.place_trades = place_trades
503
+
504
+ def initialize_langfuse(self) -> None:
505
+ super().initialize_langfuse()
506
+ # Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
507
+ self.get_betting_strategy = observe()(self.get_betting_strategy) # type: ignore[method-assign]
508
+ self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
509
+
510
+ def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
511
+ api_keys = APIKeys()
512
+
513
+ # Get the strategy to know how much it will bet.
514
+ strategy = self.get_betting_strategy(market)
515
+ # Have a little bandwidth after the bet.
516
+ min_required_balance_to_trade = strategy.maximum_possible_bet_amount * 1.01
517
+
518
+ if market.get_trade_balance(api_keys) < min_required_balance_to_trade:
519
+ raise OutOfFundsError(
520
+ f"Minimum required balance {min_required_balance_to_trade} for agent is not met."
521
+ )
522
+
523
+ def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
524
+ user_id = market.get_user_id(api_keys=APIKeys())
525
+
526
+ total_amount = market.get_tiny_bet_amount().amount
527
+ if existing_position := market.get_position(user_id=user_id):
528
+ total_amount += existing_position.total_amount.amount
529
+
530
+ return MaxAccuracyBettingStrategy(bet_amount=total_amount)
531
+
532
+ def build_trades(
533
+ self,
534
+ market: AgentMarket,
535
+ answer: ProbabilisticAnswer,
536
+ existing_position: Position | None,
537
+ ) -> list[Trade]:
538
+ strategy = self.get_betting_strategy(market=market)
539
+ trades = strategy.calculate_trades(existing_position, answer, market)
540
+ BettingStrategy.assert_trades_currency_match_markets(market, trades)
541
+ return trades
542
+
543
+ def before_process_market(
544
+ self, market_type: MarketType, market: AgentMarket
545
+ ) -> None:
546
+ super().before_process_market(market_type, market)
547
+ self.check_min_required_balance_to_trade(market)
548
+
549
+ def process_market(
550
+ self,
551
+ market_type: MarketType,
552
+ market: AgentMarket,
553
+ verify_market: bool = True,
554
+ ) -> ProcessedTradedMarket | None:
555
+ processed_market = super().process_market(market_type, market, verify_market)
556
+ if processed_market is None:
557
+ return None
558
+
559
+ api_keys = APIKeys()
560
+ existing_position = market.get_position(
561
+ user_id=market.get_user_id(api_keys=api_keys)
562
+ )
563
+ trades = self.build_trades(
564
+ market=market,
565
+ answer=processed_market.answer,
566
+ existing_position=existing_position,
567
+ )
568
+
569
+ placed_trades = []
570
+ for trade in trades:
571
+ logger.info(f"Executing trade {trade} on market {market.id} ({market.url})")
572
+
573
+ if self.place_trades:
574
+ match trade.trade_type:
575
+ case TradeType.BUY:
576
+ id = market.buy_tokens(
577
+ outcome=trade.outcome, amount=trade.amount
578
+ )
579
+ case TradeType.SELL:
580
+ id = market.sell_tokens(
581
+ outcome=trade.outcome, amount=trade.amount
582
+ )
583
+ case _:
584
+ raise ValueError(f"Unexpected trade type {trade.trade_type}.")
585
+ placed_trades.append(PlacedTrade.from_trade(trade, id))
586
+ else:
587
+ logger.info(f"Trade execution skipped because {self.place_trades=}.")
588
+
589
+ traded_market = ProcessedTradedMarket(
590
+ answer=processed_market.answer, trades=placed_trades
591
+ )
592
+ logger.info(f"Traded market {market.question=} from {market.url=}.")
593
+ return traded_market
594
+
595
+ def after_process_market(
596
+ self,
597
+ market_type: MarketType,
598
+ market: AgentMarket,
599
+ processed_market: ProcessedMarket | None,
600
+ ) -> None:
601
+ api_keys = APIKeys()
602
+ super().after_process_market(market_type, market, processed_market)
603
+ if isinstance(processed_market, ProcessedTradedMarket):
604
+ if self.store_trades:
605
+ market.store_trades(processed_market, api_keys)
606
+ else:
607
+ logger.info(
608
+ f"Trades {processed_market.trades} not stored because {self.store_trades=}."
609
+ )
@@ -24,6 +24,10 @@ from prediction_market_agent_tooling.tools.betting_strategies.utils import Simpl
24
24
  from prediction_market_agent_tooling.tools.utils import check_not_none
25
25
 
26
26
 
27
+ class GuaranteedLossError(RuntimeError):
28
+ pass
29
+
30
+
27
31
  class BettingStrategy(ABC):
28
32
  @abstractmethod
29
33
  def calculate_trades(
@@ -32,7 +36,12 @@ class BettingStrategy(ABC):
32
36
  answer: ProbabilisticAnswer,
33
37
  market: AgentMarket,
34
38
  ) -> list[Trade]:
35
- pass
39
+ raise NotImplementedError("Subclass should implement this.")
40
+
41
+ @property
42
+ @abstractmethod
43
+ def maximum_possible_bet_amount(self) -> float:
44
+ raise NotImplementedError("Subclass should implement this.")
36
45
 
37
46
  def build_zero_token_amount(self, currency: Currency) -> TokenAmount:
38
47
  return TokenAmount(amount=0, currency=currency)
@@ -58,7 +67,7 @@ class BettingStrategy(ABC):
58
67
  )
59
68
 
60
69
  if outcome_tokens_to_get.amount < trade.amount.amount:
61
- raise RuntimeError(
70
+ raise GuaranteedLossError(
62
71
  f"Trade {trade=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}."
63
72
  )
64
73
 
@@ -126,6 +135,10 @@ class MaxAccuracyBettingStrategy(BettingStrategy):
126
135
  def __init__(self, bet_amount: float):
127
136
  self.bet_amount = bet_amount
128
137
 
138
+ @property
139
+ def maximum_possible_bet_amount(self) -> float:
140
+ return self.bet_amount
141
+
129
142
  def calculate_trades(
130
143
  self,
131
144
  existing_position: Position | None,
@@ -168,6 +181,10 @@ class KellyBettingStrategy(BettingStrategy):
168
181
  self.max_bet_amount = max_bet_amount
169
182
  self.max_price_impact = max_price_impact
170
183
 
184
+ @property
185
+ def maximum_possible_bet_amount(self) -> float:
186
+ return self.max_bet_amount
187
+
171
188
  def calculate_trades(
172
189
  self,
173
190
  existing_position: Position | None,
@@ -282,6 +299,10 @@ class MaxAccuracyWithKellyScaledBetsStrategy(BettingStrategy):
282
299
  def __init__(self, max_bet_amount: float = 10):
283
300
  self.max_bet_amount = max_bet_amount
284
301
 
302
+ @property
303
+ def maximum_possible_bet_amount(self) -> float:
304
+ return self.max_bet_amount
305
+
285
306
  def adjust_bet_amount(
286
307
  self, existing_position: Position | None, market: AgentMarket
287
308
  ) -> float:
@@ -11,7 +11,9 @@ from prediction_market_agent_tooling.markets.data_models import (
11
11
  Bet,
12
12
  BetAmount,
13
13
  Currency,
14
+ PlacedTrade,
14
15
  Position,
16
+ ProbabilisticAnswer,
15
17
  Resolution,
16
18
  ResolvedBet,
17
19
  TokenAmount,
@@ -25,6 +27,14 @@ from prediction_market_agent_tooling.tools.utils import (
25
27
  )
26
28
 
27
29
 
30
+ class ProcessedMarket(BaseModel):
31
+ answer: ProbabilisticAnswer
32
+
33
+
34
+ class ProcessedTradedMarket(ProcessedMarket):
35
+ trades: list[PlacedTrade]
36
+
37
+
28
38
  class SortBy(str, Enum):
29
39
  CLOSING_SOONEST = "closing-soonest"
30
40
  NEWEST = "newest"
@@ -198,6 +208,44 @@ class AgentMarket(BaseModel):
198
208
  def get_binary_market(id: str) -> "AgentMarket":
199
209
  raise NotImplementedError("Subclasses must implement this method")
200
210
 
211
+ @staticmethod
212
+ def redeem_winnings(api_keys: APIKeys) -> None:
213
+ """
214
+ On some markets (like Omen), it's needed to manually claim the winner bets. If it's not needed, just implement with `pass`.
215
+ """
216
+ raise NotImplementedError("Subclasses must implement this method")
217
+
218
+ @staticmethod
219
+ def get_trade_balance(api_keys: APIKeys) -> float:
220
+ """
221
+ Return balance that can be used to trade on the given market.
222
+ """
223
+ raise NotImplementedError("Subclasses must implement this method")
224
+
225
+ @staticmethod
226
+ def verify_operational_balance(api_keys: APIKeys) -> bool:
227
+ """
228
+ Return `True` if the user has enough of operational balance. If not needed, just return `True`.
229
+ For example: Omen needs at least some xDai in the wallet to execute transactions.
230
+ """
231
+ raise NotImplementedError("Subclasses must implement this method")
232
+
233
+ def store_prediction(
234
+ self, processed_market: ProcessedMarket | None, keys: APIKeys
235
+ ) -> None:
236
+ """
237
+ If market allows to upload predictions somewhere, implement it in this method.
238
+ """
239
+ raise NotImplementedError("Subclasses must implement this method")
240
+
241
+ def store_trades(
242
+ self, traded_market: ProcessedTradedMarket | None, keys: APIKeys
243
+ ) -> None:
244
+ """
245
+ If market allows to upload trades somewhere, implement it in this method.
246
+ """
247
+ raise NotImplementedError("Subclasses must implement this method")
248
+
201
249
  @staticmethod
202
250
  def get_bets_made_since(
203
251
  better_address: ChecksumAddress, start_time: DatetimeUTC
@@ -125,6 +125,11 @@ class ManifoldAgentMarket(AgentMarket):
125
125
  )
126
126
  ]
127
127
 
128
+ @staticmethod
129
+ def redeem_winnings(api_keys: APIKeys) -> None:
130
+ # It's done automatically on Manifold.
131
+ pass
132
+
128
133
  @classmethod
129
134
  def get_user_url(cls, keys: APIKeys) -> str:
130
135
  return get_authenticated_user(keys.manifold_api_key.get_secret_value()).url
@@ -46,6 +46,10 @@ class MarketType(str, Enum):
46
46
  raise ValueError(f"Unknown market type: {self}")
47
47
  return MARKET_TYPE_TO_AGENT_MARKET[self]
48
48
 
49
+ @property
50
+ def is_blockchain_market(self) -> bool:
51
+ return self in [MarketType.OMEN, MarketType.POLYMARKET]
52
+
49
53
 
50
54
  MARKET_TYPE_TO_AGENT_MARKET: dict[MarketType, type[AgentMarket]] = {
51
55
  MarketType.MANIFOLD: ManifoldAgentMarket,