prediction-market-agent-tooling 0.61.1__py3-none-any.whl → 0.61.1.dev462__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 (48) 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 -52
  7. prediction_market_agent_tooling/markets/blockchain_utils.py +1 -27
  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 +8 -11
  12. prediction_market_agent_tooling/markets/market_fees.py +4 -2
  13. prediction_market_agent_tooling/markets/omen/data_models.py +66 -57
  14. prediction_market_agent_tooling/markets/omen/omen.py +214 -249
  15. prediction_market_agent_tooling/markets/omen/omen_contracts.py +31 -29
  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 +8 -8
  22. prediction_market_agent_tooling/markets/seer/seer.py +85 -71
  23. prediction_market_agent_tooling/markets/seer/seer_contracts.py +10 -5
  24. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +5 -2
  25. prediction_market_agent_tooling/monitor/monitor.py +2 -2
  26. prediction_market_agent_tooling/tools/_generic_value.py +248 -0
  27. prediction_market_agent_tooling/tools/balances.py +9 -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 +27 -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 +3 -4
  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 +32 -30
  38. prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +5 -22
  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 +63 -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.dist-info → prediction_market_agent_tooling-0.61.1.dev462.dist-info}/METADATA +2 -1
  45. {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev462.dist-info}/RECORD +48 -45
  46. {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev462.dist-info}/LICENSE +0 -0
  47. {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev462.dist-info}/WHEEL +0 -0
  48. {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev462.dist-info}/entry_points.txt +0 -0
@@ -6,17 +6,22 @@ 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
+ OutcomeStr,
11
+ OutcomeToken,
12
+ Probability,
13
+ Token,
14
+ )
10
15
  from prediction_market_agent_tooling.markets.data_models import (
16
+ USD,
11
17
  Bet,
12
- BetAmount,
13
- Currency,
18
+ ExistingPosition,
14
19
  PlacedTrade,
15
20
  Position,
16
21
  ProbabilisticAnswer,
17
22
  Resolution,
18
23
  ResolvedBet,
19
- TokenAmount,
24
+ Token,
20
25
  )
21
26
  from prediction_market_agent_tooling.markets.market_fees import MarketFees
