prediction-market-agent-tooling 0.63.3__py3-none-any.whl → 0.63.5__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.
@@ -30,7 +30,7 @@ from prediction_market_agent_tooling.deploy.trade_interval import (
30
30
  FixedInterval,
31
31
  TradeInterval,
32
32
  )
33
- from prediction_market_agent_tooling.gtypes import xDai
33
+ from prediction_market_agent_tooling.gtypes import USD, OutcomeToken, xDai
34
34
  from prediction_market_agent_tooling.loggers import logger
35
35
  from prediction_market_agent_tooling.markets.agent_market import (
36
36
  AgentMarket,
@@ -499,7 +499,7 @@ class DeployablePredictionAgent(DeployableAgent):
499
499
 
500
500
  for market_idx, market in enumerate(available_markets):
501
501
  logger.info(
502
- f"Going to process market {market_idx+1} / {len(available_markets)}."
502
+ f"Going to process market {market.url}: {market_idx+1} / {len(available_markets)}."
503
503
  )
504
504
  self.before_process_market(market_type, market)
505
505
  processed_market = self.process_market(market_type, market)
@@ -648,19 +648,27 @@ class DeployableTraderAgent(DeployablePredictionAgent):
648
648
  )
649
649
  case TradeType.SELL:
650
650
  # Get actual value of the position we are going to sell, and if it's less than we wanted to sell, simply sell all of it.
