prediction-market-agent-tooling 0.48.18__py3-none-any.whl → 0.49.1__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 (30) hide show
  1. prediction_market_agent_tooling/abis/debuggingcontract.abi.json +29 -0
  2. prediction_market_agent_tooling/abis/omen_agentresultmapping.abi.json +171 -0
  3. prediction_market_agent_tooling/benchmark/benchmark.py +0 -93
  4. prediction_market_agent_tooling/config.py +16 -0
  5. prediction_market_agent_tooling/deploy/agent.py +86 -13
  6. prediction_market_agent_tooling/deploy/betting_strategy.py +5 -35
  7. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +2 -1
  8. prediction_market_agent_tooling/markets/agent_market.py +14 -6
  9. prediction_market_agent_tooling/markets/data_models.py +14 -0
  10. prediction_market_agent_tooling/markets/manifold/api.py +3 -1
  11. prediction_market_agent_tooling/markets/manifold/manifold.py +7 -2
  12. prediction_market_agent_tooling/markets/metaculus/metaculus.py +6 -1
  13. prediction_market_agent_tooling/markets/omen/data_models.py +247 -6
  14. prediction_market_agent_tooling/markets/omen/omen.py +77 -43
  15. prediction_market_agent_tooling/markets/omen/omen_contracts.py +179 -33
  16. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +35 -0
  17. prediction_market_agent_tooling/markets/polymarket/polymarket.py +1 -1
  18. prediction_market_agent_tooling/monitor/markets/polymarket.py +4 -0
  19. prediction_market_agent_tooling/monitor/monitor.py +3 -3
  20. prediction_market_agent_tooling/monitor/monitor_app.py +2 -2
  21. prediction_market_agent_tooling/tools/contract.py +50 -1
  22. prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py +33 -0
  23. prediction_market_agent_tooling/tools/langfuse_client_utils.py +27 -12
  24. prediction_market_agent_tooling/tools/utils.py +28 -4
  25. prediction_market_agent_tooling/tools/web3_utils.py +7 -0
  26. {prediction_market_agent_tooling-0.48.18.dist-info → prediction_market_agent_tooling-0.49.1.dist-info}/METADATA +2 -1
  27. {prediction_market_agent_tooling-0.48.18.dist-info → prediction_market_agent_tooling-0.49.1.dist-info}/RECORD +30 -27
  28. {prediction_market_agent_tooling-0.48.18.dist-info → prediction_market_agent_tooling-0.49.1.dist-info}/LICENSE +0 -0
  29. {prediction_market_agent_tooling-0.48.18.dist-info → prediction_market_agent_tooling-0.49.1.dist-info}/WHEEL +0 -0
  30. {prediction_market_agent_tooling-0.48.18.dist-info → prediction_market_agent_tooling-0.49.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,29 @@
1
+ [
2
+ {
3
+ "constant": false,
4
+ "inputs": [],
5
+ "name": "inc",
6
+ "outputs": [],
7
+ "payable": false,
8
+ "stateMutability": "nonpayable",
9
+ "type": "function"
10
+ },
11
+ {
12
+ "constant": true,
13
+ "inputs": [],
14
+ "name": "counter",
15
+ "outputs": [{ "name": "", "type": "uint256" }],
16
+ "payable": false,
17
+ "stateMutability": "view",
18
+ "type": "function"
19
+ },
20
+ {
21
+ "constant": true,
22
+ "inputs": [],
23
+ "name": "getNow",
24
+ "outputs": [{ "name": "", "type": "uint32" }],
25
+ "payable": false,
26
+ "stateMutability": "view",
27
+ "type": "function"
28
+ }
29
+ ]
@@ -0,0 +1,171 @@
1
+ [
2
+ {
3
+ "type": "constructor",
4
+ "inputs": [],
5
+ "stateMutability": "nonpayable"
6
+ },
7
+ {
8
+ "type": "function",
9
+ "name": "addPrediction",
10
+ "inputs": [
11
+ {
12
+ "name": "marketAddress",
13
+ "type": "address",
14
+ "internalType": "address"
15
+ },
16
+ {
17
+ "name": "prediction",
18
+ "type": "tuple",
19
+ "internalType": "struct Prediction",
20
+ "components": [
21
+ {
22
+ "name": "publisherAddress",
23
+ "type": "address",
24
+ "internalType": "address"
25
+ },
26
+ {
27
+ "name": "ipfsHash",
28
+ "type": "bytes32",
29
+ "internalType": "bytes32"
30
+ },
31
+ {
32
+ "name": "txHashes",
33
+ "type": "bytes32[]",
34
+ "internalType": "bytes32[]"
35
+ },
36
+ {
37
+ "name": "estimatedProbabilityBps",
38
+ "type": "uint16",
39
+ "internalType": "uint16"
40
+ }
41
+ ]
42
+ }
43
+ ],
44
+ "outputs": [],
45
+ "stateMutability": "nonpayable"
46
+ },
47
+ {
48
+ "type": "function",
49
+ "name": "getPredictionByIndex",
50
+ "inputs": [
51
+ {
52
+ "name": "marketAddress",
53
+ "type": "address",
54
+ "internalType": "address"
55
+ },
56
+ {
57
+ "name": "index",
58
+ "type": "uint256",
59
+ "internalType": "uint256"
60
+ }
61
+ ],
62
+ "outputs": [
63
+ {
64
+ "name": "",
65
+ "type": "tuple",
66
+ "internalType": "struct Prediction",
67
+ "components": [
68
+ {
69
+ "name": "publisherAddress",
70
+ "type": "address",
71
+ "internalType": "address"
72
+ },
73
+ {
74
+ "name": "ipfsHash",
75
+ "type": "bytes32",
76
+ "internalType": "bytes32"
77
+ },
78
+ {
79
+ "name": "txHashes",
80
+ "type": "bytes32[]",
81
+ "internalType": "bytes32[]"
82
+ },
83
+ {
84
+ "name": "estimatedProbabilityBps",
85
+ "type": "uint16",
86
+ "internalType": "uint16"
87
+ }
88
+ ]
89
+ }
90
+ ],
91
+ "stateMutability": "view"
92
+ },
93
+ {
94
+ "type": "function",
95
+ "name": "getPredictions",
96
+ "inputs": [
97
+ {
98
+ "name": "marketAddress",
99
+ "type": "address",
100
+ "internalType": "address"
101
+ }
102
+ ],
103
+ "outputs": [
104
+ {
105
+ "name": "",
106
+ "type": "tuple[]",
107
+ "internalType": "struct Prediction[]",
108
+ "components": [
109
+ {
110
+ "name": "publisherAddress",
111
+ "type": "address",
112
+ "internalType": "address"
113
+ },
114
+ {
115
+ "name": "ipfsHash",
116
+ "type": "bytes32",
117
+ "internalType": "bytes32"
118
+ },
119
+ {
120
+ "name": "txHashes",
121
+ "type": "bytes32[]",
122
+ "internalType": "bytes32[]"
123
+ },
124
+ {
125
+ "name": "estimatedProbabilityBps",
126
+ "type": "uint16",
127
+ "internalType": "uint16"
128
+ }
129
+ ]
130
+ }
131
+ ],
132
+ "stateMutability": "view"
133
+ },
134
+ {
135
+ "type": "event",
136
+ "name": "PredictionAdded",
137
+ "inputs": [
138
+ {
139
+ "name": "marketAddress",
140
+ "type": "address",
141
+ "indexed": true,
142
+ "internalType": "address"
143
+ },
144
+ {
145
+ "name": "estimatedProbabilityBps",
146
+ "type": "uint16",
147
+ "indexed": false,
148
+ "internalType": "uint16"
149
+ },
150
+ {
151
+ "name": "publisherAddress",
152
+ "type": "address",
153
+ "indexed": true,
154
+ "internalType": "address"
155
+ },
156
+ {
157
+ "name": "txHashes",
158
+ "type": "bytes32[]",
159
+ "indexed": false,
160
+ "internalType": "bytes32[]"
161
+ },
162
+ {
163
+ "name": "ipfsHash",
164
+ "type": "bytes32",
165
+ "indexed": false,
166
+ "internalType": "bytes32"
167
+ }
168
+ ],
169
+ "anonymous": false
170
+ }
171
+ ]
@@ -1,7 +1,6 @@
1
1
  import concurrent.futures
2
2
  import os
3
3
  import typing as t
4
- from collections import defaultdict
5
4
 
6
5
  import numpy as np
7
6
  import pandas as pd
@@ -436,92 +435,6 @@ class Benchmarker:
436
435
  ],
