prediction-market-agent-tooling 0.54.0__py3-none-any.whl → 0.55.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prediction_market_agent_tooling/deploy/agent.py +162 -86
- prediction_market_agent_tooling/deploy/betting_strategy.py +5 -1
- prediction_market_agent_tooling/markets/agent_market.py +12 -1
- prediction_market_agent_tooling/markets/metaculus/data_models.py +4 -1
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +30 -8
- prediction_market_agent_tooling/markets/omen/omen.py +14 -6
- prediction_market_agent_tooling/tools/is_invalid.py +4 -3
- prediction_market_agent_tooling/tools/tavily/tavily_search.py +11 -1
- {prediction_market_agent_tooling-0.54.0.dist-info → prediction_market_agent_tooling-0.55.0.dist-info}/METADATA +2 -2
- {prediction_market_agent_tooling-0.54.0.dist-info → prediction_market_agent_tooling-0.55.0.dist-info}/RECORD +13 -13
- {prediction_market_agent_tooling-0.54.0.dist-info → prediction_market_agent_tooling-0.55.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.54.0.dist-info → prediction_market_agent_tooling-0.55.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.54.0.dist-info → prediction_market_agent_tooling-0.55.0.dist-info}/entry_points.txt +0 -0
@@ -36,6 +36,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
36
36
|
AgentMarket,
|
37
37
|
FilterBy,
|
38
38
|
ProcessedMarket,
|
39
|
+
ProcessedTradedMarket,
|
39
40
|
SortBy,
|
40
41
|
)
|
41
42
|
from prediction_market_agent_tooling.markets.data_models import (
|
@@ -165,9 +166,11 @@ class DeployableAgent:
|
|
165
166
|
return f"{self.__class__.__name__} - {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}"
|
166
167
|
|
167
168
|
def __init_subclass__(cls, **kwargs: t.Any) -> None:
|
168
|
-
if
|
169
|
-
cls.__init__
|
170
|
-
|
169
|
+
if (
|
170
|
+
"DeployableAgent" not in str(cls.__init__)
|
171
|
+
and "DeployableTraderAgent" not in str(cls.__init__)
|
172
|
+
and "DeployablePredictionAgent" not in str(cls.__init__)
|
173
|
+
):
|
171
174
|
raise TypeError(
|
172
175
|
"Cannot override __init__ method of deployable agent class, please override the `load` method to set up the agent."
|
173
176
|
)
|
@@ -274,28 +277,21 @@ def {entrypoint_function_name}(request) -> str:
|
|
274
277
|
return f"{self.__class__.__name__.lower()}-{market_type}-{utcnow().strftime('%Y-%m-%d--%H-%M-%S')}"
|
275
278
|
|
276
279
|
|
277
|
-
class
|
280
|
+
class DeployablePredictionAgent(DeployableAgent):
|
278
281
|
bet_on_n_markets_per_run: int = 1
|
279
282
|
min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
|
280
283
|
allow_invalid_questions: bool = False
|
281
284
|
same_market_bet_interval: timedelta = timedelta(hours=24)
|
285
|
+
# Only Metaculus allows to post predictions without trading (buying/selling of outcome tokens).
|
286
|
+
supported_markets: t.Sequence[MarketType] = [MarketType.METACULUS]
|
282
287
|
|
283
288
|
def __init__(
|
284
289
|
self,
|
285
290
|
enable_langfuse: bool = APIKeys().default_enable_langfuse,
|
286
|
-
|
291
|
+
store_prediction: bool = True,
|
287
292
|
) -> None:
|
288
293
|
super().__init__(enable_langfuse=enable_langfuse)
|
289
|
-
self.
|
290
|
-
|
291
|
-
def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
|
292
|
-
user_id = market.get_user_id(api_keys=APIKeys())
|
293
|
-
|
294
|
-
total_amount = market.get_tiny_bet_amount().amount
|
295
|
-
if existing_position := market.get_position(user_id=user_id):
|
296
|
-
total_amount += existing_position.total_amount.amount
|
297
|
-
|
298
|
-
return MaxAccuracyBettingStrategy(bet_amount=total_amount)
|
294
|
+
self.store_prediction = store_prediction
|
299
295
|
|
300
296
|
def initialize_langfuse(self) -> None:
|
301
297
|
super().initialize_langfuse()
|
@@ -304,7 +300,6 @@ class DeployableTraderAgent(DeployableAgent):
|
|
304
300
|
self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
|
305
301
|
self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
|
306
302
|
self.process_market = observe()(self.process_market) # type: ignore[method-assign]
|
307
|
-
self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
|
308
303
|
|
309
304
|
def update_langfuse_trace_by_market(
|
310
305
|
self, market_type: MarketType, market: AgentMarket
|
@@ -342,19 +337,6 @@ class DeployableTraderAgent(DeployableAgent):
|
|
342
337
|
f"{api_keys=} doesn't have enough operational balance."
|
343
338
|
)
|
344
339
|
|
345
|
-
def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
|
346
|
-
api_keys = APIKeys()
|
347
|
-
|
348
|
-
# Get the strategy to know how much it will bet.
|
349
|
-
strategy = self.get_betting_strategy(market)
|
350
|
-
# Have a little bandwidth after the bet.
|
351
|
-
min_required_balance_to_trade = strategy.maximum_possible_bet_amount * 1.01
|
352
|
-
|
353
|
-
if market.get_trade_balance(api_keys) < min_required_balance_to_trade:
|
354
|
-
raise OutOfFundsError(
|
355
|
-
f"Minimum required balance {min_required_balance_to_trade} for agent is not met."
|
356
|
-
)
|
357
|
-
|
358
340
|
def have_bet_on_market_since(self, market: AgentMarket, since: timedelta) -> bool:
|
359
341
|
return have_bet_on_market_since(keys=APIKeys(), market=market, since=since)
|
360
342
|
|
@@ -399,26 +381,11 @@ class DeployableTraderAgent(DeployableAgent):
|
|
399
381
|
)
|
400
382
|
return available_markets
|
401
383
|
|
402
|
-
def build_trades(
|
403
|
-
self,
|
404
|
-
market: AgentMarket,
|
405
|
-
answer: ProbabilisticAnswer,
|
406
|
-
existing_position: Position | None,
|
407
|
-
) -> list[Trade]:
|
408
|
-
strategy = self.get_betting_strategy(market=market)
|
409
|
-
trades = strategy.calculate_trades(existing_position, answer, market)
|
410
|
-
BettingStrategy.assert_trades_currency_match_markets(market, trades)
|
411
|
-
return trades
|
412
|
-
|
413
384
|
def before_process_market(
|
414
385
|
self, market_type: MarketType, market: AgentMarket
|
415
386
|
) -> None:
|
416
|
-
self.update_langfuse_trace_by_market(market_type, market)
|
417
|
-
|
418
387
|
api_keys = APIKeys()
|
419
388
|
|
420
|
-
self.check_min_required_balance_to_trade(market)
|
421
|
-
|
422
389
|
if market_type.is_blockchain_market:
|
423
390
|
# Exchange wxdai back to xdai if the balance is getting low, so we can keep paying for fees.
|
424
391
|
if self.min_balance_to_keep_in_native_currency is not None:
|
@@ -434,65 +401,39 @@ class DeployableTraderAgent(DeployableAgent):
|
|
434
401
|
market: AgentMarket,
|
435
402
|
verify_market: bool = True,
|
436
403
|
) -> ProcessedMarket | None:
|
404
|
+
self.update_langfuse_trace_by_market(market_type, market)
|
437
405
|
logger.info(f"Processing market {market.question=} from {market.url=}.")
|
438
406
|
|
439
|
-
|
440
|
-
|
407
|
+
answer: ProbabilisticAnswer | None
|
441
408
|
if verify_market and not self.verify_market(market_type, market):
|
442
409
|
logger.info(f"Market '{market.question}' doesn't meet the criteria.")
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
answer = self.answer_binary_market(market)
|
447
|
-
|
448
|
-
if answer is None:
|
449
|
-
logger.info(f"No answer for market '{market.question}'.")
|
450
|
-
self.update_langfuse_trace_by_processed_market(market_type, None)
|
451
|
-
return None
|
410
|
+
answer = None
|
411
|
+
else:
|
412
|
+
answer = self.answer_binary_market(market)
|
452
413
|
|
453
|
-
|
454
|
-
|
455
|
-
market=market,
|
456
|
-
answer=answer,
|
457
|
-
existing_position=existing_position,
|
414
|
+
processed_market = (
|
415
|
+
ProcessedMarket(answer=answer) if answer is not None else None
|
458
416
|
)
|
459
417
|
|
460
|
-
placed_trades = []
|
461
|
-
if self.place_bet:
|
462
|
-
for trade in trades:
|
463
|
-
logger.info(f"Executing trade {trade} on market {market.id}")
|
464
|
-
|
465
|
-
match trade.trade_type:
|
466
|
-
case TradeType.BUY:
|
467
|
-
id = market.buy_tokens(
|
468
|
-
outcome=trade.outcome, amount=trade.amount
|
469
|
-
)
|
470
|
-
case TradeType.SELL:
|
471
|
-
id = market.sell_tokens(
|
472
|
-
outcome=trade.outcome, amount=trade.amount
|
473
|
-
)
|
474
|
-
case _:
|
475
|
-
raise ValueError(f"Unexpected trade type {trade.trade_type}.")
|
476
|
-
placed_trades.append(PlacedTrade.from_trade(trade, id))
|
477
|
-
|
478
|
-
processed_market = ProcessedMarket(answer=answer, trades=placed_trades)
|
479
418
|
self.update_langfuse_trace_by_processed_market(market_type, processed_market)
|
480
|
-
|
481
|
-
|
482
|
-
market_type, market, processed_market=processed_market
|
419
|
+
logger.info(
|
420
|
+
f"Processed market {market.question=} from {market.url=} with {answer=}."
|
483
421
|
)
|
484
|
-
|
485
|
-
logger.info(f"Processed market {market.question=} from {market.url=}.")
|
486
422
|
return processed_market
|
487
423
|
|
488
424
|
def after_process_market(
|
489
425
|
self,
|
490
426
|
market_type: MarketType,
|
491
427
|
market: AgentMarket,
|
492
|
-
processed_market: ProcessedMarket,
|
428
|
+
processed_market: ProcessedMarket | None,
|
493
429
|
) -> None:
|
494
430
|
keys = APIKeys()
|
495
|
-
|
431
|
+
if self.store_prediction:
|
432
|
+
market.store_prediction(processed_market=processed_market, keys=keys)
|
433
|
+
else:
|
434
|
+
logger.info(
|
435
|
+
f"Prediction {processed_market} not stored because {self.store_prediction=}."
|
436
|
+
)
|
496
437
|
|
497
438
|
def before_process_markets(self, market_type: MarketType) -> None:
|
498
439
|
"""
|
@@ -514,7 +455,9 @@ class DeployableTraderAgent(DeployableAgent):
|
|
514
455
|
processed = 0
|
515
456
|
|
516
457
|
for market in available_markets:
|
458
|
+
self.before_process_market(market_type, market)
|
517
459
|
processed_market = self.process_market(market_type, market)
|
460
|
+
self.after_process_market(market_type, market, processed_market)
|
518
461
|
|
519
462
|
if processed_market is not None:
|
520
463
|
processed += 1
|
@@ -528,6 +471,139 @@ class DeployableTraderAgent(DeployableAgent):
|
|
528
471
|
"Executes actions that occur after bets are placed."
|
529
472
|
|
530
473
|
def run(self, market_type: MarketType) -> None:
|
474
|
+
if market_type not in self.supported_markets:
|
475
|
+
raise ValueError(
|
476
|
+
f"Only {self.supported_markets} are supported by this agent."
|
477
|
+
)
|
531
478
|
self.before_process_markets(market_type)
|
532
479
|
self.process_markets(market_type)
|
533
480
|
self.after_process_markets(market_type)
|
481
|
+
|
482
|
+
|
483
|
+
class DeployableTraderAgent(DeployablePredictionAgent):
|
484
|
+
# These markets require place of bet, not just predictions.
|
485
|
+
supported_markets: t.Sequence[MarketType] = [
|
486
|
+
MarketType.OMEN,
|
487
|
+
MarketType.MANIFOLD,
|
488
|
+
MarketType.POLYMARKET,
|
489
|
+
]
|
490
|
+
|
491
|
+
def __init__(
|
492
|
+
self,
|
493
|
+
enable_langfuse: bool = APIKeys().default_enable_langfuse,
|
494
|
+
store_prediction: bool = True,
|
495
|
+
store_trades: bool = True,
|
496
|
+
place_trades: bool = True,
|
497
|
+
) -> None:
|
498
|
+
super().__init__(
|
499
|
+
enable_langfuse=enable_langfuse, store_prediction=store_prediction
|
500
|
+
)
|
501
|
+
self.store_trades = store_trades
|
502
|
+
self.place_trades = place_trades
|
503
|
+
|
504
|
+
def initialize_langfuse(self) -> None:
|
505
|
+
super().initialize_langfuse()
|
506
|
+
# Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
|
507
|
+
self.get_betting_strategy = observe()(self.get_betting_strategy) # type: ignore[method-assign]
|
508
|
+
self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
|
509
|
+
|
510
|
+
def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
|
511
|
+
api_keys = APIKeys()
|
512
|
+
|
513
|
+
# Get the strategy to know how much it will bet.
|
514
|
+
strategy = self.get_betting_strategy(market)
|
515
|
+
# Have a little bandwidth after the bet.
|
516
|
+
min_required_balance_to_trade = strategy.maximum_possible_bet_amount * 1.01
|
517
|
+
|
518
|
+
if market.get_trade_balance(api_keys) < min_required_balance_to_trade:
|
519
|
+
raise OutOfFundsError(
|
520
|
+
f"Minimum required balance {min_required_balance_to_trade} for agent is not met."
|
521
|
+
)
|
522
|
+
|
523
|
+
def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
|
524
|
+
user_id = market.get_user_id(api_keys=APIKeys())
|
525
|
+
|
526
|
+
total_amount = market.get_tiny_bet_amount().amount
|
527
|
+
if existing_position := market.get_position(user_id=user_id):
|
528
|
+
total_amount += existing_position.total_amount.amount
|
529
|
+
|
530
|
+
return MaxAccuracyBettingStrategy(bet_amount=total_amount)
|
531
|
+
|
532
|
+
def build_trades(
|
533
|
+
self,
|
534
|
+
market: AgentMarket,
|
535
|
+
answer: ProbabilisticAnswer,
|
536
|
+
existing_position: Position | None,
|
537
|
+
) -> list[Trade]:
|
538
|
+
strategy = self.get_betting_strategy(market=market)
|
539
|
+
trades = strategy.calculate_trades(existing_position, answer, market)
|
540
|
+
BettingStrategy.assert_trades_currency_match_markets(market, trades)
|
541
|
+
return trades
|
542
|
+
|
543
|
+
def before_process_market(
|
544
|
+
self, market_type: MarketType, market: AgentMarket
|
545
|
+
) -> None:
|
546
|
+
super().before_process_market(market_type, market)
|
547
|
+
self.check_min_required_balance_to_trade(market)
|
548
|
+
|
549
|
+
def process_market(
|
550
|
+
self,
|
551
|
+
market_type: MarketType,
|
552
|
+
market: AgentMarket,
|
553
|
+
verify_market: bool = True,
|
554
|
+
) -> ProcessedTradedMarket | None:
|
555
|
+
processed_market = super().process_market(market_type, market, verify_market)
|
556
|
+
if processed_market is None:
|
557
|
+
return None
|
558
|
+
|
559
|
+
api_keys = APIKeys()
|
560
|
+
existing_position = market.get_position(
|
561
|
+
user_id=market.get_user_id(api_keys=api_keys)
|
562
|
+
)
|
563
|
+
trades = self.build_trades(
|
564
|
+
market=market,
|
565
|
+
answer=processed_market.answer,
|
566
|
+
existing_position=existing_position,
|
567
|
+
)
|
568
|
+
|
569
|
+
placed_trades = []
|
570
|
+
for trade in trades:
|
571
|
+
logger.info(f"Executing trade {trade} on market {market.id} ({market.url})")
|
572
|
+
|
573
|
+
if self.place_trades:
|
574
|
+
match trade.trade_type:
|
575
|
+
case TradeType.BUY:
|
576
|
+
id = market.buy_tokens(
|
577
|
+
outcome=trade.outcome, amount=trade.amount
|
578
|
+
)
|
579
|
+
case TradeType.SELL:
|
580
|
+
id = market.sell_tokens(
|
581
|
+
outcome=trade.outcome, amount=trade.amount
|
582
|
+
)
|
583
|
+
case _:
|
584
|
+
raise ValueError(f"Unexpected trade type {trade.trade_type}.")
|
585
|
+
placed_trades.append(PlacedTrade.from_trade(trade, id))
|
586
|
+
else:
|
587
|
+
logger.info(f"Trade execution skipped because {self.place_trades=}.")
|
588
|
+
|
589
|
+
traded_market = ProcessedTradedMarket(
|
590
|
+
answer=processed_market.answer, trades=placed_trades
|
591
|
+
)
|
592
|
+
logger.info(f"Traded market {market.question=} from {market.url=}.")
|
593
|
+
return traded_market
|
594
|
+
|
595
|
+
def after_process_market(
|
596
|
+
self,
|
597
|
+
market_type: MarketType,
|
598
|
+
market: AgentMarket,
|
599
|
+
processed_market: ProcessedMarket | None,
|
600
|
+
) -> None:
|
601
|
+
api_keys = APIKeys()
|
602
|
+
super().after_process_market(market_type, market, processed_market)
|
603
|
+
if isinstance(processed_market, ProcessedTradedMarket):
|
604
|
+
if self.store_trades:
|
605
|
+
market.store_trades(processed_market, api_keys)
|
606
|
+
else:
|
607
|
+
logger.info(
|
608
|
+
f"Trades {processed_market.trades} not stored because {self.store_trades=}."
|
609
|
+
)
|
@@ -24,6 +24,10 @@ from prediction_market_agent_tooling.tools.betting_strategies.utils import Simpl
|
|
24
24
|
from prediction_market_agent_tooling.tools.utils import check_not_none
|
25
25
|
|
26
26
|
|
27
|
+
class GuaranteedLossError(RuntimeError):
|
28
|
+
pass
|
29
|
+
|
30
|
+
|
27
31
|
class BettingStrategy(ABC):
|
28
32
|
@abstractmethod
|
29
33
|
def calculate_trades(
|
@@ -63,7 +67,7 @@ class BettingStrategy(ABC):
|
|
63
67
|
)
|
64
68
|
|
65
69
|
if outcome_tokens_to_get.amount < trade.amount.amount:
|
66
|
-
raise
|
70
|
+
raise GuaranteedLossError(
|
67
71
|
f"Trade {trade=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}."
|
68
72
|
)
|
69
73
|
|
@@ -29,6 +29,9 @@ from prediction_market_agent_tooling.tools.utils import (
|
|
29
29
|
|
30
30
|
class ProcessedMarket(BaseModel):
|
31
31
|
answer: ProbabilisticAnswer
|
32
|
+
|
33
|
+
|
34
|
+
class ProcessedTradedMarket(ProcessedMarket):
|
32
35
|
trades: list[PlacedTrade]
|
33
36
|
|
34
37
|
|
@@ -228,13 +231,21 @@ class AgentMarket(BaseModel):
|
|
228
231
|
raise NotImplementedError("Subclasses must implement this method")
|
229
232
|
|
230
233
|
def store_prediction(
|
231
|
-
self, processed_market: ProcessedMarket, keys: APIKeys
|
234
|
+
self, processed_market: ProcessedMarket | None, keys: APIKeys
|
232
235
|
) -> None:
|
233
236
|
"""
|
234
237
|
If market allows to upload predictions somewhere, implement it in this method.
|
235
238
|
"""
|
236
239
|
raise NotImplementedError("Subclasses must implement this method")
|
237
240
|
|
241
|
+
def store_trades(
|
242
|
+
self, traded_market: ProcessedTradedMarket | None, keys: APIKeys
|
243
|
+
) -> None:
|
244
|
+
"""
|
245
|
+
If market allows to upload trades somewhere, implement it in this method.
|
246
|
+
"""
|
247
|
+
raise NotImplementedError("Subclasses must implement this method")
|
248
|
+
|
238
249
|
@staticmethod
|
239
250
|
def get_bets_made_since(
|
240
251
|
better_address: ChecksumAddress, start_time: DatetimeUTC
|
@@ -56,6 +56,9 @@ class Question(BaseModel):
|
|
56
56
|
my_forecasts: MyAggregation
|
57
57
|
type: QuestionType
|
58
58
|
possibilities: dict[str, str] | None
|
59
|
+
description: str
|
60
|
+
fine_print: str
|
61
|
+
resolution_criteria: str
|
59
62
|
|
60
63
|
|
61
64
|
class MetaculusQuestion(BaseModel):
|
@@ -64,7 +67,7 @@ class MetaculusQuestion(BaseModel):
|
|
64
67
|
author_username: str
|
65
68
|
title: str
|
66
69
|
created_at: DatetimeUTC
|
67
|
-
published_at: DatetimeUTC
|
70
|
+
published_at: DatetimeUTC | None
|
68
71
|
scheduled_close_time: DatetimeUTC
|
69
72
|
scheduled_resolve_time: DatetimeUTC
|
70
73
|
user_permission: str
|
@@ -1,11 +1,11 @@
|
|
1
1
|
import typing as t
|
2
2
|
|
3
3
|
from prediction_market_agent_tooling.config import APIKeys
|
4
|
-
from prediction_market_agent_tooling.gtypes import Probability
|
5
4
|
from prediction_market_agent_tooling.markets.agent_market import (
|
6
5
|
AgentMarket,
|
7
6
|
FilterBy,
|
8
7
|
MarketFees,
|
8
|
+
ProcessedMarket,
|
9
9
|
SortBy,
|
10
10
|
)
|
11
11
|
from prediction_market_agent_tooling.markets.metaculus.api import (
|
@@ -17,7 +17,7 @@ from prediction_market_agent_tooling.markets.metaculus.api import (
|
|
17
17
|
from prediction_market_agent_tooling.markets.metaculus.data_models import (
|
18
18
|
MetaculusQuestion,
|
19
19
|
)
|
20
|
-
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
20
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC, check_not_none
|
21
21
|
|
22
22
|
|
23
23
|
class MetaculusAgentMarket(AgentMarket):
|
@@ -27,9 +27,9 @@ class MetaculusAgentMarket(AgentMarket):
|
|
27
27
|
|
28
28
|
have_predicted: bool
|
29
29
|
base_url: t.ClassVar[str] = METACULUS_API_BASE_URL
|
30
|
-
description: str
|
31
|
-
|
32
|
-
|
30
|
+
description: str
|
31
|
+
fine_print: str
|
32
|
+
resolution_criteria: str
|
33
33
|
fees: MarketFees = MarketFees.get_zero_fees() # No fees on Metaculus.
|
34
34
|
|
35
35
|
@staticmethod
|
@@ -46,6 +46,9 @@ class MetaculusAgentMarket(AgentMarket):
|
|
46
46
|
volume=None,
|
47
47
|
have_predicted=model.question.my_forecasts.latest is not None,
|
48
48
|
outcome_token_pool=None,
|
49
|
+
description=model.question.description,
|
50
|
+
fine_print=model.question.fine_print,
|
51
|
+
resolution_criteria=model.question.resolution_criteria,
|
49
52
|
)
|
50
53
|
|
51
54
|
@staticmethod
|
@@ -103,10 +106,29 @@ class MetaculusAgentMarket(AgentMarket):
|
|
103
106
|
break
|
104
107
|
return [MetaculusAgentMarket.from_data_model(q) for q in all_questions[:limit]]
|
105
108
|
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
+
def store_prediction(
|
110
|
+
self, processed_market: ProcessedMarket | None, keys: APIKeys
|
111
|
+
) -> None:
|
112
|
+
if processed_market is not None:
|
113
|
+
make_prediction(self.id, processed_market.answer.p_yes)
|
114
|
+
post_question_comment(
|
115
|
+
self.id,
|
116
|
+
check_not_none(
|
117
|
+
processed_market.answer.reasoning,
|
118
|
+
"Reasoning must be provided for Metaculus.",
|
119
|
+
),
|
120
|
+
)
|
109
121
|
|
110
122
|
@staticmethod
|
111
123
|
def get_user_id(api_keys: APIKeys) -> str:
|
112
124
|
return str(api_keys.metaculus_user_id)
|
125
|
+
|
126
|
+
@staticmethod
|
127
|
+
def verify_operational_balance(api_keys: APIKeys) -> bool:
|
128
|
+
# No operational balance for Metaculus.
|
129
|
+
return True
|
130
|
+
|
131
|
+
@staticmethod
|
132
|
+
def redeem_winnings(api_keys: APIKeys) -> None:
|
133
|
+
# Nothing to redeem on Metaculus.
|
134
|
+
pass
|
@@ -25,6 +25,7 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
25
25
|
FilterBy,
|
26
26
|
MarketFees,
|
27
27
|
ProcessedMarket,
|
28
|
+
ProcessedTradedMarket,
|
28
29
|
SortBy,
|
29
30
|
)
|
30
31
|
from prediction_market_agent_tooling.markets.data_models import (
|
@@ -417,12 +418,19 @@ class OmenAgentMarket(AgentMarket):
|
|
417
418
|
) > xdai_type(0.001)
|
418
419
|
|
419
420
|
def store_prediction(
|
420
|
-
self, processed_market: ProcessedMarket, keys: APIKeys
|
421
|
+
self, processed_market: ProcessedMarket | None, keys: APIKeys
|
421
422
|
) -> None:
|
423
|
+
"""On Omen, we have to store predictions along with trades, see `store_trades`."""
|
424
|
+
|
425
|
+
def store_trades(
|
426
|
+
self, traded_market: ProcessedTradedMarket | None, keys: APIKeys
|
427
|
+
) -> None:
|
428
|
+
if traded_market is None:
|
429
|
+
logger.warning(f"No prediction for market {self.id}, not storing anything.")
|
430
|
+
return
|
431
|
+
|
422
432
|
reasoning = (
|
423
|
-
|
424
|
-
if processed_market.answer.reasoning
|
425
|
-
else ""
|
433
|
+
traded_market.answer.reasoning if traded_market.answer.reasoning else ""
|
426
434
|
)
|
427
435
|
|
428
436
|
ipfs_hash_decoded = HexBytes(HASH_ZERO)
|
@@ -434,13 +442,13 @@ class OmenAgentMarket(AgentMarket):
|
|
434
442
|
ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)
|
435
443
|
|
436
444
|
tx_hashes = [
|
437
|
-
HexBytes(HexStr(i.id)) for i in
|
445
|
+
HexBytes(HexStr(i.id)) for i in traded_market.trades if i.id is not None
|
438
446
|
]
|
439
447
|
prediction = ContractPrediction(
|
440
448
|
publisher=keys.public_key,
|
441
449
|
ipfs_hash=ipfs_hash_decoded,
|
442
450
|
tx_hashes=tx_hashes,
|
443
|
-
estimated_probability_bps=int(
|
451
|
+
estimated_probability_bps=int(traded_market.answer.p_yes * 10000),
|
444
452
|
)
|
445
453
|
tx_receipt = OmenAgentResultMappingContract().add_prediction(
|
446
454
|
api_keys=keys,
|
@@ -24,7 +24,7 @@ QUESTION_IS_INVALID_PROMPT = """Main signs about an invalid question (sometimes
|
|
24
24
|
- The violent event can be caused by a single conscious being.
|
25
25
|
- The violent event is done illegally.
|
26
26
|
- The market should not directly incentivize immoral violent (such as murder, rape or unjust imprisonment) actions which could likely be performed by any participant.
|
27
|
-
- Invalid: Will Donald Trump be alive on the 01/12/2021? (Anyone could bet on
|
27
|
+
- Invalid: Will Donald Trump be alive on the 01/12/2021? (Anyone could bet on "No" and kill him for a guaranteed profit. Anyone could bet on "Yes" to effectively put a bounty on his head).
|
28
28
|
- Invalid: Will Hera be a victim of swatting in 2020? (Anyone could falsely call the emergency services on him in order to win the bet)
|
29
29
|
- This does not prevent markets:
|
30
30
|
- Whose topics are violent events not caused by conscious beings.
|
@@ -35,9 +35,10 @@ QUESTION_IS_INVALID_PROMPT = """Main signs about an invalid question (sometimes
|
|
35
35
|
- Valid: Will the US be engaged in a military conflict with a UN member state in 2021? (It’s unlikely for the US to declare war in order to win a bet on this market).
|
36
36
|
- Valid: Will Derek Chauvin go to jail for the murder of George Flyod? (It’s unlikely that the jurors would collude to make a wrong verdict in order to win this market).
|
37
37
|
- Questions with relative dates will resolve as invalid. Dates must be stated in absolute terms, not relative depending on the current time.
|
38
|
-
- Invalid: Who will be the president of the United States in 6 months? (
|
38
|
+
- Invalid: Who will be the president of the United States in 6 months? ("in 6 months depends on the current time").
|
39
|
+
- Invalid: In the next 14 days, will Gnosis Chain gain another 1M users? ("in the next 14 days depends on the current time").
|
39
40
|
- Questions about moral values and not facts will be resolved as invalid.
|
40
|
-
- Invalid:
|
41
|
+
- Invalid: "Is it ethical to eat meat?".
|
41
42
|
|
42
43
|
Follow a chain of thought to evaluate if the question is invalid:
|
43
44
|
|
@@ -33,6 +33,11 @@ def tavily_search(
|
|
33
33
|
|
34
34
|
Argument default values are different from the original method, to return everything by default, because it can be handy in the future and it doesn't increase the costs.
|
35
35
|
"""
|
36
|
+
if topic == "news" and days is None:
|
37
|
+
raise ValueError("When topic is 'news', days must be an integer")
|
38
|
+
if topic == "general" and days is not None:
|
39
|
+
raise ValueError("When topic is 'general', days must be None")
|
40
|
+
|
36
41
|
if tavily_storage and (
|
37
42
|
response_parsed := tavily_storage.find(
|
38
43
|
query=query,
|
@@ -103,11 +108,15 @@ def _tavily_search(
|
|
103
108
|
tavily = TavilyClient(
|
104
109
|
api_key=(api_keys or APIKeys()).tavily_api_key.get_secret_value()
|
105
110
|
)
|
111
|
+
|
112
|
+
# Optional `days` arg can only be specified if not None, otherwise Tavily
|
113
|
+
# will throw an error
|
114
|
+
kwargs = {"days": days} if days else {}
|
115
|
+
|
106
116
|
response: dict[str, t.Any] = tavily.search(
|
107
117
|
query=query,
|
108
118
|
search_depth=search_depth,
|
109
119
|
topic=topic,
|
110
|
-
days=days,
|
111
120
|
max_results=max_results,
|
112
121
|
include_domains=include_domains,
|
113
122
|
exclude_domains=exclude_domains,
|
@@ -115,6 +124,7 @@ def _tavily_search(
|
|
115
124
|
include_raw_content=include_raw_content,
|
116
125
|
include_images=include_images,
|
117
126
|
use_cache=use_cache,
|
127
|
+
**kwargs,
|
118
128
|
)
|
119
129
|
return response
|
120
130
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: prediction-market-agent-tooling
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.55.0
|
4
4
|
Summary: Tools to benchmark, deploy and monitor prediction market agents.
|
5
5
|
Author: Gnosis
|
6
6
|
Requires-Python: >=3.10,<3.12
|
@@ -44,7 +44,7 @@ Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
|
|
44
44
|
Requires-Dist: streamlit (>=1.31.0,<2.0.0)
|
45
45
|
Requires-Dist: subgrounds (>=1.9.1,<2.0.0)
|
46
46
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
47
|
-
Requires-Dist: tavily-python (>=0.
|
47
|
+
Requires-Dist: tavily-python (>=0.5.0,<0.6.0)
|
48
48
|
Requires-Dist: tqdm (>=4.66.2,<5.0.0)
|
49
49
|
Requires-Dist: typer (>=0.9.0,<1.0.0)
|
50
50
|
Requires-Dist: types-python-dateutil (>=2.9.0.20240906,<3.0.0.0)
|
@@ -17,9 +17,9 @@ prediction_market_agent_tooling/benchmark/agents.py,sha256=B1-uWdyeN4GGKMWGK_-Cc
|
|
17
17
|
prediction_market_agent_tooling/benchmark/benchmark.py,sha256=MqTiaaJ3cYiOLUVR7OyImLWxcEya3Rl5JyFYW-K0lwM,17097
|
18
18
|
prediction_market_agent_tooling/benchmark/utils.py,sha256=D0MfUkVZllmvcU0VOurk9tcKT7JTtwwOp-63zuCBVuc,2880
|
19
19
|
prediction_market_agent_tooling/config.py,sha256=WC30Nr16RGueTafA9i67OIB-6KDHZRryhiLPzebg9_I,6740
|
20
|
-
prediction_market_agent_tooling/deploy/agent.py,sha256=
|
20
|
+
prediction_market_agent_tooling/deploy/agent.py,sha256=1a1VPaCA77MhK9wQwX1MjEycareP_NYfsm73YFXwyxY,22222
|
21
21
|
prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
|
22
|
-
prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=
|
22
|
+
prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=kMrIE3wMv_IB6nJd_1DmDXDkEZhsXFOgyTd7JZ0gqHI,13068
|
23
23
|
prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
|
24
24
|
prediction_market_agent_tooling/deploy/gcp/deploy.py,sha256=CYUgnfy-9XVk04kkxA_5yp0GE9Mw5caYqlFUZQ2j3ks,3739
|
25
25
|
prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py,sha256=OsPboCFGiZKsvGyntGZHwdqPlLTthITkNF5rJFvGgU8,2582
|
@@ -30,7 +30,7 @@ prediction_market_agent_tooling/jobs/jobs.py,sha256=I07yh0GJ-xhlvQaOUQB8xlSnihhc
|
|
30
30
|
prediction_market_agent_tooling/jobs/jobs_models.py,sha256=I5uBTHJ2S1Wi3H4jDxxU7nsswSIP9r3BevHmljLh5Pg,1370
|
31
31
|
prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=I2_vGrEJj1reSI8M377ab5QCsYNp_l4l4QeYEmDBkFM,3989
|
32
32
|
prediction_market_agent_tooling/loggers.py,sha256=Am6HHXRNO545BO3l7Ue9Wb2TkYE1OK8KKhGbI3XypVU,3751
|
33
|
-
prediction_market_agent_tooling/markets/agent_market.py,sha256=
|
33
|
+
prediction_market_agent_tooling/markets/agent_market.py,sha256=OgB6bvDGfTAxbh6cDGD3XFO0iy0MAaOQvXEP6nw8xW8,12817
|
34
34
|
prediction_market_agent_tooling/markets/categorize.py,sha256=jsoHWvZk9pU6n17oWSCcCxNNYVwlb_NXsZxKRI7vmsk,1301
|
35
35
|
prediction_market_agent_tooling/markets/data_models.py,sha256=jMqrSFO_w2z-5N3PFVgZqTHdVdkzSDhhzky2lHsGGKA,3621
|
36
36
|
prediction_market_agent_tooling/markets/manifold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -41,11 +41,11 @@ prediction_market_agent_tooling/markets/manifold/utils.py,sha256=cPPFWXm3vCYH1jy
|
|
41
41
|
prediction_market_agent_tooling/markets/market_fees.py,sha256=Q64T9uaJx0Vllt0BkrPmpMEz53ra-hMVY8Czi7CEP7s,1227
|
42
42
|
prediction_market_agent_tooling/markets/markets.py,sha256=mwubc567OIlA32YKqlIdTloYV8FGJia9gPv0wE0xUEA,3368
|
43
43
|
prediction_market_agent_tooling/markets/metaculus/api.py,sha256=4TRPGytQQbSdf42DCg2M_JWYPAuNjqZ3eBqaQBLkNks,2736
|
44
|
-
prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=
|
45
|
-
prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=
|
44
|
+
prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=Suxa7xELdYuFNKqvGvFh8qyfVtAg79E-vaQ6dqNZOtA,3261
|
45
|
+
prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=E_TUf5q73lWzdMp40Ne-3w4MjEd7AHcaif4pvFh9FMU,4360
|
46
46
|
prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
47
|
prediction_market_agent_tooling/markets/omen/data_models.py,sha256=nCjsc-ylIzQOCK_1BW-5NoYrS-NIXz2Hg9N1-IqhhC8,27516
|
48
|
-
prediction_market_agent_tooling/markets/omen/omen.py,sha256
|
48
|
+
prediction_market_agent_tooling/markets/omen/omen.py,sha256=LqNZjngo6LoktKecfYmGmJJ9D5rj-s0Poy4x4_GZfp0,51116
|
49
49
|
prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=Zq7SncCq-hvpgXKsVruGBGCn1OhKZTe7r1qLdCTrT2w,28297
|
50
50
|
prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=iDWdjICGkt968exwCjY-6nsnQyrrNAg3YjnDdP430GQ,9415
|
51
51
|
prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=zQH3iu0SVH1RmE-W3NMEpcKVMILXJYxMhL6w1wh5RUo,37348
|
@@ -79,7 +79,7 @@ prediction_market_agent_tooling/tools/httpx_cached_client.py,sha256=0-N1r0zcGKlY
|
|
79
79
|
prediction_market_agent_tooling/tools/image_gen/image_gen.py,sha256=HzRwBx62hOXBOmrtpkXaP9Qq1Ku03uUGdREocyjLQ_k,1266
|
80
80
|
prediction_market_agent_tooling/tools/image_gen/market_thumbnail_gen.py,sha256=8A3U2uxsCsOfLjru-6R_PPIAuiKY4qFkWp_GSBPV6-s,1280
|
81
81
|
prediction_market_agent_tooling/tools/ipfs/ipfs_handler.py,sha256=CTTMfTvs_8PH4kAtlQby2aeEKwgpmxtuGbd4oYIdJ2A,1201
|
82
|
-
prediction_market_agent_tooling/tools/is_invalid.py,sha256=
|
82
|
+
prediction_market_agent_tooling/tools/is_invalid.py,sha256=VjjOCrkt6S8ytOg_0s2gUL8IKX2muWq1QLIZX0MPMxY,5094
|
83
83
|
prediction_market_agent_tooling/tools/is_predictable.py,sha256=NIoR2bTNMmADcyNY2aKNMWkiDw7Z_9kZMcFXEdyewy4,6771
|
84
84
|
prediction_market_agent_tooling/tools/langfuse_.py,sha256=jI_4ROxqo41CCnWGS1vN_AeDVhRzLMaQLxH3kxDu3L8,1153
|
85
85
|
prediction_market_agent_tooling/tools/langfuse_client_utils.py,sha256=B0PhAQyviFnVbtOCYMxYmcCn66cu9nbqAOIAZcdgiRI,5771
|
@@ -89,12 +89,12 @@ prediction_market_agent_tooling/tools/safe.py,sha256=h0xOO0eNtitClf0fPkn-0oTc6A_
|
|
89
89
|
prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0rnVhtQGzwoUdAgn_7u_GBM,729
|
90
90
|
prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
|
91
91
|
prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=Rz4tZzwCRzPaq49SFT33SCRQrqHXtqWdD9ajb2tGCWc,2723
|
92
|
-
prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=
|
92
|
+
prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=inGgWo_TFLA7Q0agYx2MYdEsh659oIdovQMniiG_Q20,4895
|
93
93
|
prediction_market_agent_tooling/tools/tavily/tavily_storage.py,sha256=t-tZzbCzBBdFedRZDuVBn3A3mIDX8Z5wza6SxWswu_E,4093
|
94
94
|
prediction_market_agent_tooling/tools/utils.py,sha256=W-9SqeCKd51BYMRhDjYPQ7lfNO_zE9EvYpmu2r5WXGA,7163
|
95
95
|
prediction_market_agent_tooling/tools/web3_utils.py,sha256=dkcjG-LtuaWRh7WEMzRGmZ5B5rsxZTlliFOI6fj-EJ8,11842
|
96
|
-
prediction_market_agent_tooling-0.
|
97
|
-
prediction_market_agent_tooling-0.
|
98
|
-
prediction_market_agent_tooling-0.
|
99
|
-
prediction_market_agent_tooling-0.
|
100
|
-
prediction_market_agent_tooling-0.
|
96
|
+
prediction_market_agent_tooling-0.55.0.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
97
|
+
prediction_market_agent_tooling-0.55.0.dist-info/METADATA,sha256=2a-TbhiR5XyBGScQZDd1pV1vzC-LBWt7zFa6WorpmD8,8056
|
98
|
+
prediction_market_agent_tooling-0.55.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
99
|
+
prediction_market_agent_tooling-0.55.0.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
|
100
|
+
prediction_market_agent_tooling-0.55.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|