prediction-market-agent-tooling 0.48.13__py3-none-any.whl → 0.48.15__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.
Files changed (21) hide show
  1. prediction_market_agent_tooling/deploy/agent.py +9 -0
  2. prediction_market_agent_tooling/deploy/betting_strategy.py +23 -6
  3. prediction_market_agent_tooling/gtypes.py +1 -1
  4. prediction_market_agent_tooling/jobs/__init__.py +0 -0
  5. prediction_market_agent_tooling/jobs/jobs.py +45 -0
  6. prediction_market_agent_tooling/jobs/jobs_models.py +53 -0
  7. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +113 -0
  8. prediction_market_agent_tooling/markets/omen/data_models.py +3 -0
  9. prediction_market_agent_tooling/markets/omen/omen.py +97 -8
  10. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +17 -8
  11. prediction_market_agent_tooling/monitor/monitor.py +5 -1
  12. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +91 -9
  13. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +83 -67
  14. prediction_market_agent_tooling/tools/betting_strategies/utils.py +6 -0
  15. prediction_market_agent_tooling/tools/is_predictable.py +3 -0
  16. prediction_market_agent_tooling/tools/langfuse_client_utils.py +159 -0
  17. {prediction_market_agent_tooling-0.48.13.dist-info → prediction_market_agent_tooling-0.48.15.dist-info}/METADATA +1 -1
  18. {prediction_market_agent_tooling-0.48.13.dist-info → prediction_market_agent_tooling-0.48.15.dist-info}/RECORD +21 -15
  19. {prediction_market_agent_tooling-0.48.13.dist-info → prediction_market_agent_tooling-0.48.15.dist-info}/LICENSE +0 -0
  20. {prediction_market_agent_tooling-0.48.13.dist-info → prediction_market_agent_tooling-0.48.15.dist-info}/WHEEL +0 -0
  21. {prediction_market_agent_tooling-0.48.13.dist-info → prediction_market_agent_tooling-0.48.15.dist-info}/entry_points.txt +0 -0
@@ -426,6 +426,8 @@ class DeployableTraderAgent(DeployableAgent):
426
426
  market: AgentMarket,
427
427
  verify_market: bool = True,
428
428
  ) -> ProcessedMarket | None:
429
+ logger.info(f"Processing market {market.question=} from {market.url=}.")
430
+
429
431
  self.before_process_market(market_type, market)
430
432
 
431
433
  if verify_market and not self.verify_market(market_type, market):
@@ -462,6 +464,7 @@ class DeployableTraderAgent(DeployableAgent):
462
464
  processed_market = ProcessedMarket(answer=answer, trades=trades)
463
465
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
464
466
 
467
+ logger.info(f"Processed market {market.question=} from {market.url=}.")
465
468
  return processed_market
466
469
 
467
470
  def after_process_market(
@@ -495,7 +498,11 @@ class DeployableTraderAgent(DeployableAgent):
495
498
  """
496
499
  Processes bets placed by agents on a given market.
497
500
  """
501
+ logger.info("Start processing of markets.")
498
502
  available_markets = self.get_markets(market_type)
503
+ logger.info(
504
+ f"Fetched {len(available_markets)=} markets to process, going to process {self.bet_on_n_markets_per_run=}."
505
+ )
499
506
  processed = 0
500
507
 
501
508
  for market in available_markets:
@@ -510,6 +517,8 @@ class DeployableTraderAgent(DeployableAgent):
510
517
  if processed == self.bet_on_n_markets_per_run:
511
518
  break
512
519
 
520
+ logger.info("All markets processed.")
521
+
513
522
  def after_process_markets(self, market_type: MarketType) -> None:
514
523
  pass
515
524
 
@@ -11,8 +11,10 @@ from prediction_market_agent_tooling.markets.data_models import (
11
11
  )
12
12
  from prediction_market_agent_tooling.markets.omen.data_models import get_boolean_outcome
13
13
  from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
14
- get_kelly_bet,
14
+ get_kelly_bet_full,
15
+ get_kelly_bet_simplified,
15
16
  )
17
+ from prediction_market_agent_tooling.tools.utils import check_not_none
16
18
 
17
19
 
18
20
  class BettingStrategy(ABC):
@@ -168,11 +170,26 @@ class KellyBettingStrategy(BettingStrategy):
168
170
  market: AgentMarket,
169
171
  ) -> list[Trade]:
170
172
  adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