22
27
  from prediction_market_agent_tooling.tools.utils import (
@@ -55,31 +60,28 @@ class AgentMarket(BaseModel):
55
60
  Contains everything that is needed for an agent to make a prediction.
56
61
  """
57
62
 
58
- currency: t.ClassVar[Currency]
59
63
  base_url: t.ClassVar[str]
60
64
 
61
65
  id: str
62
66
  question: str
63
67
  description: str | None
64
- outcomes: list[str]
65
- outcome_token_pool: (
66
- dict[str, float] | None
67
- ) # Should be in currency of `currency` above.
68
+ outcomes: t.Sequence[OutcomeStr]
69
+ outcome_token_pool: dict[str, OutcomeToken] | None
68
70
  resolution: Resolution | None
69
71
  created_time: DatetimeUTC | None
70
72
  close_time: DatetimeUTC | None
71
73
  current_p_yes: Probability
72
74
  url: str
73
- volume: float | None # Should be in currency of `currency` above.
75
+ volume: Token | None
74
76
  fees: MarketFees
75
77
 
76
78
  @field_validator("outcome_token_pool")
77
79
  def validate_outcome_token_pool(
78
80
  cls,
79
- outcome_token_pool: dict[str, float] | None,
81
+ outcome_token_pool: dict[str, OutcomeToken] | None,
80
82
  info: FieldValidationInfo,
81
- ) -> dict[str, float] | None:
82
- outcomes: list[str] = check_not_none(info.data.get("outcomes"))
83
+ ) -> dict[str, OutcomeToken] | None:
84
+ outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
83
85
  if outcome_token_pool is not None:
84
86
  outcome_keys = set(outcome_token_pool.keys())
85
87
  expected_keys = set(outcomes)
@@ -102,20 +104,28 @@ class AgentMarket(BaseModel):
102
104
  return Probability(1 - self.current_p_yes)
103
105
 
104
106
  @property
105
- def yes_outcome_price(self) -> float:
107
+ def yes_outcome_price(self) -> Token:
106
108
  """
107
109
  Price at prediction market is equal to the probability of given outcome.
108
110
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
109
111
  """
110
- return self.current_p_yes
112
+ return Token(self.current_p_yes)
113
+
114
+ @property
115
+ def yes_outcome_price_usd(self) -> USD:
116
+ return self.get_token_in_usd(self.yes_outcome_price)
111
117
 
112
118
  @property
113
- def no_outcome_price(self) -> float:
119
+ def no_outcome_price(self) -> Token:
114
120
  """
115
121
  Price at prediction market is equal to the probability of given outcome.
116
122
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
117
123
  """
118
- return self.current_p_no
124
+ return Token(self.current_p_no)
125
+
126
+ @property
127
+ def no_outcome_price_usd(self) -> USD:
128
+ return self.get_token_in_usd(self.no_outcome_price)
119
129
 
120
130
  @property
121
131
  def probable_resolution(self) -> Resolution:
@@ -150,48 +160,86 @@ class AgentMarket(BaseModel):
150
160
  """
151
161
  raise NotImplementedError("Subclasses must implement this method")
152
162
 
153
- def get_last_trade_yes_outcome_price(self) -> float | None:
163
+ def get_last_trade_yes_outcome_price(self) -> Token | None:
154
164
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
155
165
  # Just making it explicit in this function.
156
166
  if last_trade_p_yes := self.get_last_trade_p_yes():
157
- return float(last_trade_p_yes)
167
+ return Token(last_trade_p_yes)
158
168
  return None
159
169
 
160
- def get_last_trade_no_outcome_price(self) -> float | None:
170
+ def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
171
+ if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
172
+ return self.get_token_in_usd(last_trade_yes_outcome_price)
173
+ return None
174
+
175
+ def get_last_trade_no_outcome_price(self) -> Token | None:
161
176
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
162
177
  # Just making it explicit in this function.
163
178
  if last_trade_p_no := self.get_last_trade_p_no():
164
- return float(last_trade_p_no)
179
+ return Token(last_trade_p_no)
165
180
  return None
166
181
 
167
- def get_bet_amount(self, amount: float) -> BetAmount:
168
- return BetAmount(amount=amount, currency=self.currency)
182
+ def get_last_trade_no_outcome_price_usd(self) -> USD | None:
183
+ if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
184
+ return self.get_token_in_usd(last_trade_no_outcome_price)
185
+ return None
169
186
 
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
187
+ def get_liquidatable_amount(self) -> OutcomeToken:
188
+ tiny_amount = self.get_tiny_bet_amount()
189
+ return OutcomeToken.from_token(tiny_amount / 10)
175
190
 
176
- @classmethod
177
- def get_tiny_bet_amount(cls) -> BetAmount:
191
+ def get_token_in_usd(self, x: Token) -> USD:
192
+ """
193
+ 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.
194
+ """
195
+ raise NotImplementedError("Subclasses must implement this method")
196
+
197
+ def get_usd_in_token(self, x: USD) -> Token:
198
+ """
199
+ 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.
200
+ """
201
+ raise NotImplementedError("Subclasses must implement this method")
202
+
203
+ def get_sell_value_of_outcome_token(
204
+ self, outcome: str, amount: OutcomeToken
205
+ ) -> Token:
206
+ """
207
+ 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).
208
+ But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
209
+ """
210
+ raise NotImplementedError("Subclasses must implement this method")
211
+
212
+ def get_in_usd(self, x: USD | Token) -> USD:
213
+ if isinstance(x, USD):
214
+ return x
215
+ return self.get_token_in_usd(x)
216
+
217
+ def get_in_token(self, x: USD | Token) -> Token:
218
+ if isinstance(x, Token):
219
+ return x
220
+ return self.get_usd_in_token(x)
221
+
222
+ def get_tiny_bet_amount(self) -> Token:
223
+ """
224
+ Tiny bet amount that the platform still allows us to do.
225
+ """
178
226
  raise NotImplementedError("Subclasses must implement this method")
179
227
 
180
228
  def liquidate_existing_positions(self, outcome: bool) -> None:
181
229
  raise NotImplementedError("Subclasses must implement this method")
182
230
 
183
- def place_bet(self, outcome: bool, amount: BetAmount) -> str:
231
+ def place_bet(self, outcome: bool, amount: USD) -> str:
184
232
  raise NotImplementedError("Subclasses must implement this method")
185
233
 
186
- def buy_tokens(self, outcome: bool, amount: TokenAmount) -> str:
234
+ def buy_tokens(self, outcome: bool, amount: USD) -> str:
187
235
  return self.place_bet(outcome=outcome, amount=amount)
188
236
 
189
237
  def get_buy_token_amount(
190
- self, bet_amount: BetAmount, direction: bool
191
- ) -> TokenAmount:
238
+ self, bet_amount: USD | Token, direction: bool
239
+ ) -> OutcomeToken:
192
240
  raise NotImplementedError("Subclasses must implement this method")
