prediction-market-agent-tooling 0.61.1.dev462__py3-none-any.whl → 0.61.1.dev477__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 +5 -4
  2. prediction_market_agent_tooling/deploy/betting_strategy.py +69 -53
  3. prediction_market_agent_tooling/gtypes.py +27 -105
  4. prediction_market_agent_tooling/jobs/jobs_models.py +7 -5
  5. prediction_market_agent_tooling/jobs/omen/omen_jobs.py +17 -13
  6. prediction_market_agent_tooling/markets/agent_market.py +52 -96
  7. prediction_market_agent_tooling/markets/blockchain_utils.py +27 -1
  8. prediction_market_agent_tooling/markets/data_models.py +44 -40
  9. prediction_market_agent_tooling/markets/manifold/api.py +6 -2
  10. prediction_market_agent_tooling/markets/manifold/data_models.py +25 -33
  11. prediction_market_agent_tooling/markets/manifold/manifold.py +11 -8
  12. prediction_market_agent_tooling/markets/market_fees.py +2 -4
  13. prediction_market_agent_tooling/markets/omen/data_models.py +57 -66
  14. prediction_market_agent_tooling/markets/omen/omen.py +249 -214
  15. prediction_market_agent_tooling/markets/omen/omen_contracts.py +29 -31
  16. prediction_market_agent_tooling/markets/omen/omen_resolving.py +14 -7
  17. prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +14 -20
  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 +5 -3
  21. prediction_market_agent_tooling/markets/seer/data_models.py +11 -8
  22. prediction_market_agent_tooling/markets/seer/seer.py +71 -85
  23. prediction_market_agent_tooling/markets/seer/seer_contracts.py +5 -10
  24. prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +7 -7
  25. prediction_market_agent_tooling/monitor/monitor.py +2 -2
  26. prediction_market_agent_tooling/tools/balances.py +11 -9
  27. prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +10 -12
  28. prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +24 -27
  29. prediction_market_agent_tooling/tools/betting_strategies/utils.py +1 -3
  30. prediction_market_agent_tooling/tools/contract.py +10 -14
  31. prediction_market_agent_tooling/tools/cow/cow_manager.py +4 -3
  32. prediction_market_agent_tooling/tools/cow/cow_order.py +4 -3
  33. prediction_market_agent_tooling/tools/langfuse_client_utils.py +1 -13
  34. prediction_market_agent_tooling/tools/omen/sell_positions.py +3 -6
  35. prediction_market_agent_tooling/tools/safe.py +6 -5
  36. prediction_market_agent_tooling/tools/tokens/auto_deposit.py +30 -32
  37. prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +22 -5
  38. prediction_market_agent_tooling/tools/tokens/main_token.py +2 -2
  39. prediction_market_agent_tooling/tools/utils.py +8 -14
  40. prediction_market_agent_tooling/tools/web3_utils.py +41 -24
  41. {prediction_market_agent_tooling-0.61.1.dev462.dist-info → prediction_market_agent_tooling-0.61.1.dev477.dist-info}/METADATA +1 -2
  42. {prediction_market_agent_tooling-0.61.1.dev462.dist-info → prediction_market_agent_tooling-0.61.1.dev477.dist-info}/RECORD +45 -48
  43. prediction_market_agent_tooling/tools/_generic_value.py +0 -248
  44. prediction_market_agent_tooling/tools/tokens/token_utils.py +0 -46
  45. prediction_market_agent_tooling/tools/tokens/usd.py +0 -63
  46. {prediction_market_agent_tooling-0.61.1.dev462.dist-info → prediction_market_agent_tooling-0.61.1.dev477.dist-info}/LICENSE +0 -0
  47. {prediction_market_agent_tooling-0.61.1.dev462.dist-info → prediction_market_agent_tooling-0.61.1.dev477.dist-info}/WHEEL +0 -0
  48. {prediction_market_agent_tooling-0.61.1.dev462.dist-info → prediction_market_agent_tooling-0.61.1.dev477.dist-info}/entry_points.txt +0 -0
@@ -6,22 +6,17 @@ 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 (
10
- OutcomeStr,
11
- OutcomeToken,
12
- Probability,
13
- Token,
14
- )
9
+ from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
15
10
  from prediction_market_agent_tooling.markets.data_models import (
16
- USD,
17
11
  Bet,
18
- ExistingPosition,
12
+ BetAmount,
13
+ Currency,
19
14
  PlacedTrade,
20
15
  Position,
21
16
  ProbabilisticAnswer,
22
17
  Resolution,
23
18
  ResolvedBet,
24
- Token,
19
+ TokenAmount,
25
20
  )
