prediction-market-agent-tooling 0.57.1__tar.gz → 0.57.3__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 (110) hide show
  1. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/PKG-INFO +3 -1
  2. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/base_subgraph_handler.py +2 -2
  3. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/data_models.py +14 -1
  4. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/omen/data_models.py +25 -7
  5. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/omen/omen.py +11 -2
  6. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +59 -9
  7. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +5 -3
  8. prediction_market_agent_tooling-0.57.3/prediction_market_agent_tooling/tools/caches/inmemory_cache.py +57 -0
  9. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/httpx_cached_client.py +4 -1
  10. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/langfuse_client_utils.py +2 -0
  11. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/transaction_cache.py +2 -2
  12. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/pyproject.toml +3 -1
  13. prediction_market_agent_tooling-0.57.1/prediction_market_agent_tooling/tools/caches/inmemory_cache.py +0 -17
  14. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/LICENSE +0 -0
  15. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/README.md +0 -0
  16. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/debuggingcontract.abi.json +0 -0
  17. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
  18. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
  19. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
  20. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_agentresultmapping.abi.json +0 -0
  21. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
  22. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
  23. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
  24. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
  25. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
  26. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
  27. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
  28. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
  29. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
  30. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
  31. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/benchmark/agents.py +0 -0
  32. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
  33. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/benchmark/utils.py +0 -0
  34. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/config.py +0 -0
  35. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/agent.py +0 -0
  36. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/agent_example.py +0 -0
  37. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/betting_strategy.py +0 -0
  38. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/constants.py +0 -0
  39. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
  40. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
  41. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
  42. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/deploy/trade_interval.py +0 -0
  43. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/gtypes.py +0 -0
  44. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/jobs/__init__.py +0 -0
  45. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/jobs/jobs_models.py +0 -0
  46. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/jobs/omen/omen_jobs.py +0 -0
  47. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/loggers.py +0 -0
  48. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/agent_market.py +0 -0
  49. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/categorize.py +0 -0
  50. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/manifold/__init__.py +0 -0
  51. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
  52. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
  53. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/manifold/manifold.py +0 -0
  54. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
  55. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/market_fees.py +0 -0
  56. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/markets.py +0 -0
  57. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
  58. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/metaculus/data_models.py +0 -0
  59. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +0 -0
  60. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
  61. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +0 -0
  62. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +0 -0
  63. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
  64. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
  65. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
  66. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
  67. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
  68. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/seer/data_models.py +0 -0
  69. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +0 -0
  70. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
  71. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
  72. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
  73. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
  74. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/monitor.py +0 -0
  75. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/monitor_app.py +0 -0
  76. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
  77. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/py.typed +0 -0
  78. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/balances.py +0 -0
  79. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +0 -0
  80. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -0
  81. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
  82. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
  83. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/betting_strategies/utils.py +0 -0
  84. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/caches/db_cache.py +0 -0
  85. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/caches/serializers.py +0 -0
  86. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/contract.py +0 -0
  87. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/costs.py +0 -0
  88. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/custom_exceptions.py +0 -0
  89. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/datetime_utc.py +0 -0
  90. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/db/db_manager.py +0 -0
  91. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/google_utils.py +0 -0
  92. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
  93. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
  94. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
  95. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py +0 -0
  96. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/is_invalid.py +0 -0
  97. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/is_predictable.py +0 -0
  98. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
  99. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/omen/reality_accuracy.py +0 -0
  100. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
  101. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/relevant_news_analysis/data_models.py +0 -0
  102. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_analysis.py +0 -0
  103. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py +0 -0
  104. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/safe.py +0 -0
  105. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/singleton.py +0 -0
  106. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
  107. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/tavily/tavily_models.py +0 -0
  108. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/tavily/tavily_search.py +0 -0
  109. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/prediction_market_agent_tooling/tools/utils.py +0 -0
  110. {prediction_market_agent_tooling-0.57.1 → prediction_market_agent_tooling-0.57.3}/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.57.1
3
+ Version: 0.57.3
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Provides-Extra: google
11
11
  Provides-Extra: langchain