171
- kelly_bet = get_kelly_bet(
172
- adjusted_bet_amount,
173
- market.current_p_yes,
174
- answer.p_yes,
175
- answer.confidence,
173
+ outcome_token_pool = check_not_none(market.outcome_token_pool)
174
+ kelly_bet = (
175
+ get_kelly_bet_full(
176
+ yes_outcome_pool_size=outcome_token_pool[
177
+ market.get_outcome_str_from_bool(True)
178
+ ],
179
+ no_outcome_pool_size=outcome_token_pool[
180
+ market.get_outcome_str_from_bool(False)
181
+ ],
182
+ estimated_p_yes=answer.p_yes,
183
+ max_bet=adjusted_bet_amount,
184
+ confidence=answer.confidence,
185
+ )
186
+ if market.has_token_pool()
187
+ else get_kelly_bet_simplified(
188
+ adjusted_bet_amount,
189
+ market.current_p_yes,
190
+ answer.p_yes,
191
+ answer.confidence,
192
+ )
176
193
  )
177
194
 
178
195
  amounts = {
@@ -27,7 +27,7 @@ PrivateKey = NewType("PrivateKey", SecretStr)
27
27
  xDai = NewType("xDai", float)
28
28
  GNO = NewType("GNO", float)
29
29
  ABI = NewType("ABI", str)
30
- OmenOutcomeToken = NewType("OmenOutcomeToken", int)
30
+ OmenOutcomeToken = NewType("OmenOutcomeToken", Wei)
31
31
  OutcomeStr = NewType("OutcomeStr", str)
32
32
  Probability = NewType("Probability", float)
33
33
  Mana = NewType("Mana", float) # Manifold's "currency"
File without changes
@@ -0,0 +1,45 @@
1
+ import typing as t
2
+
3
+ from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
4
+ from prediction_market_agent_tooling.jobs.omen.omen_jobs import OmenJobAgentMarket
5
+ from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
6
+ from prediction_market_agent_tooling.markets.markets import MarketType
7
+
8
+ JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET: dict[MarketType, type[JobAgentMarket]] = {
9
+ MarketType.OMEN: OmenJobAgentMarket,
10
+ }
11
+
12
+
13
+ @t.overload
14
+ def get_jobs(
15
+ market_type: t.Literal[MarketType.OMEN],
16
+ limit: int | None,
17
+ filter_by: FilterBy = FilterBy.OPEN,
18
+ sort_by: SortBy = SortBy.NONE,
19
+ ) -> t.Sequence[OmenJobAgentMarket]:
20
+ ...
21
+
22
+
23
+ @t.overload
24
+ def get_jobs(
25
+ market_type: MarketType,
26
+ limit: int | None,
27
+ filter_by: FilterBy = FilterBy.OPEN,
28
+ sort_by: SortBy = SortBy.NONE,
29
+ ) -> t.Sequence[JobAgentMarket]:
30
+ ...
31
+
32
+
33
+ def get_jobs(
34
+ market_type: MarketType,
35
+ limit: int | None,
36
+ filter_by: FilterBy = FilterBy.OPEN,
37
+ sort_by: SortBy = SortBy.NONE,
38
+ ) -> t.Sequence[JobAgentMarket]:
39
+ job_class = JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET[market_type]
40
+ markets = job_class.get_jobs(
41
+ limit=limit,
42
+ sort_by=sort_by,
43
+ filter_by=filter_by,
44
+ )
45
+ return markets
@@ -0,0 +1,53 @@
1
+ import typing as t
2
+ from abc import ABC, abstractmethod
3
+ from datetime import datetime
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from prediction_market_agent_tooling.markets.agent_market import AgentMarket
8
+ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
9
+ FilterBy,
10
+ SortBy,
11
+ )
12
+
13
+
14
+ class SimpleJob(BaseModel):
15
+ id: str
16
+ job: str
17
+ reward: float
18
+ currency: str
19
+ deadline: datetime
20
+
21
+
22
+ class JobAgentMarket(AgentMarket, ABC):
23
+ CATEGORY: t.ClassVar[str]
24
+
25
+ @property
26
+ @abstractmethod
27
+ def job(self) -> str:
28
+ """Holds description of the job that needs to be done."""
29
+
30
+ @property
31
+ @abstractmethod
32
+ def deadline(self) -> datetime:
33
+ """Deadline for the job completion."""
34
+
35
+ @abstractmethod
36
+ def get_reward(self, max_bond: float) -> float:
37
+ """Reward for completing this job."""
38
+
39
+ @abstractmethod
40
+ @classmethod
41
+ def get_jobs(
42
+ cls, limit: int | None, filter_by: FilterBy, sort_by: SortBy
43
+ ) -> t.Sequence["JobAgentMarket"]:
44
+ """Get all available jobs."""
45
+
46
+ def to_simple_job(self, max_bond: float) -> SimpleJob:
47
+ return SimpleJob(
48
+ id=self.id,
49
+ job=self.job,
50
+ reward=self.get_reward(max_bond),
51
+ currency=self.currency.value,
52
+ deadline=self.deadline,
53
+ )
@@ -0,0 +1,113 @@
1
+ import typing as t
2
+ from datetime import datetime
3
+
4
+ from web3 import Web3
5
+
6
+ from prediction_market_agent_tooling.deploy.betting_strategy import (
7
+ Currency,
8
+ KellyBettingStrategy,
9
+ ProbabilisticAnswer,
10
+ TradeType,
11
+ )
12
+ from prediction_market_agent_tooling.gtypes import Probability
13
+ from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
14
+ from prediction_market_agent_tooling.markets.omen.omen import (
15
+ BetAmount,
16
+ OmenAgentMarket,
17
+ OmenMarket,
18
+ )
19
+ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
20
+ FilterBy,
21
+ OmenSubgraphHandler,
22
+ SortBy,
23
+ )
24
+
25
+
26
+ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
27
+ CATEGORY = "jobs"
28
+
29
+ @property
30
+ def job(self) -> str:
31
+ """Omen market's have only question, so that's where the job description is."""
32
+ return self.question
33
+
34
+ @property
35
+ def deadline(self) -> datetime:
36
+ return self.close_time
37
+
38
+ def get_reward(self, max_bond: float) -> float:
39
+ return compute_job_reward(self, max_bond)
40
+
41
+ @classmethod
42
+ def get_jobs(
43
+ cls, limit: int | None, filter_by: FilterBy, sort_by: SortBy
44
+ ) -> t.Sequence["OmenJobAgentMarket"]:
45
+ markets = OmenSubgraphHandler().get_omen_binary_markets_simple(
46
+ limit=limit,
47
+ filter_by=filter_by,
48
+ sort_by=sort_by,
49
+ category=cls.CATEGORY,
50
+ )
51
+ return [OmenJobAgentMarket.from_omen_market(market) for market in markets]
52
+
53
+ @staticmethod
54
+ def from_omen_market(market: OmenMarket) -> "OmenJobAgentMarket":
55
+ return OmenJobAgentMarket.from_omen_agent_market(
56
+ OmenAgentMarket.from_data_model(market)
57
+ )
58
+
59
+ @staticmethod
60
+ def from_omen_agent_market(market: OmenAgentMarket) -> "OmenJobAgentMarket":
61
+ return OmenJobAgentMarket(
62
+ id=market.id,
63
+ question=market.question,
64
+ description=market.description,
65
+ outcomes=market.outcomes,
66
+ outcome_token_pool=market.outcome_token_pool,
67
+ resolution=market.resolution,
68
+ created_time=market.created_time,
69
+ close_time=market.close_time,
70
+ current_p_yes=market.current_p_yes,
71
+ url=market.url,
72
+ volume=market.volume,
73
+ creator=market.creator,
74
+ collateral_token_contract_address_checksummed=market.collateral_token_contract_address_checksummed,
75
+ market_maker_contract_address_checksummed=market.market_maker_contract_address_checksummed,
76
+ condition=market.condition,
77
+ finalized_time=market.finalized_time,
78
+ fee=market.fee,
79
+ )
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
+ required_trades = KellyBettingStrategy(max_bet_amount=max_bond).calculate_trades(
87
+ existing_position=None,
88
+ # We assume that we finish the job and so the probability of the market happening will be 100%.
89
+ answer=ProbabilisticAnswer(p_yes=Probability(1.0), confidence=1.0),
90
+ market=market,
91
+ )
92
+
93
+ assert (
94
+ len(required_trades) == 1
95
+ ), f"Shouldn't process same job twice: {required_trades}"
96
+ trade = required_trades[0]
97
+ assert trade.trade_type == TradeType.BUY, "Should only buy on job markets."
98
+ assert trade.outcome, "Should buy only YES on job markets."
99
+ assert (
100
+ trade.amount.currency == Currency.xDai
101
+ ), "Should work only on real-money markets."
102
+
103
+ reward = (
104
+ market.get_buy_token_amount(
105
+ bet_amount=BetAmount(
106
+ amount=trade.amount.amount, currency=trade.amount.currency
107
+ ),
108
+ direction=trade.outcome,
109
+ ).amount
110
+ - trade.amount.amount
111
+ )
112
+
113
+ return reward
@@ -9,6 +9,7 @@ from prediction_market_agent_tooling.gtypes import (
9
9
  ChecksumAddress,
10
10
  HexAddress,
11
11
  HexBytes,
12
+ HexStr,
12
13
  OmenOutcomeToken,
13
14
  Probability,
14
15
  Wei,
@@ -30,8 +31,10 @@ from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
30
31
 
31
32
  OMEN_TRUE_OUTCOME = "Yes"
32
33
  OMEN_FALSE_OUTCOME = "No"
34
+ OMEN_BINARY_MARKET_OUTCOMES = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME]
33
35
  INVALID_ANSWER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
34
36
  INVALID_ANSWER_HEX_BYTES = HexBytes(INVALID_ANSWER)
37
+ INVALID_ANSWER_STR = HexStr(INVALID_ANSWER_HEX_BYTES.hex())
35
38
  OMEN_BASE_URL = "https://aiomen.eth.limo"
36
39
  PRESAGIO_BASE_URL = "https://presagio.pages.dev"
37
40
 
