prediction-market-agent-tooling 0.55.2.dev120__py3-none-any.whl → 0.56.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prediction_market_agent_tooling/deploy/agent.py +17 -7
- prediction_market_agent_tooling/jobs/jobs_models.py +27 -2
- prediction_market_agent_tooling/jobs/omen/omen_jobs.py +67 -41
- prediction_market_agent_tooling/markets/agent_market.py +8 -2
- prediction_market_agent_tooling/markets/markets.py +12 -0
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +1 -1
- prediction_market_agent_tooling/markets/omen/data_models.py +11 -2
- prediction_market_agent_tooling/markets/omen/omen.py +16 -9
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +14 -0
- prediction_market_agent_tooling/tools/caches/db_cache.py +351 -0
- prediction_market_agent_tooling/tools/google.py +3 -2
- prediction_market_agent_tooling/tools/is_invalid.py +2 -2
- prediction_market_agent_tooling/tools/is_predictable.py +3 -3
- prediction_market_agent_tooling/tools/relevant_news_analysis/relevant_news_analysis.py +6 -10
- prediction_market_agent_tooling/tools/tavily/tavily_models.py +0 -66
- prediction_market_agent_tooling/tools/tavily/tavily_search.py +12 -44
- prediction_market_agent_tooling/tools/utils.py +2 -0
- {prediction_market_agent_tooling-0.55.2.dev120.dist-info → prediction_market_agent_tooling-0.56.0.dist-info}/METADATA +2 -1
- {prediction_market_agent_tooling-0.55.2.dev120.dist-info → prediction_market_agent_tooling-0.56.0.dist-info}/RECORD +23 -24
- prediction_market_agent_tooling/jobs/jobs.py +0 -45
- prediction_market_agent_tooling/tools/tavily/tavily_storage.py +0 -105
- /prediction_market_agent_tooling/tools/{cache.py → caches/inmemory_cache.py} +0 -0
- {prediction_market_agent_tooling-0.55.2.dev120.dist-info → prediction_market_agent_tooling-0.56.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.55.2.dev120.dist-info → prediction_market_agent_tooling-0.56.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.55.2.dev120.dist-info → prediction_market_agent_tooling-0.56.0.dist-info}/entry_points.txt +0 -0
@@ -153,10 +153,10 @@ class DeployableAgent:
|
|
153
153
|
input=input,
|
154
154
|
output=output,
|
155
155
|
user_id=user_id or getpass.getuser(),
|
156
|
-
session_id=session_id
|
157
|
-
|
158
|
-
version=version
|
159
|
-
|
156
|
+
session_id=session_id or self.session_id,
|
157
|
+
# All traces within a single run execution will be grouped under a single session.
|
158
|
+
version=version or APIKeys().LANGFUSE_DEPLOYMENT_VERSION,
|
159
|
+
# Optionally, mark the current deployment with version (e.g. add git commit hash during docker building).
|
160
160
|
release=release,
|
161
161
|
metadata=metadata,
|
162
162
|
tags=tags,
|
@@ -342,6 +342,10 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
342
342
|
]
|
343
343
|
)
|
344
344
|
|
345
|
+
@property
|
346
|
+
def agent_name(self) -> str:
|
347
|
+
return self.__class__.__name__
|
348
|
+
|
345
349
|
def check_min_required_balance_to_operate(self, market_type: MarketType) -> None:
|
346
350
|
api_keys = APIKeys()
|
347
351
|
|
@@ -444,7 +448,9 @@ class DeployablePredictionAgent(DeployableAgent):
|
|
444
448
|
) -> None:
|
445
449
|
keys = APIKeys()
|
446
450
|
if self.store_prediction:
|
447
|
-
market.store_prediction(
|
451
|
+
market.store_prediction(
|
452
|
+
processed_market=processed_market, keys=keys, agent_name=self.agent_name
|
453
|
+
)
|
448
454
|
else:
|
449
455
|
logger.info(
|
450
456
|
f"Prediction {processed_market} not stored because {self.store_prediction=}."
|
@@ -613,10 +619,14 @@ class DeployableTraderAgent(DeployablePredictionAgent):
|
|
613
619
|
processed_market: ProcessedMarket | None,
|
614
620
|
) -> None:
|
615
621
|
api_keys = APIKeys()
|
616
|
-
super().after_process_market(
|
622
|
+
super().after_process_market(
|
623
|
+
market_type,
|
624
|
+
market,
|
625
|
+
processed_market,
|
626
|
+
)
|
617
627
|
if isinstance(processed_market, ProcessedTradedMarket):
|
618
628
|
if self.store_trades:
|
619
|
-
market.store_trades(processed_market, api_keys)
|
629
|
+
market.store_trades(processed_market, api_keys, self.agent_name)
|
620
630
|
else:
|
621
631
|
logger.info(
|
622
632
|
f"Trades {processed_market.trades} not stored because {self.store_trades=}."
|
@@ -3,7 +3,12 @@ from abc import ABC, abstractmethod
|
|
3
3
|
|
4
4
|
from pydantic import BaseModel
|
5
5
|
|
6
|
-
from prediction_market_agent_tooling.
|
6
|
+
from prediction_market_agent_tooling.deploy.betting_strategy import ProbabilisticAnswer
|
7
|
+
from prediction_market_agent_tooling.gtypes import Probability
|
8
|
+
from prediction_market_agent_tooling.markets.agent_market import (
|
9
|
+
AgentMarket,
|
10
|
+
ProcessedTradedMarket,
|
11
|
+
)
|
7
12
|
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
8
13
|
FilterBy,
|
9
14
|
SortBy,
|
@@ -39,10 +44,24 @@ class JobAgentMarket(AgentMarket, ABC):
|
|
39
44
|
@classmethod
|
40
45
|
@abstractmethod
|
41
46
|
def get_jobs(
|
42
|
-
cls,
|
47
|
+
cls,
|
48
|
+
limit: int | None,
|
49
|
+
filter_by: FilterBy = FilterBy.OPEN,
|
50
|
+
sort_by: SortBy = SortBy.CLOSING_SOONEST,
|
43
51
|
) -> t.Sequence["JobAgentMarket"]:
|
44
52
|
"""Get all available jobs."""
|
45
53
|
|
54
|
+
@staticmethod
|
55
|
+
@abstractmethod
|
56
|
+
def get_job(id: str) -> "JobAgentMarket":
|
57
|
+
"""Get a single job by its id."""
|
58
|
+
|
59
|
+
@abstractmethod
|
60
|
+
def submit_job_result(
|
61
|
+
self, agent_name: str, max_bond: float, result: str
|
62
|
+
) -> ProcessedTradedMarket:
|
63
|
+
"""Submit the completed result for this job."""
|
64
|
+
|
46
65
|
def to_simple_job(self, max_bond: float) -> SimpleJob:
|
47
66
|
return SimpleJob(
|
48
67
|
id=self.id,
|
@@ -51,3 +70,9 @@ class JobAgentMarket(AgentMarket, ABC):
|
|
51
70
|
currency=self.currency.value,
|
52
71
|
deadline=self.deadline,
|
53
72
|
)
|
73
|
+
|
74
|
+
def get_job_answer(self, result: str) -> ProbabilisticAnswer:
|
75
|
+
# Just return 100% yes with 100% confidence, because we assume the job is completed correctly.
|
76
|
+
return ProbabilisticAnswer(
|
77
|
+
p_yes=Probability(1.0), confidence=1.0, reasoning=result
|
78
|
+
)
|
@@ -1,15 +1,14 @@
|
|
1
1
|
import typing as t
|
2
2
|
|
3
|
-
from
|
4
|
-
|
3
|
+
from prediction_market_agent_tooling.config import APIKeys
|
5
4
|
from prediction_market_agent_tooling.deploy.betting_strategy import (
|
6
5
|
Currency,
|
7
6
|
KellyBettingStrategy,
|
8
|
-
ProbabilisticAnswer,
|
9
7
|
TradeType,
|
10
8
|
)
|
11
|
-
from prediction_market_agent_tooling.gtypes import Probability
|
12
9
|
from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
|
10
|
+
from prediction_market_agent_tooling.markets.agent_market import ProcessedTradedMarket
|
11
|
+
from prediction_market_agent_tooling.markets.data_models import PlacedTrade, Trade
|
13
12
|
from prediction_market_agent_tooling.markets.omen.omen import (
|
14
13
|
BetAmount,
|
15
14
|
OmenAgentMarket,
|
@@ -36,11 +35,27 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
|
|
36
35
|
return self.close_time
|
37
36
|
|
38
37
|
def get_reward(self, max_bond: float) -> float:
|
39
|
-
|
38
|
+
trade = self.get_job_trade(
|
39
|
+
max_bond,
|
40
|
+
result="", # Pass empty result, as we are computing only potential reward at this point.
|
41
|
+
)
|
42
|
+
reward = (
|
43
|
+
self.get_buy_token_amount(
|
44
|
+
bet_amount=BetAmount(
|
45
|
+
amount=trade.amount.amount, currency=trade.amount.currency
|
46
|
+
),
|
47
|
+
direction=trade.outcome,
|
48
|
+
).amount
|
49
|
+
- trade.amount.amount
|
50
|
+
)
|
51
|
+
return reward
|
40
52
|
|
41
53
|
@classmethod
|
42
54
|
def get_jobs(
|
43
|
-
cls,
|
55
|
+
cls,
|
56
|
+
limit: int | None,
|
57
|
+
filter_by: FilterBy = FilterBy.OPEN,
|
58
|
+
sort_by: SortBy = SortBy.CLOSING_SOONEST,
|
44
59
|
) -> t.Sequence["OmenJobAgentMarket"]:
|
45
60
|
markets = OmenSubgraphHandler().get_omen_binary_markets_simple(
|
46
61
|
limit=limit,
|
@@ -50,6 +65,52 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
|
|
50
65
|
)
|
51
66
|
return [OmenJobAgentMarket.from_omen_market(market) for market in markets]
|
52
67
|
|
68
|
+
@staticmethod
|
69
|
+
def get_job(id: str) -> "OmenJobAgentMarket":
|
70
|
+
return OmenJobAgentMarket.from_omen_agent_market(
|
71
|
+
OmenJobAgentMarket.get_binary_market(id=id)
|
72
|
+
)
|
73
|
+
|
74
|
+
def submit_job_result(
|
75
|
+
self, agent_name: str, max_bond: float, result: str
|
76
|
+
) -> ProcessedTradedMarket:
|
77
|
+
if not APIKeys().enable_ipfs_upload:
|
78
|
+
raise RuntimeError(
|
79
|
+
f"ENABLE_IPFS_UPLOAD must be set to True to upload job results."
|
80
|
+
)
|
81
|
+
|
82
|
+
trade = self.get_job_trade(max_bond, result)
|
83
|
+
buy_id = self.buy_tokens(outcome=trade.outcome, amount=trade.amount)
|
84
|
+
|
85
|
+
processed_traded_market = ProcessedTradedMarket(
|
86
|
+
answer=self.get_job_answer(result),
|
87
|
+
trades=[PlacedTrade.from_trade(trade, id=buy_id)],
|
88
|
+
)
|
89
|
+
|
90
|
+
keys = APIKeys()
|
91
|
+
self.store_trades(processed_traded_market, keys, agent_name)
|
92
|
+
|
93
|
+
return processed_traded_market
|
94
|
+
|
95
|
+
def get_job_trade(self, max_bond: float, result: str) -> Trade:
|
96
|
+
# Because jobs are powered by prediction markets, potentional reward depends on job's liquidity and our will to bond (bet) our xDai into our job completion.
|
97
|
+
strategy = KellyBettingStrategy(max_bet_amount=max_bond)
|
98
|
+
required_trades = strategy.calculate_trades(
|
99
|
+
existing_position=None,
|
100
|
+
answer=self.get_job_answer(result),
|
101
|
+
market=self,
|
102
|
+
)
|
103
|
+
assert (
|
104
|
+
len(required_trades) == 1
|
105
|
+
), f"Shouldn't process same job twice: {required_trades}"
|
106
|
+
trade = required_trades[0]
|
107
|
+
assert trade.trade_type == TradeType.BUY, "Should only buy on job markets."
|
108
|
+
assert trade.outcome, "Should buy only YES on job markets."
|
109
|
+
assert (
|
110
|
+
trade.amount.currency == Currency.xDai
|
111
|
+
), "Should work only on real-money markets."
|
112
|
+
return required_trades[0]
|
113
|
+
|
53
114
|
@staticmethod
|
54
115
|
def from_omen_market(market: OmenMarket) -> "OmenJobAgentMarket":
|
55
116
|
return OmenJobAgentMarket.from_omen_agent_market(
|
@@ -77,38 +138,3 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
|
|
77
138
|
finalized_time=market.finalized_time,
|
78
139
|
fees=market.fees,
|
79
140
|
)
|
80
|
-
|
81
|
-
|
82
|
-
def compute_job_reward(
|
83
|
-
market: OmenAgentMarket, max_bond: float, web3: Web3 | None = None
|
84
|
-
) -> float:
|
85
|
-
# Because jobs are powered by prediction markets, potentional reward depends on job's liquidity and our will to bond (bet) our xDai into our job completion.
|
86
|
-
strategy = KellyBettingStrategy(max_bet_amount=max_bond)
|
87
|
-
required_trades = strategy.calculate_trades(
|
88
|
-
existing_position=None,
|
89
|
-
# We assume that we finish the job and so the probability of the market happening will be 100%.
|
90
|
-
answer=ProbabilisticAnswer(p_yes=Probability(1.0), confidence=1.0),
|
91
|
-
market=market,
|
92
|
-
)
|
93
|
-
|
94
|
-
assert (
|
95
|
-
len(required_trades) == 1
|
96
|
-
), f"Shouldn't process same job twice: {required_trades}"
|
97
|
-
trade = required_trades[0]
|
98
|
-
assert trade.trade_type == TradeType.BUY, "Should only buy on job markets."
|
99
|
-
assert trade.outcome, "Should buy only YES on job markets."
|
100
|
-
assert (
|
101
|
-
trade.amount.currency == Currency.xDai
|
102
|
-
), "Should work only on real-money markets."
|
103
|
-
|
104
|
-
reward = (
|
105
|
-
market.get_buy_token_amount(
|
106
|
-
bet_amount=BetAmount(
|
107
|
-
amount=trade.amount.amount, currency=trade.amount.currency
|
108
|
-
),
|
109
|
-
direction=trade.outcome,
|
110
|
-
).amount
|
111
|
-
- trade.amount.amount
|
112
|
-
)
|
113
|
-
|
114
|
-
return reward
|
@@ -231,7 +231,10 @@ class AgentMarket(BaseModel):
|
|
231
231
|
raise NotImplementedError("Subclasses must implement this method")
|
232
232
|
|
233
233
|
def store_prediction(
|
234
|
-
self,
|
234
|
+
self,
|
235
|
+
processed_market: ProcessedMarket | None,
|
236
|
+
keys: APIKeys,
|
237
|
+
agent_name: str,
|
235
238
|
) -> None:
|
236
239
|
"""
|
237
240
|
If market allows to upload predictions somewhere, implement it in this method.
|
@@ -239,7 +242,10 @@ class AgentMarket(BaseModel):
|
|
239
242
|
raise NotImplementedError("Subclasses must implement this method")
|
240
243
|
|
241
244
|
def store_trades(
|
242
|
-
self,
|
245
|
+
self,
|
246
|
+
traded_market: ProcessedTradedMarket | None,
|
247
|
+
keys: APIKeys,
|
248
|
+
agent_name: str,
|
243
249
|
) -> None:
|
244
250
|
"""
|
245
251
|
If market allows to upload trades somewhere, implement it in this method.
|
@@ -3,6 +3,8 @@ from datetime import timedelta
|
|
3
3
|
from enum import Enum
|
4
4
|
|
5
5
|
from prediction_market_agent_tooling.config import APIKeys
|
6
|
+
from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
|
7
|
+
from prediction_market_agent_tooling.jobs.omen.omen_jobs import OmenJobAgentMarket
|
6
8
|
from prediction_market_agent_tooling.markets.agent_market import (
|
7
9
|
AgentMarket,
|
8
10
|
FilterBy,
|
@@ -46,6 +48,12 @@ class MarketType(str, Enum):
|
|
46
48
|
raise ValueError(f"Unknown market type: {self}")
|
47
49
|
return MARKET_TYPE_TO_AGENT_MARKET[self]
|
48
50
|
|
51
|
+
@property
|
52
|
+
def job_class(self) -> type[JobAgentMarket]:
|
53
|
+
if self not in JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET:
|
54
|
+
raise ValueError(f"Unknown market type: {self}")
|
55
|
+
return JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET[self]
|
56
|
+
|
49
57
|
@property
|
50
58
|
def is_blockchain_market(self) -> bool:
|
51
59
|
return self in [MarketType.OMEN, MarketType.POLYMARKET]
|
@@ -58,6 +66,10 @@ MARKET_TYPE_TO_AGENT_MARKET: dict[MarketType, type[AgentMarket]] = {
|
|
58
66
|
MarketType.METACULUS: MetaculusAgentMarket,
|
59
67
|
}
|
60
68
|
|
69
|
+
JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET: dict[MarketType, type[JobAgentMarket]] = {
|
70
|
+
MarketType.OMEN: OmenJobAgentMarket,
|
71
|
+
}
|
72
|
+
|
61
73
|
|
62
74
|
def get_binary_markets(
|
63
75
|
limit: int,
|
@@ -107,7 +107,7 @@ class MetaculusAgentMarket(AgentMarket):
|
|
107
107
|
return [MetaculusAgentMarket.from_data_model(q) for q in all_questions[:limit]]
|
108
108
|
|
109
109
|
def store_prediction(
|
110
|
-
self, processed_market: ProcessedMarket | None, keys: APIKeys
|
110
|
+
self, processed_market: ProcessedMarket | None, keys: APIKeys, agent_name: str
|
111
111
|
) -> None:
|
112
112
|
if processed_market is not None:
|
113
113
|
make_prediction(self.id, processed_market.answer.p_yes)
|
@@ -24,6 +24,7 @@ from prediction_market_agent_tooling.markets.data_models import (
|
|
24
24
|
ResolvedBet,
|
25
25
|
)
|
26
26
|
from prediction_market_agent_tooling.tools.utils import (
|
27
|
+
BPS_CONSTANT,
|
27
28
|
DatetimeUTC,
|
28
29
|
check_not_none,
|
29
30
|
should_not_happen,
|
@@ -502,7 +503,7 @@ class OmenBet(BaseModel):
|
|
502
503
|
feeAmount: Wei
|
503
504
|
outcomeIndex: int
|
504
505
|
outcomeTokensTraded: Wei
|
505
|
-
transactionHash:
|
506
|
+
transactionHash: HexBytes
|
506
507
|
fpmm: OmenMarket
|
507
508
|
|
508
509
|
@property
|
@@ -785,6 +786,14 @@ class ContractPrediction(BaseModel):
|
|
785
786
|
tx_hashes: list[HexBytes] = Field(..., alias="txHashes")
|
786
787
|
estimated_probability_bps: int = Field(..., alias="estimatedProbabilityBps")
|
787
788
|
|
789
|
+
@property
|
790
|
+
def estimated_probability(self) -> Probability:
|
791
|
+
return Probability(self.estimated_probability_bps / BPS_CONSTANT)
|
792
|
+
|
793
|
+
@property
|
794
|
+
def boolean_outcome(self) -> bool:
|
795
|
+
return self.estimated_probability > 0.5
|
796
|
+
|
788
797
|
@computed_field # type: ignore[prop-decorator] # Mypy issue: https://github.com/python/mypy/issues/14461
|
789
798
|
@property
|
790
799
|
def publisher_checksummed(self) -> ChecksumAddress:
|
@@ -798,7 +807,7 @@ class ContractPrediction(BaseModel):
|
|
798
807
|
|
799
808
|
class IPFSAgentResult(BaseModel):
|
800
809
|
reasoning: str
|
801
|
-
|
810
|
+
agent_name: str
|
802
811
|
model_config = ConfigDict(
|
803
812
|
extra="forbid",
|
804
813
|
)
|
@@ -78,6 +78,7 @@ from prediction_market_agent_tooling.tools.contract import (
|
|
78
78
|
from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
|
79
79
|
from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
|
80
80
|
from prediction_market_agent_tooling.tools.utils import (
|
81
|
+
BPS_CONSTANT,
|
81
82
|
DatetimeUTC,
|
82
83
|
calculate_sell_amount_in_collateral,
|
83
84
|
check_not_none,
|
@@ -413,17 +414,21 @@ class OmenAgentMarket(AgentMarket):
|
|
413
414
|
@staticmethod
|
414
415
|
def verify_operational_balance(api_keys: APIKeys) -> bool:
|
415
416
|
return get_total_balance(
|
416
|
-
api_keys.public_key,
|
417
|
+
api_keys.public_key,
|
418
|
+
# Use `public_key`, not `bet_from_address` because transaction costs are paid from the EOA wallet.
|
417
419
|
sum_wxdai=False,
|
418
420
|
) > xdai_type(0.001)
|
419
421
|
|
420
422
|
def store_prediction(
|
421
|
-
self, processed_market: ProcessedMarket | None, keys: APIKeys
|
423
|
+
self, processed_market: ProcessedMarket | None, keys: APIKeys, agent_name: str
|
422
424
|
) -> None:
|
423
425
|
"""On Omen, we have to store predictions along with trades, see `store_trades`."""
|
424
426
|
|
425
427
|
def store_trades(
|
426
|
-
self,
|
428
|
+
self,
|
429
|
+
traded_market: ProcessedTradedMarket | None,
|
430
|
+
keys: APIKeys,
|
431
|
+
agent_name: str,
|
427
432
|
) -> None:
|
428
433
|
if traded_market is None:
|
429
434
|
logger.warning(f"No prediction for market {self.id}, not storing anything.")
|
@@ -437,7 +442,7 @@ class OmenAgentMarket(AgentMarket):
|
|
437
442
|
if keys.enable_ipfs_upload:
|
438
443
|
logger.info("Storing prediction on IPFS.")
|
439
444
|
ipfs_hash = IPFSHandler(keys).store_agent_result(
|
440
|
-
IPFSAgentResult(reasoning=reasoning)
|
445
|
+
IPFSAgentResult(reasoning=reasoning, agent_name=agent_name)
|
441
446
|
)
|
442
447
|
ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)
|
443
448
|
|
@@ -448,7 +453,7 @@ class OmenAgentMarket(AgentMarket):
|
|
448
453
|
publisher=keys.public_key,
|
449
454
|
ipfs_hash=ipfs_hash_decoded,
|
450
455
|
tx_hashes=tx_hashes,
|
451
|
-
estimated_probability_bps=int(traded_market.answer.p_yes *
|
456
|
+
estimated_probability_bps=int(traded_market.answer.p_yes * BPS_CONSTANT),
|
452
457
|
)
|
453
458
|
tx_receipt = OmenAgentResultMappingContract().add_prediction(
|
454
459
|
api_keys=keys,
|
@@ -1237,12 +1242,12 @@ def redeem_from_all_user_positions(
|
|
1237
1242
|
|
1238
1243
|
if not conditional_token_contract.is_condition_resolved(condition_id):
|
1239
1244
|
logger.info(
|
1240
|
-
f"[{index+1} / {len(user_positions)}] Skipping redeem, {user_position.id=} isn't resolved yet."
|
1245
|
+
f"[{index + 1} / {len(user_positions)}] Skipping redeem, {user_position.id=} isn't resolved yet."
|
1241
1246
|
)
|
1242
1247
|
continue
|
1243
1248
|
|
1244
1249
|
logger.info(
|
1245
|
-
f"[{index+1} / {len(user_positions)}] Processing redeem from {user_position.id=}."
|
1250
|
+
f"[{index + 1} / {len(user_positions)}] Processing redeem from {user_position.id=}."
|
1246
1251
|
)
|
1247
1252
|
|
1248
1253
|
original_balances = get_balances(public_key, web3)
|
@@ -1263,9 +1268,11 @@ def redeem_from_all_user_positions(
|
|
1263
1268
|
def get_binary_market_p_yes_history(market: OmenAgentMarket) -> list[Probability]:
|
1264
1269
|
history: list[Probability] = []
|
1265
1270
|
trades = sorted(
|
1266
|
-
OmenSubgraphHandler().get_trades(
|
1271
|
+
OmenSubgraphHandler().get_trades(
|
1272
|
+
# We need to look at price both after buying or selling, so get trades, not bets.
|
1267
1273
|
market_id=market.market_maker_contract_address_checksummed,
|
1268
|
-
end_time=market.close_time,
|
1274
|
+
end_time=market.close_time,
|
1275
|
+
# Even after market is closed, there can be many `Sell` trades which will converge the probability to the true one.
|
1269
1276
|
),
|
1270
1277
|
key=lambda x: x.creation_datetime,
|
1271
1278
|
)
|
@@ -908,3 +908,17 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
908
908
|
if not items:
|
909
909
|
return []
|
910
910
|
return [ContractPrediction.model_validate(i) for i in items]
|
911
|
+
|
912
|
+
def get_agent_results_for_bet(self, bet: OmenBet) -> ContractPrediction | None:
|
913
|
+
results = [
|
914
|
+
result
|
915
|
+
for result in self.get_agent_results_for_market(bet.fpmm.id)
|
916
|
+
if bet.transactionHash in result.tx_hashes
|
917
|
+
]
|
918
|
+
|
919
|
+
if not results:
|
920
|
+
return None
|
921
|
+
elif len(results) > 1:
|
922
|
+
raise RuntimeError("Multiple results found for a single bet.")
|
923
|
+
|
924
|
+
return results[0]
|