prediction-market-agent-tooling 0.61.1.dev489__py3-none-any.whl → 0.62.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 (50) hide show
  1. prediction_market_agent_tooling/deploy/agent.py +4 -5
  2. prediction_market_agent_tooling/deploy/betting_strategy.py +53 -69
  3. prediction_market_agent_tooling/gtypes.py +105 -27
  4. prediction_market_agent_tooling/jobs/jobs_models.py +5 -7
  5. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +13 -17
  6. prediction_market_agent_tooling/markets/agent_market.py +96 -55
  7. prediction_market_agent_tooling/markets/blockchain_utils.py +2 -32
  8. prediction_market_agent_tooling/markets/data_models.py +40 -44
  9. prediction_market_agent_tooling/markets/manifold/api.py +2 -6
  10. prediction_market_agent_tooling/markets/manifold/data_models.py +33 -25
  11. prediction_market_agent_tooling/markets/manifold/manifold.py +13 -11
  12. prediction_market_agent_tooling/markets/market_fees.py +6 -2
  13. prediction_market_agent_tooling/markets/omen/data_models.py +66 -57
  14. prediction_market_agent_tooling/markets/omen/omen.py +222 -250
  15. prediction_market_agent_tooling/markets/omen/omen_contracts.py +32 -33
  16. prediction_market_agent_tooling/markets/omen/omen_resolving.py +7 -14
  17. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +20 -14
  18. prediction_market_agent_tooling/markets/polymarket/data_models.py +3 -3
  19. prediction_market_agent_tooling/markets/polymarket/data_models_web.py +4 -4
  20. prediction_market_agent_tooling/markets/polymarket/polymarket.py +3 -5
  21. prediction_market_agent_tooling/markets/seer/data_models.py +92 -5
  22. prediction_market_agent_tooling/markets/seer/seer.py +93 -115
  23. prediction_market_agent_tooling/markets/seer/seer_contracts.py +11 -6
  24. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +18 -26
  25. prediction_market_agent_tooling/monitor/monitor.py +2 -2
  26. prediction_market_agent_tooling/tools/_generic_value.py +261 -0
  27. prediction_market_agent_tooling/tools/balances.py +14 -11
  28. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +12 -10
  29. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +31 -24
  30. prediction_market_agent_tooling/tools/betting_strategies/utils.py +3 -1
  31. prediction_market_agent_tooling/tools/contract.py +14 -10
  32. prediction_market_agent_tooling/tools/cow/cow_manager.py +3 -4
  33. prediction_market_agent_tooling/tools/cow/cow_order.py +51 -7
  34. prediction_market_agent_tooling/tools/langfuse_client_utils.py +13 -1
  35. prediction_market_agent_tooling/tools/omen/sell_positions.py +6 -3
  36. prediction_market_agent_tooling/tools/safe.py +5 -6
  37. prediction_market_agent_tooling/tools/tokens/auto_deposit.py +36 -27
  38. prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +4 -25
  39. prediction_market_agent_tooling/tools/tokens/main_token.py +2 -2
  40. prediction_market_agent_tooling/tools/tokens/token_utils.py +46 -0
  41. prediction_market_agent_tooling/tools/tokens/usd.py +79 -0
  42. prediction_market_agent_tooling/tools/utils.py +14 -8
  43. prediction_market_agent_tooling/tools/web3_utils.py +24 -41
  44. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/METADATA +2 -1
  45. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/RECORD +48 -47
  46. prediction_market_agent_tooling/markets/seer/price_manager.py +0 -111
  47. prediction_market_agent_tooling/markets/seer/subgraph_data_models.py +0 -57
  48. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/LICENSE +0 -0
  49. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/WHEEL +0 -0
  50. {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/entry_points.txt +0 -0
@@ -4,20 +4,23 @@ from enum import Enum
4
4
  from eth_typing import ChecksumAddress
5
5
  from pydantic import BaseModel, field_validator, model_validator
6
6
  from pydantic_core.core_schema import FieldValidationInfo
7
- from web3 import Web3
8
7
 
9
8
  from prediction_market_agent_tooling.config import APIKeys
10
- from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
9
+ from prediction_market_agent_tooling.gtypes import (
10
+ CollateralToken,
11
+ OutcomeStr,
12
+ OutcomeToken,
13
+ Probability,
14
+ )
11
15
  from prediction_market_agent_tooling.markets.data_models import (
16
+ USD,
12
17
  Bet,
13
- BetAmount,
14
- Currency,
18
+ CollateralToken,
19
+ ExistingPosition,
15
20
  PlacedTrade,
16
- Position,
17
21
  ProbabilisticAnswer,
18
22
  Resolution,
19
23
  ResolvedBet,
20
- TokenAmount,
21
24
  )
22
25
  from prediction_market_agent_tooling.markets.market_fees import MarketFees
23
26
  from prediction_market_agent_tooling.tools.utils import (
@@ -56,31 +59,28 @@ class AgentMarket(BaseModel):
56
59
  Contains everything that is needed for an agent to make a prediction.
57
60
  """
58
61
 
59
- currency: t.ClassVar[Currency]
60
62
  base_url: t.ClassVar[str]
61
63
 
62
64
  id: str
63
65
  question: str
64
66
  description: str | None
65
- outcomes: list[str]
66
- outcome_token_pool: (
67
- dict[str, float] | None
68
- ) # Should be in currency of `currency` above.
67
+ outcomes: t.Sequence[OutcomeStr]
68
+ outcome_token_pool: dict[str, OutcomeToken] | None
69
69
  resolution: Resolution | None
70
70
  created_time: DatetimeUTC | None
71
71
  close_time: DatetimeUTC | None
72
72
  current_p_yes: Probability
73
73
  url: str
74
- volume: float | None # Should be in currency of `currency` above.
74
+ volume: CollateralToken | None
75
75
  fees: MarketFees
76
76
 
77
77
  @field_validator("outcome_token_pool")
78
78
  def validate_outcome_token_pool(
79
79
  cls,
80
- outcome_token_pool: dict[str, float] | None,
80
+ outcome_token_pool: dict[str, OutcomeToken] | None,
81
81
  info: FieldValidationInfo,
82
- ) -> dict[str, float] | None:
83
- outcomes: list[str] = check_not_none(info.data.get("outcomes"))
82
+ ) -> dict[str, OutcomeToken] | None:
83
+ outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
84
84
  if outcome_token_pool is not None:
85
85
  outcome_keys = set(outcome_token_pool.keys())
86
86
  expected_keys = set(outcomes)
@@ -103,20 +103,28 @@ class AgentMarket(BaseModel):
103
103
  return Probability(1 - self.current_p_yes)
104
104
 
105
105
  @property
106
- def yes_outcome_price(self) -> float:
106
+ def yes_outcome_price(self) -> CollateralToken:
107
107
  """
108
108
  Price at prediction market is equal to the probability of given outcome.
109
109
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
110
110
  """
111
- return self.current_p_yes
111
+ return CollateralToken(self.current_p_yes)
112
+
113
+ @property
114
+ def yes_outcome_price_usd(self) -> USD:
115
+ return self.get_token_in_usd(self.yes_outcome_price)
112
116
 
113
117
  @property
114
- def no_outcome_price(self) -> float:
118
+ def no_outcome_price(self) -> CollateralToken:
115
119
  """
116
120
  Price at prediction market is equal to the probability of given outcome.
117
121
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
118
122
  """
119
- return self.current_p_no
123
+ return CollateralToken(self.current_p_no)
124
+
125
+ @property
126
+ def no_outcome_price_usd(self) -> USD:
127
+ return self.get_token_in_usd(self.no_outcome_price)
120
128
 
121
129
  @property
122
130
  def probable_resolution(self) -> Resolution:
@@ -151,48 +159,86 @@ class AgentMarket(BaseModel):
151
159
  """
152
160
  raise NotImplementedError("Subclasses must implement this method")
153
161
 
154
- def get_last_trade_yes_outcome_price(self) -> float | None:
162
+ def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
155
163
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
156
164
  # Just making it explicit in this function.
157
165
  if last_trade_p_yes := self.get_last_trade_p_yes():
158
- return float(last_trade_p_yes)
166
+ return CollateralToken(last_trade_p_yes)
159
167
  return None
160
168
 
161
- def get_last_trade_no_outcome_price(self) -> float | None:
169
+ def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
170
+ if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
171
+ return self.get_token_in_usd(last_trade_yes_outcome_price)
172
+ return None
173
+
174
+ def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
162
175
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
163
176
  # Just making it explicit in this function.
164
177
  if last_trade_p_no := self.get_last_trade_p_no():
165
- return float(last_trade_p_no)
178
+ return CollateralToken(last_trade_p_no)
166
179
  return None
167
180
 
168
- def get_bet_amount(self, amount: float) -> BetAmount:
169
- return BetAmount(amount=amount, currency=self.currency)
181
+ def get_last_trade_no_outcome_price_usd(self) -> USD | None:
182
+ if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
183
+ return self.get_token_in_usd(last_trade_no_outcome_price)
184
+ return None
170
185
 
171
- @classmethod
172
- def get_liquidatable_amount(cls) -> BetAmount:
173
- tiny_amount = cls.get_tiny_bet_amount()
174
- tiny_amount.amount /= 10
175
- return tiny_amount
186
+ def get_liquidatable_amount(self) -> OutcomeToken:
187
+ tiny_amount = self.get_tiny_bet_amount()
188
+ return OutcomeToken.from_token(tiny_amount / 10)
176
189
 
177
- @classmethod
178
- def get_tiny_bet_amount(cls) -> BetAmount:
190
+ def get_token_in_usd(self, x: CollateralToken) -> USD:
191
+ """
192
+ Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.
193
+ """
194
+ raise NotImplementedError("Subclasses must implement this method")
195
+
196
+ def get_usd_in_token(self, x: USD) -> CollateralToken:
197
+ """
198
+ Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.
199
+ """
200
+ raise NotImplementedError("Subclasses must implement this method")
201
+
202
+ def get_sell_value_of_outcome_token(
203
+ self, outcome: str, amount: OutcomeToken
204
+ ) -> CollateralToken:
205
+ """
206
+ When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token).
207
+ But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
208
+ """
209
+ raise NotImplementedError("Subclasses must implement this method")
210
+
211
+ def get_in_usd(self, x: USD | CollateralToken) -> USD:
212
+ if isinstance(x, USD):
213
+ return x
214
+ return self.get_token_in_usd(x)
215
+
216
+ def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
217
+ if isinstance(x, CollateralToken):
218
+ return x
219
+ return self.get_usd_in_token(x)
220
+
221
+ def get_tiny_bet_amount(self) -> CollateralToken:
222
+ """
223
+ Tiny bet amount that the platform still allows us to do.
224
+ """
179
225
  raise NotImplementedError("Subclasses must implement this method")
180
226
 
181
227
  def liquidate_existing_positions(self, outcome: bool) -> None:
182
228
  raise NotImplementedError("Subclasses must implement this method")
183
229
 
184
- def place_bet(self, outcome: bool, amount: BetAmount) -> str:
230
+ def place_bet(self, outcome: bool, amount: USD) -> str:
185
231
  raise NotImplementedError("Subclasses must implement this method")
186
232
 
187
- def buy_tokens(self, outcome: bool, amount: TokenAmount) -> str:
233
+ def buy_tokens(self, outcome: bool, amount: USD) -> str:
188
234
  return self.place_bet(outcome=outcome, amount=amount)
189
235
 
190
236
  def get_buy_token_amount(
191
- self, bet_amount: BetAmount, direction: bool
192
- ) -> TokenAmount:
237
+ self, bet_amount: USD | CollateralToken, direction: bool
238
+ ) -> OutcomeToken:
193
239
  raise NotImplementedError("Subclasses must implement this method")
194
240
 
195
- def sell_tokens(self, outcome: bool, amount: TokenAmount) -> str:
241
+ def sell_tokens(self, outcome: bool, amount: USD | OutcomeToken) -> str:
196
242
  raise NotImplementedError("Subclasses must implement this method")
197
243
 
198
244
  @staticmethod
@@ -216,8 +262,8 @@ class AgentMarket(BaseModel):
216
262
  """
217
263
  raise NotImplementedError("Subclasses must implement this method")
218
264
 
219
- @staticmethod
220
- def get_trade_balance(api_keys: APIKeys) -> float:
265
+ @classmethod
266
+ def get_trade_balance(cls, api_keys: APIKeys) -> USD:
221
267
  """
222
268
  Return balance that can be used to trade on the given market.
223
269
  """
@@ -247,7 +293,6 @@ class AgentMarket(BaseModel):
247
293
  traded_market: ProcessedTradedMarket | None,
248
294
  keys: APIKeys,
249
295
  agent_name: str,
250
- web3: Web3 | None = None,
251
296
  ) -> None:
252
297
  """
253
298
  If market allows to upload trades somewhere, implement it in this method.
@@ -274,11 +319,11 @@ class AgentMarket(BaseModel):
274
319
  def is_resolved(self) -> bool:
275
320
  return self.resolution is not None
276
321
 
277
- def get_liquidity(self) -> TokenAmount:
322
+ def get_liquidity(self) -> CollateralToken:
278
323
  raise NotImplementedError("Subclasses must implement this method")
279
324
 
280
325
  def has_liquidity(self) -> bool:
281
- return self.get_liquidity().amount > 0
326
+ return self.get_liquidity() > 0
282
327
 
283
328
  def has_successful_resolution(self) -> bool:
284
329
  return self.resolution in [Resolution.YES, Resolution.NO]
@@ -289,7 +334,7 @@ class AgentMarket(BaseModel):
289
334
  def get_outcome_str_from_bool(self, outcome: bool) -> OutcomeStr:
290
335
  raise NotImplementedError("Subclasses must implement this method")
291
336
 
292
- def get_outcome_str(self, outcome_index: int) -> str:
337
+ def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
293
338
  try:
294
339
  return self.outcomes[outcome_index]
295
340
  except IndexError:
@@ -303,16 +348,19 @@ class AgentMarket(BaseModel):
303
348
  except ValueError:
304
349
  raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
305
350
 
306
- def get_token_balance(self, user_id: str, outcome: str) -> TokenAmount:
351
+ def get_token_balance(self, user_id: str, outcome: str) -> OutcomeToken:
307
352
  raise NotImplementedError("Subclasses must implement this method")
308
353
 
309
- def get_position(self, user_id: str) -> Position | None:
354
+ def get_position(self, user_id: str) -> ExistingPosition | None:
310
355
  raise NotImplementedError("Subclasses must implement this method")
311
356
 
312
357
  @classmethod
313
358
  def get_positions(
314
- cls, user_id: str, liquid_only: bool = False, larger_than: float = 0
315
- ) -> list[Position]:
359
+ cls,
360
+ user_id: str,
361
+ liquid_only: bool = False,
362
+ larger_than: OutcomeToken = OutcomeToken(0),
363
+ ) -> t.Sequence[ExistingPosition]:
316
364
  """
317
365
  Get all non-zero positions a user has in any market.
318
366
 
@@ -323,13 +371,6 @@ class AgentMarket(BaseModel):
323
371
  """
