prediction-market-agent-tooling 0.48.11__tar.gz → 0.48.13__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 (85) hide show
  1. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/PKG-INFO +2 -2
  2. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/deploy/agent.py +35 -33
  3. prediction_market_agent_tooling-0.48.13/prediction_market_agent_tooling/deploy/betting_strategy.py +187 -0
  4. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/agent_market.py +46 -1
  5. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/data_models.py +11 -0
  6. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/manifold/data_models.py +6 -0
  7. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/manifold/manifold.py +3 -0
  8. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +1 -0
  9. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/omen/data_models.py +4 -0
  10. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/omen/omen.py +70 -6
  11. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +18 -8
  12. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +1 -0
  13. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/contract.py +6 -1
  14. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/pyproject.toml +2 -2
  15. prediction_market_agent_tooling-0.48.11/prediction_market_agent_tooling/deploy/betting_strategy.py +0 -62
  16. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/LICENSE +0 -0
  17. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/README.md +0 -0
  18. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
  19. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
  20. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
  21. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
  22. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
  23. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
  24. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
  25. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
  26. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
  27. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
  28. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
  29. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
  30. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
  31. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/benchmark/agents.py +0 -0
  32. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
  33. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/benchmark/utils.py +0 -0
  34. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/config.py +0 -0
  35. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/deploy/agent_example.py +0 -0
  36. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/deploy/constants.py +0 -0
  37. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
  38. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
  39. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
  40. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/gtypes.py +0 -0
  41. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/loggers.py +0 -0
  42. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/categorize.py +0 -0
  43. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/manifold/__init__.py +0 -0
  44. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
  45. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
  46. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/markets.py +0 -0
  47. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
  48. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/metaculus/data_models.py +0 -0
  49. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
  50. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +0 -0
  51. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +0 -0
  52. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
  53. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
  54. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
  55. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
  56. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
  57. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
  58. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
  59. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
  60. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/monitor.py +0 -0
  61. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/monitor_app.py +0 -0
  62. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
  63. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/py.typed +0 -0
  64. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/balances.py +0 -0
  65. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +0 -0
  66. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -0
  67. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
  68. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
  69. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/cache.py +0 -0
  70. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/costs.py +0 -0
  71. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -0
  72. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/google.py +0 -0
  73. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
  74. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
  75. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
  76. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/is_predictable.py +0 -0
  77. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
  78. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
  79. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/safe.py +0 -0
  80. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/singleton.py +0 -0
  81. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
  82. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +0 -0
  83. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py +0 -0
  84. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/prediction_market_agent_tooling/tools/utils.py +0 -0
  85. {prediction_market_agent_tooling-0.48.11 → prediction_market_agent_tooling-0.48.13}/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.11
3
+ Version: 0.48.13
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -31,7 +31,7 @@ Requires-Dist: openai (>=1.0.0,<2.0.0) ; extra == "openai"
31
31
  Requires-Dist: prompt-toolkit (>=3.0.43,<4.0.0)
32
32
  Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
33
33
  Requires-Dist: pydantic (>=2.6.1,<3.0.0)
34
- Requires-Dist: pydantic-settings (>=2.1.0,<3.0.0)
34
+ Requires-Dist: pydantic-settings (>=2.4.0,<3.0.0)
35
35
  Requires-Dist: pymongo (>=4.8.0,<5.0.0)
36
36
  Requires-Dist: safe-cli (>=1.0.0,<2.0.0)
37
37
  Requires-Dist: safe-eth-py (>=6.0.0b14,<7.0.0)
@@ -15,6 +15,7 @@ from prediction_market_agent_tooling.config import APIKeys
15
15
  from prediction_market_agent_tooling.deploy.betting_strategy import (
16
16
  BettingStrategy,
17
17
  MaxAccuracyBettingStrategy,
18
+ TradeType,
18
19
  )
