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
@@ -3,7 +3,6 @@ import typing as t
3
3
  from copy import deepcopy
4
4
 
5
5
  from eth_account.signers.local import LocalAccount
6
- from eth_typing import URI
7
6
  from pydantic import Field, model_validator
8
7
  from pydantic.types import SecretStr
9
8
  from pydantic.v1.types import SecretStr as SecretStrV1
@@ -278,11 +277,11 @@ class RPCConfig(BaseSettings):
278
277
  env_file=".env", env_file_encoding="utf-8", extra="ignore"
279
278
  )
280
279
 
281
- GNOSIS_RPC_URL: URI = Field(default=URI("https://rpc.gnosischain.com"))
280
+ GNOSIS_RPC_URL: str = Field(default="https://rpc.gnosischain.com")
282
281
  CHAIN_ID: ChainID = Field(default=ChainID(100))
283
282
 
284
283
  @property
285
- def gnosis_rpc_url(self) -> URI:
284
+ def gnosis_rpc_url(self) -> str:
286
285
  return check_not_none(
287
286
  self.GNOSIS_RPC_URL, "GNOSIS_RPC_URL missing in the environment."
288
287
  )
@@ -40,8 +40,8 @@ from prediction_market_agent_tooling.markets.agent_market import (
40
40
  SortBy,
41
41
  )
42
42
  from prediction_market_agent_tooling.markets.data_models import (
43
+ ExistingPosition,
43
44
  PlacedTrade,
44
- Position,
45
45
  ProbabilisticAnswer,
46
46
  Trade,
47
47
  )
