prediction-market-agent-tooling 0.61.2.dev479__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/config.py +2 -3
  2. prediction_market_agent_tooling/deploy/agent.py +4 -5
  3. prediction_market_agent_tooling/deploy/betting_strategy.py +53 -69
  4. prediction_market_agent_tooling/gtypes.py +105 -27
  5. prediction_market_agent_tooling/jobs/jobs_models.py +5 -7
  6. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +13 -17
  7. prediction_market_agent_tooling/markets/agent_market.py +96 -53
  8. prediction_market_agent_tooling/markets/blockchain_utils.py +1 -27
  9. prediction_market_agent_tooling/markets/data_models.py +40 -44
  10. prediction_market_agent_tooling/markets/manifold/api.py +2 -6
  11. prediction_market_agent_tooling/markets/manifold/data_models.py +33 -25
  12. prediction_market_agent_tooling/markets/manifold/manifold.py +13 -11
  13. prediction_market_agent_tooling/markets/market_fees.py +6 -2
  14. prediction_market_agent_tooling/markets/omen/data_models.py +66 -57
  15. prediction_market_agent_tooling/markets/omen/omen.py +222 -250
  16. prediction_market_agent_tooling/markets/omen/omen_contracts.py +31 -53
  17. prediction_market_agent_tooling/markets/omen/omen_resolving.py +7 -14
  18. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +20 -14
  19. prediction_market_agent_tooling/markets/polymarket/data_models.py +3 -3
  20. prediction_market_agent_tooling/markets/polymarket/data_models_web.py +4 -4
  21. prediction_market_agent_tooling/markets/polymarket/polymarket.py +3 -5
  22. prediction_market_agent_tooling/markets/seer/data_models.py +8 -8
  23. prediction_market_agent_tooling/markets/seer/seer.py +85 -71
  24. prediction_market_agent_tooling/markets/seer/seer_contracts.py +10 -5
  25. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +5 -2
  26. prediction_market_agent_tooling/monitor/monitor.py +2 -2
  27. prediction_market_agent_tooling/tools/_generic_value.py +261 -0
  28. prediction_market_agent_tooling/tools/balances.py +14 -11
  29. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +12 -10
  30. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +31 -24
  31. prediction_market_agent_tooling/tools/betting_strategies/utils.py +3 -1
  32. prediction_market_agent_tooling/tools/contract.py +14 -10
  33. prediction_market_agent_tooling/tools/cow/cow_manager.py +3 -4
  34. prediction_market_agent_tooling/tools/cow/cow_order.py +51 -7
  35. prediction_market_agent_tooling/tools/langfuse_client_utils.py +13 -1
  36. prediction_market_agent_tooling/tools/omen/sell_positions.py +6 -3
  37. prediction_market_agent_tooling/tools/safe.py +5 -6
  38. prediction_market_agent_tooling/tools/tokens/auto_deposit.py +36 -27
  39. prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +4 -25
  40. prediction_market_agent_tooling/tools/tokens/main_token.py +2 -2
  41. prediction_market_agent_tooling/tools/tokens/token_utils.py +46 -0
  42. prediction_market_agent_tooling/tools/tokens/usd.py +79 -0
  43. prediction_market_agent_tooling/tools/utils.py +14 -8
  44. prediction_market_agent_tooling/tools/web3_utils.py +24 -41
  45. {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/METADATA +2 -1
  46. {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/RECORD +49 -47
  47. prediction_market_agent_tooling/abis/gvp2_settlement.abi.json +0 -89
  48. {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/LICENSE +0 -0
  49. {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/WHEEL +0 -0
  50. {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/entry_points.txt +0 -0
@@ -6,17 +6,21 @@ from pydantic import BaseModel, field_validator, model_validator
6
6
  from pydantic_core.core_schema import FieldValidationInfo
7
7
 
8
8
  from prediction_market_agent_tooling.config import APIKeys
9
- 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
+ )
10
15
  from prediction_market_agent_tooling.markets.data_models import (
16
+ USD,
11
17
  Bet,
12
- BetAmount,
13
- Currency,
18
+ CollateralToken,
19
+ ExistingPosition,
14
20
  PlacedTrade,
15
- Position,
16
21
  ProbabilisticAnswer,
17
22
  Resolution,
18
23
  ResolvedBet,
19
- TokenAmount,
20
24
  )
21
25
  from prediction_market_agent_tooling.markets.market_fees import MarketFees
22
26
  from prediction_market_agent_tooling.tools.utils import (
@@ -55,31 +59,28 @@ class AgentMarket(BaseModel):
55
59
  Contains everything that is needed for an agent to make a prediction.
56
60
  """
57
61
 
58
- currency: t.ClassVar[Currency]
59
62
  base_url: t.ClassVar[str]
60
63
 
61
64
  id: str
62
65
  question: str
63
66
  description: str | None
64
- outcomes: list[str]
65
- outcome_token_pool: (
66
- dict[str, float] | None
67
- ) # Should be in currency of `currency` above.
67
+ outcomes: t.Sequence[OutcomeStr]
68
+ outcome_token_pool: dict[str, OutcomeToken] | None
68
69
  resolution: Resolution | None
69
70
  created_time: DatetimeUTC | None
70
71
  close_time: DatetimeUTC | None
71
72
  current_p_yes: Probability
72
73
  url: str
73
- volume: float | None # Should be in currency of `currency` above.
74
+ volume: CollateralToken | None
74
75
  fees: MarketFees
75
76
 
76
77
  @field_validator("outcome_token_pool")
77
78
  def validate_outcome_token_pool(
78
79
  cls,
79
- outcome_token_pool: dict[str, float] | None,
80
+ outcome_token_pool: dict[str, OutcomeToken] | None,
80
81
  info: FieldValidationInfo,
81
- ) -> dict[str, float] | None:
82
- 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"))
83
84
  if outcome_token_pool is not None:
84
85
  outcome_keys = set(outcome_token_pool.keys())
85
86
  expected_keys = set(outcomes)
@@ -102,20 +103,28 @@ class AgentMarket(BaseModel):
102
103
  return Probability(1 - self.current_p_yes)
103
104
 
104
105
  @property
105
- def yes_outcome_price(self) -> float:
106
+ def yes_outcome_price(self) -> CollateralToken:
106
107
  """
107
108
  Price at prediction market is equal to the probability of given outcome.
108
109
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
109
110
  """
110
- 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)
111
116
 
112
117
  @property
113
- def no_outcome_price(self) -> float:
118
+ def no_outcome_price(self) -> CollateralToken:
114
119
  """
115
120
  Price at prediction market is equal to the probability of given outcome.
116
121
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
117
122
  """
118
- 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)
119
128
 
120
129
  @property
121
130
  def probable_resolution(self) -> Resolution:
@@ -150,48 +159,86 @@ class AgentMarket(BaseModel):
150
159
  """
151
160
  raise NotImplementedError("Subclasses must implement this method")
152
161
 
153
- def get_last_trade_yes_outcome_price(self) -> float | None:
162
+ def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
154
163
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
155
164
  # Just making it explicit in this function.
156
165
  if last_trade_p_yes := self.get_last_trade_p_yes():
157
- return float(last_trade_p_yes)
166
+ return CollateralToken(last_trade_p_yes)
158
167
  return None
159
168
 
160
- 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:
161
175
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
162
176
  # Just making it explicit in this function.
163
177
  if last_trade_p_no := self.get_last_trade_p_no():
164
- return float(last_trade_p_no)
178
+ return CollateralToken(last_trade_p_no)
165
179
  return None
166
180
 
167
- def get_bet_amount(self, amount: float) -> BetAmount:
168
- 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
169
185
 
170
- @classmethod
171
- def get_liquidatable_amount(cls) -> BetAmount:
172
- tiny_amount = cls.get_tiny_bet_amount()
173
- tiny_amount.amount /= 10
174
- 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)
175
189
 
176
- @classmethod
177
- 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
+ """
178
225
  raise NotImplementedError("Subclasses must implement this method")
179
226
 
180
227
  def liquidate_existing_positions(self, outcome: bool) -> None:
181
228
  raise NotImplementedError("Subclasses must implement this method")
182
229
 
183
- def place_bet(self, outcome: bool, amount: BetAmount) -> str:
230
+ def place_bet(self, outcome: bool, amount: USD) -> str:
184
231
  raise NotImplementedError("Subclasses must implement this method")
185
232
 
186
- def buy_tokens(self, outcome: bool, amount: TokenAmount) -> str:
233
+ def buy_tokens(self, outcome: bool, amount: USD) -> str:
187
234
  return self.place_bet(outcome=outcome, amount=amount)
188
235
 
189
236
  def get_buy_token_amount(
190
- self, bet_amount: BetAmount, direction: bool
191
- ) -> TokenAmount:
237
+ self, bet_amount: USD | CollateralToken, direction: bool
238
+ ) -> OutcomeToken:
192
239
  raise NotImplementedError("Subclasses must implement this method")
193
240
 
194
- def sell_tokens(self, outcome: bool, amount: TokenAmount) -> str:
241
+ def sell_tokens(self, outcome: bool, amount: USD | OutcomeToken) -> str:
195
242
  raise NotImplementedError("Subclasses must implement this method")
196
243
 
197
244
  @staticmethod
@@ -215,8 +262,8 @@ class AgentMarket(BaseModel):
215
262
  """
216
263
  raise NotImplementedError("Subclasses must implement this method")
217
264
 
218
- @staticmethod
219
- def get_trade_balance(api_keys: APIKeys) -> float:
265
+ @classmethod
266
+ def get_trade_balance(cls, api_keys: APIKeys) -> USD:
220
267
  """
221
268
  Return balance that can be used to trade on the given market.
222
269
  """
@@ -272,11 +319,11 @@ class AgentMarket(BaseModel):
272
319
  def is_resolved(self) -> bool:
273
320
  return self.resolution is not None
274
321
 
275
- def get_liquidity(self) -> TokenAmount:
322
+ def get_liquidity(self) -> CollateralToken:
276
323
  raise NotImplementedError("Subclasses must implement this method")
277
324
 
278
325
  def has_liquidity(self) -> bool:
279
- return self.get_liquidity().amount > 0
326
+ return self.get_liquidity() > 0
280
327
 
281
328
  def has_successful_resolution(self) -> bool:
282
329
  return self.resolution in [Resolution.YES, Resolution.NO]
@@ -287,7 +334,7 @@ class AgentMarket(BaseModel):
287
334
  def get_outcome_str_from_bool(self, outcome: bool) -> OutcomeStr:
288
335
  raise NotImplementedError("Subclasses must implement this method")
289
336
 
290
- def get_outcome_str(self, outcome_index: int) -> str:
337
+ def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
291
338
  try:
292
339
  return self.outcomes[outcome_index]
293
340
  except IndexError:
@@ -301,16 +348,19 @@ class AgentMarket(BaseModel):
301
348
  except ValueError:
302
349
  raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
303
350
 
304
- def get_token_balance(self, user_id: str, outcome: str) -> TokenAmount:
351
+ def get_token_balance(self, user_id: str, outcome: str) -> OutcomeToken:
305
352
  raise NotImplementedError("Subclasses must implement this method")
306
353
 
307
- def get_position(self, user_id: str) -> Position | None:
354
+ def get_position(self, user_id: str) -> ExistingPosition | None:
308
355
  raise NotImplementedError("Subclasses must implement this method")
309
356
 
310
357
  @classmethod
311
358
  def get_positions(
312
- cls, user_id: str, liquid_only: bool = False, larger_than: float = 0
313
- ) -> list[Position]:
359
+ cls,
360
+ user_id: str,
361
+ liquid_only: bool = False,
362
+ larger_than: OutcomeToken = OutcomeToken(0),
363
+ ) -> t.Sequence[ExistingPosition]:
314
364
  """
315
365
  Get all non-zero positions a user has in any market.
316
366
 
@@ -321,13 +371,6 @@ class AgentMarket(BaseModel):
321
371
  """
322
372
  raise NotImplementedError("Subclasses must implement this method")
323
373
 
324
- @classmethod
325
- def get_positions_value(cls, positions: list[Position]) -> BetAmount:
326
- """
327
- Get the total value of all positions held by a user.
328
- """
329
- raise NotImplementedError("Subclasses must implement this method")
330
-
331
374
  def can_be_traded(self) -> bool:
332
375
  if self.is_closed() or not self.has_liquidity():
333
376
  return False
@@ -340,7 +383,7 @@ class AgentMarket(BaseModel):
340
383
  def has_token_pool(self) -> bool:
341
384
  return self.outcome_token_pool is not None
342
385
 
343
- def get_pool_tokens(self, outcome: str) -> float:
386
+ def get_pool_tokens(self, outcome: str) -> OutcomeToken:
344
387
  if not self.outcome_token_pool:
345
388
  raise ValueError("Outcome token pool is not available.")
346
389
 
@@ -2,13 +2,7 @@ from web3 import Web3
2
2
  from web3.constants import HASH_ZERO
3
3
 
4
4
  from prediction_market_agent_tooling.config import APIKeys
5
- from prediction_market_agent_tooling.gtypes import (
6
- ChecksumAddress,
7
- HexBytes,
8
- HexStr,
9
- xDai,
10
- xdai_type,
11
- )
5
+ from prediction_market_agent_tooling.gtypes import HexBytes, HexStr
12
6
  from prediction_market_agent_tooling.loggers import logger
13
7
  from prediction_market_agent_tooling.markets.agent_market import ProcessedTradedMarket
14
8
  from prediction_market_agent_tooling.markets.omen.data_models import (
@@ -18,31 +12,11 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
18
12
  from prediction_market_agent_tooling.markets.omen.omen_contracts import (
19
13
  OmenAgentResultMappingContract,
20
14
  )
21
- from prediction_market_agent_tooling.tools.balances import get_balances
22
15
  from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
23
16
  from prediction_market_agent_tooling.tools.utils import BPS_CONSTANT
24
17
  from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
25
18
 
26
19
 
27
- def get_total_balance(
28
- address: ChecksumAddress,
29
- web3: Web3 | None = None,
30
- sum_xdai: bool = True,
31
- sum_wxdai: bool = True,
32
- ) -> xDai:
33
- """
34
- Checks if the total balance of xDai and wxDai in the wallet is above the minimum required balance.
35
- """
36
- current_balances = get_balances(address, web3)
37
- # xDai and wxDai have equal value and can be exchanged for almost no cost, so we can sum them up.
38
- total_balance = 0.0
39
- if sum_xdai:
40
- total_balance += current_balances.xdai
41
- if sum_wxdai:
42
- total_balance += current_balances.wxdai
43
- return xdai_type(total_balance)
44
-
45
-
46
20
  def store_trades(
47
21
  market_id: str,
48
22
  traded_market: ProcessedTradedMarket | None,
@@ -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):
@@ -2,14 +2,18 @@ import typing as t
2
2
  from math import ceil
3
3
 
4
4
  from prediction_market_agent_tooling.config import APIKeys
5
- from prediction_market_agent_tooling.gtypes import Mana, Probability, mana_type
5
+ from prediction_market_agent_tooling.gtypes import (
6
+ USD,
7
+ CollateralToken,
8
+ Mana,
9
+ Probability,
10
+ )
6
11
  from prediction_market_agent_tooling.markets.agent_market import (
7
12
  AgentMarket,
8
13
  FilterBy,
9
14
  MarketFees,
10
15
  SortBy,
11
16
  )
12
- from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency
13
17
  from prediction_market_agent_tooling.markets.manifold.api import (
14
18
  get_authenticated_user,
15
19
  get_manifold_binary_markets,
@@ -19,6 +23,7 @@ from prediction_market_agent_tooling.markets.manifold.api import (
19
23
  from prediction_market_agent_tooling.markets.manifold.data_models import (
20
24
  MANIFOLD_BASE_URL,
21
25
  FullManifoldMarket,
26
+ usd_to_mana,
22
27
  )
23
28
  from prediction_market_agent_tooling.tools.betting_strategies.minimum_bet_to_win import (
24
29
  minimum_bet_to_win,
@@ -31,7 +36,6 @@ class ManifoldAgentMarket(AgentMarket):
31
36
  Manifold's market class that can be used by agents to make predictions.
32
37
  """
33
38
 
34
- currency: t.ClassVar[Currency] = Currency.Mana
35
39
  base_url: t.ClassVar[str] = MANIFOLD_BASE_URL
36
40
 
37
41
  # Manifold has additional fees than `platform_absolute`, but they don't expose them in the API before placing the bet, see https://docs.manifold.markets/api.
@@ -49,19 +53,17 @@ class ManifoldAgentMarket(AgentMarket):
49
53
  """On Manifold, probablities aren't updated after the closure, so we can just use the current probability"""
50
54
  return self.current_p_no
51
55
 
52
- @classmethod
53
- def get_tiny_bet_amount(cls) -> BetAmount:
54
- return BetAmount(amount=1, currency=cls.currency)
56
+ def get_tiny_bet_amount(self) -> CollateralToken:
57
+ return CollateralToken(1)
55
58
 
56
59
  def get_minimum_bet_to_win(self, answer: bool, amount_to_win: float) -> Mana:
57
60
  # Manifold lowest bet is 1 Mana, so we need to ceil the result.
58
- return mana_type(ceil(minimum_bet_to_win(answer, amount_to_win, self)))
61
+ return Mana(ceil(minimum_bet_to_win(answer, amount_to_win, self)))
59
62
 
60
- def place_bet(self, outcome: bool, amount: BetAmount) -> str:
61
- if amount.currency != self.currency:
62
- raise ValueError(f"Manifold bets are made in Mana. Got {amount.currency}.")
63
+ def place_bet(self, outcome: bool, amount: USD) -> str:
64
+ self.get_usd_in_token(amount)
63
65
  bet = place_bet(
64
- amount=Mana(amount.amount),
66
+ amount=usd_to_mana(amount),
65
67
  market_id=self.id,
66
68
  outcome=outcome,
67
69
  manifold_api_key=APIKeys().manifold_api_key,