19
20
  from prediction_market_agent_tooling.deploy.constants import (
20
21
  MARKET_TYPE_KEY,
@@ -37,10 +38,9 @@ from prediction_market_agent_tooling.markets.agent_market import (
37
38
  SortBy,
38
39
  )
39
40
  from prediction_market_agent_tooling.markets.data_models import (
40
- BetAmount,
41
+ Position,
41
42
  ProbabilisticAnswer,
42
- TokenAmount,
43
- TokenAmountAndDirection,
43
+ Trade,
44
44
  )
45
45
  from prediction_market_agent_tooling.markets.markets import (
46
46
  MarketType,
@@ -110,7 +110,7 @@ class OutOfFundsError(ValueError):
110
110
 
111
111
  class ProcessedMarket(BaseModel):
112
112
  answer: ProbabilisticAnswer
113
- amount: BetAmount
113
+ trades: list[Trade]
114
114
 
115
115
 
116
116
  class AnsweredEnum(str, Enum):
@@ -283,6 +283,7 @@ class DeployableTraderAgent(DeployableAgent):
283
283
  min_required_balance_to_operate: xDai | None = xdai_type(1)
284
284
  min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
285
285
  strategy: BettingStrategy = MaxAccuracyBettingStrategy()
286
+ allow_opposite_bets: bool = False
286
287
 
287
288
  def __init__(
288
289
  self,
@@ -298,8 +299,8 @@ class DeployableTraderAgent(DeployableAgent):
298
299
  self.have_bet_on_market_since = observe()(self.have_bet_on_market_since) # type: ignore[method-assign]
299
300
  self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
300
301
  self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
301
- self.calculate_bet_amount_and_direction = observe()(self.calculate_bet_amount_and_direction) # type: ignore[method-assign]
302
302
  self.process_market = observe()(self.process_market) # type: ignore[method-assign]
303
+ self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
303
304
 
304
305
  def update_langfuse_trace_by_market(
305
306
  self, market_type: MarketType, market: AgentMarket
@@ -314,18 +315,6 @@ class DeployableTraderAgent(DeployableAgent):
314
315
  },
315
316
  )
316
317
 
317
- def calculate_bet_amount_and_direction(
318
- self, answer: ProbabilisticAnswer, market: AgentMarket
319
- ) -> TokenAmountAndDirection:
320
- amount_and_direction = self.strategy.calculate_bet_amount_and_direction(
321
- answer, market
322
- )
323
- if amount_and_direction.currency != market.currency:
324
- raise ValueError(
325
- f"Currency mismatch. Strategy yields {amount_and_direction.currency}, market has currency {market.currency}"
326
- )
327
- return amount_and_direction
328
-
329
318
  def update_langfuse_trace_by_processed_market(
330
319
  self, market_type: MarketType, processed_market: ProcessedMarket | None
331
320
  ) -> None:
@@ -416,13 +405,26 @@ class DeployableTraderAgent(DeployableAgent):
416
405
  )
417
406
  return available_markets
418
407
 
408
+ def build_trades(
409
+ self,
410
+ market: AgentMarket,
411
+ answer: ProbabilisticAnswer,
412
+ existing_position: Position | None,
413
+ ) -> list[Trade]:
414
+ trades = self.strategy.calculate_trades(existing_position, answer, market)
415
+ BettingStrategy.assert_trades_currency_match_markets(market, trades)
416
+ return trades
417
+
419
418
  def before_process_market(
420
419
  self, market_type: MarketType, market: AgentMarket
421
420
  ) -> None:
422
421
  self.update_langfuse_trace_by_market(market_type, market)
423
422
 
424
423
  def process_market(
425
- self, market_type: MarketType, market: AgentMarket, verify_market: bool = True
424
+ self,
425
+ market_type: MarketType,
426
+ market: AgentMarket,
427
+ verify_market: bool = True,
426
428
  ) -> ProcessedMarket | None:
427
429
  self.before_process_market(market_type, market)
428
430
 
@@ -438,26 +440,26 @@ class DeployableTraderAgent(DeployableAgent):
438
440
  self.update_langfuse_trace_by_processed_market(market_type, None)
439
441
  return None
440
442
 
441
- amount_and_direction = self.calculate_bet_amount_and_direction(answer, market)
443
+ existing_position = market.get_position(user_id=APIKeys().bet_from_address)
444
+ trades = self.build_trades(
445
+ market=market, answer=answer, existing_position=existing_position
446
+ )
442
447
 
443
448
  if self.place_bet:
444
- logger.info(
445
- f"Placing bet on {market} with direction {amount_and_direction.direction} and amount {amount_and_direction.amount}"
446
- )
447
- market.place_bet(
448
- amount=TokenAmount(
449
- amount=amount_and_direction.amount,
450
- currency=amount_and_direction.currency,
451
- ),
452
- outcome=amount_and_direction.direction,
453
- )
449
+ for trade in trades:
450
+ logger.info(f"Executing trade {trade}")
451
+
452
+ match trade.trade_type:
453
+ case TradeType.BUY:
454
+ market.buy_tokens(outcome=trade.outcome, amount=trade.amount)
455
+ case TradeType.SELL:
456
+ market.sell_tokens(outcome=trade.outcome, amount=trade.amount)
457
+ case _:
458
+ raise ValueError(f"Unexpected trade type {trade.trade_type}.")
454
459
 
455
460
  self.after_process_market(market_type, market)
456
461
 
457
- processed_market = ProcessedMarket(
458
- answer=answer,
459
- amount=amount_and_direction,
460
- )
462
+ processed_market = ProcessedMarket(answer=answer, trades=trades)
461
463
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
462
464
 
463
465
  return processed_market
@@ -0,0 +1,187 @@
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
+ Currency,
6
+ Position,
7
+ ProbabilisticAnswer,
8
+ TokenAmount,
9
+ Trade,
10
+ TradeType,
11
+ )
12
+ from prediction_market_agent_tooling.markets.omen.data_models import get_boolean_outcome
13
+ from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
14
+ get_kelly_bet,
15
+ )
16
+
17
+
18
+ class BettingStrategy(ABC):
19
+ @abstractmethod
20
+ def calculate_trades(
21
+ self,
22
+ existing_position: Position | None,
23
+ answer: ProbabilisticAnswer,
24
+ market: AgentMarket,
25
+ ) -> list[Trade]:
26
+ pass
27
+
28
+ def build_zero_token_amount(self, currency: Currency) -> TokenAmount:
29
+ return TokenAmount(amount=0, currency=currency)
30
+
31
+ @abstractmethod
32
+ def adjust_bet_amount(
33
+ self, existing_position: Position | None, market: AgentMarket
34
+ ) -> float:
35
+ pass
36
+
37
+ @staticmethod
38
+ def assert_trades_currency_match_markets(
39
+ market: AgentMarket, trades: list[Trade]
40
+ ) -> None:
41
+ currencies_match = all([t.amount.currency == market.currency for t in trades])
42
+ if not currencies_match:
43
+ raise ValueError(
44
+ "Cannot handle trades with currencies that deviate from market's currency"
45
+ )
46
+
47
+ def _build_rebalance_trades_from_positions(
48
+ self,
49
+ existing_position: Position | None,
50
+ target_position: Position,
51
+ market: AgentMarket,
52
+ ) -> list[Trade]:
53
+ """
54
+ This helper method builds trades by rebalancing token allocations to each outcome.
55
+ For example, if we have an existing position with 10 tokens in outcome 0 and 5 in outcome 1,
56
+ and our target position is 20 tokens in outcome 0 and 0 in outcome 1, we would return these trades:
57
+ trades = [
58
+ Trade(outcome=0, amount=10, trade_type=TradeType.BUY),
59
+ Trade(outcome=1, amount=5, trade_type=TradeType.SELL)
60
+ ]
61
+ Note that we order the trades to first buy then sell, in order to minimally tilt the odds so that
62
+ sell price is higher.
63
+ """
64
+ trades = []
65
+ for outcome in [
66
+ market.get_outcome_str_from_bool(True),
67
+ market.get_outcome_str_from_bool(False),
68
+ ]:
69
+ outcome_bool = get_boolean_outcome(outcome)
70
+ prev_amount: TokenAmount = (
71
+ existing_position.amounts[outcome]
72
+ if existing_position and outcome in existing_position.amounts
73
+ else self.build_zero_token_amount(currency=market.currency)
74
+ )
75
+ new_amount: TokenAmount = target_position.amounts.get(
76
+ outcome, self.build_zero_token_amount(currency=market.currency)
77
+ )
78
+
79
+ if prev_amount.currency != new_amount.currency:
80
+ raise ValueError("Cannot handle positions with different currencies")
81
+ diff_amount = new_amount.amount - prev_amount.amount
82
+ if diff_amount == 0:
83
+ continue
84
+ trade_type = TradeType.SELL if diff_amount < 0 else TradeType.BUY
85
+ trade = Trade(
86
+ amount=TokenAmount(amount=abs(diff_amount), currency=market.currency),
87
+ outcome=outcome_bool,
88
+ trade_type=trade_type,
89
+ )
90
+
91
+ trades.append(trade)
92
+
93
+ # Sort inplace with SELL last
94
+ trades.sort(key=lambda t: t.trade_type == TradeType.SELL)
95
+ BettingStrategy.assert_trades_currency_match_markets(market, trades)
96
+ return trades
97
+
98
+
99
+ class MaxAccuracyBettingStrategy(BettingStrategy):
100
+ def adjust_bet_amount(
101
+ self, existing_position: Position | None, market: AgentMarket
102
+ ) -> float:
103
+ existing_position_total_amount = (
104
+ existing_position.total_amount.amount if existing_position else 0
105
+ )
106
+ bet_amount = (
107
+ market.get_tiny_bet_amount().amount
108
+ if self.bet_amount is None
109
+ else self.bet_amount
110
+ )
111
+ return bet_amount + existing_position_total_amount
112
+
113
+ def __init__(self, bet_amount: float | None = None):
114
+ self.bet_amount = bet_amount
115
+
116
+ def calculate_trades(
117
+ self,
118
+ existing_position: Position | None,
119
+ answer: ProbabilisticAnswer,
120
+ market: AgentMarket,
121
+ ) -> list[Trade]:
122
+ adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
123
+
124
+ direction = self.calculate_direction(market.current_p_yes, answer.p_yes)
125
+
126
+ amounts = {
127
+ market.get_outcome_str_from_bool(direction): TokenAmount(
128
+ amount=adjusted_bet_amount,
129
+ currency=market.currency,
130
+ ),
131
+ }
132
+ target_position = Position(market_id=market.id, amounts=amounts)
133
+ trades = self._build_rebalance_trades_from_positions(
134
+ existing_position, target_position, market=market
135
+ )
136
+ return trades
137
+
138
+ @staticmethod
139
+ def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:
140
+ return estimate_p_yes >= 0.5
141
+
142
+
143
+ class MaxExpectedValueBettingStrategy(MaxAccuracyBettingStrategy):
144
+ @staticmethod
145
+ def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:
146
+ # If estimate_p_yes >= market.current_p_yes, then bet TRUE, else bet FALSE.
147
+ # This is equivalent to saying EXPECTED_VALUE = (estimate_p_yes * num_tokens_obtained_by_betting_yes) -
148
+ # ((1 - estimate_p_yes) * num_tokens_obtained_by_betting_no) >= 0
149
+ return estimate_p_yes >= market_p_yes
150
+
151
+
152
+ class KellyBettingStrategy(BettingStrategy):
153
+ def __init__(self, max_bet_amount: float = 10):
154
+ self.max_bet_amount = max_bet_amount
155
+
156
+ def adjust_bet_amount(
157
+ self, existing_position: Position | None, market: AgentMarket
158
+ ) -> float:
159
+ existing_position_total_amount = (
160
+ existing_position.total_amount.amount if existing_position else 0
161
+ )
162
+ return self.max_bet_amount + existing_position_total_amount
163
+
164
+ def calculate_trades(
165
+ self,
166
+ existing_position: Position | None,
167
+ answer: ProbabilisticAnswer,
168
+ market: AgentMarket,
169
+ ) -> list[Trade]:
170
+ 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,
176
+ )
177
+
178
+ amounts = {
179
+ market.get_outcome_str_from_bool(kelly_bet.direction): TokenAmount(
180
+ amount=kelly_bet.size, currency=market.currency
181
+ ),
182
+ }
183
+ target_position = Position(market_id=market.id, amounts=amounts)
184
+ trades = self._build_rebalance_trades_from_positions(
185
+ existing_position, target_position, market=market
186
+ )
187
+ return trades
@@ -4,9 +4,10 @@ from enum import Enum
4
4
 