26
21
  from prediction_market_agent_tooling.markets.market_fees import MarketFees
27
22
  from prediction_market_agent_tooling.tools.utils import (
@@ -60,28 +55,31 @@ class AgentMarket(BaseModel):
60
55
  Contains everything that is needed for an agent to make a prediction.
61
56
  """
62
57
 
58
+ currency: t.ClassVar[Currency]
63
59
  base_url: t.ClassVar[str]
64
60
 
65
61
  id: str
66
62
  question: str
67
63
  description: str | None
68
- outcomes: t.Sequence[OutcomeStr]
69
- outcome_token_pool: dict[str, OutcomeToken] | None
64
+ outcomes: list[str]
65
+ outcome_token_pool: (
66
+ dict[str, float] | None
67
+ ) # Should be in currency of `currency` above.
70
68
  resolution: Resolution | None
71
69
  created_time: DatetimeUTC | None
72
70
  close_time: DatetimeUTC | None
73
71
  current_p_yes: Probability
74
72
  url: str
75
- volume: Token | None
73
+ volume: float | None # Should be in currency of `currency` above.
76
74
  fees: MarketFees
77
75
 
78
76
  @field_validator("outcome_token_pool")
79
77
  def validate_outcome_token_pool(
80
78
  cls,
81
- outcome_token_pool: dict[str, OutcomeToken] | None,
79
+ outcome_token_pool: dict[str, float] | None,
82
80
  info: FieldValidationInfo,
83
- ) -> dict[str, OutcomeToken] | None:
84
- outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
81
+ ) -> dict[str, float] | None:
82
+ outcomes: list[str] = check_not_none(info.data.get("outcomes"))
85
83
  if outcome_token_pool is not None:
86
84
  outcome_keys = set(outcome_token_pool.keys())
87
85
  expected_keys = set(outcomes)
@@ -104,28 +102,20 @@ class AgentMarket(BaseModel):
104
102
  return Probability(1 - self.current_p_yes)
105
103
 
106
104
  @property
107
- def yes_outcome_price(self) -> Token:
105
+ def yes_outcome_price(self) -> float:
108
106
  """
109
107
  Price at prediction market is equal to the probability of given outcome.
110
108
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
111
109
  """
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)
110
+ return self.current_p_yes
117
111
 
118
112
  @property
119
- def no_outcome_price(self) -> Token:
113
+ def no_outcome_price(self) -> float:
120
114
  """
121
115
  Price at prediction market is equal to the probability of given outcome.
122
116
  Keep as an extra property, in case it wouldn't be true for some prediction market platform.
123
117
  """
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)
118
+ return self.current_p_no
129
119
 
130
120
  @property
131
121
  def probable_resolution(self) -> Resolution:
@@ -160,86 +150,48 @@ class AgentMarket(BaseModel):
160
150
  """
161
151
  raise NotImplementedError("Subclasses must implement this method")
162
152
 
163
- def get_last_trade_yes_outcome_price(self) -> Token | None:
153
+ def get_last_trade_yes_outcome_price(self) -> float | None:
164
154
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
165
155
  # Just making it explicit in this function.
166
156
  if last_trade_p_yes := self.get_last_trade_p_yes():
167
- return Token(last_trade_p_yes)
157
+ return float(last_trade_p_yes)
168
158
  return None
169
159
 
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:
160
+ def get_last_trade_no_outcome_price(self) -> float | None:
176
161
  # Price on prediction markets are, by definition, equal to the probability of an outcome.
177
162
  # Just making it explicit in this function.
178
163
  if last_trade_p_no := self.get_last_trade_p_no():
179
- return Token(last_trade_p_no)
180
- return None
181
-
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)
164
+ return float(last_trade_p_no)
185
165
  return None
186
166
 
187
- def get_liquidatable_amount(self) -> OutcomeToken:
188
- tiny_amount = self.get_tiny_bet_amount()
189
- return OutcomeToken.from_token(tiny_amount / 10)
190
-
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)
167
+ def get_bet_amount(self, amount: float) -> BetAmount:
168
+ return BetAmount(amount=amount, currency=self.currency)
216
169
 
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)
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
221
175
 
