prediction-market-agent-tooling 0.48.5__tar.gz → 0.48.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/PKG-INFO +1 -1
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/benchmark/agents.py +0 -2
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/benchmark/utils.py +5 -3
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/deploy/agent.py +35 -27
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/deploy/agent_example.py +5 -6
- prediction_market_agent_tooling-0.48.8/prediction_market_agent_tooling/deploy/betting_strategy.py +62 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/data_models.py +44 -3
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/omen/data_models.py +71 -3
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +3 -2
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +22 -44
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +63 -1
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +41 -38
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/monitor_app.py +9 -8
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +6 -13
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/is_predictable.py +1 -1
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/pyproject.toml +1 -1
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/README.md +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/depositablewrapper_erc20.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/erc4626.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/omen_thumbnailmapping.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/abis/proxy.abi.json +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/config.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/deploy/constants.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/gtypes.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/loggers.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/agent_market.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/categorize.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/manifold/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/manifold/manifold.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/markets.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/metaculus/api.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/metaculus/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/metaculus/metaculus.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/omen/omen.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/monitor.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/py.typed +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/balances.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/cache.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/contract.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/costs.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/google.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/image_gen/image_gen.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/langfuse_.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/safe.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/singleton.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/streamlit_user_login.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/utils.py +0 -0
- {prediction_market_agent_tooling-0.48.5 → prediction_market_agent_tooling-0.48.8}/prediction_market_agent_tooling/tools/web3_utils.py +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!")
|
prediction_market_agent_tooling-0.48.8/prediction_market_agent_tooling/deploy/betting_strategy.py
ADDED
@@ -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,
|