@@ -582,9 +582,9 @@ class DeployableTraderAgent(DeployablePredictionAgent):
582
582
  """
583
583
  user_id = market.get_user_id(api_keys=APIKeys())
584
584
 
585
- total_amount = market.get_tiny_bet_amount().amount
585
+ total_amount = market.get_in_usd(market.get_tiny_bet_amount())
586
586
  if existing_position := market.get_position(user_id=user_id):
587
- total_amount += existing_position.total_amount.amount
587
+ total_amount += existing_position.total_amount_current
588
588
 
589
589
  return MaxAccuracyBettingStrategy(bet_amount=total_amount)
590
590
 
@@ -592,11 +592,10 @@ class DeployableTraderAgent(DeployablePredictionAgent):
592
592
  self,
593
593
  market: AgentMarket,
594
594
  answer: ProbabilisticAnswer,
595
- existing_position: Position | None,
595
+ existing_position: ExistingPosition | None,
596
596
  ) -> list[Trade]:
597
597
  strategy = self.get_betting_strategy(market=market)
598
598
  trades = strategy.calculate_trades(existing_position, answer, market)
599
- BettingStrategy.assert_trades_currency_match_markets(market, trades)
600
599
  return trades
601
600
 
602
601
  def before_process_market(
@@ -2,14 +2,13 @@ from abc import ABC, abstractmethod
2
2
 
3
3
  from scipy.optimize import minimize_scalar
4
4
 
5
- from prediction_market_agent_tooling.gtypes import xDai
5
+ from prediction_market_agent_tooling.gtypes import USD, CollateralToken, OutcomeToken
6
6
  from prediction_market_agent_tooling.loggers import logger
7
7
  from prediction_market_agent_tooling.markets.agent_market import AgentMarket, MarketFees
8
8
  from prediction_market_agent_tooling.markets.data_models import (
9
- Currency,
9
+ ExistingPosition,
10
10
  Position,
11
11
  ProbabilisticAnswer,
12
- TokenAmount,
13
12
  Trade,
14
13
  TradeType,
15
14
  )
@@ -31,7 +30,7 @@ class BettingStrategy(ABC):
31
30
  @abstractmethod
32
31
  def calculate_trades(
33
32
  self,
34
- existing_position: Position | None,
33
+ existing_position: ExistingPosition | None,
35
34
  answer: ProbabilisticAnswer,
36
35
  market: AgentMarket,
37
36
  ) -> list[Trade]:
@@ -39,21 +38,11 @@ class BettingStrategy(ABC):
39
38
 
40
39
  @property
41
40
  @abstractmethod
42
- def maximum_possible_bet_amount(self) -> float:
41
+ def maximum_possible_bet_amount(self) -> USD:
43
42
  raise NotImplementedError("Subclass should implement this.")
44
43
 
45
- def build_zero_token_amount(self, currency: Currency) -> TokenAmount:
46
- return TokenAmount(amount=0, currency=currency)
47
-
48
- @staticmethod
49
- def assert_trades_currency_match_markets(
50
- market: AgentMarket, trades: list[Trade]
51
- ) -> None:
52
- currencies_match = all([t.amount.currency == market.currency for t in trades])
53
- if not currencies_match:
54
- raise ValueError(
55
- "Cannot handle trades with currencies that deviate from market's currency"
56
- )
44
+ def build_zero_usd_amount(self) -> USD:
45
+ return USD(0)
57
46
 
58
47
  @staticmethod
59
48
  def assert_buy_trade_wont_be_guaranteed_loss(
@@ -64,20 +53,21 @@ class BettingStrategy(ABC):
64
53
  outcome_tokens_to_get = market.get_buy_token_amount(
65
54
  trade.amount, trade.outcome
66
55
  )
67
-
68
- if outcome_tokens_to_get.amount < trade.amount.amount:
56
+ outcome_tokens_to_get_in_usd = market.get_token_in_usd(
57
+ outcome_tokens_to_get.as_token
58
+ )
59
+ if outcome_tokens_to_get_in_usd <= trade.amount:
69
60
  raise GuaranteedLossError(
70
61
  f"Trade {trade=} would result in guaranteed loss by getting only {outcome_tokens_to_get=}."
71
62
  )
72
63
 
73
64
  @staticmethod
74
65
  def check_trades(market: AgentMarket, trades: list[Trade]) -> None:
75
- BettingStrategy.assert_trades_currency_match_markets(market, trades)
76
66
  BettingStrategy.assert_buy_trade_wont_be_guaranteed_loss(market, trades)
77
67
 
78
68
  def _build_rebalance_trades_from_positions(
79
69
  self,
80
- existing_position: Position | None,
70
+ existing_position: ExistingPosition | None,
81
71
  target_position: Position,
82
72
  market: AgentMarket,
83
73
  ) -> list[Trade]:
@@ -95,23 +85,20 @@ class BettingStrategy(ABC):
95
85
  trades = []
96
86
  for outcome_bool in [True, False]:
97
87
  outcome = market.get_outcome_str_from_bool(outcome_bool)
98
- prev_amount: TokenAmount = (
99
- existing_position.amounts[outcome]
100
- if existing_position and outcome in existing_position.amounts
101
- else self.build_zero_token_amount(currency=market.currency)
88
+ prev_amount = (
89
+ existing_position.amounts_current[outcome]
90
+ if existing_position and outcome in existing_position.amounts_current
91
+ else self.build_zero_usd_amount()
102
92
  )
103
- new_amount: TokenAmount = target_position.amounts.get(
104
- outcome, self.build_zero_token_amount(currency=market.currency)
93
+ new_amount = target_position.amounts_current.get(
94
+ outcome, self.build_zero_usd_amount()
105
95
  )
106
-
107
- if prev_amount.currency != new_amount.currency:
108
- raise ValueError("Cannot handle positions with different currencies")
109
- diff_amount = new_amount.amount - prev_amount.amount
96
+ diff_amount = new_amount - prev_amount
110
97
  if diff_amount == 0:
111
98
  continue
112
99
  trade_type = TradeType.SELL if diff_amount < 0 else TradeType.BUY
113
100
  trade = Trade(
114
- amount=TokenAmount(amount=abs(diff_amount), currency=market.currency),
101
+ amount=abs(diff_amount),
115
102
  outcome=outcome_bool,
116
103
  trade_type=trade_type,
117
104
  )
@@ -128,28 +115,25 @@ class BettingStrategy(ABC):
128
115
 
129
116
 
130
117
  class MaxAccuracyBettingStrategy(BettingStrategy):
131
- def __init__(self, bet_amount: float):
118
+ def __init__(self, bet_amount: USD):
132
119
  self.bet_amount = bet_amount
133
120
 
134
121
  @property
135
- def maximum_possible_bet_amount(self) -> float:
122
+ def maximum_possible_bet_amount(self) -> USD:
136
123
  return self.bet_amount
137
124
 
138
125
  def calculate_trades(
139
126
  self,
140
- existing_position: Position | None,
127
+ existing_position: ExistingPosition | None,
141
128
  answer: ProbabilisticAnswer,
142
129
  market: AgentMarket,
143
130
  ) -> list[Trade]:
144
131
  direction = self.calculate_direction(market.current_p_yes, answer.p_yes)
145
132
 
146
133
  amounts = {
147
- market.get_outcome_str_from_bool(direction): TokenAmount(
148
- amount=self.bet_amount,
149
- currency=market.currency,
150
- ),
134
+ market.get_outcome_str_from_bool(direction): self.bet_amount,
151
135
  }
152
- target_position = Position(market_id=market.id, amounts=amounts)
136
+ target_position = Position(market_id=market.id, amounts_current=amounts)
153
137
  trades = self._build_rebalance_trades_from_positions(
154
138
  existing_position, target_position, market=market
155
139
  )
@@ -173,17 +157,17 @@ class MaxExpectedValueBettingStrategy(MaxAccuracyBettingStrategy):
173
157
 
174
158
 
175
159
  class KellyBettingStrategy(BettingStrategy):
176
- def __init__(self, max_bet_amount: float, max_price_impact: float | None = None):
160
+ def __init__(self, max_bet_amount: USD, max_price_impact: float | None = None):
177
161
  self.max_bet_amount = max_bet_amount
178
162
  self.max_price_impact = max_price_impact
179
163
 
180
164
  @property
181
- def maximum_possible_bet_amount(self) -> float:
165
+ def maximum_possible_bet_amount(self) -> USD:
182
166
  return self.max_bet_amount
183
167
 
184
168
  def calculate_trades(
185
169
  self,
186
- existing_position: Position | None,
170
+ existing_position: ExistingPosition | None,
187
171
  answer: ProbabilisticAnswer,
188
172
  market: AgentMarket,
189
173
  ) -> list[Trade]:
@@ -196,7 +180,7 @@ class KellyBettingStrategy(BettingStrategy):
196
180
  market.get_outcome_str_from_bool(False)
197
181
  ],
198
182
  estimated_p_yes=answer.p_yes,
199
- max_bet=self.max_bet_amount,
183
+ max_bet=market.get_usd_in_token(self.max_bet_amount),
200
184
  confidence=answer.confidence,
201
185
  fees=market.fees,
202
186
  )
@@ -212,11 +196,11 @@ class KellyBettingStrategy(BettingStrategy):
212
196
  kelly_bet_size = min(kelly_bet.size, max_price_impact_bet_amount)
213
197
 
214
198
  amounts = {
215
- market.get_outcome_str_from_bool(kelly_bet.direction): TokenAmount(
216
- amount=kelly_bet_size, currency=market.currency
217
- ),
199
+ market.get_outcome_str_from_bool(
200
+ kelly_bet.direction
201
+ ): market.get_token_in_usd(kelly_bet_size),
218
202
  }
219
- target_position = Position(market_id=market.id, amounts=amounts)
203
+ target_position = Position(market_id=market.id, amounts_current=amounts)
220
204
  trades = self._build_rebalance_trades_from_positions(
221
205
  existing_position, target_position, market=market
222
206
  )
@@ -225,9 +209,9 @@ class KellyBettingStrategy(BettingStrategy):
225
209
  def calculate_price_impact_for_bet_amount(
226
210
  self,
227
211
  buy_direction: bool,
228
- bet_amount: float,
229
- yes: float,
230
- no: float,
212
+ bet_amount: CollateralToken,
213
+ yes: OutcomeToken,
214
+ no: OutcomeToken,
231
215
  fees: MarketFees,
232
216
  ) -> float:
233
217
  total_outcome_tokens = yes + no
@@ -239,7 +223,7 @@ class KellyBettingStrategy(BettingStrategy):
239
223
  bet_amount, buy_direction, yes, no, fees
240
224
  )
241
225
 
242
- actual_price = bet_amount / tokens_to_buy
226
+ actual_price = bet_amount.value / tokens_to_buy.value
243
227
  # price_impact should always be > 0
244
228
  price_impact = (actual_price - expected_price) / expected_price
245
229
  return price_impact
@@ -248,13 +232,13 @@ class KellyBettingStrategy(BettingStrategy):
248
232
  self,
249
233
  market: AgentMarket,
250
234
  kelly_bet: SimpleBet,
251
- ) -> float:
235
+ ) -> CollateralToken:
252
236
  def calculate_price_impact_deviation_from_target_price_impact(
253
- bet_amount: xDai,
237
+ bet_amount_usd: float, # Needs to be float because it's used in minimize_scalar internally.
254
238
  ) -> float:
255
239
  price_impact = self.calculate_price_impact_for_bet_amount(
256
240
  kelly_bet.direction,
257
- bet_amount,
241
+ market.get_usd_in_token(USD(bet_amount_usd)),
258
242
  yes_outcome_pool_size,
259
243
  no_outcome_pool_size,
260
244
  market.fees,
@@ -280,40 +264,41 @@ class KellyBettingStrategy(BettingStrategy):
280
264
  # The bounds below have been found to work heuristically.
281
265
  optimized_bet_amount = minimize_scalar(
282
266
  calculate_price_impact_deviation_from_target_price_impact,
283
- bounds=(0, 1000 * (yes_outcome_pool_size + no_outcome_pool_size)),
267
+ bounds=(0, 1000 * (yes_outcome_pool_size + no_outcome_pool_size).value),
284
268
  method="bounded",
285
269
  tol=1e-11,
286
270
  options={"maxiter": 10000},
287
271
  )
288
- return float(optimized_bet_amount.x)
272
+ return CollateralToken(optimized_bet_amount.x)
289
273
 
290
274
  def __repr__(self) -> str:
291
275
  return f"{self.__class__.__name__}(max_bet_amount={self.max_bet_amount}, max_price_impact={self.max_price_impact})"
292
276
 
293
277
 
294
278
  class MaxAccuracyWithKellyScaledBetsStrategy(BettingStrategy):
295
- def __init__(self, max_bet_amount: float = 10):
279
+ def __init__(self, max_bet_amount: USD):
296
280
  self.max_bet_amount = max_bet_amount
297
281
 
298
282
  @property
299
- def maximum_possible_bet_amount(self) -> float:
283
+ def maximum_possible_bet_amount(self) -> USD:
300
284
  return self.max_bet_amount
301
285
 
302
286
  def adjust_bet_amount(
303
- self, existing_position: Position | None, market: AgentMarket
304
- ) -> float:
287
+ self, existing_position: ExistingPosition | None, market: AgentMarket
288
+ ) -> USD:
305
289
  existing_position_total_amount = (
306
- existing_position.total_amount.amount if existing_position else 0
290
+ existing_position.total_amount_current if existing_position else USD(0)
307
291
  )
308
292
  return self.max_bet_amount + existing_position_total_amount
309
293
 
310
294
  def calculate_trades(
311
295
  self,
312
- existing_position: Position | None,
296
+ existing_position: ExistingPosition | None,
313
297
  answer: ProbabilisticAnswer,
314
298
  market: AgentMarket,
315
299
  ) -> list[Trade]:
316
- adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
300
+ adjusted_bet_amount_usd = self.adjust_bet_amount(existing_position, market)
301
+ adjusted_bet_amount_token = market.get_usd_in_token(adjusted_bet_amount_usd)
317
302
  outcome_token_pool = check_not_none(market.outcome_token_pool)
318
303
 
319
304
  # Fixed direction of bet, only use Kelly to adjust the bet size based on market's outcome pool size.
@@ -328,17 +313,16 @@ class MaxAccuracyWithKellyScaledBetsStrategy(BettingStrategy):
328
313
  market.get_outcome_str_from_bool(False)
329
314
  ],
330
315
  estimated_p_yes=estimated_p_yes,
331
- max_bet=adjusted_bet_amount,
316
+ max_bet=adjusted_bet_amount_token,
332
317
  confidence=confidence,
333
318
  fees=market.fees,
334
319
  )
320
+ kelly_bet_size_usd = market.get_token_in_usd(kelly_bet.size)
335
321
 
336
322
  amounts = {
337
- market.get_outcome_str_from_bool(kelly_bet.direction): TokenAmount(
338
- amount=kelly_bet.size, currency=market.currency
339
- ),
323
+ market.get_outcome_str_from_bool(kelly_bet.direction): kelly_bet_size_usd,
340
324
  }
341
- target_position = Position(market_id=market.id, amounts=amounts)
325
+ target_position = Position(market_id=market.id, amounts_current=amounts)
342
326
  trades = self._build_rebalance_trades_from_positions(
343
327
  existing_position, target_position, market=market
344
328
  )
@@ -1,5 +1,6 @@
1
1
  import typing as t
2
- from typing import NewType, Union
2
+ from decimal import Decimal
3
+ from typing import NewType
3
4
 
4
5
  from eth_typing.evm import ( # noqa: F401 # Import for the sake of easy importing with others from here.
5
6
  Address,
@@ -9,13 +10,15 @@ from eth_typing.evm import ( # noqa: F401 # Import for the sake of easy import
9
10
  )
10
11
  from pydantic.types import SecretStr
11
12
  from pydantic.v1.types import SecretStr as SecretStrV1
13
+ from web3 import Web3
12
14
  from web3.types import ( # noqa: F401 # Import for the sake of easy importing with others from here.
13
15
  Nonce,
14
16
  TxParams,
15
17
  TxReceipt,
16
- Wei,
17
18
  )
19
+ from web3.types import Wei as Web3Wei
18
20
 
21
+ from prediction_market_agent_tooling.tools._generic_value import _GenericValue
19
22
  from prediction_market_agent_tooling.tools.datetime_utc import ( # noqa: F401 # Import for the sake of easy importing with others from here.
20
23
  DatetimeUTC,
21
24
  )
@@ -23,43 +26,118 @@ from prediction_market_agent_tooling.tools.hexbytes_custom import ( # noqa: F40
23
26
  HexBytes,
24
27
  )
25
28
 
26
- Wad = Wei # Wei tends to be referred to as `wad` variable in contracts.
27
- USD = NewType("USD", float)
28
- PrivateKey = NewType("PrivateKey", SecretStr)
29
- xDai = NewType("xDai", float)
30
- GNO = NewType("GNO", float)
31
- ABI = NewType("ABI", str)
32
- OmenOutcomeToken = NewType("OmenOutcomeToken", Wei)
33
- OutcomeStr = NewType("OutcomeStr", str)
34
- Probability = NewType("Probability", float)
35
- Mana = NewType("Mana", float) # Manifold's "currency"
36
- USDC = NewType("USDC", float)
37
- ChainID = NewType("ChainID", int)
38
- IPFSCIDVersion0 = NewType("IPFSCIDVersion0", str)
39
29
 
30
+ class CollateralToken(_GenericValue[int | float | str | Decimal, float], parser=float):
31
+ """
32
+ Represents any token in its decimal form, it could be 1.1 GNO, WXDAI, XDAI, Mana, whatever. We don't know the currency, just that it's in the decimal form.
33
+ """
34
+
35
+ @property
36
+ def as_wei(self) -> "Wei":
37
+ return Wei(Web3.to_wei(self.value, "ether"))
38
+
39
+
40
+ class OutcomeToken(_GenericValue[int | float | str | Decimal, float], parser=float):
41
+ """
42
+ Represents outcome tokens in market in decimal form.
43
+ After you redeem the outcome tokens, 1 OutcomeToken equals to 1 Token, but before, it's important to distinguish between them.
44
+ For example, it's a big difference if you are going to sell 1 OutcomeToken, or 1 (collateral) Token.
45
+ But still, Token and OutcomeToken needs to be handled together in many cases, use available properties to convert between them explicitly.
46
+ """
47
+
48
+ @staticmethod
49
+ def from_token(token: CollateralToken) -> "OutcomeToken":
50
+ return OutcomeToken(token.value)
51
+
52
+ @property
53
+ def as_outcome_wei(self) -> "OutcomeWei":
54
+ return OutcomeWei(Web3.to_wei(self.value, "ether"))
55
+
56
+ @property
57
+ def as_token(self) -> CollateralToken:
58
+ """
59
+ OutcomeToken is essentialy Token as well, when you know you really need to convert it, you can convert it explicitly using this.
60
+ """
61
+ return CollateralToken(self.value)
62
+
63
+
64
+ class USD(_GenericValue[int | float | str | Decimal, float], parser=float):
65
+ """Represents values in USD."""
66
+
67
+
68
+ class xDai(_GenericValue[int | float | str | Decimal, float], parser=float):
69
+ """Represents values in xDai."""
40
70
 
41
- def usd_type(amount: Union[str, int, float]) -> USD:
42
- return USD(float(amount))
71
+ @property
72
+ def as_token(self) -> CollateralToken:
73
+ """
74
+ xDai is essentialy Token as well, when you know you need to pass it, you can convert it using this.
75
+ """
76
+ return CollateralToken(self.value)
43
77
 
78
+ @property
79
+ def as_xdai_wei(self) -> "xDaiWei":
80
+ return xDaiWei(Web3.to_wei(self.value, "ether"))
44
81
 
45
- def wei_type(amount: Union[str, int, float]) -> Wei:
46
- return Wei(int(amount))
47
82
 
83
+ class Mana(_GenericValue[int | float | str | Decimal, float], parser=float):
84
+ """Represents values in Manifold's Mana."""
48
85
 