324
372
  raise NotImplementedError("Subclasses must implement this method")
325
373
 
326
- @classmethod
327
- def get_positions_value(cls, positions: list[Position]) -> BetAmount:
328
- """
329
- Get the total value of all positions held by a user.
330
- """
331
- raise NotImplementedError("Subclasses must implement this method")
332
-
333
374
  def can_be_traded(self) -> bool:
334
375
  if self.is_closed() or not self.has_liquidity():
335
376
  return False
@@ -342,7 +383,7 @@ class AgentMarket(BaseModel):
342
383
  def has_token_pool(self) -> bool:
343
384
  return self.outcome_token_pool is not None
344
385
 
345
- def get_pool_tokens(self, outcome: str) -> float:
386
+ def get_pool_tokens(self, outcome: str) -> OutcomeToken:
346
387
  if not self.outcome_token_pool:
347
388
  raise ValueError("Outcome token pool is not available.")
348
389
 
@@ -1,15 +1,8 @@
1
1
  from web3 import Web3
2
2
  from web3.constants import HASH_ZERO
3
- from web3.types import TxReceipt
4
3
 
5
4
  from prediction_market_agent_tooling.config import APIKeys
6
- from prediction_market_agent_tooling.gtypes import (
7
- ChecksumAddress,
8
- HexBytes,
9
- HexStr,
10
- xDai,
11
- xdai_type,
12
- )
5
+ from prediction_market_agent_tooling.gtypes import HexBytes, HexStr
13
6
  from prediction_market_agent_tooling.loggers import logger
