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.
- prediction_market_agent_tooling/config.py +2 -3
- 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 -53
- prediction_market_agent_tooling/markets/blockchain_utils.py +1 -27
- 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 +31 -53
- 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 +8 -8
- prediction_market_agent_tooling/markets/seer/seer.py +85 -71
- prediction_market_agent_tooling/markets/seer/seer_contracts.py +10 -5
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +5 -2
- 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.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/METADATA +2 -1
- {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/RECORD +49 -47
- prediction_market_agent_tooling/abis/gvp2_settlement.abi.json +0 -89
- {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.61.2.dev479.dist-info → prediction_market_agent_tooling-0.62.0.dist-info}/entry_points.txt +0 -0
@@ -6,17 +6,21 @@ from pydantic import BaseModel, field_validator, model_validator
|
|
6
6
|
from pydantic_core.core_schema import FieldValidationInfo
|
7
7
|
|
8
8
|
from prediction_market_agent_tooling.config import APIKeys
|
9
|
-
from prediction_market_agent_tooling.gtypes import
|
9
|
+
from prediction_market_agent_tooling.gtypes import (
|
10
|
+
CollateralToken,
|
11
|
+
OutcomeStr,
|
12
|
+
OutcomeToken,
|
13
|
+
Probability,
|
14
|
+
)
|
10
15
|
from prediction_market_agent_tooling.markets.data_models import (
|
16
|
+
USD,
|
11
17
|
Bet,
|
12
|
-
|
13
|
-
|
18
|
+
CollateralToken,
|
19
|
+
ExistingPosition,
|
14
20
|
PlacedTrade,
|
15
|
-
Position,
|
16
21
|
ProbabilisticAnswer,
|
17
22
|
Resolution,
|
18
23
|
ResolvedBet,
|
19
|
-
TokenAmount,
|
20
24
|
)
|
21
25
|
from prediction_market_agent_tooling.markets.market_fees import MarketFees
|
22
26
|
from prediction_market_agent_tooling.tools.utils import (
|
@@ -55,31 +59,28 @@ class AgentMarket(BaseModel):
|
|
55
59
|
Contains everything that is needed for an agent to make a prediction.
|
56
60
|
"""
|
57
61
|
|
58
|
-
currency: t.ClassVar[Currency]
|
59
62
|
base_url: t.ClassVar[str]
|
60
63
|
|
61
64
|
id: str
|
62
65
|
question: str
|
63
66
|
description: str | None
|
64
|
-
outcomes:
|
65
|
-
outcome_token_pool:
|
66
|
-
dict[str, float] | None
|
67
|
-
) # Should be in currency of `currency` above.
|
67
|
+
outcomes: t.Sequence[OutcomeStr]
|
68
|
+
outcome_token_pool: dict[str, OutcomeToken] | None
|
68
69
|
resolution: Resolution | None
|
69
70
|
created_time: DatetimeUTC | None
|
70
71
|
close_time: DatetimeUTC | None
|
71
72
|
current_p_yes: Probability
|
72
73
|
url: str
|
73
|
-
volume:
|
74
|
+
volume: CollateralToken | None
|
74
75
|
fees: MarketFees
|
75
76
|
|
76
77
|
@field_validator("outcome_token_pool")
|
77
78
|
def validate_outcome_token_pool(
|
78
79
|
cls,
|
79
|
-
outcome_token_pool: dict[str,
|
80
|
+
outcome_token_pool: dict[str, OutcomeToken] | None,
|
80
81
|
info: FieldValidationInfo,
|
81
|
-
) -> dict[str,
|
82
|
-
outcomes:
|
82
|
+
) -> dict[str, OutcomeToken] | None:
|
83
|
+
outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
|
83
84
|
if outcome_token_pool is not None:
|
84
85
|
outcome_keys = set(outcome_token_pool.keys())
|
85
86
|
expected_keys = set(outcomes)
|
@@ -102,20 +103,28 @@ class AgentMarket(BaseModel):
|
|
102
103
|
return Probability(1 - self.current_p_yes)
|
103
104
|
|
104
105
|
@property
|
105
|
-
def yes_outcome_price(self) ->
|
106
|
+
def yes_outcome_price(self) -> CollateralToken:
|
106
107
|
"""
|
107
108
|
Price at prediction market is equal to the probability of given outcome.
|
108
109
|
Keep as an extra property, in case it wouldn't be true for some prediction market platform.
|
109
110
|
"""
|
110
|
-
return self.current_p_yes
|
111
|
+
return CollateralToken(self.current_p_yes)
|
112
|
+
|
113
|
+
@property
|
114
|
+
def yes_outcome_price_usd(self) -> USD:
|
115
|
+
return self.get_token_in_usd(self.yes_outcome_price)
|
111
116
|
|
112
117
|
@property
|
113
|
-
def no_outcome_price(self) ->
|
118
|
+
def no_outcome_price(self) -> CollateralToken:
|
114
119
|
"""
|
115
120
|
Price at prediction market is equal to the probability of given outcome.
|
116
121
|
Keep as an extra property, in case it wouldn't be true for some prediction market platform.
|
117
122
|
"""
|
118
|
-
return self.current_p_no
|
123
|
+
return CollateralToken(self.current_p_no)
|
124
|
+
|
125
|
+
@property
|
126
|
+
def no_outcome_price_usd(self) -> USD:
|
127
|
+
return self.get_token_in_usd(self.no_outcome_price)
|
119
128
|
|
120
129
|
@property
|
121
130
|
def probable_resolution(self) -> Resolution:
|
@@ -150,48 +159,86 @@ class AgentMarket(BaseModel):
|
|
150
159
|
"""
|
151
160
|
raise NotImplementedError("Subclasses must implement this method")
|
152
161
|
|
153
|
-
def get_last_trade_yes_outcome_price(self) ->
|
162
|
+
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
|
154
163
|
# Price on prediction markets are, by definition, equal to the probability of an outcome.
|
155
164
|
# Just making it explicit in this function.
|
156
165
|
if last_trade_p_yes := self.get_last_trade_p_yes():
|
157
|
-
return
|
166
|
+
return CollateralToken(last_trade_p_yes)
|
158
167
|
return None
|
159
168
|
|
160
|
-
def
|
169
|
+
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
|
170
|
+
if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
|
171
|
+
return self.get_token_in_usd(last_trade_yes_outcome_price)
|
172
|
+
return None
|
173
|
+
|
174
|
+
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
|
161
175
|
# Price on prediction markets are, by definition, equal to the probability of an outcome.
|
162
176
|
# Just making it explicit in this function.
|
163
177
|
if last_trade_p_no := self.get_last_trade_p_no():
|
164
|
-
return
|
178
|
+
return CollateralToken(last_trade_p_no)
|
165
179
|
return None
|
166
180
|
|
167
|
-
def
|
168
|
-
|
181
|
+
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
|
182
|
+
if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
|
183
|
+
return self.get_token_in_usd(last_trade_no_outcome_price)
|
184
|
+
return None
|
169
185
|
|
170
|
-
|
171
|
-
|
172
|
-
tiny_amount
|
173
|
-
tiny_amount.amount /= 10
|
174
|
-
return tiny_amount
|
186
|
+
def get_liquidatable_amount(self) -> OutcomeToken:
|
187
|
+
tiny_amount = self.get_tiny_bet_amount()
|
188
|
+
return OutcomeToken.from_token(tiny_amount / 10)
|
175
189
|
|
176
|
-
|
177
|
-
|
190
|
+
def get_token_in_usd(self, x: CollateralToken) -> USD:
|
191
|
+
"""
|
192
|
+
Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.
|
193
|
+
"""
|
194
|
+
raise NotImplementedError("Subclasses must implement this method")
|
195
|
+
|
196
|
+
def get_usd_in_token(self, x: USD) -> CollateralToken:
|
197
|
+
"""
|
198
|
+
Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.
|
199
|
+
"""
|
200
|
+
raise NotImplementedError("Subclasses must implement this method")
|
201
|
+
|
202
|
+
def get_sell_value_of_outcome_token(
|
203
|
+
self, outcome: str, amount: OutcomeToken
|
204
|
+
) -> CollateralToken:
|
205
|
+
"""
|
206
|
+
When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token).
|
207
|
+
But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
|
208
|
+
"""
|
209
|
+
raise NotImplementedError("Subclasses must implement this method")
|
210
|
+
|
211
|
+
def get_in_usd(self, x: USD | CollateralToken) -> USD:
|
212
|
+
if isinstance(x, USD):
|
213
|
+
return x
|
214
|
+
return self.get_token_in_usd(x)
|
215
|
+
|
216
|
+
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
|
217
|
+
if isinstance(x, CollateralToken):
|
218
|
+
return x
|
219
|
+
return self.get_usd_in_token(x)
|
220
|
+
|
221
|
+
def get_tiny_bet_amount(self) -> CollateralToken:
|
222
|
+
"""
|
223
|
+
Tiny bet amount that the platform still allows us to do.
|
224
|
+
"""
|
178
225
|
raise NotImplementedError("Subclasses must implement this method")
|
179
226
|
|
180
227
|
def liquidate_existing_positions(self, outcome: bool) -> None:
|
181
228
|
raise NotImplementedError("Subclasses must implement this method")
|
182
229
|
|
183
|
-
def place_bet(self, outcome: bool, amount:
|
230
|
+
def place_bet(self, outcome: bool, amount: USD) -> str:
|
184
231
|
raise NotImplementedError("Subclasses must implement this method")
|
185
232
|
|
186
|
-
def buy_tokens(self, outcome: bool, amount:
|
233
|
+
def buy_tokens(self, outcome: bool, amount: USD) -> str:
|
187
234
|
return self.place_bet(outcome=outcome, amount=amount)
|
188
235
|
|
189
236
|
def get_buy_token_amount(
|
190
|
-
self, bet_amount:
|
191
|
-
) ->
|
237
|
+
self, bet_amount: USD | CollateralToken, direction: bool
|
238
|
+
) -> OutcomeToken:
|
192
239
|
raise NotImplementedError("Subclasses must implement this method")
|
193
240
|
|
194
|
-
def sell_tokens(self, outcome: bool, amount:
|
241
|
+
def sell_tokens(self, outcome: bool, amount: USD | OutcomeToken) -> str:
|
195
242
|
raise NotImplementedError("Subclasses must implement this method")
|
196
243
|
|
197
244
|
@staticmethod
|
@@ -215,8 +262,8 @@ class AgentMarket(BaseModel):
|
|
215
262
|
"""
|
216
263
|
raise NotImplementedError("Subclasses must implement this method")
|
217
264
|
|
218
|
-
@
|
219
|
-
def get_trade_balance(api_keys: APIKeys) ->
|
265
|
+
@classmethod
|
266
|
+
def get_trade_balance(cls, api_keys: APIKeys) -> USD:
|
220
267
|
"""
|
221
268
|
Return balance that can be used to trade on the given market.
|
222
269
|
"""
|
@@ -272,11 +319,11 @@ class AgentMarket(BaseModel):
|
|
272
319
|
def is_resolved(self) -> bool:
|
273
320
|
return self.resolution is not None
|
274
321
|
|
275
|
-
def get_liquidity(self) ->
|
322
|
+
def get_liquidity(self) -> CollateralToken:
|
276
323
|
raise NotImplementedError("Subclasses must implement this method")
|
277
324
|
|
278
325
|
def has_liquidity(self) -> bool:
|
279
|
-
return self.get_liquidity()
|
326
|
+
return self.get_liquidity() > 0
|
280
327
|
|
281
328
|
def has_successful_resolution(self) -> bool:
|
282
329
|
return self.resolution in [Resolution.YES, Resolution.NO]
|
@@ -287,7 +334,7 @@ class AgentMarket(BaseModel):
|
|
287
334
|
def get_outcome_str_from_bool(self, outcome: bool) -> OutcomeStr:
|
288
335
|
raise NotImplementedError("Subclasses must implement this method")
|
289
336
|
|
290
|
-
def get_outcome_str(self, outcome_index: int) ->
|
337
|
+
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
|
291
338
|
try:
|
292
339
|
return self.outcomes[outcome_index]
|
293
340
|
except IndexError:
|
@@ -301,16 +348,19 @@ class AgentMarket(BaseModel):
|
|
301
348
|
except ValueError:
|
302
349
|
raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
|
303
350
|
|
304
|
-
def get_token_balance(self, user_id: str, outcome: str) ->
|
351
|
+
def get_token_balance(self, user_id: str, outcome: str) -> OutcomeToken:
|
305
352
|
raise NotImplementedError("Subclasses must implement this method")
|
306
353
|
|
307
|
-
def get_position(self, user_id: str) ->
|
354
|
+
def get_position(self, user_id: str) -> ExistingPosition | None:
|
308
355
|
raise NotImplementedError("Subclasses must implement this method")
|
309
356
|
|
310
357
|
@classmethod
|
311
358
|
def get_positions(
|
312
|
-
cls,
|
313
|
-
|
359
|
+
cls,
|
360
|
+
user_id: str,
|
361
|
+
liquid_only: bool = False,
|
362
|
+
larger_than: OutcomeToken = OutcomeToken(0),
|
363
|
+
) -> t.Sequence[ExistingPosition]:
|
314
364
|
"""
|
315
365
|
Get all non-zero positions a user has in any market.
|
316
366
|
|
@@ -321,13 +371,6 @@ class AgentMarket(BaseModel):
|
|
321
371
|
"""
|
322
372
|
raise NotImplementedError("Subclasses must implement this method")
|
323
373
|
|
324
|
-
@classmethod
|
325
|
-
def get_positions_value(cls, positions: list[Position]) -> BetAmount:
|
326
|
-
"""
|
327
|
-
Get the total value of all positions held by a user.
|
328
|
-
"""
|
329
|
-
raise NotImplementedError("Subclasses must implement this method")
|
330
|
-
|
331
374
|
def can_be_traded(self) -> bool:
|
332
375
|
if self.is_closed() or not self.has_liquidity():
|
333
376
|
return False
|
@@ -340,7 +383,7 @@ class AgentMarket(BaseModel):
|
|
340
383
|
def has_token_pool(self) -> bool:
|
341
384
|
return self.outcome_token_pool is not None
|
342
385
|
|
343
|
-
def get_pool_tokens(self, outcome: str) ->
|
386
|
+
def get_pool_tokens(self, outcome: str) -> OutcomeToken:
|
344
387
|
if not self.outcome_token_pool:
|
345
388
|
raise ValueError("Outcome token pool is not available.")
|
346
389
|
|
@@ -2,13 +2,7 @@ from web3 import Web3
|
|
2
2
|
from web3.constants import HASH_ZERO
|
3
3
|
|
4
4
|
from prediction_market_agent_tooling.config import APIKeys
|
5
|
-
from prediction_market_agent_tooling.gtypes import
|
6
|
-
ChecksumAddress,
|
7
|
-
HexBytes,
|
8
|
-
HexStr,
|
9
|
-
xDai,
|
10
|
-
xdai_type,
|
11
|
-
)
|
5
|
+
from prediction_market_agent_tooling.gtypes import HexBytes, HexStr
|
12
6
|
from prediction_market_agent_tooling.loggers import logger
|
13
7
|
from prediction_market_agent_tooling.markets.agent_market import ProcessedTradedMarket
|
14
8
|
from prediction_market_agent_tooling.markets.omen.data_models import (
|
@@ -18,31 +12,11 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
|
|
18
12
|
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
19
13
|
OmenAgentResultMappingContract,
|
20
14
|
)
|
21
|
-
from prediction_market_agent_tooling.tools.balances import get_balances
|
22
15
|
from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
|
23
16
|
from prediction_market_agent_tooling.tools.utils import BPS_CONSTANT
|
24
17
|
from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
|
25
18
|
|
26
19
|
|
27
|
-
def get_total_balance(
|
28
|
-
address: ChecksumAddress,
|
29
|
-
web3: Web3 | None = None,
|
30
|
-
sum_xdai: bool = True,
|
31
|
-
sum_wxdai: bool = True,
|
32
|
-
) -> xDai:
|
33
|
-
"""
|
34
|
-
Checks if the total balance of xDai and wxDai in the wallet is above the minimum required balance.
|
35
|
-
"""
|
36
|
-
current_balances = get_balances(address, web3)
|
37
|
-
# xDai and wxDai have equal value and can be exchanged for almost no cost, so we can sum them up.
|
38
|
-
total_balance = 0.0
|
39
|
-
if sum_xdai:
|
40
|
-
total_balance += current_balances.xdai
|
41
|
-
if sum_wxdai:
|
42
|
-
total_balance += current_balances.wxdai
|
43
|
-
return xdai_type(total_balance)
|
44
|
-
|
45
|
-
|
46
20
|
def store_trades(
|
47
21
|
market_id: str,
|
48
22
|
traded_market: ProcessedTradedMarket | None,
|
@@ -1,19 +1,18 @@
|
|
1
1
|
from enum import Enum
|
2
|
-
from typing import Annotated
|
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
|
6
|
+
from prediction_market_agent_tooling.gtypes import (
|
7
|
+
USD,
|
8
|
+
CollateralToken,
|
9
|
+
OutcomeStr,
|
10
|
+
OutcomeToken,
|
11
|
+
Probability,
|
12
|
+
)
|
7
13
|
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
8
14
|
|
9
15
|
|
10
|
-
class Currency(str, Enum):
|
11
|
-
xDai = "xDai"
|
12
|
-
sDai = "sDai"
|
13
|
-
Mana = "Mana"
|
14
|
-
USDC = "USDC"
|
15
|
-
|
16
|
-
|
17
16
|
class Resolution(str, Enum):
|
18
17
|
YES = "YES"
|
19
18
|
NO = "NO"
|
@@ -25,21 +24,9 @@ class Resolution(str, Enum):
|
|
25
24
|
return Resolution.YES if value else Resolution.NO
|
26
25
|
|
27
26
|
|
28
|
-
class TokenAmount(BaseModel):
|
29
|
-
amount: float
|
30
|
-
currency: Currency
|
31
|
-
|
32
|
-
def __str__(self) -> str:
|
33
|
-
return f"Amount {self.amount} currency {self.currency}"
|
34
|
-
|
35
|
-
|
36
|
-
BetAmount: TypeAlias = TokenAmount
|
37
|
-
ProfitAmount: TypeAlias = TokenAmount
|
38
|
-
|
39
|
-
|
40
27
|
class Bet(BaseModel):
|
41
28
|
id: str
|
42
|
-
amount:
|
29
|
+
amount: CollateralToken
|
43
30
|
outcome: bool
|
44
31
|
created_time: DatetimeUTC
|
45
32
|
market_question: str
|
@@ -52,7 +39,7 @@ class Bet(BaseModel):
|
|
52
39
|
class ResolvedBet(Bet):
|
53
40
|
market_outcome: bool
|
54
41
|
resolved_time: DatetimeUTC
|
55
|
-
profit:
|
42
|
+
profit: CollateralToken
|
56
43
|
|
57
44
|
@computed_field # type: ignore[prop-decorator]
|
58
45
|
@property
|
@@ -63,10 +50,6 @@ class ResolvedBet(Bet):
|
|
63
50
|
return f"Resolved bet for market {self.market_id} for question {self.market_question} created at {self.created_time}: {self.amount} on {self.outcome}. Bet was resolved at {self.resolved_time} and was {'correct' if self.is_correct else 'incorrect'}. Profit was {self.profit}"
|
64
51
|
|
65
52
|
|
66
|
-
class TokenAmountAndDirection(TokenAmount):
|
67
|
-
direction: bool
|
68
|
-
|
69
|
-
|
70
53
|
def to_boolean_outcome(value: str | bool) -> bool:
|
71
54
|
if isinstance(value, bool):
|
72
55
|
return value
|
@@ -102,23 +85,36 @@ class ProbabilisticAnswer(BaseModel):
|
|
102
85
|
|
103
86
|
class Position(BaseModel):
|
104
87
|
market_id: str
|
105
|
-
|
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
|
109
|
-
return
|
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
|
117
|
-
for outcome, amount in self.
|
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:
|
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:
|
153
|
-
sim_bet:
|
148
|
+
org_bet: CollateralToken
|
149
|
+
sim_bet: CollateralToken
|
154
150
|
org_dir: bool
|
155
151
|
sim_dir: bool
|
156
|
-
org_profit:
|
157
|
-
sim_profit:
|
152
|
+
org_profit: CollateralToken
|
153
|
+
sim_profit: CollateralToken
|
158
154
|
timestamp: DatetimeUTC
|
159
155
|
|
160
156
|
|
@@ -166,10 +162,10 @@ class SharpeOutput(BaseModel):
|
|
166
162
|
|
167
163
|
class SimulatedLifetimeDetail(BaseModel):
|
168
164
|
p_yes_mse: float
|
169
|
-
total_bet_amount:
|
170
|
-
total_bet_profit:
|
171
|
-
total_simulated_amount:
|
172
|
-
total_simulated_profit:
|
165
|
+
total_bet_amount: CollateralToken
|
166
|
+
total_bet_profit: CollateralToken
|
167
|
+
total_simulated_amount: CollateralToken
|
168
|
+
total_simulated_profit: CollateralToken
|
173
169
|
roi: float
|
174
170
|
simulated_roi: float
|
175
171
|
sharpe_output_original: SharpeOutput
|
@@ -5,11 +5,7 @@ import tenacity
|
|
5
5
|
|
6
6
|
from prediction_market_agent_tooling.gtypes import Mana, SecretStr
|
7
7
|
from prediction_market_agent_tooling.loggers import logger
|
8
|
-
from prediction_market_agent_tooling.markets.data_models import
|
9
|
-
BetAmount,
|
10
|
-
Currency,
|
11
|
-
ResolvedBet,
|
12
|
-
)
|
8
|
+
from prediction_market_agent_tooling.markets.data_models import ResolvedBet
|
13
9
|
from prediction_market_agent_tooling.markets.manifold.data_models import (
|
14
10
|
FullManifoldMarket,
|
15
11
|
ManifoldBet,
|
@@ -211,7 +207,7 @@ def manifold_to_generic_resolved_bet(
|
|
211
207
|
market_outcome = market.get_resolved_boolean_outcome()
|
212
208
|
return ResolvedBet(
|
213
209
|
id=bet.id,
|
214
|
-
amount=
|
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
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
from prediction_market_agent_tooling.gtypes import (
|
7
|
+
USD,
|
8
|
+
CollateralToken,
|
9
|
+
Mana,
|
10
|
+
OutcomeStr,
|
11
|
+
OutcomeToken,
|
12
|
+
Probability,
|
11
13
|
)
|
14
|
+
from prediction_market_agent_tooling.markets.data_models import Resolution
|
12
15
|
from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
|
13
16
|
|
14
17
|
MANIFOLD_BASE_URL = "https://manifold.markets"
|
15
18
|
|
16
19
|
|
20
|
+
def mana_to_usd(mana: Mana) -> USD:
|
21
|
+
# Not really, but for sake of simplicity. Mana are just play money.
|
22
|
+
return USD(mana.value)
|
23
|
+
|
24
|
+
|
25
|
+
def usd_to_mana(usd: USD) -> Mana:
|
26
|
+
# Not really, but for sake of simplicity. Mana are just play money.
|
27
|
+
return Mana(usd.value)
|
28
|
+
|
29
|
+
|
17
30
|
class ManifoldPool(BaseModel):
|
18
|
-
NO:
|
19
|
-
YES:
|
31
|
+
NO: OutcomeToken
|
32
|
+
YES: OutcomeToken
|
20
33
|
|
21
|
-
def size_for_outcome(self, outcome: str) ->
|
34
|
+
def size_for_outcome(self, outcome: str) -> OutcomeToken:
|
22
35
|
if hasattr(self, outcome):
|
23
|
-
return
|
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[
|
85
|
+
totalLiquidity: t.Optional[CollateralToken] = None
|
75
86
|
uniqueBettorCount: int
|
76
87
|
url: str
|
77
|
-
volume:
|
78
|
-
volume24Hours:
|
88
|
+
volume: CollateralToken
|
89
|
+
volume24Hours: CollateralToken
|
79
90
|
|
80
91
|
@property
|
81
|
-
def outcomes(self) ->
|
82
|
-
return
|
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:
|
184
|
+
shares: CollateralToken
|
174
185
|
probBefore: Probability
|
175
186
|
isFilled: t.Optional[bool] = None
|
176
187
|
probAfter: Probability
|
177
188
|
userId: str
|
178
|
-
amount:
|
189
|
+
amount: CollateralToken
|
179
190
|
contractId: str
|
180
191
|
id: str
|
181
192
|
fees: ManifoldBetFees
|
182
193
|
isCancelled: t.Optional[bool] = None
|
183
|
-
loanAmount:
|
184
|
-
orderAmount: t.Optional[
|
194
|
+
loanAmount: CollateralToken | None
|
195
|
+
orderAmount: t.Optional[CollateralToken] = None
|
185
196
|
fills: t.Optional[list[ManifoldBetFills]] = None
|
186
197
|
createdTime: DatetimeUTC
|
187
198
|
outcome: Resolution
|
@@ -194,16 +205,13 @@ class ManifoldBet(BaseModel):
|
|
194
205
|
else:
|
195
206
|
should_not_happen(f"Unexpected bet outcome string, '{self.outcome.value}'.")
|
196
207
|
|
197
|
-
def get_profit(self, market_outcome: bool) ->
|
208
|
+
def get_profit(self, market_outcome: bool) -> CollateralToken:
|
198
209
|
profit = (
|
199
210
|
self.shares - self.amount
|
200
211
|
if self.get_resolved_boolean_outcome() == market_outcome
|
201
212
|
else -self.amount
|
202
213
|
)
|
203
|
-
return
|
204
|
-
amount=profit,
|
205
|
-
currency=Currency.Mana,
|
206
|
-
)
|
214
|
+
return profit
|
207
215
|
|
208
216
|
|
209
217
|
class ManifoldContractMetric(BaseModel):
|
@@ -2,14 +2,18 @@ import typing as t
|
|
2
2
|
from math import ceil
|
3
3
|
|
4
4
|
from prediction_market_agent_tooling.config import APIKeys
|
5
|
-
from prediction_market_agent_tooling.gtypes import
|
5
|
+
from prediction_market_agent_tooling.gtypes import (
|
6
|
+
USD,
|
7
|
+
CollateralToken,
|
8
|
+
Mana,
|
9
|
+
Probability,
|
10
|
+
)
|
6
11
|
from prediction_market_agent_tooling.markets.agent_market import (
|
7
12
|
AgentMarket,
|
8
13
|
FilterBy,
|
9
14
|
MarketFees,
|
10
15
|
SortBy,
|
11
16
|
)
|
12
|
-
from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency
|
13
17
|
from prediction_market_agent_tooling.markets.manifold.api import (
|
14
18
|
get_authenticated_user,
|
15
19
|
get_manifold_binary_markets,
|
@@ -19,6 +23,7 @@ from prediction_market_agent_tooling.markets.manifold.api import (
|
|
19
23
|
from prediction_market_agent_tooling.markets.manifold.data_models import (
|
20
24
|
MANIFOLD_BASE_URL,
|
21
25
|
FullManifoldMarket,
|
26
|
+
usd_to_mana,
|
22
27
|
)
|
23
28
|
from prediction_market_agent_tooling.tools.betting_strategies.minimum_bet_to_win import (
|
24
29
|
minimum_bet_to_win,
|
@@ -31,7 +36,6 @@ class ManifoldAgentMarket(AgentMarket):
|
|
31
36
|
Manifold's market class that can be used by agents to make predictions.
|
32
37
|
"""
|
33
38
|
|
34
|
-
currency: t.ClassVar[Currency] = Currency.Mana
|
35
39
|
base_url: t.ClassVar[str] = MANIFOLD_BASE_URL
|
36
40
|
|
37
41
|
# Manifold has additional fees than `platform_absolute`, but they don't expose them in the API before placing the bet, see https://docs.manifold.markets/api.
|
@@ -49,19 +53,17 @@ class ManifoldAgentMarket(AgentMarket):
|
|
49
53
|
"""On Manifold, probablities aren't updated after the closure, so we can just use the current probability"""
|
50
54
|
return self.current_p_no
|
51
55
|
|
52
|
-
|
53
|
-
|
54
|
-
return BetAmount(amount=1, currency=cls.currency)
|
56
|
+
def get_tiny_bet_amount(self) -> CollateralToken:
|
57
|
+
return CollateralToken(1)
|
55
58
|
|
56
59
|
def get_minimum_bet_to_win(self, answer: bool, amount_to_win: float) -> Mana:
|
57
60
|
# Manifold lowest bet is 1 Mana, so we need to ceil the result.
|
58
|
-
return
|
61
|
+
return Mana(ceil(minimum_bet_to_win(answer, amount_to_win, self)))
|
59
62
|
|
60
|
-
def place_bet(self, outcome: bool, amount:
|
61
|
-
|
62
|
-
raise ValueError(f"Manifold bets are made in Mana. Got {amount.currency}.")
|
63
|
+
def place_bet(self, outcome: bool, amount: USD) -> str:
|
64
|
+
self.get_usd_in_token(amount)
|
63
65
|
bet = place_bet(
|
64
|
-
amount=
|
66
|
+
amount=usd_to_mana(amount),
|
65
67
|
market_id=self.id,
|
66
68
|
outcome=outcome,
|
67
69
|
manifold_api_key=APIKeys().manifold_api_key,
|