prediction-market-agent-tooling 0.67.2__py3-none-any.whl → 0.67.4__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/erc1155.abi.json +352 -0
  2. prediction_market_agent_tooling/deploy/agent.py +57 -51
  3. prediction_market_agent_tooling/deploy/betting_strategy.py +8 -5
  4. prediction_market_agent_tooling/markets/agent_market.py +24 -1
  5. prediction_market_agent_tooling/markets/blockchain_utils.py +5 -3
  6. prediction_market_agent_tooling/markets/data_models.py +23 -3
  7. prediction_market_agent_tooling/markets/manifold/manifold.py +2 -1
  8. prediction_market_agent_tooling/markets/metaculus/metaculus.py +2 -1
  9. prediction_market_agent_tooling/markets/omen/omen.py +2 -1
  10. prediction_market_agent_tooling/markets/polymarket/api.py +9 -3
  11. prediction_market_agent_tooling/markets/polymarket/data_models.py +5 -3
  12. prediction_market_agent_tooling/markets/polymarket/polymarket.py +17 -8
  13. prediction_market_agent_tooling/markets/seer/data_models.py +25 -1
  14. prediction_market_agent_tooling/markets/seer/seer.py +85 -26
  15. prediction_market_agent_tooling/markets/seer/seer_contracts.py +18 -0
  16. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +122 -18
  17. prediction_market_agent_tooling/markets/seer/swap_pool_handler.py +4 -1
  18. prediction_market_agent_tooling/tools/contract.py +59 -0
  19. prediction_market_agent_tooling/tools/cow/cow_order.py +4 -1
  20. prediction_market_agent_tooling/tools/hexbytes_custom.py +9 -0
  21. prediction_market_agent_tooling/tools/httpx_cached_client.py +5 -3
  22. prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -0
  23. prediction_market_agent_tooling/tools/rephrase.py +1 -1
  24. prediction_market_agent_tooling/tools/singleton.py +11 -6
  25. prediction_market_agent_tooling/tools/tokens/auto_deposit.py +57 -0
  26. {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/METADATA +1 -1
  27. {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/RECORD +30 -29
  28. {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/LICENSE +0 -0
  29. {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/WHEEL +0 -0
  30. {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,352 @@
1
+ [
2
+ {
3
+ "inputs": [],
4
+ "stateMutability": "nonpayable",
5
+ "type": "constructor"
6
+ },
7
+ {
8
+ "anonymous": false,
9
+ "inputs": [
10
+ {
11
+ "indexed": true,
12
+ "internalType": "address",
13
+ "name": "owner",
14
+ "type": "address"
15
+ },
16
+ {
17
+ "indexed": true,
18
+ "internalType": "address",
19
+ "name": "spender",
20
+ "type": "address"
21
+ },
22
+ {
23
+ "indexed": false,
24
+ "internalType": "uint256",
25
+ "name": "value",
26
+ "type": "uint256"
27
+ }
28
+ ],
29
+ "name": "Approval",
30
+ "type": "event"
31
+ },
32
+ {
33
+ "anonymous": false,
34
+ "inputs": [
35
+ {
36
+ "indexed": true,
37
+ "internalType": "address",
38
+ "name": "from",
39
+ "type": "address"
40
+ },
41
+ {
42
+ "indexed": true,
43
+ "internalType": "address",
44
+ "name": "to",
45
+ "type": "address"
46
+ },
47
+ {
48
+ "indexed": false,
49
+ "internalType": "uint256",
50
+ "name": "value",
51
+ "type": "uint256"
52
+ }
53
+ ],
54
+ "name": "Transfer",
55
+ "type": "event"
56
+ },
57
+ {
58
+ "inputs": [
59
+ {
60
+ "internalType": "address",
61
+ "name": "owner",
62
+ "type": "address"
63
+ },
64
+ {
65
+ "internalType": "address",
66
+ "name": "spender",
67
+ "type": "address"
68
+ }
69
+ ],
70
+ "name": "allowance",
71
+ "outputs": [
72
+ {
73
+ "internalType": "uint256",
74
+ "name": "",
75
+ "type": "uint256"
76
+ }
77
+ ],
78
+ "stateMutability": "view",
79
+ "type": "function"
80
+ },
81
+ {
82
+ "inputs": [
83
+ {
84
+ "internalType": "address",
85
+ "name": "spender",
86
+ "type": "address"
87
+ },
88
+ {
89
+ "internalType": "uint256",
90
+ "name": "amount",
91
+ "type": "uint256"
92
+ }
93
+ ],
94
+ "name": "approve",
95
+ "outputs": [
96
+ {
97
+ "internalType": "bool",
98
+ "name": "",
99
+ "type": "bool"
100
+ }
101
+ ],
102
+ "stateMutability": "nonpayable",
103
+ "type": "function"
104
+ },
105
+ {
106
+ "inputs": [
107
+ {
108
+ "internalType": "address",
109
+ "name": "account",
110
+ "type": "address"
111
+ }
112
+ ],
113
+ "name": "balanceOf",
114
+ "outputs": [
115
+ {
116
+ "internalType": "uint256",
117
+ "name": "",
118
+ "type": "uint256"
119
+ }
120
+ ],
121
+ "stateMutability": "view",
122
+ "type": "function"
123
+ },
124
+ {
125
+ "inputs": [
126
+ {
127
+ "internalType": "address",
128
+ "name": "account",
129
+ "type": "address"
130
+ },
131
+ {
132
+ "internalType": "uint256",
133
+ "name": "amount",
134
+ "type": "uint256"
135
+ }
136
+ ],
137
+ "name": "burn",
138
+ "outputs": [],
139
+ "stateMutability": "nonpayable",
140
+ "type": "function"
141
+ },
142
+ {
143
+ "inputs": [],
144
+ "name": "decimals",
145
+ "outputs": [
146
+ {
147
+ "internalType": "uint8",
148
+ "name": "",
149
+ "type": "uint8"
150
+ }
151
+ ],
152
+ "stateMutability": "view",
153
+ "type": "function"
154
+ },
155
+ {
156
+ "inputs": [
157
+ {
158
+ "internalType": "address",
159
+ "name": "spender",
160
+ "type": "address"
161
+ },
162
+ {
163
+ "internalType": "uint256",
164
+ "name": "subtractedValue",
165
+ "type": "uint256"
166
+ }
167
+ ],
168
+ "name": "decreaseAllowance",
169
+ "outputs": [
170
+ {
171
+ "internalType": "bool",
172
+ "name": "",
173
+ "type": "bool"
174
+ }
175
+ ],
176
+ "stateMutability": "nonpayable",
177
+ "type": "function"
178
+ },
179
+ {
180
+ "inputs": [],
181
+ "name": "factory",
182
+ "outputs": [
183
+ {
184
+ "internalType": "contract Wrapped1155Factory",
185
+ "name": "",
186
+ "type": "address"
187
+ }
188
+ ],
189
+ "stateMutability": "view",
190
+ "type": "function"
191
+ },
192
+ {
193
+ "inputs": [
194
+ {
195
+ "internalType": "address",
196
+ "name": "spender",
197
+ "type": "address"
198
+ },
199
+ {
200
+ "internalType": "uint256",
201
+ "name": "addedValue",
202
+ "type": "uint256"
203
+ }
204
+ ],
205
+ "name": "increaseAllowance",
206
+ "outputs": [
207
+ {
208
+ "internalType": "bool",
209
+ "name": "",
210
+ "type": "bool"
211
+ }
212
+ ],
213
+ "stateMutability": "nonpayable",
214
+ "type": "function"
215
+ },
216
+ {
217
+ "inputs": [
218
+ {
219
+ "internalType": "address",
220
+ "name": "account",
221
+ "type": "address"
222
+ },
223
+ {
224
+ "internalType": "uint256",
225
+ "name": "amount",
226
+ "type": "uint256"
227
+ }
228
+ ],
229
+ "name": "mint",
230
+ "outputs": [],
231
+ "stateMutability": "nonpayable",
232
+ "type": "function"
233
+ },
234
+ {
235
+ "inputs": [],
236
+ "name": "multiToken",
237
+ "outputs": [
238
+ {
239
+ "internalType": "contract IERC1155",
240
+ "name": "",
241
+ "type": "address"
242
+ }
243
+ ],
244
+ "stateMutability": "view",
245
+ "type": "function"
246
+ },
247
+ {
248
+ "inputs": [],
249
+ "name": "name",
250
+ "outputs": [
251
+ {
252
+ "internalType": "string",
253
+ "name": "",
254
+ "type": "string"
255
+ }
256
+ ],
257
+ "stateMutability": "view",
258
+ "type": "function"
259
+ },
260
+ {
261
+ "inputs": [],
262
+ "name": "symbol",
263
+ "outputs": [
264
+ {
265
+ "internalType": "string",
266
+ "name": "",
267
+ "type": "string"
268
+ }
269
+ ],
270
+ "stateMutability": "view",
271
+ "type": "function"
272
+ },
273
+ {
274
+ "inputs": [],
275
+ "name": "tokenId",
276
+ "outputs": [
277
+ {
278
+ "internalType": "uint256",
279
+ "name": "",
280
+ "type": "uint256"
281
+ }
282
+ ],
283
+ "stateMutability": "view",
284
+ "type": "function"
285
+ },
286
+ {
287
+ "inputs": [],
288
+ "name": "totalSupply",
289
+ "outputs": [
290
+ {
291
+ "internalType": "uint256",
292
+ "name": "",
293
+ "type": "uint256"
294
+ }
295
+ ],
296
+ "stateMutability": "view",
297
+ "type": "function"
298
+ },
299
+ {
300
+ "inputs": [
301
+ {
302
+ "internalType": "address",
303
+ "name": "recipient",
304
+ "type": "address"
305
+ },
306
+ {
307
+ "internalType": "uint256",
308
+ "name": "amount",
309
+ "type": "uint256"
310
+ }
311
+ ],
312
+ "name": "transfer",
313
+ "outputs": [
314
+ {
315
+ "internalType": "bool",
316
+ "name": "",
317
+ "type": "bool"
318
+ }
319
+ ],
320
+ "stateMutability": "nonpayable",
321
+ "type": "function"
322
+ },
323
+ {
324
+ "inputs": [
325
+ {
326
+ "internalType": "address",
327
+ "name": "sender",
328
+ "type": "address"
329
+ },
330
+ {
331
+ "internalType": "address",
332
+ "name": "recipient",
333
+ "type": "address"
334
+ },
335
+ {
336
+ "internalType": "uint256",
337
+ "name": "amount",
338
+ "type": "uint256"
339
+ }
340
+ ],
341
+ "name": "transferFrom",
342
+ "outputs": [
343
+ {
344
+ "internalType": "bool",
345
+ "name": "",
346
+ "type": "bool"
347
+ }
348
+ ],
349
+ "stateMutability": "nonpayable",
350
+ "type": "function"
351
+ }
352
+ ]
@@ -22,6 +22,7 @@ from prediction_market_agent_tooling.gtypes import USD, OutcomeToken, xDai
22
22
  from prediction_market_agent_tooling.loggers import logger
23
23
  from prediction_market_agent_tooling.markets.agent_market import (
24
24
  AgentMarket,
25
+ ConditionalFilterType,
25
26
  FilterBy,
26
27
  ProcessedMarket,
27
28
  ProcessedTradedMarket,
@@ -48,7 +49,7 @@ from prediction_market_agent_tooling.tools.is_invalid import is_invalid
48
49
  from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
49
50
  from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
50
51
  from prediction_market_agent_tooling.tools.rephrase import (
51
- rephrase_question_to_unconditioned,
52
+ rephrase_question_to_unconditional,
52
53
  )
53
54
  from prediction_market_agent_tooling.tools.tokens.main_token import (
54
55
  MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
@@ -199,7 +200,7 @@ class DeployablePredictionAgent(DeployableAgent):
199
200
  trade_on_markets_created_after: DatetimeUTC | None = None
200
201
  get_markets_sort_by: SortBy = SortBy.CLOSING_SOONEST
201
202
  get_markets_filter_by: FilterBy = FilterBy.OPEN
202
- rephrase_conditioned_markets: bool = True
203
+ rephrase_conditional_markets: bool = True
203
204
 
204
205
  # Agent behaviour when filtering fetched markets
205
206
  allow_invalid_questions: bool = False
@@ -230,7 +231,7 @@ class DeployablePredictionAgent(DeployableAgent):
230
231
  self.answer_categorical_market = observe()(self.answer_categorical_market) # type: ignore[method-assign]
231
232
  self.answer_scalar_market = observe()(self.answer_scalar_market) # type: ignore[method-assign]
232
233
  self.process_market = observe()(self.process_market) # type: ignore[method-assign]
233
- self.rephrase_market_to_unconditioned = observe()(self.rephrase_market_to_unconditioned) # type: ignore[method-assign]
234
+ self.rephrase_market_to_unconditional = observe()(self.rephrase_market_to_unconditional) # type: ignore[method-assign]
234
235
 
235
236
  def update_langfuse_trace_by_market(
236
237
  self, market_type: MarketType, market: AgentMarket
@@ -305,21 +306,21 @@ class DeployablePredictionAgent(DeployableAgent):
305
306
 
306
307
  return True
307
308
 
308
- def rephrase_market_to_unconditioned(
309
+ def rephrase_market_to_unconditional(
309
310
  self,
310
311
  market_: AgentMarket,
311
312
  ) -> AgentMarket:
312
313
  """
313
- If `rephrase_conditioned_markets` is set to True,
314
+ If `rephrase_conditional_markets` is set to True,
314
315
  this method will be used to rephrase the question to account for the parent's market probability in the agent's decision process.
315
316
  """
316
317
  new = market_.model_copy(deep=True)
317
318
 
318
319
  if new.parent is not None and new.parent.market.parent is not None:
319
- new.parent.market = self.rephrase_market_to_unconditioned(new.parent.market)
320
+ new.parent.market = self.rephrase_market_to_unconditional(new.parent.market)
320
321
 
321
322
  rephrased_question = (
322
- rephrase_question_to_unconditioned(
323
+ rephrase_question_to_unconditional(
323
324
  new.question,
324
325
  new.parent.market.question,
325
326
  new.parent.market.outcomes[new.parent.parent_outcome],
@@ -373,14 +374,10 @@ class DeployablePredictionAgent(DeployableAgent):
373
374
  return False
374
375
 
375
376
  @property
376
- def include_conditional_markets(self) -> bool:
377
- # TODO: All should work in our code, except that currently CoW most of the time completely fails to swap xDai into outcome tokens of conditioned market.
378
- # Enable after https://github.com/gnosis/prediction-market-agent-tooling/issues/748 and/or https://github.com/gnosis/prediction-market-agent-tooling/issues/759 is resolved.
379
- return False
380
- # `include_conditional_markets` if `rephrase_conditioned_markets` is enabled.
381
- # We can expand this method in teh future, when we implement also more complex logic about conditional markets.
382
- # Note that conditional market isn't a type of the market like Binary or Categorical, it means that it uses outcome tokens from parent market as a collateral token in this market.
383
- return self.rephrase_conditioned_markets
377
+ def conditional_filter_type(self) -> ConditionalFilterType:
378
+ if self.rephrase_conditional_markets:
379
+ return ConditionalFilterType.ALL
380
+ return ConditionalFilterType.ONLY_NOT_CONDITIONAL
384
381
 
385
382
  @property
386
383
  def agent_question_type(self) -> QuestionType:
@@ -407,7 +404,7 @@ class DeployablePredictionAgent(DeployableAgent):
407
404
  filter_by=self.get_markets_filter_by,
408
405
  created_after=self.trade_on_markets_created_after,
409
406
  question_type=self.agent_question_type,
410
- include_conditional_markets=self.include_conditional_markets,
407
+ conditional_filter_type=self.conditional_filter_type,
411
408
  )
412
409
  return available_markets
413
410
 
@@ -440,8 +437,8 @@ class DeployablePredictionAgent(DeployableAgent):
440
437
 
441
438
  logger.info(f"Answering market '{market.question}'.")
442
439
 
443
- if self.rephrase_conditioned_markets and market.parent is not None:
444
- market = self.rephrase_market_to_unconditioned(market)
440
+ if self.rephrase_conditional_markets and market.parent is not None:
441
+ market = self.rephrase_market_to_unconditional(market)
445
442
 
446
443
  if market.is_binary:
447
444
  try:
@@ -633,6 +630,7 @@ class DeployableTraderAgent(DeployablePredictionAgent):
633
630
  super().initialize_langfuse()
634
631
  # Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
635
632
  self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]
633
+ self.execute_trades = observe()(self.execute_trades) # type: ignore[method-assign]
636
634
 
637
635
  def check_min_required_balance_to_trade(self, market: AgentMarket) -> None:
638
636
  api_keys = APIKeys()
@@ -678,37 +676,9 @@ class DeployableTraderAgent(DeployablePredictionAgent):
678
676
  trades = strategy.calculate_trades(existing_position, answer, market)
679
677
  return trades
680
678
 
681
- def before_process_market(
682
- self, market_type: MarketType, market: AgentMarket
683
- ) -> None:
684
- super().before_process_market(market_type, market)
685
- self.check_min_required_balance_to_trade(market)
686
-
687
- def process_market(
688
- self,
689
- market_type: MarketType,
690
- market: AgentMarket,
691
- verify_market: bool = True,
692
- ) -> ProcessedTradedMarket | None:
693
- processed_market = super().process_market(market_type, market, verify_market)
694
- if processed_market is None:
695
- return None
696
-
697
- api_keys = APIKeys()
698
- user_id = market.get_user_id(api_keys=api_keys)
699
-
700
- try:
701
- existing_position = market.get_position(user_id=user_id)
702
- except Exception as e:
703
- logger.warning(f"Could not get position for user {user_id}, exception {e}")
704
- return None
705
-
706
- trades = self.build_trades(
707
- market=market,
708
- answer=processed_market.answer,
709
- existing_position=existing_position,
710
- )
711
-
679
+ def execute_trades(
680
+ self, market: AgentMarket, trades: list[Trade]
681
+ ) -> list[PlacedTrade]:
712
682
  # It can take quite some time before agent processes all the markets, recheck here if the market didn't get closed in the meantime, to not error out completely.
713
683
  # Unfortunately, we can not just add some room into closing time of the market while fetching them, because liquidity can be removed at any time by the liquidity providers.
714
684
  still_tradeable = market.can_be_traded()
@@ -717,7 +687,8 @@ class DeployableTraderAgent(DeployablePredictionAgent):
717
687
  f"Market {market.question=} ({market.url}) was selected to processing, but is not tradeable anymore."
718
688
  )
719
689
 
720
- placed_trades = []
690
+ placed_trades: list[PlacedTrade] = []
691
+
721
692
  for trade in trades:
722
693
  logger.info(f"Executing trade {trade} on market {market.id} ({market.url})")
723
694
 
@@ -730,7 +701,9 @@ class DeployableTraderAgent(DeployablePredictionAgent):
730
701
  case TradeType.SELL:
731
702
  # Get actual value of the position we are going to sell, and if it's less than we wanted to sell, simply sell all of it.
732
703
  current_position = check_not_none(
733
- market.get_position(user_id),
704
+ market.get_position(
705
+ market.get_user_id(api_keys=self.api_keys)
706
+ ),
734
707
  "Should exists if we are going to sell outcomes.",
735
708
  )
736
709
 
@@ -758,6 +731,39 @@ class DeployableTraderAgent(DeployablePredictionAgent):
758
731
  f"Trade execution skipped because, {self.place_trades=} or {still_tradeable=}."
759
732
  )
760
733
 
734
+ return placed_trades
735
+
736
+ def before_process_market(
737
+ self, market_type: MarketType, market: AgentMarket
738
+ ) -> None:
739
+ super().before_process_market(market_type, market)
740
+ self.check_min_required_balance_to_trade(market)
741
+
742
+ def process_market(
743
+ self,
744
+ market_type: MarketType,
745
+ market: AgentMarket,
746
+ verify_market: bool = True,
747
+ ) -> ProcessedTradedMarket | None:
748
+ processed_market = super().process_market(market_type, market, verify_market)
749
+ if processed_market is None:
750
+ return None
751
+
752
+ user_id = market.get_user_id(api_keys=self.api_keys)
753
+
754
+ try:
755
+ existing_position = market.get_position(user_id=user_id)
756
+ except Exception as e:
757
+ logger.warning(f"Could not get position for user {user_id}, exception {e}")
758
+ return None
759
+
760
+ trades = self.build_trades(
761
+ market=market,
762
+ answer=processed_market.answer,
763
+ existing_position=existing_position,
764
+ )
765
+ placed_trades = self.execute_trades(market, trades)
766
+
761
767
  traded_market = ProcessedTradedMarket(
762
768
  answer=processed_market.answer, trades=placed_trades
763
769
  )
@@ -84,7 +84,7 @@ class BettingStrategy(ABC):
84
84
 
85
85
  if outcome_tokens_to_get_in_usd <= trade.amount:
86
86
  raise GuaranteedLossError(
87
- f"Trade {trade=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}. Halting execution."
87
+ f"Trade {trade=} on market {market.url=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}. Halting execution."
88
88
  )
89
89
 
90
90
  clean_trades.append(trade)
@@ -443,8 +443,11 @@ class KellyBettingStrategy(BettingStrategy):
443
443
  kelly_bet_size = min(kelly_bet.size, max_price_impact_bet_amount)
444
444
 
445
445
  bet_outcome = direction if kelly_bet.direction else other_direction
446
+
446
447
  amounts = {
447
- bet_outcome: market.get_token_in_usd(kelly_bet_size),
448
+ bet_outcome: BettingStrategy.cap_to_profitable_bet_amount(
449
+ market, market.get_token_in_usd(kelly_bet_size), bet_outcome
450
+ ),
448
451
  }
449
452
  target_position = Position(market_id=market.id, amounts_current=amounts)
450
453
  trades = self._build_rebalance_trades_from_positions(
@@ -475,12 +478,12 @@ class KellyBettingStrategy(BettingStrategy):
475
478
  self, market: AgentMarket, kelly_bet: SimpleBet, direction: OutcomeStr
476
479
  ) -> CollateralToken:
477
480
  def calculate_price_impact_deviation_from_target_price_impact(
478
- bet_amount_usd: float, # Needs to be float because it's used in minimize_scalar internally.
481
+ bet_amount_collateral: float, # Needs to be float because it's used in minimize_scalar internally.
479
482
  ) -> float:
480
483
  outcome_idx = market.get_outcome_index(direction)
481
484
  price_impact = self.calculate_price_impact_for_bet_amount(
482
485
  outcome_idx=outcome_idx,
483
- bet_amount=market.get_usd_in_token(USD(bet_amount_usd)),
486
+ bet_amount=CollateralToken(bet_amount_collateral),
484
487
  pool_balances=pool_balances,
485
488
  fees=market.fees,
486
489
  )
@@ -504,7 +507,7 @@ class KellyBettingStrategy(BettingStrategy):
504
507
  calculate_price_impact_deviation_from_target_price_impact,
505
508
  bounds=(0, 1000 * total_pool_balance),
506
509
  method="bounded",
507
- tol=1e-11,
510
+ tol=1e-13,
508
511
  options={"maxiter": 10000},
509
512
  )
510
513
  return CollateralToken(optimized_bet_amount.x)
@@ -76,6 +76,12 @@ class QuestionType(str, Enum):
76
76
  BINARY = "binary"
77
77
 
78
78
 
79
+ class ConditionalFilterType(Enum):
80
+ ALL = 1
81
+ ONLY_CONDITIONAL = 2
82
+ ONLY_NOT_CONDITIONAL = 3
83
+
84
+
79
85
  class AgentMarket(BaseModel):
80
86
  """
81
87
  Common market class that can be created from vendor specific markets.
@@ -147,6 +153,23 @@ class AgentMarket(BaseModel):
147
153
  if "fees" not in data and "fee" in data:
148
154
  data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
149
155
  del data["fee"]
156
+ # Backward compatibility for older `AgentMarket` without `probabilities`.
157
+ if "probabilities" not in data and "current_p_yes" in data:
158
+ yes_outcome = data["outcomes"][
159
+ [o.lower() for o in data["outcomes"]].index(
160
+ YES_OUTCOME_LOWERCASE_IDENTIFIER
161
+ )
162
+ ]
163
+ no_outcome = data["outcomes"][
164
+ [o.lower() for o in data["outcomes"]].index(
165
+ NO_OUTCOME_LOWERCASE_IDENTIFIER
166
+ )
167
+ ]
168
+ data["probabilities"] = {
169
+ yes_outcome: data["current_p_yes"],
170
+ no_outcome: 1 - data["current_p_yes"],
171
+ }
172
+ del data["current_p_yes"]
150
173
  return data
151
174
 
152
175
  def market_outcome_for_probability_key(
@@ -384,7 +407,7 @@ class AgentMarket(BaseModel):
384
407
  created_after: t.Optional[DatetimeUTC] = None,
385
408
  excluded_questions: set[str] | None = None,
386
409
  question_type: QuestionType = QuestionType.ALL,
387
- include_conditional_markets: bool = False,
410
+ conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
388
411
  ) -> t.Sequence["AgentMarket"]:
389
412
  raise NotImplementedError("Subclasses must implement this method")
390
413
 
@@ -37,6 +37,10 @@ def store_trades(
37
37
  logger.warning(f"No prediction for market {market_id}, not storing anything.")
38
38
  return None
39
39
 
40
+ logger.info(
41
+ f"Storing trades for market {market_id}, with outcomes {outcomes}, {traded_market=}."
42
+ )
43
+
40
44
  probabilities = traded_market.answer.probabilities
41
45
  if not probabilities:
42
46
  logger.info("Skipping this since no probabilities available.")
@@ -56,9 +60,7 @@ def store_trades(
56
60
  ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)
57
61
 
58
62
  # tx_hashes must be list of bytes32 (see Solidity contract).
59
- tx_hashes = [
60
- HexBytes(HexStr(i.id)) for i in traded_market.trades if i.id is not None
61
- ]
63
+ tx_hashes = [HexBytes(HexStr(i.id)) for i in traded_market.trades]
62
64
 
63
65
  # Dune dashboard expects the probs to be in the same order as on the market.
64
66
  probabilities_converted = [