14
7
  from prediction_market_agent_tooling.markets.agent_market import ProcessedTradedMarket
15
8
  from prediction_market_agent_tooling.markets.omen.data_models import (
@@ -19,38 +12,17 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
19
12
  from prediction_market_agent_tooling.markets.omen.omen_contracts import (
20
13
  OmenAgentResultMappingContract,
21
14
  )
22
- from prediction_market_agent_tooling.tools.balances import get_balances
23
15
  from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
24
16
  from prediction_market_agent_tooling.tools.utils import BPS_CONSTANT
25
17
  from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
26
18
 
27
19
 
28
- def get_total_balance(
29
- address: ChecksumAddress,
30
- web3: Web3 | None = None,
31
- sum_xdai: bool = True,
32
- sum_wxdai: bool = True,
33
- ) -> xDai:
34
- """
35
- Checks if the total balance of xDai and wxDai in the wallet is above the minimum required balance.
36
- """
37
- current_balances = get_balances(address, web3)
38
- # xDai and wxDai have equal value and can be exchanged for almost no cost, so we can sum them up.
39
- total_balance = 0.0
40
- if sum_xdai:
41
- total_balance += current_balances.xdai
42
- if sum_wxdai:
43
- total_balance += current_balances.wxdai
44
- return xdai_type(total_balance)
45
-
46
-
47
20
  def store_trades(
48
21
  market_id: str,
49
22
  traded_market: ProcessedTradedMarket | None,
50
23
  keys: APIKeys,
51
24
  agent_name: str,
52
- web3: Web3 | None = None,
53
- ) -> TxReceipt:
25
+ ) -> None:
54
26
  if traded_market is None:
55
27
  logger.warning(f"No prediction for market {market_id}, not storing anything.")
56
28
  return
@@ -78,9 +50,7 @@ def store_trades(
78
50
  api_keys=keys,
79
51
  market_address=Web3.to_checksum_address(market_id),
80
52
  prediction=prediction,
81
- web3=web3,
82
53
  )
83
54
  logger.info(
84
55
  f"Added prediction to market {market_id}. - receipt {tx_receipt['transactionHash'].hex()}."
85
56
  )
86
- return tx_receipt
@@ -1,19 +1,18 @@
1
1
  from enum import Enum
2
- from typing import Annotated, TypeAlias
2
+ from typing import Annotated
3
3
 
4
4
  from pydantic import BaseModel, BeforeValidator, computed_field
5
5
 
6
- from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
6
+ from prediction_market_agent_tooling.gtypes import (
7
+ USD,
8
+ CollateralToken,
9
+ OutcomeStr,
10
+ OutcomeToken,
11
+ Probability,
12
+ )
7
13
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC
8
14
 
9
15
 
10
- class Currency(str, Enum):
11
- xDai = "xDai"
12
- sDai = "sDai"
13
- Mana = "Mana"
14
- USDC = "USDC"
15
-
16
-
17
16
  class Resolution(str, Enum):
