prediction-market-agent-tooling 0.67.2__py3-none-any.whl → 0.67.4__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/abis/erc1155.abi.json +352 -0
- prediction_market_agent_tooling/deploy/agent.py +57 -51
- prediction_market_agent_tooling/deploy/betting_strategy.py +8 -5
- prediction_market_agent_tooling/markets/agent_market.py +24 -1
- prediction_market_agent_tooling/markets/blockchain_utils.py +5 -3
- prediction_market_agent_tooling/markets/data_models.py +23 -3
- prediction_market_agent_tooling/markets/manifold/manifold.py +2 -1
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +2 -1
- prediction_market_agent_tooling/markets/omen/omen.py +2 -1
- prediction_market_agent_tooling/markets/polymarket/api.py +9 -3
- prediction_market_agent_tooling/markets/polymarket/data_models.py +5 -3
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +17 -8
- prediction_market_agent_tooling/markets/seer/data_models.py +25 -1
- prediction_market_agent_tooling/markets/seer/seer.py +85 -26
- prediction_market_agent_tooling/markets/seer/seer_contracts.py +18 -0
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +122 -18
- prediction_market_agent_tooling/markets/seer/swap_pool_handler.py +4 -1
- prediction_market_agent_tooling/tools/contract.py +59 -0
- prediction_market_agent_tooling/tools/cow/cow_order.py +4 -1
- prediction_market_agent_tooling/tools/hexbytes_custom.py +9 -0
- prediction_market_agent_tooling/tools/httpx_cached_client.py +5 -3
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -0
- prediction_market_agent_tooling/tools/rephrase.py +1 -1
- prediction_market_agent_tooling/tools/singleton.py +11 -6
- prediction_market_agent_tooling/tools/tokens/auto_deposit.py +57 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/METADATA +1 -1
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/RECORD +30 -29
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
from enum import Enum
|
2
|
-
from typing import Annotated, Sequence
|
2
|
+
from typing import Annotated, Any, Sequence
|
3
3
|
|
4
|
-
from pydantic import BaseModel, BeforeValidator, computed_field
|
4
|
+
from pydantic import BaseModel, BeforeValidator, computed_field, model_validator
|
5
5
|
|
6
6
|
from prediction_market_agent_tooling.deploy.constants import (
|
7
7
|
DOWN_OUTCOME_LOWERCASE_IDENTIFIER,
|
@@ -157,6 +157,15 @@ class CategoricalProbabilisticAnswer(BaseModel):
|
|
157
157
|
confidence: float
|
158
158
|
reasoning: str | None = None
|
159
159
|
|
160
|
+
@model_validator(mode="before")
|
161
|
+
@classmethod
|
162
|
+
def _model_validator(cls, data: Any) -> Any:
|
163
|
+
if "p_yes" in data:
|
164
|
+
return CategoricalProbabilisticAnswer.from_probabilistic_answer(
|
165
|
+
ProbabilisticAnswer.model_validate(data)
|
166
|
+
).model_dump()
|
167
|
+
return data
|
168
|
+
|
160
169
|
@property
|
161
170
|
def probable_resolution(self) -> Resolution:
|
162
171
|
most_likely_outcome = max(
|
@@ -290,9 +299,20 @@ class Trade(BaseModel):
|
|
290
299
|
outcome: OutcomeStr
|
291
300
|
amount: USD
|
292
301
|
|
302
|
+
@model_validator(mode="before")
|
303
|
+
@classmethod
|
304
|
+
def _model_validator(cls, data: Any) -> Any:
|
305
|
+
if isinstance(data["outcome"], bool):
|
306
|
+
data["outcome"] = (
|
307
|
+
YES_OUTCOME_LOWERCASE_IDENTIFIER
|
308
|
+
if data["outcome"]
|
309
|
+
else NO_OUTCOME_LOWERCASE_IDENTIFIER
|
310
|
+
)
|
311
|
+
return data
|
312
|
+
|
293
313
|
|
294
314
|
class PlacedTrade(Trade):
|
295
|
-
id: str
|
315
|
+
id: str
|
296
316
|
|
297
317
|
@staticmethod
|
298
318
|
def from_trade(trade: Trade, id: str) -> "PlacedTrade":
|
@@ -10,6 +10,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
10
10
|
)
|
11
11
|
from prediction_market_agent_tooling.markets.agent_market import (
|
12
12
|
AgentMarket,
|
13
|
+
ConditionalFilterType,
|
13
14
|
FilterBy,
|
14
15
|
MarketFees,
|
15
16
|
QuestionType,
|
@@ -112,7 +113,7 @@ class ManifoldAgentMarket(AgentMarket):
|
|
112
113
|
created_after: t.Optional[DatetimeUTC] = None,
|
113
114
|
excluded_questions: set[str] | None = None,
|
114
115
|
question_type: QuestionType = QuestionType.ALL,
|
115
|
-
|
116
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
116
117
|
) -> t.Sequence["ManifoldAgentMarket"]:
|
117
118
|
sort: t.Literal["newest", "close-date"] | None
|
118
119
|
if sort_by == SortBy.CLOSING_SOONEST:
|
@@ -7,6 +7,7 @@ from prediction_market_agent_tooling.config import APIKeys
|
|
7
7
|
from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
|
8
8
|
from prediction_market_agent_tooling.markets.agent_market import (
|
9
9
|
AgentMarket,
|
10
|
+
ConditionalFilterType,
|
10
11
|
FilterBy,
|
11
12
|
MarketFees,
|
12
13
|
ProcessedMarket,
|
@@ -74,7 +75,7 @@ class MetaculusAgentMarket(AgentMarket):
|
|
74
75
|
created_after: t.Optional[DatetimeUTC] = None,
|
75
76
|
excluded_questions: set[str] | None = None,
|
76
77
|
question_type: QuestionType = QuestionType.ALL,
|
77
|
-
|
78
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
78
79
|
tournament_id: int | None = None,
|
79
80
|
) -> t.Sequence["MetaculusAgentMarket"]:
|
80
81
|
order_by: str | None
|
@@ -23,6 +23,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
23
23
|
from prediction_market_agent_tooling.loggers import logger
|
24
24
|
from prediction_market_agent_tooling.markets.agent_market import (
|
25
25
|
AgentMarket,
|
26
|
+
ConditionalFilterType,
|
26
27
|
FilterBy,
|
27
28
|
MarketFees,
|
28
29
|
ProcessedMarket,
|
@@ -381,7 +382,7 @@ class OmenAgentMarket(AgentMarket):
|
|
381
382
|
created_after: t.Optional[DatetimeUTC] = None,
|
382
383
|
excluded_questions: set[str] | None = None,
|
383
384
|
question_type: QuestionType = QuestionType.ALL,
|
384
|
-
|
385
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
385
386
|
) -> t.Sequence["OmenAgentMarket"]:
|
386
387
|
fetch_categorical_markets = question_type == QuestionType.CATEGORICAL
|
387
388
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import typing as t
|
2
|
+
from datetime import timedelta
|
2
3
|
from enum import Enum
|
3
4
|
from urllib.parse import urljoin
|
4
5
|
|
@@ -49,7 +50,7 @@ def get_polymarkets_with_pagination(
|
|
49
50
|
Binary markets have len(model.markets) == 1.
|
50
51
|
Categorical markets have len(model.markets) > 1
|
51
52
|
"""
|
52
|
-
client: httpx.Client = HttpxCachedClient(ttl=60).get_client()
|
53
|
+
client: httpx.Client = HttpxCachedClient(ttl=timedelta(seconds=60)).get_client()
|
53
54
|
all_markets: list[PolymarketGammaResponseDataItem] = []
|
54
55
|
offset = 0
|
55
56
|
remaining = limit
|
@@ -82,6 +83,9 @@ def get_polymarkets_with_pagination(
|
|
82
83
|
|
83
84
|
markets_to_add = []
|
84
85
|
for m in market_response.data:
|
86
|
+
# Some Polymarket markets are missing the markets field
|
87
|
+
if m.markets is None:
|
88
|
+
continue
|
85
89
|
if excluded_questions and m.title in excluded_questions:
|
86
90
|
continue
|
87
91
|
|
@@ -94,14 +98,16 @@ def get_polymarkets_with_pagination(
|
|
94
98
|
]:
|
95
99
|
continue
|
96
100
|
|
97
|
-
if created_after and created_after > m.startDate:
|
101
|
+
if not m.startDate or (created_after and created_after > m.startDate):
|
98
102
|
continue
|
99
103
|
|
100
104
|
markets_to_add.append(m)
|
101
105
|
|
102
106
|
if only_binary:
|
103
107
|
markets_to_add = [
|
104
|
-
market
|
108
|
+
market
|
109
|
+
for market in markets_to_add
|
110
|
+
if market.markets is not None and len(market.markets) == 1
|
105
111
|
]
|
106
112
|
|
107
113
|
# Add the markets from this batch to our results
|
@@ -59,16 +59,18 @@ class PolymarketGammaResponseDataItem(BaseModel):
|
|
59
59
|
id: str
|
60
60
|
slug: str
|
61
61
|
volume: float | None = None
|
62
|
-
startDate: DatetimeUTC
|
62
|
+
startDate: DatetimeUTC | None = None
|
63
63
|
endDate: DatetimeUTC | None = None
|
64
64
|
liquidity: float | None = None
|
65
65
|
liquidityClob: float | None = None
|
66
66
|
title: str
|
67
|
-
description: str
|
67
|
+
description: str | None = None
|
68
68
|
archived: bool
|
69
69
|
closed: bool
|
70
70
|
active: bool
|
71
|
-
markets: list[
|
71
|
+
markets: list[
|
72
|
+
PolymarketGammaMarket
|
73
|
+
] | None = None # Some Polymarket markets have missing markets field. We skip these markets manually when retrieving.
|
72
74
|
tags: list[PolymarketGammaTag]
|
73
75
|
|
74
76
|
@property
|
@@ -7,8 +7,10 @@ from prediction_market_agent_tooling.gtypes import (
|
|
7
7
|
OutcomeStr,
|
8
8
|
Probability,
|
9
9
|
)
|
10
|
+
from prediction_market_agent_tooling.loggers import logger
|
10
11
|
from prediction_market_agent_tooling.markets.agent_market import (
|
11
12
|
AgentMarket,
|
13
|
+
ConditionalFilterType,
|
12
14
|
FilterBy,
|
13
15
|
MarketFees,
|
14
16
|
QuestionType,
|
@@ -30,6 +32,7 @@ from prediction_market_agent_tooling.markets.polymarket.polymarket_subgraph_hand
|
|
30
32
|
PolymarketSubgraphHandler,
|
31
33
|
)
|
32
34
|
from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
|
35
|
+
from prediction_market_agent_tooling.tools.utils import check_not_none
|
33
36
|
|
34
37
|
|
35
38
|
class PolymarketAgentMarket(AgentMarket):
|
@@ -68,9 +71,11 @@ class PolymarketAgentMarket(AgentMarket):
|
|
68
71
|
]
|
69
72
|
# For a binary market, there should be exactly one payout numerator greater than 0.
|
70
73
|
if len(payout_numerator_indices_gt_0) != 1:
|
71
|
-
|
72
|
-
|
74
|
+
# These cases involve multi-categorical resolution (to be implemented https://github.com/gnosis/prediction-market-agent-tooling/issues/770)
|
75
|
+
logger.warning(
|
76
|
+
f"Only binary markets are supported. Got payout numerators: {condition_model.payoutNumerators} for condition_id {condition_id.hex()}"
|
73
77
|
)
|
78
|
+
return Resolution(outcome=None, invalid=False)
|
74
79
|
|
75
80
|
# we return the only payout numerator greater than 0 as resolution
|
76
81
|
resolved_outcome = outcomes[payout_numerator_indices_gt_0[0]]
|
@@ -82,16 +87,16 @@ class PolymarketAgentMarket(AgentMarket):
|
|
82
87
|
condition_model_dict: dict[HexBytes, ConditionSubgraphModel],
|
83
88
|
) -> "PolymarketAgentMarket":
|
84
89
|
# If len(model.markets) > 0, this denotes a categorical market.
|
85
|
-
|
86
|
-
outcomes =
|
87
|
-
outcome_prices =
|
90
|
+
markets = check_not_none(model.markets)
|
91
|
+
outcomes = markets[0].outcomes_list
|
92
|
+
outcome_prices = markets[0].outcome_prices
|
88
93
|
if not outcome_prices:
|
89
94
|
# We give random prices
|
90
95
|
outcome_prices = [0.5, 0.5]
|
91
96
|
probabilities = {o: Probability(op) for o, op in zip(outcomes, outcome_prices)}
|
92
97
|
|
93
98
|
resolution = PolymarketAgentMarket.build_resolution_from_condition(
|
94
|
-
condition_id=
|
99
|
+
condition_id=markets[0].conditionId,
|
95
100
|
condition_model_dict=condition_model_dict,
|
96
101
|
outcomes=outcomes,
|
97
102
|
)
|
@@ -124,7 +129,7 @@ class PolymarketAgentMarket(AgentMarket):
|
|
124
129
|
created_after: t.Optional[DatetimeUTC] = None,
|
125
130
|
excluded_questions: set[str] | None = None,
|
126
131
|
question_type: QuestionType = QuestionType.ALL,
|
127
|
-
|
132
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
128
133
|
) -> t.Sequence["PolymarketAgentMarket"]:
|
129
134
|
closed: bool | None
|
130
135
|
|
@@ -163,7 +168,11 @@ class PolymarketAgentMarket(AgentMarket):
|
|
163
168
|
)
|
164
169
|
|
165
170
|
condition_models = PolymarketSubgraphHandler().get_conditions(
|
166
|
-
condition_ids=[
|
171
|
+
condition_ids=[
|
172
|
+
market.markets[0].conditionId
|
173
|
+
for market in markets
|
174
|
+
if market.markets is not None
|
175
|
+
]
|
167
176
|
)
|
168
177
|
condition_models_dict = {c.id: c for c in condition_models}
|
169
178
|
|
@@ -48,10 +48,14 @@ class CreateCategoricalMarketsParams(BaseModel):
|
|
48
48
|
SEER_BASE_URL = "https://app.seer.pm"
|
49
49
|
|
50
50
|
|
51
|
-
def seer_normalize_wei(value: int | None) -> int | None:
|
51
|
+
def seer_normalize_wei(value: int | dict[str, t.Any] | None) -> int | None:
|
52
52
|
# See https://github.com/seer-pm/demo/blob/main/web/netlify/edge-functions/utils/common.ts#L22
|
53
53
|
if value is None:
|
54
54
|
return value
|
55
|
+
elif isinstance(value, dict):
|
56
|
+
if value.get("value") is None:
|
57
|
+
raise ValueError(f"Expected a dictionary with a value key, but got {value}")
|
58
|
+
value = int(value["value"])
|
55
59
|
is_in_wei = value > 1e10
|
56
60
|
return value if is_in_wei else value * 10**18
|
57
61
|
|
@@ -59,6 +63,21 @@ def seer_normalize_wei(value: int | None) -> int | None:
|
|
59
63
|
SeerNormalizedWei = Annotated[Wei | None, BeforeValidator(seer_normalize_wei)]
|
60
64
|
|
61
65
|
|
66
|
+
class MarketId(BaseModel):
|
67
|
+
id: HexBytes
|
68
|
+
|
69
|
+
|
70
|
+
class SeerQuestion(BaseModel):
|
71
|
+
id: str
|
72
|
+
best_answer: HexBytes
|
73
|
+
finalize_ts: int
|
74
|
+
|
75
|
+
|
76
|
+
class SeerMarketQuestions(BaseModel):
|
77
|
+
question: SeerQuestion
|
78
|
+
market: MarketId
|
79
|
+
|
80
|
+
|
62
81
|
class SeerMarket(BaseModel):
|
63
82
|
model_config = ConfigDict(populate_by_name=True)
|
64
83
|
|
@@ -71,6 +90,7 @@ class SeerMarket(BaseModel):
|
|
71
90
|
alias="parentOutcome", description="It comes as 0 from non-conditioned markets."
|
72
91
|
)
|
73
92
|
parent_market: t.Optional["SeerMarket"] = Field(alias="parentMarket", default=None)
|
93
|
+
template_id: int = Field(alias="templateId")
|
74
94
|
collateral_token: HexAddress = Field(alias="collateralToken")
|
75
95
|
condition_id: HexBytes = Field(alias="conditionId")
|
76
96
|
opening_ts: int = Field(alias="openingTs")
|
@@ -143,6 +163,10 @@ class SeerMarket(BaseModel):
|
|
143
163
|
return urljoin(SEER_BASE_URL, f"markets/{chain_id}/{self.id.hex()}")
|
144
164
|
|
145
165
|
|
166
|
+
class SeerMarketWithQuestions(SeerMarket):
|
167
|
+
questions: list[SeerMarketQuestions]
|
168
|
+
|
169
|
+
|
146
170
|
class RedeemParams(BaseModel):
|
147
171
|
model_config = ConfigDict(populate_by_name=True)
|
148
172
|
market: ChecksumAddress
|
@@ -2,6 +2,7 @@ import asyncio
|
|
2
2
|
import typing as t
|
3
3
|
from datetime import timedelta
|
4
4
|
|
5
|
+
import cachetools
|
5
6
|
from cowdao_cowpy.common.api.errors import UnexpectedResponseError
|
6
7
|
from eth_typing import ChecksumAddress
|
7
8
|
from web3 import Web3
|
@@ -23,8 +24,8 @@ from prediction_market_agent_tooling.gtypes import (
|
|
23
24
|
from prediction_market_agent_tooling.loggers import logger
|
24
25
|
from prediction_market_agent_tooling.markets.agent_market import (
|
25
26
|
AgentMarket,
|
27
|
+
ConditionalFilterType,
|
26
28
|
FilterBy,
|
27
|
-
MarketFees,
|
28
29
|
ParentMarket,
|
29
30
|
ProcessedMarket,
|
30
31
|
ProcessedTradedMarket,
|
@@ -32,7 +33,10 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
32
33
|
SortBy,
|
33
34
|
)
|
34
35
|
from prediction_market_agent_tooling.markets.blockchain_utils import store_trades
|
35
|
-
from prediction_market_agent_tooling.markets.data_models import
|
36
|
+
from prediction_market_agent_tooling.markets.data_models import (
|
37
|
+
ExistingPosition,
|
38
|
+
Resolution,
|
39
|
+
)
|
36
40
|
from prediction_market_agent_tooling.markets.market_fees import MarketFees
|
37
41
|
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
|
38
42
|
from prediction_market_agent_tooling.markets.omen.omen_constants import (
|
@@ -44,6 +48,7 @@ from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
|
44
48
|
from prediction_market_agent_tooling.markets.seer.data_models import (
|
45
49
|
RedeemParams,
|
46
50
|
SeerMarket,
|
51
|
+
SeerMarketWithQuestions,
|
47
52
|
)
|
48
53
|
from prediction_market_agent_tooling.markets.seer.exceptions import (
|
49
54
|
PriceCalculationError,
|
@@ -54,7 +59,9 @@ from prediction_market_agent_tooling.markets.seer.seer_contracts import (
|
|
54
59
|
SeerMarketFactory,
|
55
60
|
)
|
56
61
|
from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
|
62
|
+
SeerQuestionsCache,
|
57
63
|
SeerSubgraphHandler,
|
64
|
+
TemplateId,
|
58
65
|
)
|
59
66
|
from prediction_market_agent_tooling.markets.seer.subgraph_data_models import (
|
60
67
|
NewMarketEvent,
|
@@ -93,6 +100,11 @@ from prediction_market_agent_tooling.tools.utils import check_not_none, utcnow
|
|
93
100
|
SEER_TINY_BET_AMOUNT = USD(0.1)
|
94
101
|
|
95
102
|
|
103
|
+
SHARED_CACHE: cachetools.TTLCache[t.Hashable, t.Any] = cachetools.TTLCache(
|
104
|
+
maxsize=256, ttl=10 * 60
|
105
|
+
)
|
106
|
+
|
107
|
+
|
96
108
|
class SeerAgentMarket(AgentMarket):
|
97
109
|
wrapped_tokens: list[ChecksumAddress]
|
98
110
|
creator: HexAddress
|
@@ -257,7 +269,7 @@ class SeerAgentMarket(AgentMarket):
|
|
257
269
|
@staticmethod
|
258
270
|
def _filter_markets_contained_in_trades(
|
259
271
|
api_keys: APIKeys,
|
260
|
-
markets:
|
272
|
+
markets: t.Sequence[SeerMarket],
|
261
273
|
) -> list[SeerMarket]:
|
262
274
|
"""
|
263
275
|
We filter the markets using previous trades by the user so that we don't have to process all Seer markets.
|
@@ -267,7 +279,7 @@ class SeerAgentMarket(AgentMarket):
|
|
267
279
|
traded_tokens = {t.buyToken for t in trades_by_user}.union(
|
268
280
|
[t.sellToken for t in trades_by_user]
|
269
281
|
)
|
270
|
-
filtered_markets = []
|
282
|
+
filtered_markets: list[SeerMarket] = []
|
271
283
|
for market in markets:
|
272
284
|
if any(
|
273
285
|
[
|
@@ -338,9 +350,64 @@ class SeerAgentMarket(AgentMarket):
|
|
338
350
|
return OmenAgentMarket.verify_operational_balance(api_keys=api_keys)
|
339
351
|
|
340
352
|
@staticmethod
|
341
|
-
def
|
353
|
+
def build_resolution(
|
354
|
+
model: SeerMarketWithQuestions,
|
355
|
+
) -> Resolution | None:
|
356
|
+
if model.questions[0].question.finalize_ts == 0:
|
357
|
+
# resolution not yet finalized
|
358
|
+
return None
|
359
|
+
|
360
|
+
if model.template_id != TemplateId.CATEGORICAL:
|
361
|
+
logger.warning("Resolution can only be built for categorical markets.")
|
362
|
+
# Future note - for scalar markets, simply fetch best_answer and convert
|
363
|
+
# from hex into int and divide by 1e18 (because Wei).
|
364
|
+
return None
|
365
|
+
|
366
|
+
if len(model.questions) != 1:
|
367
|
+
raise ValueError("Seer categorical markets must have 1 question.")
|
368
|
+
|
369
|
+
question = model.questions[0]
|
370
|
+
outcome = model.outcomes[int(question.question.best_answer.hex(), 16)]
|
371
|
+
return Resolution(outcome=outcome, invalid=False)
|
372
|
+
|
373
|
+
@staticmethod
|
374
|
+
def convert_seer_market_into_market_with_questions(
|
375
|
+
seer_market: SeerMarket, seer_subgraph: SeerSubgraphHandler
|
376
|
+
) -> "SeerMarketWithQuestions":
|
377
|
+
q = SeerQuestionsCache(seer_subgraph_handler=seer_subgraph)
|
378
|
+
q.fetch_questions([seer_market.id])
|
379
|
+
questions = q.market_id_to_questions[seer_market.id]
|
380
|
+
return SeerMarketWithQuestions(**seer_market.model_dump(), questions=questions)
|
381
|
+
|
382
|
+
@staticmethod
|
383
|
+
def get_parent(
|
342
384
|
model: SeerMarket,
|
343
385
|
seer_subgraph: SeerSubgraphHandler,
|
386
|
+
) -> t.Optional["ParentMarket"]:
|
387
|
+
if not model.parent_market:
|
388
|
+
return None
|
389
|
+
|
390
|
+
# turn into a market with questions
|
391
|
+
parent_market_with_questions = (
|
392
|
+
SeerAgentMarket.convert_seer_market_into_market_with_questions(
|
393
|
+
model.parent_market, seer_subgraph=seer_subgraph
|
394
|
+
)
|
395
|
+
)
|
396
|
+
|
397
|
+
market_with_questions = check_not_none(
|
398
|
+
SeerAgentMarket.from_data_model_with_subgraph(
|
399
|
+
parent_market_with_questions, seer_subgraph, False
|
400
|
+
)
|
401
|
+
)
|
402
|
+
|
403
|
+
return ParentMarket(
|
404
|
+
market=market_with_questions, parent_outcome=model.parent_outcome
|
405
|
+
)
|
406
|
+
|
407
|
+
@staticmethod
|
408
|
+
def from_data_model_with_subgraph(
|
409
|
+
model: SeerMarketWithQuestions,
|
410
|
+
seer_subgraph: SeerSubgraphHandler,
|
344
411
|
must_have_prices: bool,
|
345
412
|
) -> t.Optional["SeerAgentMarket"]:
|
346
413
|
price_manager = PriceManager(seer_market=model, seer_subgraph=seer_subgraph)
|
@@ -356,6 +423,10 @@ class SeerAgentMarket(AgentMarket):
|
|
356
423
|
# Price calculation failed, so don't return the market
|
357
424
|
return None
|
358
425
|
|
426
|
+
resolution = SeerAgentMarket.build_resolution(model=model)
|
427
|
+
|
428
|
+
parent = SeerAgentMarket.get_parent(model=model, seer_subgraph=seer_subgraph)
|
429
|
+
|
359
430
|
market = SeerAgentMarket(
|
360
431
|
id=model.id.hex(),
|
361
432
|
question=model.title,
|
@@ -370,27 +441,12 @@ class SeerAgentMarket(AgentMarket):
|
|
370
441
|
fees=MarketFees.get_zero_fees(),
|
371
442
|
outcome_token_pool=None,
|
372
443
|
outcomes_supply=model.outcomes_supply,
|
373
|
-
resolution=
|
444
|
+
resolution=resolution,
|
374
445
|
volume=None,
|
375
446
|
probabilities=probability_map,
|
376
447
|
upper_bound=model.upper_bound,
|
377
448
|
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
|
-
),
|
449
|
+
parent=parent,
|
394
450
|
)
|
395
451
|
|
396
452
|
return market
|
@@ -403,7 +459,7 @@ class SeerAgentMarket(AgentMarket):
|
|
403
459
|
created_after: t.Optional[DatetimeUTC] = None,
|
404
460
|
excluded_questions: set[str] | None = None,
|
405
461
|
question_type: QuestionType = QuestionType.ALL,
|
406
|
-
|
462
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
407
463
|
) -> t.Sequence["SeerAgentMarket"]:
|
408
464
|
seer_subgraph = SeerSubgraphHandler()
|
409
465
|
|
@@ -412,7 +468,7 @@ class SeerAgentMarket(AgentMarket):
|
|
412
468
|
sort_by=sort_by,
|
413
469
|
filter_by=filter_by,
|
414
470
|
question_type=question_type,
|
415
|
-
|
471
|
+
conditional_filter_type=conditional_filter_type,
|
416
472
|
)
|
417
473
|
|
418
474
|
# We exclude the None values below because `from_data_model_with_subgraph` can return None, which
|
@@ -491,6 +547,7 @@ class SeerAgentMarket(AgentMarket):
|
|
491
547
|
liquidity = self.get_liquidity_for_outcome(outcome)
|
492
548
|
return liquidity > self.minimum_market_liquidity_required
|
493
549
|
|
550
|
+
@cachetools.cached(cache=SHARED_CACHE, key=lambda self: f"has_liquidity_{self.id}")
|
494
551
|
def has_liquidity(self) -> bool:
|
495
552
|
# We define a market as having liquidity if it has liquidity for all outcomes except for the invalid (index -1)
|
496
553
|
return all(
|
@@ -544,7 +601,7 @@ class SeerAgentMarket(AgentMarket):
|
|
544
601
|
f"Expected exactly 1 trade from {order_metadata=}, but got {len(trades)=}."
|
545
602
|
)
|
546
603
|
cow_tx_hash = trades[0].txHash
|
547
|
-
logger.info(f"TxHash for {order_metadata.uid.root=}
|
604
|
+
logger.info(f"TxHash is {cow_tx_hash=} for {order_metadata.uid.root=}.")
|
548
605
|
return cow_tx_hash.hex()
|
549
606
|
|
550
607
|
except (
|
@@ -576,7 +633,9 @@ class SeerAgentMarket(AgentMarket):
|
|
576
633
|
amount_wei=amount_wei,
|
577
634
|
web3=web3,
|
578
635
|
)
|
579
|
-
|
636
|
+
swap_pool_tx_hash = tx_receipt["transactionHash"].hex()
|
637
|
+
logger.info(f"TxHash is {swap_pool_tx_hash=}.")
|
638
|
+
return swap_pool_tx_hash
|
580
639
|
|
581
640
|
def place_bet(
|
582
641
|
self,
|
@@ -9,6 +9,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
9
9
|
ChecksumAddress,
|
10
10
|
OutcomeStr,
|
11
11
|
TxReceipt,
|
12
|
+
Wei,
|
12
13
|
xDai,
|
13
14
|
)
|
14
15
|
from prediction_market_agent_tooling.markets.seer.data_models import (
|
@@ -115,6 +116,23 @@ class GnosisRouter(ContractOnGnosisChain):
|
|
115
116
|
)
|
116
117
|
return receipt_tx
|
117
118
|
|
119
|
+
def split_position(
|
120
|
+
self,
|
121
|
+
api_keys: APIKeys,
|
122
|
+
collateral_token: ChecksumAddress,
|
123
|
+
market_id: ChecksumAddress,
|
124
|
+
amount: Wei,
|
125
|
+
web3: Web3 | None = None,
|
126
|
+
) -> TxReceipt:
|
127
|
+
"""Splits collateral token into full set of outcome tokens."""
|
128
|
+
receipt_tx = self.send(
|
129
|
+
api_keys=api_keys,
|
130
|
+
function_name="splitPosition",
|
131
|
+
function_params=[collateral_token, market_id, amount],
|
132
|
+
web3=web3,
|
133
|
+
)
|
134
|
+
return receipt_tx
|
135
|
+
|
118
136
|
|
119
137
|
class SwaprRouterContract(ContractOnGnosisChain):
|
120
138
|
# File content taken from https://github.com/protofire/omen-exchange/blob/master/app/src/abi/marketMaker.json.
|