437
436
  }
438
437
 
439
- def calculate_expected_returns(
440
- self, prediction: Prediction, market: AgentMarket
441
- ) -> float | None:
442
- """
443
- The expected value if betting on a binary market in its initialized state of 50:50 'yes' and 'no' shares, with the assumption that the correct `p_yes` is that of the market.
444
- """
445
- if not prediction.is_answered:
446
- return None
447
-
448
- # TODO: Add support for different bet sizes -- if we bet a low amount (such as <10 units), the real shares will be very close to that we calculate below (bet_units / share_price),
449
- # but if one bets a lot, it will change the share price along the way, and so he/she receives less than `bet_units / share_price`, but it's more complicated to calculate.
450
- bet_units = 10 # Assuming the agent always bet 10 units per market.
451
-
452
- assert prediction.outcome_prediction is not None
453
- # Assume that market starts at 50/50 and so the price is 0.5 at the time we are buying it,
454
- # we can't use {yes,no}_outcome_price atm, because it would just cancel out to EV = 0.0,
455
- # as it's the same as the probability.
456
- yes_shares = (
457
- bet_units / 0.5 # market.yes_outcome_price
458
- if prediction.outcome_prediction.probable_resolution == Resolution.YES
459
- and market.yes_outcome_price > 0
460
- else 0
461
- )
462
- no_shares = (
463
- bet_units / 0.5 # market.no_outcome_price
464
- if prediction.outcome_prediction.probable_resolution == Resolution.NO
465
- and market.no_outcome_price > 0
466
- else 0
467
- )
468
-
469
- # If we don't bet, we don't have any expected returns.
470
- if yes_shares == 0 and no_shares == 0:
471
- return None
472
-
473
- expected_value = (
474
- yes_shares * market.current_p_yes
475
- + no_shares * (1 - market.current_p_yes)
476
- - bet_units
477
- )
478
- expected_returns_perc = 100 * expected_value / bet_units
479
-
480
- return expected_returns_perc
481
-
482
- def compute_expected_returns_summary(
483
- self,
484
- ) -> t.Tuple[dict[str, list[str | float]], dict[str, list[str | float | None]]]:
485
- overall_summary: dict[str, list[str | float]] = defaultdict(list)
486
-
487
- for agent in self.registered_agents:
488
- expected_returns = []
489
-
490
- for market in self.markets:
491
- if (
492
- prediction := self.get_prediction(agent.agent_name, market.question)
493
- ).is_answered and (
494
- expected_return := self.calculate_expected_returns(
495
- prediction, market
496
- )
497
- ) is not None:
498
- expected_returns.append(expected_return)
499
-
500
- overall_summary["Agent"].append(agent.agent_name)
501
- overall_summary["Mean expected returns"].append(
502
- float(np.mean(expected_returns))
503
- )
504
- overall_summary["Median expected returns"].append(
505
- float(np.median(expected_returns))
506
- )
507
- overall_summary["Total expected returns"].append(
508
- float(np.sum(expected_returns))
509
- )
510
-
511
- per_market: dict[str, list[str | float | None]] = defaultdict(list)
512
-
513
- for market in self.markets:
514
- per_market["Market Question"].append(market.question)
515
-
516
- for agent in self.registered_agents:
517
- per_market[agent.agent_name].append(
518
- self.calculate_expected_returns(
519
- self.get_prediction(agent.agent_name, market.question), market
520
- )
521
- )
522
-
523
- return dict(overall_summary), dict(per_market)
524
-
525
438
  def generate_markdown_report(self) -> str:
526
439
  md = "# Comparison Report\n\n"
527
440
  md += "## Market Results\n\n"
@@ -533,10 +446,4 @@ class Benchmarker:
533
446
  md += "\n\n"
534
447
  md += "### Markets\n\n"
535
448
  md += pd.DataFrame(self.get_markets_summary()).to_markdown(index=False)
536
- md += "\n\n"
537
- md += "### Expected value\n\n"
538
- overall_summary, per_market = self.compute_expected_returns_summary()
539
- md += pd.DataFrame(overall_summary).to_markdown(index=False)
540
- md += "\n\n"
541
- md += pd.DataFrame(per_market).to_markdown(index=False)
542
449
  return md
@@ -35,6 +35,7 @@ class APIKeys(BaseSettings):
35
35
  SAFE_ADDRESS: t.Optional[ChecksumAddress] = None
36
36
  OPENAI_API_KEY: t.Optional[SecretStr] = None
37
37
  GRAPH_API_KEY: t.Optional[SecretStr] = None
38
+ TENDERLY_FORK_RPC: t.Optional[str] = None
38
39
 
39
40
  GOOGLE_SEARCH_API_KEY: t.Optional[SecretStr] = None
40
41
  GOOGLE_SEARCH_ENGINE_ID: t.Optional[SecretStr] = None
