prediction-market-agent-tooling 0.65.6__py3-none-any.whl → 0.65.9__py3-none-any.whl

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 (20) hide show
  1. prediction_market_agent_tooling/deploy/agent.py +3 -10
  2. prediction_market_agent_tooling/markets/agent_market.py +4 -0
  3. prediction_market_agent_tooling/markets/data_models.py +21 -1
  4. prediction_market_agent_tooling/markets/manifold/data_models.py +5 -3
  5. prediction_market_agent_tooling/markets/manifold/manifold.py +17 -1
  6. prediction_market_agent_tooling/markets/manifold/utils.py +8 -2
  7. prediction_market_agent_tooling/markets/markets.py +1 -47
  8. prediction_market_agent_tooling/markets/omen/omen.py +9 -0
  9. prediction_market_agent_tooling/markets/omen/omen_resolving.py +27 -10
  10. prediction_market_agent_tooling/markets/seer/exceptions.py +2 -0
  11. prediction_market_agent_tooling/markets/seer/price_manager.py +5 -2
  12. prediction_market_agent_tooling/markets/seer/seer.py +45 -9
  13. prediction_market_agent_tooling/tools/cow/cow_order.py +37 -6
  14. prediction_market_agent_tooling/tools/cow/models.py +16 -0
  15. prediction_market_agent_tooling/tools/utils.py +10 -0
  16. {prediction_market_agent_tooling-0.65.6.dist-info → prediction_market_agent_tooling-0.65.9.dist-info}/METADATA +1 -1
  17. {prediction_market_agent_tooling-0.65.6.dist-info → prediction_market_agent_tooling-0.65.9.dist-info}/RECORD +20 -18
  18. {prediction_market_agent_tooling-0.65.6.dist-info → prediction_market_agent_tooling-0.65.9.dist-info}/LICENSE +0 -0
  19. {prediction_market_agent_tooling-0.65.6.dist-info → prediction_market_agent_tooling-0.65.9.dist-info}/WHEEL +0 -0
  20. {prediction_market_agent_tooling-0.65.6.dist-info → prediction_market_agent_tooling-0.65.9.dist-info}/entry_points.txt +0 -0
@@ -33,10 +33,7 @@ from prediction_market_agent_tooling.markets.data_models import (
33
33
  ProbabilisticAnswer,
34
34
  Trade,
35
35
  )
36
- from prediction_market_agent_tooling.markets.markets import (
37
- MarketType,
38
- have_bet_on_market_since,
39
- )
36
+ from prediction_market_agent_tooling.markets.markets import MarketType
40
37
  from prediction_market_agent_tooling.markets.omen.omen import (
41
38
  send_keeping_token_to_eoa_xdai,
42
39
  )
@@ -219,7 +216,6 @@ class DeployablePredictionAgent(DeployableAgent):
219
216
  def initialize_langfuse(self) -> None:
220
217
  super().initialize_langfuse()
221
218
  # Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
222
- self.have_bet_on_market_since = observe()(self.have_bet_on_market_since) # type: ignore[method-assign]
223
219
  self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
224
220
  self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
225
221
  self.answer_categorical_market = observe()(self.answer_categorical_market) # type: ignore[method-assign]
@@ -265,16 +261,13 @@ class DeployablePredictionAgent(DeployableAgent):
265
261
  f"{api_keys=} doesn't have enough operational balance."
266
262
  )
267
263
 
268
- def have_bet_on_market_since(self, market: AgentMarket, since: timedelta) -> bool:
269
- return have_bet_on_market_since(keys=APIKeys(), market=market, since=since)
270
-
271
264
  def verify_market(self, market_type: MarketType, market: AgentMarket) -> bool:
272
265
  """
273
266
  Subclasses can implement their own logic instead of this one, or on top of this one.
274
267
  By default, it allows only markets where user didn't bet recently and it's a reasonable question.
275
268
  """
