prediction-market-agent-tooling 0.61.1.dev489__py3-none-any.whl → 0.62.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prediction_market_agent_tooling/deploy/agent.py +4 -5
- prediction_market_agent_tooling/deploy/betting_strategy.py +53 -69
- prediction_market_agent_tooling/gtypes.py +105 -27
- prediction_market_agent_tooling/jobs/jobs_models.py +5 -7
- prediction_market_agent_tooling/jobs/omen/omen_jobs.py +13 -17
- prediction_market_agent_tooling/markets/agent_market.py +96 -55
- prediction_market_agent_tooling/markets/blockchain_utils.py +2 -32
- prediction_market_agent_tooling/markets/data_models.py +40 -44
- prediction_market_agent_tooling/markets/manifold/api.py +2 -6
- prediction_market_agent_tooling/markets/manifold/data_models.py +33 -25
- prediction_market_agent_tooling/markets/manifold/manifold.py +13 -11
- prediction_market_agent_tooling/markets/market_fees.py +6 -2
- prediction_market_agent_tooling/markets/omen/data_models.py +66 -57
- prediction_market_agent_tooling/markets/omen/omen.py +222 -250
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +32 -33
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +7 -14
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +20 -14
- prediction_market_agent_tooling/markets/polymarket/data_models.py +3 -3
- prediction_market_agent_tooling/markets/polymarket/data_models_web.py +4 -4
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +3 -5
- prediction_market_agent_tooling/markets/seer/data_models.py +92 -5
- prediction_market_agent_tooling/markets/seer/seer.py +93 -115
- prediction_market_agent_tooling/markets/seer/seer_contracts.py +11 -6
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +18 -26
- prediction_market_agent_tooling/monitor/monitor.py +2 -2
- prediction_market_agent_tooling/tools/_generic_value.py +261 -0
- prediction_market_agent_tooling/tools/balances.py +14 -11
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +12 -10
- prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +31 -24
- prediction_market_agent_tooling/tools/betting_strategies/utils.py +3 -1
- prediction_market_agent_tooling/tools/contract.py +14 -10
- prediction_market_agent_tooling/tools/cow/cow_manager.py +3 -4
- prediction_market_agent_tooling/tools/cow/cow_order.py +51 -7
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +13 -1
- prediction_market_agent_tooling/tools/omen/sell_positions.py +6 -3
- prediction_market_agent_tooling/tools/safe.py +5 -6
- prediction_market_agent_tooling/tools/tokens/auto_deposit.py +36 -27
- prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +4 -25
- prediction_market_agent_tooling/tools/tokens/main_token.py +2 -2
- prediction_market_agent_tooling/tools/tokens/token_utils.py +46 -0
- prediction_market_agent_tooling/tools/tokens/usd.py +79 -0
- prediction_market_agent_tooling/tools/utils.py +14 -8
- prediction_market_agent_tooling/tools/web3_utils.py +24 -41
- {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/METADATA +2 -1
- {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/RECORD +48 -47
- prediction_market_agent_tooling/markets/seer/price_manager.py +0 -111
- prediction_market_agent_tooling/markets/seer/subgraph_data_models.py +0 -57
- {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.61.1.dev489.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/entry_points.txt +0 -0
@@ -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()
|
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.
|
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:
|
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
|
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
|
-
|
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:
|
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) ->
|
41
|
+
def maximum_possible_bet_amount(self) -> USD:
|
43
42
|
raise NotImplementedError("Subclass should implement this.")
|
44
43
|
|
45
|
-
def
|
46
|
-
return
|
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
|
-
|
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:
|
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
|
99
|
-
existing_position.
|
100
|
-
if existing_position and outcome in existing_position.
|
101
|
-
else self.
|
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
|
104
|
-
outcome, self.
|
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=
|
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:
|
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) ->
|
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:
|
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):
|
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,
|
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:
|
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) ->
|
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:
|
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(
|
216
|
-
|
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,
|
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:
|
229
|
-
yes:
|
230
|
-
no:
|
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
|
-
) ->
|
235
|
+
) -> CollateralToken:
|
252
236
|
def calculate_price_impact_deviation_from_target_price_impact(
|
253
|
-
|
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
|
-
|
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
|
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:
|
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) ->
|
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:
|
304
|
-
) ->
|
287
|
+
self, existing_position: ExistingPosition | None, market: AgentMarket
|
288
|
+
) -> USD:
|
305
289
|
existing_position_total_amount = (
|
306
|
-
existing_position.
|
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:
|
296
|
+
existing_position: ExistingPosition | None,
|
313
297
|
answer: ProbabilisticAnswer,
|
314
298
|
market: AgentMarket,
|
315
299
|
) -> list[Trade]:
|
316
|
-
|
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=
|
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):
|
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,
|
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
|
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
|
-
|
42
|
-
|
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
|
-
|
58
|
-
|
94
|
+
@property
|
95
|
+
def as_token(self) -> CollateralToken:
|
96
|
+
return CollateralToken(Web3.from_wei(self.value, "ether"))
|
59
97
|
|
60
98
|
|
61
|
-
|
62
|
-
|
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:
|
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:
|
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:
|
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:
|
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:
|
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
|
-
|
43
|
-
self.
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
)
|
49
|
-
- trade.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
|
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:
|
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:
|
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
|