18
17
  YES = "YES"
19
18
  NO = "NO"
@@ -25,21 +24,9 @@ class Resolution(str, Enum):
25
24
  return Resolution.YES if value else Resolution.NO
26
25
 
27
26
 
28
- class TokenAmount(BaseModel):
29
- amount: float
30
- currency: Currency
31
-
32
- def __str__(self) -> str:
33
- return f"Amount {self.amount} currency {self.currency}"
34
-
35
-
36
- BetAmount: TypeAlias = TokenAmount
37
- ProfitAmount: TypeAlias = TokenAmount
38
-
39
-
40
27
  class Bet(BaseModel):
41
28
  id: str
42
- amount: BetAmount
29
+ amount: CollateralToken
43
30
  outcome: bool
44
31
  created_time: DatetimeUTC
45
32
  market_question: str
@@ -52,7 +39,7 @@ class Bet(BaseModel):
52
39
  class ResolvedBet(Bet):
53
40
  market_outcome: bool
54
41
  resolved_time: DatetimeUTC
55
- profit: ProfitAmount
42
+ profit: CollateralToken
56
43
 
57
44
  @computed_field # type: ignore[prop-decorator]
58
45
  @property
@@ -63,10 +50,6 @@ class ResolvedBet(Bet):
63
50
  return f"Resolved bet for market {self.market_id} for question {self.market_question} created at {self.created_time}: {self.amount} on {self.outcome}. Bet was resolved at {self.resolved_time} and was {'correct' if self.is_correct else 'incorrect'}. Profit was {self.profit}"