651
- current_position_value = check_not_none(
651
+ current_position = check_not_none(
652
652
  market.get_position(user_id),
653
653
  "Should exists if we are going to sell outcomes.",
654
- ).amounts_current[
654
+ )
655
+ current_position_value_usd = current_position.amounts_current[
655
656
  market.get_outcome_str_from_bool(trade.outcome)
656
657
  ]
657
- if current_position_value < trade.amount:
658
+ amount_to_sell: USD | OutcomeToken
659
+ if current_position_value_usd <= trade.amount:
658
660
  logger.warning(
659
- f"Current value of position {trade.outcome=}, {current_position_value=} is less than the desired selling amount {trade.amount=}. Selling all."
661
+ f"Current value of position {trade.outcome=}, {current_position_value_usd=} is less than the desired selling amount {trade.amount=}. Selling all."
660
662
  )
663
+ # In case the agent asked to sell too much, provide the amount to sell as all outcome tokens, instead of in USD, to minimze fx fluctuations when selling.
664
+ amount_to_sell = current_position.amounts_ot[
665
+ market.get_outcome_str_from_bool(trade.outcome)
666
+ ]
667
+ else:
668
+ amount_to_sell = trade.amount
661
669
  id = market.sell_tokens(
662
670
  outcome=trade.outcome,
663
- amount=min(trade.amount, current_position_value),
671
+ amount=amount_to_sell,
664
672
  )
665
673
  case _:
666
674
  raise ValueError(f"Unexpected trade type {trade.trade_type}.")
@@ -34,7 +34,7 @@ class CollateralToken(_GenericValue[int | float | str | Decimal, float], parser=
34
34
 
35
35
  @property
36
36
  def as_wei(self) -> "Wei":
37
- return Wei(Web3.to_wei(self.value, "ether"))
37
+ return Wei(to_wei_inc_negative(self.value))
38
38
 
39
39
 
40
40
  class OutcomeToken(_GenericValue[int | float | str | Decimal, float], parser=float):
@@ -51,7 +51,7 @@ class OutcomeToken(_GenericValue[int | float | str | Decimal, float], parser=flo
51
51
 
52
52
  @property
53
53
  def as_outcome_wei(self) -> "OutcomeWei":
54
- return OutcomeWei(Web3.to_wei(self.value, "ether"))
54
+ return OutcomeWei(to_wei_inc_negative(self.value))
55
55
 
56
56
  @property
57
57
  def as_token(self) -> CollateralToken:
@@ -77,7 +77,7 @@ class xDai(_GenericValue[int | float | str | Decimal, float], parser=float):
77
77
 
78
78
  @property
79
79
  def as_xdai_wei(self) -> "xDaiWei":
80
- return xDaiWei(Web3.to_wei(self.value, "ether"))
80
+ return xDaiWei(to_wei_inc_negative(self.value))
81
81
 
82
82
 
83
83
  class Mana(_GenericValue[int | float | str | Decimal, float], parser=float):
@@ -93,7 +93,7 @@ class Wei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
93
93
 
94
94
  @property
95
95
  def as_token(self) -> CollateralToken:
96
- return CollateralToken(Web3.from_wei(self.value, "ether"))
96
+ return CollateralToken(from_wei_inc_negative(self.value))
97
97
 
98
98
 
99
99
  class OutcomeWei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
@@ -107,7 +107,7 @@ class OutcomeWei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
107
107
 
108
108
  @property
109
109
  def as_outcome_token(self) -> OutcomeToken:
110
- return OutcomeToken(Web3.from_wei(self.value, "ether"))
110
+ return OutcomeToken(from_wei_inc_negative(self.value))
111
111
 
112
112
  @property
113
113
  def as_wei(self) -> Wei:
@@ -122,7 +122,7 @@ class xDaiWei(_GenericValue[Web3Wei | int | str, Web3Wei], parser=int):
122
122
 
123
123
  @property
124
124
  def as_xdai(self) -> xDai:
125
- return xDai(Web3.from_wei(self.value, "ether"))
125
+ return xDai(from_wei_inc_negative(self.value))
126
126
 
127
127
  @property
128
128
  def as_wei(self) -> Wei:
@@ -162,3 +162,19 @@ def secretstr_to_v1_secretstr(s: SecretStr | None) -> SecretStrV1 | None:
162
162
  def int_to_hexbytes(v: int) -> HexBytes:
163
163
  # Example: 1 -> HexBytes("0x0000000000000000000000000000000000000000000000000000000000000001"). # web3-private-key-ok
164
164
  return HexBytes.fromhex(format(v, "064x"))
165
+
166
+
167
+ def to_wei_inc_negative(value: int | float | str | Decimal) -> Web3Wei:
168
+ """
169
+ Handles conversion of a value to Wei, taking into account negative values.
170
+ """
171
+ return Web3Wei(
172
+ Web3.to_wei(abs(Decimal(value)), "ether") * (-1 if Decimal(value) < 0 else 1)
173
+ )
174
+
175
+
176
+ def from_wei_inc_negative(value: int) -> int | Decimal:
177
+ """
178
+ Handles conversion from Wei to a float value, taking into account negative values.
179
+ """
180
+ return Web3.from_wei(abs(value), "ether") * (-1 if value < 0 else 1)
@@ -1,6 +1,6 @@
1
1
  import typing as t
2
2
 
3
- from pydantic import BaseModel, ConfigDict, Field, computed_field
3
+ from pydantic import BaseModel, ConfigDict, Field, computed_field, model_validator
4
4
  from web3 import Web3
5
5
 
6
6
  from prediction_market_agent_tooling.gtypes import (
@@ -17,6 +17,7 @@ from prediction_market_agent_tooling.gtypes import (
17
17
  xDai,
18
18
  xDaiWei,
19
19
  )
20
+ from prediction_market_agent_tooling.loggers import logger
20
21
  from prediction_market_agent_tooling.markets.data_models import (
21
22
  Bet,
22
23
  Resolution,
@@ -35,6 +36,7 @@ from prediction_market_agent_tooling.tools.utils import (
35
36
  should_not_happen,
36
37
  utcnow,
37
38
  )
39
+ from prediction_market_agent_tooling.tools.web3_utils import is_valid_wei
38
40
 
39
41
  OMEN_TRUE_OUTCOME = OutcomeStr("Yes")
40
42
  OMEN_FALSE_OUTCOME = OutcomeStr("No")
@@ -235,6 +237,27 @@ class OmenMarket(BaseModel):
235
237
  condition: Condition
236
238
  question: Question
237
239
 
240
+ @model_validator(mode="after")
241
+ def _model_validator(self) -> "OmenMarket":
242
+ if not all(is_valid_wei(number.value) for number in self.outcomeTokenAmounts):
243
+ # Sometimes we receive markets with outcomeTokenAmounts as `model.outcomeTokenAmounts=[OutcomeWei(24662799387878572), OutcomeWei(-24750000000000000)]`, which should be impossible.
244
+ # Current huntch is that it's a weird transitional status.
245
+ # Try to set them to zeros if market isn't open anymore (that's expected behaviour in such case),
246
+ # otherwise raise an error to investigate further.
247
+ if not self.is_open:
248
+ logger.warning(
249
+ f"Market {self.url} has invalid {self.outcomeTokenAmounts=}, but isn't open anymore. Setting them to zeros."
250
+ )
251
+ self.outcomeTokenAmounts = [OutcomeWei(0) for _ in self.outcomes]
252
+ self.outcomeTokenMarginalPrices = None
253
+
254
+ else:
255
+ raise ValueError(
256
+ f"Market {self.url} has invalid {self.outcomeTokenAmounts=}: {self=}"
257
+ )
258
+
259
+ return self
260
+
238
261
  @property
239
262
  def openingTimestamp(self) -> int:
240
263
  # This field is also available on this model itself, but for some reason it's typed to be optional,
@@ -6,6 +6,7 @@ import base58
6
6
  import tenacity
7
7
  from eth_account import Account
8
8
  from eth_typing import URI
9
+ from eth_utils.currency import MAX_WEI, MIN_WEI
9
10
  from pydantic.types import SecretStr
10
11
  from safe_eth.eth import EthereumClient
11
12
  from safe_eth.safe.safe import SafeV141
@@ -21,6 +22,7 @@ from prediction_market_agent_tooling.gtypes import (
21
22
  HexStr,
22
23
  IPFSCIDVersion0,
23
24
  PrivateKey,
25
+ Web3Wei,
24
26
  private_key_type,
25
27
  xDai,
26
28
  xDaiWei,
@@ -354,3 +356,7 @@ def get_receipt_block_timestamp(receipt_tx: TxReceipt, web3: Web3) -> int:
354
356
  block = web3.eth.get_block(block_number)
355
357
  block_timestamp: int = block["timestamp"]
356
358
  return block_timestamp
359
+
360
+
361
+ def is_valid_wei(value: Web3Wei) -> bool:
362
+ return MIN_WEI <= value <= MAX_WEI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.63.3
3
+ Version: 0.63.5
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.13
@@ -21,7 +21,7 @@ prediction_market_agent_tooling/benchmark/agents.py,sha256=B1-uWdyeN4GGKMWGK_-Cc
21
21
  prediction_market_agent_tooling/benchmark/benchmark.py,sha256=MqTiaaJ3cYiOLUVR7OyImLWxcEya3Rl5JyFYW-K0lwM,17097
22
22
  prediction_market_agent_tooling/benchmark/utils.py,sha256=D0MfUkVZllmvcU0VOurk9tcKT7JTtwwOp-63zuCBVuc,2880
23
23
  prediction_market_agent_tooling/config.py,sha256=So5l8KbgmzcCpxzzf13TNrEJPu_4iQnUDhzus6XRvSc,10151
24
- prediction_market_agent_tooling/deploy/agent.py,sha256=OyrhPOjByQOAi1_VWhef7ieKqREjVhvjGHgnUIQc3gI,25877
24
+ prediction_market_agent_tooling/deploy/agent.py,sha256=uh1_sf7W6Ti9HHdRw5uf540EYUEU13tLi-XQsXPzN6Q,26469
25
25
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
26
26
  prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=p25t7VU7I4hSkSl6SpzI_W55kLbYEySQdBqeschmARY,12918
27
27
  prediction_market_agent_tooling/deploy/constants.py,sha256=M5ty8URipYMGe_G-RzxRydK3AFL6CyvmqCraJUrLBnE,82
@@ -29,7 +29,7 @@ prediction_market_agent_tooling/deploy/gcp/deploy.py,sha256=CYUgnfy-9XVk04kkxA_5
29
29
  prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py,sha256=OsPboCFGiZKsvGyntGZHwdqPlLTthITkNF5rJFvGgU8,2582
30
30
  prediction_market_agent_tooling/deploy/gcp/utils.py,sha256=WI2ycX1X-IlTRoNoG4ggFlRwPL28kwM9VGDFD2fePLo,5699
31
31
  prediction_market_agent_tooling/deploy/trade_interval.py,sha256=Xk9j45alQ_vrasGvsNyuW70XHIQ7wfvjoxNR3F6HYCw,1155
32
- prediction_market_agent_tooling/gtypes.py,sha256=DaerVkKVqhIhTwCB9A8piwCR97ZMnd8nSTSOlI9XcLM,5549
32
+ prediction_market_agent_tooling/gtypes.py,sha256=bUIZfZIGvIi3aiZNu5rVE9kRevw8sfMa4bcze6QeBg8,6058
33
33
  prediction_market_agent_tooling/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  prediction_market_agent_tooling/jobs/jobs_models.py,sha256=8vYafsK1cqMWQtjBoq9rruroF84xAVD00vBTMWH6QMg,2166
35
35
  prediction_market_agent_tooling/jobs/omen/omen_jobs.py,sha256=Pf6QxPXGyie-2l_wZUjaGPTjZTlpv50_JhP40mULBaU,5048
@@ -50,7 +50,7 @@ prediction_market_agent_tooling/markets/metaculus/api.py,sha256=4TRPGytQQbSdf42D
50
50
  prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=FaBCTPPezXbBwZ9p791CiVgQ4vB696xnMbz9XVXmiVI,3267
51
51
  prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=86TIx6cavEWc8Cv4KpZxSvwiSw9oFybXE3YB49pg-CA,4377
52
52
  prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- prediction_market_agent_tooling/markets/omen/data_models.py,sha256=URvplDcTLeL6vfpkSgi8ln2iEKxt8w5qf6mTZkSyrCo,29189
53
+ prediction_market_agent_tooling/markets/omen/data_models.py,sha256=T08irohOXQnNeL45mIhGYgpJTShHVgXvQyYnLX4YLmU,30477
54
54
  prediction_market_agent_tooling/markets/omen/omen.py,sha256=Jf2qSJJn0UUISpi24xYRwoVycGuYE42kZ2z1HRGl43w,51927
55
55
  prediction_market_agent_tooling/markets/omen/omen_constants.py,sha256=D9oflYKafLQiHYtB5sScMHqmXyzM8JP8J0yATmc4SQQ,233
56
56
  prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=bCC9A7ZTJxMDJcPbl3jof6HcAGGHv1BrFq3RRWbkQ_c,28739
@@ -120,9 +120,9 @@ prediction_market_agent_tooling/tools/tokens/token_utils.py,sha256=fhs-FH9m9IbzG
120
120
  prediction_market_agent_tooling/tools/tokens/usd.py,sha256=yuW8iPPtcpP4eLH2nORMDAfztcq0Nv2ascSrCquF1f8,3115
121
121
  prediction_market_agent_tooling/tools/transaction_cache.py,sha256=K5YKNL2_tR10Iw2TD9fuP-CTGpBbZtNdgbd0B_R7pjg,1814
122
122
  prediction_market_agent_tooling/tools/utils.py,sha256=1xsyBBJfiEdSoMlceB2F8o2sCb6Z8-qNz11pEJFrdyE,6566
123
- prediction_market_agent_tooling/tools/web3_utils.py,sha256=eYCc1iWAVtqDKUPTwnMUHuYolPdwh_OTiM3-AdRgDp4,12198
124
- prediction_market_agent_tooling-0.63.3.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
125
- prediction_market_agent_tooling-0.63.3.dist-info/METADATA,sha256=0RZTVpeWNV3EurtBs_MQ55JxJVW1EmHzuxKQsVpAHHU,8689
126
- prediction_market_agent_tooling-0.63.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
127
- prediction_market_agent_tooling-0.63.3.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
128
- prediction_market_agent_tooling-0.63.3.dist-info/RECORD,,
123
+ prediction_market_agent_tooling/tools/web3_utils.py,sha256=zRq-eeBGWt8uUGN9G_WfjmJ0eVvO8aWE9S0Pz_Y6AOA,12342
124
+ prediction_market_agent_tooling-0.63.5.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
125
+ prediction_market_agent_tooling-0.63.5.dist-info/METADATA,sha256=IaLaaqZx0vS4t15q4zcpbXt_leP49eUYv8K7rGUh-p8,8689
126
+ prediction_market_agent_tooling-0.63.5.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
127
+ prediction_market_agent_tooling-0.63.5.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
128
+ prediction_market_agent_tooling-0.63.5.dist-info/RECORD,,