5
5
  from eth_typing import ChecksumAddress
6
6
  from pydantic import BaseModel, field_validator
7
+ from pydantic_core.core_schema import FieldValidationInfo
7
8
 
8
9
  from prediction_market_agent_tooling.config import APIKeys
9
- from prediction_market_agent_tooling.gtypes import Probability
10
+ from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
10
11
  from prediction_market_agent_tooling.markets.data_models import (
11
12
  Bet,
12
13
  BetAmount,
@@ -49,6 +50,9 @@ class AgentMarket(BaseModel):
49
50
  question: str
50
51
  description: str | None
51
52
  outcomes: list[str]
53
+ outcome_token_pool: dict[
54
+ str, float
55
+ ] | None # Should be in currency of `currency` above.
52
56
  resolution: Resolution | None
53
57
  created_time: datetime | None
54
58
  close_time: datetime | None
@@ -63,6 +67,22 @@ class AgentMarket(BaseModel):
63
67
  add_utc_timezone_validator
64
68
  )
65
69
 
70
+ @field_validator("outcome_token_pool")
71
+ def validate_outcome_token_pool(
72
+ cls,
73
+ outcome_token_pool: dict[str, float] | None,
74
+ info: FieldValidationInfo,
75
+ ) -> dict[str, float] | None:
76
+ outcomes: list[str] = check_not_none(info.data.get("outcomes"))
77
+ if outcome_token_pool is not None:
78
+ outcome_keys = set(outcome_token_pool.keys())
79
+ expected_keys = set(outcomes)
80
+ if outcome_keys != expected_keys:
81
+ raise ValueError(
82
+ f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
83
+ )
84
+ return outcome_token_pool
85
+
66
86
  @property