222
- def get_tiny_bet_amount(self) -> Token:
223
- """
224
- Tiny bet amount that the platform still allows us to do.
225
- """
176
+ @classmethod
177
+ def get_tiny_bet_amount(cls) -> BetAmount:
226
178
  raise NotImplementedError("Subclasses must implement this method")
227
179
 
228
180
  def liquidate_existing_positions(self, outcome: bool) -> None:
229
181
  raise NotImplementedError("Subclasses must implement this method")
230
182
 
231
- def place_bet(self, outcome: bool, amount: USD) -> str:
183
+ def place_bet(self, outcome: bool, amount: BetAmount) -> str:
232
184
  raise NotImplementedError("Subclasses must implement this method")
233
185
 
234
- def buy_tokens(self, outcome: bool, amount: USD) -> str:
186
+ def buy_tokens(self, outcome: bool, amount: TokenAmount) -> str:
235
187
  return self.place_bet(outcome=outcome, amount=amount)
236
188
 
237
189
  def get_buy_token_amount(
238
- self, bet_amount: USD | Token, direction: bool
239
- ) -> OutcomeToken:
190
+ self, bet_amount: BetAmount, direction: bool
191
+ ) -> TokenAmount:
240
192
  raise NotImplementedError("Subclasses must implement this method")
241
193
 
242
- def sell_tokens(self, outcome: bool, amount: USD | OutcomeToken) -> str:
194
+ def sell_tokens(self, outcome: bool, amount: TokenAmount) -> str:
243
195
  raise NotImplementedError("Subclasses must implement this method")
244
196
 
245
197
  @staticmethod
@@ -263,8 +215,8 @@ class AgentMarket(BaseModel):
263
215
  """
264
216
  raise NotImplementedError("Subclasses must implement this method")
265
217
 
266
- @classmethod
267
- def get_trade_balance(cls, api_keys: APIKeys) -> USD:
218
+ @staticmethod
219
+ def get_trade_balance(api_keys: APIKeys) -> float:
268
220
  """
269
221
  Return balance that can be used to trade on the given market.
270
222
  """
@@ -320,11 +272,11 @@ class AgentMarket(BaseModel):
320
272
  def is_resolved(self) -> bool:
321
273
  return self.resolution is not None
322
274
 
323
- def get_liquidity(self) -> Token:
275
+ def get_liquidity(self) -> TokenAmount:
324
276
  raise NotImplementedError("Subclasses must implement this method")
325
277
 
326
278
  def has_liquidity(self) -> bool:
327
- return self.get_liquidity() > 0
279
+ return self.get_liquidity().amount > 0
328
280
 
329
281
  def has_successful_resolution(self) -> bool:
330
282
  return self.resolution in [Resolution.YES, Resolution.NO]
@@ -335,7 +287,7 @@ class AgentMarket(BaseModel):
335
287
  def get_outcome_str_from_bool(self, outcome: bool) -> OutcomeStr:
336
288
  raise NotImplementedError("Subclasses must implement this method")
337
289
 
338
- def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
290
+ def get_outcome_str(self, outcome_index: int) -> str:
339
291
  try:
340
292
  return self.outcomes[outcome_index]
341
293
  except IndexError:
@@ -349,19 +301,16 @@ class AgentMarket(BaseModel):
349
301
  except ValueError:
350
302
  raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
351
303
 
352
- def get_token_balance(self, user_id: str, outcome: str) -> OutcomeToken:
304
+ def get_token_balance(self, user_id: str, outcome: str) -> TokenAmount:
353
305
  raise NotImplementedError("Subclasses must implement this method")
354
306
 
355
- def get_position(self, user_id: str) -> ExistingPosition | None:
307
+ def get_position(self, user_id: str) -> Position | None:
356
308
  raise NotImplementedError("Subclasses must implement this method")
357
309
 
358
310
  @classmethod
359
311
  def get_positions(
360
- cls,
361
- user_id: str,
362
- liquid_only: bool = False,
363
- larger_than: OutcomeToken = OutcomeToken(0),
364
- ) -> t.Sequence[Position]:
312
+ cls, user_id: str, liquid_only: bool = False, larger_than: float = 0
313
+ ) -> list[Position]:
365
314
  """
366
315
  Get all non-zero positions a user has in any market.
367
316
 