12
12
  Provides-Extra: openai
13
+ Provides-Extra: optuna
13
14
  Requires-Dist: autoflake (>=2.2.1,<3.0.0)
14
15
  Requires-Dist: base58 (>=1.0.2,<2.0)
15
16
  Requires-Dist: cron-validator (>=1.0.8,<2.0.0)
@@ -30,6 +31,7 @@ Requires-Dist: loguru (>=0.7.2,<0.8.0)
30
31
  Requires-Dist: loky (>=3.4.1,<4.0.0)
31
32
  Requires-Dist: numpy (>=1.26.4,<2.0.0)
32
33
  Requires-Dist: openai (>=1.0.0,<2.0.0) ; extra == "openai"
34
+ Requires-Dist: optuna (>=4.1.0,<5.0.0) ; extra == "optuna"
33
35
  Requires-Dist: pinatapy-vourhey (>=0.2.0,<0.3.0)
34
36
  Requires-Dist: prompt-toolkit (>=3.0.43,<4.0.0)
35
37
  Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
@@ -12,8 +12,8 @@ T = t.TypeVar("T", bound=BaseModel)
12
12
 
13
13
 
14
14
  class BaseSubgraphHandler(metaclass=SingletonMeta):
15
- def __init__(self) -> None:
16
- self.sg = Subgrounds()
15
+ def __init__(self, timeout: int = 30) -> None:
16
+ self.sg = Subgrounds(timeout=timeout)
17
17
  # Patch methods to retry on failure.
18
18
  self.sg.query_json = tenacity.retry(
19
19
  stop=tenacity.stop_after_attempt(3),
@@ -142,7 +142,7 @@ class PlacedTrade(Trade):
142
142
  )
143
143
 
144
144
 
145
- class SimulationDetail(BaseModel):
145
+ class SimulatedBetDetail(BaseModel):
146
146
  strategy: str
147
147
  url: str
148
148
  market_p_yes: float
@@ -161,3 +161,16 @@ class SharpeOutput(BaseModel):
161
161
  annualized_volatility: float
162
162
  mean_daily_return: float
163
163
  annualized_sharpe_ratio: float
164
+
165
+
166
+ class SimulatedLifetimeDetail(BaseModel):
167
+ p_yes_mse: float
168
+ total_bet_amount: float
169
+ total_bet_profit: float
170
+ total_simulated_amount: float
171
+ total_simulated_profit: float
172
+ roi: float
173
+ simulated_roi: float
174
+ sharpe_output_original: SharpeOutput
175
+ sharpe_output_simulation: SharpeOutput
176
+ maximize: float
@@ -42,6 +42,10 @@ PRESAGIO_BASE_URL = "https://presagio.pages.dev"
42
42
  TEST_CATEGORY = "test" # This category is hidden on Presagio for testing purposes.
43
43
 
44
44
 
45
+ def construct_presagio_url(market_id: HexAddress) -> str:
46
+ return f"{PRESAGIO_BASE_URL}/markets?id={market_id}"
47
+
48
+
45
49
  def get_boolean_outcome(outcome_str: str) -> bool:
46
50
  if outcome_str == OMEN_TRUE_OUTCOME:
47
51
  return True
@@ -392,7 +396,7 @@ class OmenMarket(BaseModel):
392
396
 
393
397
  @property
394
398
  def url(self) -> str:
395
- return f"{PRESAGIO_BASE_URL}/markets?id={self.id}"
399
+ return construct_presagio_url(self.id)
396
400
 
397
401
  @staticmethod
398
402
  def from_created_market(model: "CreatedMarket") -> "OmenMarket":
@@ -499,13 +503,17 @@ class OmenBet(BaseModel):
499
503
  creator: OmenBetCreator
500
504
  creationTimestamp: int
501
505
  collateralAmount: Wei
502
- collateralAmountUSD: USD
503
506
  feeAmount: Wei
504
507
  outcomeIndex: int
505
508
  outcomeTokensTraded: Wei