67
87
  def current_p_no(self) -> Probability:
68
88
  return Probability(1 - self.current_p_yes)
@@ -133,10 +153,19 @@ class AgentMarket(BaseModel):
133
153
  def get_bet_amount(self, amount: float) -> BetAmount:
134
154
  return BetAmount(amount=amount, currency=self.currency)
135
155
 
156
+ @classmethod
157
+ def get_liquidatable_amount(cls) -> BetAmount:
158
+ tiny_amount = cls.get_tiny_bet_amount()
159
+ tiny_amount.amount /= 10
160
+ return tiny_amount
161
+
136
162
  @classmethod
137
163
  def get_tiny_bet_amount(cls) -> BetAmount:
138
164
  raise NotImplementedError("Subclasses must implement this method")
139
165
 
166
+ def liquidate_existing_positions(self, outcome: bool) -> None:
167
+ raise NotImplementedError("Subclasses must implement this method")
168
+
140
169
  def place_bet(self, outcome: bool, amount: BetAmount) -> None:
141
170
  raise NotImplementedError("Subclasses must implement this method")
142
171
 
@@ -190,6 +219,10 @@ class AgentMarket(BaseModel):
190
219
  def has_unsuccessful_resolution(self) -> bool:
191
220
  return self.resolution in [Resolution.CANCEL, Resolution.MKT]