193
241
 
194
- def sell_tokens(self, outcome: bool, amount: TokenAmount) -> str:
242
+ def sell_tokens(self, outcome: bool, amount: USD | OutcomeToken) -> str:
195
243
  raise NotImplementedError("Subclasses must implement this method")
196
244
 
197
245
  @staticmethod
@@ -215,8 +263,8 @@ class AgentMarket(BaseModel):
215
263
  """
216
264
  raise NotImplementedError("Subclasses must implement this method")
217
265
 
218
- @staticmethod
219
- def get_trade_balance(api_keys: APIKeys) -> float:
266
+ @classmethod
267
+ def get_trade_balance(cls, api_keys: APIKeys) -> USD:
220
268
  """
221
269
  Return balance that can be used to trade on the given market.
222
270
  """
@@ -272,11 +320,11 @@ class AgentMarket(BaseModel):
272
320
  def is_resolved(self) -> bool:
273
321
  return self.resolution is not None
274
322
 
275
- def get_liquidity(self) -> TokenAmount:
323
+ def get_liquidity(self) -> Token:
276
324
  raise NotImplementedError("Subclasses must implement this method")
277
325
 
278
326
  def has_liquidity(self) -> bool:
279
- return self.get_liquidity().amount > 0
327
+ return self.get_liquidity() > 0
280
328
 
281
329
  def has_successful_resolution(self) -> bool:
282
330
  return self.resolution in [Resolution.YES, Resolution.NO]
@@ -287,7 +335,7 @@ class AgentMarket(BaseModel):
287
335
  def get_outcome_str_from_bool(self, outcome: bool) -> OutcomeStr:
288
336
  raise NotImplementedError("Subclasses must implement this method")
289
337
 
290
- def get_outcome_str(self, outcome_index: int) -> str:
338
+ def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
291
339
  try:
292
340
  return self.outcomes[outcome_index]
293
341
  except IndexError:
@@ -301,16 +349,19 @@ class AgentMarket(BaseModel):
301
349
  except ValueError:
302
350
  raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
303
351
 
304
- def get_token_balance(self, user_id: str, outcome: str) -> TokenAmount:
352
+ def get_token_balance(self, user_id: str, outcome: str) -> OutcomeToken:
305
353
  raise NotImplementedError("Subclasses must implement this method")
306
354
 
307
- def get_position(self, user_id: str) -> Position | None:
355
+ def get_position(self, user_id: str) -> ExistingPosition | None:
308
356
  raise NotImplementedError("Subclasses must implement this method")
309
357
 
310
358
  @classmethod
311
359
  def get_positions(
312
- cls, user_id: str, liquid_only: bool = False, larger_than: float = 0
313
- ) -> list[Position]:
360
+ cls,
361
+ user_id: str,
362
+ liquid_only: bool = False,
363
+ larger_than: OutcomeToken = OutcomeToken(0),
364
+ ) -> t.Sequence[Position]:
314
365
  """
315
366
  Get all non-zero positions a user has in any market.
316
367
 
