prediction-market-agent-tooling 0.64.12.dev660__py3-none-any.whl → 0.65.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.
Files changed (51) hide show
  1. prediction_market_agent_tooling/benchmark/agents.py +19 -16
  2. prediction_market_agent_tooling/benchmark/benchmark.py +94 -84
  3. prediction_market_agent_tooling/benchmark/utils.py +8 -9
  4. prediction_market_agent_tooling/deploy/agent.py +85 -125
  5. prediction_market_agent_tooling/deploy/agent_example.py +20 -10
  6. prediction_market_agent_tooling/deploy/betting_strategy.py +222 -96
  7. prediction_market_agent_tooling/deploy/constants.py +4 -0
  8. prediction_market_agent_tooling/jobs/jobs_models.py +15 -4
  9. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +3 -3
  10. prediction_market_agent_tooling/markets/agent_market.py +145 -50
  11. prediction_market_agent_tooling/markets/blockchain_utils.py +10 -1
  12. prediction_market_agent_tooling/markets/data_models.py +83 -17
  13. prediction_market_agent_tooling/markets/manifold/api.py +18 -7
  14. prediction_market_agent_tooling/markets/manifold/data_models.py +23 -16
  15. prediction_market_agent_tooling/markets/manifold/manifold.py +18 -18
  16. prediction_market_agent_tooling/markets/manifold/utils.py +7 -12
  17. prediction_market_agent_tooling/markets/markets.py +2 -1
  18. prediction_market_agent_tooling/markets/metaculus/metaculus.py +29 -4
  19. prediction_market_agent_tooling/markets/omen/data_models.py +17 -32
  20. prediction_market_agent_tooling/markets/omen/omen.py +65 -108
  21. prediction_market_agent_tooling/markets/omen/omen_contracts.py +2 -5
  22. prediction_market_agent_tooling/markets/omen/omen_resolving.py +13 -13
  23. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +18 -12
  24. prediction_market_agent_tooling/markets/polymarket/data_models.py +7 -3
  25. prediction_market_agent_tooling/markets/polymarket/data_models_web.py +7 -3
  26. prediction_market_agent_tooling/markets/polymarket/polymarket.py +5 -4
  27. prediction_market_agent_tooling/markets/seer/data_models.py +0 -83
  28. prediction_market_agent_tooling/markets/seer/price_manager.py +44 -30
  29. prediction_market_agent_tooling/markets/seer/seer.py +105 -105
  30. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +34 -41
  31. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +1 -1
  32. prediction_market_agent_tooling/tools/cow/cow_order.py +10 -3
  33. prediction_market_agent_tooling/tools/is_predictable.py +2 -3
  34. prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -4
  35. prediction_market_agent_tooling/tools/omen/sell_positions.py +3 -2
  36. prediction_market_agent_tooling/tools/utils.py +26 -13
  37. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/METADATA +2 -2
  38. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/RECORD +41 -51
  39. prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +0 -68
  40. prediction_market_agent_tooling/monitor/markets/manifold.py +0 -90
  41. prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -43
  42. prediction_market_agent_tooling/monitor/markets/omen.py +0 -88
  43. prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -49
  44. prediction_market_agent_tooling/monitor/monitor.py +0 -406
  45. prediction_market_agent_tooling/monitor/monitor_app.py +0 -149
  46. prediction_market_agent_tooling/monitor/monitor_settings.py +0 -27
  47. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -146
  48. prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -12
  49. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/LICENSE +0 -0
  50. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/WHEEL +0 -0
  51. {prediction_market_agent_tooling-0.64.12.dev660.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/entry_points.txt +0 -0
@@ -1,14 +1,25 @@
1
1
  from abc import ABC, abstractmethod
2
+ from typing import Sequence
2
3
 
3
4
  from scipy.optimize import minimize_scalar
4
5
 
5
- from prediction_market_agent_tooling.gtypes import USD, CollateralToken, OutcomeToken
6
+ from prediction_market_agent_tooling.benchmark.utils import get_most_probable_outcome
7
+ from prediction_market_agent_tooling.deploy.constants import (
8
+ INVALID_OUTCOME_LOWERCASE_IDENTIFIER,
9
+ )
10
+ from prediction_market_agent_tooling.gtypes import (
11
+ USD,
12
+ CollateralToken,
13
+ OutcomeStr,
14
+ OutcomeWei,
15
+ Probability,
16
+ )
6
17
  from prediction_market_agent_tooling.loggers import logger
7
18
  from prediction_market_agent_tooling.markets.agent_market import AgentMarket, MarketFees
8
19
  from prediction_market_agent_tooling.markets.data_models import (
20
+ CategoricalProbabilisticAnswer,
9
21
  ExistingPosition,
10
22
  Position,
11
- ProbabilisticAnswer,
12
23
  Trade,
13
24
  TradeType,
14
25
  )
@@ -17,6 +28,7 @@ from prediction_market_agent_tooling.markets.omen.omen import (
17
28
  )
18
29
  from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
19
30
  get_kelly_bet_full,
31
+ get_kelly_bet_simplified,
20
32
  )