@@ -97,10 +97,6 @@ class OmenAgentMarket(AgentMarket):
97
97
  close_time: datetime
98
98
  fee: float # proportion, from 0 to 1
99
99
 
100
- INVALID_MARKET_ANSWER: HexStr = HexStr(
101
- "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
102
- )
103
-
104
100
  _binary_market_p_yes_history: list[Probability] | None = None
105
101
  description: str | None = (
106
102
  None # Omen markets don't have a description, so just default to None.
@@ -577,6 +573,66 @@ class OmenAgentMarket(AgentMarket):
577
573
  def get_user_url(cls, keys: APIKeys) -> str:
578
574
  return get_omen_user_url(keys.bet_from_address)
579
575
 
576
+ def get_buy_token_amount(
577
+ self, bet_amount: BetAmount, direction: bool
578
+ ) -> TokenAmount:
579
+ """
580
+ Note: this is only valid if the market instance's token pool is
581
+ up-to-date with the smart contract.
582
+ """
583
+ outcome_token_pool = check_not_none(self.outcome_token_pool)
584
+ amount = get_buy_outcome_token_amount(
585
+ investment_amount=bet_amount.amount,
586
+ buy_direction=direction,
587
+ yes_outcome_pool_size=outcome_token_pool[OMEN_TRUE_OUTCOME],
588
+ no_outcome_pool_size=outcome_token_pool[OMEN_FALSE_OUTCOME],
589
+ fee=self.fee,
590
+ )
591
+ return TokenAmount(amount=amount, currency=self.currency)
592
+
593
+ def _get_buy_token_amount_from_smart_contract(
594
+ self, bet_amount: BetAmount, direction: bool
595
+ ) -> TokenAmount:
596
+ received_token_amount_wei = Wei(
597
+ self.get_contract().calcBuyAmount(
598
+ investment_amount=xdai_to_wei(xDai(bet_amount.amount)),
599
+ outcome_index=self.get_outcome_index(
600
+ self.get_outcome_str_from_bool(direction)
601
+ ),
602
+ )
603
+ )
604
+ received_token_amount = float(wei_to_xdai(received_token_amount_wei))
605
+ return TokenAmount(amount=received_token_amount, currency=self.currency)
606
+
607
+ def get_new_p_yes(self, bet_amount: BetAmount, direction: bool) -> Probability:
608
+ """
609
+ Calculate the new p_yes based on the bet amount and direction.
610
+ """
611
+ if not self.has_token_pool():
612
+ raise ValueError("Outcome token pool is required to calculate new p_yes.")
613
+
614
+ outcome_token_pool = check_not_none(self.outcome_token_pool)
615
+ yes_outcome_pool_size = outcome_token_pool[self.get_outcome_str_from_bool(True)]
616
+ no_outcome_pool_size = outcome_token_pool[self.get_outcome_str_from_bool(False)]
617
+
618
+ new_yes_outcome_pool_size = yes_outcome_pool_size + (
619
+ bet_amount.amount * (1 - self.fee)
620
+ )
621
+ new_no_outcome_pool_size = no_outcome_pool_size + (
622
+ bet_amount.amount * (1 - self.fee)
623
+ )
624
+
625
+ received_token_amount = self.get_buy_token_amount(bet_amount, direction).amount
626
+ if direction:
627
+ new_yes_outcome_pool_size -= received_token_amount
628
+ else:
629
+ new_no_outcome_pool_size -= received_token_amount
630
+
631
+ new_p_yes = new_no_outcome_pool_size / (
632
+ new_yes_outcome_pool_size + new_no_outcome_pool_size
633
+ )
634
+ return Probability(new_p_yes)
635
+
580
636
 
581
637
  def get_omen_user_url(address: ChecksumAddress) -> str:
582
638
  return f"https://gnosisscan.io/address/{address}"
@@ -876,17 +932,20 @@ def omen_fund_market_tx(
876
932
  market_contract = market.get_contract()
877
933
  collateral_token_contract = market_contract.get_collateral_token_contract()
878
934
 
879
- if auto_deposit:
880
- auto_deposit_collateral_token(collateral_token_contract, funds, api_keys, web3)
935
+ amount_to_fund = collateral_token_contract.get_in_shares(funds, web3)
881
936
 
882
937
  collateral_token_contract.approve(
883
938
  api_keys=api_keys,
884
939
  for_address=market_contract.address,
885
- amount_wei=funds,
940
+ amount_wei=amount_to_fund,
886
941
  web3=web3,
887
942
  )
888
943
 
889
- market_contract.addFunding(api_keys, funds, web3=web3)
944
+ if auto_deposit:
945
+ # In auto-depositing, we need to deposit the original `funds`, e.g. we can deposit 2 xDai, but receive 1.8 sDai, so for the funding we will use `amount_to_fund`.
946
+ auto_deposit_collateral_token(collateral_token_contract, funds, api_keys, web3)
947
+
948
+ market_contract.addFunding(api_keys, amount_to_fund, web3=web3)
890
949
 
891
950
 
892
951
  def build_parent_collection_id() -> HexStr:
@@ -1162,3 +1221,33 @@ def withdraw_wxdai_to_xdai_to_keep_balance(
1162
1221
  logger.info(
1163
1222
  f"Withdrew {need_to_withdraw} wxDai to keep the balance above the minimum required balance {min_required_balance}."
1164
1223
  )
1224
+
1225
+
1226
+ def get_buy_outcome_token_amount(
1227
+ investment_amount: float,
1228
+ buy_direction: bool,
1229
+ yes_outcome_pool_size: float,
1230
+ no_outcome_pool_size: float,
1231
+ fee: float,
1232
+ ) -> float:
1233
+ """
1234
+ Calculates the amount of outcome tokens received for a given investment
1235
+
1236
+ Taken from https://github.com/gnosis/conditional-tokens-market-makers/blob/6814c0247c745680bb13298d4f0dd7f5b574d0db/contracts/FixedProductMarketMaker.sol#L264
1237
+ """
1238
+ investment_amount_minus_fees = investment_amount * (1 - fee)
1239
+ buy_token_pool_balance = (
1240
+ yes_outcome_pool_size if buy_direction else no_outcome_pool_size
1241
+ )
1242
+
1243
+ pool_balance = no_outcome_pool_size if buy_direction else yes_outcome_pool_size
1244
+ denominator = pool_balance + investment_amount_minus_fees
1245
+ ending_outcome_balance = buy_token_pool_balance * pool_balance / denominator
1246
+
1247
+ if ending_outcome_balance <= 0:
1248
+ raise ValueError("must have non-zero balances")
1249
+
1250
+ result = (
1251
+ buy_token_pool_balance + investment_amount_minus_fees - ending_outcome_balance
1252
+ )
1253
+ return result
@@ -19,8 +19,7 @@ from prediction_market_agent_tooling.gtypes import (
19
19
  from prediction_market_agent_tooling.loggers import logger
20
20
  from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy
21
21
  from prediction_market_agent_tooling.markets.omen.data_models import (
22
- OMEN_FALSE_OUTCOME,
23
- OMEN_TRUE_OUTCOME,
22
+ OMEN_BINARY_MARKET_OUTCOMES,
24
23
  OmenBet,
25
24
  OmenMarket,
26
25
  OmenPosition,
@@ -204,7 +203,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
204
203
  self,
205
204
  creator: t.Optional[HexAddress] = None,
206
205
  creator_in: t.Optional[t.Sequence[HexAddress]] = None,
207
- outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
206
+ outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
208
207
  created_after: t.Optional[datetime] = None,
209
208
  opened_before: t.Optional[datetime] = None,
210
209
  opened_after: t.Optional[datetime] = None,
@@ -217,6 +216,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
217
216
  id_in: list[str] | None = None,
218
217
  excluded_questions: set[str] | None = None,
219
218
  collateral_token_address_in: tuple[ChecksumAddress, ...] | None = None,
219
+ category: str | None = None,
220
220
  ) -> dict[str, t.Any]:
221
221
  where_stms: dict[str, t.Any] = {
222
222
  "isPendingArbitration": False,
@@ -282,6 +282,9 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
282
282
  finalized_after
283
283
  )
284
284
 
285
+ if category:
286
+ where_stms["category"] = category
287
+
285
288
  # `excluded_question_titles` can not be an empty list, otherwise the API bugs out and returns nothing.
286
289
  excluded_question_titles = [""]
287
290
  if excluded_questions:
@@ -324,8 +327,10 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
324
327
  # Additional filters, these can not be modified by the enums above.
325
328
  created_after: datetime | None = None,
326
329
  excluded_questions: set[str] | None = None, # question titles
327
- collateral_token_address_in: tuple[ChecksumAddress, ...]
328
- | None = SAFE_COLLATERAL_TOKEN_MARKETS,
330
+ collateral_token_address_in: (
331
+ tuple[ChecksumAddress, ...] | None
332
+ ) = SAFE_COLLATERAL_TOKEN_MARKETS,
333
+ category: str | None = None,
329
334
  ) -> t.List[OmenMarket]:
330
335
  """
331
336
  Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
@@ -363,6 +368,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
363
368
  created_after=created_after,
364
369
  excluded_questions=excluded_questions,
365
370
  collateral_token_address_in=collateral_token_address_in,
371
+ category=category,
366
372
  )
367
373
 
368
374
  def get_omen_binary_markets(
@@ -383,9 +389,11 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
383
389
  excluded_questions: set[str] | None = None, # question titles
384
390
  sort_by_field: FieldPath | None = None,
385
391
  sort_direction: str | None = None,
386
- outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
387
- collateral_token_address_in: tuple[ChecksumAddress, ...]
388
- | None = SAFE_COLLATERAL_TOKEN_MARKETS,
392
+ outcomes: list[str] = OMEN_BINARY_MARKET_OUTCOMES,
393
+ collateral_token_address_in: (
394
+ tuple[ChecksumAddress, ...] | None
395
+ ) = SAFE_COLLATERAL_TOKEN_MARKETS,
396
+ category: str | None = None,
389
397
  ) -> t.List[OmenMarket]:
390
398
  """
391
399
  Complete method to fetch Omen binary markets with various filters, use `get_omen_binary_markets_simple` for simplified version that uses FilterBy and SortBy enums.
@@ -406,6 +414,7 @@ class OmenSubgraphHandler(metaclass=SingletonMeta):
406
414
  excluded_questions=excluded_questions,
407
415
  liquidity_bigger_than=liquidity_bigger_than,
408
416
  collateral_token_address_in=collateral_token_address_in,
417
+ category=category,
409
418
  )
410
419
 
411
420
  # These values can not be set to `None`, but they can be omitted.
@@ -211,7 +211,11 @@ def monitor_agent(agent: DeployedAgent) -> None:
211
211
  key=f"{agent.name}_x_axis_column",
212
212
  )
213
213
 
214
- bets_df = pd.DataFrame(bets_info).sort_values(by=x_axis_column, ascending=False)
214
+ bets_df = (
215
+ pd.DataFrame(bets_info)
216
+ .sort_values(by=x_axis_column, ascending=False)
217
+ .reset_index(drop=True)
218
+ )
215
219
  bets_df["x-axis-day"] = bets_df[x_axis_column].dt.date
216
220
 
217
221
  # Metrics
@@ -1,4 +1,4 @@
1
- from pydantic import BaseModel
1
+ from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
2
2
 
3
3
 
4
4
  def check_is_valid_probability(probability: float) -> None:
@@ -6,17 +6,12 @@ def check_is_valid_probability(probability: float) -> None:
6
6
  raise ValueError("Probability must be between 0 and 1")
7
7
 
8
8
 
9
- class KellyBet(BaseModel):
10
- direction: bool
11
- size: float
12
-
13
-
14
- def get_kelly_bet(
9
+ def get_kelly_bet_simplified(
15
10
  max_bet: float,
16
11
  market_p_yes: float,
17
12
  estimated_p_yes: float,
18
13
  confidence: float,
19
- ) -> KellyBet:
14
+ ) -> SimpleBet:
20
15
  """
21
16
  Calculate the optimal bet amount using the Kelly Criterion for a binary outcome market.
22
17
 
@@ -57,4 +52,91 @@ def get_kelly_bet(
57
52
  # Ensure bet size is non-negative does not exceed the wallet balance
58
53
  bet_size = min(kelly_fraction * max_bet, max_bet)
59
54
 
60
- return KellyBet(direction=bet_direction, size=bet_size)
55
+ return SimpleBet(direction=bet_direction, size=bet_size)
56
+
57
+
58
+ def get_kelly_bet_full(
59
+ yes_outcome_pool_size: float,
60
+ no_outcome_pool_size: float,
61
+ estimated_p_yes: float,
62
+ confidence: float,
63
+ max_bet: float,
64
+ fee: float = 0.0, # proportion, 0 to 1
65
+ ) -> SimpleBet:
66
+ """
67
+ Calculate the optimal bet amount using the Kelly Criterion for a binary outcome market.
68
+
69
+ 'Full' as in it accounts for how the bet changes the market odds.
70
+
71
+ Taken from https://github.com/valory-xyz/trader/blob/main/strategies/kelly_criterion/kelly_criterion.py
72
+
73
+ with derivation in PR description: https://github.com/valory-xyz/trader/pull/119
74
+
75
+ ```
76
+ Licensed under the Apache License, Version 2.0 (the "License");
77
+ you may not use this file except in compliance with the License.
78
+ You may obtain a copy of the License at
79
+
80
+ http://www.apache.org/licenses/LICENSE-2.0
81
+
82
+ Unless required by applicable law or agreed to in writing, software
83
+ distributed under the License is distributed on an "AS IS" BASIS,
84
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
85
+ See the License for the specific language governing permissions and
86
+ limitations under the License.
87
+ ```
88
+ """
89
+ check_is_valid_probability(estimated_p_yes)
90
+ check_is_valid_probability(confidence)
91
+ check_is_valid_probability(fee)
92
+
93
+ if max_bet == 0:
94
+ return SimpleBet(direction=True, size=0)
95
+
96
+ x = yes_outcome_pool_size
97
+ y = no_outcome_pool_size
98
+ p = estimated_p_yes
99
+ c = confidence
100
+ b = max_bet
101
+ f = 1 - fee
102
+
103
+ if x == y:
104
+ # Add a delta to prevent division by zero
105
+ y += 1e-10
106
+
107
+ numerator = (
108
+ -4 * x**2 * y
109
+ + b * y**2 * p * c * f
110
+ + 2 * b * x * y * p * c * f
111
+ + b * x**2 * p * c * f
112
+ - 2 * b * y**2 * f
113
+ - 2 * b * x * y * f
114
+ + (
115
+ (
116
+ 4 * x**2 * y
117
+ - b * y**2 * p * c * f
118
+ - 2 * b * x * y * p * c * f
119
+ - b * x**2 * p * c * f
120
+ + 2 * b * y**2 * f
121
+ + 2 * b * x * y * f
122
+ )
123
+ ** 2
124
+ - (
125
+ 4
126
+ * (x**2 * f - y**2 * f)
127
+ * (
128
+ -4 * b * x * y**2 * p * c
129
+ - 4 * b * x**2 * y * p * c
130
+ + 4 * b * x * y**2
131
+ )
132
+ )
133
+ )
134
+ ** (1 / 2)
135
+ )
136
+ denominator = 2 * (x**2 * f - y**2 * f)
137
+ kelly_bet_amount = numerator / denominator
138
+
139
+ # Clip the bet size to max_bet to account for rounding errors.
140
+ return SimpleBet(
141
+ direction=kelly_bet_amount > 0, size=min(max_bet, abs(kelly_bet_amount))
142
+ )
@@ -1,29 +1,22 @@
1
- import typing as t
2
1
  from functools import reduce
3
2
 
4
3
  import numpy as np
5
4
 
6
- from prediction_market_agent_tooling.gtypes import Probability, wei_type, xDai
7
- from prediction_market_agent_tooling.loggers import logger
8
- from prediction_market_agent_tooling.markets.omen.data_models import OmenMarket
5
+ from prediction_market_agent_tooling.gtypes import Probability, Wei, xDai
9
6
  from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
7
+ from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
10
8
  from prediction_market_agent_tooling.tools.utils import check_not_none
11
- from prediction_market_agent_tooling.tools.web3_utils import (
12
- ONE_XDAI,
13
- wei_to_xdai,
14
- xdai_to_wei,
15
- )
16
-
17
- OutcomeIndex = t.Literal[0, 1]
9
+ from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai, xdai_to_wei
18
10
 
19
11
 
20
12
  def get_market_moving_bet(
21
- market: OmenMarket,
22
- target_p_yes: Probability,
13
+ yes_outcome_pool_size: float,
14
+ no_outcome_pool_size: float,
15
+ market_p_yes: float,
16
+ target_p_yes: float,
17
+ fee: float = 0.0, # proportion, 0 to 1
23
18
  max_iters: int = 100,
24
- check_vs_contract: bool = False, # Disable by default, as it's slow
25
- verbose: bool = False,
26
- ) -> t.Tuple[xDai, OutcomeIndex]:
19
+ ) -> SimpleBet:
27
20
  """
28
21
  Implements a binary search to determine the bet that will move the market's
29
22
  `p_yes` to that of the target.
@@ -43,74 +36,97 @@ def get_market_moving_bet(
43
36
  new_product - fixed_product = dx * na_y
44
37
  dx = (new_product - fixed_product) / na_y
45
38
  """
46
- market_agent = OmenAgentMarket.from_data_model(market)
47
- amounts = market.outcomeTokenAmounts
48
- prices = check_not_none(
49
- market.outcomeTokenProbabilities, "No probabilities, is marked closed?"
50
- )
51
- if len(amounts) != 2 or len(prices) != 2:
52
- raise ValueError("Only binary markets are supported.")
53
-
54
- fixed_product = reduce(lambda x, y: x * y, amounts, 1)
55
- assert np.isclose(float(sum(prices)), 1)
39
+ fixed_product = yes_outcome_pool_size * no_outcome_pool_size
40
+ bet_direction: bool = target_p_yes > market_p_yes
56
41
 
57
- # For FPMMs, the probability is equal to the marginal price
58
- current_p_yes = Probability(prices[0])
59
- bet_outcome_index: OutcomeIndex = 0 if target_p_yes > current_p_yes else 1
60
-
61
- min_bet_amount = 0
62
- max_bet_amount = 100 * sum(amounts) # TODO set a better upper bound
42
+ min_bet_amount = 0.0
43
+ max_bet_amount = 100 * (
44
+ yes_outcome_pool_size + no_outcome_pool_size
45
+ ) # TODO set a better upper bound
63
46
 
64
47
  # Binary search for the optimal bet amount
65
48
  for _ in range(max_iters):
66
- bet_amount = (min_bet_amount + max_bet_amount) // 2
67
- bet_amount_ = (
68
- bet_amount
69
- * (
70
- xdai_to_wei(ONE_XDAI)
71
- - check_not_none(market.fee, "No fee for the market.")
72
- )
73
- / xdai_to_wei(ONE_XDAI)
74
- )
49
+ bet_amount = (min_bet_amount + max_bet_amount) / 2
50
+ amounts_diff = bet_amount * (1 - fee)
75
51
 
76
52
  # Initial new amounts are old amounts + equal new amounts for each outcome
77
- amounts_diff = bet_amount_
78
- new_amounts = [amounts[i] + amounts_diff for i in range(len(amounts))]
53
+ yes_outcome_new_pool_size = yes_outcome_pool_size + amounts_diff
54
+ no_outcome_new_pool_size = no_outcome_pool_size + amounts_diff
55
+ new_amounts = {
56
+ True: yes_outcome_new_pool_size,
57
+ False: no_outcome_new_pool_size,
58
+ }
79
59
 
80
60
  # Now give away tokens at `bet_outcome_index` to restore invariant
81
- new_product = reduce(lambda x, y: x * y, new_amounts, 1.0)
82
- dx = (new_product - fixed_product) / new_amounts[1 - bet_outcome_index]
83
-
84
- # Sanity check the number of tokens against the contract
85
- if check_vs_contract:
86
- expected_trade = market_agent.get_contract().calcBuyAmount(
87
- investment_amount=wei_type(bet_amount),
88
- outcome_index=bet_outcome_index,
89
- )
90
- assert np.isclose(float(expected_trade), dx)
91
-
92
- new_amounts[bet_outcome_index] -= dx
61
+ new_product = yes_outcome_new_pool_size * no_outcome_new_pool_size
62
+ dx = (new_product - fixed_product) / new_amounts[not bet_direction]
63
+ new_amounts[bet_direction] -= dx
64
+
93
65
  # Check that the invariant is restored
94
66
  assert np.isclose(
95
- reduce(lambda x, y: x * y, new_amounts, 1.0), float(fixed_product)
67
+ reduce(lambda x, y: x * y, list(new_amounts.values()), 1.0),
68
+ float(fixed_product),
96
69
  )
97
- new_p_yes = Probability(new_amounts[1] / sum(new_amounts))
98
- bet_amount_wei = wei_type(bet_amount)
99
- if verbose:
100
- outcome = market_agent.get_outcome_str(bet_outcome_index)
101
- logger.debug(
102
- f"Target p_yes: {target_p_yes:.2f}, bet: {wei_to_xdai(bet_amount_wei):.2f}{market_agent.currency} for {outcome}, new p_yes: {new_p_yes:.2f}"
103
- )
104
- if abs(target_p_yes - new_p_yes) < 0.01:
70
+
71
+ new_p_yes = Probability(new_amounts[False] / sum(list(new_amounts.values())))
72
+ if abs(target_p_yes - new_p_yes) < 1e-6:
105
73
  break
106
74
  elif new_p_yes > target_p_yes:
107
- if bet_outcome_index == 0:
75
+ if bet_direction:
108
76
  max_bet_amount = bet_amount
109
77
  else:
110
78
  min_bet_amount = bet_amount
111
79
  else:
112
- if bet_outcome_index == 0:
80
+ if bet_direction:
113
81
  min_bet_amount = bet_amount
114
82
  else:
115
83
  max_bet_amount = bet_amount
116
- return wei_to_xdai(bet_amount_wei), bet_outcome_index
84
+
85
+ return SimpleBet(direction=bet_direction, size=bet_amount)
86
+
87
+
88
+ def _sanity_check_omen_market_moving_bet(
89
+ bet_to_check: SimpleBet, market: OmenAgentMarket, target_p_yes: float
90
+ ) -> None:
91
+ """
92
+ A util function for checking that a bet moves the market to the target_p_yes
93
+ by calling the market's calcBuyAmount method from the smart contract, and
94
+ using the adjusted outcome pool sizes to calculate the new p_yes.
95
+ """
96
+ buy_amount_ = market.get_contract().calcBuyAmount(
97
+ investment_amount=xdai_to_wei(xDai(bet_to_check.size)),
98
+ outcome_index=market.get_outcome_index(
99
+ market.get_outcome_str_from_bool(bet_to_check.direction)
100
+ ),
101
+ )
102
+ buy_amount = float(wei_to_xdai(Wei(buy_amount_)))
103
+
104
+ outcome_token_pool = check_not_none(market.outcome_token_pool)
105
+ yes_outcome_pool_size = outcome_token_pool[market.get_outcome_str_from_bool(True)]
106
+ no_outcome_pool_size = outcome_token_pool[market.get_outcome_str_from_bool(False)]
107
+ market_const = yes_outcome_pool_size * no_outcome_pool_size
108
+
109
+ # When you buy 'yes' tokens, you add your bet size to the both pools, then
110
+ # subtract `buy_amount` from the 'yes' pool. And vice versa for 'no' tokens.
111
+ new_yes_outcome_pool_size = (
112
+ yes_outcome_pool_size
113
+ + (bet_to_check.size * (1 - market.fee))
114
+ - float(bet_to_check.direction) * buy_amount
115
+ )
116
+ new_no_outcome_pool_size = (
117
+ no_outcome_pool_size
118
+ + (bet_to_check.size * (1 - market.fee))
119
+ - float(not bet_to_check.direction) * buy_amount
120
+ )
121
+ new_market_const = new_yes_outcome_pool_size * new_no_outcome_pool_size
122
+ # Check the invariant is restored
123
+ assert np.isclose(new_market_const, market_const)
124
+
125
+ # Now check that the market's new p_yes is equal to the target_p_yes
126
+ new_p_yes = new_no_outcome_pool_size / (
127
+ new_yes_outcome_pool_size + new_no_outcome_pool_size
128
+ )
129
+ if not np.isclose(new_p_yes, target_p_yes, atol=0.01):
130
+ raise ValueError(
131
+ f"Bet does not move market to target_p_yes {target_p_yes=}. Got {new_p_yes=}"
132
+ )
@@ -0,0 +1,6 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class SimpleBet(BaseModel):
5
+ direction: bool
6
+ size: float
@@ -1,3 +1,4 @@
1
+ import tenacity
1
2
  from loguru import logger
2
3
 
3
4
  from prediction_market_agent_tooling.config import APIKeys
@@ -76,6 +77,7 @@ Finally, write your final decision, write `decision: ` followed by either "yes i
76
77
 
77
78
 
78
79
  @persistent_inmemory_cache
80
+ @tenacity.retry(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1))
79
81
  @observe()
80
82
  def is_predictable_binary(
81
83
  question: str,
@@ -111,6 +113,7 @@ def is_predictable_binary(
111
113
 
112
114
 
113
115
  @persistent_inmemory_cache
116
+ @tenacity.retry(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1))
114
117
  @observe()
115
118
  def is_predictable_without_description(
116
119
  question: str,
@@ -0,0 +1,159 @@
1
+ import typing as t
2
+ from datetime import datetime
3
+
4
+ import numpy as np
5
+ from langfuse import Langfuse
6
+ from langfuse.client import TraceWithDetails
7
+ from pydantic import BaseModel
8
+
9
+ from prediction_market_agent_tooling.markets.data_models import (
10
+ ProbabilisticAnswer,
11
+ ResolvedBet,
12
+ Trade,
13
+ TradeType,
14
+ )
15
+ from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
16
+ from prediction_market_agent_tooling.tools.utils import add_utc_timezone_validator
17
+
18
+
19
+ class ProcessMarketTrace(BaseModel):
20
+ timestamp: datetime
21
+ market: OmenAgentMarket
22
+ answer: ProbabilisticAnswer
23
+ trades: list[Trade]
24
+
25
+ @property
26
+ def buy_trade(self) -> Trade:
27
+ buy_trades = [t for t in self.trades if t.trade_type == TradeType.BUY]
28
+ if len(buy_trades) == 1:
29
+ return buy_trades[0]
30
+ raise ValueError("No buy trade found")
31
+
32
+ @staticmethod
33
+ def from_langfuse_trace(
34
+ trace: TraceWithDetails,
35
+ ) -> t.Optional["ProcessMarketTrace"]:
36
+ market = trace_to_omen_agent_market(trace)
37
+ answer = trace_to_answer(trace)
38
+ trades = trace_to_trades(trace)
39
+
40
+ if not market or not answer or not trades:
41
+ return None
42
+
43
+ return ProcessMarketTrace(
44
+ market=market,
45
+ answer=answer,
46
+ trades=trades,
47
+ timestamp=trace.timestamp,
48
+ )
49
+
50
+
51
+ class ResolvedBetWithTrace(BaseModel):
52
+ bet: ResolvedBet
53
+ trace: ProcessMarketTrace
54
+
55
+
56
+ def get_traces_for_agent(
57
+ agent_name: str,
58
+ trace_name: str,
59
+ from_timestamp: datetime,
60
+ has_output: bool,
61
+ client: Langfuse,
62
+ ) -> list[TraceWithDetails]:
63
+ """
64
+ Fetch agent traces using pagination
65
+ """
66
+ page = 1 # index starts from 1
67
+ all_agent_traces = []
68
+ while True:
69
+ traces = client.fetch_traces(
70
+ name=trace_name,
71
+ limit=100,
72
+ page=page,
73
+ from_timestamp=from_timestamp,
74
+ )
75
+ if not traces.data:
76
+ break
77
+ page += 1
78
+
79
+ agent_traces = [
80
+ t
81
+ for t in traces.data
82
+ if t.session_id is not None and agent_name in t.session_id
83
+ ]
84
+ if has_output:
85
+ agent_traces = [t for t in agent_traces if t.output is not None]
86
+ all_agent_traces.extend(agent_traces)
87
+ return all_agent_traces
88
+
89
+
90
+ def trace_to_omen_agent_market(trace: TraceWithDetails) -> OmenAgentMarket | None:
91
+ if not trace.input:
92
+ return None
93
+ if not trace.input["args"]:
94
+ return None
95
+ assert len(trace.input["args"]) == 2 and trace.input["args"][0] == "omen"
96
+ try:
97
+ # If the market model is invalid (e.g. outdated), it will raise an exception
98
+ market = OmenAgentMarket.model_validate(trace.input["args"][1])
99
+ return market
100
+ except Exception:
101
+ return None
102
+
103
+
104
+ def trace_to_answer(trace: TraceWithDetails) -> ProbabilisticAnswer:
105
+ assert trace.output is not None, "Trace output is None"
106
+ assert trace.output["answer"] is not None, "Trace output result is None"
107
+ return ProbabilisticAnswer.model_validate(trace.output["answer"])
108
+
109
+
110
+ def trace_to_trades(trace: TraceWithDetails) -> list[Trade]:
111
+ assert trace.output is not None, "Trace output is None"
112
+ assert trace.output["trades"] is not None, "Trace output trades is None"
113
+ return [Trade.model_validate(t) for t in trace.output["trades"]]
114
+
115
+
116
+ def get_closest_datetime_from_list(
117
+ ref_datetime: datetime, datetimes: list[datetime]
118
+ ) -> int:
119
+ """Get the index of the closest datetime to the reference datetime"""
120
+ if len(datetimes) == 1:
121
+ return 0
122
+
123
+ closest_datetime = min(datetimes, key=lambda dt: abs(dt - ref_datetime))
124
+ return datetimes.index(closest_datetime)
125
+
126
+
127
+ def get_trace_for_bet(
128
+ bet: ResolvedBet, traces: list[ProcessMarketTrace]
129
+ ) -> ProcessMarketTrace | None:
130
+ # Filter for traces with the same market id
131
+ traces = [t for t in traces if t.market.id == bet.market_id]
132
+
133
+ # Filter for traces with the same bet outcome and amount
134
+ traces_for_bet: list[ProcessMarketTrace] = []
135
+ for t in traces:
136
+ # Cannot use exact comparison due to gas fees
137
+ if t.buy_trade.outcome == bet.outcome and np.isclose(
138
+ t.buy_trade.amount.amount, bet.amount.amount
139
+ ):
140
+ traces_for_bet.append(t)
141
+
142
+ if not traces_for_bet:
143
+ return None
144
+ elif len(traces_for_bet) == 1:
145
+ return traces_for_bet[0]
146
+ else:
147
+ # In-case there are multiple traces for the same market, get the closest
148
+ # trace to the bet
149
+ closest_trace_index = get_closest_datetime_from_list(
150
+ add_utc_timezone_validator(bet.created_time),
151
+ [t.timestamp for t in traces_for_bet],
152
+ )
153
+ # Sanity check - the trace should be after the bet
154
+ if traces_for_bet[closest_trace_index].timestamp < add_utc_timezone_validator(
155
+ bet.created_time
156
+ ):
157
+ return None
158
+
159
+ return traces_for_bet[closest_trace_index]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.48.13
3
+ Version: 0.48.15
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -15,14 +15,18 @@ prediction_market_agent_tooling/benchmark/agents.py,sha256=BwE3U11tQq0rfOJBn-Xn5
15
15
  prediction_market_agent_tooling/benchmark/benchmark.py,sha256=xiHKzZx5GHSsDerFHMZ9j_LXAXnSaITSvv67iPe3MEU,21095
16
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=vLAfzWXzgIdNft07gqHsGDOWQHb8jTM9jqKECAi44j0,18700
18
+ prediction_market_agent_tooling/deploy/agent.py,sha256=xT0L1BLU89t7lfBcbbqojcH_4H7ElnXKMQB2wjFxwos,19112
19
19
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
20
- prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=TX5xRaUz7JWQs92dPp8Y_cdRiJH8I9yvtaSMmNkty10,6905
20
+ prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=edatJ7temvl9TBTy05JjtFCAlaoVXCfLOQlYWJ61wvw,7636
21
21
  prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
22
22
  prediction_market_agent_tooling/deploy/gcp/deploy.py,sha256=CYUgnfy-9XVk04kkxA_5yp0GE9Mw5caYqlFUZQ2j3ks,3739
23
23
  prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py,sha256=qYIHRxQLac3yxtZ8ChikiPG9O1aUQucHW0muTSm1nto,2627
24
24
  prediction_market_agent_tooling/deploy/gcp/utils.py,sha256=oyW0jgrUT2Tr49c7GlpcMsYNQjoCSOcWis3q-MmVAhU,6089
25
- prediction_market_agent_tooling/gtypes.py,sha256=ezM2iAycTRJ0uHKK03s0z76a8YFSF438kjOwT_BAqz4,2474
25
+ prediction_market_agent_tooling/gtypes.py,sha256=O77co9-GWmHJo_NyBzRVkli5L1xqweI28JmsyaAHUHs,2474
26
+ prediction_market_agent_tooling/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ prediction_market_agent_tooling/jobs/jobs.py,sha256=I07yh0GJ-xhlvQaOUQB8xlSnihhcbU2c7DZ4ZND14c0,1246
28
+ prediction_market_agent_tooling/jobs/jobs_models.py,sha256=wlBhh-D8pUegBCTpzTfla6crGlNMSqqNX7yDqMcRLYI,1326
29
+ prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=EAzo63vhSt1P7ddoDubdmvgzFf-ieQRTDCRLzYkH3js,3922
26
30
  prediction_market_agent_tooling/loggers.py,sha256=JiBTgvb34O9dKHYKZyQ0UzojPUy6KSFQSTfbBIXopSY,3721
27
31
  prediction_market_agent_tooling/markets/agent_market.py,sha256=nVJO1MY7_l-YP1Q7-mGLtRgIHK8M-yt3Ht1yyQPwImM,10176
28
32
  prediction_market_agent_tooling/markets/categorize.py,sha256=jsoHWvZk9pU6n17oWSCcCxNNYVwlb_NXsZxKRI7vmsk,1301
@@ -37,11 +41,11 @@ prediction_market_agent_tooling/markets/metaculus/api.py,sha256=gvPQVAM5NlCyWzEM
37
41
  prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=6TBy17xntdLBR61QCE5wddwTa_k2D0D8ZgK6p7sGUuc,2448
38
42
  prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=1aaainHlMExDSp6nfKY31iATQsaJx1LdYp9p2PkQVAs,3453
39
43
  prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- prediction_market_agent_tooling/markets/omen/data_models.py,sha256=vCDr8FX3FA5LIQydormq8q1aU4GxMh2tQ8i59v_X134,16887
41
- prediction_market_agent_tooling/markets/omen/omen.py,sha256=BuDfmZBegHqqJCErFDstzbqzVDLVuFpoSbyYD4ZteSI,42972
44
+ prediction_market_agent_tooling/markets/omen/data_models.py,sha256=PQyoENpXkhu5TXZhbUdDvKLsb2sQt8rwryPTVQ9fIeM,17029
45
+ prediction_market_agent_tooling/markets/omen/omen.py,sha256=TGXthQITMoYvx1iK388nHbv8U0DQeX_jyHb-OEinvfg,46676
42
46
  prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=MfaWfDDfEzHYVAbeT3Dgtl8KG7XsqEpdY3m3-rsOPwo,23588
43
47
  prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=Awaw1r32IBAClCktrXbYJ24RNyLDWcLb8RqAx6FGSkI,9529
44
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=EwknHxU_EkK8H5-6fWW5IN5k6OAW5il4T2FhXrSKEJc,29984
48
+ prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=W3JIq3NDGoJuHZlPtv2Gv_EmCtYVwsgZwClSY-RloZw,30219
45
49
  prediction_market_agent_tooling/markets/polymarket/api.py,sha256=HXmA1akA0qDj0m3e-GEvWG8x75pm6BX4H7YJPQcST7I,4767
46
50
  prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=9CJzakyEcsn6DQBK2nOXjOMzTZBLAmK_KqevXvW17DI,4292
47
51
  prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=IPsFT3FX9Ge5l5zR1nBd2w-sd5ue7oR8PJSW710vFWY,12479
@@ -51,15 +55,16 @@ prediction_market_agent_tooling/monitor/markets/manifold.py,sha256=GdYpgRX1GahDi
51
55
  prediction_market_agent_tooling/monitor/markets/metaculus.py,sha256=S8zeDVN2aA6yvQykQNPb8GUGohczfJuCYt9Ds9ESCzs,1473
52
56
  prediction_market_agent_tooling/monitor/markets/omen.py,sha256=jOLPnIbDU9syjnYtHfOb2xa6-Ize3vbplgh-8WWkuT4,3323
53
57
  prediction_market_agent_tooling/monitor/markets/polymarket.py,sha256=I9z9aO1wncyGI3a09ihrw17JkeBKjAuMmC0I9pl_9o4,1781
54
- prediction_market_agent_tooling/monitor/monitor.py,sha256=snMSPmoHFV7QchqsFBaNkOEQhHJshF1qmIGPuID1giQ,14579
58
+ prediction_market_agent_tooling/monitor/monitor.py,sha256=tvYzHJg8Nau4vGWVTngjUAcM26LcH3Emss76351xl2o,14636
55
59
  prediction_market_agent_tooling/monitor/monitor_app.py,sha256=1e4LuzhAVjb7cPS6rGPZuZHMwMiNOeRhSxG8AVG-e0o,4839
56
60
  prediction_market_agent_tooling/monitor/monitor_settings.py,sha256=Xiozs3AsufuJ04JOe1vjUri-IAMWHjjmc2ugGGiHNH4,947
57
61
  prediction_market_agent_tooling/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
62
  prediction_market_agent_tooling/tools/balances.py,sha256=nR8_dSfbm3yTOOmMAwhGlurftEiNo1w1WIVzbskjdmM,837
59
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py,sha256=IoLx8fIOqjC9d2gSWIc5Y-3wlukib6cuJaJzguHqNbU,1835
60
- prediction_market_agent_tooling/tools/betting_strategies/market_moving.py,sha256=wtrHVQRuA0uDx06z0OxQLYbswuOpHQ1UyCWwLCrD_oM,4400
63
+ prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py,sha256=a_0VmjfQIVU2rrQjAcnWRl_WSYTqeq-7Wj35zUgpokY,4464
64
+ prediction_market_agent_tooling/tools/betting_strategies/market_moving.py,sha256=pCS0ISf6SgdhNx_t-h8_aHsLffnQ7kaLXVPPM7rGKJY,5163
61
65
  prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py,sha256=-FUSuQQgjcWSSnoFxnlAyTeilY6raJABJVM2QKkFqAY,438
62
66
  prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py,sha256=THMXwFlskvzbjnX_OiYtDSzI8XVFyULWfP2525_9UGc,429
67
+ prediction_market_agent_tooling/tools/betting_strategies/utils.py,sha256=kpIb-ci67Vc1Yqqaa-_S4OUkbhWSIYog4_Iwp69HU_k,97
63
68
  prediction_market_agent_tooling/tools/cache.py,sha256=tGHHd9HCiE_hCCtPtloHZQdDfBuiow9YsqJNYi2Tx_0,499
64
69
  prediction_market_agent_tooling/tools/contract.py,sha256=0XcT94HSiFMjca3f0WtoxzCGeCcj3Gcw-o0NGi6Fb3g,19627
65
70
  prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
@@ -68,8 +73,9 @@ prediction_market_agent_tooling/tools/google.py,sha256=SfVDxb3oEOUK8mpd0l3mTX9yb
68
73
  prediction_market_agent_tooling/tools/hexbytes_custom.py,sha256=Bp94qgPjvjWf1Vb4lNzGFDXRdThw1rJ91vL6r2PWq5E,2096
69
74
  prediction_market_agent_tooling/tools/image_gen/image_gen.py,sha256=HzRwBx62hOXBOmrtpkXaP9Qq1Ku03uUGdREocyjLQ_k,1266
70
75
  prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=8A3U2uxsCsOfLjru-6R_PPIAuiKY4qFkWp_GSBPV6-s,1280
71
- prediction_market_agent_tooling/tools/is_predictable.py,sha256=GqBgp4aHbY97PdprKfCHV-lJy2bbGIrd9yCtkqg6gEc,6558
76
+ prediction_market_agent_tooling/tools/is_predictable.py,sha256=QapzvJVgUZdhucgmxhzWAQ885BwSwvYUi0SG8mkLQMQ,6738
72
77
  prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_AeDVhRzLMaQLxH3kxDu3L8,1153
78
+ prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=EljGXIal3xApSzi5oaNJl4pRm66nayBWEpXR68SuVy0,4951
73
79
  prediction_market_agent_tooling/tools/parallelism.py,sha256=Rz8QdVUWX8KCbr8UZfaC_b1GBWIb3bXwITUumuvBJ60,1633
74
80
  prediction_market_agent_tooling/tools/safe.py,sha256=h0xOO0eNtitClf0fPkn-0oTc6A_bflDTee98V_aiV-A,5195
75
81
  prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
@@ -78,8 +84,8 @@ prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py,sha256=Uq2
78
84
  prediction_market_agent_tooling/tools/tavily_storage/tavily_storage.py,sha256=xrtQH9v5pXycBRyc5j45pWqkSffkoc9efNIU1_G633Q,3706
79
85
  prediction_market_agent_tooling/tools/utils.py,sha256=JE9YWtPPhnTgLiOyGAZDNG5K8nCwUY9IZEuAlm9UcxA,6611
80
86
  prediction_market_agent_tooling/tools/web3_utils.py,sha256=IZDxHhUJH5RsaRkK9DW6z1RYdk2cz5RqLMZG3T6Gv1U,11602
81
- prediction_market_agent_tooling-0.48.13.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
82
- prediction_market_agent_tooling-0.48.13.dist-info/METADATA,sha256=hzpD4sc9MsTT8zyAaLg8feXkawAQ25AnDorJfCuQBKI,7811
83
- prediction_market_agent_tooling-0.48.13.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
84
- prediction_market_agent_tooling-0.48.13.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
85
- prediction_market_agent_tooling-0.48.13.dist-info/RECORD,,
87
+ prediction_market_agent_tooling-0.48.15.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
88
+ prediction_market_agent_tooling-0.48.15.dist-info/METADATA,sha256=vp4AH8yoRuOMnzAizjbHTWMTVxKWnvRLNrJxLt-Utm0,7811
89
+ prediction_market_agent_tooling-0.48.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
90
+ prediction_market_agent_tooling-0.48.15.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
91
+ prediction_market_agent_tooling-0.48.15.dist-info/RECORD,,