64
51
 
65
52
 
66
- class TokenAmountAndDirection(TokenAmount):
67
- direction: bool
68
-
69
-
70
53
  def to_boolean_outcome(value: str | bool) -> bool:
71
54
  if isinstance(value, bool):
72
55
  return value
@@ -102,23 +85,36 @@ class ProbabilisticAnswer(BaseModel):
102
85
 
103
86
  class Position(BaseModel):
104
87
  market_id: str
105
- amounts: dict[OutcomeStr, TokenAmount]
88
+ # This is for how much we could buy or sell the position right now.
89
+ amounts_current: dict[OutcomeStr, USD]
106
90
 
107
91
  @property
108
- def total_amount(self) -> TokenAmount:
109
- return TokenAmount(
110
- amount=sum(amount.amount for amount in self.amounts.values()),
111
- currency=self.amounts[next(iter(self.amounts.keys()))].currency,
112
- )
92
+ def total_amount_current(self) -> USD:
93
+ return sum(self.amounts_current.values(), start=USD(0))
113
94
 
114
95
  def __str__(self) -> str:
115
96
  amounts_str = ", ".join(
116
- f"{amount.amount} '{outcome}' tokens"
117
- for outcome, amount in self.amounts.items()
97
+ f"{amount} USD of '{outcome}' tokens"
98
+ for outcome, amount in self.amounts_current.items()
118
99
  )
119
100
  return f"Position for market id {self.market_id}: {amounts_str}"
120
101
 
121
102
 
103
+ class ExistingPosition(Position):
104
+ # This is how much we will get if we win.
105
+ amounts_potential: dict[OutcomeStr, USD]
106
+ # These are raw outcome tokens of the market.
107
+ amounts_ot: dict[OutcomeStr, OutcomeToken]
108
+
109
+ @property
110
+ def total_amount_potential(self) -> USD:
111
+ return sum(self.amounts_potential.values(), start=USD(0))
112
+
113
+ @property
114
+ def total_amount_ot(self) -> OutcomeToken:
115
+ return sum(self.amounts_ot.values(), start=OutcomeToken(0))
116
+
117
+
122
118
  class TradeType(str, Enum):