21
33
  from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
22
34
  from prediction_market_agent_tooling.tools.utils import check_not_none
@@ -31,7 +43,7 @@ class BettingStrategy(ABC):
31
43
  def calculate_trades(
32
44
  self,
33
45
  existing_position: ExistingPosition | None,
34
- answer: ProbabilisticAnswer,
46
+ answer: CategoricalProbabilisticAnswer,
35
47
  market: AgentMarket,
36
48
  ) -> list[Trade]:
37
49
  raise NotImplementedError("Subclass should implement this.")
@@ -41,7 +53,8 @@ class BettingStrategy(ABC):
41
53
  def maximum_possible_bet_amount(self) -> USD:
42
54
  raise NotImplementedError("Subclass should implement this.")
43
55
 
44
- def build_zero_usd_amount(self) -> USD:
56
+ @staticmethod
57
+ def build_zero_usd_amount() -> USD:
45
58
  return USD(0)
46
59
 
47
60
  @staticmethod
@@ -95,27 +108,43 @@ class BettingStrategy(ABC):
95
108
  Trade(outcome=0, amount=10, trade_type=TradeType.BUY),
96
109
  Trade(outcome=1, amount=5, trade_type=TradeType.SELL)
97
110
  ]
98
- Note that we order the trades to first buy then sell, in order to minimally tilt the odds so that
99
- sell price is higher.
111
+ Note that we order the trades to first buy then sell, in order to minimally tilt the odds so that sell price is higher.
100
112
  """
113
+
114
+ existing_amounts = (
115
+ {
116
+ outcome.lower(): amount
117
+ for outcome, amount in existing_position.amounts_current.items()
118
+ }
119
+ if existing_position
120
+ else {}
121
+ )
122
+ target_amounts = (
123
+ {
124
+ outcome.lower(): amount
125
+ for outcome, amount in target_position.amounts_current.items()
126
+ }
127
+ if target_position
128
+ else {}
129
+ )
130
+
101
131
  trades = []
102
- for outcome_bool in [True, False]:
103
- outcome = market.get_outcome_str_from_bool(outcome_bool)
104
- prev_amount = (
105
- existing_position.amounts_current[outcome]
106
- if existing_position and outcome in existing_position.amounts_current
107
- else self.build_zero_usd_amount()
132
+ for outcome in market.outcomes:
133
+ existing_amount = existing_amounts.get(
134
+ outcome.lower(), self.build_zero_usd_amount()
108
135
  )
109
- new_amount = target_position.amounts_current.get(
110
- outcome, self.build_zero_usd_amount()
136
+ target_amount = target_amounts.get(
137
+ outcome.lower(), self.build_zero_usd_amount()
111
138
  )
112
- diff_amount = new_amount - prev_amount
139
+
140
+ diff_amount = target_amount - existing_amount
113
141
  if diff_amount == 0:
114
142
  continue
143
+
115
144
  trade_type = TradeType.SELL if diff_amount < 0 else TradeType.BUY
116
145
  trade = Trade(
117
146
  amount=abs(diff_amount),
118
- outcome=outcome_bool,
147
+ outcome=outcome,
119
148
  trade_type=trade_type,
120
149
  )
121
150
 
@@ -130,7 +159,7 @@ class BettingStrategy(ABC):
130
159
  return trades
131
160
 
132
161
 
133
- class MaxAccuracyBettingStrategy(BettingStrategy):
162
+ class MultiCategoricalMaxAccuracyBettingStrategy(BettingStrategy):
134
163
  def __init__(self, bet_amount: USD):
135
164
  self.bet_amount = bet_amount
136
165
 
@@ -138,38 +167,89 @@ class MaxAccuracyBettingStrategy(BettingStrategy):
138
167
  def maximum_possible_bet_amount(self) -> USD:
139
168
  return self.bet_amount
140
169
 
170
+ @staticmethod
171
+ def calculate_direction(
172
+ market: AgentMarket, answer: CategoricalProbabilisticAnswer
173
+ ) -> OutcomeStr:
174
+ # We place a bet on the most likely outcome
175
+ most_likely_outcome = max(
176
+ answer.probabilities.items(),
177
+ key=lambda item: item[1],
178
+ )[0]
179
+
180
+ return market.market_outcome_for_probability_key(most_likely_outcome)
181
+
182
+ @staticmethod
183
+ def get_other_direction(
184
+ outcomes: Sequence[OutcomeStr], direction: OutcomeStr
185
+ ) -> OutcomeStr:
186
+ # We get the first direction which is != direction.
187
+ other_direction = [i for i in outcomes if i.lower() != direction.lower()][0]
188
+ if INVALID_OUTCOME_LOWERCASE_IDENTIFIER in other_direction.lower():
189
+ raise ValueError("Invalid outcome found as opposite direction. Exitting.")
190
+ return other_direction
191
+
141
192
  def calculate_trades(
142
193
  self,
143
194
  existing_position: ExistingPosition | None,
144
- answer: ProbabilisticAnswer,
195
+ answer: CategoricalProbabilisticAnswer,
145
196
  market: AgentMarket,
146
197
  ) -> list[Trade]:
147
- direction = self.calculate_direction(market.current_p_yes, answer.p_yes)
198
+ """We place bet on only one outcome."""
148
199
 
149
- amounts = {
150
- market.get_outcome_str_from_bool(direction): self.bet_amount,
151
- }
152
- target_position = Position(market_id=market.id, amounts_current=amounts)
200
+ outcome_to_bet_on = self.calculate_direction(market, answer)
201
+
202
+ target_position = Position(
203
+ market_id=market.id, amounts_current={outcome_to_bet_on: self.bet_amount}
204
+ )
153
205
  trades = self._build_rebalance_trades_from_positions(
154
- existing_position, target_position, market=market
206
+ existing_position=existing_position,
207
+ target_position=target_position,
208
+ market=market,
155
209
  )
156
210
  return trades
157
211
 
212
+
213
+ class MaxExpectedValueBettingStrategy(MultiCategoricalMaxAccuracyBettingStrategy):
158
214
  @staticmethod
159
- def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:
160
- return estimate_p_yes >= 0.5
215
+ def calculate_direction(
216
+ market: AgentMarket, answer: CategoricalProbabilisticAnswer
217
+ ) -> OutcomeStr:
218
+ """
219
+ Returns the index of the outcome with the highest expected value.
220
+ """
161
221
 
162
- def __repr__(self) -> str:
163
- return f"{self.__class__.__name__}(bet_amount={self.bet_amount})"
222
+ missing_outcomes = set([i.lower() for i in market.outcomes]) - set(
223
+ [i.lower() for i in market.probabilities.keys()]
224
+ )
164
225
 
226
+ if missing_outcomes:
227
+ raise ValueError(
228
+ f"Outcomes {missing_outcomes} not found in answer probabilities {answer.probabilities}"
229
+ )
165
230
 
166
- class MaxExpectedValueBettingStrategy(MaxAccuracyBettingStrategy):
167
- @staticmethod
168
- def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:
169
- # If estimate_p_yes >= market.current_p_yes, then bet TRUE, else bet FALSE.
170
- # This is equivalent to saying EXPECTED_VALUE = (estimate_p_yes * num_tokens_obtained_by_betting_yes) -
171
- # ((1 - estimate_p_yes) * num_tokens_obtained_by_betting_no) >= 0
172
- return estimate_p_yes >= market_p_yes
231
+ best_outcome = None
232
+ best_ev = float("-inf")
233
+ for market_outcome in market.outcomes:
234
+ if market.probability_for_market_outcome(market_outcome) == Probability(
235
+ 0.0
236
+ ):
237
+ # avoid division by 0
238
+ continue
239
+
240
+ ev = answer.probability_for_market_outcome(
241
+ market_outcome
242
+ ) / market.probability_for_market_outcome(market_outcome)
243
+ if ev > best_ev:
244
+ best_ev = ev
245
+ best_outcome = market_outcome
246
+
247
+ if best_outcome is None:
248
+ raise ValueError(
249
+ "Cannot determine best outcome - all market probabilities are zero"
250
+ )
251
+
252
+ return best_outcome
173
253
 
174
254
 
175
255
  class KellyBettingStrategy(BettingStrategy):
@@ -181,40 +261,85 @@ class KellyBettingStrategy(BettingStrategy):
181
261
  def maximum_possible_bet_amount(self) -> USD:
182
262
  return self.max_bet_amount
183
263
 
264
+ @staticmethod
265
+ def get_kelly_bet(
266
+ market: AgentMarket,
267
+ max_bet_amount: USD,
268
+ direction: OutcomeStr,
269
+ other_direction: OutcomeStr,
270
+ answer: CategoricalProbabilisticAnswer,
271
+ override_p_yes: float | None = None,
272
+ ) -> SimpleBet:
273
+ estimated_p_yes = (
274
+ answer.probability_for_market_outcome(direction)
275
+ if not override_p_yes
276
+ else override_p_yes
277
+ )
278
+
279
+ if not market.is_binary:
280
+ # use Kelly simple, since Kelly full only supports 2 outcomes
281
+
282
+ kelly_bet = get_kelly_bet_simplified(
283
+ max_bet=market.get_usd_in_token(max_bet_amount),
284
+ market_p_yes=market.probability_for_market_outcome(direction),
285
+ estimated_p_yes=estimated_p_yes,
286
+ confidence=answer.confidence,
287
+ )
288
+ else:
289
+ # We consider only binary markets, since the Kelly strategy is not yet implemented
290
+ # for markets with more than 2 outcomes (https://github.com/gnosis/prediction-market-agent-tooling/issues/671).
291
+ direction_to_bet_pool_size = market.get_outcome_token_pool_by_outcome(
292
+ direction
293
+ )
294
+ other_direction_pool_size = market.get_outcome_token_pool_by_outcome(
295
+ other_direction
296
+ )
297
+ kelly_bet = get_kelly_bet_full(
298
+ yes_outcome_pool_size=direction_to_bet_pool_size,
299
+ no_outcome_pool_size=other_direction_pool_size,
300
+ estimated_p_yes=estimated_p_yes,
301
+ max_bet=market.get_usd_in_token(max_bet_amount),
302
+ confidence=answer.confidence,
303
+ fees=market.fees,
304
+ )
305
+ return kelly_bet
306
+
184
307
  def calculate_trades(
185
308
  self,
186
309
  existing_position: ExistingPosition | None,
187
- answer: ProbabilisticAnswer,
310
+ answer: CategoricalProbabilisticAnswer,
188
311
  market: AgentMarket,
189
312
  ) -> list[Trade]:
190
- outcome_token_pool = check_not_none(market.outcome_token_pool)
191
- kelly_bet = get_kelly_bet_full(
192
- yes_outcome_pool_size=outcome_token_pool[
193
- market.get_outcome_str_from_bool(True)
194
- ],
195
- no_outcome_pool_size=outcome_token_pool[
196
- market.get_outcome_str_from_bool(False)
197
- ],
198
- estimated_p_yes=answer.p_yes,
199
- max_bet=market.get_usd_in_token(self.max_bet_amount),
200
- confidence=answer.confidence,
201
- fees=market.fees,
313
+ # We consider the p_yes as the direction with highest probability.
314
+ direction = MultiCategoricalMaxAccuracyBettingStrategy.calculate_direction(
315
+ market, answer
316
+ )
317
+ # We get the first direction which is != direction.
318
+ other_direction = [i for i in market.outcomes if i != direction][0]
319
+ if INVALID_OUTCOME_LOWERCASE_IDENTIFIER in other_direction.lower():
320
+ raise ValueError("Invalid outcome found as opposite direction. Exitting.")
321
+
322
+ kelly_bet = self.get_kelly_bet(
323
+ market=market,
324
+ max_bet_amount=self.max_bet_amount,
325
+ direction=direction,
326
+ other_direction=other_direction,
327
+ answer=answer,
202
328
  )
203
329
 
204
330
  kelly_bet_size = kelly_bet.size
205
331
  if self.max_price_impact:
206
332
  # Adjust amount
207
333
  max_price_impact_bet_amount = self.calculate_bet_amount_for_price_impact(
208
- market, kelly_bet
334
+ market, kelly_bet, direction=direction
209
335
  )
210
336
 
211
337
  # We just don't want Kelly size to extrapolate price_impact - hence we take the min.
212
338
  kelly_bet_size = min(kelly_bet.size, max_price_impact_bet_amount)
213
339
 
340
+ bet_outcome = direction if kelly_bet.direction else other_direction
214
341
  amounts = {
215
- market.get_outcome_str_from_bool(
216
- kelly_bet.direction
217
- ): market.get_token_in_usd(kelly_bet_size),
342
+ bet_outcome: market.get_token_in_usd(kelly_bet_size),
218
343
  }
219
344
  target_position = Position(market_id=market.id, amounts_current=amounts)
220
345
  trades = self._build_rebalance_trades_from_positions(
@@ -224,19 +349,16 @@ class KellyBettingStrategy(BettingStrategy):
224
349
 
225
350
  def calculate_price_impact_for_bet_amount(
226
351
  self,
227
- buy_direction: bool,
352
+ outcome_idx: int,
228
353
  bet_amount: CollateralToken,
229
- yes: OutcomeToken,
230
- no: OutcomeToken,
354
+ pool_balances: list[OutcomeWei],
231
355
  fees: MarketFees,
232
356
  ) -> float:
233
- total_outcome_tokens = yes + no
234
- expected_price = (
235
- no / total_outcome_tokens if buy_direction else yes / total_outcome_tokens
236
- )
357
+ prices = AgentMarket.compute_fpmm_probabilities(pool_balances)
358
+ expected_price = prices[outcome_idx]
237
359
 
238
360
  tokens_to_buy = get_buy_outcome_token_amount(
239
- bet_amount, buy_direction, yes, no, fees
361
+ bet_amount, outcome_idx, [i.as_outcome_token for i in pool_balances], fees
240
362
  )
241
363
 
242
364
  actual_price = bet_amount.value / tokens_to_buy.value
@@ -245,19 +367,17 @@ class KellyBettingStrategy(BettingStrategy):
245
367
  return price_impact
246
368
 
247
369
  def calculate_bet_amount_for_price_impact(
248
- self,
249
- market: AgentMarket,
250
- kelly_bet: SimpleBet,
370
+ self, market: AgentMarket, kelly_bet: SimpleBet, direction: OutcomeStr
251
371
  ) -> CollateralToken:
252
372
  def calculate_price_impact_deviation_from_target_price_impact(
253
373
  bet_amount_usd: float, # Needs to be float because it's used in minimize_scalar internally.
254
374
  ) -> float:
375
+ outcome_idx = market.get_outcome_index(direction)
255
376
  price_impact = self.calculate_price_impact_for_bet_amount(
256
- kelly_bet.direction,
257
- market.get_usd_in_token(USD(bet_amount_usd)),
258
- yes_outcome_pool_size,
259
- no_outcome_pool_size,
260
- market.fees,
377
+ outcome_idx=outcome_idx,
378
+ bet_amount=market.get_usd_in_token(USD(bet_amount_usd)),
379
+ pool_balances=pool_balances,
380
+ fees=market.fees,
261
381
  )
262
382
  # We return abs for the algorithm to converge to 0 instead of the min (and possibly negative) value.
263
383
 
@@ -270,17 +390,14 @@ class KellyBettingStrategy(BettingStrategy):
270
390
  )
271
391
  return kelly_bet.size
272
392
 
273
- yes_outcome_pool_size = market.outcome_token_pool[
274
- market.get_outcome_str_from_bool(True)
275
- ]
276
- no_outcome_pool_size = market.outcome_token_pool[
277
- market.get_outcome_str_from_bool(False)
278
- ]
393
+ pool_balances = [i.as_outcome_wei for i in market.outcome_token_pool.values()]
394
+ # stay float for compatibility with `minimize_scalar`
395
+ total_pool_balance = sum([i.value for i in market.outcome_token_pool.values()])
279
396
 
280
397
  # The bounds below have been found to work heuristically.
281
398
  optimized_bet_amount = minimize_scalar(
282
399
  calculate_price_impact_deviation_from_target_price_impact,
283
- bounds=(0, 1000 * (yes_outcome_pool_size + no_outcome_pool_size).value),
400
+ bounds=(0, 1000 * total_pool_balance),
284
401
  method="bounded",
285
402
  tol=1e-11,
286
403
  options={"maxiter": 10000},
@@ -310,37 +427,46 @@ class MaxAccuracyWithKellyScaledBetsStrategy(BettingStrategy):
310
427
  def calculate_trades(
311
428
  self,
312
429
  existing_position: ExistingPosition | None,
313
- answer: ProbabilisticAnswer,
430
+ answer: CategoricalProbabilisticAnswer,
314
431
  market: AgentMarket,
315
432
  ) -> list[Trade]:
316
433
  adjusted_bet_amount_usd = self.adjust_bet_amount(existing_position, market)
317
- adjusted_bet_amount_token = market.get_usd_in_token(adjusted_bet_amount_usd)
318
- outcome_token_pool = check_not_none(market.outcome_token_pool)
319
-
320
- # Fixed direction of bet, only use Kelly to adjust the bet size based on market's outcome pool size.
321
- estimated_p_yes = float(answer.p_yes > 0.5)
322
- confidence = 1.0
323
-
324
- kelly_bet = get_kelly_bet_full(
325
- yes_outcome_pool_size=outcome_token_pool[
326
- market.get_outcome_str_from_bool(True)
327
- ],
328
- no_outcome_pool_size=outcome_token_pool[
329
- market.get_outcome_str_from_bool(False)
330
- ],
331
- estimated_p_yes=estimated_p_yes,
332
- max_bet=adjusted_bet_amount_token,
333
- confidence=confidence,
334
- fees=market.fees,
434
+
435
+ outcome = get_most_probable_outcome(answer.probabilities)
436
+
437
+ direction = MultiCategoricalMaxAccuracyBettingStrategy.calculate_direction(
438
+ market, answer
439
+ )
440
+ # We get the first direction which is != direction.
441
+ other_direction = (
442
+ MultiCategoricalMaxAccuracyBettingStrategy.get_other_direction(
443
+ outcomes=market.outcomes, direction=direction
444
+ )
445
+ )
446
+
447
+ # We ignore the direction nudge given by Kelly, hence we assume we have a perfect prediction.
448
+ estimated_p_yes = 1.0
449
+
450
+ kelly_bet = KellyBettingStrategy.get_kelly_bet(
451
+ market=market,
452
+ max_bet_amount=adjusted_bet_amount_usd,
453
+ direction=direction,
454
+ other_direction=other_direction,
455
+ answer=answer,
456
+ override_p_yes=estimated_p_yes,
335
457
  )
458
+
336
459
  kelly_bet_size_usd = market.get_token_in_usd(kelly_bet.size)
337
460
 
338
461
  amounts = {
339
- market.get_outcome_str_from_bool(kelly_bet.direction): kelly_bet_size_usd,
462
+ outcome: kelly_bet_size_usd,
340
463
  }
341
464
  target_position = Position(market_id=market.id, amounts_current=amounts)
465
+
342
466
  trades = self._build_rebalance_trades_from_positions(
343
- existing_position, target_position, market=market
467
+ existing_position=existing_position,
468
+ target_position=target_position,
469
+ market=market,
344
470
  )
345
471
  return trades
346
472
 
@@ -1,3 +1,7 @@
1
1
  MARKET_TYPE_KEY = "market_type"
2
2
  REPOSITORY_KEY = "repository"
3
3
  OWNER_KEY = "owner"
4
+ INVALID_OUTCOME_LOWERCASE_IDENTIFIER = "invalid"
5
+ # Market-agnostic outcome identifiers
6
+ YES_OUTCOME_LOWERCASE_IDENTIFIER = "yes"
7
+ NO_OUTCOME_LOWERCASE_IDENTIFIER = "no"
@@ -3,12 +3,18 @@ from abc import ABC, abstractmethod
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
6
- from prediction_market_agent_tooling.deploy.betting_strategy import ProbabilisticAnswer
6
+ from prediction_market_agent_tooling.deploy.betting_strategy import (
7
+ CategoricalProbabilisticAnswer,
8
+ )
7
9
  from prediction_market_agent_tooling.gtypes import USD, Probability
8
10
  from prediction_market_agent_tooling.markets.agent_market import (
9
11
  AgentMarket,
10
12
  ProcessedTradedMarket,
11
13
  )
14
+ from prediction_market_agent_tooling.markets.omen.data_models import (
15
+ OMEN_FALSE_OUTCOME,
16
+ OMEN_TRUE_OUTCOME,
17
+ )
12
18
  from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
13
19
  FilterBy,
14
20
  SortBy,
@@ -69,8 +75,13 @@ class JobAgentMarket(AgentMarket, ABC):
69
75
  deadline=self.deadline,
70
76
  )
71
77
 
72
- def get_job_answer(self, result: str) -> ProbabilisticAnswer:
78
+ def get_job_answer(self, result: str) -> CategoricalProbabilisticAnswer:
73
79
  # Just return 100% yes with 100% confidence, because we assume the job is completed correctly.
74
- return ProbabilisticAnswer(
75
- p_yes=Probability(1.0), confidence=1.0, reasoning=result
80
+ return CategoricalProbabilisticAnswer(
81
+ probabilities={
82
+ OMEN_TRUE_OUTCOME: Probability(1.0),
83
+ OMEN_FALSE_OUTCOME: Probability(0.0),
84
+ },
85
+ confidence=1.0,
86
+ reasoning=result,
76
87
  )
@@ -42,7 +42,7 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
42
42
  self.get_token_in_usd(
43
43
  self.get_buy_token_amount(
44
44
  bet_amount=trade.amount,
45
- direction=trade.outcome,
45
+ outcome=trade.outcome,
46
46
  ).as_token
47
47
  )
48
48
  - trade.amount
@@ -56,7 +56,7 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
56
56
  filter_by: FilterBy = FilterBy.OPEN,
57
57
  sort_by: SortBy = SortBy.CLOSING_SOONEST,
58
58
  ) -> t.Sequence["OmenJobAgentMarket"]:
59
- markets = OmenSubgraphHandler().get_omen_binary_markets_simple(
59
+ markets = OmenSubgraphHandler().get_omen_markets_simple(
60
60
  limit=limit,
61
61
  filter_by=filter_by,
62
62
  sort_by=sort_by,
@@ -124,7 +124,7 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
124
124
  resolution=market.resolution,
125
125
  created_time=market.created_time,
126
126
  close_time=market.close_time,
127
- current_p_yes=market.current_p_yes,
127
+ probabilities=market.probabilities,
128
128
  url=market.url,
129
129
  volume=market.volume,
130
130
  creator=market.creator,