@@ -372,6 +321,13 @@ class AgentMarket(BaseModel):
372
321
  """
373
322
  raise NotImplementedError("Subclasses must implement this method")
374
323
 
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
+
375
331
  def can_be_traded(self) -> bool:
376
332
  if self.is_closed() or not self.has_liquidity():
377
333
  return False
@@ -384,7 +340,7 @@ class AgentMarket(BaseModel):
384
340
  def has_token_pool(self) -> bool:
385
341
  return self.outcome_token_pool is not None
386
342
 
387
- def get_pool_tokens(self, outcome: str) -> OutcomeToken:
343
+ def get_pool_tokens(self, outcome: str) -> float:
388
344
  if not self.outcome_token_pool:
389
345
  raise ValueError("Outcome token pool is not available.")
390
346
 
@@ -2,7 +2,13 @@ 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 HexBytes, HexStr
5
+ from prediction_market_agent_tooling.gtypes import (
6
+ ChecksumAddress,
7
+ HexBytes,
8
+ HexStr,
9
+ xDai,
10
+ xdai_type,
11
+ )
6
12
  from prediction_market_agent_tooling.loggers import logger
7
13
  from prediction_market_agent_tooling.markets.agent_market import ProcessedTradedMarket
8
14
  from prediction_market_agent_tooling.markets.omen.data_models import (
@@ -12,11 +18,31 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
12
18
  from prediction_market_agent_tooling.markets.omen.omen_contracts import (
13
19
  OmenAgentResultMappingContract,
14
20
  )
21
+ from prediction_market_agent_tooling.tools.balances import get_balances
15
22
  from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
16
23
  from prediction_market_agent_tooling.tools.utils import BPS_CONSTANT
17
24
  from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
18
25
 
19
26
 
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
+
20
46
  def store_trades(
21
47
  market_id: str,
22
48
  traded_market: ProcessedTradedMarket | None,
@@ -1,18 +1,19 @@
1
1
  from enum import Enum
2
- from typing import Annotated
2
+ from typing import Annotated, TypeAlias
3
3
 
4
4
  from pydantic import BaseModel, BeforeValidator, computed_field
5
5
 
6
- from prediction_market_agent_tooling.gtypes import (
7
- USD,
8
- OutcomeStr,
9
- OutcomeToken,
10
- Probability,
11
- Token,
12
- )
6
+ from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
13
7
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC
14
8
 
15
9
 
10
+ class Currency(str, Enum):
11
+ xDai = "xDai"
12
+ sDai = "sDai"
13
+ Mana = "Mana"
14
+ USDC = "USDC"
15
+
16
+
16
17
  class Resolution(str, Enum):
17
18
  YES = "YES"
18
19
  NO = "NO"
@@ -24,9 +25,21 @@ class Resolution(str, Enum):
24
25
  return Resolution.YES if value else Resolution.NO
25
26
 
26
27
 
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
+
27
40
  class Bet(BaseModel):
28
41
  id: str
29
- amount: Token
42
+ amount: BetAmount
30
43
  outcome: bool
31
44
  created_time: DatetimeUTC
32
45
  market_question: str
@@ -39,7 +52,7 @@ class Bet(BaseModel):
39
52
  class ResolvedBet(Bet):
40
53
  market_outcome: bool
41
54
  resolved_time: DatetimeUTC
42
- profit: Token
55
+ profit: ProfitAmount
43
56
 
44
57
  @computed_field # type: ignore[prop-decorator]
45
58
  @property
@@ -50,6 +63,10 @@ class ResolvedBet(Bet):
50
63
  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}"
51
64
 
52
65
 
66
+ class TokenAmountAndDirection(TokenAmount):
67
+ direction: bool
68
+
69
+
53
70
  def to_boolean_outcome(value: str | bool) -> bool:
54
71
  if isinstance(value, bool):
55
72
  return value
@@ -85,36 +102,23 @@ class ProbabilisticAnswer(BaseModel):
85
102
 
86
103
  class Position(BaseModel):
87
104
  market_id: str
88
- # This is for how much we could buy or sell the position right now.
89
- amounts_current: dict[OutcomeStr, USD]
105
+ amounts: dict[OutcomeStr, TokenAmount]
90
106
 
91
107
  @property
92
- def total_amount_current(self) -> USD:
93
- return sum(self.amounts_current.values(), start=USD(0))
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
+ )
94
113
 
95
114
  def __str__(self) -> str:
96
115
  amounts_str = ", ".join(
97
- f"{amount} USD of '{outcome}' tokens"
98
- for outcome, amount in self.amounts_current.items()
116
+ f"{amount.amount} '{outcome}' tokens"
117
+ for outcome, amount in self.amounts.items()
99
118
  )
100
119
  return f"Position for market id {self.market_id}: {amounts_str}"
101
120
 
102
121
 
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
-
118
122
  class TradeType(str, Enum):
119
123
  SELL = "sell"
120
124
  BUY = "buy"
@@ -123,7 +127,7 @@ class TradeType(str, Enum):
123
127
  class Trade(BaseModel):
124
128
  trade_type: TradeType
125
129
  outcome: bool
126
- amount: USD
130
+ amount: TokenAmount
127
131
 
128
132
 
129
133
  class PlacedTrade(Trade):
@@ -145,12 +149,12 @@ class SimulatedBetDetail(BaseModel):
145
149
  market_p_yes: float
146
150
  agent_p_yes: float
147
151
  agent_conf: float
148
- org_bet: Token
149
- sim_bet: Token
152
+ org_bet: float
153
+ sim_bet: float
150
154
  org_dir: bool
151
155
  sim_dir: bool
152
- org_profit: Token
153
- sim_profit: Token
156
+ org_profit: float
157
+ sim_profit: float
154
158
  timestamp: DatetimeUTC
155
159
 
156
160
 
@@ -162,10 +166,10 @@ class SharpeOutput(BaseModel):
162
166
 
163
167
  class SimulatedLifetimeDetail(BaseModel):
164
168
  p_yes_mse: float
165
- total_bet_amount: Token
166
- total_bet_profit: Token
167
- total_simulated_amount: Token
168
- total_simulated_profit: Token
169
+ total_bet_amount: float
170
+ total_bet_profit: float
171
+ total_simulated_amount: float
172
+ total_simulated_profit: float
169
173
  roi: float
170
174
  simulated_roi: float
171
175
  sharpe_output_original: SharpeOutput
@@ -5,7 +5,11 @@ 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 ResolvedBet
8
+ from prediction_market_agent_tooling.markets.data_models import (
9
+ BetAmount,
10
+ Currency,
11
+ ResolvedBet,
12
+ )
9
13
  from prediction_market_agent_tooling.markets.manifold.data_models import (
10
14
  FullManifoldMarket,
11
15
  ManifoldBet,
@@ -207,7 +211,7 @@ def manifold_to_generic_resolved_bet(
207
211
  market_outcome = market.get_resolved_boolean_outcome()
208
212
  return ResolvedBet(
209
213
  id=bet.id,
210
- amount=bet.amount,
214
+ amount=BetAmount(amount=bet.amount, currency=Currency.Mana),
211
215
  outcome=bet.get_resolved_boolean_outcome(),
212
216
  created_time=bet.createdTime,
213
217
  market_question=market.question,
@@ -3,37 +3,24 @@ from enum import Enum
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
6
- from prediction_market_agent_tooling.gtypes import (
7
- USD,
8
- Mana,
9
- OutcomeStr,
10
- OutcomeToken,
11
- Probability,
12
- Token,
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,
13
11
  )
14
- from prediction_market_agent_tooling.markets.data_models import Resolution
15
12
  from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
16
13
 
17
14
  MANIFOLD_BASE_URL = "https://manifold.markets"
18
15
 
19
16
 
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
-
30
17
  class ManifoldPool(BaseModel):
31
- NO: OutcomeToken
32
- YES: OutcomeToken
18
+ NO: float
19
+ YES: float
33
20
 
34
- def size_for_outcome(self, outcome: str) -> OutcomeToken:
21
+ def size_for_outcome(self, outcome: str) -> float:
35
22
  if hasattr(self, outcome):
36
- return OutcomeToken(getattr(self, outcome))
23
+ return float(getattr(self, outcome))
37
24
  else:
38
25
  should_not_happen(f"Unexpected outcome string, '{outcome}'.")
39
26
 
@@ -62,6 +49,8 @@ class ManifoldMarket(BaseModel):
62
49
  https://docs.manifold.markets/api#get-v0markets
63
50
  """