123
119
  SELL = "sell"
124
120
  BUY = "buy"
@@ -127,7 +123,7 @@ class TradeType(str, Enum):
127
123
  class Trade(BaseModel):
128
124
  trade_type: TradeType
129
125
  outcome: bool
130
- amount: TokenAmount
126
+ amount: USD
131
127
 
132
128
 
133
129
  class PlacedTrade(Trade):
@@ -149,12 +145,12 @@ class SimulatedBetDetail(BaseModel):
149
145
  market_p_yes: float
150
146
  agent_p_yes: float
151
147
  agent_conf: float
152
- org_bet: float
153
- sim_bet: float
148
+ org_bet: CollateralToken
149
+ sim_bet: CollateralToken
154
150
  org_dir: bool
155
151
  sim_dir: bool
156
- org_profit: float
157
- sim_profit: float
152
+ org_profit: CollateralToken
153
+ sim_profit: CollateralToken
158
154
  timestamp: DatetimeUTC
159
155
 
160
156
 
@@ -166,10 +162,10 @@ class SharpeOutput(BaseModel):
166
162
 
167
163
  class SimulatedLifetimeDetail(BaseModel):
168
164
  p_yes_mse: float
169
- total_bet_amount: float
170
- total_bet_profit: float
171
- total_simulated_amount: float
172
- total_simulated_profit: float
165
+ total_bet_amount: CollateralToken
166
+ total_bet_profit: CollateralToken
167
+ total_simulated_amount: CollateralToken
168
+ total_simulated_profit: CollateralToken
173
169
  roi: float
174
170
  simulated_roi: float
175
171
  sharpe_output_original: SharpeOutput
@@ -5,11 +5,7 @@ import tenacity
5
5
 
6
6
  from prediction_market_agent_tooling.gtypes import Mana, SecretStr
7
7
  from prediction_market_agent_tooling.loggers import logger