49
- def omen_outcome_type(amount: Union[str, int, Wei]) -> OmenOutcomeToken:
50
- return OmenOutcomeToken(wei_type(amount))
51
86
 
87
+ class USDC(_GenericValue[int | float | str | Decimal, float], parser=float):
88
+ """Represents values in USDC."""
52
89
 
53
- def xdai_type(amount: Union[str, int, float]) -> xDai:
54
- return xDai(float(amount))
55
90
 
91
+ class Wei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
92
+ """Represents values in Wei. We don't know what currency, but in its integer form called Wei."""
56
93
 
57
- def mana_type(amount: Union[str, int, float]) -> Mana:
58
- return Mana(float(amount))
94
+ @property
95
+ def as_token(self) -> CollateralToken:
96
+ return CollateralToken(Web3.from_wei(self.value, "ether"))
59
97
 
60
98
 
61
- def usdc_type(amount: Union[str, int, float]) -> USDC:
62
- return USDC(float(amount))
99
+ class OutcomeWei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
100
+ """
101
+ Similar to OutcomeToken, but in Wei units.
102
+ """
103
+
104
+ @staticmethod
105
+ def from_wei(wei: Wei) -> "OutcomeWei":
106
+ return OutcomeWei(wei.value)
107
+
108
+ @property
109
+ def as_outcome_token(self) -> OutcomeToken:
110
+ return OutcomeToken(Web3.from_wei(self.value, "ether"))
111
+
112
+ @property
113
+ def as_wei(self) -> Wei:
114
+ """
115
+ OutcomeWei is essentialy Wei as well, when you know you need to pass it, you can convert it using this.
116
+ """
117
+ return Wei(self.value)
118
+
119
+
120
+ class xDaiWei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
121
+ """Represents xDai in Wei, like 1.9 xDai is 1.9 * 10**18 Wei. In contrast to just `Wei`, we don't know what unit Wei is (Wei of GNO, sDai, or whatever), but xDaiWei is xDai converted to Wei."""
122
+
123
+ @property
124
+ def as_xdai(self) -> xDai:
125
+ return xDai(Web3.from_wei(self.value, "ether"))
126
+
127
+ @property
128
+ def as_wei(self) -> Wei:
129
+ """
130
+ xDaiWei is essentialy Wei as well, when you know you need to pass it, you can convert it using this.
131
+ """
132
+ return Wei(self.value)
133
+
134
+
135
+ PrivateKey = NewType("PrivateKey", SecretStr)
136
+ ABI = NewType("ABI", str)
137
+ OutcomeStr = NewType("OutcomeStr", str)
138
+ Probability = NewType("Probability", float)
139
+ ChainID = NewType("ChainID", int)
140
+ IPFSCIDVersion0 = NewType("IPFSCIDVersion0", str)
63
141
 
