prediction-market-agent-tooling 0.64.12.dev659__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.
- prediction_market_agent_tooling/benchmark/agents.py +19 -16
- prediction_market_agent_tooling/benchmark/benchmark.py +94 -84
- prediction_market_agent_tooling/benchmark/utils.py +8 -9
- prediction_market_agent_tooling/chains.py +4 -0
- prediction_market_agent_tooling/config.py +4 -3
- prediction_market_agent_tooling/deploy/agent.py +85 -125
- prediction_market_agent_tooling/deploy/agent_example.py +20 -10
- prediction_market_agent_tooling/deploy/betting_strategy.py +222 -96
- prediction_market_agent_tooling/deploy/constants.py +4 -0
- prediction_market_agent_tooling/jobs/jobs_models.py +15 -4
- prediction_market_agent_tooling/jobs/omen/omen_jobs.py +3 -3
- prediction_market_agent_tooling/markets/agent_market.py +145 -50
- prediction_market_agent_tooling/markets/blockchain_utils.py +10 -1
- prediction_market_agent_tooling/markets/data_models.py +83 -17
- prediction_market_agent_tooling/markets/manifold/api.py +18 -7
- prediction_market_agent_tooling/markets/manifold/data_models.py +23 -16
- prediction_market_agent_tooling/markets/manifold/manifold.py +18 -18
- prediction_market_agent_tooling/markets/manifold/utils.py +7 -12
- prediction_market_agent_tooling/markets/markets.py +2 -1
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +29 -4
- prediction_market_agent_tooling/markets/omen/data_models.py +17 -32
- prediction_market_agent_tooling/markets/omen/omen.py +65 -108
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +2 -5
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +13 -13
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +18 -12
- prediction_market_agent_tooling/markets/polymarket/data_models.py +7 -3
- prediction_market_agent_tooling/markets/polymarket/data_models_web.py +7 -3
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +5 -4
- prediction_market_agent_tooling/markets/seer/data_models.py +0 -83
- prediction_market_agent_tooling/markets/seer/price_manager.py +44 -30
- prediction_market_agent_tooling/markets/seer/seer.py +105 -105
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +34 -41
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +1 -1
- prediction_market_agent_tooling/tools/cow/cow_order.py +10 -3
- prediction_market_agent_tooling/tools/is_predictable.py +2 -3
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -4
- prediction_market_agent_tooling/tools/omen/sell_positions.py +3 -2
- prediction_market_agent_tooling/tools/utils.py +26 -13
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/METADATA +2 -2
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/RECORD +43 -52
- prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py +0 -68
- prediction_market_agent_tooling/monitor/markets/manifold.py +0 -90
- prediction_market_agent_tooling/monitor/markets/metaculus.py +0 -43
- prediction_market_agent_tooling/monitor/markets/omen.py +0 -88
- prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -49
- prediction_market_agent_tooling/monitor/monitor.py +0 -406
- prediction_market_agent_tooling/monitor/monitor_app.py +0 -149
- prediction_market_agent_tooling/monitor/monitor_settings.py +0 -27
- prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -146
- prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -12
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.64.12.dev659.dist-info → prediction_market_agent_tooling-0.65.0.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,9 @@
|
|
1
1
|
import typing as t
|
2
|
+
from collections import defaultdict
|
2
3
|
from datetime import timedelta
|
3
4
|
|
4
5
|
import tenacity
|
6
|
+
from tqdm import tqdm
|
5
7
|
from web3 import Web3
|
6
8
|
|
7
9
|
from prediction_market_agent_tooling.config import APIKeys
|
@@ -43,8 +45,6 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
|
|
43
45
|
OmenBet,
|
44
46
|
OmenMarket,
|
45
47
|
OmenUserPosition,
|
46
|
-
get_bet_outcome,
|
47
|
-
get_boolean_outcome,
|
48
48
|
)
|
49
49
|
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
50
50
|
OMEN_DEFAULT_MARKET_FEE_PERC,
|
@@ -59,7 +59,6 @@ from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
|
59
59
|
build_parent_collection_id,
|
60
60
|
)
|
61
61
|
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
62
|
-
SAFE_COLLATERAL_TOKENS_ADDRESSES,
|
63
62
|
OmenSubgraphHandler,
|
64
63
|
)
|
65
64
|
from prediction_market_agent_tooling.tools.balances import get_balances
|
@@ -160,14 +159,14 @@ class OmenAgentMarket(AgentMarket):
|
|
160
159
|
|
161
160
|
def liquidate_existing_positions(
|
162
161
|
self,
|
163
|
-
bet_outcome:
|
162
|
+
bet_outcome: OutcomeStr,
|
164
163
|
web3: Web3 | None = None,
|
165
164
|
api_keys: APIKeys | None = None,
|
166
165
|
larger_than: OutcomeToken | None = None,
|
167
166
|
) -> None:
|
168
167
|
"""
|
169
168
|
Liquidates all previously existing positions.
|
170
|
-
|
169
|
+
|
171
170
|
"""
|
172
171
|
api_keys = api_keys if api_keys is not None else APIKeys()
|
173
172
|
better_address = api_keys.bet_from_address
|
@@ -180,10 +179,9 @@ class OmenAgentMarket(AgentMarket):
|
|
180
179
|
|
181
180
|
for prev_position in prev_positions_for_market:
|
182
181
|
for position_outcome, token_amount in prev_position.amounts_ot.items():
|
183
|
-
|
184
|
-
if position_outcome_bool != bet_outcome:
|
182
|
+
if position_outcome != bet_outcome:
|
185
183
|
self.sell_tokens(
|
186
|
-
outcome=
|
184
|
+
outcome=position_outcome,
|
187
185
|
amount=token_amount,
|
188
186
|
auto_withdraw=True,
|
189
187
|
web3=web3,
|
@@ -192,7 +190,7 @@ class OmenAgentMarket(AgentMarket):
|
|
192
190
|
|
193
191
|
def place_bet(
|
194
192
|
self,
|
195
|
-
outcome:
|
193
|
+
outcome: OutcomeStr,
|
196
194
|
amount: USD,
|
197
195
|
auto_deposit: bool = True,
|
198
196
|
web3: Web3 | None = None,
|
@@ -206,14 +204,14 @@ class OmenAgentMarket(AgentMarket):
|
|
206
204
|
api_keys=api_keys if api_keys is not None else APIKeys(),
|
207
205
|
amount=amount,
|
208
206
|
market=self,
|
209
|
-
|
207
|
+
outcome=outcome,
|
210
208
|
auto_deposit=auto_deposit,
|
211
209
|
web3=web3,
|
212
210
|
)
|
213
211
|
|
214
212
|
def buy_tokens(
|
215
213
|
self,
|
216
|
-
outcome:
|
214
|
+
outcome: OutcomeStr,
|
217
215
|
amount: USD,
|
218
216
|
web3: Web3 | None = None,
|
219
217
|
api_keys: APIKeys | None = None,
|
@@ -226,7 +224,7 @@ class OmenAgentMarket(AgentMarket):
|
|
226
224
|
)
|
227
225
|
|
228
226
|
def get_sell_value_of_outcome_token(
|
229
|
-
self, outcome:
|
227
|
+
self, outcome: OutcomeStr, amount: OutcomeToken, web3: Web3 | None = None
|
230
228
|
) -> CollateralToken:
|
231
229
|
"""
|
232
230
|
Market can have as collateral token GNO for example.
|
@@ -234,19 +232,15 @@ class OmenAgentMarket(AgentMarket):
|
|
234
232
|
When selling, you need to provide the amount in GNO, which is cumbersome because you know how much shares you have, but you don't have the price of the shares in GNO.
|
235
233
|
Use this to convert how much collateral token (GNO in our example) to sell, to get the amount of shares you want to sell.
|
236
234
|
"""
|
237
|
-
outcome_bool = get_boolean_outcome(outcome)
|
238
235
|
|
239
236
|
pool_balance = get_conditional_tokens_balance_for_market(
|
240
237
|
self, self.market_maker_contract_address_checksummed, web3=web3
|
241
238
|
)
|
242
|
-
|
243
|
-
sell_str = self.outcomes[self.yes_index if outcome_bool else self.no_index]
|
244
|
-
other_str = self.outcomes[self.no_index if outcome_bool else self.yes_index]
|
245
|
-
|
239
|
+
outcome_idx = self.index_set_to_outcome_index(self.get_index_set(outcome))
|
246
240
|
collateral = calculate_sell_amount_in_collateral(
|
247
241
|
shares_to_sell=amount,
|
248
|
-
|
249
|
-
|
242
|
+
outcome_index=outcome_idx,
|
243
|
+
pool_balances=[x.as_outcome_token for x in pool_balance.values()],
|
250
244
|
fees=self.fees,
|
251
245
|
)
|
252
246
|
|
@@ -254,7 +248,7 @@ class OmenAgentMarket(AgentMarket):
|
|
254
248
|
|
255
249
|
def sell_tokens(
|
256
250
|
self,
|
257
|
-
outcome:
|
251
|
+
outcome: OutcomeStr,
|
258
252
|
amount: USD | OutcomeToken,
|
259
253
|
auto_withdraw: bool = True,
|
260
254
|
api_keys: APIKeys | None = None,
|
@@ -268,7 +262,7 @@ class OmenAgentMarket(AgentMarket):
|
|
268
262
|
amount=amount,
|
269
263
|
api_keys=api_keys if api_keys is not None else APIKeys(),
|
270
264
|
market=self,
|
271
|
-
|
265
|
+
outcome=outcome,
|
272
266
|
auto_withdraw=auto_withdraw,
|
273
267
|
web3=web3,
|
274
268
|
)
|
@@ -347,7 +341,6 @@ class OmenAgentMarket(AgentMarket):
|
|
347
341
|
resolution=model.get_resolution_enum(),
|
348
342
|
created_time=model.creation_datetime,
|
349
343
|
finalized_time=model.finalized_datetime,
|
350
|
-
current_p_yes=model.current_p_yes,
|
351
344
|
condition=model.condition,
|
352
345
|
url=model.url,
|
353
346
|
volume=model.collateralVolume.as_token,
|
@@ -362,24 +355,30 @@ class OmenAgentMarket(AgentMarket):
|
|
362
355
|
model.outcomes[i]: model.outcomeTokenAmounts[i].as_outcome_token
|
363
356
|
for i in range(len(model.outcomes))
|
364
357
|
},
|
358
|
+
probabilities=AgentMarket.build_probability_map(
|
359
|
+
outcome_token_amounts=model.outcomeTokenAmounts,
|
360
|
+
outcomes=list(model.outcomes),
|
361
|
+
),
|
365
362
|
)
|
366
363
|
|
367
364
|
@staticmethod
|
368
|
-
def
|
365
|
+
def get_markets(
|
369
366
|
limit: int,
|
370
367
|
sort_by: SortBy,
|
371
368
|
filter_by: FilterBy = FilterBy.OPEN,
|
372
369
|
created_after: t.Optional[DatetimeUTC] = None,
|
373
370
|
excluded_questions: set[str] | None = None,
|
371
|
+
fetch_categorical_markets: bool = False,
|
374
372
|
) -> t.Sequence["OmenAgentMarket"]:
|
375
373
|
return [
|
376
374
|
OmenAgentMarket.from_data_model(m)
|
377
|
-
for m in OmenSubgraphHandler().
|
375
|
+
for m in OmenSubgraphHandler().get_omen_markets_simple(
|
378
376
|
limit=limit,
|
379
377
|
sort_by=sort_by,
|
380
378
|
filter_by=filter_by,
|
381
379
|
created_after=created_after,
|
382
380
|
excluded_questions=excluded_questions,
|
381
|
+
include_categorical_markets=fetch_categorical_markets,
|
383
382
|
)
|
384
383
|
]
|
385
384
|
|
@@ -471,7 +470,7 @@ class OmenAgentMarket(AgentMarket):
|
|
471
470
|
address=self.market_maker_contract_address_checksummed,
|
472
471
|
)
|
473
472
|
|
474
|
-
def get_index_set(self, outcome:
|
473
|
+
def get_index_set(self, outcome: OutcomeStr) -> int:
|
475
474
|
return self.get_outcome_index(outcome) + 1
|
476
475
|
|
477
476
|
def index_set_to_outcome_index(cls, index_set: int) -> int:
|
@@ -482,13 +481,14 @@ class OmenAgentMarket(AgentMarket):
|
|
482
481
|
cls.get_outcome_str(cls.index_set_to_outcome_index(index_set))
|
483
482
|
)
|
484
483
|
|
485
|
-
|
484
|
+
@staticmethod
|
485
|
+
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
|
486
486
|
return (
|
487
487
|
OutcomeStr(OMEN_TRUE_OUTCOME) if outcome else OutcomeStr(OMEN_FALSE_OUTCOME)
|
488
488
|
)
|
489
489
|
|
490
490
|
def get_token_balance(
|
491
|
-
self, user_id: str, outcome:
|
491
|
+
self, user_id: str, outcome: OutcomeStr, web3: Web3 | None = None
|
492
492
|
) -> OutcomeToken:
|
493
493
|
index_set = self.get_index_set(outcome)
|
494
494
|
balances = get_conditional_tokens_balance_for_market(
|
@@ -522,16 +522,20 @@ class OmenAgentMarket(AgentMarket):
|
|
522
522
|
)
|
523
523
|
|
524
524
|
# Sort positions and corresponding markets by condition_id
|
525
|
-
omen_positions_dict: dict[HexBytes, list[OmenUserPosition]] =
|
525
|
+
omen_positions_dict: dict[HexBytes, list[OmenUserPosition]] = defaultdict(list)
|
526
|
+
|
526
527
|
for omen_position in omen_positions:
|
527
|
-
|
528
|
-
|
528
|
+
omen_positions_dict[omen_position.position.condition_id].append(
|
529
|
+
omen_position
|
530
|
+
)
|
529
531
|
|
532
|
+
# We include categorical markets below simply because we are already filtering on condition_ids.
|
530
533
|
omen_markets: dict[HexBytes, OmenMarket] = {
|
531
534
|
m.condition.id: m
|
532
|
-
for m in sgh.
|
535
|
+
for m in sgh.get_omen_markets(
|
533
536
|
limit=None,
|
534
537
|
condition_id_in=list(omen_positions_dict.keys()),
|
538
|
+
include_categorical_markets=True,
|
535
539
|
)
|
536
540
|
}
|
537
541
|
|
@@ -545,7 +549,9 @@ class OmenAgentMarket(AgentMarket):
|
|
545
549
|
)
|
546
550
|
|
547
551
|
positions = []
|
548
|
-
for condition_id, omen_positions in
|
552
|
+
for condition_id, omen_positions in tqdm(
|
553
|
+
omen_positions_dict.items(), mininterval=3
|
554
|
+
):
|
549
555
|
market = cls.from_data_model(omen_markets[condition_id])
|
550
556
|
|
551
557
|
# Skip markets that cannot be traded if `liquid_only`` is True.
|
@@ -595,7 +601,7 @@ class OmenAgentMarket(AgentMarket):
|
|
595
601
|
return get_omen_user_url(keys.bet_from_address)
|
596
602
|
|
597
603
|
def get_buy_token_amount(
|
598
|
-
self, bet_amount: USD | CollateralToken,
|
604
|
+
self, bet_amount: USD | CollateralToken, outcome: OutcomeStr
|
599
605
|
) -> OutcomeToken:
|
600
606
|
"""
|
601
607
|
Note: this is only valid if the market instance's token pool is
|
@@ -604,63 +610,26 @@ class OmenAgentMarket(AgentMarket):
|
|
604
610
|
outcome_token_pool = check_not_none(self.outcome_token_pool)
|
605
611
|
amount = get_buy_outcome_token_amount(
|
606
612
|
investment_amount=self.get_in_token(bet_amount),
|
607
|
-
|
608
|
-
|
609
|
-
no_outcome_pool_size=outcome_token_pool[OMEN_FALSE_OUTCOME],
|
613
|
+
outcome_index=self.get_outcome_index(outcome),
|
614
|
+
pool_balances=[outcome_token_pool[x] for x in self.outcomes],
|
610
615
|
fees=self.fees,
|
611
616
|
)
|
612
617
|
return amount
|
613
618
|
|
614
619
|
def _get_buy_token_amount_from_smart_contract(
|
615
|
-
self, bet_amount: USD,
|
620
|
+
self, bet_amount: USD, outcome: OutcomeStr
|
616
621
|
) -> OutcomeToken:
|
617
622
|
bet_amount_in_tokens = get_usd_in_token(
|
618
623
|
bet_amount, self.collateral_token_contract_address_checksummed
|
619
624
|
)
|
625
|
+
|
620
626
|
received_token_amount_wei = self.get_contract().calcBuyAmount(
|
621
627
|
investment_amount=bet_amount_in_tokens.as_wei,
|
622
|
-
outcome_index=self.get_outcome_index(
|
623
|
-
self.get_outcome_str_from_bool(direction)
|
624
|
-
),
|
628
|
+
outcome_index=self.get_outcome_index(outcome),
|
625
629
|
)
|
626
630
|
received_token_amount = received_token_amount_wei.as_outcome_token
|
627
631
|
return received_token_amount
|
628
632
|
|
629
|
-
def get_new_p_yes(self, bet_amount: USD, direction: bool) -> Probability:
|
630
|
-
"""
|
631
|
-
Calculate the new p_yes based on the bet amount and direction.
|
632
|
-
"""
|
633
|
-
if not self.has_token_pool():
|
634
|
-
raise ValueError("Outcome token pool is required to calculate new p_yes.")
|
635
|
-
|
636
|
-
bet_amount_in_tokens = self.get_usd_in_token(bet_amount)
|
637
|
-
outcome_token_pool = check_not_none(self.outcome_token_pool)
|
638
|
-
|
639
|
-
yes_outcome_pool_size = outcome_token_pool[
|
640
|
-
self.get_outcome_str_from_bool(True)
|
641
|
-
].value
|
642
|
-
no_outcome_pool_size = outcome_token_pool[
|
643
|
-
self.get_outcome_str_from_bool(False)
|
644
|
-
].value
|
645
|
-
|
646
|
-
new_yes_outcome_pool_size = yes_outcome_pool_size + (
|
647
|
-
self.fees.get_after_fees(bet_amount_in_tokens).value
|
648
|
-
)
|
649
|
-
new_no_outcome_pool_size = no_outcome_pool_size + (
|
650
|
-
self.fees.get_after_fees(bet_amount_in_tokens).value
|
651
|
-
)
|
652
|
-
|
653
|
-
received_token_amount = self.get_buy_token_amount(bet_amount, direction).value
|
654
|
-
if direction:
|
655
|
-
new_yes_outcome_pool_size -= received_token_amount
|
656
|
-
else:
|
657
|
-
new_no_outcome_pool_size -= received_token_amount
|
658
|
-
|
659
|
-
new_p_yes = new_no_outcome_pool_size / (
|
660
|
-
new_yes_outcome_pool_size + new_no_outcome_pool_size
|
661
|
-
)
|
662
|
-
return Probability(new_p_yes)
|
663
|
-
|
664
633
|
@staticmethod
|
665
634
|
def get_user_balance(user_id: str) -> float:
|
666
635
|
return float(get_balances(Web3.to_checksum_address(user_id)).total)
|
@@ -688,22 +657,6 @@ def get_omen_user_url(address: ChecksumAddress) -> str:
|
|
688
657
|
return f"https://gnosisscan.io/address/{address}"
|
689
658
|
|
690
659
|
|
691
|
-
def pick_binary_market(
|
692
|
-
sort_by: SortBy = SortBy.CLOSING_SOONEST,
|
693
|
-
filter_by: FilterBy = FilterBy.OPEN,
|
694
|
-
collateral_token_address_in: (
|
695
|
-
tuple[ChecksumAddress, ...] | None
|
696
|
-
) = SAFE_COLLATERAL_TOKENS_ADDRESSES,
|
697
|
-
) -> OmenMarket:
|
698
|
-
subgraph_handler = OmenSubgraphHandler()
|
699
|
-
return subgraph_handler.get_omen_binary_markets_simple(
|
700
|
-
limit=1,
|
701
|
-
sort_by=sort_by,
|
702
|
-
filter_by=filter_by,
|
703
|
-
collateral_token_address_in=collateral_token_address_in,
|
704
|
-
)[0]
|
705
|
-
|
706
|
-
|
707
660
|
@tenacity.retry(
|
708
661
|
stop=tenacity.stop_after_attempt(3),
|
709
662
|
wait=tenacity.wait_fixed(1),
|
@@ -713,7 +666,7 @@ def omen_buy_outcome_tx(
|
|
713
666
|
api_keys: APIKeys,
|
714
667
|
amount: USD | CollateralToken,
|
715
668
|
market: OmenAgentMarket,
|
716
|
-
outcome:
|
669
|
+
outcome: OutcomeStr,
|
717
670
|
auto_deposit: bool,
|
718
671
|
web3: Web3 | None = None,
|
719
672
|
slippage: float = 0.01,
|
@@ -769,7 +722,7 @@ def binary_omen_buy_outcome_tx(
|
|
769
722
|
api_keys: APIKeys,
|
770
723
|
amount: USD | CollateralToken,
|
771
724
|
market: OmenAgentMarket,
|
772
|
-
|
725
|
+
outcome: OutcomeStr,
|
773
726
|
auto_deposit: bool,
|
774
727
|
web3: Web3 | None = None,
|
775
728
|
) -> str:
|
@@ -777,7 +730,7 @@ def binary_omen_buy_outcome_tx(
|
|
777
730
|
api_keys=api_keys,
|
778
731
|
amount=amount,
|
779
732
|
market=market,
|
780
|
-
outcome=
|
733
|
+
outcome=outcome,
|
781
734
|
auto_deposit=auto_deposit,
|
782
735
|
web3=web3,
|
783
736
|
)
|
@@ -787,7 +740,7 @@ def omen_sell_outcome_tx(
|
|
787
740
|
api_keys: APIKeys,
|
788
741
|
amount: OutcomeToken | CollateralToken | USD,
|
789
742
|
market: OmenAgentMarket,
|
790
|
-
outcome:
|
743
|
+
outcome: OutcomeStr,
|
791
744
|
auto_withdraw: bool,
|
792
745
|
web3: Web3 | None = None,
|
793
746
|
slippage: float = 0.01,
|
@@ -863,7 +816,7 @@ def binary_omen_sell_outcome_tx(
|
|
863
816
|
api_keys: APIKeys,
|
864
817
|
amount: OutcomeToken | CollateralToken | USD,
|
865
818
|
market: OmenAgentMarket,
|
866
|
-
|
819
|
+
outcome: OutcomeStr,
|
867
820
|
auto_withdraw: bool,
|
868
821
|
web3: Web3 | None = None,
|
869
822
|
) -> str:
|
@@ -871,7 +824,7 @@ def binary_omen_sell_outcome_tx(
|
|
871
824
|
api_keys=api_keys,
|
872
825
|
amount=amount,
|
873
826
|
market=market,
|
874
|
-
outcome=
|
827
|
+
outcome=outcome,
|
875
828
|
auto_withdraw=auto_withdraw,
|
876
829
|
web3=web3,
|
877
830
|
)
|
@@ -1354,9 +1307,8 @@ def send_keeping_token_to_eoa_xdai(
|
|
1354
1307
|
|
1355
1308
|
def get_buy_outcome_token_amount(
|
1356
1309
|
investment_amount: CollateralToken,
|
1357
|
-
|
1358
|
-
|
1359
|
-
no_outcome_pool_size: OutcomeToken,
|
1310
|
+
outcome_index: int,
|
1311
|
+
pool_balances: list[OutcomeToken],
|
1360
1312
|
fees: MarketFees,
|
1361
1313
|
) -> OutcomeToken:
|
1362
1314
|
"""
|
@@ -1364,19 +1316,24 @@ def get_buy_outcome_token_amount(
|
|
1364
1316
|
|
1365
1317
|
Taken from https://github.com/gnosis/conditional-tokens-market-makers/blob/6814c0247c745680bb13298d4f0dd7f5b574d0db/contracts/FixedProductMarketMaker.sol#L264
|
1366
1318
|
"""
|
1319
|
+
if outcome_index >= len(pool_balances):
|
1320
|
+
raise ValueError("invalid outcome index")
|
1321
|
+
|
1367
1322
|
investment_amount_minus_fees = fees.get_after_fees(investment_amount)
|
1368
1323
|
investment_amount_minus_fees_as_ot = OutcomeToken(
|
1369
1324
|
investment_amount_minus_fees.value
|
1370
1325
|
)
|
1371
|
-
buy_token_pool_balance = (
|
1372
|
-
yes_outcome_pool_size if buy_direction else no_outcome_pool_size
|
1373
|
-
)
|
1374
1326
|
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
)
|
1327
|
+
buy_token_pool_balance = pool_balances[outcome_index]
|
1328
|
+
ending_outcome_balance = buy_token_pool_balance
|
1329
|
+
|
1330
|
+
# Calculate the ending balance considering all other outcomes
|
1331
|
+
for i, pool_balance in enumerate(pool_balances):
|
1332
|
+
if i != outcome_index:
|
1333
|
+
denominator = pool_balance + investment_amount_minus_fees_as_ot
|
1334
|
+
ending_outcome_balance = OutcomeToken(
|
1335
|
+
(ending_outcome_balance * pool_balance / denominator)
|
1336
|
+
)
|
1380
1337
|
|
1381
1338
|
if ending_outcome_balance <= 0:
|
1382
1339
|
raise ValueError("must have non-zero balances")
|
@@ -697,21 +697,18 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
697
697
|
self,
|
698
698
|
api_keys: APIKeys,
|
699
699
|
question_id: HexBytes,
|
700
|
-
|
701
|
-
outcomes: t.Sequence[str],
|
700
|
+
outcome_index: int,
|
702
701
|
bond: xDaiWei,
|
703
702
|
max_previous: xDaiWei | None = None,
|
704
703
|
web3: Web3 | None = None,
|
705
704
|
) -> TxReceipt:
|
706
705
|
# Normalise the answer to lowercase, to match Enum values as [YES, NO] against outcomes as ["Yes", "No"].
|
707
|
-
answer = answer.lower()
|
708
|
-
outcomes = [o.lower() for o in outcomes]
|
709
706
|
|
710
707
|
return self.submitAnswer(
|
711
708
|
api_keys=api_keys,
|
712
709
|
question_id=question_id,
|
713
710
|
answer=int_to_hexbytes(
|
714
|
-
|
711
|
+
outcome_index
|
715
712
|
), # Contract's method expects answer index in bytes.
|
716
713
|
bond=bond,
|
717
714
|
max_previous=max_previous,
|
@@ -10,7 +10,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
10
10
|
)
|
11
11
|
from prediction_market_agent_tooling.loggers import logger
|
12
12
|
from prediction_market_agent_tooling.markets.data_models import Resolution
|
13
|
-
from prediction_market_agent_tooling.markets.manifold.
|
13
|
+
from prediction_market_agent_tooling.markets.manifold.api import (
|
14
14
|
find_resolution_on_manifold,
|
15
15
|
)
|
16
16
|
from prediction_market_agent_tooling.markets.markets import MarketType
|
@@ -168,28 +168,28 @@ def finalize_markets(
|
|
168
168
|
f"Skipping, no resolution provided, market closed before {closed_before_days} days: {market.url=}"
|
169
169
|
)
|
170
170
|
|
171
|
-
elif resolution
|
172
|
-
logger.
|
173
|
-
|
171
|
+
elif resolution.invalid:
|
172
|
+
logger.warning(
|
173
|
+
f"Invalid resolution found, {resolution=}, for {market.url=}, finalizing as invalid."
|
174
|
+
)
|
175
|
+
omen_submit_invalid_answer_market_tx(
|
174
176
|
api_keys,
|
175
177
|
market,
|
176
|
-
resolution,
|
177
178
|
realitio_bond,
|
178
179
|
web3=web3,
|
179
180
|
)
|
180
|
-
finalized_markets.append(market.id)
|
181
|
-
logger.info(f"Finalized {market.url=}")
|
182
181
|
|
183
182
|
else:
|
184
|
-
logger.
|
185
|
-
|
186
|
-
)
|
187
|
-
omen_submit_invalid_answer_market_tx(
|
183
|
+
logger.info(f"Found resolution {resolution=} for {market.url=}")
|
184
|
+
omen_submit_answer_market_tx(
|
188
185
|
api_keys,
|
189
186
|
market,
|
187
|
+
resolution,
|
190
188
|
realitio_bond,
|
191
189
|
web3=web3,
|
192
190
|
)
|
191
|
+
finalized_markets.append(market.id)
|
192
|
+
logger.info(f"Finalized {market.url=}")
|
193
193
|
|
194
194
|
return finalized_markets
|
195
195
|
|
@@ -223,11 +223,11 @@ def omen_submit_answer_market_tx(
|
|
223
223
|
And after the period is over, you need to resolve the market using `omen_resolve_market_tx`.
|
224
224
|
"""
|
225
225
|
realitio_contract = OmenRealitioContract()
|
226
|
+
outcome_index = market.outcomes.index(resolution.outcome)
|
226
227
|
realitio_contract.submit_answer(
|
227
228
|
api_keys=api_keys,
|
228
229
|
question_id=market.question.id,
|
229
|
-
|
230
|
-
outcomes=market.question.outcomes,
|
230
|
+
outcome_index=outcome_index,
|
231
231
|
bond=bond.as_xdai_wei,
|
232
232
|
web3=web3,
|
233
233
|
)
|
@@ -23,7 +23,6 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
|
|
23
23
|
OmenMarket,
|
24
24
|
OmenPosition,
|
25
25
|
OmenUserPosition,
|
26
|
-
OutcomeStr,
|
27
26
|
OutcomeWei,
|
28
27
|
RealityAnswer,
|
29
28
|
RealityQuestion,
|
@@ -221,7 +220,6 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
221
220
|
self,
|
222
221
|
creator: HexAddress | None,
|
223
222
|
creator_in: t.Sequence[HexAddress] | None,
|
224
|
-
outcomes: t.Sequence[OutcomeStr],
|
225
223
|
created_after: DatetimeUTC | None,
|
226
224
|
question_opened_before: DatetimeUTC | None,
|
227
225
|
question_opened_after: DatetimeUTC | None,
|
@@ -239,12 +237,15 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
239
237
|
id_in: list[str] | None,
|
240
238
|
collateral_token_address_in: tuple[ChecksumAddress, ...] | None,
|
241
239
|
category: str | None,
|
240
|
+
include_categorical_markets: bool = False,
|
242
241
|
) -> dict[str, t.Any]:
|
243
242
|
where_stms: dict[str, t.Any] = {
|
244
|
-
"outcomes": outcomes,
|
245
243
|
"title_not": None,
|
246
244
|
"condition_": {},
|
247
245
|
}
|
246
|
+
if not include_categorical_markets:
|
247
|
+
where_stms["outcomeSlotCount"] = 2
|
248
|
+
where_stms["outcomes"] = OMEN_BINARY_MARKET_OUTCOMES
|
248
249
|
|
249
250
|
where_stms["question_"] = self.get_omen_question_filters(
|
250
251
|
question_id=question_id,
|
@@ -329,12 +330,13 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
329
330
|
|
330
331
|
return sort_direction, sort_by_field
|
331
332
|
|
332
|
-
def
|
333
|
+
def get_omen_markets_simple(
|
333
334
|
self,
|
334
335
|
limit: t.Optional[int],
|
335
336
|
# Enumerated values for simpler usage.
|
336
337
|
filter_by: FilterBy,
|
337
338
|
sort_by: SortBy,
|
339
|
+
include_categorical_markets: bool = False,
|
338
340
|
# Additional filters, these can not be modified by the enums above.
|
339
341
|
created_after: DatetimeUTC | None = None,
|
340
342
|
excluded_questions: set[str] | None = None, # question titles
|
@@ -345,7 +347,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
345
347
|
creator_in: t.Sequence[HexAddress] | None = None,
|
346
348
|
) -> t.List[OmenMarket]:
|
347
349
|
"""
|
348
|
-
Simplified `
|
350
|
+
Simplified `get_omen_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
|
349
351
|
"""
|
350
352
|
# These values need to be set according to the filter_by value, so they can not be passed as arguments.
|
351
353
|
resolved: bool | None = None
|
@@ -367,7 +369,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
367
369
|
|
368
370
|
sort_direction, sort_by_field = self._build_sort_params(sort_by)
|
369
371
|
|
370
|
-
|
372
|
+
all_markets = self.get_omen_markets(
|
371
373
|
limit=limit,
|
372
374
|
resolved=resolved,
|
373
375
|
question_opened_after=opened_after,
|
@@ -379,9 +381,12 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
379
381
|
collateral_token_address_in=collateral_token_address_in,
|
380
382
|
category=category,
|
381
383
|
creator_in=creator_in,
|
384
|
+
include_categorical_markets=include_categorical_markets,
|
382
385
|
)
|
383
386
|
|
384
|
-
|
387
|
+
return all_markets
|
388
|
+
|
389
|
+
def get_omen_markets(
|
385
390
|
self,
|
386
391
|
limit: t.Optional[int],
|
387
392
|
creator: HexAddress | None = None,
|
@@ -403,19 +408,18 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
403
408
|
id_in: list[str] | None = None,
|
404
409
|
sort_by_field: FieldPath | None = None,
|
405
410
|
sort_direction: str | None = None,
|
406
|
-
outcomes: t.Sequence[OutcomeStr] = OMEN_BINARY_MARKET_OUTCOMES,
|
407
411
|
collateral_token_address_in: (
|
408
412
|
tuple[ChecksumAddress, ...] | None
|
409
413
|
) = SAFE_COLLATERAL_TOKENS_ADDRESSES,
|
410
414
|
category: str | None = None,
|
415
|
+
include_categorical_markets: bool = True,
|
411
416
|
) -> t.List[OmenMarket]:
|
412
417
|
"""
|
413
|
-
Complete method to fetch Omen
|
418
|
+
Complete method to fetch Omen markets with various filters, use `get_omen_markets_simple` for simplified version that uses FilterBy and SortBy enums.
|
414
419
|
"""
|
415
420
|
where_stms = self._build_where_statements(
|
416
421
|
creator=creator,
|
417
422
|
creator_in=creator_in,
|
418
|
-
outcomes=outcomes,
|
419
423
|
created_after=created_after,
|
420
424
|
question_opened_before=question_opened_before,
|
421
425
|
question_opened_after=question_opened_after,
|
@@ -433,6 +437,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
433
437
|
liquidity_bigger_than=liquidity_bigger_than,
|
434
438
|
collateral_token_address_in=collateral_token_address_in,
|
435
439
|
category=category,
|
440
|
+
include_categorical_markets=include_categorical_markets,
|
436
441
|
)
|
437
442
|
|
438
443
|
# These values can not be set to `None`, but they can be omitted.
|
@@ -451,6 +456,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
451
456
|
)
|
452
457
|
|
453
458
|
fields = self._get_fields_for_markets(markets)
|
459
|
+
|
454
460
|
omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket)
|
455
461
|
return omen_markets
|
456
462
|
|
@@ -914,7 +920,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
914
920
|
unique_condition_ids: list[HexBytes] = list(
|
915
921
|
set(sum([u.position.conditionIds for u in user_positions], []))
|
916
922
|
)
|
917
|
-
markets = self.
|
923
|
+
markets = self.get_omen_markets(
|
918
924
|
limit=sys.maxsize, condition_id_in=unique_condition_ids
|
919
925
|
)
|
920
926
|
return markets
|
@@ -924,7 +930,7 @@ class OmenSubgraphHandler(BaseSubgraphHandler):
|
|
924
930
|
) -> OmenMarket:
|
925
931
|
"""Markets and user positions are uniquely connected via condition_ids"""
|
926
932
|
condition_ids = user_position.position.conditionIds
|
927
|
-
markets = self.
|
933
|
+
markets = self.get_omen_markets(limit=1, condition_id_in=condition_ids)
|
928
934
|
if len(markets) != 1:
|
929
935
|
raise ValueError(
|
930
936
|
f"Incorrect number of markets fetched {len(markets)}, expected 1."
|
@@ -69,17 +69,21 @@ class PolymarketMarket(BaseModel):
|
|
69
69
|
def resolution(self) -> Resolution | None:
|
70
70
|
winner_tokens = [token for token in self.tokens if token.winner]
|
71
71
|
if len(winner_tokens) == 0:
|
72
|
-
return None
|
72
|
+
return Resolution(outcome=None, invalid=True)
|
73
73
|
elif (
|
74
74
|
len(winner_tokens) == 1
|
75
75
|
and winner_tokens[0].outcome == POLYMARKET_TRUE_OUTCOME
|
76
76
|
):
|
77
|
-
return Resolution
|
77
|
+
return Resolution(
|
78
|
+
outcome=OutcomeStr(POLYMARKET_TRUE_OUTCOME), invalid=False
|
79
|
+
)
|
78
80
|
elif (
|
79
81
|
len(winner_tokens) == 1
|
80
82
|
and winner_tokens[0].outcome == POLYMARKET_FALSE_OUTCOME
|
81
83
|
):
|
82
|
-
return Resolution
|
84
|
+
return Resolution(
|
85
|
+
outcome=OutcomeStr(POLYMARKET_FALSE_OUTCOME), invalid=False
|
86
|
+
)
|
83
87
|
else:
|
84
88
|
raise ValueError(
|
85
89
|
f"Should not happen, invalid winner tokens: {winner_tokens}"
|