@@ -321,13 +372,6 @@ class AgentMarket(BaseModel):
321
372
  """
322
373
  raise NotImplementedError("Subclasses must implement this method")
323
374
 
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
375
  def can_be_traded(self) -> bool:
332
376
  if self.is_closed() or not self.has_liquidity():
333
377
  return False
@@ -340,7 +384,7 @@ class AgentMarket(BaseModel):
340
384
  def has_token_pool(self) -> bool:
341
385
  return self.outcome_token_pool is not None
342
386
 
343
- def get_pool_tokens(self, outcome: str) -> float:
387
+ def get_pool_tokens(self, outcome: str) -> OutcomeToken:
344
388
  if not self.outcome_token_pool:
345
389
  raise ValueError("Outcome token pool is not available.")
346
390
 
@@ -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
+ OutcomeStr,
9
+ OutcomeToken,
10
+ Probability,
11
+ Token,
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: Token
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: Token
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: Token
149
+ sim_bet: Token
154
150
  org_dir: bool
155
151
  sim_dir: bool
156
- org_profit: float
157
- sim_profit: float
152
+ org_profit: Token
153
+ sim_profit: Token
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: Token
166
+ total_bet_profit: Token
167
+ total_simulated_amount: Token
168
+ total_simulated_profit: Token
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
+ Mana,
9
+ OutcomeStr,
10
+ OutcomeToken,
11
+ Probability,
12
+ Token,
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[Token] = None
75
86
  uniqueBettorCount: int
76
87
  url: str
77
- volume: Mana
78
- volume24Hours: Mana
88
+ volume: Token
89
+ volume24Hours: Token
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: Token
174
185
  probBefore: Probability
175
186
  isFilled: t.Optional[bool] = None
176
187
  probAfter: Probability
177
188
  userId: str
178
- amount: Mana
189
+ amount: Token
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: Token | None
195
+ orderAmount: t.Optional[Token] = 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) -> Token:
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,13 @@ 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 USD, Mana, Probability, Token
6
6
  from prediction_market_agent_tooling.markets.agent_market import (
7
7
  AgentMarket,
8
8
  FilterBy,
9
9
  MarketFees,
10
10
  SortBy,
11
11
  )
12
- from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency
13
12
  from prediction_market_agent_tooling.markets.manifold.api import (
14
13
  get_authenticated_user,
15
14
  get_manifold_binary_markets,
@@ -19,6 +18,7 @@ from prediction_market_agent_tooling.markets.manifold.api import (
19
18
  from prediction_market_agent_tooling.markets.manifold.data_models import (
20
19
  MANIFOLD_BASE_URL,
21
20
  FullManifoldMarket,
21
+ usd_to_mana,
22
22
  )
23
23
  from prediction_market_agent_tooling.tools.betting_strategies.minimum_bet_to_win import (
24
24
  minimum_bet_to_win,
@@ -31,7 +31,6 @@ class ManifoldAgentMarket(AgentMarket):
31
31
  Manifold's market class that can be used by agents to make predictions.
32
32
  """
33
33
 
34
- currency: t.ClassVar[Currency] = Currency.Mana
35
34
  base_url: t.ClassVar[str] = MANIFOLD_BASE_URL
36
35
 
37
36
  # 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 +48,17 @@ class ManifoldAgentMarket(AgentMarket):
49
48
  """On Manifold, probablities aren't updated after the closure, so we can just use the current probability"""
50
49
  return self.current_p_no
51
50
 
52
- @classmethod
53
- def get_tiny_bet_amount(cls) -> BetAmount:
54
- return BetAmount(amount=1, currency=cls.currency)
51
+ def get_tiny_bet_amount(self) -> Token:
52
+ return Token(1)
55
53
 
56
54
  def get_minimum_bet_to_win(self, answer: bool, amount_to_win: float) -> Mana:
57
55
  # 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)))
56
+ return Mana(ceil(minimum_bet_to_win(answer, amount_to_win, self)))
59
57
 
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}.")
58
+ def place_bet(self, outcome: bool, amount: USD) -> str:
59
+ self.get_usd_in_token(amount)
63
60
  bet = place_bet(
64
- amount=Mana(amount.amount),
61
+ amount=usd_to_mana(amount),
65
62
  market_id=self.id,
66
63
  outcome=outcome,
67
64
  manifold_api_key=APIKeys().manifold_api_key,