192
221
 
222
+ @staticmethod
223
+ def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
224
+ raise NotImplementedError("Subclasses must implement this method")
225
+
193
226
  def get_outcome_str(self, outcome_index: int) -> str:
194
227
  try:
195
228
  return self.outcomes[outcome_index]
@@ -207,6 +240,9 @@ class AgentMarket(BaseModel):
207
240
  def get_token_balance(self, user_id: str, outcome: str) -> TokenAmount:
208
241
  raise NotImplementedError("Subclasses must implement this method")
209
242
 
243
+ def get_position(self, user_id: str) -> Position | None:
244
+ raise NotImplementedError("Subclasses must implement this method")
245
+
210
246
  @classmethod
211
247
  def get_positions(
212
248
  cls, user_id: str, liquid_only: bool = False, larger_than: float = 0
@@ -236,3 +272,12 @@ class AgentMarket(BaseModel):
236
272
  @classmethod
237
273
  def get_user_url(cls, keys: APIKeys) -> str:
238
274
  raise NotImplementedError("Subclasses must implement this method")
275
+
276
+ def has_token_pool(self) -> bool:
277
+ return self.outcome_token_pool is not None
278
+
279
+ def get_pool_tokens(self, outcome: str) -> float:
280
+ if not self.outcome_token_pool:
281
+ raise ValueError("Outcome token pool is not available.")
282
+
283
+ return self.outcome_token_pool[outcome]
@@ -115,3 +115,14 @@ class Position(BaseModel):
115
115
  for outcome, amount in self.amounts.items()
116
116
  )
117
117
  return f"Position for market id {self.market_id}: {amounts_str}"
118
+
119
+
120
+ class TradeType(str, Enum):
121
+ SELL = "sell"
122
+ BUY = "buy"
123
+
124
+
125
+ class Trade(BaseModel):
126
+ trade_type: TradeType
127
+ outcome: bool
128
+ amount: TokenAmount
@@ -19,6 +19,12 @@ class ManifoldPool(BaseModel):
19
19
  NO: float
20
20
  YES: float
21
21
 
22
+ def size_for_outcome(self, outcome: str) -> float:
23
+ if hasattr(self, outcome):
24
+ return float(getattr(self, outcome))
25
+ else:
26
+ should_not_happen(f"Unexpected outcome string, '{outcome}'.")
27
+
22
28
 
23
29
  class ManifoldAnswersMode(str, Enum):
24
30
  ANYONE = "ANYONE"
@@ -72,6 +72,9 @@ class ManifoldAgentMarket(AgentMarket):
72
72
  current_p_yes=model.probability,
73
73
  url=model.url,
74
74
  volume=model.volume,
75
+ outcome_token_pool={
76
+ o: model.pool.size_for_outcome(o) for o in model.outcomes
77
+ },
75
78
  )
76
79
 
77
80
  @staticmethod
@@ -43,6 +43,7 @@ class MetaculusAgentMarket(AgentMarket):
43
43
  volume=None,
44
44
  have_predicted=model.my_predictions is not None
45
45
  and len(model.my_predictions.predictions) > 0,
46
+ outcome_token_pool=None,
46
47
  )
47
48
 
48
49
  @staticmethod
@@ -44,6 +44,10 @@ def get_boolean_outcome(outcome_str: str) -> bool:
44
44
  raise ValueError(f"Outcome `{outcome_str}` is not a valid boolean outcome.")
45
45
 
46
46
 
47
+ def get_bet_outcome(binary_outcome: bool) -> str:
48
+ return OMEN_TRUE_OUTCOME if binary_outcome else OMEN_FALSE_OUTCOME
49
+
50
+
47
51
  class Condition(BaseModel):
