prediction-market-agent-tooling 0.66.6__py3-none-any.whl → 0.67.2__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.
- prediction_market_agent_tooling/deploy/agent.py +70 -14
- prediction_market_agent_tooling/markets/agent_market.py +9 -2
- prediction_market_agent_tooling/markets/manifold/manifold.py +2 -2
- prediction_market_agent_tooling/markets/markets.py +6 -5
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +4 -4
- prediction_market_agent_tooling/markets/omen/omen.py +3 -3
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +3 -3
- prediction_market_agent_tooling/markets/seer/data_models.py +5 -7
- prediction_market_agent_tooling/markets/seer/price_manager.py +65 -46
- prediction_market_agent_tooling/markets/seer/seer.py +78 -85
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +34 -9
- prediction_market_agent_tooling/markets/seer/subgraph_data_models.py +0 -4
- prediction_market_agent_tooling/markets/seer/swap_pool_handler.py +1 -11
- prediction_market_agent_tooling/tools/cow/cow_order.py +24 -11
- prediction_market_agent_tooling/tools/cow/models.py +4 -2
- prediction_market_agent_tooling/tools/rephrase.py +71 -0
- {prediction_market_agent_tooling-0.66.6.dist-info → prediction_market_agent_tooling-0.67.2.dist-info}/METADATA +2 -1
- {prediction_market_agent_tooling-0.66.6.dist-info → prediction_market_agent_tooling-0.67.2.dist-info}/RECORD +21 -20
- {prediction_market_agent_tooling-0.66.6.dist-info → prediction_market_agent_tooling-0.67.2.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.66.6.dist-info → prediction_market_agent_tooling-0.67.2.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.66.6.dist-info → prediction_market_agent_tooling-0.67.2.dist-info}/entry_points.txt +0 -0
@@ -6,6 +6,7 @@ from enum import Enum
|
|
6
6
|
from functools import cached_property
|
7
7
|
|
8
8
|
from pydantic import computed_field
|
9
|
+
from pydantic_ai.exceptions import UnexpectedModelBehavior
|
9
10
|
|
10
11
|
from prediction_market_agent_tooling.config import APIKeys
|
11
12
|
from prediction_market_agent_tooling.deploy.betting_strategy import (
|
@@ -19,13 +20,12 @@ from prediction_market_agent_tooling.deploy.trade_interval import (
|
|
19
20
|
)
|
20
21
|
from prediction_market_agent_tooling.gtypes import USD, OutcomeToken, xDai
|
21
22
|
from prediction_market_agent_tooling.loggers import logger
|
22
|
-
from prediction_market_agent_tooling.markets.agent_market import AgentMarket, FilterBy
|
23
|
-
from prediction_market_agent_tooling.markets.agent_market import (
|
24
|
-
MarketType as AgentMarketType,
|
25
|
-
)
|
26
23
|
from prediction_market_agent_tooling.markets.agent_market import (
|
24
|
+
AgentMarket,
|
25
|
+
FilterBy,
|
27
26
|
ProcessedMarket,
|
28
27
|
ProcessedTradedMarket,
|
28
|
+
QuestionType,
|
29
29
|
SortBy,
|
30
30
|
)
|
31
31
|
from prediction_market_agent_tooling.markets.data_models import (
|
@@ -47,6 +47,9 @@ from prediction_market_agent_tooling.tools.custom_exceptions import (
|
|
47
47
|
from prediction_market_agent_tooling.tools.is_invalid import is_invalid
|
48
48
|
from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
|
49
49
|
from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
|
50
|
+
from prediction_market_agent_tooling.tools.rephrase import (
|
51
|
+
rephrase_question_to_unconditioned,
|
52
|
+
)
|
50
53
|
from prediction_market_agent_tooling.tools.tokens.main_token import (
|
51
54
|
MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
|
52
55
|
)
|
@@ -196,6 +199,7 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
196
199
|
trade_on_markets_created_after: DatetimeUTC | None = None
|
197
200
|
get_markets_sort_by: SortBy = SortBy.CLOSING_SOONEST
|
198
201
|
get_markets_filter_by: FilterBy = FilterBy.OPEN
|
202
|
+
rephrase_conditioned_markets: bool = True
|
199
203
|
|
200
204
|
# Agent behaviour when filtering fetched markets
|
201
205
|
allow_invalid_questions: bool = False
|
@@ -205,6 +209,8 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
205
209
|
MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES
|
206
210
|
)
|
207
211
|
|
212
|
+
just_warn_on_unexpected_model_behavior: bool = False
|
213
|
+
|
208
214
|
# Only Metaculus allows to post predictions without trading (buying/selling of outcome tokens).
|
209
215
|
supported_markets: t.Sequence[MarketType] = [MarketType.METACULUS]
|
210
216
|
|
@@ -224,6 +230,7 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
224
230
|
self.answer_categorical_market = observe()(self.answer_categorical_market) # type: ignore[method-assign]
|
225
231
|
self.answer_scalar_market = observe()(self.answer_scalar_market) # type: ignore[method-assign]
|
226
232
|
self.process_market = observe()(self.process_market) # type: ignore[method-assign]
|
233
|
+
self.rephrase_market_to_unconditioned = observe()(self.rephrase_market_to_unconditioned) # type: ignore[method-assign]
|
227
234
|
|
228
235
|
def update_langfuse_trace_by_market(
|
229
236
|
self, market_type: MarketType, market: AgentMarket
|
@@ -298,6 +305,34 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
298
305
|
|
299
306
|
return True
|
300
307
|
|
308
|
+
def rephrase_market_to_unconditioned(
|
309
|
+
self,
|
310
|
+
market_: AgentMarket,
|
311
|
+
) -> AgentMarket:
|
312
|
+
"""
|
313
|
+
If `rephrase_conditioned_markets` is set to True,
|
314
|
+
this method will be used to rephrase the question to account for the parent's market probability in the agent's decision process.
|
315
|
+
"""
|
316
|
+
new = market_.model_copy(deep=True)
|
317
|
+
|
318
|
+
if new.parent is not None and new.parent.market.parent is not None:
|
319
|
+
new.parent.market = self.rephrase_market_to_unconditioned(new.parent.market)
|
320
|
+
|
321
|
+
rephrased_question = (
|
322
|
+
rephrase_question_to_unconditioned(
|
323
|
+
new.question,
|
324
|
+
new.parent.market.question,
|
325
|
+
new.parent.market.outcomes[new.parent.parent_outcome],
|
326
|
+
)
|
327
|
+
if new.parent is not None
|
328
|
+
else new.question
|
329
|
+
)
|
330
|
+
|
331
|
+
new.question = rephrased_question
|
332
|
+
new.parent = None
|
333
|
+
|
334
|
+
return new
|
335
|
+
|
301
336
|
def answer_categorical_market(
|
302
337
|
self, market: AgentMarket
|
303
338
|
) -> CategoricalProbabilisticAnswer | None:
|
@@ -338,13 +373,23 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
338
373
|
return False
|
339
374
|
|
340
375
|
@property
|
341
|
-
def
|
376
|
+
def include_conditional_markets(self) -> bool:
|
377
|
+
# TODO: All should work in our code, except that currently CoW most of the time completely fails to swap xDai into outcome tokens of conditioned market.
|
378
|
+
# Enable after https://github.com/gnosis/prediction-market-agent-tooling/issues/748 and/or https://github.com/gnosis/prediction-market-agent-tooling/issues/759 is resolved.
|
379
|
+
return False
|
380
|
+
# `include_conditional_markets` if `rephrase_conditioned_markets` is enabled.
|
381
|
+
# We can expand this method in teh future, when we implement also more complex logic about conditional markets.
|
382
|
+
# Note that conditional market isn't a type of the market like Binary or Categorical, it means that it uses outcome tokens from parent market as a collateral token in this market.
|
383
|
+
return self.rephrase_conditioned_markets
|
384
|
+
|
385
|
+
@property
|
386
|
+
def agent_question_type(self) -> QuestionType:
|
342
387
|
if self.fetch_scalar_markets:
|
343
|
-
return
|
388
|
+
return QuestionType.SCALAR
|
344
389
|
elif self.fetch_categorical_markets:
|
345
|
-
return
|
390
|
+
return QuestionType.CATEGORICAL
|
346
391
|
else:
|
347
|
-
return
|
392
|
+
return QuestionType.BINARY
|
348
393
|
|
349
394
|
def get_markets(
|
350
395
|
self,
|
@@ -355,15 +400,14 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
355
400
|
"""
|
356
401
|
cls = market_type.market_class
|
357
402
|
|
358
|
-
agent_market_type = self.agent_market_type
|
359
|
-
|
360
403
|
# Fetch the soonest closing markets to choose from
|
361
404
|
available_markets = cls.get_markets(
|
362
405
|
limit=self.n_markets_to_fetch,
|
363
406
|
sort_by=self.get_markets_sort_by,
|
364
407
|
filter_by=self.get_markets_filter_by,
|
365
408
|
created_after=self.trade_on_markets_created_after,
|
366
|
-
|
409
|
+
question_type=self.agent_question_type,
|
410
|
+
include_conditional_markets=self.include_conditional_markets,
|
367
411
|
)
|
368
412
|
return available_markets
|
369
413
|
|
@@ -396,6 +440,9 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
396
440
|
|
397
441
|
logger.info(f"Answering market '{market.question}'.")
|
398
442
|
|
443
|
+
if self.rephrase_conditioned_markets and market.parent is not None:
|
444
|
+
market = self.rephrase_market_to_unconditioned(market)
|
445
|
+
|
399
446
|
if market.is_binary:
|
400
447
|
try:
|
401
448
|
binary_answer = self.answer_binary_market(market)
|
@@ -455,9 +502,18 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
455
502
|
f"Processing market {market.question=} from {market.url=} with liquidity {market.get_liquidity()}."
|
456
503
|
)
|
457
504
|
|
458
|
-
|
459
|
-
|
460
|
-
|
505
|
+
try:
|
506
|
+
answer = self.build_answer(
|
507
|
+
market=market, market_type=market_type, verify_market=verify_market
|
508
|
+
)
|
509
|
+
except UnexpectedModelBehavior:
|
510
|
+
(
|
511
|
+
logger.warning
|
512
|
+
if self.just_warn_on_unexpected_model_behavior
|
513
|
+
else logger.exception
|
514
|
+
)(f"Unexpected model behaviour in {self.__class__.__name__}.")
|
515
|
+
answer = None
|
516
|
+
|
461
517
|
if answer is not None:
|
462
518
|
self.verify_answer_outcomes(market=market, answer=answer)
|
463
519
|
|
@@ -64,7 +64,12 @@ class FilterBy(str, Enum):
|
|
64
64
|
NONE = "none"
|
65
65
|
|
66
66
|
|
67
|
-
class
|
67
|
+
class ParentMarket(BaseModel):
|
68
|
+
market: "AgentMarket"
|
69
|
+
parent_outcome: int
|
70
|
+
|
71
|
+
|
72
|
+
class QuestionType(str, Enum):
|
68
73
|
ALL = "all"
|
69
74
|
CATEGORICAL = "categorical"
|
70
75
|
SCALAR = "scalar"
|
@@ -96,6 +101,8 @@ class AgentMarket(BaseModel):
|
|
96
101
|
upper_bound: Wei | None = None
|
97
102
|
lower_bound: Wei | None = None
|
98
103
|
|
104
|
+
parent: ParentMarket | None = None
|
105
|
+
|
99
106
|
@field_validator("probabilities")
|
100
107
|
def validate_probabilities(
|
101
108
|
cls,
|
@@ -376,7 +383,7 @@ class AgentMarket(BaseModel):
|
|
376
383
|
filter_by: FilterBy = FilterBy.OPEN,
|
377
384
|
created_after: t.Optional[DatetimeUTC] = None,
|
378
385
|
excluded_questions: set[str] | None = None,
|
379
|
-
|
386
|
+
question_type: QuestionType = QuestionType.ALL,
|
380
387
|
include_conditional_markets: bool = False,
|
381
388
|
) -> t.Sequence["AgentMarket"]:
|
382
389
|
raise NotImplementedError("Subclasses must implement this method")
|
@@ -12,7 +12,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
12
12
|
AgentMarket,
|
13
13
|
FilterBy,
|
14
14
|
MarketFees,
|
15
|
-
|
15
|
+
QuestionType,
|
16
16
|
SortBy,
|
17
17
|
)
|
18
18
|
from prediction_market_agent_tooling.markets.manifold.api import (
|
@@ -111,7 +111,7 @@ class ManifoldAgentMarket(AgentMarket):
|
|
111
111
|
filter_by: FilterBy = FilterBy.OPEN,
|
112
112
|
created_after: t.Optional[DatetimeUTC] = None,
|
113
113
|
excluded_questions: set[str] | None = None,
|
114
|
-
|
114
|
+
question_type: QuestionType = QuestionType.ALL,
|
115
115
|
include_conditional_markets: bool = False,
|
116
116
|
) -> t.Sequence["ManifoldAgentMarket"]:
|
117
117
|
sort: t.Literal["newest", "close-date"] | None
|
@@ -3,11 +3,12 @@ from enum import Enum
|
|
3
3
|
|
4
4
|
from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
|
5
5
|
from prediction_market_agent_tooling.jobs.omen.omen_jobs import OmenJobAgentMarket
|
6
|
-
from prediction_market_agent_tooling.markets.agent_market import AgentMarket, FilterBy
|
7
6
|
from prediction_market_agent_tooling.markets.agent_market import (
|
8
|
-
|
7
|
+
AgentMarket,
|
8
|
+
FilterBy,
|
9
|
+
QuestionType,
|
10
|
+
SortBy,
|
9
11
|
)
|
10
|
-
from prediction_market_agent_tooling.markets.agent_market import SortBy
|
11
12
|
from prediction_market_agent_tooling.markets.manifold.manifold import (
|
12
13
|
ManifoldAgentMarket,
|
13
14
|
)
|
@@ -68,7 +69,7 @@ def get_binary_markets(
|
|
68
69
|
sort_by: SortBy = SortBy.NONE,
|
69
70
|
excluded_questions: set[str] | None = None,
|
70
71
|
created_after: DatetimeUTC | None = None,
|
71
|
-
|
72
|
+
question_type: QuestionType = QuestionType.BINARY,
|
72
73
|
) -> t.Sequence[AgentMarket]:
|
73
74
|
agent_market_class = MARKET_TYPE_TO_AGENT_MARKET[market_type]
|
74
75
|
markets = agent_market_class.get_markets(
|
@@ -77,6 +78,6 @@ def get_binary_markets(
|
|
77
78
|
filter_by=filter_by,
|
78
79
|
created_after=created_after,
|
79
80
|
excluded_questions=excluded_questions,
|
80
|
-
|
81
|
+
question_type=question_type,
|
81
82
|
)
|
82
83
|
return markets
|
@@ -9,8 +9,8 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
9
9
|
AgentMarket,
|
10
10
|
FilterBy,
|
11
11
|
MarketFees,
|
12
|
-
MarketType,
|
13
12
|
ProcessedMarket,
|
13
|
+
QuestionType,
|
14
14
|
SortBy,
|
15
15
|
)
|
16
16
|
from prediction_market_agent_tooling.markets.metaculus.api import (
|
@@ -67,15 +67,15 @@ class MetaculusAgentMarket(AgentMarket):
|
|
67
67
|
)
|
68
68
|
|
69
69
|
@staticmethod
|
70
|
-
def get_markets(
|
70
|
+
def get_markets(
|
71
71
|
limit: int,
|
72
72
|
sort_by: SortBy = SortBy.NONE,
|
73
73
|
filter_by: FilterBy = FilterBy.OPEN,
|
74
74
|
created_after: t.Optional[DatetimeUTC] = None,
|
75
75
|
excluded_questions: set[str] | None = None,
|
76
|
-
|
77
|
-
market_type: MarketType = MarketType.ALL,
|
76
|
+
question_type: QuestionType = QuestionType.ALL,
|
78
77
|
include_conditional_markets: bool = False,
|
78
|
+
tournament_id: int | None = None,
|
79
79
|
) -> t.Sequence["MetaculusAgentMarket"]:
|
80
80
|
order_by: str | None
|
81
81
|
if sort_by == SortBy.NONE:
|
@@ -25,9 +25,9 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
25
25
|
AgentMarket,
|
26
26
|
FilterBy,
|
27
27
|
MarketFees,
|
28
|
-
MarketType,
|
29
28
|
ProcessedMarket,
|
30
29
|
ProcessedTradedMarket,
|
30
|
+
QuestionType,
|
31
31
|
SortBy,
|
32
32
|
)
|
33
33
|
from prediction_market_agent_tooling.markets.blockchain_utils import store_trades
|
@@ -380,10 +380,10 @@ class OmenAgentMarket(AgentMarket):
|
|
380
380
|
filter_by: FilterBy = FilterBy.OPEN,
|
381
381
|
created_after: t.Optional[DatetimeUTC] = None,
|
382
382
|
excluded_questions: set[str] | None = None,
|
383
|
-
|
383
|
+
question_type: QuestionType = QuestionType.ALL,
|
384
384
|
include_conditional_markets: bool = False,
|
385
385
|
) -> t.Sequence["OmenAgentMarket"]:
|
386
|
-
fetch_categorical_markets =
|
386
|
+
fetch_categorical_markets = question_type == QuestionType.CATEGORICAL
|
387
387
|
|
388
388
|
return [
|
389
389
|
OmenAgentMarket.from_data_model(m)
|
@@ -11,7 +11,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
11
11
|
AgentMarket,
|
12
12
|
FilterBy,
|
13
13
|
MarketFees,
|
14
|
-
|
14
|
+
QuestionType,
|
15
15
|
SortBy,
|
16
16
|
)
|
17
17
|
from prediction_market_agent_tooling.markets.data_models import Resolution
|
@@ -123,7 +123,7 @@ class PolymarketAgentMarket(AgentMarket):
|
|
123
123
|
filter_by: FilterBy = FilterBy.OPEN,
|
124
124
|
created_after: t.Optional[DatetimeUTC] = None,
|
125
125
|
excluded_questions: set[str] | None = None,
|
126
|
-
|
126
|
+
question_type: QuestionType = QuestionType.ALL,
|
127
127
|
include_conditional_markets: bool = False,
|
128
128
|
) -> t.Sequence["PolymarketAgentMarket"]:
|
129
129
|
closed: bool | None
|
@@ -159,7 +159,7 @@ class PolymarketAgentMarket(AgentMarket):
|
|
159
159
|
ascending=ascending,
|
160
160
|
created_after=created_after,
|
161
161
|
excluded_questions=excluded_questions,
|
162
|
-
only_binary=
|
162
|
+
only_binary=question_type is not QuestionType.CATEGORICAL,
|
163
163
|
)
|
164
164
|
|
165
165
|
condition_models = PolymarketSubgraphHandler().get_conditions(
|
@@ -3,7 +3,7 @@ from datetime import timedelta
|
|
3
3
|
from typing import Annotated
|
4
4
|
from urllib.parse import urljoin
|
5
5
|
|
6
|
-
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field
|
6
|
+
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, computed_field
|
7
7
|
from web3 import Web3
|
8
8
|
from web3.constants import ADDRESS_ZERO
|
9
9
|
|
@@ -17,9 +17,6 @@ from prediction_market_agent_tooling.gtypes import (
|
|
17
17
|
Web3Wei,
|
18
18
|
Wei,
|
19
19
|
)
|
20
|
-
from prediction_market_agent_tooling.markets.seer.subgraph_data_models import (
|
21
|
-
SeerParentMarket,
|
22
|
-
)
|
23
20
|
from prediction_market_agent_tooling.tools.contract import ContractERC20OnGnosisChain
|
24
21
|
from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
|
25
22
|
from prediction_market_agent_tooling.tools.utils import utcnow
|
@@ -70,10 +67,10 @@ class SeerMarket(BaseModel):
|
|
70
67
|
title: str = Field(alias="marketName")
|
71
68
|
outcomes: t.Sequence[OutcomeStr]
|
72
69
|
wrapped_tokens: list[HexAddress] = Field(alias="wrappedTokens")
|
73
|
-
parent_outcome: int = Field(
|
74
|
-
|
75
|
-
alias="parentMarket", default=None
|
70
|
+
parent_outcome: int = Field(
|
71
|
+
alias="parentOutcome", description="It comes as 0 from non-conditioned markets."
|
76
72
|
)
|
73
|
+
parent_market: t.Optional["SeerMarket"] = Field(alias="parentMarket", default=None)
|
77
74
|
collateral_token: HexAddress = Field(alias="collateralToken")
|
78
75
|
condition_id: HexBytes = Field(alias="conditionId")
|
79
76
|
opening_ts: int = Field(alias="openingTs")
|
@@ -139,6 +136,7 @@ class SeerMarket(BaseModel):
|
|
139
136
|
def created_time(self) -> DatetimeUTC:
|
140
137
|
return DatetimeUTC.to_datetime_utc(self.block_timestamp)
|
141
138
|
|
139
|
+
@computed_field # type: ignore[prop-decorator]
|
142
140
|
@property
|
143
141
|
def url(self) -> str:
|
144
142
|
chain_id = RPCConfig().chain_id
|
@@ -1,6 +1,5 @@
|
|
1
|
-
import typing as t
|
2
|
-
|
3
1
|
from cachetools import TTLCache, cached
|
2
|
+
from pydantic import BaseModel
|
4
3
|
from web3 import Web3
|
5
4
|
|
6
5
|
from prediction_market_agent_tooling.gtypes import (
|
@@ -18,32 +17,15 @@ from prediction_market_agent_tooling.markets.seer.exceptions import (
|
|
18
17
|
from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
|
19
18
|
SeerSubgraphHandler,
|
20
19
|
)
|
21
|
-
from prediction_market_agent_tooling.markets.seer.subgraph_data_models import SeerPool
|
22
20
|
from prediction_market_agent_tooling.tools.cow.cow_order import (
|
23
21
|
get_buy_token_amount_else_raise,
|
24
22
|
)
|
25
23
|
from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
|
26
24
|
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
collateral_exchange_amount: CollateralToken | None = None,
|
32
|
-
) -> str:
|
33
|
-
"""
|
34
|
-
Generate a unique cache key based on a token address and optional collateral token.
|
35
|
-
"""
|
36
|
-
|
37
|
-
if collateral_exchange_amount is None:
|
38
|
-
return f"{token}-no_collateral"
|
39
|
-
|
40
|
-
return "-".join(
|
41
|
-
[
|
42
|
-
token,
|
43
|
-
collateral_exchange_amount.symbol,
|
44
|
-
str(collateral_exchange_amount.value),
|
45
|
-
]
|
46
|
-
)
|
26
|
+
class Prices(BaseModel):
|
27
|
+
priceOfCollateralInAskingToken: CollateralToken
|
28
|
+
priceOfAskingTokenInCollateral: CollateralToken
|
47
29
|
|
48
30
|
|
49
31
|
class PriceManager:
|
@@ -67,17 +49,17 @@ class PriceManager:
|
|
67
49
|
f"{price_diff_pct=} larger than {max_price_diff=} for seer market {self.seer_market.id.hex()} "
|
68
50
|
)
|
69
51
|
|
70
|
-
|
71
|
-
|
52
|
+
def get_price_for_token(self, token: ChecksumAddress) -> CollateralToken | None:
|
53
|
+
return self.get_amount_of_token_in_collateral(token, CollateralToken(1))
|
54
|
+
|
55
|
+
@cached(TTLCache(maxsize=100, ttl=5 * 60))
|
56
|
+
def get_amount_of_collateral_in_token(
|
72
57
|
self,
|
73
58
|
token: ChecksumAddress,
|
74
|
-
collateral_exchange_amount: CollateralToken
|
59
|
+
collateral_exchange_amount: CollateralToken,
|
75
60
|
) -> CollateralToken | None:
|
76
|
-
|
77
|
-
collateral_exchange_amount
|
78
|
-
if collateral_exchange_amount is not None
|
79
|
-
else CollateralToken(1)
|
80
|
-
)
|
61
|
+
if token == self.seer_market.collateral_token_contract_address_checksummed:
|
62
|
+
return collateral_exchange_amount
|
81
63
|
|
82
64
|
try:
|
83
65
|
buy_token_amount = get_buy_token_amount_else_raise(
|
@@ -85,23 +67,51 @@ class PriceManager:
|
|
85
67
|
sell_token=self.seer_market.collateral_token_contract_address_checksummed,
|
86
68
|
buy_token=token,
|
87
69
|
)
|
88
|
-
|
89
|
-
return CollateralToken(price)
|
70
|
+
return buy_token_amount.as_token
|
90
71
|
|
91
72
|
except Exception as e:
|
92
73
|
logger.warning(
|
93
74
|
f"Could not get quote for {token=} from Cow, exception {e=}. Falling back to pools. "
|
94
75
|
)
|
95
|
-
|
76
|
+
prices = self.get_token_price_from_pools(token=token)
|
77
|
+
return (
|
78
|
+
prices.priceOfCollateralInAskingToken * collateral_exchange_amount
|
79
|
+
if prices
|
80
|
+
else None
|
81
|
+
)
|
96
82
|
|
97
|
-
@
|
98
|
-
def
|
99
|
-
|
83
|
+
@cached(TTLCache(maxsize=100, ttl=5 * 60))
|
84
|
+
def get_amount_of_token_in_collateral(
|
85
|
+
self,
|
86
|
+
token: ChecksumAddress,
|
87
|
+
token_exchange_amount: CollateralToken,
|
88
|
+
) -> CollateralToken | None:
|
89
|
+
if token == self.seer_market.collateral_token_contract_address_checksummed:
|
90
|
+
return token_exchange_amount
|
91
|
+
|
92
|
+
try:
|
93
|
+
buy_collateral_amount = get_buy_token_amount_else_raise(
|
94
|
+
sell_amount=token_exchange_amount.as_wei,
|
95
|
+
sell_token=token,
|
96
|
+
buy_token=self.seer_market.collateral_token_contract_address_checksummed,
|
97
|
+
)
|
98
|
+
return buy_collateral_amount.as_token
|
99
|
+
|
100
|
+
except Exception as e:
|
101
|
+
logger.warning(
|
102
|
+
f"Could not get quote for {token=} from Cow, exception {e=}. Falling back to pools. "
|
103
|
+
)
|
104
|
+
prices = self.get_token_price_from_pools(token=token)
|
105
|
+
return (
|
106
|
+
prices.priceOfAskingTokenInCollateral * token_exchange_amount
|
107
|
+
if prices
|
108
|
+
else None
|
109
|
+
)
|
100
110
|
|
101
111
|
def get_token_price_from_pools(
|
102
112
|
self,
|
103
113
|
token: ChecksumAddress,
|
104
|
-
) ->
|
114
|
+
) -> Prices | None:
|
105
115
|
pool = SeerSubgraphHandler().get_pool_by_token(
|
106
116
|
token_address=token,
|
107
117
|
collateral_address=self.seer_market.collateral_token_contract_address_checksummed,
|
@@ -111,15 +121,24 @@ class PriceManager:
|
|
111
121
|
logger.warning(f"Could not find a pool for {token=}")
|
112
122
|
return None
|
113
123
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
124
|
+
if (
|
125
|
+
Web3.to_checksum_address(pool.token0.id)
|
126
|
+
== self.seer_market.collateral_token_contract_address_checksummed
|
127
|
+
):
|
128
|
+
price_coll_in_asking = (
|
129
|
+
pool.token1Price
|
130
|
+
) # how many outcome tokens per 1 collateral
|
131
|
+
price_asking_in_coll = (
|
132
|
+
pool.token0Price
|
133
|
+
) # how many collateral tokens per 1 outcome
|
134
|
+
else:
|
135
|
+
price_coll_in_asking = pool.token0Price
|
136
|
+
price_asking_in_coll = pool.token1Price
|
137
|
+
|
138
|
+
return Prices(
|
139
|
+
priceOfCollateralInAskingToken=price_coll_in_asking,
|
140
|
+
priceOfAskingTokenInCollateral=price_asking_in_coll,
|
121
141
|
)
|
122
|
-
return price
|
123
142
|
|
124
143
|
def build_probability_map(self) -> dict[OutcomeStr, Probability]:
|
125
144
|
# Inspired by https://github.com/seer-pm/demo/blob/ca682153a6b4d4dd3dcc4ad8bdcbe32202fc8fe7/web/src/hooks/useMarketOdds.ts#L15
|
@@ -160,6 +179,6 @@ class PriceManager:
|
|
160
179
|
outcome = self.seer_market.outcomes[
|
161
180
|
self.seer_market.wrapped_tokens.index(outcome_token)
|
162
181
|
]
|
163
|
-
normalized_prices[
|
182
|
+
normalized_prices[outcome] = new_price
|
164
183
|
|
165
184
|
return normalized_prices
|
@@ -25,9 +25,10 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
25
25
|
AgentMarket,
|
26
26
|
FilterBy,
|
27
27
|
MarketFees,
|
28
|
-
|
28
|
+
ParentMarket,
|
29
29
|
ProcessedMarket,
|
30
30
|
ProcessedTradedMarket,
|
31
|
+
QuestionType,
|
31
32
|
SortBy,
|
32
33
|
)
|
33
34
|
from prediction_market_agent_tooling.markets.blockchain_utils import store_trades
|
@@ -68,8 +69,9 @@ from prediction_market_agent_tooling.tools.contract import (
|
|
68
69
|
)
|
69
70
|
from prediction_market_agent_tooling.tools.cow.cow_order import (
|
70
71
|
NoLiquidityAvailableOnCowException,
|
71
|
-
|
72
|
+
OrderStatusError,
|
72
73
|
get_orders_by_owner,
|
74
|
+
get_trades_by_order_uid,
|
73
75
|
get_trades_by_owner,
|
74
76
|
swap_tokens_waiting,
|
75
77
|
wait_for_order_completion,
|
@@ -85,7 +87,7 @@ from prediction_market_agent_tooling.tools.tokens.usd import (
|
|
85
87
|
get_token_in_usd,
|
86
88
|
get_usd_in_token,
|
87
89
|
)
|
88
|
-
from prediction_market_agent_tooling.tools.utils import utcnow
|
90
|
+
from prediction_market_agent_tooling.tools.utils import check_not_none, utcnow
|
89
91
|
|
90
92
|
# We place a larger bet amount by default than Omen so that cow presents valid quotes.
|
91
93
|
SEER_TINY_BET_AMOUNT = USD(0.1)
|
@@ -137,45 +139,34 @@ class SeerAgentMarket(AgentMarket):
|
|
137
139
|
web3=web3,
|
138
140
|
)
|
139
141
|
|
142
|
+
def get_price_manager(self) -> PriceManager:
|
143
|
+
return PriceManager.build(HexBytes(HexStr(self.id)))
|
144
|
+
|
140
145
|
def get_token_in_usd(self, x: CollateralToken) -> USD:
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
146
|
+
p = self.get_price_manager()
|
147
|
+
sdai_amount = p.get_amount_of_collateral_in_token(
|
148
|
+
# Hard-coded SDAI, because Seer is atm hard-coded it as well, and it's needed in case of fallback to pools. CoW would work with other tokens as well.
|
149
|
+
SDAI_CONTRACT_ADDRESS,
|
150
|
+
x,
|
151
|
+
)
|
152
|
+
if sdai_amount is None:
|
153
|
+
raise RuntimeError(
|
154
|
+
"Both CoW and pool-fallback way of getting price failed."
|
148
155
|
)
|
149
|
-
|
150
|
-
if usd_token_price is None:
|
151
|
-
raise RuntimeError(
|
152
|
-
"Both CoW and pool-fallback way of getting price failed."
|
153
|
-
) from e
|
154
|
-
return USD(x.value * usd_token_price.value)
|
155
|
-
|
156
|
-
def get_collateral_price_from_pools(self) -> USD | None:
|
157
|
-
p = PriceManager.build(HexBytes(HexStr(self.id)))
|
158
|
-
token_price = p.get_token_price_from_pools(token=SDAI_CONTRACT_ADDRESS)
|
159
|
-
if token_price:
|
160
|
-
return get_token_in_usd(token_price, SDAI_CONTRACT_ADDRESS)
|
161
|
-
|
162
|
-
return None
|
156
|
+
return get_token_in_usd(sdai_amount, SDAI_CONTRACT_ADDRESS)
|
163
157
|
|
164
158
|
def get_usd_in_token(self, x: USD) -> CollateralToken:
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
159
|
+
p = self.get_price_manager()
|
160
|
+
token_amount = p.get_amount_of_token_in_collateral(
|
161
|
+
# Hard-coded SDAI, because Seer is atm hard-coded it as well, and it's needed in case of fallback to pools. CoW would work with other tokens as well.
|
162
|
+
SDAI_CONTRACT_ADDRESS,
|
163
|
+
get_usd_in_token(x, SDAI_CONTRACT_ADDRESS),
|
164
|
+
)
|
165
|
+
if token_amount is None:
|
166
|
+
raise RuntimeError(
|
167
|
+
"Both CoW and pool-fallback way of getting price failed."
|
172
168
|
)
|
173
|
-
|
174
|
-
if not usd_token_price:
|
175
|
-
raise RuntimeError(
|
176
|
-
"Both CoW and pool-fallback way of getting price failed."
|
177
|
-
) from e
|
178
|
-
return CollateralToken(x.value / usd_token_price.value)
|
169
|
+
return token_amount
|
179
170
|
|
180
171
|
def get_buy_token_amount(
|
181
172
|
self, bet_amount: USD | CollateralToken, outcome_str: OutcomeStr
|
@@ -191,16 +182,15 @@ class SeerAgentMarket(AgentMarket):
|
|
191
182
|
|
192
183
|
bet_amount_in_tokens = self.get_in_token(bet_amount)
|
193
184
|
|
194
|
-
p =
|
195
|
-
|
185
|
+
p = self.get_price_manager()
|
186
|
+
amount_outcome_tokens = p.get_amount_of_collateral_in_token(
|
196
187
|
token=outcome_token, collateral_exchange_amount=bet_amount_in_tokens
|
197
188
|
)
|
198
|
-
if not
|
189
|
+
if not amount_outcome_tokens:
|
199
190
|
logger.info(f"Could not get price for token {outcome_token}")
|
200
191
|
return None
|
201
192
|
|
202
|
-
amount_outcome_tokens
|
203
|
-
return OutcomeToken(amount_outcome_tokens)
|
193
|
+
return OutcomeToken(amount_outcome_tokens.value)
|
204
194
|
|
205
195
|
def get_sell_value_of_outcome_token(
|
206
196
|
self, outcome: OutcomeStr, amount: OutcomeToken
|
@@ -209,26 +199,18 @@ class SeerAgentMarket(AgentMarket):
|
|
209
199
|
return CollateralToken.zero()
|
210
200
|
|
211
201
|
wrapped_outcome_token = self.get_wrapped_token_for_outcome(outcome)
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
logger.warning(
|
222
|
-
f"No liquidity available on Cow for {wrapped_outcome_token} -> {self.collateral_token_contract_address_checksummed}."
|
202
|
+
|
203
|
+
p = self.get_price_manager()
|
204
|
+
value_outcome_token_in_collateral = p.get_amount_of_token_in_collateral(
|
205
|
+
wrapped_outcome_token, amount.as_token
|
206
|
+
)
|
207
|
+
|
208
|
+
if value_outcome_token_in_collateral is None:
|
209
|
+
raise RuntimeError(
|
210
|
+
f"Could not get price for token from pools for {wrapped_outcome_token}"
|
223
211
|
)
|
224
|
-
|
225
|
-
|
226
|
-
if not price:
|
227
|
-
logger.info(
|
228
|
-
f"Could not get price for token from pools for {wrapped_outcome_token}"
|
229
|
-
)
|
230
|
-
raise e
|
231
|
-
return CollateralToken(price.value * amount.value)
|
212
|
+
|
213
|
+
return value_outcome_token_in_collateral
|
232
214
|
|
233
215
|
@staticmethod
|
234
216
|
def get_trade_balance(api_keys: APIKeys) -> USD:
|
@@ -393,6 +375,22 @@ class SeerAgentMarket(AgentMarket):
|
|
393
375
|
probabilities=probability_map,
|
394
376
|
upper_bound=model.upper_bound,
|
395
377
|
lower_bound=model.lower_bound,
|
378
|
+
parent=(
|
379
|
+
ParentMarket(
|
380
|
+
market=(
|
381
|
+
check_not_none(
|
382
|
+
SeerAgentMarket.from_data_model_with_subgraph(
|
383
|
+
model.parent_market,
|
384
|
+
seer_subgraph,
|
385
|
+
False,
|
386
|
+
)
|
387
|
+
)
|
388
|
+
),
|
389
|
+
parent_outcome=model.parent_outcome,
|
390
|
+
)
|
391
|
+
if model.parent_market
|
392
|
+
else None
|
393
|
+
),
|
396
394
|
)
|
397
395
|
|
398
396
|
return market
|
@@ -404,7 +402,7 @@ class SeerAgentMarket(AgentMarket):
|
|
404
402
|
filter_by: FilterBy = FilterBy.OPEN,
|
405
403
|
created_after: t.Optional[DatetimeUTC] = None,
|
406
404
|
excluded_questions: set[str] | None = None,
|
407
|
-
|
405
|
+
question_type: QuestionType = QuestionType.ALL,
|
408
406
|
include_conditional_markets: bool = False,
|
409
407
|
) -> t.Sequence["SeerAgentMarket"]:
|
410
408
|
seer_subgraph = SeerSubgraphHandler()
|
@@ -413,8 +411,8 @@ class SeerAgentMarket(AgentMarket):
|
|
413
411
|
limit=limit,
|
414
412
|
sort_by=sort_by,
|
415
413
|
filter_by=filter_by,
|
416
|
-
|
417
|
-
include_conditional_markets=
|
414
|
+
question_type=question_type,
|
415
|
+
include_conditional_markets=include_conditional_markets,
|
418
416
|
)
|
419
417
|
|
420
418
|
# We exclude the None values below because `from_data_model_with_subgraph` can return None, which
|
@@ -456,7 +454,8 @@ class SeerAgentMarket(AgentMarket):
|
|
456
454
|
f"Could not fetch pool for token {outcome_token}, no liquidity available for outcome."
|
457
455
|
)
|
458
456
|
return CollateralToken(0)
|
459
|
-
|
457
|
+
|
458
|
+
p = self.get_price_manager()
|
460
459
|
total = CollateralToken(0)
|
461
460
|
|
462
461
|
for token_address in [pool.token0.id, pool.token1.id]:
|
@@ -469,19 +468,13 @@ class SeerAgentMarket(AgentMarket):
|
|
469
468
|
for_address=Web3.to_checksum_address(HexAddress(HexStr(pool.id.hex()))),
|
470
469
|
web3=web3,
|
471
470
|
)
|
472
|
-
|
473
|
-
|
474
|
-
token_price_in_sdai = (
|
475
|
-
p.get_token_price_from_pools(token=token_address_checksummed)
|
476
|
-
if token_address_checksummed
|
477
|
-
!= self.collateral_token_contract_address_checksummed
|
478
|
-
else CollateralToken(1.0)
|
471
|
+
collateral_balance = p.get_amount_of_token_in_collateral(
|
472
|
+
token_address_checksummed, token_balance
|
479
473
|
)
|
480
474
|
|
481
475
|
# We ignore the liquidity in outcome tokens if price unknown.
|
482
|
-
if
|
483
|
-
|
484
|
-
total += sdai_balance
|
476
|
+
if collateral_balance:
|
477
|
+
total += collateral_balance
|
485
478
|
|
486
479
|
return total
|
487
480
|
|
@@ -542,15 +535,23 @@ class SeerAgentMarket(AgentMarket):
|
|
542
535
|
slippage_tolerance=slippage_tolerance,
|
543
536
|
)
|
544
537
|
order_metadata = asyncio.run(wait_for_order_completion(order=order))
|
545
|
-
logger.
|
538
|
+
logger.info(
|
546
539
|
f"Swapped {sell_token} for {buy_token}. Order details {order_metadata}"
|
547
540
|
)
|
548
|
-
|
541
|
+
trades = get_trades_by_order_uid(HexBytes(order_metadata.uid.root))
|
542
|
+
if len(trades) != 1:
|
543
|
+
raise ValueError(
|
544
|
+
f"Expected exactly 1 trade from {order_metadata=}, but got {len(trades)=}."
|
545
|
+
)
|
546
|
+
cow_tx_hash = trades[0].txHash
|
547
|
+
logger.info(f"TxHash for {order_metadata.uid.root=} is {cow_tx_hash=}.")
|
548
|
+
return cow_tx_hash.hex()
|
549
549
|
|
550
550
|
except (
|
551
551
|
UnexpectedResponseError,
|
552
552
|
TimeoutError,
|
553
553
|
NoLiquidityAvailableOnCowException,
|
554
|
+
OrderStatusError,
|
554
555
|
) as e:
|
555
556
|
# We don't retry if not enough balance.
|
556
557
|
if "InsufficientBalance" in str(e):
|
@@ -601,14 +602,6 @@ class SeerAgentMarket(AgentMarket):
|
|
601
602
|
collateral_contract, amount_wei, api_keys, web3
|
602
603
|
)
|
603
604
|
|
604
|
-
collateral_balance = collateral_contract.balanceOf(
|
605
|
-
api_keys.bet_from_address, web3=web3
|
606
|
-
)
|
607
|
-
if collateral_balance < amount_wei:
|
608
|
-
raise ValueError(
|
609
|
-
f"Balance {collateral_balance} not enough for bet size {amount}"
|
610
|
-
)
|
611
|
-
|
612
605
|
return self._swap_tokens_with_fallback(
|
613
606
|
sell_token=collateral_contract.address,
|
614
607
|
buy_token=outcome_token,
|
@@ -16,7 +16,7 @@ from prediction_market_agent_tooling.gtypes import ChecksumAddress, Wei
|
|
16
16
|
from prediction_market_agent_tooling.loggers import logger
|
17
17
|
from prediction_market_agent_tooling.markets.agent_market import (
|
18
18
|
FilterBy,
|
19
|
-
|
19
|
+
QuestionType,
|
20
20
|
SortBy,
|
21
21
|
)
|
22
22
|
from prediction_market_agent_tooling.markets.base_subgraph_handler import (
|
@@ -62,7 +62,9 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
62
62
|
)
|
63
63
|
)
|
64
64
|
|
65
|
-
def _get_fields_for_markets(
|
65
|
+
def _get_fields_for_markets(
|
66
|
+
self, markets_field: FieldPath, current_level: int = 0, max_level: int = 1
|
67
|
+
) -> list[FieldPath]:
|
66
68
|
fields = [
|
67
69
|
markets_field.id,
|
68
70
|
markets_field.factory,
|
@@ -76,7 +78,6 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
76
78
|
markets_field.payoutNumerators,
|
77
79
|
markets_field.hasAnswers,
|
78
80
|
markets_field.blockTimestamp,
|
79
|
-
markets_field.parentMarket.id,
|
80
81
|
markets_field.openingTs,
|
81
82
|
markets_field.finalizeTs,
|
82
83
|
markets_field.wrappedTokens,
|
@@ -84,7 +85,25 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
84
85
|
markets_field.upperBound,
|
85
86
|
markets_field.lowerBound,
|
86
87
|
markets_field.templateId,
|
88
|
+
# TODO: On the Subgraph, `questions` field is a kind of sub-query, instead of a classic list of values.
|
89
|
+
# See how it is shown on their UI: https://thegraph.com/explorer/subgraphs/B4vyRqJaSHD8dRDb3BFRoAzuBK18c1QQcXq94JbxDxWH?view=Query&chain=arbitrum-one.
|
90
|
+
# And that doesn't work with subgrounds.
|
91
|
+
# markets_field.questions.question.id,
|
92
|
+
# markets_field.questions.question.finalize_ts,
|
93
|
+
# markets_field.questions.question.best_answer,
|
87
94
|
]
|
95
|
+
if current_level < max_level:
|
96
|
+
fields.extend(
|
97
|
+
self._get_fields_for_markets(
|
98
|
+
markets_field.parentMarket, current_level + 1, max_level
|
99
|
+
)
|
100
|
+
)
|
101
|
+
# TODO: Same situation as with `questions` field above.
|
102
|
+
# fields.extend(
|
103
|
+
# self._get_fields_for_markets(
|
104
|
+
# markets_field.childMarkets, current_level + 1, max_level
|
105
|
+
# )
|
106
|
+
# )
|
88
107
|
return fields
|
89
108
|
|
90
109
|
@staticmethod
|
@@ -114,7 +133,8 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
114
133
|
filter_by: FilterBy,
|
115
134
|
outcome_supply_gt_if_open: Wei,
|
116
135
|
include_conditional_markets: bool = False,
|
117
|
-
|
136
|
+
question_type: QuestionType = QuestionType.ALL,
|
137
|
+
parent_market_id: HexBytes | None = None,
|
118
138
|
) -> dict[Any, Any]:
|
119
139
|
now = to_int_timestamp(utcnow())
|
120
140
|
|
@@ -136,9 +156,12 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
136
156
|
if not include_conditional_markets:
|
137
157
|
and_stms["parentMarket"] = ADDRESS_ZERO.lower()
|
138
158
|
|
159
|
+
if parent_market_id:
|
160
|
+
and_stms["parentMarket"] = parent_market_id.hex().lower()
|
161
|
+
|
139
162
|
outcome_filters: list[dict[str, t.Any]] = []
|
140
163
|
|
141
|
-
if
|
164
|
+
if question_type == QuestionType.SCALAR:
|
142
165
|
# Template ID "1" + UP/DOWN outcomes for scalar markets
|
143
166
|
and_stms["templateId"] = TemplateId.SCALAR.value
|
144
167
|
up_filter = SeerSubgraphHandler._create_case_variations_condition(
|
@@ -149,7 +172,7 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
149
172
|
)
|
150
173
|
outcome_filters.extend([up_filter, down_filter])
|
151
174
|
|
152
|
-
elif
|
175
|
+
elif question_type == QuestionType.BINARY:
|
153
176
|
# Template ID "2" + YES/NO outcomes for binary markets
|
154
177
|
and_stms["templateId"] = TemplateId.CATEGORICAL.value
|
155
178
|
yes_filter = SeerSubgraphHandler._create_case_variations_condition(
|
@@ -160,7 +183,7 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
160
183
|
)
|
161
184
|
outcome_filters.extend([yes_filter, no_filter])
|
162
185
|
|
163
|
-
elif
|
186
|
+
elif question_type == QuestionType.CATEGORICAL:
|
164
187
|
# Template ID 2 (categorical) OR Template ID 3 (multi-categorical,
|
165
188
|
# we treat them as categorical for now for simplicity)
|
166
189
|
# https://reality.eth.limo/app/docs/html/contracts.html#templates
|
@@ -211,16 +234,18 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
211
234
|
sort_by: SortBy = SortBy.NONE,
|
212
235
|
limit: int | None = None,
|
213
236
|
outcome_supply_gt_if_open: Wei = Wei(0),
|
214
|
-
|
237
|
+
question_type: QuestionType = QuestionType.ALL,
|
215
238
|
include_conditional_markets: bool = False,
|
239
|
+
parent_market_id: HexBytes | None = None,
|
216
240
|
) -> list[SeerMarket]:
|
217
241
|
sort_direction, sort_by_field = self._build_sort_params(sort_by)
|
218
242
|
|
219
243
|
where_stms = self._build_where_statements(
|
220
244
|
filter_by=filter_by,
|
221
245
|
outcome_supply_gt_if_open=outcome_supply_gt_if_open,
|
246
|
+
parent_market_id=parent_market_id,
|
247
|
+
question_type=question_type,
|
222
248
|
include_conditional_markets=include_conditional_markets,
|
223
|
-
market_type=market_type,
|
224
249
|
)
|
225
250
|
|
226
251
|
# These values can not be set to `None`, but they can be omitted.
|
@@ -58,7 +58,3 @@ class CreateCategoricalMarketsParams(BaseModel):
|
|
58
58
|
) # typed as int for later .model_dump() usage (if using Wei, other keys also exported)
|
59
59
|
opening_time: int = Field(..., alias="openingTime")
|
60
60
|
token_names: list[str] = Field(..., alias="tokenNames")
|
61
|
-
|
62
|
-
|
63
|
-
class SeerParentMarket(BaseModel):
|
64
|
-
id: HexBytes
|
@@ -19,7 +19,6 @@ from prediction_market_agent_tooling.markets.seer.seer_contracts import (
|
|
19
19
|
from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
|
20
20
|
SeerSubgraphHandler,
|
21
21
|
)
|
22
|
-
from prediction_market_agent_tooling.tools.contract import ContractERC20OnGnosisChain
|
23
22
|
|
24
23
|
|
25
24
|
class SwapPoolHandler:
|
@@ -80,7 +79,7 @@ class SwapPoolHandler:
|
|
80
79
|
amount_out_minimum = self._calculate_amount_out_minimum(
|
81
80
|
amount_wei=amount_wei,
|
82
81
|
token_in=token_in,
|
83
|
-
price_outcome_token=price_outcome_token,
|
82
|
+
price_outcome_token=price_outcome_token.priceOfCollateralInAskingToken,
|
84
83
|
)
|
85
84
|
|
86
85
|
p = ExactInputSingleParams(
|
@@ -91,15 +90,6 @@ class SwapPoolHandler:
|
|
91
90
|
amount_out_minimum=amount_out_minimum,
|
92
91
|
)
|
93
92
|
|
94
|
-
# make sure user has enough tokens to sell
|
95
|
-
balance_collateral_token = ContractERC20OnGnosisChain(
|
96
|
-
address=token_in
|
97
|
-
).balanceOf(self.api_keys.bet_from_address, web3=web3)
|
98
|
-
if balance_collateral_token < amount_wei:
|
99
|
-
raise ValueError(
|
100
|
-
f"Balance {balance_collateral_token} of {token_in} insufficient for trade, required {amount_wei}"
|
101
|
-
)
|
102
|
-
|
103
93
|
tx_receipt = SwaprRouterContract().exact_input_single(
|
104
94
|
api_keys=self.api_keys, params=p, web3=web3
|
105
95
|
)
|
@@ -33,12 +33,7 @@ from cowdao_cowpy.order_book.generated.model import (
|
|
33
33
|
from eth_account import Account
|
34
34
|
from eth_account.signers.local import LocalAccount
|
35
35
|
from eth_keys.datatypes import PrivateKey as eth_keys_PrivateKey
|
36
|
-
from tenacity import
|
37
|
-
retry_if_not_exception_type,
|
38
|
-
stop_after_attempt,
|
39
|
-
wait_exponential,
|
40
|
-
wait_fixed,
|
41
|
-
)
|
36
|
+
from tenacity import stop_after_attempt, wait_exponential, wait_fixed
|
42
37
|
from web3 import Web3
|
43
38
|
|
44
39
|
from prediction_market_agent_tooling.config import APIKeys
|
@@ -54,7 +49,7 @@ from prediction_market_agent_tooling.markets.omen.cow_contracts import (
|
|
54
49
|
CowGPv2SettlementContract,
|
55
50
|
)
|
56
51
|
from prediction_market_agent_tooling.tools.contract import ContractERC20OnGnosisChain
|
57
|
-
from prediction_market_agent_tooling.tools.cow.models import
|
52
|
+
from prediction_market_agent_tooling.tools.cow.models import MinimalisticTrade, Order
|
58
53
|
from prediction_market_agent_tooling.tools.cow.semaphore import postgres_rate_limited
|
59
54
|
from prediction_market_agent_tooling.tools.utils import utcnow
|
60
55
|
|
@@ -108,7 +103,6 @@ def get_sell_token_amount(
|
|
108
103
|
@tenacity.retry(
|
109
104
|
stop=stop_after_attempt(4),
|
110
105
|
wait=wait_exponential(min=4, max=10),
|
111
|
-
retry=retry_if_not_exception_type(NoLiquidityAvailableOnCowException),
|
112
106
|
)
|
113
107
|
def get_quote(
|
114
108
|
amount_wei: Wei,
|
@@ -198,7 +192,7 @@ def handle_allowance(
|
|
198
192
|
reraise=True,
|
199
193
|
stop=stop_after_attempt(3),
|
200
194
|
wait=wait_fixed(1),
|
201
|
-
retry=tenacity.retry_if_not_exception_type((TimeoutError
|
195
|
+
retry=tenacity.retry_if_not_exception_type((TimeoutError)),
|
202
196
|
after=lambda x: logger.debug(f"swap_tokens_waiting failed, {x.attempt_number=}."),
|
203
197
|
)
|
204
198
|
def swap_tokens_waiting(
|
@@ -355,14 +349,33 @@ async def sign_safe_cow_swap(
|
|
355
349
|
)
|
356
350
|
def get_trades_by_owner(
|
357
351
|
owner: ChecksumAddress,
|
358
|
-
) -> list[
|
352
|
+
) -> list[MinimalisticTrade]:
|
359
353
|
# Using this until cowpy gets fixed (https://github.com/cowdao-grants/cow-py/issues/35)
|
360
354
|
response = httpx.get(
|
361
355
|
f"https://api.cow.fi/xdai/api/v1/trades",
|
362
356
|
params={"owner": owner},
|
363
357
|
)
|
364
358
|
response.raise_for_status()
|
365
|
-
return [
|
359
|
+
return [MinimalisticTrade.model_validate(i) for i in response.json()]
|
360
|
+
|
361
|
+
|
362
|
+
@tenacity.retry(
|
363
|
+
stop=stop_after_attempt(3),
|
364
|
+
wait=wait_fixed(1),
|
365
|
+
after=lambda x: logger.debug(
|
366
|
+
f"get_trades_by_order_uid failed, {x.attempt_number=}."
|
367
|
+
),
|
368
|
+
)
|
369
|
+
def get_trades_by_order_uid(
|
370
|
+
order_uid: HexBytes,
|
371
|
+
) -> list[MinimalisticTrade]:
|
372
|
+
# Using this until cowpy gets fixed (https://github.com/cowdao-grants/cow-py/issues/35)
|
373
|
+
response = httpx.get(
|
374
|
+
f"https://api.cow.fi/xdai/api/v1/trades",
|
375
|
+
params={"orderUid": order_uid.hex()},
|
376
|
+
)
|
377
|
+
response.raise_for_status()
|
378
|
+
return [MinimalisticTrade.model_validate(i) for i in response.json()]
|
366
379
|
|
367
380
|
|
368
381
|
@tenacity.retry(
|
@@ -1,14 +1,16 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
2
|
from sqlmodel import Field, SQLModel
|
3
3
|
|
4
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress
|
4
|
+
from prediction_market_agent_tooling.gtypes import ChecksumAddress, HexBytes
|
5
5
|
from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
|
6
6
|
from prediction_market_agent_tooling.tools.utils import utcnow
|
7
7
|
|
8
8
|
|
9
|
-
class
|
9
|
+
class MinimalisticTrade(BaseModel):
|
10
10
|
sellToken: ChecksumAddress
|
11
11
|
buyToken: ChecksumAddress
|
12
|
+
orderUid: HexBytes
|
13
|
+
txHash: HexBytes
|
12
14
|
|
13
15
|
|
14
16
|
class Order(BaseModel):
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import tenacity
|
2
|
+
|
3
|
+
from prediction_market_agent_tooling.config import APIKeys
|
4
|
+
from prediction_market_agent_tooling.tools.caches.db_cache import db_cache
|
5
|
+
from prediction_market_agent_tooling.tools.langfuse_ import (
|
6
|
+
get_langfuse_langchain_config,
|
7
|
+
observe,
|
8
|
+
)
|
9
|
+
from prediction_market_agent_tooling.tools.utils import (
|
10
|
+
LLM_SEED,
|
11
|
+
LLM_SUPER_LOW_TEMPERATURE,
|
12
|
+
)
|
13
|
+
|
14
|
+
REPHRASE_QUESTION_PROMPT = """Given the following question of main interest: {question}
|
15
|
+
|
16
|
+
But it's conditioned on `{parent_question}` resolving to `{needed_parent_outcome}`.
|
17
|
+
|
18
|
+
Rewrite the main question to contain the parent question in the correct form.
|
19
|
+
|
20
|
+
The main question will be used as a prediction market, so it does need to be rephrased using the parent question properly. Such that the probability of the main question also accounts for the conditioned outcome.
|
21
|
+
|
22
|
+
For example:
|
23
|
+
```
|
24
|
+
Main question: What is the probability of <X> happening before <date>?
|
25
|
+
Conditioned on: Will <Y> happen before <another-date>?
|
26
|
+
Rephrased: What is the joint probability of Y happening before <another-date> and then X happening before <date>?
|
27
|
+
```
|
28
|
+
|
29
|
+
Output only the rephrased question.
|
30
|
+
"""
|
31
|
+
|
32
|
+
|
33
|
+
@tenacity.retry(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1))
|
34
|
+
@observe()
|
35
|
+
@db_cache
|
36
|
+
def rephrase_question_to_unconditioned(
|
37
|
+
question: str,
|
38
|
+
parent_question: str,
|
39
|
+
needed_parent_outcome: str,
|
40
|
+
engine: str = "gpt-4.1",
|
41
|
+
temperature: float = LLM_SUPER_LOW_TEMPERATURE,
|
42
|
+
seed: int = LLM_SEED,
|
43
|
+
prompt_template: str = REPHRASE_QUESTION_PROMPT,
|
44
|
+
max_tokens: int = 1024,
|
45
|
+
) -> str:
|
46
|
+
try:
|
47
|
+
from langchain.prompts import ChatPromptTemplate
|
48
|
+
from langchain_openai import ChatOpenAI
|
49
|
+
except ImportError:
|
50
|
+
raise ImportError("langchain not installed")
|
51
|
+
|
52
|
+
llm = ChatOpenAI(
|
53
|
+
model_name=engine,
|
54
|
+
temperature=temperature,
|
55
|
+
seed=seed,
|
56
|
+
openai_api_key=APIKeys().openai_api_key,
|
57
|
+
)
|
58
|
+
|
59
|
+
prompt = ChatPromptTemplate.from_template(template=prompt_template)
|
60
|
+
messages = prompt.format_messages(
|
61
|
+
question=question,
|
62
|
+
parent_question=parent_question,
|
63
|
+
needed_parent_outcome=needed_parent_outcome,
|
64
|
+
)
|
65
|
+
completion = str(
|
66
|
+
llm.invoke(
|
67
|
+
messages, max_tokens=max_tokens, config=get_langfuse_langchain_config()
|
68
|
+
).content
|
69
|
+
)
|
70
|
+
|
71
|
+
return completion
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: prediction-market-agent-tooling
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.67.2
|
4
4
|
Summary: Tools to benchmark, deploy and monitor prediction market agents.
|
5
5
|
Author: Gnosis
|
6
6
|
Requires-Python: >=3.10,<3.13
|
@@ -42,6 +42,7 @@ Requires-Dist: proto-plus (>=1.0.0,<2.0.0)
|
|
42
42
|
Requires-Dist: protobuf (>=5.0.0,<6.0.0)
|
43
43
|
Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
|
44
44
|
Requires-Dist: pydantic (>=2.6.1,<3.0.0)
|
45
|
+
Requires-Dist: pydantic-ai (>=0.1.9,<1.0.0)
|
45
46
|
Requires-Dist: pydantic-settings (>=2.4.0,<3.0.0)
|
46
47
|
Requires-Dist: pymongo (>=4.8.0,<5.0.0)
|
47
48
|
Requires-Dist: pytest-postgresql (>=6.1.1,<7.0.0)
|
@@ -26,7 +26,7 @@ prediction_market_agent_tooling/benchmark/utils.py,sha256=vmVTQLER8I7MM_bHFiavrN
|
|
26
26
|
prediction_market_agent_tooling/chains.py,sha256=1qQstoqXMwqwM7k-KH7MjMz8Ei-D83KZByvDbCZpAxs,116
|
27
27
|
prediction_market_agent_tooling/config.py,sha256=-kJfdDr-m0R-tGZ1KRI-hJJk0mXDt142CAlvwaJ2N2I,11778
|
28
28
|
prediction_market_agent_tooling/data_download/langfuse_data_downloader.py,sha256=VY23h324VKIVkevj1B1O-zL1eEp9AElmcfn6SwYDUSc,14246
|
29
|
-
prediction_market_agent_tooling/deploy/agent.py,sha256
|
29
|
+
prediction_market_agent_tooling/deploy/agent.py,sha256=-kLE5US4F3tFOhpztO4PHgopz5xTTGU-iHdgrRBIOLI,30623
|
30
30
|
prediction_market_agent_tooling/deploy/agent_example.py,sha256=yS1fWkHynr9MYGNOM2WsCnRWLPaffY4bOc6bIudrdd4,1377
|
31
31
|
prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=wiUV_zyB9ASLH-VUETIme0H_IMih8jZwiwEmcRa3W5Q,21125
|
32
32
|
prediction_market_agent_tooling/deploy/constants.py,sha256=iobTlZpQD6_2UL9TfoElAnBEbqzIIAKZSsAoMCGhwmA,331
|
@@ -40,7 +40,7 @@ prediction_market_agent_tooling/jobs/jobs_models.py,sha256=DoZ9dlvVhpNrnINiR1uy6
|
|
40
40
|
prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=lCymxn0iH4xDmqouTP2LMORoGCiTzlK1_yqYtx1Njj4,5132
|
41
41
|
prediction_market_agent_tooling/loggers.py,sha256=o1HyvwtK1DbuC0YWQwJNqzXLLbSC41gNBkEUxiAziEg,5796
|
42
42
|
prediction_market_agent_tooling/logprobs_parser.py,sha256=DBlBQtWX8_URXhzTU3YWIPa76Zx3QDHlx1ARqbgJsVI,5008
|
43
|
-
prediction_market_agent_tooling/markets/agent_market.py,sha256=
|
43
|
+
prediction_market_agent_tooling/markets/agent_market.py,sha256=4ktJolOR4z4M7o3qnj2HBnBIA6WgXtYNe_Mbzgp5gTc,20411
|
44
44
|
prediction_market_agent_tooling/markets/base_subgraph_handler.py,sha256=7RaYO_4qAmQ6ZGM8oPK2-CkiJfKmV9MxM-rJlduaecU,1971
|
45
45
|
prediction_market_agent_tooling/markets/blockchain_utils.py,sha256=6REOt70v3vnzmtCbuRcUTdwt6htXy9nAfNkLOH3Bv1U,2987
|
46
46
|
prediction_market_agent_tooling/markets/categorize.py,sha256=orLZlPaHgeREU66m1amxfWikeV77idV4sZDPB8NgSD0,1300
|
@@ -48,17 +48,17 @@ prediction_market_agent_tooling/markets/data_models.py,sha256=x4dBbdPb7dd2JiaD15
|
|
48
48
|
prediction_market_agent_tooling/markets/manifold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
prediction_market_agent_tooling/markets/manifold/api.py,sha256=tWnjuqvU8pcCuja2B_ynHeds1iiEFc6QWHjeSO_GSxY,7676
|
50
50
|
prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=3z1gFbPMEgCDGqeH-IK8wcvqmIHgLdZX8C2M1UQ7iDw,6740
|
51
|
-
prediction_market_agent_tooling/markets/manifold/manifold.py,sha256=
|
51
|
+
prediction_market_agent_tooling/markets/manifold/manifold.py,sha256=wbv2dMWFinxCiNJoGJQuzC9o9XiwmWPJ4fI2dJAx_IQ,5377
|
52
52
|
prediction_market_agent_tooling/markets/manifold/utils.py,sha256=DCigEbpGWkXR-RZT_oJrvJhilmxKFAxcWMjUFi0nBBI,530
|
53
53
|
prediction_market_agent_tooling/markets/market_fees.py,sha256=YeK3ynjYIguB0xf6sO5iyg9lOdW_HD4C6nbJfiGyRCU,1351
|
54
|
-
prediction_market_agent_tooling/markets/markets.py,sha256=
|
54
|
+
prediction_market_agent_tooling/markets/markets.py,sha256=jEyYZVubrxnMvPHVO3ofxVXgzwmQ5sPlPx4QAVLy9qc,2777
|
55
55
|
prediction_market_agent_tooling/markets/metaculus/api.py,sha256=4TRPGytQQbSdf42DCg2M_JWYPAuNjqZ3eBqaQBLkNks,2736
|
56
56
|
prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=WjPt0MKeJNtoY-8oLQTLC8vQYYQ-dBj8UZoPq-UBYsQ,3288
|
57
|
-
prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=
|
57
|
+
prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=JO7hO5RdoW0qFZ3HzSMNeAivT6ddqyVTpbq6M-WmonY,5235
|
58
58
|
prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
59
|
prediction_market_agent_tooling/markets/omen/cow_contracts.py,sha256=sFaW82u_haL4nze8fjTmnQsOuV0OecunQlAhh1OAw0w,1091
|
60
60
|
prediction_market_agent_tooling/markets/omen/data_models.py,sha256=RsBYSbM4deA6Os4kQ3egH3HvwT80tQho6T1yyoATCMs,31103
|
61
|
-
prediction_market_agent_tooling/markets/omen/omen.py,sha256=
|
61
|
+
prediction_market_agent_tooling/markets/omen/omen.py,sha256=UUeAxVRN0eSuRH2_T2qmYO7yEp3xf2_wv2aLtCtdDk0,50623
|
62
62
|
prediction_market_agent_tooling/markets/omen/omen_constants.py,sha256=XtRk4vpxwUYkTndfjlcmghA-NOIneV8zdHFdyI7tHhM,487
|
63
63
|
prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=27-HRngTqfk_wgvttB3GeVHhy_O2YZcz9izo9OufOI0,29991
|
64
64
|
prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=D-ubf_LumHs_c5rBAAntQ8wGKprtO2V1JZeedmChNIE,11035
|
@@ -66,17 +66,17 @@ prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=2fL
|
|
66
66
|
prediction_market_agent_tooling/markets/polymarket/api.py,sha256=-WW49xTYxzb6wBWZ8t6oOpf9puD6Vph8y2AYBocLGcM,4211
|
67
67
|
prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=iRorGwuC9lXcWiVeOqykPip7FSx41RCAo6FOhyKlokU,4971
|
68
68
|
prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=QQoFZp1hSllB9r3yaiglMbEtP3YNCCMm3VmqZ2RDnlo,10576
|
69
|
-
prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=
|
69
|
+
prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=sNlhjTmmsmEXpjj7vGdLrZaZOJYXTTGrXjaZBgu8P0M,6689
|
70
70
|
prediction_market_agent_tooling/markets/polymarket/polymarket_subgraph_handler.py,sha256=bjgTQXf7HhN0Pc_oy5fFNE6v12F8rK05nq6bozgOYIg,1587
|
71
71
|
prediction_market_agent_tooling/markets/polymarket/utils.py,sha256=A_diygWKYyp4WHbxAlAVAuC0S0ZqbEKE80wxL6mxHKQ,1275
|
72
|
-
prediction_market_agent_tooling/markets/seer/data_models.py,sha256=
|
72
|
+
prediction_market_agent_tooling/markets/seer/data_models.py,sha256=CPWJptYHHCINgKIsfOO8b7anqQ_FMfyYL9tzESnnxOw,6103
|
73
73
|
prediction_market_agent_tooling/markets/seer/exceptions.py,sha256=cEObdjluivD94tgOLzmimR7wgQEOt6SRakrYdhsRQtk,112
|
74
|
-
prediction_market_agent_tooling/markets/seer/price_manager.py,sha256=
|
75
|
-
prediction_market_agent_tooling/markets/seer/seer.py,sha256=
|
74
|
+
prediction_market_agent_tooling/markets/seer/price_manager.py,sha256=PZf6-6zc6DvH1u65wHWyeD55lgG-UGnN_xzBMvrb3ug,7120
|
75
|
+
prediction_market_agent_tooling/markets/seer/seer.py,sha256=WX8xFdxnDiXCj4AkXnnjNvb3DJ4d8pQQdNK9bb397RQ,26670
|
76
76
|
prediction_market_agent_tooling/markets/seer/seer_contracts.py,sha256=uMzpHpI6_tgfhWxPzupLdUJlZ1P2wr0rRiYjAGClKgU,4984
|
77
|
-
prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py,sha256=
|
78
|
-
prediction_market_agent_tooling/markets/seer/subgraph_data_models.py,sha256=
|
79
|
-
prediction_market_agent_tooling/markets/seer/swap_pool_handler.py,sha256=
|
77
|
+
prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py,sha256=Thg9GWnA8wnLR_fdVHDo5k1X0Olk5BIiOcksRFDcvR8,12979
|
78
|
+
prediction_market_agent_tooling/markets/seer/subgraph_data_models.py,sha256=7szvK5we3LF38UthWHg5V3LD6C137O5_WMEcP9Dwl8w,1763
|
79
|
+
prediction_market_agent_tooling/markets/seer/swap_pool_handler.py,sha256=kyU65phG7eHUyFMuwQ2HQUIayjBe6PCjBJ_pS7p164s,3434
|
80
80
|
prediction_market_agent_tooling/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
81
|
prediction_market_agent_tooling/tools/_generic_value.py,sha256=pD_PI13lpPp1gFoljHwa_Lzlp-u2pu0m-Z7LcxwDM2U,10618
|
82
82
|
prediction_market_agent_tooling/tools/balances.py,sha256=Osab21btfJDw2Y-jT_TV-KHGrseCRxcsYeW6WcOMB8E,1050
|
@@ -88,8 +88,8 @@ prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=ZW5iI5rmjq
|
|
88
88
|
prediction_market_agent_tooling/tools/caches/serializers.py,sha256=vFDx4fsPxclXp2q0sv27j4al_M_Tj9aR2JJP-xNHQXA,2151
|
89
89
|
prediction_market_agent_tooling/tools/contract.py,sha256=pdr9ZYmj4QVUfgVKdvOU6ucYdBpJGdha_FMR_LgtcEs,22912
|
90
90
|
prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
|
91
|
-
prediction_market_agent_tooling/tools/cow/cow_order.py,sha256=
|
92
|
-
prediction_market_agent_tooling/tools/cow/models.py,sha256=
|
91
|
+
prediction_market_agent_tooling/tools/cow/cow_order.py,sha256=CtCPnBcHSrWmcCuNxqqPHQwyBTL6pLqtSvNJt49nHno,13810
|
92
|
+
prediction_market_agent_tooling/tools/cow/models.py,sha256=Z_XUAMj5AGHZE-A92oRG0DuPeXTSyWnZRIwKQ49LaXU,709
|
93
93
|
prediction_market_agent_tooling/tools/cow/semaphore.py,sha256=IJGKRgvXnRSkEt_z1i5-eFIoVMcyhr7HK0JfIz1MXdQ,3738
|
94
94
|
prediction_market_agent_tooling/tools/custom_exceptions.py,sha256=Fh8z1fbwONvP4-j7AmV_PuEcoqb6-QXa9PJ9m7guMcM,93
|
95
95
|
prediction_market_agent_tooling/tools/datetime_utc.py,sha256=8_WackjtjC8zHXrhQFTGQ6e6Fz_6llWoKR4CSFvIv9I,2766
|
@@ -113,6 +113,7 @@ prediction_market_agent_tooling/tools/perplexity/perplexity_search.py,sha256=Ii-
|
|
113
113
|
prediction_market_agent_tooling/tools/relevant_news_analysis/data_models.py,sha256=95l84aztFaxcRLLcRQ46yKJbIlOEuDAbIGLouyliDzA,1316
|
114
114
|
prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_analysis.py,sha256=r4MdP5uEMlaCwoa2XQZzGq3oZEqnoo9S4dg8uzXfSOY,5473
|
115
115
|
prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_cache.py,sha256=kNWq92T11Knb9mYBZlMiZUzOpKgCd-5adanylQUMRJA,3085
|
116
|
+
prediction_market_agent_tooling/tools/rephrase.py,sha256=mvprYrZST2LaN3F6n5Ew3IpO_xUMnKpxSMggFcZ3IgM,2319
|
116
117
|
prediction_market_agent_tooling/tools/safe.py,sha256=o477HGPQv7X_eDoOeYoELCHryiq1_102y_JVhGEPDXw,5165
|
117
118
|
prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
|
118
119
|
prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
|
@@ -127,8 +128,8 @@ prediction_market_agent_tooling/tools/tokens/usd.py,sha256=yuW8iPPtcpP4eLH2nORMD
|
|
127
128
|
prediction_market_agent_tooling/tools/transaction_cache.py,sha256=K5YKNL2_tR10Iw2TD9fuP-CTGpBbZtNdgbd0B_R7pjg,1814
|
128
129
|
prediction_market_agent_tooling/tools/utils.py,sha256=mbOGoWKalNIm7M2K51TEPGwU9oVp1Z6SPsFaBgbn6ws,7397
|
129
130
|
prediction_market_agent_tooling/tools/web3_utils.py,sha256=0r26snqCXGdLKCWA8jpe7DV8x2NPYWZwOy4oyKyDCYk,12615
|
130
|
-
prediction_market_agent_tooling-0.
|
131
|
-
prediction_market_agent_tooling-0.
|
132
|
-
prediction_market_agent_tooling-0.
|
133
|
-
prediction_market_agent_tooling-0.
|
134
|
-
prediction_market_agent_tooling-0.
|
131
|
+
prediction_market_agent_tooling-0.67.2.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
132
|
+
prediction_market_agent_tooling-0.67.2.dist-info/METADATA,sha256=dgNwYA_B2Ko36dTkAxhrAxKAfcrsvpIF26gN3sOiM-A,8770
|
133
|
+
prediction_market_agent_tooling-0.67.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
134
|
+
prediction_market_agent_tooling-0.67.2.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
|
135
|
+
prediction_market_agent_tooling-0.67.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|