276
- if self.have_bet_on_market_since(
277
- market, since=self.same_market_trade_interval.get(market=market)
269
+ if market.have_bet_on_market_since(
270
+ keys=APIKeys(), since=self.same_market_trade_interval.get(market=market)
278
271
  ):
279
272
  logger.info(
280
273
  f"Market already bet on within {self.same_market_trade_interval}."
@@ -1,4 +1,5 @@
1
1
  import typing as t
2
+ from datetime import timedelta
2
3
  from enum import Enum
3
4
  from math import prod
4
5
 
@@ -115,6 +116,9 @@ class AgentMarket(BaseModel):
115
116
  )
116
117
  return outcome_token_pool
117
118
 
119
+ def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
120
+ raise NotImplementedError("Subclasses must implement this method")
121
+
118
122
  def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
119
123
  if self.outcome_token_pool is None or not self.outcome_token_pool:
120
124
  return OutcomeToken(0)
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import Annotated
2
+ from typing import Annotated, Sequence
3
3
 
4
4
  from pydantic import BaseModel, BeforeValidator, computed_field
5
5
 
@@ -26,6 +26,26 @@ class Resolution(BaseModel):
26
26
  def from_answer(answer: OutcomeStr) -> "Resolution":
27
27
  return Resolution(outcome=answer, invalid=False)
28
28
 
29
+ def find_outcome_matching_market(
30
+ self, market_outcomes: Sequence[OutcomeStr]
31
+ ) -> OutcomeStr | None:
32
+ """
33
+ Finds a matching outcome in the provided market outcomes.
34
+
35
+ Performs case-insensitive matching between this resolution's outcome
36
+ and the provided market outcomes.
37
+
38
+ """
39
+
40
+ if not self.outcome:
41
+ return None
42
+
43
+ normalized_outcome = self.outcome.lower()
44
+ for outcome in market_outcomes:
45
+ if outcome.lower() == normalized_outcome:
46
+ return outcome
47
+ return None
48
+
29
49
 
30
50
  class Bet(BaseModel):
31
51
  id: str
@@ -12,7 +12,9 @@ from prediction_market_agent_tooling.gtypes import (
12
12
  Probability,
13
13
  )
14
14
  from prediction_market_agent_tooling.markets.data_models import Resolution
15
- from prediction_market_agent_tooling.markets.manifold.utils import validate_resolution
15
+ from prediction_market_agent_tooling.markets.manifold.utils import (
16
+ validate_manifold_resolution,
17
+ )
16
18
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
17
19
 
18
20
  MANIFOLD_BASE_URL = "https://manifold.markets"
@@ -110,7 +112,7 @@ class ManifoldMarket(BaseModel):
110
112
 
111
113
  @field_validator("resolution", mode="before")
112
114
  def validate_resolution(cls, v: t.Any) -> Resolution:
113
- return validate_resolution(v)
115
+ return validate_manifold_resolution(v)
114
116
 
115
117
  def __repr__(self) -> str:
116
118
  return f"Manifold's market: {self.question}"
@@ -204,7 +206,7 @@ class ManifoldBet(BaseModel):
204
206
 
205
207
  @field_validator("outcome", mode="before")
206
208
  def validate_resolution(cls, v: t.Any) -> Resolution:
207
- return validate_resolution(v)
209
+ return validate_manifold_resolution(v)
208
210
 
209
211
  def get_resolved_outcome(self) -> OutcomeStr:
210
212
  if self.outcome.outcome:
@@ -1,4 +1,5 @@
1
1
  import typing as t
2
+ from datetime import timedelta
2
3
 
3
4
  from prediction_market_agent_tooling.config import APIKeys
4
5
  from prediction_market_agent_tooling.gtypes import (
@@ -15,6 +16,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
15
16
  )
16
17
  from prediction_market_agent_tooling.markets.manifold.api import (
17
18
  get_authenticated_user,
19
+ get_manifold_bets,
18
20
  get_manifold_binary_markets,
19
21
  get_manifold_market,
20
22
  place_bet,
@@ -24,7 +26,7 @@ from prediction_market_agent_tooling.markets.manifold.data_models import (
24
26
  FullManifoldMarket,
25
27
  usd_to_mana,
26
28
  )
27
- from prediction_market_agent_tooling.tools.utils import DatetimeUTC
29
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC, utcnow
28
30
 
29
31
 
30
32
  class ManifoldAgentMarket(AgentMarket):
@@ -51,6 +53,20 @@ class ManifoldAgentMarket(AgentMarket):
51
53
  def get_tiny_bet_amount(self) -> CollateralToken:
52
54
  return CollateralToken(1)
53
55
 
56
+ def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
57
+ start_time = utcnow() - since
58
+ recently_betted_questions = set(
59
+ get_manifold_market(b.contractId).question
60
+ for b in get_manifold_bets(
61
+ user_id=get_authenticated_user(
62
+ keys.manifold_api_key.get_secret_value()
63
+ ).id,
64
+ start_time=start_time,
65
+ end_time=None,
66
+ )
67
+ )
68
+ return self.question in recently_betted_questions
69
+
54
70
  def place_bet(self, outcome: OutcomeStr, amount: USD) -> str:
55
71
  self.get_usd_in_token(amount)
56
72
  bet = place_bet(
@@ -3,8 +3,14 @@ import typing as t
3
3
  from prediction_market_agent_tooling.gtypes import OutcomeStr
4
4
  from prediction_market_agent_tooling.markets.data_models import Resolution
5
5
 
6
+ MANIFOLD_CANCEL_OUTCOME = "CANCEL"
6
7
 
7
- def validate_resolution(v: t.Any) -> Resolution:
8
+
9
+ def validate_manifold_resolution(v: t.Any) -> Resolution:
8
10
  if isinstance(v, str):
9
- return Resolution(outcome=OutcomeStr(v), invalid=False)
11
+ return (
12
+ Resolution(outcome=OutcomeStr(v), invalid=False)
13
+ if v != MANIFOLD_CANCEL_OUTCOME
14
+ else Resolution(outcome=None, invalid=True)
15
+ )
10
16
  raise ValueError(f"Expected a string, got {v} {type(v)}")
@@ -1,8 +1,6 @@
1
1
  import typing as t
2
- from datetime import timedelta
3
2
  from enum import Enum
4
3
 
5
- from prediction_market_agent_tooling.config import APIKeys
6
4
  from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
7
5
  from prediction_market_agent_tooling.jobs.omen.omen_jobs import OmenJobAgentMarket
8
6
  from prediction_market_agent_tooling.markets.agent_market import (
@@ -10,11 +8,6 @@ from prediction_market_agent_tooling.markets.agent_market import (
10
8
  FilterBy,
11
9
  SortBy,
12
10
  )
13
- from prediction_market_agent_tooling.markets.manifold.api import (
14
- get_authenticated_user,
15
- get_manifold_bets,
16
- get_manifold_market,
17
- )
18
11
  from prediction_market_agent_tooling.markets.manifold.manifold import (
19
12
  ManifoldAgentMarket,
20
13
  )
@@ -22,18 +15,11 @@ from prediction_market_agent_tooling.markets.metaculus.metaculus import (
22
15
  MetaculusAgentMarket,
23
16
  )
24
17
  from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
25
- from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
26
- OmenSubgraphHandler,
27
- )
28
18
  from prediction_market_agent_tooling.markets.polymarket.polymarket import (
29
19
  PolymarketAgentMarket,
30
20
  )
31
21
  from prediction_market_agent_tooling.markets.seer.seer import SeerAgentMarket
32
- from prediction_market_agent_tooling.tools.utils import (
33
- DatetimeUTC,
34
- should_not_happen,
35
- utcnow,
36
- )
22
+ from prediction_market_agent_tooling.tools.utils import DatetimeUTC
37
23
 
38
24
 
39
25
  class MarketType(str, Enum):
@@ -92,35 +78,3 @@ def get_binary_markets(
92
78
  excluded_questions=excluded_questions,
93
79
  )
94
80
  return markets
95
-
96
-
97
- def have_bet_on_market_since(
98
- keys: APIKeys, market: AgentMarket, since: timedelta
99
- ) -> bool:
100
- start_time = utcnow() - since
101
- recently_betted_questions = (
102
- set(
103
- get_manifold_market(b.contractId).question
104
- for b in get_manifold_bets(
105
- user_id=get_authenticated_user(
106
- keys.manifold_api_key.get_secret_value()
107
- ).id,
108
- start_time=start_time,
109
- end_time=None,
110
- )
111
- )
112
- if isinstance(market, ManifoldAgentMarket)
113
- else (
114
- set(
115
- b.title
116
- for b in OmenSubgraphHandler().get_bets(
117
- better_address=keys.bet_from_address,
118
- start_time=start_time,
119
- market_id=market.market_maker_contract_address_checksummed,
120
- )
121
- )
122
- if isinstance(market, OmenAgentMarket)
123
- else should_not_happen(f"Unknown market: {market}")
124
- )
125
- )
126
- return market.question in recently_betted_questions
@@ -84,6 +84,7 @@ from prediction_market_agent_tooling.tools.utils import (
84
84
  DatetimeUTC,
85
85
  calculate_sell_amount_in_collateral,
86
86
  check_not_none,
87
+ utcnow,
87
88
  )
88
89
  from prediction_market_agent_tooling.tools.web3_utils import get_receipt_block_timestamp
89
90
 
@@ -157,6 +158,14 @@ class OmenAgentMarket(AgentMarket):
157
158
  def get_usd_in_token(self, x: USD) -> CollateralToken:
158
159
  return get_usd_in_token(x, self.collateral_token_contract_address_checksummed)
159
160
 
161
+ def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
162
+ start_time = utcnow() - since
163
+ prev_bets = self.get_bets_made_since(
164
+ better_address=keys.bet_from_address, start_time=start_time
165
+ )
166
+
167
+ return self.id in [b.market_id for b in prev_bets]
168
+
160
169
  def liquidate_existing_positions(
161
170
  self,
162
171
  bet_outcome: OutcomeStr,
@@ -31,7 +31,11 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
31
31
  from prediction_market_agent_tooling.tools.tokens.main_token import (
32
32
  MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
33
33
  )
34
- from prediction_market_agent_tooling.tools.utils import utcnow
34
+ from prediction_market_agent_tooling.tools.utils import (
35
+ check_not_none,
36
+ extract_error_from_retry_error,
37
+ utcnow,
38
+ )
35
39
  from prediction_market_agent_tooling.tools.web3_utils import ZERO_BYTES
36
40
 
37
41
 
@@ -223,7 +227,10 @@ def omen_submit_answer_market_tx(
223
227
  And after the period is over, you need to resolve the market using `omen_resolve_market_tx`.
224
228
  """
225
229
  realitio_contract = OmenRealitioContract()
226
- outcome_index = market.outcomes.index(resolution.outcome)
230
+ outcome_matching_market = check_not_none(
231
+ resolution.find_outcome_matching_market(market.outcomes)
232
+ )
233
+ outcome_index = market.outcomes.index(outcome_matching_market)
227
234
  realitio_contract.submit_answer(
228
235
  api_keys=api_keys,
229
236
  question_id=market.question.id,
@@ -261,14 +268,24 @@ def omen_resolve_market_tx(
261
268
  Market can be resolved after the answer if finalized on Reality.
262
269
  """
263
270
  oracle_contract = OmenOracleContract()
264
- oracle_contract.resolve(
265
- api_keys=api_keys,
266
- question_id=market.question.id,
267
- template_id=market.question.templateId,
268
- question_raw=market.question.question_raw,
269
- n_outcomes=market.question.n_outcomes,
270
- web3=web3,
271
- )
271
+ try:
272
+ oracle_contract.resolve(
273
+ api_keys=api_keys,
274
+ question_id=market.question.id,
275
+ template_id=market.question.templateId,
276
+ question_raw=market.question.question_raw,
277
+ n_outcomes=market.question.n_outcomes,
278
+ web3=web3,
279
+ )
280
+ except BaseException as e:
281
+ e = extract_error_from_retry_error(e)
282
+ if "condition not prepared or found" in str(e):
283
+ # We can't do anything about these, so just skip them with warning.
284
+ logger.warning(
285
+ f"Market {market.url=} not resolved, because `condition not prepared or found`, skipping."
286
+ )
287
+ else:
288
+ raise
272
289
 
273
290
 
274
291
  def find_resolution_on_other_markets(market: OmenMarket) -> Resolution | None:
@@ -0,0 +1,2 @@
1
+ class PriceCalculationError(Exception):
2
+ """Raised when there's an error discovering prices for a market."""
@@ -12,6 +12,9 @@ from prediction_market_agent_tooling.gtypes import (
12
12
  )
13
13
  from prediction_market_agent_tooling.loggers import logger
14
14
  from prediction_market_agent_tooling.markets.seer.data_models import SeerMarket
15
+ from prediction_market_agent_tooling.markets.seer.exceptions import (
16
+ PriceCalculationError,
17
+ )
15
18
  from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
16
19
  SeerSubgraphHandler,
17
20
  )
@@ -128,7 +131,7 @@ class PriceManager:
128
131
  )
129
132
  # It's okay if invalid (last) outcome has price 0, but not the other outcomes.
130
133
  if price is None and idx != len(self.seer_market.wrapped_tokens) - 1:
131
- raise ValueError(
134
+ raise PriceCalculationError(
132
135
  f"Couldn't get price for {wrapped_token} for market {self.seer_market.url}."
133
136
  )
134
137
  price_data[wrapped_token] = (
@@ -142,7 +145,7 @@ class PriceManager:
142
145
  sum(price_data.values(), start=CollateralToken.zero())
143
146
  == CollateralToken.zero()
144
147
  ):
145
- raise ValueError(
148
+ raise PriceCalculationError(
146
149
  f"All prices for market {self.seer_market.url} are zero. This shouldn't happen."
147
150
  )
148
151
 
@@ -1,4 +1,5 @@
1
1
  import typing as t
2
+ from datetime import timedelta
2
3
 
3
4
  from eth_typing import ChecksumAddress
4
5
  from web3 import Web3
@@ -31,6 +32,9 @@ from prediction_market_agent_tooling.markets.seer.data_models import (
31
32
  RedeemParams,
32
33
  SeerMarket,
33
34
  )
35
+ from prediction_market_agent_tooling.markets.seer.exceptions import (
36
+ PriceCalculationError,
37
+ )
34
38
  from prediction_market_agent_tooling.markets.seer.price_manager import PriceManager
35
39
  from prediction_market_agent_tooling.markets.seer.seer_contracts import (
36
40
  GnosisRouter,
@@ -49,6 +53,7 @@ from prediction_market_agent_tooling.tools.contract import (
49
53
  )
50
54
  from prediction_market_agent_tooling.tools.cow.cow_order import (
51
55
  get_buy_token_amount_else_raise,
56
+ get_orders_by_owner,
52
57
  get_trades_by_owner,
53
58
  swap_tokens_waiting,
54
59
  )
@@ -60,6 +65,7 @@ from prediction_market_agent_tooling.tools.tokens.usd import (
60
65
  get_token_in_usd,
61
66
  get_usd_in_token,
62
67
  )
68
+ from prediction_market_agent_tooling.tools.utils import utcnow
63
69
 
64
70
  # We place a larger bet amount by default than Omen so that cow presents valid quotes.
65
71
  SEER_TINY_BET_AMOUNT = USD(0.1)
@@ -269,24 +275,44 @@ class SeerAgentMarket(AgentMarket):
269
275
 
270
276
  # GnosisRouter withdraws sDai into wxDAI/xDai on its own, so no auto-withdraw needed by us.
271
277
 
278
+ def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
279
+ """Check if the user has placed a bet on this market since a specific time using Cow API."""
280
+ # Cow endpoint doesn't allow us to filter by time.
281
+ start_time = utcnow() - since
282
+ prev_orders = get_orders_by_owner(owner=keys.bet_from_address)
283
+ for order in prev_orders:
284
+ if order.creationDate >= start_time and {
285
+ Web3.to_checksum_address(order.sellToken),
286
+ Web3.to_checksum_address(order.buyToken),
287
+ }.intersection(set(self.wrapped_tokens)):
288
+ return True
289
+
290
+ return False
291
+
272
292
  @staticmethod
273
293
  def verify_operational_balance(api_keys: APIKeys) -> bool:
274
294
  return OmenAgentMarket.verify_operational_balance(api_keys=api_keys)
275
295
 
276
296
  @staticmethod
277
297
  def from_data_model_with_subgraph(
278
- model: SeerMarket, seer_subgraph: SeerSubgraphHandler
298
+ model: SeerMarket,
299
+ seer_subgraph: SeerSubgraphHandler,
300
+ must_have_prices: bool,
279
301
  ) -> t.Optional["SeerAgentMarket"]:
280
- p = PriceManager(seer_market=model, seer_subgraph=seer_subgraph)
302
+ price_manager = PriceManager(seer_market=model, seer_subgraph=seer_subgraph)
281
303
 
282
- probability_map = p.build_probability_map()
283
- if not probability_map:
304
+ probability_map = {}
305
+ try:
306
+ probability_map = price_manager.build_probability_map()
307
+ except PriceCalculationError as e:
284
308
  logger.info(
285
- f"probability_map for market {model.id.hex()} could not be calculated. Skipping."
309
+ f"Error when calculating probabilities for market {model.id.hex()} - {e}"
286
310
  )
287
- return None
311
+ if must_have_prices:
312
+ # Price calculation failed, so don't return the market
313
+ return None
288
314
 
289
- return SeerAgentMarket(
315
+ market = SeerAgentMarket(
290
316
  id=model.id.hex(),
291
317
  question=model.title,
292
318
  creator=model.creator,
@@ -305,6 +331,8 @@ class SeerAgentMarket(AgentMarket):
305
331
  probabilities=probability_map,
306
332
  )
307
333
 
334
+ return market
335
+
308
336
  @staticmethod
309
337
  def get_markets(
310
338
  limit: int,
@@ -324,17 +352,25 @@ class SeerAgentMarket(AgentMarket):
324
352
 
325
353
  # We exclude the None values below because `from_data_model_with_subgraph` can return None, which
326
354
  # represents an invalid market.
327
- return [
355
+ seer_agent_markets = [
328
356
  market
329
357
  for m in markets
330
358
  if (
331
359
  market := SeerAgentMarket.from_data_model_with_subgraph(
332
- model=m, seer_subgraph=seer_subgraph
360
+ model=m,
361
+ seer_subgraph=seer_subgraph,
362
+ must_have_prices=filter_by == FilterBy.OPEN,
333
363
  )
334
364
  )
335
365
  is not None
336
366
  ]
337
367
 
368
+ if filter_by == FilterBy.OPEN:
369
+ # Extra manual filter for liquidity, as subgraph is sometimes unreliable.
370
+ seer_agent_markets = [m for m in seer_agent_markets if m.has_liquidity()]
371
+
372
+ return seer_agent_markets
373
+
338
374
  def get_outcome_str_from_idx(self, outcome_index: int) -> OutcomeStr:
339
375
  return self.outcomes[outcome_index]
340
376
 
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import typing as t
2
3
  from datetime import timedelta
3
4
 
4
5
  import httpx
@@ -23,7 +24,6 @@ from cowdao_cowpy.order_book.generated.model import (
23
24
  OrderStatus,
24
25
  TokenAmount,
25
26
  )
26
- from cowdao_cowpy.subgraph.client import BaseModel
27
27
  from tenacity import (
28
28
  retry_if_not_exception_type,
29
29
  stop_after_attempt,
@@ -39,14 +39,10 @@ from prediction_market_agent_tooling.markets.omen.cow_contracts import (
39
39
  CowGPv2SettlementContract,
40
40
  )
41
41
  from prediction_market_agent_tooling.tools.contract import ContractERC20OnGnosisChain
42
+ from prediction_market_agent_tooling.tools.cow.models import MinimalisticToken, Order
42
43
  from prediction_market_agent_tooling.tools.utils import check_not_none, utcnow
43
44
 
44
45
 
45
- class MinimalisticToken(BaseModel):
46
- sellToken: ChecksumAddress
47
- buyToken: ChecksumAddress
48
-
49
-
50
46
  class OrderStatusError(Exception):
51
47
  pass
52
48
 
@@ -292,3 +288,38 @@ def get_trades_by_owner(
292
288
  )
293
289
  response.raise_for_status()
294
290
  return [MinimalisticToken.model_validate(i) for i in response.json()]
291
+
292
+
293
+ @tenacity.retry(
294
+ stop=stop_after_attempt(3),
295
+ wait=wait_fixed(1),
296
+ after=lambda x: logger.debug(f"get_orders_by_owner failed, {x.attempt_number=}."),
297
+ )
298
+ def get_orders_by_owner(
299
+ owner: ChecksumAddress,
300
+ ) -> list[Order]:
301
+ """Retrieves all orders with pagination."""
302
+ items = paginate_endpoint(f"https://api.cow.fi/xdai/api/v1/account/{owner}/orders")
303
+ return [Order.model_validate(i) for i in items]
304
+
305
+
306
+ @tenacity.retry(
307
+ stop=stop_after_attempt(3),
308
+ wait=wait_fixed(1),
309
+ after=lambda x: logger.debug(f"paginate_endpoint failed, {x.attempt_number=}."),
310
+ )
311
+ def paginate_endpoint(url: str, limit: int = 1000) -> t.Any:
312
+ offset = 0
313
+ results = []
314
+
315
+ while True:
316
+ response = httpx.get(url, params={"offset": offset, "limit": limit})
317
+ response.raise_for_status()
318
+ items = response.json()
319
+ if not items:
320
+ break
321
+
322
+ results.extend(items)
323
+ offset += limit
324
+
325
+ return results
@@ -0,0 +1,16 @@
1
+ from pydantic import BaseModel
2
+
3
+ from prediction_market_agent_tooling.gtypes import ChecksumAddress
4
+ from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
5
+
6
+
7
+ class MinimalisticToken(BaseModel):
8
+ sellToken: ChecksumAddress
9
+ buyToken: ChecksumAddress
10
+
11
+
12
+ class Order(BaseModel):
13
+ uid: str
14
+ sellToken: str
15
+ buyToken: str
16
+ creationDate: DatetimeUTC
@@ -9,6 +9,7 @@ import requests
9
9
  from pydantic import BaseModel, ValidationError
10
10
  from scipy.optimize import newton
11
11
  from scipy.stats import entropy
12
+ from tenacity import RetryError
12
13
 
13
14
  from prediction_market_agent_tooling.gtypes import (
14
15
  CollateralToken,
@@ -230,3 +231,12 @@ def calculate_sell_amount_in_collateral(
230
231
 
231
232
  amount_to_sell = newton(f, 0)
232
233
  return CollateralToken(float(amount_to_sell) * 0.999999) # Avoid rounding errors
234
+
235
+
236
+ def extract_error_from_retry_error(e: BaseException | RetryError) -> BaseException:
237
+ if (
238
+ isinstance(e, RetryError)
239
+ and (exp_from_retry := e.last_attempt.exception()) is not None
240
+ ):
241
+ e = exp_from_retry
242
+ return e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.65.6
3
+ Version: 0.65.9
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.13
@@ -24,7 +24,7 @@ prediction_market_agent_tooling/benchmark/benchmark.py,sha256=KwMZzwise3sgmhdjw7
24
24
  prediction_market_agent_tooling/benchmark/utils.py,sha256=xQd7p9H08-OtN3iC4QT2i9bkUTmrXa6rxGXeg9yMhgU,2986
25
25
  prediction_market_agent_tooling/chains.py,sha256=1qQstoqXMwqwM7k-KH7MjMz8Ei-D83KZByvDbCZpAxs,116
26
26
  prediction_market_agent_tooling/config.py,sha256=-kJfdDr-m0R-tGZ1KRI-hJJk0mXDt142CAlvwaJ2N2I,11778
27
- prediction_market_agent_tooling/deploy/agent.py,sha256=ddFvBUhB_WVWtuUGkiZNKBME4Hd3AdquVv588c1rpu0,25602
27
+ prediction_market_agent_tooling/deploy/agent.py,sha256=ONuXYe9zRO37lzume5z-2hPKTBrCljBg5OD_38gNRc0,25289
28
28
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=yS1fWkHynr9MYGNOM2WsCnRWLPaffY4bOc6bIudrdd4,1377
29
29
  prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=YYayGjTKW02d3BUavJ8M3NmFk41oldEM3FHbwppZGRM,17184
30
30
  prediction_market_agent_tooling/deploy/constants.py,sha256=Qe9cllgsGMkecfmbhXoFkPxuJyG6ATsrT87RF9SmPWM,249
@@ -38,28 +38,28 @@ prediction_market_agent_tooling/jobs/jobs_models.py,sha256=DoZ9dlvVhpNrnINiR1uy6
38
38
  prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=qbTZ9HVvu_iP4dDxuvOZxAp6JsRKejvEW2YDYCnRmd4,5039
39
39
  prediction_market_agent_tooling/loggers.py,sha256=kFZ1BrI8hvWgZO1vzptFnYiOEDx9Ozs86DA9yF3bSgY,5212
40
40
  prediction_market_agent_tooling/logprobs_parser.py,sha256=DBlBQtWX8_URXhzTU3YWIPa76Zx3QDHlx1ARqbgJsVI,5008
41
- prediction_market_agent_tooling/markets/agent_market.py,sha256=smKuMaP1zyTtw31Robbj3ospF03xptWX0EDKPJbHH9I,19070
41
+ prediction_market_agent_tooling/markets/agent_market.py,sha256=pnyxTMUrL8cQsP4lmBBpe07R-Mi7S4vNjrvNLdOzAa8,19258
42
42
  prediction_market_agent_tooling/markets/base_subgraph_handler.py,sha256=7RaYO_4qAmQ6ZGM8oPK2-CkiJfKmV9MxM-rJlduaecU,1971
43
43
  prediction_market_agent_tooling/markets/blockchain_utils.py,sha256=gZMwCTO-1d2ALXeY7-LP6U4kCpotJ6GMPZwN11kFOfE,2604
44
44
  prediction_market_agent_tooling/markets/categorize.py,sha256=orLZlPaHgeREU66m1amxfWikeV77idV4sZDPB8NgSD0,1300
45
- prediction_market_agent_tooling/markets/data_models.py,sha256=vFlX1iNWJcYZlImPkTOQCp2C0veIO7HuAxe0Wf_S8yg,6952
45
+ prediction_market_agent_tooling/markets/data_models.py,sha256=seifqxjVpzZdVKJQ7a5sKPPUM749EHV80SdI6pVQ3b4,7542
46
46
  prediction_market_agent_tooling/markets/manifold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  prediction_market_agent_tooling/markets/manifold/api.py,sha256=tWnjuqvU8pcCuja2B_ynHeds1iiEFc6QWHjeSO_GSxY,7676
48
- prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=QzRfR2g_3vzCw4azj_lOSKSFAZczjAWlM0lQy2dZry8,6704
49
- prediction_market_agent_tooling/markets/manifold/manifold.py,sha256=dUVBkTqnD7M35H20nvSFG6dalYi9CbYZ8cyAqEMz9AE,4700
50
- prediction_market_agent_tooling/markets/manifold/utils.py,sha256=aprL6vd4xZjzRDRyuhKkw5MnjPNFvwoqjDGLu9XoLXQ,361
48
+ prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=3z1gFbPMEgCDGqeH-IK8wcvqmIHgLdZX8C2M1UQ7iDw,6740
49
+ prediction_market_agent_tooling/markets/manifold/manifold.py,sha256=7s8BC32QkzU80coLTKzms1FrG0E-GEeHfkrl_MgrrLk,5301
50
+ prediction_market_agent_tooling/markets/manifold/utils.py,sha256=DCigEbpGWkXR-RZT_oJrvJhilmxKFAxcWMjUFi0nBBI,530
51
51
  prediction_market_agent_tooling/markets/market_fees.py,sha256=YeK3ynjYIguB0xf6sO5iyg9lOdW_HD4C6nbJfiGyRCU,1351
52
- prediction_market_agent_tooling/markets/markets.py,sha256=cXN37xnNNOFJUcdmfD9osrGiffTxGVzWVNUFAPyPGkc,4119
52
+ prediction_market_agent_tooling/markets/markets.py,sha256=lIEPfPJD1Gz90pTvN2pZi51rpb969STPgQtNFCqHUJg,2667
53
53
  prediction_market_agent_tooling/markets/metaculus/api.py,sha256=4TRPGytQQbSdf42DCg2M_JWYPAuNjqZ3eBqaQBLkNks,2736
54
54
  prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=FaBCTPPezXbBwZ9p791CiVgQ4vB696xnMbz9XVXmiVI,3267
55
55
  prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=6iuUpDXgdbFOh-XuxH0rbeAf_m55r2KbHTaSTePNSWA,5614
56
56
  prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  prediction_market_agent_tooling/markets/omen/cow_contracts.py,sha256=sl1L4cK5nAJwZ2wdhLzqh8p7h_IEValNvLwKUlInKxw,957
58
58
  prediction_market_agent_tooling/markets/omen/data_models.py,sha256=0jCxgUEVpaggt_avkNqgXAnZWdA6D-ew9PE2gm0aqDk,29930
59
- prediction_market_agent_tooling/markets/omen/omen.py,sha256=jyoAV-E_Qcbqqs4oi7junE3rHTG3kaYLMtIVzEkNC6M,49981
59
+ prediction_market_agent_tooling/markets/omen/omen.py,sha256=q3SqvQEpZAsbFBbSid_rhRn-hVi-roCRN4piGj2wxU8,50301
60
60
  prediction_market_agent_tooling/markets/omen/omen_constants.py,sha256=D9oflYKafLQiHYtB5sScMHqmXyzM8JP8J0yATmc4SQQ,233
61
61
  prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=f0dKbdVM2OUTSpkCSZdPtKqeckS2c48j3rjnK8oD3wE,29716
62
- prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=05x-Yb0bUsJSEtsWQLqSHyl4HqLChksIb64OGygN-Rw,10226
62
+ prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=_9FjpH-y_yDwua2-mZ0Iop49cRBs2m7w-Z1s3mOofFk,10848
63
63
  prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=149T_tJxK2GFnGCulOlGr2Mwiaa7sa74E4lqYsuCv68,39962
64
64
  prediction_market_agent_tooling/markets/polymarket/api.py,sha256=UZ4_TG8ceb9Y-qgsOKs8Qiv8zDt957QkT8IX2c83yqo,4800
65
65
  prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=U1SXTz93FIG3GO1h5BJWSt1hPKn_YAMBeZ3k8IS-ook,4552
@@ -67,8 +67,9 @@ prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=wCw
67
67
  prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=meAhQ5_gwVDvlSxhGGVAvRB7B47zKLnRvZ-_13tKtwY,3433
68
68
  prediction_market_agent_tooling/markets/polymarket/utils.py,sha256=8kTeVjXPcXC6DkDvWYsZQLY7x8DS6CEp_yznSEazsNU,2037
69
69
  prediction_market_agent_tooling/markets/seer/data_models.py,sha256=G0i-fnVaK16KWDYVI6w3lvyte6Op7ca_iIC8IfrXmlM,4702
70
- prediction_market_agent_tooling/markets/seer/price_manager.py,sha256=4hSSDyxSj9po9-tRrdtNvJ2d9v0xXT08Ezgbk1JDE3c,6122
71
- prediction_market_agent_tooling/markets/seer/seer.py,sha256=5uSKUhyM_3PTguYi9yiD3-E5Famb6M_fayBX1NqebAE,20273
70
+ prediction_market_agent_tooling/markets/seer/exceptions.py,sha256=cEObdjluivD94tgOLzmimR7wgQEOt6SRakrYdhsRQtk,112
71
+ prediction_market_agent_tooling/markets/seer/price_manager.py,sha256=MClY2NGwOV70nZYIcmzXFy6Ogd8NBIq7telQcQ3VcU4,6243
72
+ prediction_market_agent_tooling/markets/seer/seer.py,sha256=IsFK8XXjHq8UBvIkueiWpm_b3ddMslr84DqF_qNFQw0,21745
72
73
  prediction_market_agent_tooling/markets/seer/seer_contracts.py,sha256=kH9nPXsx6UM5er42g2f3fLvy36sY5JM2f_beXeuNgUc,3790
73
74
  prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py,sha256=pJxch9_u0EdiIatQP1-UFClt8UEfMZAXBlk5wDO_ovk,9940
74
75
  prediction_market_agent_tooling/markets/seer/subgraph_data_models.py,sha256=0izxS8Mtzonfdl9UqvFVXrdj0hVzieroekXhogfZKCw,1817
@@ -83,7 +84,8 @@ prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=ZW5iI5rmjq
83
84
  prediction_market_agent_tooling/tools/caches/serializers.py,sha256=vFDx4fsPxclXp2q0sv27j4al_M_Tj9aR2JJP-xNHQXA,2151
84
85
  prediction_market_agent_tooling/tools/contract.py,sha256=pdr9ZYmj4QVUfgVKdvOU6ucYdBpJGdha_FMR_LgtcEs,22912
85
86
  prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
86
- prediction_market_agent_tooling/tools/cow/cow_order.py,sha256=-nuV2rrLKWKnMn7mN3knRdRemwGiq46OY6m6jX4sm7c,9219
87
+ prediction_market_agent_tooling/tools/cow/cow_order.py,sha256=KpklYy5DY3j-aIlf6dB_o0mEX4MD3fqMiZK3tJs5Gro,10129
88
+ prediction_market_agent_tooling/tools/cow/models.py,sha256=ZWGW0og5vXjgZVXxkx4uV2m61Q8AmL-L-D_D4f7ApR0,379
87
89
  prediction_market_agent_tooling/tools/custom_exceptions.py,sha256=Fh8z1fbwONvP4-j7AmV_PuEcoqb6-QXa9PJ9m7guMcM,93
88
90
  prediction_market_agent_tooling/tools/datetime_utc.py,sha256=8_WackjtjC8zHXrhQFTGQ6e6Fz_6llWoKR4CSFvIv9I,2766
89
91
  prediction_market_agent_tooling/tools/db/db_manager.py,sha256=GtzHH1NLl8HwqC8Z7s6eTlIQXuV0blxfaV2PeQrBnfQ,3013
@@ -117,10 +119,10 @@ prediction_market_agent_tooling/tools/tokens/main_token.py,sha256=1rbwpdCusPgQIV
117
119
  prediction_market_agent_tooling/tools/tokens/token_utils.py,sha256=fhs-FH9m9IbzGa-30R3ZleSKLeKfLEDoJ7F5Om285Vk,1369
118
120
  prediction_market_agent_tooling/tools/tokens/usd.py,sha256=yuW8iPPtcpP4eLH2nORMDAfztcq0Nv2ascSrCquF1f8,3115
119
121
  prediction_market_agent_tooling/tools/transaction_cache.py,sha256=K5YKNL2_tR10Iw2TD9fuP-CTGpBbZtNdgbd0B_R7pjg,1814
120
- prediction_market_agent_tooling/tools/utils.py,sha256=Jzpck3_QwShhejhgbAhmNxPSOPQJssBQep0eVemVjZ4,7064
122
+ prediction_market_agent_tooling/tools/utils.py,sha256=RlWSlzS2LavMIWrpwn1fevbzgPZruD4VcXTa-XxjWnE,7343
121
123
  prediction_market_agent_tooling/tools/web3_utils.py,sha256=zRq-eeBGWt8uUGN9G_WfjmJ0eVvO8aWE9S0Pz_Y6AOA,12342
122
- prediction_market_agent_tooling-0.65.6.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
123
- prediction_market_agent_tooling-0.65.6.dist-info/METADATA,sha256=XT7Kp9vv8HBSZjedQV7VgRp7H4Awv4GQdjrzz1F7Z-E,8734
124
- prediction_market_agent_tooling-0.65.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
125
- prediction_market_agent_tooling-0.65.6.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
126
- prediction_market_agent_tooling-0.65.6.dist-info/RECORD,,
124
+ prediction_market_agent_tooling-0.65.9.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
125
+ prediction_market_agent_tooling-0.65.9.dist-info/METADATA,sha256=fwrbaHBiT91jMhqYkTWJ-vPB2dav-bxrgqfV7oDjGHY,8734
126
+ prediction_market_agent_tooling-0.65.9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
127
+ prediction_market_agent_tooling-0.65.9.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
128
+ prediction_market_agent_tooling-0.65.9.dist-info/RECORD,,