506
509
  transactionHash: HexBytes
507
510
  fpmm: OmenMarket
508
511
 
512
+ @property
513
+ def collateral_amount_usd(self) -> USD:
514
+ # Convert manually instad of using the field `collateralAmountUSD` available on the graph, because it's bugged, it's 0 for non-xDai markets.
515
+ return USD(wei_to_xdai(self.collateralAmount))
516
+
509
517
  @property
510
518
  def creation_datetime(self) -> DatetimeUTC:
511
519
  return DatetimeUTC.to_datetime_utc(self.creationTimestamp)
@@ -540,7 +548,7 @@ class OmenBet(BaseModel):
540
548
  return Bet(
541
549
  id=str(self.transactionHash),
542
550
  # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
543
- amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai),
551
+ amount=BetAmount(amount=self.collateral_amount_usd, currency=Currency.xDai),
544
552
  outcome=self.boolean_outcome,
545
553
  created_time=self.creation_datetime,
546
554
  market_question=self.title,
@@ -556,7 +564,7 @@ class OmenBet(BaseModel):
556
564
  return ResolvedBet(
557
565
  id=self.transactionHash.hex(),
558
566
  # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
559
- amount=BetAmount(amount=self.collateralAmountUSD, currency=Currency.xDai),
567
+ amount=BetAmount(amount=self.collateral_amount_usd, currency=Currency.xDai),
560
568
  outcome=self.boolean_outcome,
561
569
  created_time=self.creation_datetime,
562
570
  market_question=self.title,
@@ -586,7 +594,7 @@ class RealityQuestion(BaseModel):
586
594
  updatedTimestamp: int
587
595
  contentHash: HexBytes
588
596
  questionId: HexBytes # This is the `id` on question from omen subgraph.
589
- answerFinalizedTimestamp: int
597
+ answerFinalizedTimestamp: int | None
590
598
  currentScheduledFinalizationTimestamp: int
591
599
 
592
600
  @property
@@ -594,8 +602,12 @@ class RealityQuestion(BaseModel):
594
602
  return DatetimeUTC.to_datetime_utc(self.updatedTimestamp)
595
603
 
596
604
  @property
597
- def answer_finalized_datetime(self) -> DatetimeUTC:
598
- return DatetimeUTC.to_datetime_utc(self.answerFinalizedTimestamp)
605
+ def answer_finalized_datetime(self) -> DatetimeUTC | None:
606
+ return (
607
+ DatetimeUTC.to_datetime_utc(self.answerFinalizedTimestamp)
608
+ if self.answerFinalizedTimestamp is not None
609
+ else None
610
+ )
599
611
 
600
612
  @property
601
613
  def current_scheduled_finalization_datetime(self) -> DatetimeUTC:
@@ -775,6 +787,12 @@ class CreatedMarket(BaseModel):
775
787
  fee: Wei
776
788
  distribution_hint: list[OmenOutcomeToken] | None
777
789
 
790
+ @property
791
+ def url(self) -> str:
792
+ return construct_presagio_url(
793
+ self.market_event.fixed_product_market_maker_checksummed
794
+ )
795
+
778
796
 
779
797
  class ContractPrediction(BaseModel):
780
798
  model_config = ConfigDict(populate_by_name=True)
@@ -480,6 +480,8 @@ class OmenAgentMarket(AgentMarket):
480
480
  better_address: ChecksumAddress,
481
481
  start_time: DatetimeUTC,
482
482
  end_time: DatetimeUTC | None,
483
+ market_resolved_before: DatetimeUTC | None = None,
484
+ market_resolved_after: DatetimeUTC | None = None,
483
485
  ) -> list[ResolvedBet]:
484
486
  subgraph_handler = OmenSubgraphHandler()
485
487
  bets = subgraph_handler.get_resolved_bets_with_valid_answer(
@@ -487,6 +489,8 @@ class OmenAgentMarket(AgentMarket):
487
489
  start_time=start_time,
488
490
  end_time=end_time,
489
491
  market_id=None,
492
+ market_resolved_before=market_resolved_before,
493
+ market_resolved_after=market_resolved_after,
490
494
  )
