prediction-market-agent-tooling 0.45.1__py3-none-any.whl → 0.47.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prediction_market_agent_tooling/deploy/agent.py +23 -4
- prediction_market_agent_tooling/markets/omen/data_models.py +1 -0
- prediction_market_agent_tooling/markets/omen/omen.py +14 -0
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +47 -9
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +48 -3
- {prediction_market_agent_tooling-0.45.1.dist-info → prediction_market_agent_tooling-0.47.0.dist-info}/METADATA +1 -1
- {prediction_market_agent_tooling-0.45.1.dist-info → prediction_market_agent_tooling-0.47.0.dist-info}/RECORD +10 -10
- {prediction_market_agent_tooling-0.45.1.dist-info → prediction_market_agent_tooling-0.47.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.45.1.dist-info → prediction_market_agent_tooling-0.47.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.45.1.dist-info → prediction_market_agent_tooling-0.47.0.dist-info}/entry_points.txt +0 -0
@@ -22,7 +22,7 @@ from prediction_market_agent_tooling.deploy.gcp.utils import (
|
|
22
22
|
gcp_function_is_active,
|
23
23
|
gcp_resolve_api_keys_secrets,
|
24
24
|
)
|
25
|
-
from prediction_market_agent_tooling.gtypes import Probability, xdai_type
|
25
|
+
from prediction_market_agent_tooling.gtypes import Probability, xDai, xdai_type
|
26
26
|
from prediction_market_agent_tooling.loggers import logger
|
27
27
|
from prediction_market_agent_tooling.markets.agent_market import (
|
28
28
|
AgentMarket,
|
@@ -35,6 +35,7 @@ from prediction_market_agent_tooling.markets.markets import (
|
|
35
35
|
have_bet_on_market_since,
|
36
36
|
)
|
37
37
|
from prediction_market_agent_tooling.markets.omen.omen import (
|
38
|
+
is_minimum_required_balance,
|
38
39
|
redeem_from_all_user_positions,
|
39
40
|
withdraw_wxdai_to_xdai_to_keep_balance,
|
40
41
|
)
|
@@ -73,6 +74,10 @@ def to_boolean_outcome(value: str | bool) -> bool:
|
|
73
74
|
Decision = Annotated[bool, BeforeValidator(to_boolean_outcome)]
|
74
75
|
|
75
76
|
|
77
|
+
class OutOfFundsError(ValueError):
|
78
|
+
pass
|
79
|
+
|
80
|
+
|
76
81
|
class Answer(BaseModel):
|
77
82
|
decision: Decision # Warning: p_yes > 0.5 doesn't necessarily mean decision is True! For example, if our p_yes is 55%, but market's p_yes is 80%, then it might be profitable to bet on False.
|
78
83
|
p_yes: Probability
|
@@ -201,6 +206,8 @@ def {entrypoint_function_name}(request) -> str:
|
|
201
206
|
|
202
207
|
class DeployableTraderAgent(DeployableAgent):
|
203
208
|
bet_on_n_markets_per_run: int = 1
|
209
|
+
min_required_balance_to_operate: xDai | None = xdai_type(1)
|
210
|
+
min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
|
204
211
|
|
205
212
|
def __init__(self, place_bet: bool = True) -> None:
|
206
213
|
super().__init__()
|
@@ -273,10 +280,22 @@ class DeployableTraderAgent(DeployableAgent):
|
|
273
280
|
if market_type == MarketType.OMEN:
|
274
281
|
# Omen is specific, because the user (agent) needs to manually withdraw winnings from the market.
|
275
282
|
redeem_from_all_user_positions(api_keys)
|
283
|
+
# Check if we have enough of balance to operate.
|
284
|
+
if self.min_required_balance_to_operate is not None:
|
285
|
+
if not is_minimum_required_balance(
|
286
|
+
api_keys.public_key,
|
287
|
+
min_required_balance=self.min_required_balance_to_operate,
|
288
|
+
):
|
289
|
+
raise OutOfFundsError(
|
290
|
+
f"Minimum required balance {self.min_required_balance_to_operate} is not met."
|
291
|
+
)
|
276
292
|
# Exchange wxdai back to xdai if the balance is getting low, so we can keep paying for fees.
|
277
|
-
|
278
|
-
|
279
|
-
|
293
|
+
if self.min_balance_to_keep_in_native_currency is not None:
|
294
|
+
withdraw_wxdai_to_xdai_to_keep_balance(
|
295
|
+
api_keys,
|
296
|
+
min_required_balance=self.min_balance_to_keep_in_native_currency,
|
297
|
+
withdraw_multiplier=2,
|
298
|
+
)
|
280
299
|
|
281
300
|
def process_bets(self, market_type: MarketType) -> None:
|
282
301
|
"""
|
@@ -31,6 +31,7 @@ from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
|
31
31
|
OMEN_TRUE_OUTCOME = "Yes"
|
32
32
|
OMEN_FALSE_OUTCOME = "No"
|
33
33
|
INVALID_ANSWER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
34
|
+
INVALID_ANSWER_HEX_BYTES = HexBytes(INVALID_ANSWER)
|
34
35
|
OMEN_BASE_URL = "https://aiomen.eth.limo"
|
35
36
|
PRESAGIO_BASE_URL = "https://presagio.pages.dev"
|
36
37
|
|
@@ -1043,6 +1043,20 @@ def get_binary_market_p_yes_history(market: OmenAgentMarket) -> list[Probability
|
|
1043
1043
|
return history
|
1044
1044
|
|
1045
1045
|
|
1046
|
+
def is_minimum_required_balance(
|
1047
|
+
address: ChecksumAddress,
|
1048
|
+
min_required_balance: xDai,
|
1049
|
+
web3: Web3 | None = None,
|
1050
|
+
) -> bool:
|
1051
|
+
"""
|
1052
|
+
Checks if the total balance of xDai and wxDai in the wallet is above the minimum required balance.
|
1053
|
+
"""
|
1054
|
+
current_balances = get_balances(address, web3)
|
1055
|
+
# xDai and wxDai have equal value and can be exchanged for almost no cost, so we can sum them up.
|
1056
|
+
total_balance = current_balances.xdai + current_balances.wxdai
|
1057
|
+
return total_balance >= min_required_balance
|
1058
|
+
|
1059
|
+
|
1046
1060
|
def withdraw_wxdai_to_xdai_to_keep_balance(
|
1047
1061
|
api_keys: APIKeys,
|
1048
1062
|
min_required_balance: xDai,
|
@@ -22,6 +22,9 @@ from prediction_market_agent_tooling.gtypes import (
|
|
22
22
|
wei_type,
|
23
23
|
xdai_type,
|
24
24
|
)
|
25
|
+
from prediction_market_agent_tooling.markets.omen.data_models import (
|
26
|
+
INVALID_ANSWER_HEX_BYTES,
|
27
|
+
)
|
25
28
|
from prediction_market_agent_tooling.tools.contract import (
|
26
29
|
ContractDepositableWrapperERC20OnGnosisChain,
|
27
30
|
ContractERC20OnGnosisChain,
|
@@ -562,8 +565,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
562
565
|
self,
|
563
566
|
api_keys: APIKeys,
|
564
567
|
question_id: HexBytes,
|
565
|
-
answer:
|
566
|
-
outcomes: list[str],
|
568
|
+
answer: HexBytes,
|
567
569
|
bond: Wei,
|
568
570
|
max_previous: Wei | None = None,
|
569
571
|
web3: Web3 | None = None,
|
@@ -573,24 +575,60 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
573
575
|
# same as on Omen website: https://github.com/protofire/omen-exchange/blob/763d9c9d05ebf9edacbc1dbaa561aa5d08813c0f/app/src/services/realitio.ts#L363.
|
574
576
|
max_previous = Wei(0)
|
575
577
|
|
576
|
-
# Normalise the answer to lowercase, to match Enum values as [YES, NO] against outcomes as ["Yes", "No"].
|
577
|
-
answer = answer.lower()
|
578
|
-
outcomes = [o.lower() for o in outcomes]
|
579
|
-
|
580
578
|
return self.send_with_value(
|
581
579
|
api_keys=api_keys,
|
582
580
|
function_name="submitAnswer",
|
583
581
|
function_params=dict(
|
584
582
|
question_id=question_id,
|
585
|
-
answer=
|
586
|
-
outcomes.index(answer)
|
587
|
-
), # Contract's method expects answer index in bytes.
|
583
|
+
answer=answer,
|
588
584
|
max_previous=max_previous,
|
589
585
|
),
|
590
586
|
amount_wei=bond,
|
591
587
|
web3=web3,
|
592
588
|
)
|
593
589
|
|
590
|
+
def submit_answer(
|
591
|
+
self,
|
592
|
+
api_keys: APIKeys,
|
593
|
+
question_id: HexBytes,
|
594
|
+
answer: str,
|
595
|
+
outcomes: list[str],
|
596
|
+
bond: Wei,
|
597
|
+
max_previous: Wei | None = None,
|
598
|
+
web3: Web3 | None = None,
|
599
|
+
) -> TxReceipt:
|
600
|
+
# Normalise the answer to lowercase, to match Enum values as [YES, NO] against outcomes as ["Yes", "No"].
|
601
|
+
answer = answer.lower()
|
602
|
+
outcomes = [o.lower() for o in outcomes]
|
603
|
+
|
604
|
+
return self.submitAnswer(
|
605
|
+
api_keys=api_keys,
|
606
|
+
question_id=question_id,
|
607
|
+
answer=int_to_hexbytes(
|
608
|
+
outcomes.index(answer)
|
609
|
+
), # Contract's method expects answer index in bytes.
|
610
|
+
bond=bond,
|
611
|
+
max_previous=max_previous,
|
612
|
+
web3=web3,
|
613
|
+
)
|
614
|
+
|
615
|
+
def submit_answer_invalid(
|
616
|
+
self,
|
617
|
+
api_keys: APIKeys,
|
618
|
+
question_id: HexBytes,
|
619
|
+
bond: Wei,
|
620
|
+
max_previous: Wei | None = None,
|
621
|
+
web3: Web3 | None = None,
|
622
|
+
) -> TxReceipt:
|
623
|
+
return self.submitAnswer(
|
624
|
+
api_keys=api_keys,
|
625
|
+
question_id=question_id,
|
626
|
+
answer=INVALID_ANSWER_HEX_BYTES,
|
627
|
+
bond=bond,
|
628
|
+
max_previous=max_previous,
|
629
|
+
web3=web3,
|
630
|
+
)
|
631
|
+
|
594
632
|
def claimWinnings(
|
595
633
|
self,
|
596
634
|
api_keys: APIKeys,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
|
1
3
|
from web3 import Web3
|
2
4
|
|
3
5
|
from prediction_market_agent_tooling.config import APIKeys
|
@@ -140,6 +142,7 @@ def claim_bonds_on_realitio_question(
|
|
140
142
|
def finalize_markets(
|
141
143
|
api_keys: APIKeys,
|
142
144
|
markets_with_resolutions: list[tuple[OmenMarket, Resolution | None]],
|
145
|
+
wait_n_days_before_invalid: int = 30,
|
143
146
|
web3: Web3 | None = None,
|
144
147
|
) -> list[HexAddress]:
|
145
148
|
finalized_markets: list[HexAddress] = []
|
@@ -148,9 +151,24 @@ def finalize_markets(
|
|
148
151
|
logger.info(
|
149
152
|
f"[{idx+1} / {len(markets_with_resolutions)}] Looking into {market.url=} {market.question_title=}"
|
150
153
|
)
|
154
|
+
closed_before_days = (datetime.now() - market.close_time).days
|
151
155
|
|
152
156
|
if resolution is None:
|
153
|
-
|
157
|
+
if closed_before_days > wait_n_days_before_invalid:
|
158
|
+
logger.warning(
|
159
|
+
f"Finalizing as invalid, market closed before {closed_before_days} days: {market.url=}"
|
160
|
+
)
|
161
|
+
omen_submit_invalid_answer_market_tx(
|
162
|
+
api_keys,
|
163
|
+
market,
|
164
|
+
OMEN_DEFAULT_REALITIO_BOND_VALUE,
|
165
|
+
web3=web3,
|
166
|
+
)
|
167
|
+
|
168
|
+
else:
|
169
|
+
logger.warning(
|
170
|
+
f"Skipping, no resolution provided, market closed before {closed_before_days} days: {market.url=}"
|
171
|
+
)
|
154
172
|
|
155
173
|
elif resolution in (Resolution.YES, Resolution.NO):
|
156
174
|
logger.info(f"Found resolution {resolution.value=} for {market.url=}")
|
@@ -165,7 +183,15 @@ def finalize_markets(
|
|
165
183
|
logger.info(f"Finalized {market.url=}")
|
166
184
|
|
167
185
|
else:
|
168
|
-
logger.
|
186
|
+
logger.warning(
|
187
|
+
f"Invalid resolution found, {resolution=}, for {market.url=}, finalizing as invalid."
|
188
|
+
)
|
189
|
+
omen_submit_invalid_answer_market_tx(
|
190
|
+
api_keys,
|
191
|
+
market,
|
192
|
+
OMEN_DEFAULT_REALITIO_BOND_VALUE,
|
193
|
+
web3=web3,
|
194
|
+
)
|
169
195
|
|
170
196
|
return finalized_markets
|
171
197
|
|
@@ -199,7 +225,7 @@ def omen_submit_answer_market_tx(
|
|
199
225
|
And after the period is over, you need to resolve the market using `omen_resolve_market_tx`.
|
200
226
|
"""
|
201
227
|
realitio_contract = OmenRealitioContract()
|
202
|
-
realitio_contract.
|
228
|
+
realitio_contract.submit_answer(
|
203
229
|
api_keys=api_keys,
|
204
230
|
question_id=market.question.id,
|
205
231
|
answer=resolution.value,
|
@@ -209,6 +235,25 @@ def omen_submit_answer_market_tx(
|
|
209
235
|
)
|
210
236
|
|
211
237
|
|
238
|
+
def omen_submit_invalid_answer_market_tx(
|
239
|
+
api_keys: APIKeys,
|
240
|
+
market: OmenMarket,
|
241
|
+
bond: xDai,
|
242
|
+
web3: Web3 | None = None,
|
243
|
+
) -> None:
|
244
|
+
"""
|
245
|
+
After the answer is submitted, there is 24h waiting period where the answer can be challenged by others.
|
246
|
+
And after the period is over, you need to resolve the market using `omen_resolve_market_tx`.
|
247
|
+
"""
|
248
|
+
realitio_contract = OmenRealitioContract()
|
249
|
+
realitio_contract.submit_answer_invalid(
|
250
|
+
api_keys=api_keys,
|
251
|
+
question_id=market.question.id,
|
252
|
+
bond=xdai_to_wei(bond),
|
253
|
+
web3=web3,
|
254
|
+
)
|
255
|
+
|
256
|
+
|
212
257
|
def omen_resolve_market_tx(
|
213
258
|
api_keys: APIKeys,
|
214
259
|
market: OmenMarket,
|
@@ -15,7 +15,7 @@ prediction_market_agent_tooling/benchmark/agents.py,sha256=HPIFJvackW110ch3Ukktb
|
|
15
15
|
prediction_market_agent_tooling/benchmark/benchmark.py,sha256=xiHKzZx5GHSsDerFHMZ9j_LXAXnSaITSvv67iPe3MEU,21095
|
16
16
|
prediction_market_agent_tooling/benchmark/utils.py,sha256=iS1BzyXcCMfMm1Rx--1QCH0pHvBTblTndcDQFbTUJ2s,2897
|
17
17
|
prediction_market_agent_tooling/config.py,sha256=3j1iMiJWntSkqQv5HllJgihgqf9I2_0e0LPncVFRi7g,5260
|
18
|
-
prediction_market_agent_tooling/deploy/agent.py,sha256=
|
18
|
+
prediction_market_agent_tooling/deploy/agent.py,sha256=qaFbPaiVlxOV_drXUasYwuJgoA37wyMoTRA5MRPDarg,11672
|
19
19
|
prediction_market_agent_tooling/deploy/agent_example.py,sha256=V_yakxDvG3WZWRsQq6-b2ZLA8pPPnf0Jrw8iGkGZGG8,1019
|
20
20
|
prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
|
21
21
|
prediction_market_agent_tooling/deploy/gcp/deploy.py,sha256=CYUgnfy-9XVk04kkxA_5yp0GE9Mw5caYqlFUZQ2j3ks,3739
|
@@ -36,10 +36,10 @@ prediction_market_agent_tooling/markets/metaculus/api.py,sha256=gvPQVAM5NlCyWzEM
|
|
36
36
|
prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=6TBy17xntdLBR61QCE5wddwTa_k2D0D8ZgK6p7sGUuc,2448
|
37
37
|
prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=uNF7LP4evvubk818g2zbX1VlnFxeUQOkNgx_e_LwaJA,3416
|
38
38
|
prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
|
-
prediction_market_agent_tooling/markets/omen/data_models.py,sha256=
|
40
|
-
prediction_market_agent_tooling/markets/omen/omen.py,sha256=
|
41
|
-
prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=
|
42
|
-
prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=
|
39
|
+
prediction_market_agent_tooling/markets/omen/data_models.py,sha256=VCY-TD4RBCd63T1PtF5AwjPWbZ3MC02TGw_BAOEZSyc,14591
|
40
|
+
prediction_market_agent_tooling/markets/omen/omen.py,sha256=fL8Lk1H0guwNBcgvT8Ymy3jRewbWLbJ9m8br4HN7vPg,40014
|
41
|
+
prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=DWJk7GZgw_UxxFxdVeDJTcskWlJ8AchN0QaXWZrrh4E,23633
|
42
|
+
prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=8uHWH1nMYiop4wOhSSoz3tdaexa-qb6KX10SiyaYYhA,10981
|
43
43
|
prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=4F4QR8MDwyEw52rolEJNxtbCcIipX6ccNHANfWyCBjg,25945
|
44
44
|
prediction_market_agent_tooling/markets/polymarket/api.py,sha256=HXmA1akA0qDj0m3e-GEvWG8x75pm6BX4H7YJPQcST7I,4767
|
45
45
|
prediction_market_agent_tooling/markets/polymarket/data_models.py,sha256=9CJzakyEcsn6DQBK2nOXjOMzTZBLAmK_KqevXvW17DI,4292
|
@@ -75,8 +75,8 @@ prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0r
|
|
75
75
|
prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
|
76
76
|
prediction_market_agent_tooling/tools/utils.py,sha256=JE9YWtPPhnTgLiOyGAZDNG5K8nCwUY9IZEuAlm9UcxA,6611
|
77
77
|
prediction_market_agent_tooling/tools/web3_utils.py,sha256=nKRHmdLnWSKd3wpo-cysXGvhhrJ2Yf69sN2FFQfSt6s,10578
|
78
|
-
prediction_market_agent_tooling-0.
|
79
|
-
prediction_market_agent_tooling-0.
|
80
|
-
prediction_market_agent_tooling-0.
|
81
|
-
prediction_market_agent_tooling-0.
|
82
|
-
prediction_market_agent_tooling-0.
|
78
|
+
prediction_market_agent_tooling-0.47.0.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
79
|
+
prediction_market_agent_tooling-0.47.0.dist-info/METADATA,sha256=y9NRHOcSEBx1b32DwPINwQQ1lqiaDoappIIayDz1rOI,7634
|
80
|
+
prediction_market_agent_tooling-0.47.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
81
|
+
prediction_market_agent_tooling-0.47.0.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
|
82
|
+
prediction_market_agent_tooling-0.47.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|