@@ -44,6 +45,9 @@ class APIKeys(BaseSettings):
44
45
  LANGFUSE_HOST: t.Optional[str] = None
45
46
  LANGFUSE_DEPLOYMENT_VERSION: t.Optional[str] = None
46
47
 
48
+ PINATA_API_KEY: t.Optional[SecretStr] = None
49
+ PINATA_API_SECRET: t.Optional[SecretStr] = None
50
+
47
51
  TAVILY_API_KEY: t.Optional[SecretStr] = None
48
52
 
49
53
  SQLALCHEMY_DB_URL: t.Optional[SecretStr] = None
@@ -147,6 +151,18 @@ class APIKeys(BaseSettings):
147
151
  and self.LANGFUSE_HOST is not None
148
152
  )
149
153
 
154
+ @property
155
+ def pinata_api_key(self) -> SecretStr:
156
+ return check_not_none(
157
+ self.PINATA_API_KEY, "PINATA_API_KEY missing in the environment."
158
+ )
159
+
160
+ @property
161
+ def pinata_api_secret(self) -> SecretStr:
162
+ return check_not_none(
163
+ self.PINATA_API_SECRET, "PINATA_API_SECRET missing in the environment."
164
+ )
165
+
150
166
  @property
151
167
  def tavily_api_key(self) -> SecretStr:
152
168
  return check_not_none(
@@ -10,6 +10,7 @@ from functools import cached_property
10
10
 
11
11
  from pydantic import BaseModel, BeforeValidator, computed_field
12
12
  from typing_extensions import Annotated
13
+ from web3 import Web3
13
14
 
14
15
  from prediction_market_agent_tooling.config import APIKeys
15
16
  from prediction_market_agent_tooling.deploy.betting_strategy import (
@@ -30,7 +31,7 @@ from prediction_market_agent_tooling.deploy.gcp.utils import (
30
31
  gcp_function_is_active,
31
32
  gcp_resolve_api_keys_secrets,
32
33
  )
33
- from prediction_market_agent_tooling.gtypes import xDai, xdai_type
34
+ from prediction_market_agent_tooling.gtypes import HexStr, xDai, xdai_type
34
35
  from prediction_market_agent_tooling.loggers import logger
35
36
  from prediction_market_agent_tooling.markets.agent_market import (
36
37
  AgentMarket,
@@ -38,6 +39,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
38
39
  SortBy,
39
40
  )
40
41
  from prediction_market_agent_tooling.markets.data_models import (
42
+ PlacedTrade,
41
43
  Position,
42
44
  ProbabilisticAnswer,
43
45
  Trade,
@@ -46,17 +48,27 @@ from prediction_market_agent_tooling.markets.markets import (
46
48
  MarketType,
47
49
  have_bet_on_market_since,
48
50
  )
51
+ from prediction_market_agent_tooling.markets.omen.data_models import (
52
+ ContractPrediction,
53
+ IPFSAgentResult,
54
+ )
49
55
  from prediction_market_agent_tooling.markets.omen.omen import (
50
56
  is_minimum_required_balance,
51
57
  redeem_from_all_user_positions,
52
58
  withdraw_wxdai_to_xdai_to_keep_balance,
53
59
  )
60
+ from prediction_market_agent_tooling.markets.omen.omen_contracts import (
61
+ OmenAgentResultMappingContract,
62
+ )
54
63
  from prediction_market_agent_tooling.monitor.monitor_app import (
55
64
  MARKET_TYPE_TO_DEPLOYED_AGENT,
56
65
  )
66
+ from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
67
+ from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
57
68
  from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
58
69
  from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
59
70
  from prediction_market_agent_tooling.tools.utils import DatetimeWithTimezone, utcnow
71
+ from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
60
72
 
61
73
  MAX_AVAILABLE_MARKETS = 20
62
74
  TRADER_TAG = "trader"
@@ -110,7 +122,7 @@ class OutOfFundsError(ValueError):
110
122
 
111
123
  class ProcessedMarket(BaseModel):
112
124
  answer: ProbabilisticAnswer
113
- trades: list[Trade]
125
+ trades: list[PlacedTrade]
114
126
 
115
127
 
116
128
  class AnsweredEnum(str, Enum):
@@ -282,7 +294,6 @@ class DeployableTraderAgent(DeployableAgent):
282
294
  bet_on_n_markets_per_run: int = 1
283
295
  min_required_balance_to_operate: xDai | None = xdai_type(1)
284
296
  min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
285
- strategy: BettingStrategy = MaxAccuracyBettingStrategy()
286
297
 
287
298
  def __init__(
288
299
  self,
@@ -291,6 +302,16 @@ class DeployableTraderAgent(DeployableAgent):
291
302
  ) -> None:
292
303
  super().__init__(enable_langfuse=enable_langfuse)
293
304
  self.place_bet = place_bet
305
+ self.ipfs_handler = IPFSHandler(APIKeys())
306
+
307
+ def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
308
+ user_id = market.get_user_id(api_keys=APIKeys())
309
+
310
+ total_amount = market.get_user_balance(user_id=user_id) * 0.1
311
+ if existing_position := market.get_position(user_id=user_id):
312
+ total_amount += existing_position.total_amount.amount
313
+
314
+ return MaxAccuracyBettingStrategy(bet_amount=total_amount)
294
315
 
295
316
  def initialize_langfuse(self) -> None:
296
317
  super().initialize_langfuse()
@@ -410,7 +431,8 @@ class DeployableTraderAgent(DeployableAgent):
410
431
  answer: ProbabilisticAnswer,
411
432
  existing_position: Position | None,
412
433
  ) -> list[Trade]:
413
- trades = self.strategy.calculate_trades(existing_position, answer, market)
434
+ strategy = self.get_betting_strategy(market=market)
435
+ trades = strategy.calculate_trades(existing_position, answer, market)
414
436
  BettingStrategy.assert_trades_currency_match_markets(market, trades)
415
437
  return trades
416
438
 
@@ -443,33 +465,84 @@ class DeployableTraderAgent(DeployableAgent):
443
465
 
444
466
  existing_position = market.get_position(user_id=APIKeys().bet_from_address)
445
467
  trades = self.build_trades(
446
- market=market, answer=answer, existing_position=existing_position
468
+ market=market,
469
+ answer=answer,
470
+ existing_position=existing_position,
447
471
  )
448
472
 
473
+ placed_trades = []
449
474
  if self.place_bet:
450
475
  for trade in trades:
451
- logger.info(f"Executing trade {trade}")
476
+ logger.info(f"Executing trade {trade} on market {market.id}")
452
477
 
453
478
  match trade.trade_type:
454
479
  case TradeType.BUY:
455
- market.buy_tokens(outcome=trade.outcome, amount=trade.amount)
480
+ id = market.buy_tokens(
481
+ outcome=trade.outcome, amount=trade.amount
482
+ )
456
483
  case TradeType.SELL:
457
- market.sell_tokens(outcome=trade.outcome, amount=trade.amount)
484
+ id = market.sell_tokens(
485
+ outcome=trade.outcome, amount=trade.amount
486
+ )
458
487
  case _:
459
488
  raise ValueError(f"Unexpected trade type {trade.trade_type}.")
489
+ placed_trades.append(PlacedTrade.from_trade(trade, id))
460
490
 
461
- self.after_process_market(market_type, market)
462
-
463
- processed_market = ProcessedMarket(answer=answer, trades=trades)
491
+ processed_market = ProcessedMarket(answer=answer, trades=placed_trades)
464
492
  self.update_langfuse_trace_by_processed_market(market_type, processed_market)
465
493
 
494
+ self.after_process_market(
495
+ market_type, market, processed_market=processed_market
496
+ )
497
+
466
498
  logger.info(f"Processed market {market.question=} from {market.url=}.")
467
499
  return processed_market
468
500
 
469
501
  def after_process_market(
470
- self, market_type: MarketType, market: AgentMarket
502
+ self,
503
+ market_type: MarketType,
504
+ market: AgentMarket,
505
+ processed_market: ProcessedMarket,
471
506
  ) -> None:
472
- pass
507
+ if market_type != MarketType.OMEN:
508
+ logger.info(
509
+ f"Skipping after_process_market since market_type {market_type} != OMEN"
510
+ )
511
+ return
512
+ keys = APIKeys()
513
+ self.store_prediction(
514
+ market_id=market.id, processed_market=processed_market, keys=keys
515
+ )
516
+
517
+ def store_prediction(
518
+ self, market_id: str, processed_market: ProcessedMarket, keys: APIKeys
519
+ ) -> None:
520
+ reasoning = (
521
+ processed_market.answer.reasoning
522
+ if processed_market.answer.reasoning
523
+ else ""
524
+ )
525
+ ipfs_hash = self.ipfs_handler.store_agent_result(
526
+ IPFSAgentResult(reasoning=reasoning)
527
+ )
528
+
529
+ tx_hashes = [
530
+ HexBytes(HexStr(i.id)) for i in processed_market.trades if i.id is not None
531
+ ]
532
+ prediction = ContractPrediction(
533
+ publisher=keys.public_key,
534
+ ipfs_hash=ipfscidv0_to_byte32(ipfs_hash),
535
+ tx_hashes=tx_hashes,
536
+ estimated_probability_bps=int(processed_market.answer.p_yes * 10000),
537
+ )
538
+ tx_receipt = OmenAgentResultMappingContract().add_prediction(
539
+ api_keys=keys,
540
+ market_address=Web3.to_checksum_address(market_id),
541
+ prediction=prediction,
542
+ )
543
+ logger.info(
544
+ f"Added prediction to market {market_id}. - receipt {tx_receipt['transactionHash'].hex()}."
545
+ )
473
546
 
474
547
  def before_process_markets(self, market_type: MarketType) -> None:
475
548
  """
@@ -30,12 +30,6 @@ class BettingStrategy(ABC):
30
30
  def build_zero_token_amount(self, currency: Currency) -> TokenAmount:
31
31
  return TokenAmount(amount=0, currency=currency)
32
32
 
33
- @abstractmethod
34
- def adjust_bet_amount(
35
- self, existing_position: Position | None, market: AgentMarket
36
- ) -> float:
37
- pass
38
-
39
33
  @staticmethod
40
34
  def assert_trades_currency_match_markets(
41
35
  market: AgentMarket, trades: list[Trade]
@@ -99,20 +93,7 @@ class BettingStrategy(ABC):
99
93
 
100
94
 
101
95
  class MaxAccuracyBettingStrategy(BettingStrategy):
102
- def adjust_bet_amount(
103
- self, existing_position: Position | None, market: AgentMarket
104
- ) -> float:
105
- existing_position_total_amount = (
106
- existing_position.total_amount.amount if existing_position else 0
107
- )
108
- bet_amount = (
109
- market.get_tiny_bet_amount().amount
110
- if self.bet_amount is None
111
- else self.bet_amount
112
- )
113
- return bet_amount + existing_position_total_amount
114
-
115
- def __init__(self, bet_amount: float | None = None):
96
+ def __init__(self, bet_amount: float):
116
97
  self.bet_amount = bet_amount
117
98
 
118
99
  def calculate_trades(
@@ -121,13 +102,11 @@ class MaxAccuracyBettingStrategy(BettingStrategy):
121
102
  answer: ProbabilisticAnswer,
122
103
  market: AgentMarket,
123
104
  ) -> list[Trade]:
124
- adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
125
-
126
105
  direction = self.calculate_direction(market.current_p_yes, answer.p_yes)
127
106
 
128
107
  amounts = {
129
108
  market.get_outcome_str_from_bool(direction): TokenAmount(
130
- amount=adjusted_bet_amount,
109
+ amount=self.bet_amount,
131
110
  currency=market.currency,
132
111
  ),
133
112
  }
@@ -155,24 +134,15 @@ class MaxExpectedValueBettingStrategy(MaxAccuracyBettingStrategy):
155
134
 
156
135
 
157
136
  class KellyBettingStrategy(BettingStrategy):
158
- def __init__(self, max_bet_amount: float = 10):
137
+ def __init__(self, max_bet_amount: float):
159
138
  self.max_bet_amount = max_bet_amount
160
139
 
161
- def adjust_bet_amount(
162
- self, existing_position: Position | None, market: AgentMarket
163
- ) -> float:
164
- existing_position_total_amount = (
165
- existing_position.total_amount.amount if existing_position else 0
166
- )
167
- return self.max_bet_amount + existing_position_total_amount
168
-
169
140
  def calculate_trades(
170
141
  self,
171
142
  existing_position: Position | None,
172
143
  answer: ProbabilisticAnswer,
173
144
  market: AgentMarket,
174
145
  ) -> list[Trade]:
175
- adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
176
146
  outcome_token_pool = check_not_none(market.outcome_token_pool)
177
147
  kelly_bet = (
178
148
  get_kelly_bet_full(
@@ -183,12 +153,12 @@ class KellyBettingStrategy(BettingStrategy):
183
153
  market.get_outcome_str_from_bool(False)
184
154
  ],
185
155
  estimated_p_yes=answer.p_yes,
186
- max_bet=adjusted_bet_amount,
156
+ max_bet=self.max_bet_amount,
187
157
  confidence=answer.confidence,
188
158
  )
189
159
  if market.has_token_pool()
190
160
  else get_kelly_bet_simplified(
191
- adjusted_bet_amount,
161
+ self.max_bet_amount,
192
162
  market.current_p_yes,
193
163
  answer.p_yes,
194
164
  answer.confidence,
@@ -83,7 +83,8 @@ def compute_job_reward(
83
83
  market: OmenAgentMarket, max_bond: float, web3: Web3 | None = None
84
84
  ) -> float:
85
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(
86
+ strategy = KellyBettingStrategy(max_bet_amount=max_bond)
87
+ required_trades = strategy.calculate_trades(
87
88
  existing_position=None,
88
89
  # We assume that we finish the job and so the probability of the market happening will be 100%.
89
90
  answer=ProbabilisticAnswer(p_yes=Probability(1.0), confidence=1.0),