48
52
  id: HexBytes
49
53
  outcomeSlotCount: int
@@ -40,6 +40,8 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
40
40
  OmenBet,
41
41
  OmenMarket,
42
42
  OmenUserPosition,
43
+ get_bet_outcome,
44
+ get_boolean_outcome,
43
45
  )
44
46
  from prediction_market_agent_tooling.markets.omen.omen_contracts import (
45
47
  OMEN_DEFAULT_MARKET_FEE,
@@ -75,6 +77,7 @@ from prediction_market_agent_tooling.tools.web3_utils import (
75
77
  )
76
78
 
77
79
  OMEN_DEFAULT_REALITIO_BOND_VALUE = xdai_type(0.01)
80
+ OMEN_TINY_BET_AMOUNT = xdai_type(0.00001)
78
81
 
79
82
 
80
83
  class OmenAgentMarket(AgentMarket):
@@ -147,7 +150,41 @@ class OmenAgentMarket(AgentMarket):
147
150
 
148
151
  @classmethod
149
152
  def get_tiny_bet_amount(cls) -> BetAmount:
150
- return BetAmount(amount=0.00001, currency=cls.currency)
153
+ return BetAmount(amount=OMEN_TINY_BET_AMOUNT, currency=cls.currency)
154
+
155
+ def liquidate_existing_positions(
156
+ self,
157
+ bet_outcome: bool,
158
+ web3: Web3 | None = None,
159
+ api_keys: APIKeys | None = None,
160
+ larger_than: float | None = None,
161
+ ) -> None:
162
+ """
163
+ Liquidates all previously existing positions.
164
+ Returns the amount in collateral obtained by selling the positions.
165
+ """
166
+ api_keys = api_keys if api_keys is not None else APIKeys()
167
+ better_address = api_keys.bet_from_address
168
+ larger_than = (
169
+ larger_than
170
+ if larger_than is not None
171
+ else self.get_liquidatable_amount().amount
172
+ )
173
+ prev_positions_for_market = self.get_positions(
174
+ user_id=better_address, liquid_only=True, larger_than=larger_than
175
+ )
176
+
177
+ for prev_position in prev_positions_for_market:
178
+ for position_outcome, token_amount in prev_position.amounts.items():
179
+ position_outcome_bool = get_boolean_outcome(position_outcome)
180
+ if position_outcome_bool != bet_outcome:
181
+ # We keep it as collateral since we want to place a bet immediately after this function.
182
+ self.sell_tokens(
183
+ outcome=position_outcome_bool,
184
+ amount=token_amount,
185
+ auto_withdraw=False,
186
+ web3=web3,
187
+ )
151
188
 
152
189
  def place_bet(
153
190
  self,
@@ -310,6 +347,10 @@ class OmenAgentMarket(AgentMarket):
310
347
  volume=wei_to_xdai(model.collateralVolume),
311
348
  close_time=model.close_time,
312
349
  fee=float(wei_to_xdai(model.fee)) if model.fee is not None else 0.0,
350
+ outcome_token_pool={
351
+ model.outcomes[i]: wei_to_xdai(Wei(model.outcomeTokenAmounts[i]))
352
+ for i in range(len(model.outcomes))
353
+ },
313
354
  )
314
355
 
315
356
  @staticmethod
@@ -381,6 +422,12 @@ class OmenAgentMarket(AgentMarket):
381
422
  cls.get_outcome_str(cls.index_set_to_outcome_index(index_set))
382
423
  )
383
424
 
425
+ @staticmethod
426
+ def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
427
+ return (
428
+ OutcomeStr(OMEN_TRUE_OUTCOME) if outcome else OutcomeStr(OMEN_FALSE_OUTCOME)
429
+ )
430
+
384
431
  def get_token_balance(
385
432
  self, user_id: str, outcome: str, web3: Web3 | None = None
386
433
  ) -> TokenAmount:
@@ -393,6 +440,18 @@ class OmenAgentMarket(AgentMarket):
393
440
  currency=self.currency,
394
441
  )
395
442
 
443
+ def get_position(self, user_id: str) -> Position | None:
444
+ liquidatable_amount = self.get_liquidatable_amount()
445
+ existing_positions = self.get_positions(
446
+ user_id=user_id,
447
+ liquid_only=True,
448
+ larger_than=liquidatable_amount.amount,
449
+ )
450
+ existing_position = next(
451
+ iter([i for i in existing_positions if i.market_id == self.id]), None
452
+ )
453
+ return existing_position
454
+
396
455
  @classmethod
