prediction-market-agent-tooling 0.61.1.dev463__py3-none-any.whl → 0.61.2.dev479__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/abis/gvp2_settlement.abi.json +89 -0
- prediction_market_agent_tooling/config.py +3 -2
- prediction_market_agent_tooling/deploy/agent.py +5 -4
- prediction_market_agent_tooling/deploy/betting_strategy.py +69 -53
- prediction_market_agent_tooling/gtypes.py +27 -105
- prediction_market_agent_tooling/jobs/jobs_models.py +7 -5
- prediction_market_agent_tooling/jobs/omen/omen_jobs.py +17 -13
- prediction_market_agent_tooling/markets/agent_market.py +52 -96
- prediction_market_agent_tooling/markets/blockchain_utils.py +27 -1
- prediction_market_agent_tooling/markets/data_models.py +44 -40
- prediction_market_agent_tooling/markets/manifold/api.py +6 -2
- prediction_market_agent_tooling/markets/manifold/data_models.py +25 -33
- prediction_market_agent_tooling/markets/manifold/manifold.py +11 -8
- prediction_market_agent_tooling/markets/market_fees.py +2 -4
- prediction_market_agent_tooling/markets/omen/data_models.py +57 -66
- prediction_market_agent_tooling/markets/omen/omen.py +249 -214
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +53 -31
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +14 -7
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +14 -20
- 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 +5 -3
- prediction_market_agent_tooling/markets/seer/data_models.py +8 -8
- prediction_market_agent_tooling/markets/seer/seer.py +71 -85
- prediction_market_agent_tooling/markets/seer/seer_contracts.py +5 -10
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +2 -5
- prediction_market_agent_tooling/monitor/monitor.py +2 -2
- prediction_market_agent_tooling/tools/balances.py +11 -9
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +10 -12
- prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +24 -27
- prediction_market_agent_tooling/tools/betting_strategies/utils.py +1 -3
- prediction_market_agent_tooling/tools/contract.py +10 -14
- prediction_market_agent_tooling/tools/cow/cow_manager.py +4 -3
- prediction_market_agent_tooling/tools/cow/cow_order.py +4 -3
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +1 -13
- prediction_market_agent_tooling/tools/omen/sell_positions.py +3 -6
- prediction_market_agent_tooling/tools/safe.py +6 -5
- prediction_market_agent_tooling/tools/tokens/auto_deposit.py +30 -32
- prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +22 -5
- prediction_market_agent_tooling/tools/tokens/main_token.py +2 -2
- prediction_market_agent_tooling/tools/utils.py +8 -14
- prediction_market_agent_tooling/tools/web3_utils.py +41 -24
- {prediction_market_agent_tooling-0.61.1.dev463.dist-info → prediction_market_agent_tooling-0.61.2.dev479.dist-info}/METADATA +1 -2
- {prediction_market_agent_tooling-0.61.1.dev463.dist-info → prediction_market_agent_tooling-0.61.2.dev479.dist-info}/RECORD +47 -49
- prediction_market_agent_tooling/tools/_generic_value.py +0 -255
- prediction_market_agent_tooling/tools/tokens/token_utils.py +0 -46
- prediction_market_agent_tooling/tools/tokens/usd.py +0 -63
- {prediction_market_agent_tooling-0.61.1.dev463.dist-info → prediction_market_agent_tooling-0.61.2.dev479.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.61.1.dev463.dist-info → prediction_market_agent_tooling-0.61.2.dev479.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.61.1.dev463.dist-info → prediction_market_agent_tooling-0.61.2.dev479.dist-info}/entry_points.txt +0 -0
@@ -4,16 +4,15 @@ from eth_typing import ChecksumAddress
|
|
4
4
|
from web3 import Web3
|
5
5
|
from web3.types import TxReceipt
|
6
6
|
|
7
|
-
from prediction_market_agent_tooling.config import APIKeys
|
7
|
+
from prediction_market_agent_tooling.config import APIKeys
|
8
8
|
from prediction_market_agent_tooling.gtypes import (
|
9
|
-
USD,
|
10
9
|
HexAddress,
|
11
10
|
HexBytes,
|
12
11
|
OutcomeStr,
|
13
|
-
|
14
|
-
|
15
|
-
Token,
|
12
|
+
Wei,
|
13
|
+
wei_type,
|
16
14
|
xDai,
|
15
|
+
xdai_type,
|
17
16
|
)
|
18
17
|
from prediction_market_agent_tooling.loggers import logger
|
19
18
|
from prediction_market_agent_tooling.markets.agent_market import (
|
@@ -24,9 +23,15 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
24
23
|
SortBy,
|
25
24
|
)
|
26
25
|
from prediction_market_agent_tooling.markets.blockchain_utils import store_trades
|
27
|
-
from prediction_market_agent_tooling.markets.data_models import
|
26
|
+
from prediction_market_agent_tooling.markets.data_models import (
|
27
|
+
BetAmount,
|
28
|
+
Currency,
|
29
|
+
Position,
|
30
|
+
TokenAmount,
|
31
|
+
)
|
28
32
|
from prediction_market_agent_tooling.markets.market_fees import MarketFees
|
29
33
|
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
|
34
|
+
from prediction_market_agent_tooling.markets.omen.omen_contracts import sDaiContract
|
30
35
|
from prediction_market_agent_tooling.markets.seer.data_models import (
|
31
36
|
NewMarketEvent,
|
32
37
|
SeerMarket,
|
@@ -38,6 +43,7 @@ from prediction_market_agent_tooling.markets.seer.seer_contracts import (
|
|
38
43
|
from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
|
39
44
|
SeerSubgraphHandler,
|
40
45
|
)
|
46
|
+
from prediction_market_agent_tooling.tools.balances import get_balances
|
41
47
|
from prediction_market_agent_tooling.tools.contract import (
|
42
48
|
ContractERC20OnGnosisChain,
|
43
49
|
init_collateral_token_contract,
|
@@ -51,16 +57,14 @@ from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
|
|
51
57
|
from prediction_market_agent_tooling.tools.tokens.auto_deposit import (
|
52
58
|
auto_deposit_collateral_token,
|
53
59
|
)
|
54
|
-
from prediction_market_agent_tooling.tools.
|
55
|
-
get_token_in_usd,
|
56
|
-
get_usd_in_token,
|
57
|
-
)
|
60
|
+
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai, xdai_to_wei
|
58
61
|
|
59
62
|
# We place a larger bet amount by default than Omen so that cow presents valid quotes.
|
60
|
-
SEER_TINY_BET_AMOUNT =
|
63
|
+
SEER_TINY_BET_AMOUNT = xdai_type(0.1)
|
61
64
|
|
62
65
|
|
63
66
|
class SeerAgentMarket(AgentMarket):
|
67
|
+
currency = Currency.sDai
|
64
68
|
wrapped_tokens: list[ChecksumAddress]
|
65
69
|
creator: HexAddress
|
66
70
|
collateral_token_contract_address_checksummed: ChecksumAddress
|
@@ -70,16 +74,6 @@ class SeerAgentMarket(AgentMarket):
|
|
70
74
|
None # Seer markets don't have a description, so just default to None.
|
71
75
|
)
|
72
76
|
|
73
|
-
def get_collateral_token_contract(
|
74
|
-
self, web3: Web3 | None = None
|
75
|
-
) -> ContractERC20OnGnosisChain:
|
76
|
-
web3 = web3 or RPCConfig().get_web3()
|
77
|
-
return to_gnosis_chain_contract(
|
78
|
-
init_collateral_token_contract(
|
79
|
-
self.collateral_token_contract_address_checksummed, web3
|
80
|
-
)
|
81
|
-
)
|
82
|
-
|
83
77
|
def store_prediction(
|
84
78
|
self,
|
85
79
|
processed_market: ProcessedMarket | None,
|
@@ -101,28 +95,29 @@ class SeerAgentMarket(AgentMarket):
|
|
101
95
|
agent_name=agent_name,
|
102
96
|
)
|
103
97
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
98
|
+
def _convert_bet_amount_into_wei(self, bet_amount: BetAmount) -> Wei:
|
99
|
+
if bet_amount.currency == self.currency:
|
100
|
+
return xdai_to_wei(xdai_type(bet_amount.amount))
|
101
|
+
raise ValueError(
|
102
|
+
f"Currencies don't match. Currency bet amount {bet_amount.currency} currency market: {self.currency}"
|
103
|
+
)
|
109
104
|
|
110
105
|
def get_buy_token_amount(
|
111
|
-
self, bet_amount:
|
112
|
-
) ->
|
106
|
+
self, bet_amount: BetAmount, direction: bool
|
107
|
+
) -> TokenAmount:
|
113
108
|
"""Returns number of outcome tokens returned for a given bet expressed in collateral units."""
|
114
109
|
|
115
110
|
outcome_token = self.get_wrapped_token_for_outcome(direction)
|
116
|
-
|
117
|
-
bet_amount_in_wei =
|
111
|
+
|
112
|
+
bet_amount_in_wei = self._convert_bet_amount_into_wei(bet_amount=bet_amount)
|
118
113
|
|
119
114
|
quote = CowManager().get_quote(
|
120
115
|
buy_token=outcome_token,
|
121
116
|
sell_amount=bet_amount_in_wei,
|
122
117
|
collateral_token=self.collateral_token_contract_address_checksummed,
|
123
118
|
)
|
124
|
-
sell_amount =
|
125
|
-
return sell_amount
|
119
|
+
sell_amount = wei_to_xdai(wei_type(quote.quote.buyAmount.root))
|
120
|
+
return TokenAmount(amount=sell_amount, currency=bet_amount.currency)
|
126
121
|
|
127
122
|
def get_outcome_str_from_bool(self, outcome: bool) -> OutcomeStr:
|
128
123
|
outcome_translated = SeerOutcomeEnum.from_bool(outcome)
|
@@ -130,46 +125,33 @@ class SeerAgentMarket(AgentMarket):
|
|
130
125
|
return OutcomeStr(self.outcomes[idx])
|
131
126
|
|
132
127
|
@staticmethod
|
133
|
-
def get_trade_balance(api_keys: APIKeys) ->
|
128
|
+
def get_trade_balance(api_keys: APIKeys) -> float:
|
134
129
|
return OmenAgentMarket.get_trade_balance(api_keys=api_keys)
|
135
130
|
|
136
|
-
|
137
|
-
|
131
|
+
@classmethod
|
132
|
+
def get_tiny_bet_amount(cls) -> BetAmount:
|
133
|
+
return BetAmount(amount=SEER_TINY_BET_AMOUNT, currency=cls.currency)
|
138
134
|
|
139
|
-
def get_position(
|
140
|
-
self, user_id: str, web3: Web3 | None = None
|
141
|
-
) -> ExistingPosition | None:
|
135
|
+
def get_position(self, user_id: str, web3: Web3 | None = None) -> Position | None:
|
142
136
|
"""
|
143
137
|
Fetches position from the user in a given market.
|
144
138
|
We ignore the INVALID balances since we are only interested in binary outcomes.
|
145
139
|
"""
|
146
140
|
|
147
|
-
|
141
|
+
amounts = {}
|
148
142
|
|
149
143
|
for outcome in [True, False]:
|
150
144
|
wrapped_token = self.get_wrapped_token_for_outcome(outcome)
|
151
145
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
)
|
156
|
-
)
|
146
|
+
outcome_token_balance = ContractERC20OnGnosisChain(
|
147
|
+
address=wrapped_token
|
148
|
+
).balanceOf(for_address=Web3.to_checksum_address(user_id), web3=web3)
|
157
149
|
outcome_str = self.get_outcome_str_from_bool(outcome=outcome)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
}
|
164
|
-
amounts_potential = {
|
165
|
-
k: self.get_token_in_usd(v.as_token) for k, v in amounts_ot.items()
|
166
|
-
}
|
167
|
-
return ExistingPosition(
|
168
|
-
market_id=self.id,
|
169
|
-
amounts_current=amounts_current,
|
170
|
-
amounts_potential=amounts_potential,
|
171
|
-
amounts_ot=amounts_ot,
|
172
|
-
)
|
150
|
+
amounts[outcome_str] = TokenAmount(
|
151
|
+
amount=wei_to_xdai(outcome_token_balance), currency=self.currency
|
152
|
+
)
|
153
|
+
|
154
|
+
return Position(market_id=self.id, amounts=amounts)
|
173
155
|
|
174
156
|
@staticmethod
|
175
157
|
def get_user_id(api_keys: APIKeys) -> str:
|
@@ -228,9 +210,9 @@ class SeerAgentMarket(AgentMarket):
|
|
228
210
|
CowManager().get_quote(
|
229
211
|
collateral_token=self.collateral_token_contract_address_checksummed,
|
230
212
|
buy_token=outcome_token,
|
231
|
-
sell_amount=
|
232
|
-
1
|
233
|
-
)
|
213
|
+
sell_amount=xdai_to_wei(
|
214
|
+
xdai_type(1)
|
215
|
+
), # we take 1 xDai as a baseline value for common trades the agents take.
|
234
216
|
)
|
235
217
|
return True
|
236
218
|
except NoLiquidityAvailableOnCowException:
|
@@ -254,7 +236,7 @@ class SeerAgentMarket(AgentMarket):
|
|
254
236
|
def place_bet(
|
255
237
|
self,
|
256
238
|
outcome: bool,
|
257
|
-
amount:
|
239
|
+
amount: BetAmount,
|
258
240
|
auto_deposit: bool = True,
|
259
241
|
web3: Web3 | None = None,
|
260
242
|
api_keys: APIKeys | None = None,
|
@@ -265,27 +247,32 @@ class SeerAgentMarket(AgentMarket):
|
|
265
247
|
f"Market {self.id} is not open for trading. Cannot place bet."
|
266
248
|
)
|
267
249
|
|
268
|
-
|
269
|
-
|
270
|
-
collateral_contract = self.get_collateral_token_contract()
|
250
|
+
if amount.currency != self.currency:
|
251
|
+
raise ValueError(f"Seer bets are made in xDai. Got {amount.currency}.")
|
271
252
|
|
253
|
+
collateral_contract = sDaiContract()
|
272
254
|
if auto_deposit:
|
255
|
+
# We convert the deposit amount (in sDai) to assets in order to convert.
|
256
|
+
asset_amount = collateral_contract.convertToAssets(
|
257
|
+
xdai_to_wei(xdai_type(amount.amount))
|
258
|
+
)
|
273
259
|
auto_deposit_collateral_token(
|
274
|
-
collateral_contract,
|
260
|
+
collateral_contract, asset_amount, api_keys, web3
|
275
261
|
)
|
276
262
|
|
277
|
-
|
278
|
-
|
263
|
+
# We require that amount is given in sDAI.
|
264
|
+
collateral_balance = get_balances(address=api_keys.bet_from_address, web3=web3)
|
265
|
+
if collateral_balance.sdai < amount.amount:
|
279
266
|
raise ValueError(
|
280
|
-
f"Balance {collateral_balance} not enough for bet size {amount}"
|
267
|
+
f"Balance {collateral_balance.sdai} not enough for bet size {amount.amount}"
|
281
268
|
)
|
282
269
|
|
283
270
|
outcome_token = self.get_wrapped_token_for_outcome(outcome)
|
284
|
-
#
|
271
|
+
# Sell sDAI using token address
|
285
272
|
order_metadata = CowManager().swap(
|
286
|
-
amount=
|
273
|
+
amount=xdai_type(amount.amount),
|
287
274
|
sell_token=collateral_contract.address,
|
288
|
-
buy_token=outcome_token,
|
275
|
+
buy_token=Web3.to_checksum_address(outcome_token),
|
289
276
|
api_keys=api_keys,
|
290
277
|
web3=web3,
|
291
278
|
)
|
@@ -295,17 +282,18 @@ class SeerAgentMarket(AgentMarket):
|
|
295
282
|
|
296
283
|
def seer_create_market_tx(
|
297
284
|
api_keys: APIKeys,
|
298
|
-
initial_funds:
|
285
|
+
initial_funds: xDai,
|
299
286
|
question: str,
|
300
287
|
opening_time: DatetimeUTC,
|
301
288
|
language: str,
|
302
|
-
outcomes:
|
289
|
+
outcomes: list[str],
|
303
290
|
auto_deposit: bool,
|
304
291
|
category: str,
|
305
292
|
min_bond_xdai: xDai,
|
306
293
|
web3: Web3 | None = None,
|
307
294
|
) -> ChecksumAddress:
|
308
295
|
web3 = web3 or SeerMarketFactory.get_web3() # Default to Gnosis web3.
|
296
|
+
initial_funds_wei = xdai_to_wei(initial_funds)
|
309
297
|
|
310
298
|
factory_contract = SeerMarketFactory()
|
311
299
|
collateral_token_address = factory_contract.collateral_token(web3=web3)
|
@@ -313,26 +301,24 @@ def seer_create_market_tx(
|
|
313
301
|
init_collateral_token_contract(collateral_token_address, web3)
|
314
302
|
)
|
315
303
|
|
316
|
-
initial_funds_in_collateral = (
|
317
|
-
get_usd_in_token(initial_funds, collateral_token_address)
|
318
|
-
if isinstance(initial_funds, USD)
|
319
|
-
else initial_funds
|
320
|
-
)
|
321
|
-
initial_funds_in_collateral_wei = initial_funds_in_collateral.as_wei
|
322
|
-
|
323
304
|
if auto_deposit:
|
324
305
|
auto_deposit_collateral_token(
|
325
306
|
collateral_token_contract=collateral_token_contract,
|
326
307
|
api_keys=api_keys,
|
327
|
-
|
308
|
+
amount_wei=initial_funds_wei,
|
328
309
|
web3=web3,
|
329
310
|
)
|
330
311
|
|
312
|
+
# In case of ERC4626, obtained (for example) sDai out of xDai could be lower than the `amount_wei`, so we need to handle it.
|
313
|
+
initial_funds_in_shares = collateral_token_contract.get_in_shares(
|
314
|
+
amount=initial_funds_wei, web3=web3
|
315
|
+
)
|
316
|
+
|
331
317
|
# Approve the market maker to withdraw our collateral token.
|
332
318
|
collateral_token_contract.approve(
|
333
319
|
api_keys=api_keys,
|
334
320
|
for_address=factory_contract.address,
|
335
|
-
amount_wei=
|
321
|
+
amount_wei=initial_funds_in_shares,
|
336
322
|
web3=web3,
|
337
323
|
)
|
338
324
|
|
@@ -343,7 +329,7 @@ def seer_create_market_tx(
|
|
343
329
|
opening_time=opening_time,
|
344
330
|
language=language,
|
345
331
|
category=category,
|
346
|
-
|
332
|
+
min_bond_xdai=min_bond_xdai,
|
347
333
|
)
|
348
334
|
tx_receipt = factory_contract.create_categorical_market(
|
349
335
|
api_keys=api_keys, params=params, web3=web3
|
@@ -1,16 +1,10 @@
|
|
1
1
|
import os
|
2
|
-
import typing as t
|
3
2
|
|
4
3
|
from web3 import Web3
|
5
4
|
from web3.types import TxReceipt
|
6
5
|
|
7
6
|
from prediction_market_agent_tooling.config import APIKeys
|
8
|
-
from prediction_market_agent_tooling.gtypes import
|
9
|
-
ABI,
|
10
|
-
ChecksumAddress,
|
11
|
-
OutcomeStr,
|
12
|
-
xDai,
|
13
|
-
)
|
7
|
+
from prediction_market_agent_tooling.gtypes import ABI, ChecksumAddress, xDai
|
14
8
|
from prediction_market_agent_tooling.markets.seer.data_models import (
|
15
9
|
CreateCategoricalMarketsParams,
|
16
10
|
)
|
@@ -19,6 +13,7 @@ from prediction_market_agent_tooling.tools.contract import (
|
|
19
13
|
abi_field_validator,
|
20
14
|
)
|
21
15
|
from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
|
16
|
+
from prediction_market_agent_tooling.tools.web3_utils import xdai_to_wei
|
22
17
|
|
23
18
|
|
24
19
|
class SeerMarketFactory(ContractOnGnosisChain):
|
@@ -36,9 +31,9 @@ class SeerMarketFactory(ContractOnGnosisChain):
|
|
36
31
|
@staticmethod
|
37
32
|
def build_market_params(
|
38
33
|
market_question: str,
|
39
|
-
outcomes:
|
34
|
+
outcomes: list[str],
|
40
35
|
opening_time: DatetimeUTC,
|
41
|
-
|
36
|
+
min_bond_xdai: xDai,
|
42
37
|
language: str = "en_US",
|
43
38
|
category: str = "misc",
|
44
39
|
) -> CreateCategoricalMarketsParams:
|
@@ -47,7 +42,7 @@ class SeerMarketFactory(ContractOnGnosisChain):
|
|
47
42
|
token_names=[
|
48
43
|
o.upper() for o in outcomes
|
49
44
|
], # Following usual token names on Seer (YES,NO).
|
50
|
-
min_bond=
|
45
|
+
min_bond=xdai_to_wei(min_bond_xdai),
|
51
46
|
opening_time=int(opening_time.timestamp()),
|
52
47
|
outcomes=outcomes,
|
53
48
|
lang=language,
|
@@ -15,7 +15,6 @@ from prediction_market_agent_tooling.markets.seer.data_models import (
|
|
15
15
|
)
|
16
16
|
from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
|
17
17
|
from prediction_market_agent_tooling.tools.utils import to_int_timestamp, utcnow
|
18
|
-
from prediction_market_agent_tooling.tools.web3_utils import unwrap_generic_value
|
19
18
|
|
20
19
|
|
21
20
|
class SeerSubgraphHandler(BaseSubgraphHandler):
|
@@ -159,7 +158,7 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
159
158
|
first=(
|
160
159
|
limit if limit else sys.maxsize
|
161
160
|
), # if not limit, we fetch all possible markets,
|
162
|
-
where=
|
161
|
+
where=where_stms,
|
163
162
|
**optional_params,
|
164
163
|
)
|
165
164
|
fields = self._get_fields_for_markets(markets_field)
|
@@ -220,9 +219,7 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
220
219
|
{"token1": wrapped_token.lower()},
|
221
220
|
]
|
222
221
|
)
|
223
|
-
pools_field = self.swapr_algebra_subgraph.Query.pools(
|
224
|
-
where=unwrap_generic_value({"or": wheres})
|
225
|
-
)
|
222
|
+
pools_field = self.swapr_algebra_subgraph.Query.pools(where={"or": wheres})
|
226
223
|
fields = self._get_fields_for_pools(pools_field)
|
227
224
|
pools = self.do_query(fields=fields, pydantic_model=SeerPool)
|
228
225
|
return pools
|
@@ -186,12 +186,12 @@ def monitor_agent(agent: DeployedAgent) -> None:
|
|
186
186
|
return
|
187
187
|
bets_info = {
|
188
188
|
"Market Question": [bet.market_question for bet in agent_bets],
|
189
|
-
"Bet Amount": [bet.amount for bet in agent_bets],
|
189
|
+
"Bet Amount": [bet.amount.amount for bet in agent_bets],
|
190
190
|
"Bet Outcome": [bet.outcome for bet in agent_bets],
|
191
191
|
"Created Time": [bet.created_time for bet in agent_bets],
|
192
192
|
"Resolved Time": [bet.resolved_time for bet in agent_bets],
|
193
193
|
"Is Correct": [bet.is_correct for bet in agent_bets],
|
194
|
-
"Profit": [round(bet.profit, 2) for bet in agent_bets],
|
194
|
+
"Profit": [round(bet.profit.amount, 2) for bet in agent_bets],
|
195
195
|
}
|
196
196
|
|
197
197
|
# Time column to use for x-axes and sorting
|
@@ -1,30 +1,32 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
2
|
from tenacity import retry, stop_after_attempt, wait_fixed
|
3
3
|
from web3 import Web3
|
4
|
+
from web3.types import Wei
|
4
5
|
|
5
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress,
|
6
|
+
from prediction_market_agent_tooling.gtypes import ChecksumAddress, xDai
|
6
7
|
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
7
8
|
WrappedxDaiContract,
|
8
9
|
sDaiContract,
|
9
10
|
)
|
11
|
+
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
10
12
|
|
11
13
|
|
12
14
|
class Balances(BaseModel):
|
13
15
|
xdai: xDai
|
14
|
-
wxdai:
|
15
|
-
sdai:
|
16
|
+
wxdai: xDai
|
17
|
+
sdai: xDai
|
16
18
|
|
17
19
|
@property
|
18
|
-
def total(self) ->
|
19
|
-
return self.xdai
|
20
|
+
def total(self) -> xDai:
|
21
|
+
return xDai(self.xdai + self.wxdai + self.sdai)
|
20
22
|
|
21
23
|
|
22
24
|
@retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
|
23
25
|
def get_balances(address: ChecksumAddress, web3: Web3 | None = None) -> Balances:
|
24
26
|
if not web3:
|
25
27
|
web3 = WrappedxDaiContract().get_web3()
|
26
|
-
xdai_balance =
|
27
|
-
xdai = xdai_balance
|
28
|
-
wxdai = WrappedxDaiContract().balanceOf(address, web3=web3)
|
29
|
-
sdai = sDaiContract().balanceOf(address, web3=web3)
|
28
|
+
xdai_balance = Wei(web3.eth.get_balance(address))
|
29
|
+
xdai = wei_to_xdai(xdai_balance)
|
30
|
+
wxdai = wei_to_xdai(WrappedxDaiContract().balanceOf(address, web3=web3))
|
31
|
+
sdai = wei_to_xdai(sDaiContract().balanceOf(address, web3=web3))
|
30
32
|
return Balances(xdai=xdai, wxdai=wxdai, sdai=sdai)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
from prediction_market_agent_tooling.gtypes import OutcomeToken, Token
|
2
1
|
from prediction_market_agent_tooling.markets.market_fees import MarketFees
|
3
2
|
from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
|
4
3
|
|
@@ -9,7 +8,7 @@ def check_is_valid_probability(probability: float) -> None:
|
|
9
8
|
|
10
9
|
|
11
10
|
def get_kelly_bet_simplified(
|
12
|
-
max_bet:
|
11
|
+
max_bet: float,
|
13
12
|
market_p_yes: float,
|
14
13
|
estimated_p_yes: float,
|
15
14
|
confidence: float,
|
@@ -52,17 +51,17 @@ def get_kelly_bet_simplified(
|
|
52
51
|
kelly_fraction = edge / odds
|
53
52
|
|
54
53
|
# Ensure bet size is non-negative does not exceed the wallet balance
|
55
|
-
bet_size =
|
54
|
+
bet_size = min(kelly_fraction * max_bet, max_bet)
|
56
55
|
|
57
56
|
return SimpleBet(direction=bet_direction, size=bet_size)
|
58
57
|
|
59
58
|
|
60
59
|
def get_kelly_bet_full(
|
61
|
-
yes_outcome_pool_size:
|
62
|
-
no_outcome_pool_size:
|
60
|
+
yes_outcome_pool_size: float,
|
61
|
+
no_outcome_pool_size: float,
|
63
62
|
estimated_p_yes: float,
|
64
63
|
confidence: float,
|
65
|
-
max_bet:
|
64
|
+
max_bet: float,
|
66
65
|
fees: MarketFees,
|
67
66
|
) -> SimpleBet:
|
68
67
|
"""
|
@@ -98,13 +97,13 @@ def get_kelly_bet_full(
|
|
98
97
|
check_is_valid_probability(confidence)
|
99
98
|
|
100
99
|
if max_bet == 0:
|
101
|
-
return SimpleBet(direction=True, size=
|
100
|
+
return SimpleBet(direction=True, size=0)
|
102
101
|
|
103
|
-
x = yes_outcome_pool_size
|
104
|
-
y = no_outcome_pool_size
|
102
|
+
x = yes_outcome_pool_size
|
103
|
+
y = no_outcome_pool_size
|
105
104
|
p = estimated_p_yes
|
106
105
|
c = confidence
|
107
|
-
b = max_bet
|
106
|
+
b = max_bet
|
108
107
|
f = 1 - fee
|
109
108
|
|
110
109
|
if x == y:
|
@@ -145,6 +144,5 @@ def get_kelly_bet_full(
|
|
145
144
|
|
146
145
|
# Clip the bet size to max_bet to account for rounding errors.
|
147
146
|
return SimpleBet(
|
148
|
-
direction=kelly_bet_amount > 0,
|
149
|
-
size=Token(min(max_bet.value, abs(kelly_bet_amount))),
|
147
|
+
direction=kelly_bet_amount > 0, size=min(max_bet, abs(kelly_bet_amount))
|
150
148
|
)
|
@@ -2,18 +2,19 @@ from functools import reduce
|
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
|
5
|
-
from prediction_market_agent_tooling.gtypes import
|
5
|
+
from prediction_market_agent_tooling.gtypes import Probability, Wei, xDai
|
6
6
|
from prediction_market_agent_tooling.markets.omen.omen import (
|
7
7
|
MarketFees,
|
8
8
|
OmenAgentMarket,
|
9
9
|
)
|
10
10
|
from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
|
11
11
|
from prediction_market_agent_tooling.tools.utils import check_not_none
|
12
|
+
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai, xdai_to_wei
|
12
13
|
|
13
14
|
|
14
15
|
def get_market_moving_bet(
|
15
|
-
yes_outcome_pool_size:
|
16
|
-
no_outcome_pool_size:
|
16
|
+
yes_outcome_pool_size: float,
|
17
|
+
no_outcome_pool_size: float,
|
17
18
|
market_p_yes: float,
|
18
19
|
target_p_yes: float,
|
19
20
|
fees: MarketFees,
|
@@ -41,21 +42,20 @@ def get_market_moving_bet(
|
|
41
42
|
fixed_product = yes_outcome_pool_size * no_outcome_pool_size
|
42
43
|
bet_direction: bool = target_p_yes > market_p_yes
|
43
44
|
|
44
|
-
min_bet_amount =
|
45
|
-
max_bet_amount = (
|
45
|
+
min_bet_amount = 0.0
|
46
|
+
max_bet_amount = 100 * (
|
46
47
|
yes_outcome_pool_size + no_outcome_pool_size
|
47
|
-
)
|
48
|
+
) # TODO set a better upper bound
|
48
49
|
|
49
50
|
# Binary search for the optimal bet amount
|
50
51
|
for _ in range(max_iters):
|
51
52
|
bet_amount = (min_bet_amount + max_bet_amount) / 2
|
52
|
-
amounts_diff = fees.
|
53
|
-
amounts_diff_as_ot = OutcomeToken.from_token(amounts_diff)
|
53
|
+
amounts_diff = fees.get_bet_size_after_fees(bet_amount)
|
54
54
|
|
55
55
|
# Initial new amounts are old amounts + equal new amounts for each outcome
|
56
|
-
yes_outcome_new_pool_size = yes_outcome_pool_size +
|
57
|
-
no_outcome_new_pool_size = no_outcome_pool_size +
|
58
|
-
new_amounts
|
56
|
+
yes_outcome_new_pool_size = yes_outcome_pool_size + amounts_diff
|
57
|
+
no_outcome_new_pool_size = no_outcome_pool_size + amounts_diff
|
58
|
+
new_amounts = {
|
59
59
|
True: yes_outcome_new_pool_size,
|
60
60
|
False: no_outcome_new_pool_size,
|
61
61
|
}
|
@@ -63,20 +63,15 @@ def get_market_moving_bet(
|
|
63
63
|
# Now give away tokens at `bet_outcome_index` to restore invariant
|
64
64
|
new_product = yes_outcome_new_pool_size * no_outcome_new_pool_size
|
65
65
|
dx = (new_product - fixed_product) / new_amounts[not bet_direction]
|
66
|
-
new_amounts[bet_direction] -=
|
66
|
+
new_amounts[bet_direction] -= dx
|
67
67
|
|
68
68
|
# Check that the invariant is restored
|
69
69
|
assert np.isclose(
|
70
|
-
reduce(lambda x, y: x * y
|
70
|
+
reduce(lambda x, y: x * y, list(new_amounts.values()), 1.0),
|
71
71
|
float(fixed_product),
|
72
72
|
)
|
73
73
|
|
74
|
-
new_p_yes = Probability(
|
75
|
-
(
|
76
|
-
new_amounts[False]
|
77
|
-
/ sum(list(new_amounts.values()), start=OutcomeToken(0))
|
78
|
-
)
|
79
|
-
)
|
74
|
+
new_p_yes = Probability(new_amounts[False] / sum(list(new_amounts.values())))
|
80
75
|
if abs(target_p_yes - new_p_yes) < 1e-6:
|
81
76
|
break
|
82
77
|
elif new_p_yes > target_p_yes:
|
@@ -102,31 +97,33 @@ def _sanity_check_omen_market_moving_bet(
|
|
102
97
|
using the adjusted outcome pool sizes to calculate the new p_yes.
|
103
98
|
"""
|
104
99
|
buy_amount_ = market.get_contract().calcBuyAmount(
|
105
|
-
investment_amount=bet_to_check.size
|
100
|
+
investment_amount=xdai_to_wei(xDai(bet_to_check.size)),
|
106
101
|
outcome_index=market.get_outcome_index(
|
107
102
|
market.get_outcome_str_from_bool(bet_to_check.direction)
|
108
103
|
),
|
109
104
|
)
|
110
|
-
buy_amount = buy_amount_
|
105
|
+
buy_amount = float(wei_to_xdai(Wei(buy_amount_)))
|
111
106
|
|
112
107
|
outcome_token_pool = check_not_none(market.outcome_token_pool)
|
113
108
|
yes_outcome_pool_size = outcome_token_pool[market.get_outcome_str_from_bool(True)]
|
114
109
|
no_outcome_pool_size = outcome_token_pool[market.get_outcome_str_from_bool(False)]
|
115
|
-
market_const = yes_outcome_pool_size
|
110
|
+
market_const = yes_outcome_pool_size * no_outcome_pool_size
|
116
111
|
|
117
|
-
bet_to_check_size_after_fees = market.fees.
|
112
|
+
bet_to_check_size_after_fees = market.fees.get_bet_size_after_fees(
|
113
|
+
bet_to_check.size
|
114
|
+
)
|
118
115
|
|
119
116
|
# When you buy 'yes' tokens, you add your bet size to the both pools, then
|
120
117
|
# subtract `buy_amount` from the 'yes' pool. And vice versa for 'no' tokens.
|
121
118
|
new_yes_outcome_pool_size = (
|
122
|
-
yes_outcome_pool_size
|
119
|
+
yes_outcome_pool_size
|
123
120
|
+ bet_to_check_size_after_fees
|
124
|
-
- float(bet_to_check.direction) * buy_amount
|
121
|
+
- float(bet_to_check.direction) * buy_amount
|
125
122
|
)
|
126
123
|
new_no_outcome_pool_size = (
|
127
|
-
no_outcome_pool_size
|
124
|
+
no_outcome_pool_size
|
128
125
|
+ bet_to_check_size_after_fees
|
129
|
-
- float(not bet_to_check.direction) * buy_amount
|
126
|
+
- float(not bet_to_check.direction) * buy_amount
|
130
127
|
)
|
131
128
|
new_market_const = new_yes_outcome_pool_size * new_no_outcome_pool_size
|
132
129
|
# Check the invariant is restored
|