491
495
  generic_bets = [b.to_generic_resolved_bet() for b in bets]
492
496
  return generic_bets
@@ -561,13 +565,18 @@ class OmenAgentMarket(AgentMarket):
561
565
  omen_markets: dict[HexBytes, OmenMarket] = {
562
566
  m.condition.id: m
563
567
  for m in sgh.get_omen_binary_markets(
564
- limit=sys.maxsize,
568
+ limit=None,
565
569
  condition_id_in=list(omen_positions_dict.keys()),
566
570
  )
567
571
  }
572
+
568
573
  if len(omen_markets) != len(omen_positions_dict):
574
+ missing_conditions_ids = set(
575
+ omen_position.position.condition_id for omen_position in omen_positions
576
+ ) - set(market.condition.id for market in omen_markets.values())
569
577
  raise ValueError(
570
- f"Number of condition ids for markets {len(omen_markets)} and positions {len(omen_positions_dict)} are not equal."
578
+ f"Number of condition ids for markets {len(omen_markets)} and positions {len(omen_positions_dict)} are not equal. "
579
+ f"Missing condition ids: {missing_conditions_ids}"
571
580
  )
572
581
 
573
582
  positions = []
@@ -33,6 +33,9 @@ from prediction_market_agent_tooling.markets.omen.omen_contracts import (
33
33
  WrappedxDaiContract,
34
34
  sDaiContract,
35
35
  )
36
+ from prediction_market_agent_tooling.tools.caches.inmemory_cache import (
37
+ persistent_inmemory_cache,
38
+ )
36
39
  from prediction_market_agent_tooling.tools.utils import (
37
40
  DatetimeUTC,
38
41
  to_int_timestamp,
@@ -112,7 +115,6 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
112
115
  bets_field.creator.id,
113
116
  bets_field.creationTimestamp,
114
117
  bets_field.collateralAmount,
115
- bets_field.collateralAmountUSD,
116
118
  bets_field.feeAmount,
117
119
  bets_field.outcomeIndex,
118
120
  bets_field.outcomeTokensTraded,
@@ -212,6 +214,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
212
214
  question_finalized_before: DatetimeUTC | None,
213
215
  question_finalized_after: DatetimeUTC | None,
214
216
  question_with_answers: bool | None,
217
+ question_pending_arbitration: bool | None,
215
218
  question_id: HexBytes | None,
216
219
  question_id_in: list[HexBytes] | None,
217
220
  question_current_answer_before: DatetimeUTC | None,
@@ -224,7 +227,6 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
224
227
  category: str | None,
225
228
  ) -> dict[str, t.Any]:
226
229
  where_stms: dict[str, t.Any] = {
227
- "isPendingArbitration": False,
228
230
  "outcomes": outcomes,
229
231
  "title_not": None,
230
232
  "condition_": {},
@@ -237,6 +239,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
237
239
  finalized_before=question_finalized_before,
238
240
  finalized_after=question_finalized_after,
239
241
  with_answers=question_with_answers,
242
+ pending_arbitration=question_pending_arbitration,
240
243
  current_answer_before=question_current_answer_before,
241
244
  question_id_in=question_id_in,
242
245
  excluded_titles=question_excluded_titles,
@@ -375,6 +378,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
375
378
  question_finalized_before: DatetimeUTC | None = None,
376
379
  question_finalized_after: DatetimeUTC | None = None,
377
380
  question_with_answers: bool | None = None,
381
+ question_pending_arbitration: bool | None = None,
378
382
  question_id: HexBytes | None = None,
379
383
  question_id_in: list[HexBytes] | None = None,
380
384
  question_current_answer_before: DatetimeUTC | None = None,
@@ -404,6 +408,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
404
408
  question_finalized_before=question_finalized_before,
405
409
  question_finalized_after=question_finalized_after,
406
410
  question_with_answers=question_with_answers,
411
+ question_pending_arbitration=question_pending_arbitration,
407
412
  question_id=question_id,
408
413
  question_id_in=question_id_in,
409
414
  question_current_answer_before=question_current_answer_before,
@@ -524,6 +529,8 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
524
529
  filter_by_answer_finalized_not_null: bool = False,
525
530
  type_: t.Literal["Buy", "Sell"] | None = None,
526
531
  market_opening_after: DatetimeUTC | None = None,
532
+ market_resolved_before: DatetimeUTC | None = None,
533
+ market_resolved_after: DatetimeUTC | None = None,
527
534
  collateral_amount_more_than: Wei | None = None,
528
535
  sort_by_field: FieldPath | None = None,
529
536
  sort_direction: str | None = None,
@@ -549,6 +556,15 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
549
556
  where_stms.append(
550
557
  trade.fpmm.openingTimestamp > to_int_timestamp(market_opening_after)
551
558
  )
559
+ if market_resolved_after is not None:
560
+ where_stms.append(
561
+ trade.fpmm.resolutionTimestamp > to_int_timestamp(market_resolved_after)
562
+ )
563
+ if market_resolved_before is not None:
564
+ where_stms.append(
565
+ trade.fpmm.resolutionTimestamp
566
+ < to_int_timestamp(market_resolved_before)
567
+ )
552
568
  if collateral_amount_more_than is not None:
553
569
  where_stms.append(trade.collateralAmount > collateral_amount_more_than)
554
570
 
@@ -577,6 +593,8 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
577
593
  market_id: t.Optional[ChecksumAddress] = None,
578
594
  filter_by_answer_finalized_not_null: bool = False,
579
595
  market_opening_after: DatetimeUTC | None = None,
596
+ market_resolved_before: DatetimeUTC | None = None,
597
+ market_resolved_after: DatetimeUTC | None = None,
580
598
  collateral_amount_more_than: Wei | None = None,
581
599
  ) -> list[OmenBet]:
582
600
  return self.get_trades(
@@ -587,15 +605,19 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
587
605
  filter_by_answer_finalized_not_null=filter_by_answer_finalized_not_null,
588
606
  type_="Buy", # We consider `bet` to be only the `Buy` trade types.
589
607
  market_opening_after=market_opening_after,
608
+ market_resolved_before=market_resolved_before,
609
+ market_resolved_after=market_resolved_after,
590
610
  collateral_amount_more_than=collateral_amount_more_than,
591
611
  )
592
612
 
593
613
  def get_resolved_bets(
594
614
  self,
595
615
  better_address: ChecksumAddress,
596
- start_time: DatetimeUTC,
616
+ start_time: DatetimeUTC | None = None,
597
617
  end_time: t.Optional[DatetimeUTC] = None,
598
618
  market_id: t.Optional[ChecksumAddress] = None,
619
+ market_resolved_before: DatetimeUTC | None = None,
620
+ market_resolved_after: DatetimeUTC | None = None,
599
621
  ) -> list[OmenBet]:
600
622
  omen_bets = self.get_bets(
601
623
  better_address=better_address,
@@ -603,14 +625,18 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
603
625
  end_time=end_time,
604
626
  market_id=market_id,
605
627
  filter_by_answer_finalized_not_null=True,
628
+ market_resolved_before=market_resolved_before,
629
+ market_resolved_after=market_resolved_after,
606
630
  )
607
631
  return [b for b in omen_bets if b.fpmm.is_resolved]
608
632
 
609
633
  def get_resolved_bets_with_valid_answer(
610
634
  self,
611
635
  better_address: ChecksumAddress,
612
- start_time: DatetimeUTC,
636
+ start_time: DatetimeUTC | None = None,
613
637
  end_time: t.Optional[DatetimeUTC] = None,
638
+ market_resolved_before: DatetimeUTC | None = None,
639
+ market_resolved_after: DatetimeUTC | None = None,
614
640
  market_id: t.Optional[ChecksumAddress] = None,
615
641
  ) -> list[OmenBet]:
616
642
  bets = self.get_resolved_bets(
@@ -618,6 +644,8 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
618
644
  start_time=start_time,
619
645
  end_time=end_time,
620
646
  market_id=market_id,
647
+ market_resolved_before=market_resolved_before,
648
+ market_resolved_after=market_resolved_after,
621
649
  )
622
650
  return [b for b in bets if b.fpmm.is_resolved_with_valid_answer]
623
651
 
@@ -629,6 +657,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
629
657
  finalized_before: DatetimeUTC | None,
630
658
  finalized_after: DatetimeUTC | None,
631
659
  with_answers: bool | None,
660
+ pending_arbitration: bool | None,
632
661
  question_id: HexBytes | None,
633
662
  question_id_in: list[HexBytes] | None,
634
663
  opened_before: t.Optional[DatetimeUTC],
@@ -675,9 +704,12 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
675
704
 
676
705
  if with_answers is not None:
677
706
  if with_answers:
678
- where_stms["answerFinalizedTimestamp_not"] = None
707
+ where_stms["currentAnswer_not"] = None
679
708
  else:
680
- where_stms["answerFinalizedTimestamp"] = None
709
+ where_stms["currentAnswer"] = None
710
+
711
+ if pending_arbitration is not None:
712
+ where_stms["isPendingArbitration"] = pending_arbitration
681
713
 
682
714
  if question_id_in is not None:
683
715
  # Be aware: On Omen subgraph, question's `id` represents `questionId` on reality subgraph. And `id` on reality subraph is just a weird concat of multiple things from the question.
@@ -695,6 +727,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
695
727
  finalized_before: DatetimeUTC | None,
696
728
  finalized_after: DatetimeUTC | None,
697
729
  with_answers: bool | None,
730
+ pending_arbitration: bool | None,
698
731
  question_id: HexBytes | None,
699
732
  question_id_in: list[HexBytes] | None,
700
733
  opened_before: t.Optional[DatetimeUTC],
@@ -707,7 +740,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
707
740
  where_stms: dict[str, t.Any] = {}
708
741
 
709
742
  if question_id is not None:
710
- where_stms["questionId"] = question_id.hex()
743
+ where_stms["id"] = question_id.hex()
711
744
 
712
745
  if current_answer_before is not None:
713
746
  where_stms["currentAnswerTimestamp_lt"] = to_int_timestamp(
@@ -732,9 +765,12 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
732
765
 
733
766
  if with_answers is not None:
734
767
  if with_answers:
735
- where_stms["answerFinalizedTimestamp_not"] = None
768
+ where_stms["currentAnswer_not"] = None
736
769
  else:
737
- where_stms["answerFinalizedTimestamp"] = None
770
+ where_stms["currentAnswer"] = None
771
+
772
+ if pending_arbitration is not None:
773
+ where_stms["isPendingArbitration"] = pending_arbitration
738
774
 
739
775
  if question_id_in is not None:
740
776
  # Be aware: On Omen subgraph, question's `id` represents `questionId` on reality subgraph. And `id` on reality subraph is just a weird concat of multiple things from the question.
@@ -755,6 +791,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
755
791
  finalized_before: DatetimeUTC | None = None,
756
792
  finalized_after: DatetimeUTC | None = None,
757
793
  with_answers: bool | None = None,
794
+ pending_arbitration: bool | None = None,
758
795
  question_id_in: list[HexBytes] | None = None,
759
796
  question_id: HexBytes | None = None,
760
797
  opened_before: DatetimeUTC | None = None,
@@ -767,6 +804,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
767
804
  finalized_before=finalized_before,
768
805
  finalized_after=finalized_after,
769
806
  with_answers=with_answers,
807
+ pending_arbitration=pending_arbitration,
770
808
  current_answer_before=current_answer_before,
771
809
  question_id_in=question_id_in,
772
810
  question_id=question_id,
@@ -809,6 +847,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
809
847
  question_finalized_before: t.Optional[DatetimeUTC] = None,
810
848
  question_finalized_after: t.Optional[DatetimeUTC] = None,
811
849
  question_with_answers: bool | None = None,
850
+ question_pending_arbitration: bool | None = None,
812
851
  question_id: HexBytes | None = None,
813
852
  question_id_in: list[HexBytes] | None = None,
814
853
  question_current_answer_before: DatetimeUTC | None = None,
@@ -828,6 +867,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
828
867
  finalized_before=question_finalized_before,
829
868
  finalized_after=question_finalized_after,
830
869
  with_answers=question_with_answers,
870
+ pending_arbitration=question_pending_arbitration,
831
871
  current_answer_before=question_current_answer_before,
832
872
  question_id_in=question_id_in,
833
873
  excluded_titles=question_excluded_titles,
@@ -926,3 +966,13 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
926
966
  raise RuntimeError("Multiple results found for a single bet.")
927
967
 
928
968
  return results[0]
969
+
970
+
971
+ @persistent_inmemory_cache
972
+ def get_omen_market_by_market_id_cached(
973
+ market_id: HexAddress,
974
+ block_number: int, # Force `block_number` to be provided, because `latest` block constantly updates.
975
+ ) -> OmenMarket:
976
+ return OmenSubgraphHandler().get_omen_market_by_market_id(
977
+ market_id, block_number=block_number
978
+ )
@@ -3,13 +3,13 @@ import pandas as pd
3
3
 
4
4
  from prediction_market_agent_tooling.markets.data_models import (
5
5
  SharpeOutput,
6
- SimulationDetail,
6
+ SimulatedBetDetail,
7
7
  )
8
8
 
9
9
 
10
10
  class SharpeRatioCalculator:
11
11
  def __init__(
12
- self, details: list[SimulationDetail], risk_free_rate: float = 0.0
12
+ self, details: list[SimulatedBetDetail], risk_free_rate: float = 0.0
13
13
  ) -> None:
14
14
  self.details = details
15
15
  self.df = pd.DataFrame([d.model_dump() for d in self.details])
@@ -19,7 +19,9 @@ class SharpeRatioCalculator:
19
19
  self, required_columns: list[str]
20
20
  ) -> None:
21
21
  if not set(required_columns).issubset(self.df.columns):
22
- raise ValueError("Dataframe doesn't contain all the required columns.")
22
+ raise ValueError(
23
+ f"Dataframe doesn't contain all the required columns. {required_columns=} {self.df.columns=}"
24
+ )
23
25
 
