prediction-market-agent-tooling 0.61.1__py3-none-any.whl → 0.61.1.dev461__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 -52
- 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 +8 -11
- prediction_market_agent_tooling/markets/market_fees.py +4 -2
- prediction_market_agent_tooling/markets/omen/data_models.py +66 -57
- prediction_market_agent_tooling/markets/omen/omen.py +214 -249
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +31 -29
- 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 +246 -0
- prediction_market_agent_tooling/tools/balances.py +9 -11
- prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +12 -10
- prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +27 -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 +3 -4
- 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 +32 -30
- prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +5 -22
- 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 +63 -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.dist-info → prediction_market_agent_tooling-0.61.1.dev461.dist-info}/METADATA +2 -1
- {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev461.dist-info}/RECORD +48 -45
- {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev461.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev461.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.61.1.dist-info → prediction_market_agent_tooling-0.61.1.dev461.dist-info}/entry_points.txt +0 -0
@@ -1,32 +1,30 @@
|
|
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
|
5
4
|
|
6
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress, xDai
|
5
|
+
from prediction_market_agent_tooling.gtypes import ChecksumAddress, Token, xDai, xDaiWei
|
7
6
|
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
8
7
|
WrappedxDaiContract,
|
9
8
|
sDaiContract,
|
10
9
|
)
|
11
|
-
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
12
10
|
|
13
11
|
|
14
12
|
class Balances(BaseModel):
|
15
13
|
xdai: xDai
|
16
|
-
wxdai:
|
17
|
-
sdai:
|
14
|
+
wxdai: Token
|
15
|
+
sdai: Token
|
18
16
|
|
19
17
|
@property
|
20
|
-
def total(self) ->
|
21
|
-
return
|
18
|
+
def total(self) -> Token:
|
19
|
+
return self.xdai.as_token + self.wxdai + self.sdai
|
22
20
|
|
23
21
|
|
24
22
|
@retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
|
25
23
|
def get_balances(address: ChecksumAddress, web3: Web3 | None = None) -> Balances:
|
26
24
|
if not web3:
|
27
25
|
web3 = WrappedxDaiContract().get_web3()
|
28
|
-
xdai_balance =
|
29
|
-
xdai =
|
30
|
-
wxdai =
|
31
|
-
sdai =
|
26
|
+
xdai_balance = xDaiWei(web3.eth.get_balance(address))
|
27
|
+
xdai = xdai_balance.as_xdai
|
28
|
+
wxdai = WrappedxDaiContract().balanceOf(address, web3=web3).as_token
|
29
|
+
sdai = sDaiContract().balanceOf(address, web3=web3).as_token
|
32
30
|
return Balances(xdai=xdai, wxdai=wxdai, sdai=sdai)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from prediction_market_agent_tooling.gtypes import OutcomeToken, Token
|
1
2
|
from prediction_market_agent_tooling.markets.market_fees import MarketFees
|
2
3
|
from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
|
3
4
|
|
@@ -8,7 +9,7 @@ def check_is_valid_probability(probability: float) -> None:
|
|
8
9
|
|
9
10
|
|
10
11
|
def get_kelly_bet_simplified(
|
11
|
-
max_bet:
|
12
|
+
max_bet: Token,
|
12
13
|
market_p_yes: float,
|
13
14
|
estimated_p_yes: float,
|
14
15
|
confidence: float,
|
@@ -51,17 +52,17 @@ def get_kelly_bet_simplified(
|
|
51
52
|
kelly_fraction = edge / odds
|
52
53
|
|
53
54
|
# Ensure bet size is non-negative does not exceed the wallet balance
|
54
|
-
bet_size = min(kelly_fraction * max_bet, max_bet)
|
55
|
+
bet_size = Token(min(kelly_fraction * max_bet.value, max_bet.value))
|
55
56
|
|
56
57
|
return SimpleBet(direction=bet_direction, size=bet_size)
|
57
58
|
|
58
59
|
|
59
60
|
def get_kelly_bet_full(
|
60
|
-
yes_outcome_pool_size:
|
61
|
-
no_outcome_pool_size:
|
61
|
+
yes_outcome_pool_size: OutcomeToken,
|
62
|
+
no_outcome_pool_size: OutcomeToken,
|
62
63
|
estimated_p_yes: float,
|
63
64
|
confidence: float,
|
64
|
-
max_bet:
|
65
|
+
max_bet: Token,
|
65
66
|
fees: MarketFees,
|
66
67
|
) -> SimpleBet:
|
67
68
|
"""
|
@@ -97,13 +98,13 @@ def get_kelly_bet_full(
|
|
97
98
|
check_is_valid_probability(confidence)
|
98
99
|
|
99
100
|
if max_bet == 0:
|
100
|
-
return SimpleBet(direction=True, size=0)
|
101
|
+
return SimpleBet(direction=True, size=Token(0))
|
101
102
|
|
102
|
-
x = yes_outcome_pool_size
|
103
|
-
y = no_outcome_pool_size
|
103
|
+
x = yes_outcome_pool_size.value
|
104
|
+
y = no_outcome_pool_size.value
|
104
105
|
p = estimated_p_yes
|
105
106
|
c = confidence
|
106
|
-
b = max_bet
|
107
|
+
b = max_bet.value
|
107
108
|
f = 1 - fee
|
108
109
|
|
109
110
|
if x == y:
|
@@ -144,5 +145,6 @@ def get_kelly_bet_full(
|
|
144
145
|
|
145
146
|
# Clip the bet size to max_bet to account for rounding errors.
|
146
147
|
return SimpleBet(
|
147
|
-
direction=kelly_bet_amount > 0,
|
148
|
+
direction=kelly_bet_amount > 0,
|
149
|
+
size=Token(min(max_bet.value, abs(kelly_bet_amount))),
|
148
150
|
)
|
@@ -2,19 +2,18 @@ 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 OutcomeToken, Probability, Token
|
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
|
13
12
|
|
14
13
|
|
15
14
|
def get_market_moving_bet(
|
16
|
-
yes_outcome_pool_size:
|
17
|
-
no_outcome_pool_size:
|
15
|
+
yes_outcome_pool_size: OutcomeToken,
|
16
|
+
no_outcome_pool_size: OutcomeToken,
|
18
17
|
market_p_yes: float,
|
19
18
|
target_p_yes: float,
|
20
19
|
fees: MarketFees,
|
@@ -42,20 +41,21 @@ def get_market_moving_bet(
|
|
42
41
|
fixed_product = yes_outcome_pool_size * no_outcome_pool_size
|
43
42
|
bet_direction: bool = target_p_yes > market_p_yes
|
44
43
|
|
45
|
-
min_bet_amount = 0.0
|
46
|
-
max_bet_amount =
|
44
|
+
min_bet_amount = Token(0.0)
|
45
|
+
max_bet_amount = (
|
47
46
|
yes_outcome_pool_size + no_outcome_pool_size
|
48
|
-
) # TODO set a better upper bound
|
47
|
+
).as_token * 100 # TODO set a better upper bound
|
49
48
|
|
50
49
|
# Binary search for the optimal bet amount
|
51
50
|
for _ in range(max_iters):
|
52
51
|
bet_amount = (min_bet_amount + max_bet_amount) / 2
|
53
|
-
amounts_diff = fees.
|
52
|
+
amounts_diff = fees.get_after_fees(bet_amount)
|
53
|
+
amounts_diff_as_ot = OutcomeToken.from_token(amounts_diff)
|
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_as_ot
|
57
|
+
no_outcome_new_pool_size = no_outcome_pool_size + amounts_diff_as_ot
|
58
|
+
new_amounts: dict[bool, OutcomeToken] = {
|
59
59
|
True: yes_outcome_new_pool_size,
|
60
60
|
False: no_outcome_new_pool_size,
|
61
61
|
}
|
@@ -63,15 +63,20 @@ 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] -= dx
|
66
|
+
new_amounts[bet_direction] -= OutcomeToken(dx)
|
67
67
|
|
68
68
|
# Check that the invariant is restored
|
69
69
|
assert np.isclose(
|
70
|
-
reduce(lambda x, y: x * y, list(new_amounts.values()), 1.0),
|
70
|
+
reduce(lambda x, y: x * y.value, list(new_amounts.values()), 1.0),
|
71
71
|
float(fixed_product),
|
72
72
|
)
|
73
73
|
|
74
|
-
new_p_yes = Probability(
|
74
|
+
new_p_yes = Probability(
|
75
|
+
(
|
76
|
+
new_amounts[False]
|
77
|
+
/ sum(list(new_amounts.values()), start=OutcomeToken(0))
|
78
|
+
)
|
79
|
+
)
|
75
80
|
if abs(target_p_yes - new_p_yes) < 1e-6:
|
76
81
|
break
|
77
82
|
elif new_p_yes > target_p_yes:
|
@@ -97,33 +102,31 @@ def _sanity_check_omen_market_moving_bet(
|
|
97
102
|
using the adjusted outcome pool sizes to calculate the new p_yes.
|
98
103
|
"""
|
99
104
|
buy_amount_ = market.get_contract().calcBuyAmount(
|
100
|
-
investment_amount=
|
105
|
+
investment_amount=bet_to_check.size.as_wei,
|
101
106
|
outcome_index=market.get_outcome_index(
|
102
107
|
market.get_outcome_str_from_bool(bet_to_check.direction)
|
103
108
|
),
|
104
109
|
)
|
105
|
-
buy_amount =
|
110
|
+
buy_amount = buy_amount_.as_outcome_token
|
106
111
|
|
107
112
|
outcome_token_pool = check_not_none(market.outcome_token_pool)
|
108
113
|
yes_outcome_pool_size = outcome_token_pool[market.get_outcome_str_from_bool(True)]
|
109
114
|
no_outcome_pool_size = outcome_token_pool[market.get_outcome_str_from_bool(False)]
|
110
|
-
market_const = yes_outcome_pool_size * no_outcome_pool_size
|
115
|
+
market_const = yes_outcome_pool_size.value * no_outcome_pool_size.value
|
111
116
|
|
112
|
-
bet_to_check_size_after_fees = market.fees.
|
113
|
-
bet_to_check.size
|
114
|
-
)
|
117
|
+
bet_to_check_size_after_fees = market.fees.get_after_fees(bet_to_check.size).value
|
115
118
|
|
116
119
|
# When you buy 'yes' tokens, you add your bet size to the both pools, then
|
117
120
|
# subtract `buy_amount` from the 'yes' pool. And vice versa for 'no' tokens.
|
118
121
|
new_yes_outcome_pool_size = (
|
119
|
-
yes_outcome_pool_size
|
122
|
+
yes_outcome_pool_size.value
|
120
123
|
+ bet_to_check_size_after_fees
|
121
|
-
- float(bet_to_check.direction) * buy_amount
|
124
|
+
- float(bet_to_check.direction) * buy_amount.value
|
122
125
|
)
|
123
126
|
new_no_outcome_pool_size = (
|
124
|
-
no_outcome_pool_size
|
127
|
+
no_outcome_pool_size.value
|
125
128
|
+ bet_to_check_size_after_fees
|
126
|
-
- float(not bet_to_check.direction) * buy_amount
|
129
|
+
- float(not bet_to_check.direction) * buy_amount.value
|
127
130
|
)
|
128
131
|
new_market_const = new_yes_outcome_pool_size * new_no_outcome_pool_size
|
129
132
|
# Check the invariant is restored
|
@@ -15,6 +15,7 @@ from prediction_market_agent_tooling.gtypes import (
|
|
15
15
|
ChainID,
|
16
16
|
ChecksumAddress,
|
17
17
|
Nonce,
|
18
|
+
Token,
|
18
19
|
TxParams,
|
19
20
|
TxReceipt,
|
20
21
|
Wei,
|
@@ -153,7 +154,7 @@ class ContractBaseClass(BaseModel):
|
|
153
154
|
api_keys=api_keys,
|
154
155
|
function_name=function_name,
|
155
156
|
function_params=function_params,
|
156
|
-
tx_params={"value": amount_wei, **(tx_params or {})},
|
157
|
+
tx_params={"value": amount_wei.value, **(tx_params or {})},
|
157
158
|
timeout=timeout,
|
158
159
|
web3=web3,
|
159
160
|
)
|
@@ -243,12 +244,13 @@ class ContractERC20BaseClass(ContractBaseClass):
|
|
243
244
|
)
|
244
245
|
|
245
246
|
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
|
246
|
-
balance
|
247
|
+
balance = Wei(self.call("balanceOf", [for_address], web3=web3))
|
247
248
|
return balance
|
248
249
|
|
249
|
-
def
|
250
|
-
|
251
|
-
|
250
|
+
def balance_of_in_tokens(
|
251
|
+
self, for_address: ChecksumAddress, web3: Web3 | None = None
|
252
|
+
) -> Token:
|
253
|
+
return self.balanceOf(for_address, web3=web3).as_token
|
252
254
|
|
253
255
|
|
254
256
|
class ContractDepositableWrapperERC20BaseClass(ContractERC20BaseClass):
|
@@ -355,11 +357,11 @@ class ContractERC4626BaseClass(ContractERC20BaseClass):
|
|
355
357
|
return self.withdraw(api_keys=api_keys, assets_wei=assets, web3=web3)
|
356
358
|
|
357
359
|
def convertToShares(self, assets: Wei, web3: Web3 | None = None) -> Wei:
|
358
|
-
shares
|
360
|
+
shares = Wei(self.call("convertToShares", [assets], web3=web3))
|
359
361
|
return shares
|
360
362
|
|
361
363
|
def convertToAssets(self, shares: Wei, web3: Web3 | None = None) -> Wei:
|
362
|
-
assets
|
364
|
+
assets = Wei(self.call("convertToAssets", [shares], web3=web3))
|
363
365
|
return assets
|
364
366
|
|
365
367
|
def get_asset_token_contract(
|
@@ -434,8 +436,8 @@ class ContractERC721BaseClass(ContractBaseClass):
|
|
434
436
|
web3=web3,
|
435
437
|
)
|
436
438
|
|
437
|
-
def balanceOf(self, owner: ChecksumAddress, web3: Web3 | None = None) ->
|
438
|
-
balance
|
439
|
+
def balanceOf(self, owner: ChecksumAddress, web3: Web3 | None = None) -> Wei:
|
440
|
+
balance = Wei(self.call("balanceOf", [owner], web3=web3))
|
439
441
|
return balance
|
440
442
|
|
441
443
|
def owner_of(self, token_id: int, web3: Web3 | None = None) -> ChecksumAddress:
|
@@ -604,12 +606,14 @@ def contract_implements_function(
|
|
604
606
|
|
605
607
|
|
606
608
|
def init_collateral_token_contract(
|
607
|
-
address: ChecksumAddress, web3: Web3
|
609
|
+
address: ChecksumAddress, web3: Web3 | None
|
608
610
|
) -> ContractERC20BaseClass:
|
609
611
|
"""
|
610
612
|
Checks if the given contract is Depositable ERC-20, ERC-20 or ERC-4626 and returns the appropriate class instance.
|
611
613
|
Throws an error if the contract is neither of them.
|
612
614
|
"""
|
615
|
+
web3 = web3 or RPCConfig().get_web3()
|
616
|
+
|
613
617
|
if contract_implements_function(address, "asset", web3=web3):
|
614
618
|
return ContractERC4626BaseClass(address=address)
|
615
619
|
|
@@ -19,10 +19,9 @@ from web3 import Web3
|
|
19
19
|
from web3.constants import ADDRESS_ZERO
|
20
20
|
|
21
21
|
from prediction_market_agent_tooling.config import APIKeys
|
22
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress,
|
22
|
+
from prediction_market_agent_tooling.gtypes import ChecksumAddress, Token, Wei
|
23
23
|
from prediction_market_agent_tooling.loggers import logger
|
24
24
|
from prediction_market_agent_tooling.tools.cow.cow_order import swap_tokens_waiting
|
25
|
-
from prediction_market_agent_tooling.tools.web3_utils import xdai_to_wei
|
26
25
|
|
27
26
|
COW_ENV: Envs = "prod"
|
28
27
|
|
@@ -94,14 +93,14 @@ class CowManager:
|
|
94
93
|
|
95
94
|
@staticmethod
|
96
95
|
def swap(
|
97
|
-
amount:
|
96
|
+
amount: Token,
|
98
97
|
sell_token: ChecksumAddress,
|
99
98
|
buy_token: ChecksumAddress,
|
100
99
|
api_keys: APIKeys,
|
101
100
|
web3: Web3 | None = None,
|
102
101
|
) -> OrderMetaData:
|
103
102
|
order_metadata = swap_tokens_waiting(
|
104
|
-
amount_wei=
|
103
|
+
amount_wei=amount.as_wei,
|
105
104
|
sell_token=sell_token,
|
106
105
|
buy_token=buy_token,
|
107
106
|
api_keys=api_keys,
|
@@ -21,10 +21,9 @@ from cowdao_cowpy.order_book.generated.model import (
|
|
21
21
|
from eth_account.signers.local import LocalAccount
|
22
22
|
from eth_typing.evm import ChecksumAddress
|
23
23
|
from web3 import Web3
|
24
|
-
from web3.types import Wei
|
25
24
|
|
26
25
|
from prediction_market_agent_tooling.config import APIKeys
|
27
|
-
from prediction_market_agent_tooling.gtypes import ChecksumAddress, Wei
|
26
|
+
from prediction_market_agent_tooling.gtypes import ChecksumAddress, Wei
|
28
27
|
from prediction_market_agent_tooling.loggers import logger
|
29
28
|
from prediction_market_agent_tooling.tools.contract import ContractERC20OnGnosisChain
|
30
29
|
from prediction_market_agent_tooling.tools.utils import utcnow
|
@@ -62,7 +61,7 @@ def get_buy_token_amount(
|
|
62
61
|
order_quote = asyncio.run(
|
63
62
|
order_book_api.post_quote(order_quote_request, order_side)
|
64
63
|
)
|
65
|
-
return
|
64
|
+
return Wei(order_quote.quote.buyAmount.root)
|
66
65
|
|
67
66
|
|
68
67
|
def swap_tokens_waiting(
|
@@ -102,7 +101,7 @@ async def swap_tokens_waiting_async(
|
|
102
101
|
timeout: timedelta = timedelta(seconds=60),
|
103
102
|
) -> OrderMetaData:
|
104
103
|
order = await swap_tokens(
|
105
|
-
amount=amount_wei,
|
104
|
+
amount=amount_wei.value,
|
106
105
|
sell_token=sell_token,
|
107
106
|
buy_token=buy_token,
|
108
107
|
account=account,
|
@@ -13,6 +13,9 @@ from prediction_market_agent_tooling.markets.data_models import (
|
|
13
13
|
TradeType,
|
14
14
|
)
|
15
15
|
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
|
16
|
+
from prediction_market_agent_tooling.markets.omen.omen_constants import (
|
17
|
+
WRAPPED_XDAI_CONTRACT_ADDRESS,
|
18
|
+
)
|
16
19
|
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
17
20
|
|
18
21
|
|
@@ -147,11 +150,20 @@ def get_trace_for_bet(
|
|
147
150
|
# Filter for traces with the same bet outcome and amount
|
148
151
|
traces_for_bet: list[ProcessMarketTrace] = []
|
149
152
|
for t in traces:
|
153
|
+
if (
|
154
|
+
t.market.collateral_token_contract_address_checksummed
|
155
|
+
not in WRAPPED_XDAI_CONTRACT_ADDRESS
|
156
|
+
):
|
157
|
+
# TODO: We need to compute bet amount token in USD here, but at the time of bet placement!
|
158
|
+
logger.warning(
|
159
|
+
"This currently works only for WXDAI markets, because we need to compare against USD value."
|
160
|
+
)
|
161
|
+
continue
|
150
162
|
# Cannot use exact comparison due to gas fees
|
151
163
|
if (
|
152
164
|
t.buy_trade
|
153
165
|
and t.buy_trade.outcome == bet.outcome
|
154
|
-
and np.isclose(t.buy_trade.amount.
|
166
|
+
and np.isclose(t.buy_trade.amount.value, bet.amount.value)
|
155
167
|
):
|
156
168
|
traces_for_bet.append(t)
|
157
169
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from datetime import timedelta
|
2
2
|
|
3
3
|
from prediction_market_agent_tooling.config import APIKeys
|
4
|
+
from prediction_market_agent_tooling.gtypes import USD
|
4
5
|
from prediction_market_agent_tooling.loggers import logger
|
5
6
|
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
|
6
7
|
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
@@ -23,7 +24,9 @@ def sell_all(
|
|
23
24
|
better_address=better_address,
|
24
25
|
market_opening_after=utcnow() + timedelta(days=closing_later_than_days),
|
25
26
|
)
|
26
|
-
bets_total_usd = sum(
|
27
|
+
bets_total_usd = sum(
|
28
|
+
(b.get_collateral_amount_usd() for b in bets), start=USD.zero()
|
29
|
+
)
|
27
30
|
unique_market_urls = set(b.fpmm.url for b in bets)
|
28
31
|
starting_balance = get_balances(better_address)
|
29
32
|
new_balance = starting_balance
|
@@ -37,9 +40,9 @@ def sell_all(
|
|
37
40
|
outcome = agent_market.outcomes[bet.outcomeIndex]
|
38
41
|
current_token_balance = agent_market.get_token_balance(better_address, outcome)
|
39
42
|
|
40
|
-
if current_token_balance.
|
43
|
+
if current_token_balance.as_token <= agent_market.get_tiny_bet_amount():
|
41
44
|
logger.info(
|
42
|
-
f"Skipping bet on {bet.fpmm.url} because the actual balance is unreasonably low {current_token_balance
|
45
|
+
f"Skipping bet on {bet.fpmm.url} because the actual balance is unreasonably low {current_token_balance}."
|
43
46
|
)
|
44
47
|
continue
|
45
48
|
|
@@ -11,11 +11,10 @@ from safe_eth.eth.constants import NULL_ADDRESS
|
|
11
11
|
from safe_eth.eth.contracts import get_safe_V1_4_1_contract
|
12
12
|
from safe_eth.safe.proxy_factory import ProxyFactoryV141
|
13
13
|
from safe_eth.safe.safe import SafeV141
|
14
|
-
from web3.types import Wei
|
15
14
|
|
15
|
+
from prediction_market_agent_tooling.gtypes import Wei
|
16
16
|
from prediction_market_agent_tooling.loggers import logger
|
17
17
|
from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
|
18
|
-
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
19
18
|
|
20
19
|
|
21
20
|
def create_safe(
|
@@ -63,10 +62,10 @@ def create_safe(
|
|
63
62
|
f"does not exist on network {ethereum_network.name}"
|
64
63
|
)
|
65
64
|
|
66
|
-
account_balance = ethereum_client.get_balance(account.address)
|
67
|
-
account_balance_xdai =
|
65
|
+
account_balance = Wei(ethereum_client.get_balance(account.address))
|
66
|
+
account_balance_xdai = account_balance.as_token
|
68
67
|
# We set a reasonable expected balance below for Safe deployment not to fail.
|
69
|
-
if account_balance_xdai < 0.01:
|
68
|
+
if account_balance_xdai.value < 0.01:
|
70
69
|
raise ValueError(
|
71
70
|
f"Client's balance is {account_balance_xdai} xDAI, too low for deploying a Safe."
|
72
71
|
)
|
@@ -108,7 +107,7 @@ def create_safe(
|
|
108
107
|
payment_token,
|
109
108
|
payment,
|
110
109
|
payment_receiver,
|
111
|
-
).build_transaction({"gas": 1, "gasPrice": Wei(1)})["data"]
|
110
|
+
).build_transaction({"gas": 1, "gasPrice": Wei(1).value})["data"]
|
112
111
|
)
|
113
112
|
|
114
113
|
proxy_factory = ProxyFactoryV141(proxy_factory_address, ethereum_client)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from web3 import Web3
|
2
2
|
|
3
3
|
from prediction_market_agent_tooling.config import APIKeys
|
4
|
-
from prediction_market_agent_tooling.gtypes import
|
4
|
+
from prediction_market_agent_tooling.gtypes import USD, Wei
|
5
5
|
from prediction_market_agent_tooling.loggers import logger
|
6
6
|
from prediction_market_agent_tooling.tools.contract import (
|
7
7
|
ContractDepositableWrapperERC20BaseClass,
|
@@ -9,32 +9,44 @@ from prediction_market_agent_tooling.tools.contract import (
|
|
9
9
|
ContractERC20OnGnosisChain,
|
10
10
|
ContractERC4626BaseClass,
|
11
11
|
)
|
12
|
-
from prediction_market_agent_tooling.tools.cow.cow_order import
|
13
|
-
get_buy_token_amount,
|
14
|
-
swap_tokens_waiting,
|
15
|
-
)
|
12
|
+
from prediction_market_agent_tooling.tools.cow.cow_order import swap_tokens_waiting
|
16
13
|
from prediction_market_agent_tooling.tools.tokens.main_token import KEEPING_ERC20_TOKEN
|
14
|
+
from prediction_market_agent_tooling.tools.tokens.usd import get_usd_in_token
|
17
15
|
from prediction_market_agent_tooling.tools.utils import should_not_happen
|
18
|
-
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
19
16
|
|
20
17
|
|
21
18
|
def auto_deposit_collateral_token(
|
22
19
|
collateral_token_contract: ContractERC20BaseClass,
|
23
|
-
|
20
|
+
collateral_amount_wei_or_usd: Wei | USD,
|
24
21
|
api_keys: APIKeys,
|
25
22
|
web3: Web3 | None = None,
|
23
|
+
slippage: float = 0.01,
|
26
24
|
) -> None:
|
25
|
+
collateral_amount_wei = (
|
26
|
+
collateral_amount_wei_or_usd
|
27
|
+
if isinstance(collateral_amount_wei_or_usd, Wei)
|
28
|
+
else get_usd_in_token(
|
29
|
+
collateral_amount_wei_or_usd, collateral_token_contract.address
|
30
|
+
).as_wei
|
31
|
+
)
|
32
|
+
# Deposit a bit more, to cover any later changes in the price.
|
33
|
+
collateral_amount_wei = collateral_amount_wei.with_fraction(slippage)
|
34
|
+
|
27
35
|
if isinstance(collateral_token_contract, ContractDepositableWrapperERC20BaseClass):
|
28
36
|
# In this case, we can use deposit function directly, no need to go through DEX.
|
29
37
|
auto_deposit_depositable_wrapper_erc20(
|
30
|
-
collateral_token_contract,
|
38
|
+
collateral_token_contract, collateral_amount_wei, api_keys, web3
|
31
39
|
)
|
32
40
|
|
33
41
|
elif isinstance(collateral_token_contract, ContractERC4626BaseClass):
|
34
|
-
auto_deposit_erc4626(
|
42
|
+
auto_deposit_erc4626(
|
43
|
+
collateral_token_contract, collateral_amount_wei, api_keys, web3
|
44
|
+
)
|
35
45
|
|
36
46
|
elif isinstance(collateral_token_contract, ContractERC20BaseClass):
|
37
|
-
auto_deposit_erc20(
|
47
|
+
auto_deposit_erc20(
|
48
|
+
collateral_token_contract, collateral_amount_wei, api_keys, web3
|
49
|
+
)
|
38
50
|
|
39
51
|
else:
|
40
52
|
should_not_happen("Unsupported ERC20 contract type.")
|
@@ -55,16 +67,16 @@ def auto_deposit_depositable_wrapper_erc20(
|
|
55
67
|
return
|
56
68
|
|
57
69
|
# If we don't have enough, we need to deposit the difference.
|
58
|
-
left_to_deposit =
|
70
|
+
left_to_deposit = amount_wei - collateral_token_balance
|
59
71
|
logger.info(
|
60
|
-
f"Depositing {
|
72
|
+
f"Depositing {left_to_deposit.as_token} {collateral_token_contract.symbol()}."
|
61
73
|
)
|
62
74
|
collateral_token_contract.deposit(api_keys, left_to_deposit, web3=web3)
|
63
75
|
|
64
76
|
|
65
77
|
def auto_deposit_erc4626(
|
66
78
|
collateral_token_contract: ContractERC4626BaseClass,
|
67
|
-
|
79
|
+
collateral_amount_wei: Wei,
|
68
80
|
api_keys: APIKeys,
|
69
81
|
web3: Web3 | None,
|
70
82
|
) -> None:
|
@@ -72,12 +84,12 @@ def auto_deposit_erc4626(
|
|
72
84
|
collateral_token_balance_in_shares = collateral_token_contract.balanceOf(
|
73
85
|
for_address=for_address, web3=web3
|
74
86
|
)
|
75
|
-
|
76
|
-
|
87
|
+
asset_amount_wei = collateral_token_contract.convertToAssets(
|
88
|
+
collateral_amount_wei, web3
|
77
89
|
)
|
78
90
|
|
79
91
|
# If we have enough shares, we don't need to deposit.
|
80
|
-
if collateral_token_balance_in_shares >=
|
92
|
+
if collateral_token_balance_in_shares >= collateral_amount_wei:
|
81
93
|
return
|
82
94
|
|
83
95
|
# If we need to deposit into erc4626, we first need to have enough of the asset token.
|
@@ -89,7 +101,7 @@ def auto_deposit_erc4626(
|
|
89
101
|
collateral_token_balance_in_assets = collateral_token_contract.convertToAssets(
|
90
102
|
collateral_token_balance_in_shares, web3
|
91
103
|
)
|
92
|
-
left_to_deposit =
|
104
|
+
left_to_deposit = asset_amount_wei - collateral_token_balance_in_assets
|
93
105
|
if (
|
94
106
|
collateral_token_contract.get_asset_token_balance(for_address, web3)
|
95
107
|
< left_to_deposit
|
@@ -108,32 +120,22 @@ def auto_deposit_erc4626(
|
|
108
120
|
|
109
121
|
def auto_deposit_erc20(
|
110
122
|
collateral_token_contract: ContractERC20BaseClass,
|
111
|
-
|
123
|
+
collateral_amount_wei: Wei,
|
112
124
|
api_keys: APIKeys,
|
113
125
|
web3: Web3 | None,
|
114
126
|
) -> None:
|
115
|
-
# How much it is in the other token (collateral token).
|
116
|
-
collateral_amount_wei = get_buy_token_amount(
|
117
|
-
amount_xdai_wei,
|
118
|
-
KEEPING_ERC20_TOKEN.address,
|
119
|
-
collateral_token_contract.address,
|
120
|
-
)
|
121
127
|
# How much do we have already in the other token (collateral token).
|
122
128
|
collateral_balance_wei = collateral_token_contract.balanceOf(
|
123
129
|
api_keys.bet_from_address
|
124
130
|
)
|
125
131
|
# Amount of collateral token remaining to get.
|
126
132
|
remaining_to_get_in_collateral_wei = max(
|
127
|
-
0, collateral_amount_wei - collateral_balance_wei
|
133
|
+
Wei(0), collateral_amount_wei - collateral_balance_wei
|
128
134
|
)
|
129
135
|
if not remaining_to_get_in_collateral_wei:
|
130
136
|
return
|
131
137
|
# Estimate of how much of the source token we need to sell in order to fill the remaining collateral amount, with 1% slippage to be sure.
|
132
|
-
amount_to_sell_wei =
|
133
|
-
(remaining_to_get_in_collateral_wei * amount_xdai_wei)
|
134
|
-
/ collateral_amount_wei
|
135
|
-
* 1.01
|
136
|
-
)
|
138
|
+
amount_to_sell_wei = remaining_to_get_in_collateral_wei * 1.01
|
137
139
|
# If we don't have enough of the source token.
|
138
140
|
if amount_to_sell_wei > ContractERC20OnGnosisChain(
|
139
141
|
address=KEEPING_ERC20_TOKEN.address
|
@@ -7,16 +7,9 @@ from prediction_market_agent_tooling.tools.contract import (
|
|
7
7
|
ContractERC20BaseClass,
|
8
8
|
ContractERC4626BaseClass,
|
9
9
|
)
|
10
|
-
from prediction_market_agent_tooling.tools.cow.cow_order import
|
11
|
-
get_buy_token_amount,
|
12
|
-
swap_tokens_waiting,
|
13
|
-
)
|
10
|
+
from prediction_market_agent_tooling.tools.cow.cow_order import swap_tokens_waiting
|
14
11
|
from prediction_market_agent_tooling.tools.tokens.main_token import KEEPING_ERC20_TOKEN
|
15
12
|
from prediction_market_agent_tooling.tools.utils import should_not_happen
|
16
|
-
from prediction_market_agent_tooling.tools.web3_utils import (
|
17
|
-
remove_fraction,
|
18
|
-
wei_to_xdai,
|
19
|
-
)
|
20
13
|
|
21
14
|
|
22
15
|
def auto_withdraw_collateral_token(
|
@@ -27,10 +20,7 @@ def auto_withdraw_collateral_token(
|
|
27
20
|
slippage: float = 0.001,
|
28
21
|
) -> None:
|
29
22
|
# Small slippage as exact exchange rate constantly changes and we don't care about small differences.
|
30
|
-
amount_wei =
|
31
|
-
amount_wei,
|
32
|
-
slippage,
|
33
|
-
)
|
23
|
+
amount_wei = amount_wei.without_fraction(slippage)
|
34
24
|
|
35
25
|
if not amount_wei:
|
36
26
|
logger.warning(
|
@@ -51,7 +41,7 @@ def auto_withdraw_collateral_token(
|
|
51
41
|
):
|
52
42
|
# If the ERC4626 is backed by KEEPING_ERC20_TOKEN, we can withdraw it directly, no need to go through DEX.
|
53
43
|
logger.info(
|
54
|
-
f"Withdrawing {
|
44
|
+
f"Withdrawing {amount_wei.as_token} from {collateral_token_contract.symbol_cached(web3)} into {KEEPING_ERC20_TOKEN.symbol_cached(web3)}"
|
55
45
|
)
|
56
46
|
collateral_token_contract.withdraw_in_shares(
|
57
47
|
api_keys,
|
@@ -60,18 +50,11 @@ def auto_withdraw_collateral_token(
|
|
60
50
|
)
|
61
51
|
elif isinstance(collateral_token_contract, ContractERC20BaseClass):
|
62
52
|
logger.info(
|
63
|
-
f"Swapping {
|
53
|
+
f"Swapping {amount_wei.as_token} from {collateral_token_contract.symbol_cached(web3)} into {KEEPING_ERC20_TOKEN.symbol_cached(web3)}"
|
64
54
|
)
|
65
55
|
# Otherwise, DEX will handle the rest of token swaps.
|
66
|
-
# First, convert `amount_wei` from xDai-based value into the collateral token-based value.
|
67
|
-
collateral_amount_wei = get_buy_token_amount(
|
68
|
-
amount_wei,
|
69
|
-
KEEPING_ERC20_TOKEN.address,
|
70
|
-
collateral_token_contract.address,
|
71
|
-
)
|
72
|
-
# And then sell it.
|
73
56
|
swap_tokens_waiting(
|
74
|
-
amount_wei=
|
57
|
+
amount_wei=amount_wei,
|
75
58
|
sell_token=collateral_token_contract.address,
|
76
59
|
buy_token=KEEPING_ERC20_TOKEN.address,
|
77
60
|
api_keys=api_keys,
|