64
51
 
52
+ BET_AMOUNT_CURRENCY: t.ClassVar[Currency] = Currency.Mana
53
+
65
54
  id: str
66
55
  question: str
67
56
  creatorId: str
@@ -82,15 +71,15 @@ class ManifoldMarket(BaseModel):
82
71
  pool: ManifoldPool
83
72
  probability: Probability
84
73
  slug: str
85
- totalLiquidity: t.Optional[Token] = None
74
+ totalLiquidity: t.Optional[Mana] = None
86
75
  uniqueBettorCount: int
87
76
  url: str
88
- volume: Token
89
- volume24Hours: Token
77
+ volume: Mana
78
+ volume24Hours: Mana
90
79
 
91
80
  @property
92
- def outcomes(self) -> t.Sequence[OutcomeStr]:
93
- return [OutcomeStr(o) for o in self.pool.model_fields.keys()]
81
+ def outcomes(self) -> list[str]:
82
+ return list(self.pool.model_fields.keys())
94
83
 
95
84
  def get_resolved_boolean_outcome(self) -> bool:
96
85
  if self.resolution == Resolution.YES:
@@ -181,18 +170,18 @@ class ManifoldBet(BaseModel):
181
170
  https://docs.manifold.markets/api#get-v0bets
182
171
  """
183
172
 
184
- shares: Token
173
+ shares: float
185
174
  probBefore: Probability
186
175
  isFilled: t.Optional[bool] = None
187
176
  probAfter: Probability
188
177
  userId: str
189
- amount: Token
178
+ amount: Mana
190
179
  contractId: str
191
180
  id: str
192
181
  fees: ManifoldBetFees
193
182
  isCancelled: t.Optional[bool] = None
194
- loanAmount: Token | None
195
- orderAmount: t.Optional[Token] = None
183
+ loanAmount: Mana | None
184
+ orderAmount: t.Optional[Mana] = None
196
185
  fills: t.Optional[list[ManifoldBetFills]] = None
197
186
  createdTime: DatetimeUTC
198
187
  outcome: Resolution
@@ -205,13 +194,16 @@ class ManifoldBet(BaseModel):
205
194
  else:
206
195
  should_not_happen(f"Unexpected bet outcome string, '{self.outcome.value}'.")
207
196
 
208
- def get_profit(self, market_outcome: bool) -> Token:
197
+ def get_profit(self, market_outcome: bool) -> ProfitAmount:
209
198
  profit = (
210
199
  self.shares - self.amount
211
200
  if self.get_resolved_boolean_outcome() == market_outcome
212
201
  else -self.amount
213
202
  )
214
- return profit
203
+ return ProfitAmount(
204
+ amount=profit,
205
+ currency=Currency.Mana,
206
+ )
215
207
 
216
208
 
217
209
  class ManifoldContractMetric(BaseModel):
@@ -2,13 +2,14 @@ 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 USD, Mana, Probability, Token
5
+ from prediction_market_agent_tooling.gtypes import Mana, Probability, mana_type
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
12
13
  from prediction_market_agent_tooling.markets.manifold.api import (
13
14
  get_authenticated_user,
14
15
  get_manifold_binary_markets,
@@ -18,7 +19,6 @@ from prediction_market_agent_tooling.markets.manifold.api import (
18
19
  from prediction_market_agent_tooling.markets.manifold.data_models import (
19
20
  MANIFOLD_BASE_URL,
20
21
  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,6 +31,7 @@ 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
34
35
  base_url: t.ClassVar[str] = MANIFOLD_BASE_URL
35
36
 
36
37
  # 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.
@@ -48,17 +49,19 @@ class ManifoldAgentMarket(AgentMarket):
48
49
  """On Manifold, probablities aren't updated after the closure, so we can just use the current probability"""
49
50
  return self.current_p_no
50
51
 
51
- def get_tiny_bet_amount(self) -> Token:
52
- return Token(1)
52
+ @classmethod
53
+ def get_tiny_bet_amount(cls) -> BetAmount:
54
+ return BetAmount(amount=1, currency=cls.currency)
53
55
 
54
56
  def get_minimum_bet_to_win(self, answer: bool, amount_to_win: float) -> Mana:
55
57
  # Manifold lowest bet is 1 Mana, so we need to ceil the result.
56
- return Mana(ceil(minimum_bet_to_win(answer, amount_to_win, self)))
58
+ return mana_type(ceil(minimum_bet_to_win(answer, amount_to_win, self)))
57
59
 
58
- def place_bet(self, outcome: bool, amount: USD) -> str:
59
- self.get_usd_in_token(amount)
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}.")
60
63
  bet = place_bet(
61
- amount=usd_to_mana(amount),
64
+ amount=Mana(amount.amount),
62
65
  market_id=self.id,
63
66
  outcome=outcome,
64
67
  manifold_api_key=APIKeys().manifold_api_key,