8
- from prediction_market_agent_tooling.markets.data_models import (
9
- BetAmount,
10
- Currency,
11
- ResolvedBet,
12
- )
8
+ from prediction_market_agent_tooling.markets.data_models import ResolvedBet
13
9
  from prediction_market_agent_tooling.markets.manifold.data_models import (
14
10
  FullManifoldMarket,
15
11
  ManifoldBet,
@@ -211,7 +207,7 @@ def manifold_to_generic_resolved_bet(
211
207
  market_outcome = market.get_resolved_boolean_outcome()
212
208
  return ResolvedBet(
213
209
  id=bet.id,
214
- amount=BetAmount(amount=bet.amount, currency=Currency.Mana),
210
+ amount=bet.amount,
215
211
  outcome=bet.get_resolved_boolean_outcome(),
216
212
  created_time=bet.createdTime,
217
213
  market_question=market.question,
@@ -3,24 +3,37 @@ from enum import Enum
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
6
- from prediction_market_agent_tooling.gtypes import Mana, Probability
7
- from prediction_market_agent_tooling.markets.data_models import (
8
- Currency,
9
- ProfitAmount,
10
- Resolution,
6
+ from prediction_market_agent_tooling.gtypes import (
7
+ USD,
8
+ CollateralToken,
9
+ Mana,
10
+ OutcomeStr,
11
+ OutcomeToken,
12
+ Probability,
11
13
  )
14
+ from prediction_market_agent_tooling.markets.data_models import Resolution
12
15
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
13
16
 
14
17
  MANIFOLD_BASE_URL = "https://manifold.markets"
15
18
 
16
19
 
20
+ def mana_to_usd(mana: Mana) -> USD:
21
+ # Not really, but for sake of simplicity. Mana are just play money.
22
+ return USD(mana.value)
23
+
24
+
25
+ def usd_to_mana(usd: USD) -> Mana:
26
+ # Not really, but for sake of simplicity. Mana are just play money.
27
+ return Mana(usd.value)
28
+
29
+
17
30
  class ManifoldPool(BaseModel):
18
- NO: float
19
- YES: float
31
+ NO: OutcomeToken
32
+ YES: OutcomeToken
20
33
 
21
- def size_for_outcome(self, outcome: str) -> float:
34
+ def size_for_outcome(self, outcome: str) -> OutcomeToken:
22
35
  if hasattr(self, outcome):
23
- return float(getattr(self, outcome))
36
+ return OutcomeToken(getattr(self, outcome))
24
37
  else:
25
38
  should_not_happen(f"Unexpected outcome string, '{outcome}'.")
26
39
 
@@ -49,8 +62,6 @@ class ManifoldMarket(BaseModel):
49
62
  https://docs.manifold.markets/api#get-v0markets
50
63
  """
51
64
 
52
- BET_AMOUNT_CURRENCY: t.ClassVar[Currency] = Currency.Mana
53
-
54
65
  id: str
55
66
  question: str
56
67
  creatorId: str
@@ -71,15 +82,15 @@ class ManifoldMarket(BaseModel):
71
82
  pool: ManifoldPool
72
83
  probability: Probability
73
84
  slug: str
74
- totalLiquidity: t.Optional[Mana] = None
85
+ totalLiquidity: t.Optional[CollateralToken] = None
75
86
  uniqueBettorCount: int
76
87
  url: str
77
- volume: Mana
78
- volume24Hours: Mana
88
+ volume: CollateralToken
89
+ volume24Hours: CollateralToken
79
90
 
80
91
  @property
81
- def outcomes(self) -> list[str]:
82
- return list(self.pool.model_fields.keys())
92
+ def outcomes(self) -> t.Sequence[OutcomeStr]:
93
+ return [OutcomeStr(o) for o in self.pool.model_fields.keys()]
83
94
 
84
95
  def get_resolved_boolean_outcome(self) -> bool:
85
96
  if self.resolution == Resolution.YES:
@@ -170,18 +181,18 @@ class ManifoldBet(BaseModel):
170
181
  https://docs.manifold.markets/api#get-v0bets
171
182
  """
172
183
 
173
- shares: float
184
+ shares: CollateralToken
174
185
  probBefore: Probability
175
186
  isFilled: t.Optional[bool] = None
176
187
  probAfter: Probability
177
188
  userId: str
178
- amount: Mana
189
+ amount: CollateralToken
179
190
  contractId: str
180
191
  id: str
181
192
  fees: ManifoldBetFees
182
193
  isCancelled: t.Optional[bool] = None
183
- loanAmount: Mana | None
184
- orderAmount: t.Optional[Mana] = None
194
+ loanAmount: CollateralToken | None
195
+ orderAmount: t.Optional[CollateralToken] = None
185
196
  fills: t.Optional[list[ManifoldBetFills]] = None
186
197
  createdTime: DatetimeUTC
187
198
  outcome: Resolution
@@ -194,16 +205,13 @@ class ManifoldBet(BaseModel):
194
205
  else:
195
206
  should_not_happen(f"Unexpected bet outcome string, '{self.outcome.value}'.")
196
207
 
197
- def get_profit(self, market_outcome: bool) -> ProfitAmount:
208
+ def get_profit(self, market_outcome: bool) -> CollateralToken:
198
209
  profit = (
199
210
  self.shares - self.amount
200
211
  if self.get_resolved_boolean_outcome() == market_outcome
201
212
  else -self.amount
202
213
  )
203
- return ProfitAmount(
204
- amount=profit,
205
- currency=Currency.Mana,
206
- )
214
+ return profit
207
215
 
208
216
 
209
217
  class ManifoldContractMetric(BaseModel):