64
142
 
65
143
  def private_key_type(k: str) -> PrivateKey:
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
4
4
  from pydantic import BaseModel
5
5
 
6
6
  from prediction_market_agent_tooling.deploy.betting_strategy import ProbabilisticAnswer
7
- from prediction_market_agent_tooling.gtypes import Probability
7
+ from prediction_market_agent_tooling.gtypes import USD, Probability
8
8
  from prediction_market_agent_tooling.markets.agent_market import (
9
9
  AgentMarket,
10
10
  ProcessedTradedMarket,
@@ -19,8 +19,7 @@ from prediction_market_agent_tooling.tools.utils import DatetimeUTC
19
19
  class SimpleJob(BaseModel):
20
20
  id: str
21
21
  job: str
22
- reward: float
23
- currency: str
22
+ reward: USD
24
23
  deadline: DatetimeUTC
25
24
 
26
25
 
@@ -38,7 +37,7 @@ class JobAgentMarket(AgentMarket, ABC):
38
37
  """Deadline for the job completion."""
39
38
 
40
39
  @abstractmethod
41
- def get_reward(self, max_bond: float) -> float:
40
+ def get_reward(self, max_bond: USD) -> USD:
42
41
  """Reward for completing this job."""
43
42
 
44
43
  @classmethod
@@ -58,16 +57,15 @@ class JobAgentMarket(AgentMarket, ABC):
58
57
 
59
58
  @abstractmethod
60
59
  def submit_job_result(
61
- self, agent_name: str, max_bond: float, result: str
60
+ self, agent_name: str, max_bond: USD, result: str
62
61
  ) -> ProcessedTradedMarket:
63
62
  """Submit the completed result for this job."""
64
63
 
65
- def to_simple_job(self, max_bond: float) -> SimpleJob:
64
+ def to_simple_job(self, max_bond: USD) -> SimpleJob:
66
65
  return SimpleJob(
67
66
  id=self.id,
68
67
  job=self.job,
69
68
  reward=self.get_reward(max_bond),
70
- currency=self.currency.value,
71
69
  deadline=self.deadline,
72
70
  )
73
71
 
@@ -2,15 +2,14 @@ import typing as t
2
2
 
3
3
  from prediction_market_agent_tooling.config import APIKeys
4
4
  from prediction_market_agent_tooling.deploy.betting_strategy import (
5
- Currency,
6
5
  KellyBettingStrategy,
7
6
  TradeType,
8
7
  )
8
+ from prediction_market_agent_tooling.gtypes import USD
9
9
  from prediction_market_agent_tooling.jobs.jobs_models import JobAgentMarket
10
10
  from prediction_market_agent_tooling.markets.agent_market import ProcessedTradedMarket
11
11
  from prediction_market_agent_tooling.markets.data_models import PlacedTrade, Trade
12
12
  from prediction_market_agent_tooling.markets.omen.omen import (
13
- BetAmount,
14
13
  OmenAgentMarket,
15
14
  OmenMarket,
16
15
  )
@@ -34,21 +33,21 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
34
33
  def deadline(self) -> DatetimeUTC:
35
34
  return self.close_time
36
35
 
37
- def get_reward(self, max_bond: float) -> float:
36
+ def get_reward(self, max_bond: USD) -> USD:
38
37
  trade = self.get_job_trade(
39
38
  max_bond,
40
39
  result="", # Pass empty result, as we are computing only potential reward at this point.
41
40
  )
42
- reward = (
43
- self.get_buy_token_amount(
44
- bet_amount=BetAmount(
45
- amount=trade.amount.amount, currency=trade.amount.currency
46
- ),
47
- direction=trade.outcome,
48
- ).amount
49
- - trade.amount.amount
41
+ reward_usd = (
42
+ self.get_token_in_usd(
43
+ self.get_buy_token_amount(
44
+ bet_amount=trade.amount,
45
+ direction=trade.outcome,
46
+ ).as_token
47
+ )
48
+ - trade.amount
50
49
  )
51
- return reward
50
+ return reward_usd
52
51
 
53
52
  @classmethod
54
53
  def get_jobs(
@@ -72,7 +71,7 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
72
71
  )
73
72
 
74
73
  def submit_job_result(
75
- self, agent_name: str, max_bond: float, result: str
74
+ self, agent_name: str, max_bond: USD, result: str
76
75
  ) -> ProcessedTradedMarket:
77
76
  if not APIKeys().enable_ipfs_upload:
78
77
  raise RuntimeError(
@@ -92,7 +91,7 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
92
91
 
93
92
  return processed_traded_market
94
93
 
95
- def get_job_trade(self, max_bond: float, result: str) -> Trade:
94
+ def get_job_trade(self, max_bond: USD, result: str) -> Trade:
96
95
  # Because jobs are powered by prediction markets, potentional reward depends on job's liquidity and our will to bond (bet) our xDai into our job completion.
97
96
  strategy = KellyBettingStrategy(max_bet_amount=max_bond)
98
97
  required_trades = strategy.calculate_trades(
@@ -106,9 +105,6 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
106
105
  trade = required_trades[0]
107
106
  assert trade.trade_type == TradeType.BUY, "Should only buy on job markets."
108
107
  assert trade.outcome, "Should buy only YES on job markets."
109
- assert (
110
- trade.amount.currency == Currency.xDai
111
- ), "Should work only on real-money markets."
112
108
  return required_trades[0]
113
109
 
114
110
  @staticmethod