24
26
  def prepare_wallet_daily_balance_df(
25
27
  self, timestamp_col_name: str, profit_col_name: str
@@ -0,0 +1,57 @@
1
+ from functools import cache
2
+ from typing import Any, Callable, TypeVar, cast, overload
3
+
4
+ from joblib import Memory
5
+
6
+ from prediction_market_agent_tooling.config import APIKeys
7
+
8
+ MEMORY = Memory(APIKeys().CACHE_DIR, verbose=0)
9
+
10
+
11
+ T = TypeVar("T", bound=Callable[..., Any])
12
+
13
+
14
+ @overload
15
+ def persistent_inmemory_cache(
16
+ func: None = None,
17
+ *,
18
+ in_memory_cache: bool = True,
19
+ ) -> Callable[[T], T]:
20
+ ...
21
+
22
+
23
+ @overload
24
+ def persistent_inmemory_cache(
25
+ func: T,
26
+ *,
27
+ in_memory_cache: bool = True,
28
+ ) -> T:
29
+ ...
30
+
31
+
32
+ def persistent_inmemory_cache(
33
+ func: T | None = None,
34
+ *,
35
+ in_memory_cache: bool = True,
36
+ ) -> T | Callable[[T], T]:
37
+ """
38
+ Wraps a function with both file cache (for persistent cache) and optional in-memory cache (for speed).
39
+ Can be used as @persistent_inmemory_cache or @persistent_inmemory_cache(in_memory_cache=False)
40
+ """
41
+ if func is None:
42
+ # Ugly Pythonic way to support this decorator as `@persistent_inmemory_cache` but also `@persistent_inmemory_cache(in_memory_cache=False)`
43
+ def decorator(func: T) -> T:
44
+ return persistent_inmemory_cache(
45
+ func,
46
+ in_memory_cache=in_memory_cache,
47
+ )
48
+
49
+ return decorator
50
+ else:
51
+ # The decorator is called without arguments.
52
+ if not APIKeys().ENABLE_CACHE:
53
+ return func
54
+ cached_func = MEMORY.cache(func)
55
+ if in_memory_cache:
56
+ cached_func = cache(cached_func)
57
+ return cast(T, cached_func)
@@ -3,7 +3,10 @@ import hishel
3
3
 
4
4
  class HttpxCachedClient:
5
5
  def __init__(self) -> None:
6
- storage = hishel.FileStorage(ttl=3600, check_ttl_every=600)
6
+ storage = hishel.FileStorage(
7
+ ttl=24 * 60 * 60,
8
+ check_ttl_every=1 * 60 * 60,
9
+ )
7
10
  controller = hishel.Controller(force_cache=True)
8
11
  self.client = hishel.CacheClient(storage=storage, controller=controller)
9
12
 
@@ -63,6 +63,7 @@ def get_traces_for_agent(
63
63
  from_timestamp: DatetimeUTC,
64
64
  has_output: bool,
65
65
  client: Langfuse,
66
+ to_timestamp: DatetimeUTC | None = None,
66
67
  ) -> list[TraceWithDetails]:
67
68
  """
68
69
  Fetch agent traces using pagination
@@ -76,6 +77,7 @@ def get_traces_for_agent(
76
77
  limit=100,
77
78
  page=page,
78
79
  from_timestamp=from_timestamp,
80
+ to_timestamp=to_timestamp,
79
81
  )
80
82
  if not traces.data:
81
83
  break
@@ -9,8 +9,8 @@ from prediction_market_agent_tooling.loggers import logger
9
9
 
10
10
  class TransactionBlockCache:
11
11
  def __init__(self, web3: Web3):
12
- self.block_number_cache = dc.Cache("block_cache_dir")
13
- self.block_timestamp_cache = dc.Cache("timestamp_cache_dir")
12
+ self.block_number_cache = dc.Cache(".cache/block_cache_dir")
13
+ self.block_timestamp_cache = dc.Cache(".cache/timestamp_cache_dir")
14
14
  self.web3 = web3
15
15
 
16
16
  @tenacity.retry(
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "prediction-market-agent-tooling"
3
- version = "0.57.1"
3
+ version = "0.57.3"
4
4
  description = "Tools to benchmark, deploy and monitor prediction market agents."
5
5
  authors = ["Gnosis"]
6
6
  readme = "README.md"
@@ -53,11 +53,13 @@ types-python-dateutil = "^2.9.0.20240906"
53
53
  pinatapy-vourhey = "^0.2.0"
54
54
  hishel = "^0.0.31"
55
55
  pytest-postgresql = "^6.1.1"
56
+ optuna = { version = "^4.1.0", optional = true}
56
57
 
57
58
  [tool.poetry.extras]
58
59
  openai = ["openai"]
59
60
  langchain = ["langchain", "langchain-openai"]
60
61
  google = ["google-api-python-client"]
62
+ optuna = ["optuna"]
61
63
 
62
64
  [tool.poetry.group.dev.dependencies]
63
65
  pytest = "*"
@@ -1,17 +0,0 @@
1
- from functools import cache
2
- from typing import Any, Callable, TypeVar, cast
3
-
4
- from joblib import Memory
5
-
6
- from prediction_market_agent_tooling.config import APIKeys
7
-
8
- MEMORY = Memory(APIKeys().CACHE_DIR, verbose=0)
9
-
10
- T = TypeVar("T", bound=Callable[..., Any])
11
-
12
-
13
- def persistent_inmemory_cache(func: T) -> T:
14
- """
15
- Wraps a function with both file cache (for persistent cache) and in-memory cache (for speed).
16
- """
17
- return cast(T, cache(MEMORY.cache(func)) if APIKeys().ENABLE_CACHE else func)