397
456
  def get_positions(
398
457
  cls,
@@ -553,12 +612,15 @@ def omen_buy_outcome_tx(
553
612
  market_contract: OmenFixedProductMarketMakerContract = market.get_contract()
554
613
  collateral_token_contract = market_contract.get_collateral_token_contract()
555
614
 
615
+ # In case of ERC4626, obtained (for example) sDai out of xDai could be lower than the `amount_wei`, so we need to handle it.
616
+ amount_wei_to_buy = collateral_token_contract.get_in_shares(amount_wei, web3)
617
+
556
618
  # Get the index of the outcome we want to buy.
557
619
  outcome_index: int = market.get_outcome_index(outcome)
558
620
 
559
621
  # Calculate the amount of shares we will get for the given investment amount.
560
622
  expected_shares = market_contract.calcBuyAmount(
561
- amount_wei, outcome_index, web3=web3
623
+ amount_wei_to_buy, outcome_index, web3=web3
562
624
  )
563
625
  # Allow 1% slippage.
564
626
  expected_shares = remove_fraction(expected_shares, 0.01)
@@ -566,11 +628,12 @@ def omen_buy_outcome_tx(
566
628
  collateral_token_contract.approve(
567
629
  api_keys=api_keys,
568
630
  for_address=market_contract.address,
569
- amount_wei=amount_wei,
631
+ amount_wei=amount_wei_to_buy,
570
632
  web3=web3,
571
633
  )
572
634
 
573
635
  if auto_deposit:
636
+ # In auto-depositing, we need to deposit the original `amount_wei`, e.g. we can deposit 2 xDai, but receive 1.8 sDai, so for the bet we will use `amount_wei_to_buy`.
574
637
  auto_deposit_collateral_token(
575
638
  collateral_token_contract, amount_wei, api_keys, web3
576
639
  )
@@ -578,7 +641,7 @@ def omen_buy_outcome_tx(
578
641
  # Buy shares using the deposited xDai in the collateral token.
579
642
  market_contract.buy(
580
643
  api_keys=api_keys,
581
- amount_wei=amount_wei,
644
+ amount_wei=amount_wei_to_buy,
582
645
  outcome_index=outcome_index,
583
646
  min_outcome_tokens_to_buy=expected_shares,
584
647
  web3=web3,
@@ -597,7 +660,7 @@ def binary_omen_buy_outcome_tx(
597
660
  api_keys=api_keys,
598
661
  amount=amount,
599
662
  market=market,
600
- outcome=OMEN_TRUE_OUTCOME if binary_outcome else OMEN_FALSE_OUTCOME,
663
+ outcome=get_bet_outcome(binary_outcome),
601
664
  auto_deposit=auto_deposit,
602
665
  web3=web3,
603
666
  )
@@ -686,7 +749,7 @@ def binary_omen_sell_outcome_tx(
686
749
  api_keys=api_keys,
687
750
  amount=amount,
688
751
  market=market,
689
- outcome=OMEN_TRUE_OUTCOME if binary_outcome else OMEN_FALSE_OUTCOME,
752
+ outcome=get_bet_outcome(binary_outcome),
690
753
  auto_withdraw=auto_withdraw,
691
754
  web3=web3,
692
755
  )
@@ -769,6 +832,7 @@ def omen_create_market_tx(
769
832
  web3=web3,
770
833
  )
771
834
 
835
+ # In case of ERC4626, obtained (for example) sDai out of xDai could be lower than the `amount_wei`, so we need to handle it.
772
836
  initial_funds_in_shares = collateral_token_contract.get_in_shares(
773
837
  amount=initial_funds_wei, web3=web3
774
838
  )
@@ -4,13 +4,18 @@ from datetime import datetime
4
4
 
5
5
  import requests
6
6
  import tenacity
7
- from eth_typing import ChecksumAddress
8
7
  from PIL import Image
9
8
  from PIL.Image import Image as ImageType
10
9
  from subgrounds import FieldPath, Subgrounds
11
10
 
12
11
  from prediction_market_agent_tooling.config import APIKeys
13
- from prediction_market_agent_tooling.gtypes import HexAddress, HexBytes, Wei, wei_type
12
+ from prediction_market_agent_tooling.gtypes import (
13
+ ChecksumAddress,
14
+ HexAddress,
15
+ HexBytes,
16
+ Wei,
17
+ wei_type,
18
+ )
14
19
  from prediction_market_agent_tooling.loggers import logger
15
20
  from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
16
21
  from prediction_market_agent_tooling.markets.omen.data_models import (
@@ -36,6 +41,12 @@ from prediction_market_agent_tooling.tools.web3_utils import (
36
41
  byte32_to_ipfscidv0,
37
42
  )
38
43
 
44
+ # TODO: Agents don't know how to convert value between other tokens, we assume 1 unit = 1xDai = $1 (for example if market would be in wETH, betting 1 unit of wETH would be crazy :D)
45
+ SAFE_COLLATERAL_TOKEN_MARKETS = (
46
+ WrappedxDaiContract().address,
47
+ sDaiContract().address,
48
+ )
49
+
39
50
 
40
51
  class OmenSubgraphHandler(metaclass=SingletonMeta):
41
52
  """
@@ -313,6 +324,8 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
313
324
  # Additional filters, these can not be modified by the enums above.
314
325
  created_after: datetime | None = None,
315
326
  excluded_questions: set[str] | None = None, # question titles
327
+ collateral_token_address_in: tuple[ChecksumAddress, ...]
328
+ | None = SAFE_COLLATERAL_TOKEN_MARKETS,
316
329
  ) -> t.List[OmenMarket]:
317
330
  """
318
331
  Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
@@ -349,6 +362,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
349
362
  sort_by_field=sort_by_field,
350
363
  created_after=created_after,
351
364
  excluded_questions=excluded_questions,
365
+ collateral_token_address_in=collateral_token_address_in,
352
366
  )
353
367
 
354
368
  def get_omen_binary_markets(
@@ -370,12 +384,8 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
370
384
  sort_by_field: FieldPath | None = None,
371
385
  sort_direction: str | None = None,
372
386
  outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
373
- # TODO: Agents don't know how to convert value between other tokens, we assume 1 unit = 1xDai = $1 (for example if market would be in wETH, betting 1 unit of wETH would be crazy :D)
374
387
  collateral_token_address_in: tuple[ChecksumAddress, ...]
375
- | None = (
376
- WrappedxDaiContract().address,
377
- sDaiContract().address,
378
- ),
388
+ | None = SAFE_COLLATERAL_TOKEN_MARKETS,
379
389
  ) -> t.List[OmenMarket]:
380
390
  """
381
391
  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.
@@ -756,7 +766,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
756
766
  def get_market_image(self, market_id: HexAddress) -> ImageType | None:
757
767
  image_url = self.get_market_image_url(market_id)
758
768
  return (
759
- Image.open(requests.get(image_url, stream=True).raw)
769
+ Image.open(requests.get(image_url, stream=True).raw) # type: ignore[arg-type]
760
770
  if image_url is not None
761
771
  else None
762
772
  )
@@ -39,6 +39,7 @@ class PolymarketAgentMarket(AgentMarket):
39
39
  close_time=model.end_date_iso,
40
40
  url=model.url,
41
41
  volume=None,
42
+ outcome_token_pool=None,
42
43
  )
43
44
 
44
45
  @classmethod
@@ -351,7 +351,7 @@ class ContractERC4626BaseClass(ContractERC20BaseClass):
351
351
  web3 = web3 or self.get_web3()
352
352
  contract = init_collateral_token_contract(self.asset(), web3=web3)
353
353
  assert not isinstance(
354
- contract, ContractERC4626OnGnosisChain
354
+ contract, ContractERC4626BaseClass
355
355
  ), "Asset token should be either Depositable Wrapper ERC-20 or ERC-20." # Shrinking down possible types.
356
356
  return contract
357
357
 
@@ -417,6 +417,11 @@ class ContractERC4626OnGnosisChain(
417
417
  ERC-4626 standard base class with Gnosis Chain configuration.
418
418
  """
419
419
 
420
+ def get_asset_token_contract(
421
+ self, web3: Web3 | None = None
422
+ ) -> ContractERC20OnGnosisChain | ContractDepositableWrapperERC20OnGnosisChain:
423
+ return to_gnosis_chain_contract(super().get_asset_token_contract(web3=web3))
424
+
420
425
 
421
426
  def contract_implements_function(
422
427
  contract_address: ChecksumAddress,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "prediction-market-agent-tooling"
3
- version = "0.48.11"
3
+ version = "0.48.13"
4
4
  description = "Tools to benchmark, deploy and monitor prediction market agents."
5
5
  authors = ["Gnosis"]
6
6
  readme = "README.md"
@@ -20,7 +20,7 @@ cron-validator = "^1.0.8"
20
20
  pydantic = "^2.6.1"
21
21
  web3 = "^6.15.1"
22
22
  eth-typing = "^3.0.0"
23
- pydantic-settings = "^2.1.0"
23
+ pydantic-settings = "^2.4.0" #eth-ape limit
24
24
  numpy = "^1.26.4"
25
25
  autoflake = "^2.2.1"
26
26
  isort = "^5.13.2"
@@ -1,62 +0,0 @@
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
- )