prediction-market-agent-tooling 0.48.5__py3-none-any.whl → 0.48.8__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/benchmark/agents.py +0 -2
- prediction_market_agent_tooling/benchmark/utils.py +5 -3
- prediction_market_agent_tooling/deploy/agent.py +35 -27
- prediction_market_agent_tooling/deploy/agent_example.py +5 -6
- prediction_market_agent_tooling/deploy/betting_strategy.py +62 -0
- prediction_market_agent_tooling/markets/data_models.py +44 -3
- prediction_market_agent_tooling/markets/omen/data_models.py +71 -3
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +3 -2
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +22 -44
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +63 -1
- prediction_market_agent_tooling/markets/polymarket/data_models_web.py +41 -38
- prediction_market_agent_tooling/monitor/monitor_app.py +9 -8
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +6 -13
- prediction_market_agent_tooling/tools/is_predictable.py +1 -1
- {prediction_market_agent_tooling-0.48.5.dist-info → prediction_market_agent_tooling-0.48.8.dist-info}/METADATA +1 -1
- {prediction_market_agent_tooling-0.48.5.dist-info → prediction_market_agent_tooling-0.48.8.dist-info}/RECORD +19 -18
- {prediction_market_agent_tooling-0.48.5.dist-info → prediction_market_agent_tooling-0.48.8.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.48.5.dist-info → prediction_market_agent_tooling-0.48.8.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.48.5.dist-info → prediction_market_agent_tooling-0.48.8.dist-info}/entry_points.txt +0 -0
@@ -87,7 +87,6 @@ class RandomAgent(AbstractBenchmarkedAgent):
|
|
87
87
|
p_yes, confidence = random.random(), random.random()
|
88
88
|
return Prediction(
|
89
89
|
outcome_prediction=OutcomePrediction(
|
90
|
-
decision=p_yes > 0.5,
|
91
90
|
p_yes=Probability(p_yes),
|
92
91
|
confidence=confidence,
|
93
92
|
info_utility=None,
|
@@ -111,7 +110,6 @@ class FixedAgent(AbstractBenchmarkedAgent):
|
|
111
110
|
p_yes, confidence = 1.0 if self.fixed_answer else 0.0, 1.0
|
112
111
|
return Prediction(
|
113
112
|
outcome_prediction=OutcomePrediction(
|
114
|
-
decision=self.fixed_answer,
|
115
113
|
p_yes=Probability(p_yes),
|
116
114
|
confidence=confidence,
|
117
115
|
info_utility=None,
|
@@ -3,11 +3,13 @@ import typing as t
|
|
3
3
|
|
4
4
|
from pydantic import BaseModel
|
5
5
|
|
6
|
-
from prediction_market_agent_tooling.
|
7
|
-
|
6
|
+
from prediction_market_agent_tooling.markets.data_models import (
|
7
|
+
ProbabilisticAnswer,
|
8
|
+
Resolution,
|
9
|
+
)
|
8
10
|
|
9
11
|
|
10
|
-
class OutcomePrediction(
|
12
|
+
class OutcomePrediction(ProbabilisticAnswer):
|
11
13
|
info_utility: t.Optional[float]
|
12
14
|
|
13
15
|
@property
|
@@ -12,6 +12,10 @@ from pydantic import BaseModel, BeforeValidator, computed_field
|
|
12
12
|
from typing_extensions import Annotated
|
13
13
|
|
14
14
|
from prediction_market_agent_tooling.config import APIKeys
|
15
|
+
from prediction_market_agent_tooling.deploy.betting_strategy import (
|
16
|
+
BettingStrategy,
|
17
|
+
MaxAccuracyBettingStrategy,
|
18
|
+
)
|
15
19
|
from prediction_market_agent_tooling.deploy.constants import (
|
16
20
|
MARKET_TYPE_KEY,
|
17
21
|
REPOSITORY_KEY,
|
@@ -25,14 +29,19 @@ from prediction_market_agent_tooling.deploy.gcp.utils import (
|
|
25
29
|
gcp_function_is_active,
|
26
30
|
gcp_resolve_api_keys_secrets,
|
27
31
|
)
|
28
|
-
from prediction_market_agent_tooling.gtypes import
|
32
|
+
from prediction_market_agent_tooling.gtypes import xDai, xdai_type
|
29
33
|
from prediction_market_agent_tooling.loggers import logger
|
30
34
|
from prediction_market_agent_tooling.markets.agent_market import (
|
31
35
|
AgentMarket,
|
32
36
|
FilterBy,
|
33
37
|
SortBy,
|
34
38
|
)
|
35
|
-
from prediction_market_agent_tooling.markets.data_models import
|
39
|
+
from prediction_market_agent_tooling.markets.data_models import (
|
40
|
+
BetAmount,
|
41
|
+
ProbabilisticAnswer,
|
42
|
+
TokenAmount,
|
43
|
+
TokenAmountAndDirection,
|
44
|
+
)
|
36
45
|
from prediction_market_agent_tooling.markets.markets import (
|
37
46
|
MarketType,
|
38
47
|
have_bet_on_market_since,
|
@@ -95,19 +104,8 @@ class OutOfFundsError(ValueError):
|
|
95
104
|
pass
|
96
105
|
|
97
106
|
|
98
|
-
class Answer(BaseModel):
|
99
|
-
decision: Decision # Warning: p_yes > 0.5 doesn't necessarily mean decision is True! For example, if our p_yes is 55%, but market's p_yes is 80%, then it might be profitable to bet on False.
|
100
|
-
p_yes: Probability
|
101
|
-
confidence: float
|
102
|
-
reasoning: str | None = None
|
103
|
-
|
104
|
-
@property
|
105
|
-
def p_no(self) -> Probability:
|
106
|
-
return Probability(1 - self.p_yes)
|
107
|
-
|
108
|
-
|
109
107
|
class ProcessedMarket(BaseModel):
|
110
|
-
answer:
|
108
|
+
answer: ProbabilisticAnswer
|
111
109
|
amount: BetAmount
|
112
110
|
|
113
111
|
|
@@ -280,6 +278,7 @@ class DeployableTraderAgent(DeployableAgent):
|
|
280
278
|
bet_on_n_markets_per_run: int = 1
|
281
279
|
min_required_balance_to_operate: xDai | None = xdai_type(1)
|
282
280
|
min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
|
281
|
+
strategy: BettingStrategy = MaxAccuracyBettingStrategy()
|
283
282
|
|
284
283
|
def __init__(
|
285
284
|
self,
|
@@ -295,7 +294,7 @@ class DeployableTraderAgent(DeployableAgent):
|
|
295
294
|
self.have_bet_on_market_since = observe()(self.have_bet_on_market_since) # type: ignore[method-assign]
|
296
295
|
self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
|
297
296
|
self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
|
298
|
-
self.
|
297
|
+
self.calculate_bet_amount_and_direction = observe()(self.calculate_bet_amount_and_direction) # type: ignore[method-assign]
|
299
298
|
self.process_market = observe()(self.process_market) # type: ignore[method-assign]
|
300
299
|
|
301
300
|
def update_langfuse_trace_by_market(
|
@@ -311,6 +310,18 @@ class DeployableTraderAgent(DeployableAgent):
|
|
311
310
|
},
|
312
311
|
)
|
313
312
|
|
313
|
+
def calculate_bet_amount_and_direction(
|
314
|
+
self, answer: ProbabilisticAnswer, market: AgentMarket
|
315
|
+
) -> TokenAmountAndDirection:
|
316
|
+
amount_and_direction = self.strategy.calculate_bet_amount_and_direction(
|
317
|
+
answer, market
|
318
|
+
)
|
319
|
+
if amount_and_direction.currency != market.currency:
|
320
|
+
raise ValueError(
|
321
|
+
f"Currency mismatch. Strategy yields {amount_and_direction.currency}, market has currency {market.currency}"
|
322
|
+
)
|
323
|
+
return amount_and_direction
|
324
|
+
|
314
325
|
def update_langfuse_trace_by_processed_market(
|
315
326
|
self, market_type: MarketType, processed_market: ProcessedMarket | None
|
316
327
|
) -> None:
|
@@ -360,18 +371,12 @@ class DeployableTraderAgent(DeployableAgent):
|
|
360
371
|
|
361
372
|
return True
|
362
373
|
|
363
|
-
def answer_binary_market(self, market: AgentMarket) ->
|
374
|
+
def answer_binary_market(self, market: AgentMarket) -> ProbabilisticAnswer | None:
|
364
375
|
"""
|
365
376
|
Answer the binary market. This method must be implemented by the subclass.
|
366
377
|
"""
|
367
378
|
raise NotImplementedError("This method must be implemented by the subclass")
|
368
379
|
|
369
|
-
def calculate_bet_amount(self, answer: Answer, market: AgentMarket) -> BetAmount:
|
370
|
-
"""
|
371
|
-
Calculate the bet amount. By default, it returns the minimum bet amount.
|
372
|
-
"""
|
373
|
-
return market.get_tiny_bet_amount()
|
374
|
-
|
375
380
|
def get_markets(
|
376
381
|
self,
|
377
382
|
market_type: MarketType,
|
@@ -408,22 +413,25 @@ class DeployableTraderAgent(DeployableAgent):
|
|
408
413
|
self.update_langfuse_trace_by_processed_market(market_type, None)
|
409
414
|
return None
|
410
415
|
|
411
|
-
|
416
|
+
amount_and_direction = self.calculate_bet_amount_and_direction(answer, market)
|
412
417
|
|
413
418
|
if self.place_bet:
|
414
419
|
logger.info(
|
415
|
-
f"Placing bet on {market} with
|
420
|
+
f"Placing bet on {market} with direction {amount_and_direction.direction} and amount {amount_and_direction.amount}"
|
416
421
|
)
|
417
422
|
market.place_bet(
|
418
|
-
amount=
|
419
|
-
|
423
|
+
amount=TokenAmount(
|
424
|
+
amount=amount_and_direction.amount,
|
425
|
+
currency=amount_and_direction.currency,
|
426
|
+
),
|
427
|
+
outcome=amount_and_direction.direction,
|
420
428
|
)
|
421
429
|
|
422
430
|
self.after_process_market(market_type, market)
|
423
431
|
|
424
432
|
processed_market = ProcessedMarket(
|
425
433
|
answer=answer,
|
426
|
-
amount=
|
434
|
+
amount=amount_and_direction,
|
427
435
|
)
|
428
436
|
self.update_langfuse_trace_by_processed_market(market_type, processed_market)
|
429
437
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import random
|
2
2
|
|
3
3
|
from prediction_market_agent_tooling.deploy.agent import (
|
4
|
-
Answer,
|
5
4
|
DeployableTraderAgent,
|
6
|
-
|
5
|
+
ProbabilisticAnswer,
|
7
6
|
)
|
7
|
+
from prediction_market_agent_tooling.gtypes import Probability
|
8
8
|
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
|
9
9
|
from prediction_market_agent_tooling.markets.markets import MarketType
|
10
10
|
|
@@ -13,10 +13,9 @@ class DeployableCoinFlipAgent(DeployableTraderAgent):
|
|
13
13
|
def verify_market(self, market_type: MarketType, market: AgentMarket) -> bool:
|
14
14
|
return True
|
15
15
|
|
16
|
-
def answer_binary_market(self, market: AgentMarket) ->
|
16
|
+
def answer_binary_market(self, market: AgentMarket) -> ProbabilisticAnswer | None:
|
17
17
|
decision = random.choice([True, False])
|
18
|
-
return
|
19
|
-
decision=decision,
|
18
|
+
return ProbabilisticAnswer(
|
20
19
|
p_yes=Probability(float(decision)),
|
21
20
|
confidence=0.5,
|
22
21
|
reasoning="I flipped a coin to decide.",
|
@@ -24,5 +23,5 @@ class DeployableCoinFlipAgent(DeployableTraderAgent):
|
|
24
23
|
|
25
24
|
|
26
25
|
class DeployableAlwaysRaiseAgent(DeployableTraderAgent):
|
27
|
-
def answer_binary_market(self, market: AgentMarket) ->
|
26
|
+
def answer_binary_market(self, market: AgentMarket) -> ProbabilisticAnswer | None:
|
28
27
|
raise RuntimeError("I always raise!")
|
@@ -0,0 +1,62 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
|
4
|
+
from prediction_market_agent_tooling.markets.data_models import (
|
5
|
+
ProbabilisticAnswer,
|
6
|
+
TokenAmountAndDirection,
|
7
|
+
)
|
8
|
+
from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
|
9
|
+
get_kelly_bet,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
class BettingStrategy(ABC):
|
14
|
+
@abstractmethod
|
15
|
+
def calculate_bet_amount_and_direction(
|
16
|
+
self, answer: ProbabilisticAnswer, market: AgentMarket
|
17
|
+
) -> TokenAmountAndDirection:
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class MaxAccuracyBettingStrategy(BettingStrategy):
|
22
|
+
def __init__(self, bet_amount: float | None = None):
|
23
|
+
self.bet_amount = bet_amount
|
24
|
+
|
25
|
+
@staticmethod
|
26
|
+
def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:
|
27
|
+
# If estimate_p_yes >= market.current_p_yes, then bet TRUE, else bet FALSE.
|
28
|
+
# This is equivalent to saying EXPECTED_VALUE = (estimate_p_yes * num_tokens_obtained_by_betting_yes) -
|
29
|
+
# ((1 - estimate_p_yes) * num_tokens_obtained_by_betting_no) >= 0
|
30
|
+
return estimate_p_yes >= market_p_yes
|
31
|
+
|
32
|
+
def calculate_bet_amount_and_direction(
|
33
|
+
self, answer: ProbabilisticAnswer, market: AgentMarket
|
34
|
+
) -> TokenAmountAndDirection:
|
35
|
+
bet_amount = (
|
36
|
+
market.get_tiny_bet_amount().amount
|
37
|
+
if self.bet_amount is None
|
38
|
+
else self.bet_amount
|
39
|
+
)
|
40
|
+
direction = self.calculate_direction(market.current_p_yes, answer.p_yes)
|
41
|
+
return TokenAmountAndDirection(
|
42
|
+
amount=bet_amount,
|
43
|
+
currency=market.currency,
|
44
|
+
direction=direction,
|
45
|
+
)
|
46
|
+
|
47
|
+
|
48
|
+
class KellyBettingStrategy(BettingStrategy):
|
49
|
+
def __init__(self, max_bet_amount: float = 10):
|
50
|
+
self.max_bet_amount = max_bet_amount
|
51
|
+
|
52
|
+
def calculate_bet_amount_and_direction(
|
53
|
+
self, answer: ProbabilisticAnswer, market: AgentMarket
|
54
|
+
) -> TokenAmountAndDirection:
|
55
|
+
kelly_bet = get_kelly_bet(
|
56
|
+
self.max_bet_amount, market.current_p_yes, answer.p_yes, answer.confidence
|
57
|
+
)
|
58
|
+
return TokenAmountAndDirection(
|
59
|
+
amount=kelly_bet.size,
|
60
|
+
currency=market.currency,
|
61
|
+
direction=kelly_bet.direction,
|
62
|
+
)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from enum import Enum
|
3
|
-
from typing import TypeAlias
|
3
|
+
from typing import Annotated, TypeAlias
|
4
4
|
|
5
|
-
from pydantic import BaseModel, computed_field
|
5
|
+
from pydantic import BaseModel, BeforeValidator, computed_field
|
6
6
|
|
7
|
-
from prediction_market_agent_tooling.gtypes import OutcomeStr
|
7
|
+
from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
|
8
8
|
|
9
9
|
|
10
10
|
class Currency(str, Enum):
|
@@ -19,6 +19,10 @@ class Resolution(str, Enum):
|
|
19
19
|
CANCEL = "CANCEL"
|
20
20
|
MKT = "MKT"
|
21
21
|
|
22
|
+
@staticmethod
|
23
|
+
def from_bool(value: bool) -> "Resolution":
|
24
|
+
return Resolution.YES if value else Resolution.NO
|
25
|
+
|
22
26
|
|
23
27
|
class TokenAmount(BaseModel):
|
24
28
|
amount: float
|
@@ -57,6 +61,43 @@ class ResolvedBet(Bet):
|
|
57
61
|
return f"Resolved bet for market {self.market_id} for question {self.market_question} created at {self.created_time}: {self.amount} on {self.outcome}. Bet was resolved at {self.resolved_time} and was {'correct' if self.is_correct else 'incorrect'}. Profit was {self.profit}"
|
58
62
|
|
59
63
|
|
64
|
+
class TokenAmountAndDirection(TokenAmount):
|
65
|
+
direction: bool
|
66
|
+
|
67
|
+
|
68
|
+
def to_boolean_outcome(value: str | bool) -> bool:
|
69
|
+
if isinstance(value, bool):
|
70
|
+
return value
|
71
|
+
|
72
|
+
elif isinstance(value, str):
|
73
|
+
value = value.lower().strip()
|
74
|
+
|
75
|
+
if value in {"true", "yes", "y", "1"}:
|
76
|
+
return True
|
77
|
+
|
78
|
+
elif value in {"false", "no", "n", "0"}:
|
79
|
+
return False
|
80
|
+
|
81
|
+
else:
|
82
|
+
raise ValueError(f"Expected a boolean string, but got {value}")
|
83
|
+
|
84
|
+
else:
|
85
|
+
raise ValueError(f"Expected a boolean or a string, but got {value}")
|
86
|
+
|
87
|
+
|
88
|
+
Decision = Annotated[bool, BeforeValidator(to_boolean_outcome)]
|
89
|
+
|
90
|
+
|
91
|
+
class ProbabilisticAnswer(BaseModel):
|
92
|
+
p_yes: Probability
|
93
|
+
confidence: float
|
94
|
+
reasoning: str | None = None
|
95
|
+
|
96
|
+
@property
|
97
|
+
def p_no(self) -> Probability:
|
98
|
+
return Probability(1 - self.p_yes)
|
99
|
+
|
100
|
+
|
60
101
|
class Position(BaseModel):
|
61
102
|
market_id: str
|
62
103
|
amounts: dict[OutcomeStr, TokenAmount]
|
@@ -77,6 +77,10 @@ class Question(BaseModel):
|
|
77
77
|
def opening_datetime(self) -> datetime:
|
78
78
|
return datetime.fromtimestamp(self.openingTimestamp)
|
79
79
|
|
80
|
+
@property
|
81
|
+
def has_answer(self) -> bool:
|
82
|
+
return self.currentAnswer is not None
|
83
|
+
|
80
84
|
@property
|
81
85
|
def outcome_index(self) -> int | None:
|
82
86
|
return (
|
@@ -88,6 +92,34 @@ class Question(BaseModel):
|
|
88
92
|
else None
|
89
93
|
)
|
90
94
|
|
95
|
+
@property
|
96
|
+
def is_binary(self) -> bool:
|
97
|
+
return len(self.outcomes) == 2
|
98
|
+
|
99
|
+
@property
|
100
|
+
def has_valid_answer(self) -> bool:
|
101
|
+
return self.outcome_index is not None and self.outcome_index != INVALID_ANSWER
|
102
|
+
|
103
|
+
@property
|
104
|
+
def boolean_outcome(self) -> bool:
|
105
|
+
if not self.is_binary:
|
106
|
+
raise ValueError(
|
107
|
+
f"Question with title {self.title} is not binary, it has {len(self.outcomes)} outcomes."
|
108
|
+
)
|
109
|
+
|
110
|
+
if not self.has_answer:
|
111
|
+
raise ValueError(f"Question with title {self.title} is not answered.")
|
112
|
+
|
113
|
+
outcome_index = check_not_none(self.outcome_index)
|
114
|
+
|
115
|
+
if not self.has_valid_answer:
|
116
|
+
raise ValueError(
|
117
|
+
f"Question with title {self.title} has invalid answer {outcome_index}."
|
118
|
+
)
|
119
|
+
|
120
|
+
outcome: str = self.outcomes[outcome_index]
|
121
|
+
return get_boolean_outcome(outcome)
|
122
|
+
|
91
123
|
|
92
124
|
class OmenPosition(BaseModel):
|
93
125
|
id: HexBytes
|
@@ -132,7 +164,15 @@ class OmenUserPosition(BaseModel):
|
|
132
164
|
|
133
165
|
class OmenMarket(BaseModel):
|
134
166
|
"""
|
135
|
-
https://
|
167
|
+
https://presagio.pages.dev
|
168
|
+
|
169
|
+
An Omen market goes through the following stages:
|
170
|
+
|
171
|
+
1. creation - can add liquidty immediately, and trade immediately if there is liquidity
|
172
|
+
2. closing - market is closed, and a question is simultaneously opened for answers on Reality
|
173
|
+
3. finalizing - the question is finalized on reality (including any disputes)
|
174
|
+
4. resolving - a manual step required by calling the Omen oracle contract
|
175
|
+
5. redeeming - a user withdraws collateral tokens from the market
|
136
176
|
"""
|
137
177
|
|
138
178
|
BET_AMOUNT_CURRENCY: t.ClassVar[Currency] = Currency.xDai
|
@@ -347,7 +387,7 @@ class OmenBet(BaseModel):
|
|
347
387
|
collateralAmountUSD: USD
|
348
388
|
feeAmount: Wei
|
349
389
|
outcomeIndex: int
|
350
|
-
outcomeTokensTraded:
|
390
|
+
outcomeTokensTraded: Wei
|
351
391
|
transactionHash: HexAddress
|
352
392
|
fpmm: OmenMarket
|
353
393
|
|
@@ -372,7 +412,7 @@ class OmenBet(BaseModel):
|
|
372
412
|
def get_profit(self) -> ProfitAmount:
|
373
413
|
bet_amount_xdai = wei_to_xdai(self.collateralAmount)
|
374
414
|
profit = (
|
375
|
-
wei_to_xdai(
|
415
|
+
wei_to_xdai(self.outcomeTokensTraded) - bet_amount_xdai
|
376
416
|
if self.boolean_outcome == self.fpmm.boolean_outcome
|
377
417
|
else -bet_amount_xdai
|
378
418
|
)
|
@@ -430,6 +470,8 @@ class RealityQuestion(BaseModel):
|
|
430
470
|
updatedTimestamp: datetime
|
431
471
|
contentHash: HexBytes
|
432
472
|
questionId: HexBytes
|
473
|
+
answerFinalizedTimestamp: datetime
|
474
|
+
currentScheduledFinalizationTimestamp: datetime
|
433
475
|
|
434
476
|
@property
|
435
477
|
def url(self) -> str:
|
@@ -446,6 +488,32 @@ class RealityAnswer(BaseModel):
|
|
446
488
|
createdBlock: int
|
447
489
|
|
448
490
|
|
491
|
+
class RealityResponse(BaseModel):
|
492
|
+
"""
|
493
|
+
This is similar to `RealityAnswer`, but contains additional fields, most importantly `historyHash`.
|
494
|
+
"""
|
495
|
+
|
496
|
+
id: str
|
497
|
+
timestamp: datetime
|
498
|
+
answer: HexBytes
|
499
|
+
isUnrevealed: bool
|
500
|
+
isCommitment: bool
|
501
|
+
bond: Wei
|
502
|
+
user: HexAddress
|
503
|
+
historyHash: HexBytes
|
504
|
+
question: RealityQuestion
|
505
|
+
createdBlock: int
|
506
|
+
revealedBlock: int | None
|
507
|
+
|
508
|
+
@property
|
509
|
+
def bond_xdai(self) -> xDai:
|
510
|
+
return wei_to_xdai(self.bond)
|
511
|
+
|
512
|
+
@property
|
513
|
+
def user_checksummed(self) -> ChecksumAddress:
|
514
|
+
return Web3.to_checksum_address(self.user)
|
515
|
+
|
516
|
+
|
449
517
|
class RealityAnswers(BaseModel):
|
450
518
|
answers: list[RealityAnswer]
|
451
519
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
import random
|
3
3
|
import typing as t
|
4
|
-
from datetime import datetime
|
4
|
+
from datetime import datetime, timedelta
|
5
5
|
from enum import Enum
|
6
6
|
|
7
7
|
from web3 import Web3
|
@@ -521,6 +521,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
521
521
|
language: str,
|
522
522
|
arbitrator: Arbitrator,
|
523
523
|
opening: datetime,
|
524
|
+
timeout: timedelta = timedelta(days=1),
|
524
525
|
nonce: int | None = None,
|
525
526
|
tx_params: t.Optional[TxParams] = None,
|
526
527
|
web3: Web3 | None = None,
|
@@ -547,7 +548,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
547
548
|
template_id=template_id,
|
548
549
|
question=realitio_question,
|
549
550
|
arbitrator=arbitrator_contract_address,
|
550
|
-
timeout=
|
551
|
+
timeout=int(timeout.total_seconds()),
|
551
552
|
opening_ts=int(opening.timestamp()),
|
552
553
|
nonce=(
|
553
554
|
nonce if nonce is not None else random.randint(0, 1000000)
|
@@ -33,7 +33,6 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
|
33
33
|
from prediction_market_agent_tooling.markets.polymarket.utils import (
|
34
34
|
find_resolution_on_polymarket,
|
35
35
|
)
|
36
|
-
from prediction_market_agent_tooling.tools.utils import check_not_none
|
37
36
|
from prediction_market_agent_tooling.tools.web3_utils import ZERO_BYTES, xdai_to_wei
|
38
37
|
|
39
38
|
|
@@ -42,7 +41,6 @@ def claim_bonds_on_realitio_questions(
|
|
42
41
|
questions: list[RealityQuestion],
|
43
42
|
auto_withdraw: bool,
|
44
43
|
web3: Web3 | None = None,
|
45
|
-
silent_errors: bool = False,
|
46
44
|
) -> list[HexBytes]:
|
47
45
|
claimed_questions: list[HexBytes] = []
|
48
46
|
|
@@ -50,19 +48,10 @@ def claim_bonds_on_realitio_questions(
|
|
50
48
|
logger.info(
|
51
49
|
f"[{idx+1} / {len(questions)}] Claiming bond for {question.questionId=} {question.url=}"
|
52
50
|
)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
claimed_questions.append(question.questionId)
|
58
|
-
except Exception as e:
|
59
|
-
# TODO: This shouldn't be required once `claim_bonds_on_realitio_question` below is fixed.
|
60
|
-
if silent_errors:
|
61
|
-
logger.warning(
|
62
|
-
f"Error while claiming bond for {question.questionId=} {question.url=}: {e}"
|
63
|
-
)
|
64
|
-
else:
|
65
|
-
raise
|
51
|
+
claim_bonds_on_realitio_question(
|
52
|
+
api_keys, question, auto_withdraw=auto_withdraw, web3=web3
|
53
|
+
)
|
54
|
+
claimed_questions.append(question.questionId)
|
66
55
|
|
67
56
|
return claimed_questions
|
68
57
|
|
@@ -77,50 +66,39 @@ def claim_bonds_on_realitio_question(
|
|
77
66
|
realitio_contract = OmenRealitioContract()
|
78
67
|
|
79
68
|
# Get all answers for the question.
|
80
|
-
|
69
|
+
responses = OmenSubgraphHandler().get_responses(question_id=question.questionId)
|
81
70
|
|
82
|
-
|
83
|
-
|
71
|
+
# They need to be processed in order.
|
72
|
+
responses = sorted(responses, key=lambda x: x.timestamp)
|
84
73
|
|
85
|
-
if
|
86
|
-
raise ValueError(f"
|
74
|
+
if not responses:
|
75
|
+
raise ValueError(f"No answers found for {question.questionId.hex()=}")
|
87
76
|
|
88
|
-
if
|
89
|
-
|
90
|
-
# The trouble is, that historyHash is updated after each new answer and the contract holds only the latest one.
|
91
|
-
# So if we have more than 1 answer, we missing the historyHash n-1 of them and this would fail.
|
92
|
-
# You can find how to calculate history hash at https://realitio.github.io/docs/html/contract_explanation.html#answer-history-entries.
|
93
|
-
# At the moment, we support only 1 answer, as for that one answer we will have the hash.
|
94
|
-
raise NotImplementedError()
|
77
|
+
if responses[-1].question.historyHash == ZERO_BYTES:
|
78
|
+
raise ValueError(f"Already claimed {question.questionId.hex()=}.")
|
95
79
|
|
96
|
-
# Logic taken from packages/valory/skills/decision_maker_abci/models.py in `def claim_params`.
|
97
80
|
history_hashes: list[HexBytes] = []
|
98
81
|
addresses: list[ChecksumAddress] = []
|
99
82
|
bonds: list[Wei] = []
|
100
83
|
answers: list[HexBytes] = []
|
101
84
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
85
|
+
# Caller must provide the answer history, in reverse order.
|
86
|
+
# See https://gnosisscan.io/address/0x79e32aE03fb27B07C89c0c568F80287C01ca2E57#code#L625 for the `claimWinnings` logic.
|
87
|
+
reversed_responses = list(reversed(responses))
|
88
|
+
|
89
|
+
for i, response in enumerate(reversed_responses):
|
90
|
+
# second-last-to-first, the hash of each history entry. (Final one should be empty).
|
91
|
+
if i == len(reversed_responses) - 1:
|
106
92
|
history_hashes.append(ZERO_BYTES)
|
107
93
|
else:
|
108
|
-
|
109
|
-
# This is from the original Olas implementation (https://github.com/kongzii/trader/blob/700af475a4538cc3d5d22caf9dec9e9d22d72af1/packages/valory/skills/market_manager_abci/graph_tooling/requests.py#L297),
|
110
|
-
# but it's most probably wrong (see comment above).
|
111
|
-
history_hashes.append(
|
112
|
-
check_not_none(
|
113
|
-
answers_objects[i + 1].question.historyHash,
|
114
|
-
"Shouldn't be None here.",
|
115
|
-
)
|
116
|
-
)
|
94
|
+
history_hashes.append(reversed_responses[i + 1].historyHash)
|
117
95
|
|
118
96
|
# last-to-first, the address of each answerer or commitment sender
|
119
|
-
addresses.append(Web3.to_checksum_address(
|
97
|
+
addresses.append(Web3.to_checksum_address(response.user))
|
120
98
|
# last-to-first, the bond supplied with each answer or commitment
|
121
|
-
bonds.append(
|
99
|
+
bonds.append(response.bond)
|
122
100
|
# last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal
|
123
|
-
answers.append(
|
101
|
+
answers.append(response.answer)
|
124
102
|
|
125
103
|
realitio_contract.claimWinnings(
|
126
104
|
api_keys=api_keys,
|
@@ -22,6 +22,7 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
|
|
22
22
|
OmenUserPosition,
|
23
23
|
RealityAnswer,
|
24
24
|
RealityQuestion,
|
25
|
+
RealityResponse,
|
25
26
|
)
|
26
27
|
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
27
28
|
OmenThumbnailMapping,
|
@@ -118,6 +119,8 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
118
119
|
questions_field.questionId,
|
119
120
|
questions_field.contentHash,
|
120
121
|
questions_field.historyHash,
|
122
|
+
questions_field.answerFinalizedTimestamp,
|
123
|
+
questions_field.currentScheduledFinalizationTimestamp,
|
121
124
|
]
|
122
125
|
|
123
126
|
def _get_fields_for_answers(self, answers_field: FieldPath) -> list[FieldPath]:
|
@@ -130,6 +133,20 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
130
133
|
answers_field.createdBlock,
|
131
134
|
] + self._get_fields_for_reality_questions(answers_field.question)
|
132
135
|
|
136
|
+
def _get_fields_for_responses(self, responses_field: FieldPath) -> list[FieldPath]:
|
137
|
+
return [
|
138
|
+
responses_field.id,
|
139
|
+
responses_field.timestamp,
|
140
|
+
responses_field.answer,
|
141
|
+
responses_field.isUnrevealed,
|
142
|
+
responses_field.isCommitment,
|
143
|
+
responses_field.bond,
|
144
|
+
responses_field.user,
|
145
|
+
responses_field.historyHash,
|
146
|
+
responses_field.createdBlock,
|
147
|
+
responses_field.revealedBlock,
|
148
|
+
] + self._get_fields_for_reality_questions(responses_field.question)
|
149
|
+
|
133
150
|
def _get_fields_for_market_questions(
|
134
151
|
self, questions_field: FieldPath
|
135
152
|
) -> list[FieldPath]:
|
@@ -175,11 +192,13 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
175
192
|
def _build_where_statements(
|
176
193
|
self,
|
177
194
|
creator: t.Optional[HexAddress] = None,
|
195
|
+
creator_in: t.Optional[t.Sequence[HexAddress]] = None,
|
178
196
|
outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
|
179
197
|
created_after: t.Optional[datetime] = None,
|
180
198
|
opened_before: t.Optional[datetime] = None,
|
181
199
|
opened_after: t.Optional[datetime] = None,
|
182
200
|
finalized_before: t.Optional[datetime] = None,
|
201
|
+
finalized_after: t.Optional[datetime] = None,
|
183
202
|
finalized: bool | None = None,
|
184
203
|
resolved: bool | None = None,
|
185
204
|
liquidity_bigger_than: Wei | None = None,
|
@@ -202,7 +221,10 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
202
221
|
]
|
203
222
|
|
204
223
|
if creator:
|
205
|
-
where_stms["creator"] = creator
|
224
|
+
where_stms["creator"] = creator.lower()
|
225
|
+
|
226
|
+
if creator_in:
|
227
|
+
where_stms["creator_in"] = [x.lower() for x in creator_in]
|
206
228
|
|
207
229
|
if created_after:
|
208
230
|
where_stms["creationTimestamp_gt"] = to_int_timestamp(created_after)
|
@@ -244,6 +266,11 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
244
266
|
finalized_before
|
245
267
|
)
|
246
268
|
|
269
|
+
if finalized_after:
|
270
|
+
where_stms["answerFinalizedTimestamp_gt"] = to_int_timestamp(
|
271
|
+
finalized_after
|
272
|
+
)
|
273
|
+
|
247
274
|
# `excluded_question_titles` can not be an empty list, otherwise the API bugs out and returns nothing.
|
248
275
|
excluded_question_titles = [""]
|
249
276
|
if excluded_questions:
|
@@ -331,9 +358,11 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
331
358
|
opened_before: t.Optional[datetime] = None,
|
332
359
|
opened_after: t.Optional[datetime] = None,
|
333
360
|
finalized_before: t.Optional[datetime] = None,
|
361
|
+
finalized_after: t.Optional[datetime] = None,
|
334
362
|
finalized: bool | None = None,
|
335
363
|
resolved: bool | None = None,
|
336
364
|
creator: t.Optional[HexAddress] = None,
|
365
|
+
creator_in: t.Optional[t.Sequence[HexAddress]] = None,
|
337
366
|
liquidity_bigger_than: Wei | None = None,
|
338
367
|
condition_id_in: list[HexBytes] | None = None,
|
339
368
|
id_in: list[str] | None = None,
|
@@ -353,11 +382,13 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
353
382
|
"""
|
354
383
|
where_stms = self._build_where_statements(
|
355
384
|
creator=creator,
|
385
|
+
creator_in=creator_in,
|
356
386
|
outcomes=outcomes,
|
357
387
|
created_after=created_after,
|
358
388
|
opened_before=opened_before,
|
359
389
|
opened_after=opened_after,
|
360
390
|
finalized_before=finalized_before,
|
391
|
+
finalized_after=finalized_after,
|
361
392
|
finalized=finalized,
|
362
393
|
resolved=resolved,
|
363
394
|
condition_id_in=condition_id_in,
|
@@ -582,6 +613,10 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
582
613
|
user: HexAddress | None = None,
|
583
614
|
claimed: bool | None = None,
|
584
615
|
current_answer_before: datetime | None = None,
|
616
|
+
finalized_before: datetime | None = None,
|
617
|
+
finalized_after: datetime | None = None,
|
618
|
+
id_in: list[str] | None = None,
|
619
|
+
question_id_in: list[HexBytes] | None = None,
|
585
620
|
) -> list[RealityQuestion]:
|
586
621
|
where_stms: dict[str, t.Any] = {}
|
587
622
|
|
@@ -599,6 +634,22 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
599
634
|
current_answer_before
|
600
635
|
)
|
601
636
|
|
637
|
+
if finalized_before is not None:
|
638
|
+
where_stms["answerFinalizedTimestamp_lt"] = to_int_timestamp(
|
639
|
+
finalized_before
|
640
|
+
)
|
641
|
+
|
642
|
+
if finalized_after is not None:
|
643
|
+
where_stms["answerFinalizedTimestamp_gt"] = to_int_timestamp(
|
644
|
+
finalized_after
|
645
|
+
)
|
646
|
+
|
647
|
+
if id_in is not None:
|
648
|
+
where_stms["id_in"] = id_in
|
649
|
+
|
650
|
+
if question_id_in is not None:
|
651
|
+
where_stms["questionId_in"] = [x.hex() for x in question_id_in]
|
652
|
+
|
602
653
|
questions = self.realityeth_subgraph.Query.questions(where=where_stms)
|
603
654
|
fields = self._get_fields_for_reality_questions(questions)
|
604
655
|
result = self.sg.query_json(fields)
|
@@ -618,6 +669,17 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
|
|
618
669
|
items = self._parse_items_from_json(result)
|
619
670
|
return [RealityAnswer.model_validate(i) for i in items]
|
620
671
|
|
672
|
+
def get_responses(self, question_id: HexBytes) -> list[RealityResponse]:
|
673
|
+
response = self.realityeth_subgraph.Response
|
674
|
+
where_stms = [
|
675
|
+
response.question.questionId == question_id.hex(),
|
676
|
+
]
|
677
|
+
responses = self.realityeth_subgraph.Query.responses(where=where_stms)
|
678
|
+
fields = self._get_fields_for_responses(responses)
|
679
|
+
result = self.sg.query_json(fields)
|
680
|
+
items = self._parse_items_from_json(result)
|
681
|
+
return [RealityResponse.model_validate(i) for i in items]
|
682
|
+
|
621
683
|
def get_markets_from_all_user_positions(
|
622
684
|
self, user_positions: list[OmenUserPosition]
|
623
685
|
) -> list[OmenMarket]:
|
@@ -38,7 +38,7 @@ class Event(BaseModel):
|
|
38
38
|
|
39
39
|
|
40
40
|
class Event1(BaseModel):
|
41
|
-
startDate: datetime
|
41
|
+
startDate: datetime | None = None
|
42
42
|
slug: str
|
43
43
|
|
44
44
|
|
@@ -78,13 +78,13 @@ class Market(BaseModel):
|
|
78
78
|
conditionId: str
|
79
79
|
slug: str
|
80
80
|
twitterCardImage: t.Any | None = None
|
81
|
-
resolutionSource: str
|
81
|
+
resolutionSource: str | None = None
|
82
82
|
endDate: datetime
|
83
83
|
category: t.Any | None = None
|
84
84
|
ammType: t.Any | None = None
|
85
85
|
description: str
|
86
86
|
liquidity: str | None = None
|
87
|
-
startDate: datetime
|
87
|
+
startDate: datetime | None = None
|
88
88
|
createdAt: datetime
|
89
89
|
xAxisValue: t.Any | None = None
|
90
90
|
yAxisValue: t.Any | None = None
|
@@ -93,8 +93,8 @@ class Market(BaseModel):
|
|
93
93
|
lowerBound: t.Any | None = None
|
94
94
|
upperBound: t.Any | None = None
|
95
95
|
outcomes: list[str]
|
96
|
-
image: str
|
97
|
-
icon: str
|
96
|
+
image: str | None = None
|
97
|
+
icon: str | None = None
|
98
98
|
imageOptimized: t.Any | None = None
|
99
99
|
iconOptimized: t.Any | None = None
|
100
100
|
outcomePrices: list[USDC]
|
@@ -108,23 +108,23 @@ class Market(BaseModel):
|
|
108
108
|
marketMakerAddress: HexAddress
|
109
109
|
closedTime: datetime | None = None
|
110
110
|
wideFormat: bool | None = None
|
111
|
-
new: bool
|
111
|
+
new: bool | None = None
|
112
112
|
sentDiscord: t.Any | None = None
|
113
113
|
mailchimpTag: t.Any | None = None
|
114
|
-
featured: bool
|
115
|
-
submitted_by: str
|
114
|
+
featured: bool | None = None
|
115
|
+
submitted_by: str | None = None
|
116
116
|
subcategory: t.Any | None = None
|
117
117
|
categoryMailchimpTag: t.Any | None = None
|
118
|
-
archived: bool
|
119
|
-
resolvedBy: str
|
120
|
-
restricted: bool
|
121
|
-
groupItemTitle: str
|
122
|
-
groupItemThreshold: str
|
123
|
-
questionID: str
|
118
|
+
archived: bool | None = None
|
119
|
+
resolvedBy: str | None = None
|
120
|
+
restricted: bool | None = None
|
121
|
+
groupItemTitle: str | None = None
|
122
|
+
groupItemThreshold: str | None = None
|
123
|
+
questionID: str | None = None
|
124
124
|
umaEndDate: t.Any | None = None
|
125
|
-
enableOrderBook: bool
|
126
|
-
orderPriceMinTickSize: float
|
127
|
-
orderMinSize: int
|
125
|
+
enableOrderBook: bool | None = None
|
126
|
+
orderPriceMinTickSize: float | None = None
|
127
|
+
orderMinSize: int | None = None
|
128
128
|
umaResolutionStatus: t.Any | None = None
|
129
129
|
curationOrder: t.Any | None = None
|
130
130
|
volumeNum: USDC | None = None
|
@@ -134,9 +134,9 @@ class Market(BaseModel):
|
|
134
134
|
umaEndDateIso: datetime | None = None
|
135
135
|
commentsEnabled: bool | None = None
|
136
136
|
disqusThread: t.Any | None = None
|
137
|
-
gameStartTime:
|
137
|
+
gameStartTime: t.Any | None = None
|
138
138
|
secondsDelay: int | None = None
|
139
|
-
clobTokenIds: list[str]
|
139
|
+
clobTokenIds: list[str] | None = None
|
140
140
|
liquidityAmm: float | None = None
|
141
141
|
liquidityClob: float | None = None
|
142
142
|
makerBaseFee: int | None = None
|
@@ -144,27 +144,27 @@ class Market(BaseModel):
|
|
144
144
|
negRisk: t.Any | None = None
|
145
145
|
negRiskRequestID: t.Any | None = None
|
146
146
|
negRiskMarketID: t.Any | None = None
|
147
|
-
events: list[Event]
|
148
|
-
markets: list[Market1]
|
147
|
+
events: list[Event] | None = None
|
148
|
+
markets: list[Market1] | None = None
|
149
149
|
lower_bound_date: t.Any | None = None
|
150
150
|
upper_bound_date: t.Any | None = None
|
151
151
|
market_type: str | None = None
|
152
|
-
resolution_source: str
|
153
|
-
end_date: str
|
152
|
+
resolution_source: str | None = None
|
153
|
+
end_date: str | None = None
|
154
154
|
amm_type: t.Any | None = None
|
155
155
|
x_axis_value: t.Any | None = None
|
156
156
|
y_axis_value: t.Any | None = None
|
157
157
|
denomination_token: t.Any | None = None
|
158
|
-
resolved_by: str
|
158
|
+
resolved_by: str | None = None
|
159
159
|
upper_bound: t.Any | None = None
|
160
160
|
lower_bound: t.Any | None = None
|
161
|
-
created_at: str
|
161
|
+
created_at: str | None = None
|
162
162
|
updated_at: t.Any | None = None
|
163
163
|
closed_time: t.Any | None = None
|
164
164
|
wide_format: bool | None = None
|
165
165
|
volume_num: USDC | None = None
|
166
166
|
liquidity_num: USDC | None = None
|
167
|
-
image_raw: str
|
167
|
+
image_raw: str | None = None
|
168
168
|
resolutionData: ResolutionData
|
169
169
|
|
170
170
|
@field_validator("closedTime", mode="before")
|
@@ -234,19 +234,19 @@ class PolymarketFullMarket(BaseModel):
|
|
234
234
|
title: str
|
235
235
|
subtitle: t.Any | None = None
|
236
236
|
description: str
|
237
|
-
commentCount: int
|
238
|
-
resolutionSource: str
|
239
|
-
startDate: datetime
|
237
|
+
commentCount: int | None = None
|
238
|
+
resolutionSource: str | None = None
|
239
|
+
startDate: datetime | None = None
|
240
240
|
endDate: datetime
|
241
|
-
image: str
|
242
|
-
icon: str
|
241
|
+
image: str | None = None
|
242
|
+
icon: str | None = None
|
243
243
|
featuredImage: str | None = None
|
244
244
|
active: bool
|
245
245
|
closed: bool
|
246
|
-
archived: bool
|
247
|
-
new: bool
|
248
|
-
featured: bool
|
249
|
-
restricted: bool
|
246
|
+
archived: bool | None = None
|
247
|
+
new: bool | None = None
|
248
|
+
featured: bool | None = None
|
249
|
+
restricted: bool | None = None
|
250
250
|
liquidity: USDC | None = None
|
251
251
|
volume: USDC | None = None
|
252
252
|
volume24hr: USDC | None = None
|
@@ -257,7 +257,7 @@ class PolymarketFullMarket(BaseModel):
|
|
257
257
|
commentsEnabled: bool | None = None
|
258
258
|
disqusThread: t.Any | None = None
|
259
259
|
updatedAt: datetime
|
260
|
-
enableOrderBook: bool
|
260
|
+
enableOrderBook: bool | None = None
|
261
261
|
liquidityAmm: float | None = None
|
262
262
|
liquidityClob: float | None = None
|
263
263
|
imageOptimized: ImageOptimized | None = None
|
@@ -269,7 +269,7 @@ class PolymarketFullMarket(BaseModel):
|
|
269
269
|
markets: list[Market]
|
270
270
|
categories: list[Category] | None = None
|
271
271
|
series: t.Any | None = None
|
272
|
-
image_raw: str
|
272
|
+
image_raw: str | None = None
|
273
273
|
|
274
274
|
@property
|
275
275
|
def url(self) -> str:
|
@@ -298,6 +298,8 @@ class PolymarketFullMarket(BaseModel):
|
|
298
298
|
|
299
299
|
Warning: This is a very slow operation, as it requires fetching the website. Use it only when necessary.
|
300
300
|
"""
|
301
|
+
logger.info(f"Fetching full market from {url}")
|
302
|
+
|
301
303
|
# Fetch the website as a normal browser would.
|
302
304
|
headers = {
|
303
305
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
|
@@ -352,7 +354,8 @@ class State(BaseModel):
|
|
352
354
|
| dict[str, MarketBidsAndAsks]
|
353
355
|
| dict[str, PriceSide]
|
354
356
|
| None
|
355
|
-
|
357
|
+
# It's none if you go to the website and it says "Oops...we didn't forecast this".
|
358
|
+
)
|
356
359
|
dataUpdateCount: int
|
357
360
|
dataUpdatedAt: int
|
358
361
|
error: t.Any | None = None
|
@@ -135,15 +135,16 @@ def monitor_app(
|
|
135
135
|
)
|
136
136
|
|
137
137
|
st.header("Market Info")
|
138
|
-
|
139
|
-
|
140
|
-
|
138
|
+
if st.checkbox("Show Market Info"):
|
139
|
+
with st.spinner("Loading markets"):
|
140
|
+
open_markets, resolved_markets = get_open_and_resolved_markets(
|
141
|
+
start_time=oldest_start_time, market_type=market_type
|
142
|
+
)
|
143
|
+
(
|
144
|
+
monitor_market(open_markets=open_markets, resolved_markets=resolved_markets)
|
145
|
+
if open_markets and resolved_markets
|
146
|
+
else st.warning("No market data found.")
|
141
147
|
)
|
142
|
-
(
|
143
|
-
monitor_market(open_markets=open_markets, resolved_markets=resolved_markets)
|
144
|
-
if open_markets and resolved_markets
|
145
|
-
else st.warning("No market data found.")
|
146
|
-
)
|
147
148
|
|
148
149
|
st.header("Agent Info")
|
149
150
|
if st.button("Export agents"):
|
@@ -1,23 +1,16 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
|
3
1
|
from pydantic import BaseModel
|
4
2
|
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
def check_is_valid_probability(probability: float) -> None:
|
5
|
+
if not 0 <= probability <= 1:
|
6
|
+
raise ValueError("Probability must be between 0 and 1")
|
9
7
|
|
10
8
|
|
11
9
|
class KellyBet(BaseModel):
|
12
|
-
direction:
|
10
|
+
direction: bool
|
13
11
|
size: float
|
14
12
|
|
15
13
|
|
16
|
-
def check_is_valid_probability(probability: float) -> None:
|
17
|
-
if not 0 <= probability <= 1:
|
18
|
-
raise ValueError("Probability must be between 0 and 1")
|
19
|
-
|
20
|
-
|
21
14
|
def get_kelly_bet(
|
22
15
|
max_bet: float,
|
23
16
|
market_p_yes: float,
|
@@ -47,10 +40,10 @@ def get_kelly_bet(
|
|
47
40
|
check_is_valid_probability(confidence)
|
48
41
|
|
49
42
|
if estimated_p_yes > market_p_yes:
|
50
|
-
bet_direction =
|
43
|
+
bet_direction = True
|
51
44
|
market_prob = market_p_yes
|
52
45
|
else:
|
53
|
-
bet_direction =
|
46
|
+
bet_direction = False
|
54
47
|
market_prob = 1 - market_p_yes
|
55
48
|
|
56
49
|
# Handle the case where market_prob is 0
|
@@ -11,12 +11,13 @@ prediction_market_agent_tooling/abis/omen_realitio.abi.json,sha256=7HmFkBF_rq83U
|
|
11
11
|
prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json,sha256=u1-3B8FB3Ys9KVJCH-lw9ArkicdxbNMf34dV-VEGMMU,930
|
12
12
|
prediction_market_agent_tooling/abis/proxy.abi.json,sha256=h24GXZ6Q0bSZlwh7zOv0EiDvbqUz_PHtWfKHTyPJ1w4,644
|
13
13
|
prediction_market_agent_tooling/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
prediction_market_agent_tooling/benchmark/agents.py,sha256=
|
14
|
+
prediction_market_agent_tooling/benchmark/agents.py,sha256=BwE3U11tQq0rfOJBn-Xn5ZgN1DTXBEehvmdZx6A7DuE,3918
|
15
15
|
prediction_market_agent_tooling/benchmark/benchmark.py,sha256=xiHKzZx5GHSsDerFHMZ9j_LXAXnSaITSvv67iPe3MEU,21095
|
16
|
-
prediction_market_agent_tooling/benchmark/utils.py,sha256=
|
16
|
+
prediction_market_agent_tooling/benchmark/utils.py,sha256=D0MfUkVZllmvcU0VOurk9tcKT7JTtwwOp-63zuCBVuc,2880
|
17
17
|
prediction_market_agent_tooling/config.py,sha256=9h68Nb9O1YZabZqtOBrH1S-4U5aIdLKfVYLSKspfUeA,6008
|
18
|
-
prediction_market_agent_tooling/deploy/agent.py,sha256=
|
19
|
-
prediction_market_agent_tooling/deploy/agent_example.py,sha256=
|
18
|
+
prediction_market_agent_tooling/deploy/agent.py,sha256=MdNiUG2y-E6S_Hc7d31ISIPF7e18-2bmO8SULg7RDd4,17760
|
19
|
+
prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
|
20
|
+
prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=nJizN4d_jsw5WA8uukaKModVezAgVwoLrCOXsaBOwXo,2223
|
20
21
|
prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
|
21
22
|
prediction_market_agent_tooling/deploy/gcp/deploy.py,sha256=CYUgnfy-9XVk04kkxA_5yp0GE9Mw5caYqlFUZQ2j3ks,3739
|
22
23
|
prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py,sha256=qYIHRxQLac3yxtZ8ChikiPG9O1aUQucHW0muTSm1nto,2627
|
@@ -25,7 +26,7 @@ prediction_market_agent_tooling/gtypes.py,sha256=ezM2iAycTRJ0uHKK03s0z76a8YFSF43
|
|
25
26
|
prediction_market_agent_tooling/loggers.py,sha256=JiBTgvb34O9dKHYKZyQ0UzojPUy6KSFQSTfbBIXopSY,3721
|
26
27
|
prediction_market_agent_tooling/markets/agent_market.py,sha256=BELq6x3F4xLZwi0XmoN84RA7ttRQIclyHL2CY6a4Ixc,8409
|
27
28
|
prediction_market_agent_tooling/markets/categorize.py,sha256=jsoHWvZk9pU6n17oWSCcCxNNYVwlb_NXsZxKRI7vmsk,1301
|
28
|
-
prediction_market_agent_tooling/markets/data_models.py,sha256=
|
29
|
+
prediction_market_agent_tooling/markets/data_models.py,sha256=muGWJ8Y3UeDhpwAAGovYGfMMdbbVQcJlS5OruFmaKD4,3108
|
29
30
|
prediction_market_agent_tooling/markets/manifold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
31
|
prediction_market_agent_tooling/markets/manifold/api.py,sha256=AC2zmkzpBU3P4kyybs7CgPbDg4hLAx3GY5mjgDi7qDo,7221
|
31
32
|
prediction_market_agent_tooling/markets/manifold/data_models.py,sha256=jHqOzOiN21wYvDNyh4VtbGtj4adWr6vA4liOQmh24cc,6239
|
@@ -36,14 +37,14 @@ prediction_market_agent_tooling/markets/metaculus/api.py,sha256=gvPQVAM5NlCyWzEM
|
|
36
37
|
prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=6TBy17xntdLBR61QCE5wddwTa_k2D0D8ZgK6p7sGUuc,2448
|
37
38
|
prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=uNF7LP4evvubk818g2zbX1VlnFxeUQOkNgx_e_LwaJA,3416
|
38
39
|
prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
|
-
prediction_market_agent_tooling/markets/omen/data_models.py,sha256=
|
40
|
+
prediction_market_agent_tooling/markets/omen/data_models.py,sha256=7KXT8a_WYyXMFR8OtcPlaazeigc8eE_AvBkx2q-CNi8,16764
|
40
41
|
prediction_market_agent_tooling/markets/omen/omen.py,sha256=fL8Lk1H0guwNBcgvT8Ymy3jRewbWLbJ9m8br4HN7vPg,40014
|
41
|
-
prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=
|
42
|
-
prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=
|
43
|
-
prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=
|
42
|
+
prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=MfaWfDDfEzHYVAbeT3Dgtl8KG7XsqEpdY3m3-rsOPwo,23588
|
43
|
+
prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=Awaw1r32IBAClCktrXbYJ24RNyLDWcLb8RqAx6FGSkI,9529
|
44
|
+
prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=XMCXEZi5TWpxxw5hDJ3QU-19VpD77otw3qhNLT_OhRE,28452
|
44
45
|
prediction_market_agent_tooling/markets/polymarket/api.py,sha256=HXmA1akA0qDj0m3e-GEvWG8x75pm6BX4H7YJPQcST7I,4767
|
45
46
|
prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=9CJzakyEcsn6DQBK2nOXjOMzTZBLAmK_KqevXvW17DI,4292
|
46
|
-
prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=
|
47
|
+
prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=IPsFT3FX9Ge5l5zR1nBd2w-sd5ue7oR8PJSW710vFWY,12479
|
47
48
|
prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=f7r79fAhLzwS22urfuhVW1Si2m2pZrr5r45WNt-Q3VU,2737
|
48
49
|
prediction_market_agent_tooling/markets/polymarket/utils.py,sha256=m4JG6WULh5epCJt4XBMHg0ae5NoVhqlOvAl0A7DR9iM,2023
|
49
50
|
prediction_market_agent_tooling/monitor/markets/manifold.py,sha256=GdYpgRX1GahDi-75Mr53jgtEg6nWcs_rHDUkg4o_7dQ,3352
|
@@ -51,11 +52,11 @@ prediction_market_agent_tooling/monitor/markets/metaculus.py,sha256=S8zeDVN2aA6y
|
|
51
52
|
prediction_market_agent_tooling/monitor/markets/omen.py,sha256=jOLPnIbDU9syjnYtHfOb2xa6-Ize3vbplgh-8WWkuT4,3323
|
52
53
|
prediction_market_agent_tooling/monitor/markets/polymarket.py,sha256=I9z9aO1wncyGI3a09ihrw17JkeBKjAuMmC0I9pl_9o4,1781
|
53
54
|
prediction_market_agent_tooling/monitor/monitor.py,sha256=snMSPmoHFV7QchqsFBaNkOEQhHJshF1qmIGPuID1giQ,14579
|
54
|
-
prediction_market_agent_tooling/monitor/monitor_app.py,sha256=
|
55
|
+
prediction_market_agent_tooling/monitor/monitor_app.py,sha256=1e4LuzhAVjb7cPS6rGPZuZHMwMiNOeRhSxG8AVG-e0o,4839
|
55
56
|
prediction_market_agent_tooling/monitor/monitor_settings.py,sha256=Xiozs3AsufuJ04JOe1vjUri-IAMWHjjmc2ugGGiHNH4,947
|
56
57
|
prediction_market_agent_tooling/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
58
|
prediction_market_agent_tooling/tools/balances.py,sha256=nR8_dSfbm3yTOOmMAwhGlurftEiNo1w1WIVzbskjdmM,837
|
58
|
-
prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py,sha256=
|
59
|
+
prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py,sha256=IoLx8fIOqjC9d2gSWIc5Y-3wlukib6cuJaJzguHqNbU,1835
|
59
60
|
prediction_market_agent_tooling/tools/betting_strategies/market_moving.py,sha256=wtrHVQRuA0uDx06z0OxQLYbswuOpHQ1UyCWwLCrD_oM,4400
|
60
61
|
prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py,sha256=-FUSuQQgjcWSSnoFxnlAyTeilY6raJABJVM2QKkFqAY,438
|
61
62
|
prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py,sha256=THMXwFlskvzbjnX_OiYtDSzI8XVFyULWfP2525_9UGc,429
|
@@ -67,7 +68,7 @@ prediction_market_agent_tooling/tools/google.py,sha256=SfVDxb3oEOUK8mpd0l3mTX9yb
|
|
67
68
|
prediction_market_agent_tooling/tools/hexbytes_custom.py,sha256=Bp94qgPjvjWf1Vb4lNzGFDXRdThw1rJ91vL6r2PWq5E,2096
|
68
69
|
prediction_market_agent_tooling/tools/image_gen/image_gen.py,sha256=HzRwBx62hOXBOmrtpkXaP9Qq1Ku03uUGdREocyjLQ_k,1266
|
69
70
|
prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=8A3U2uxsCsOfLjru-6R_PPIAuiKY4qFkWp_GSBPV6-s,1280
|
70
|
-
prediction_market_agent_tooling/tools/is_predictable.py,sha256=
|
71
|
+
prediction_market_agent_tooling/tools/is_predictable.py,sha256=GqBgp4aHbY97PdprKfCHV-lJy2bbGIrd9yCtkqg6gEc,6558
|
71
72
|
prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_AeDVhRzLMaQLxH3kxDu3L8,1153
|
72
73
|
prediction_market_agent_tooling/tools/parallelism.py,sha256=Rz8QdVUWX8KCbr8UZfaC_b1GBWIb3bXwITUumuvBJ60,1633
|
73
74
|
prediction_market_agent_tooling/tools/safe.py,sha256=h0xOO0eNtitClf0fPkn-0oTc6A_bflDTee98V_aiV-A,5195
|
@@ -77,8 +78,8 @@ prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py,sha256=dr-
|
|
77
78
|
prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py,sha256=eln3BygdfsDf8WOlSQPL7qH946QnGnThRkazRZKBA8o,3194
|
78
79
|
prediction_market_agent_tooling/tools/utils.py,sha256=JE9YWtPPhnTgLiOyGAZDNG5K8nCwUY9IZEuAlm9UcxA,6611
|
79
80
|
prediction_market_agent_tooling/tools/web3_utils.py,sha256=euq_MH-LhTowACq42colks2IXxZQjqjuisYl91EYZtU,10440
|
80
|
-
prediction_market_agent_tooling-0.48.
|
81
|
-
prediction_market_agent_tooling-0.48.
|
82
|
-
prediction_market_agent_tooling-0.48.
|
83
|
-
prediction_market_agent_tooling-0.48.
|
84
|
-
prediction_market_agent_tooling-0.48.
|
81
|
+
prediction_market_agent_tooling-0.48.8.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
82
|
+
prediction_market_agent_tooling-0.48.8.dist-info/METADATA,sha256=-l7BCizaFtD0scKftcebwoel16w9o2nggRSRjMJ_EEc,7810
|
83
|
+
prediction_market_agent_tooling-0.48.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
84
|
+
prediction_market_agent_tooling-0.48.8.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
|
85
|
+
prediction_market_agent_tooling-0.48.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|