prediction-market-agent-tooling 0.49.1__py3-none-any.whl → 0.50.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/benchmark/agents.py +6 -6
- prediction_market_agent_tooling/deploy/agent.py +5 -5
- prediction_market_agent_tooling/deploy/betting_strategy.py +83 -3
- prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +3 -4
- prediction_market_agent_tooling/gtypes.py +3 -2
- prediction_market_agent_tooling/jobs/jobs_models.py +3 -3
- prediction_market_agent_tooling/jobs/omen/omen_jobs.py +2 -2
- prediction_market_agent_tooling/markets/agent_market.py +13 -17
- prediction_market_agent_tooling/markets/data_models.py +3 -3
- prediction_market_agent_tooling/markets/manifold/api.py +6 -6
- prediction_market_agent_tooling/markets/manifold/data_models.py +13 -23
- prediction_market_agent_tooling/markets/manifold/manifold.py +2 -2
- prediction_market_agent_tooling/markets/markets.py +7 -3
- prediction_market_agent_tooling/markets/metaculus/api.py +2 -3
- prediction_market_agent_tooling/markets/metaculus/data_models.py +11 -10
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +2 -2
- prediction_market_agent_tooling/markets/omen/data_models.py +35 -22
- prediction_market_agent_tooling/markets/omen/omen.py +13 -9
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +4 -2
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +3 -4
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +46 -33
- prediction_market_agent_tooling/markets/polymarket/api.py +1 -1
- prediction_market_agent_tooling/markets/polymarket/data_models.py +5 -6
- prediction_market_agent_tooling/markets/polymarket/data_models_web.py +14 -14
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +2 -2
- prediction_market_agent_tooling/monitor/markets/manifold.py +2 -2
- prediction_market_agent_tooling/monitor/markets/metaculus.py +2 -2
- prediction_market_agent_tooling/monitor/markets/omen.py +2 -2
- prediction_market_agent_tooling/monitor/markets/polymarket.py +2 -2
- prediction_market_agent_tooling/monitor/monitor.py +5 -15
- prediction_market_agent_tooling/monitor/monitor_app.py +7 -8
- prediction_market_agent_tooling/tools/contract.py +3 -7
- prediction_market_agent_tooling/tools/datetime_utc.py +74 -0
- prediction_market_agent_tooling/tools/httpx_cached_client.py +11 -0
- prediction_market_agent_tooling/tools/is_predictable.py +1 -1
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +16 -11
- prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +5 -5
- prediction_market_agent_tooling/tools/utils.py +28 -52
- {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/METADATA +5 -2
- {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/RECORD +43 -41
- {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.49.1.dist-info → prediction_market_agent_tooling-0.50.0.dist-info}/entry_points.txt +0 -0
@@ -1,12 +1,12 @@
|
|
1
1
|
import random
|
2
2
|
import typing as t
|
3
|
-
from datetime import datetime
|
4
3
|
|
5
4
|
from prediction_market_agent_tooling.benchmark.utils import (
|
6
5
|
OutcomePrediction,
|
7
6
|
Prediction,
|
8
7
|
)
|
9
8
|
from prediction_market_agent_tooling.gtypes import Probability
|
9
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
10
10
|
|
11
11
|
|
12
12
|
class AbstractBenchmarkedAgent:
|
@@ -41,7 +41,7 @@ class AbstractBenchmarkedAgent:
|
|
41
41
|
def is_predictable_restricted(
|
42
42
|
self,
|
43
43
|
market_question: str,
|
44
|
-
time_restriction_up_to:
|
44
|
+
time_restriction_up_to: DatetimeUTC,
|
45
45
|
) -> bool:
|
46
46
|
"""
|
47
47
|
Override if the agent can decide to not predict the question, before doing the hard work.
|
@@ -53,7 +53,7 @@ class AbstractBenchmarkedAgent:
|
|
53
53
|
def predict_restricted(
|
54
54
|
self,
|
55
55
|
market_question: str,
|
56
|
-
time_restriction_up_to:
|
56
|
+
time_restriction_up_to: DatetimeUTC,
|
57
57
|
) -> Prediction:
|
58
58
|
"""
|
59
59
|
Predict the outcome of the market question.
|
@@ -65,7 +65,7 @@ class AbstractBenchmarkedAgent:
|
|
65
65
|
def check_and_predict_restricted(
|
66
66
|
self,
|
67
67
|
market_question: str,
|
68
|
-
time_restriction_up_to:
|
68
|
+
time_restriction_up_to: DatetimeUTC,
|
69
69
|
) -> Prediction:
|
70
70
|
"""
|
71
71
|
Data used must be restricted to the time_restriction_up_to.
|
@@ -94,7 +94,7 @@ class RandomAgent(AbstractBenchmarkedAgent):
|
|
94
94
|
)
|
95
95
|
|
96
96
|
def predict_restricted(
|
97
|
-
self, market_question: str, time_restriction_up_to:
|
97
|
+
self, market_question: str, time_restriction_up_to: DatetimeUTC
|
98
98
|
) -> Prediction:
|
99
99
|
return self.predict(market_question)
|
100
100
|
|
@@ -117,6 +117,6 @@ class FixedAgent(AbstractBenchmarkedAgent):
|
|
117
117
|
)
|
118
118
|
|
119
119
|
def predict_restricted(
|
120
|
-
self, market_question: str, time_restriction_up_to:
|
120
|
+
self, market_question: str, time_restriction_up_to: DatetimeUTC
|
121
121
|
) -> Prediction:
|
122
122
|
return self.predict(market_question)
|
@@ -4,7 +4,7 @@ import os
|
|
4
4
|
import tempfile
|
5
5
|
import time
|
6
6
|
import typing as t
|
7
|
-
from datetime import
|
7
|
+
from datetime import timedelta
|
8
8
|
from enum import Enum
|
9
9
|
from functools import cached_property
|
10
10
|
|
@@ -67,7 +67,7 @@ from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
|
|
67
67
|
from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
|
68
68
|
from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
|
69
69
|
from prediction_market_agent_tooling.tools.langfuse_ import langfuse_context, observe
|
70
|
-
from prediction_market_agent_tooling.tools.utils import
|
70
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC, utcnow
|
71
71
|
from prediction_market_agent_tooling.tools.web3_utils import ipfscidv0_to_byte32
|
72
72
|
|
73
73
|
MAX_AVAILABLE_MARKETS = 20
|
@@ -215,7 +215,7 @@ class DeployableAgent:
|
|
215
215
|
secrets: dict[str, str] | None = None,
|
216
216
|
cron_schedule: str | None = None,
|
217
217
|
gcp_fname: str | None = None,
|
218
|
-
start_time:
|
218
|
+
start_time: DatetimeUTC | None = None,
|
219
219
|
timeout: int = 180,
|
220
220
|
) -> None:
|
221
221
|
path_to_agent_file = os.path.relpath(inspect.getfile(self.__class__))
|
@@ -287,7 +287,7 @@ def {entrypoint_function_name}(request) -> str:
|
|
287
287
|
raise NotImplementedError("This method must be implemented by the subclass.")
|
288
288
|
|
289
289
|
def get_gcloud_fname(self, market_type: MarketType) -> str:
|
290
|
-
return f"{self.__class__.__name__.lower()}-{market_type}-{
|
290
|
+
return f"{self.__class__.__name__.lower()}-{market_type}-{utcnow().strftime('%Y-%m-%d--%H-%M-%S')}"
|
291
291
|
|
292
292
|
|
293
293
|
class DeployableTraderAgent(DeployableAgent):
|
@@ -307,7 +307,7 @@ class DeployableTraderAgent(DeployableAgent):
|
|
307
307
|
def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
|
308
308
|
user_id = market.get_user_id(api_keys=APIKeys())
|
309
309
|
|
310
|
-
total_amount = market.
|
310
|
+
total_amount = market.get_tiny_bet_amount().amount
|
311
311
|
if existing_position := market.get_position(user_id=user_id):
|
312
312
|
total_amount += existing_position.total_amount.amount
|
313
313
|
|
@@ -1,5 +1,9 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
|
3
|
+
from scipy.optimize import minimize_scalar
|
4
|
+
|
5
|
+
from prediction_market_agent_tooling.gtypes import xDai
|
6
|
+
from prediction_market_agent_tooling.loggers import logger
|
3
7
|
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
|
4
8
|
from prediction_market_agent_tooling.markets.data_models import (
|
5
9
|
Currency,
|
@@ -10,10 +14,14 @@ from prediction_market_agent_tooling.markets.data_models import (
|
|
10
14
|
TradeType,
|
11
15
|
)
|
12
16
|
from prediction_market_agent_tooling.markets.omen.data_models import get_boolean_outcome
|
17
|
+
from prediction_market_agent_tooling.markets.omen.omen import (
|
18
|
+
get_buy_outcome_token_amount,
|
19
|
+
)
|
13
20
|
from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import (
|
14
21
|
get_kelly_bet_full,
|
15
22
|
get_kelly_bet_simplified,
|
16
23
|
)
|
24
|
+
from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet
|
17
25
|
from prediction_market_agent_tooling.tools.utils import check_not_none
|
18
26
|
|
19
27
|
|
@@ -134,8 +142,9 @@ class MaxExpectedValueBettingStrategy(MaxAccuracyBettingStrategy):
|
|
134
142
|
|
135
143
|
|
136
144
|
class KellyBettingStrategy(BettingStrategy):
|
137
|
-
def __init__(self, max_bet_amount: float):
|
145
|
+
def __init__(self, max_bet_amount: float, max_price_impact: float | None = None):
|
138
146
|
self.max_bet_amount = max_bet_amount
|
147
|
+
self.max_price_impact = max_price_impact
|
139
148
|
|
140
149
|
def calculate_trades(
|
141
150
|
self,
|
@@ -165,9 +174,19 @@ class KellyBettingStrategy(BettingStrategy):
|
|
165
174
|
)
|
166
175
|
)
|
167
176
|
|
177
|
+
kelly_bet_size = kelly_bet.size
|
178
|
+
if self.max_price_impact:
|
179
|
+
# Adjust amount
|
180
|
+
max_price_impact_bet_amount = self.calculate_bet_amount_for_price_impact(
|
181
|
+
market, kelly_bet, 0
|
182
|
+
)
|
183
|
+
|
184
|
+
# We just don't want Kelly size to extrapolate price_impact - hence we take the min.
|
185
|
+
kelly_bet_size = min(kelly_bet.size, max_price_impact_bet_amount)
|
186
|
+
|
168
187
|
amounts = {
|
169
188
|
market.get_outcome_str_from_bool(kelly_bet.direction): TokenAmount(
|
170
|
-
amount=
|
189
|
+
amount=kelly_bet_size, currency=market.currency
|
171
190
|
),
|
172
191
|
}
|
173
192
|
target_position = Position(market_id=market.id, amounts=amounts)
|
@@ -176,8 +195,69 @@ class KellyBettingStrategy(BettingStrategy):
|
|
176
195
|
)
|
177
196
|
return trades
|
178
197
|
|
198
|
+
def calculate_price_impact_for_bet_amount(
|
199
|
+
self, buy_direction: bool, bet_amount: float, yes: float, no: float, fee: float
|
200
|
+
) -> float:
|
201
|
+
total_outcome_tokens = yes + no
|
202
|
+
expected_price = (
|
203
|
+
no / total_outcome_tokens if buy_direction else yes / total_outcome_tokens
|
204
|
+
)
|
205
|
+
|
206
|
+
tokens_to_buy = get_buy_outcome_token_amount(
|
207
|
+
bet_amount, buy_direction, yes, no, fee
|
208
|
+
)
|
209
|
+
|
210
|
+
actual_price = bet_amount / tokens_to_buy
|
211
|
+
# price_impact should always be > 0
|
212
|
+
price_impact = (actual_price - expected_price) / expected_price
|
213
|
+
return price_impact
|
214
|
+
|
215
|
+
def calculate_bet_amount_for_price_impact(
|
216
|
+
self,
|
217
|
+
market: AgentMarket,
|
218
|
+
kelly_bet: SimpleBet,
|
219
|
+
fee: float,
|
220
|
+
) -> float:
|
221
|
+
def calculate_price_impact_deviation_from_target_price_impact(
|
222
|
+
bet_amount: xDai,
|
223
|
+
) -> float:
|
224
|
+
price_impact = self.calculate_price_impact_for_bet_amount(
|
225
|
+
kelly_bet.direction,
|
226
|
+
bet_amount,
|
227
|
+
yes_outcome_pool_size,
|
228
|
+
no_outcome_pool_size,
|
229
|
+
fee,
|
230
|
+
)
|
231
|
+
# We return abs for the algorithm to converge to 0 instead of the min (and possibly negative) value.
|
232
|
+
|
233
|
+
max_price_impact = check_not_none(self.max_price_impact)
|
234
|
+
return abs(price_impact - max_price_impact)
|
235
|
+
|
236
|
+
if not market.outcome_token_pool:
|
237
|
+
logger.warning(
|
238
|
+
"Market outcome_token_pool is None, cannot calculate bet amount"
|
239
|
+
)
|
240
|
+
return kelly_bet.size
|
241
|
+
|
242
|
+
yes_outcome_pool_size = market.outcome_token_pool[
|
243
|
+
market.get_outcome_str_from_bool(True)
|
244
|
+
]
|
245
|
+
no_outcome_pool_size = market.outcome_token_pool[
|
246
|
+
market.get_outcome_str_from_bool(False)
|
247
|
+
]
|
248
|
+
|
249
|
+
# The bounds below have been found to work heuristically.
|
250
|
+
optimized_bet_amount = minimize_scalar(
|
251
|
+
calculate_price_impact_deviation_from_target_price_impact,
|
252
|
+
bounds=(0, 1000 * (yes_outcome_pool_size + no_outcome_pool_size)),
|
253
|
+
method="bounded",
|
254
|
+
tol=1e-11,
|
255
|
+
options={"maxiter": 10000},
|
256
|
+
)
|
257
|
+
return float(optimized_bet_amount.x)
|
258
|
+
|
179
259
|
def __repr__(self) -> str:
|
180
|
-
return f"{self.__class__.__name__}(max_bet_amount={self.max_bet_amount})"
|
260
|
+
return f"{self.__class__.__name__}(max_bet_amount={self.max_bet_amount}, max_price_impact={self.max_price_impact})"
|
181
261
|
|
182
262
|
|
183
263
|
class MaxAccuracyWithKellyScaledBetsStrategy(BettingStrategy):
|
@@ -1,11 +1,10 @@
|
|
1
|
-
from datetime import datetime
|
2
1
|
from typing import Any
|
3
2
|
|
4
3
|
from pydantic import BaseModel
|
5
4
|
|
6
5
|
|
7
6
|
class Metadata(BaseModel):
|
8
|
-
creationTimestamp:
|
7
|
+
creationTimestamp: int
|
9
8
|
generation: int
|
10
9
|
name: str
|
11
10
|
namespace: str
|
@@ -15,12 +14,12 @@ class Metadata(BaseModel):
|
|
15
14
|
|
16
15
|
|
17
16
|
class Metadata1(BaseModel):
|
18
|
-
creationTimestamp:
|
17
|
+
creationTimestamp: int | None
|
19
18
|
name: str
|
20
19
|
|
21
20
|
|
22
21
|
class Metadata2(BaseModel):
|
23
|
-
creationTimestamp:
|
22
|
+
creationTimestamp: int | None
|
24
23
|
name: str
|
25
24
|
|
26
25
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime
|
3
2
|
from typing import NewType, Union
|
4
3
|
|
5
4
|
from eth_typing.evm import ( # noqa: F401 # Import for the sake of easy importing with others from here.
|
@@ -17,6 +16,9 @@ from web3.types import ( # noqa: F401 # Import for the sake of easy importing
|
|
17
16
|
Wei,
|
18
17
|
)
|
19
18
|
|
19
|
+
from prediction_market_agent_tooling.tools.datetime_utc import ( # noqa: F401 # Import for the sake of easy importing with others from here.
|
20
|
+
DatetimeUTC,
|
21
|
+
)
|
20
22
|
from prediction_market_agent_tooling.tools.hexbytes_custom import ( # noqa: F401 # Import for the sake of easy importing with others from here.
|
21
23
|
HexBytes,
|
22
24
|
)
|
@@ -32,7 +34,6 @@ OutcomeStr = NewType("OutcomeStr", str)
|
|
32
34
|
Probability = NewType("Probability", float)
|
33
35
|
Mana = NewType("Mana", float) # Manifold's "currency"
|
34
36
|
USDC = NewType("USDC", float)
|
35
|
-
DatetimeWithTimezone = NewType("DatetimeWithTimezone", datetime)
|
36
37
|
ChainID = NewType("ChainID", int)
|
37
38
|
IPFSCIDVersion0 = NewType("IPFSCIDVersion0", str)
|
38
39
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import typing as t
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
-
from datetime import datetime
|
4
3
|
|
5
4
|
from pydantic import BaseModel
|
6
5
|
|
@@ -9,6 +8,7 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
|
9
8
|
FilterBy,
|
10
9
|
SortBy,
|
11
10
|
)
|
11
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
12
12
|
|
13
13
|
|
14
14
|
class SimpleJob(BaseModel):
|
@@ -16,7 +16,7 @@ class SimpleJob(BaseModel):
|
|
16
16
|
job: str
|
17
17
|
reward: float
|
18
18
|
currency: str
|
19
|
-
deadline:
|
19
|
+
deadline: DatetimeUTC
|
20
20
|
|
21
21
|
|
22
22
|
class JobAgentMarket(AgentMarket, ABC):
|
@@ -29,7 +29,7 @@ class JobAgentMarket(AgentMarket, ABC):
|
|
29
29
|
|
30
30
|
@property
|
31
31
|
@abstractmethod
|
32
|
-
def deadline(self) ->
|
32
|
+
def deadline(self) -> DatetimeUTC:
|
33
33
|
"""Deadline for the job completion."""
|
34
34
|
|
35
35
|
@abstractmethod
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime
|
3
2
|
|
4
3
|
from web3 import Web3
|
5
4
|
|
@@ -21,6 +20,7 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
|
21
20
|
OmenSubgraphHandler,
|
22
21
|
SortBy,
|
23
22
|
)
|
23
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
24
24
|
|
25
25
|
|
26
26
|
class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
|
@@ -32,7 +32,7 @@ class OmenJobAgentMarket(OmenAgentMarket, JobAgentMarket):
|
|
32
32
|
return self.question
|
33
33
|
|
34
34
|
@property
|
35
|
-
def deadline(self) ->
|
35
|
+
def deadline(self) -> DatetimeUTC:
|
36
36
|
return self.close_time
|
37
37
|
|
38
38
|
def get_reward(self, max_bond: float) -> float:
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime
|
3
2
|
from enum import Enum
|
4
3
|
|
5
4
|
from eth_typing import ChecksumAddress
|
@@ -18,8 +17,8 @@ from prediction_market_agent_tooling.markets.data_models import (
|
|
18
17
|
TokenAmount,
|
19
18
|
)
|
20
19
|
from prediction_market_agent_tooling.tools.utils import (
|
20
|
+
DatetimeUTC,
|
21
21
|
check_not_none,
|
22
|
-
convert_to_utc_datetime,
|
23
22
|
should_not_happen,
|
24
23
|
utcnow,
|
25
24
|
)
|
@@ -28,6 +27,8 @@ from prediction_market_agent_tooling.tools.utils import (
|
|
28
27
|
class SortBy(str, Enum):
|
29
28
|
CLOSING_SOONEST = "closing-soonest"
|
30
29
|
NEWEST = "newest"
|
30
|
+
HIGHEST_LIQUIDITY = "highest_liquidity"
|
31
|
+
LOWEST_LIQUIDITY = "lowest_liquidity"
|
31
32
|
NONE = "none"
|
32
33
|
|
33
34
|
|
@@ -50,23 +51,16 @@ class AgentMarket(BaseModel):
|
|
50
51
|
question: str
|
51
52
|
description: str | None
|
52
53
|
outcomes: list[str]
|
53
|
-
outcome_token_pool:
|
54
|
-
str, float
|
55
|
-
|
54
|
+
outcome_token_pool: (
|
55
|
+
dict[str, float] | None
|
56
|
+
) # Should be in currency of `currency` above.
|
56
57
|
resolution: Resolution | None
|
57
|
-
created_time:
|
58
|
-
close_time:
|
58
|
+
created_time: DatetimeUTC | None
|
59
|
+
close_time: DatetimeUTC | None
|
59
60
|
current_p_yes: Probability
|
60
61
|
url: str
|
61
62
|
volume: float | None # Should be in currency of `currency` above.
|
62
63
|
|
63
|
-
_add_timezone_validator_created_time = field_validator("created_time")(
|
64
|
-
convert_to_utc_datetime
|
65
|
-
)
|
66
|
-
_add_timezone_validator_close_time = field_validator("close_time")(
|
67
|
-
convert_to_utc_datetime
|
68
|
-
)
|
69
|
-
|
70
64
|
@field_validator("outcome_token_pool")
|
71
65
|
def validate_outcome_token_pool(
|
72
66
|
cls,
|
@@ -180,7 +174,7 @@ class AgentMarket(BaseModel):
|
|
180
174
|
limit: int,
|
181
175
|
sort_by: SortBy,
|
182
176
|
filter_by: FilterBy = FilterBy.OPEN,
|
183
|
-
created_after: t.Optional[
|
177
|
+
created_after: t.Optional[DatetimeUTC] = None,
|
184
178
|
excluded_questions: set[str] | None = None,
|
185
179
|
) -> t.Sequence["AgentMarket"]:
|
186
180
|
raise NotImplementedError("Subclasses must implement this method")
|
@@ -191,13 +185,15 @@ class AgentMarket(BaseModel):
|
|
191
185
|
|
192
186
|
@staticmethod
|
193
187
|
def get_bets_made_since(
|
194
|
-
better_address: ChecksumAddress, start_time:
|
188
|
+
better_address: ChecksumAddress, start_time: DatetimeUTC
|
195
189
|
) -> list[Bet]:
|
196
190
|
raise NotImplementedError("Subclasses must implement this method")
|
197
191
|
|
198
192
|
@staticmethod
|
199
193
|
def get_resolved_bets_made_since(
|
200
|
-
better_address: ChecksumAddress,
|
194
|
+
better_address: ChecksumAddress,
|
195
|
+
start_time: DatetimeUTC,
|
196
|
+
end_time: DatetimeUTC | None,
|
201
197
|
) -> list[ResolvedBet]:
|
202
198
|
raise NotImplementedError("Subclasses must implement this method")
|
203
199
|
|
@@ -1,10 +1,10 @@
|
|
1
|
-
from datetime import datetime
|
2
1
|
from enum import Enum
|
3
2
|
from typing import Annotated, TypeAlias
|
4
3
|
|
5
4
|
from pydantic import BaseModel, BeforeValidator, computed_field
|
6
5
|
|
7
6
|
from prediction_market_agent_tooling.gtypes import OutcomeStr, Probability
|
7
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
8
8
|
|
9
9
|
|
10
10
|
class Currency(str, Enum):
|
@@ -40,7 +40,7 @@ class Bet(BaseModel):
|
|
40
40
|
id: str
|
41
41
|
amount: BetAmount
|
42
42
|
outcome: bool
|
43
|
-
created_time:
|
43
|
+
created_time: DatetimeUTC
|
44
44
|
market_question: str
|
45
45
|
market_id: str
|
46
46
|
|
@@ -50,7 +50,7 @@ class Bet(BaseModel):
|
|
50
50
|
|
51
51
|
class ResolvedBet(Bet):
|
52
52
|
market_outcome: bool
|
53
|
-
resolved_time:
|
53
|
+
resolved_time: DatetimeUTC
|
54
54
|
profit: ProfitAmount
|
55
55
|
|
56
56
|
@computed_field # type: ignore[prop-decorator]
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime
|
3
2
|
|
4
3
|
import requests
|
5
4
|
import tenacity
|
@@ -20,6 +19,7 @@ from prediction_market_agent_tooling.markets.manifold.data_models import (
|
|
20
19
|
)
|
21
20
|
from prediction_market_agent_tooling.tools.parallelism import par_map
|
22
21
|
from prediction_market_agent_tooling.tools.utils import (
|
22
|
+
DatetimeUTC,
|
23
23
|
response_list_to_model,
|
24
24
|
response_to_model,
|
25
25
|
)
|
@@ -47,7 +47,7 @@ def get_manifold_binary_markets(
|
|
47
47
|
]
|
48
48
|
| None
|
49
49
|
) = "open",
|
50
|
-
created_after: t.Optional[
|
50
|
+
created_after: t.Optional[DatetimeUTC] = None,
|
51
51
|
excluded_questions: set[str] | None = None,
|
52
52
|
) -> list[ManifoldMarket]:
|
53
53
|
all_markets: list[ManifoldMarket] = []
|
@@ -167,8 +167,8 @@ def get_manifold_market(market_id: str) -> FullManifoldMarket:
|
|
167
167
|
)
|
168
168
|
def get_manifold_bets(
|
169
169
|
user_id: str,
|
170
|
-
start_time:
|
171
|
-
end_time: t.Optional[
|
170
|
+
start_time: DatetimeUTC,
|
171
|
+
end_time: t.Optional[DatetimeUTC],
|
172
172
|
) -> list[ManifoldBet]:
|
173
173
|
url = f"{MANIFOLD_API_BASE_URL}/v0/bets"
|
174
174
|
|
@@ -182,8 +182,8 @@ def get_manifold_bets(
|
|
182
182
|
|
183
183
|
def get_resolved_manifold_bets(
|
184
184
|
user_id: str,
|
185
|
-
start_time:
|
186
|
-
end_time: t.Optional[
|
185
|
+
start_time: DatetimeUTC,
|
186
|
+
end_time: t.Optional[DatetimeUTC],
|
187
187
|
) -> tuple[list[ManifoldBet], list[ManifoldMarket]]:
|
188
188
|
bets = get_manifold_bets(user_id, start_time, end_time)
|
189
189
|
markets: list[ManifoldMarket] = par_map(
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime, timedelta
|
3
2
|
from enum import Enum
|
4
3
|
|
5
|
-
from pydantic import BaseModel
|
4
|
+
from pydantic import BaseModel
|
6
5
|
|
7
6
|
from prediction_market_agent_tooling.gtypes import Mana, Probability
|
8
7
|
from prediction_market_agent_tooling.markets.data_models import (
|
@@ -10,7 +9,7 @@ from prediction_market_agent_tooling.markets.data_models import (
|
|
10
9
|
ProfitAmount,
|
11
10
|
Resolution,
|
12
11
|
)
|
13
|
-
from prediction_market_agent_tooling.tools.utils import should_not_happen
|
12
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC, should_not_happen
|
14
13
|
|
15
14
|
MANIFOLD_BASE_URL = "https://manifold.markets"
|
16
15
|
|
@@ -33,7 +32,7 @@ class ManifoldAnswersMode(str, Enum):
|
|
33
32
|
|
34
33
|
|
35
34
|
class ManifoldAnswer(BaseModel):
|
36
|
-
createdTime:
|
35
|
+
createdTime: DatetimeUTC
|
37
36
|
avatarUrl: str
|
38
37
|
id: str
|
39
38
|
username: str
|
@@ -55,17 +54,17 @@ class ManifoldMarket(BaseModel):
|
|
55
54
|
id: str
|
56
55
|
question: str
|
57
56
|
creatorId: str
|
58
|
-
closeTime:
|
59
|
-
createdTime:
|
57
|
+
closeTime: DatetimeUTC
|
58
|
+
createdTime: DatetimeUTC
|
60
59
|
creatorAvatarUrl: t.Optional[str] = None
|
61
60
|
creatorName: str
|
62
61
|
creatorUsername: str
|
63
62
|
isResolved: bool
|
64
63
|
resolution: t.Optional[Resolution] = None
|
65
|
-
resolutionTime: t.Optional[
|
66
|
-
lastBetTime: t.Optional[
|
67
|
-
lastCommentTime: t.Optional[
|
68
|
-
lastUpdatedTime:
|
64
|
+
resolutionTime: t.Optional[DatetimeUTC] = None
|
65
|
+
lastBetTime: t.Optional[DatetimeUTC] = None
|
66
|
+
lastCommentTime: t.Optional[DatetimeUTC] = None
|
67
|
+
lastUpdatedTime: DatetimeUTC
|
69
68
|
mechanism: str
|
70
69
|
outcomeType: str
|
71
70
|
p: t.Optional[float] = None
|
@@ -100,15 +99,6 @@ class ManifoldMarket(BaseModel):
|
|
100
99
|
def __repr__(self) -> str:
|
101
100
|
return f"Manifold's market: {self.question}"
|
102
101
|
|
103
|
-
@field_validator("closeTime", mode="before")
|
104
|
-
def clip_timestamp(cls, value: int) -> datetime:
|
105
|
-
"""
|
106
|
-
Clip the timestamp to the maximum valid timestamp.
|
107
|
-
"""
|
108
|
-
max_timestamp = (datetime.max - timedelta(days=1)).timestamp()
|
109
|
-
value = int(min(value / 1000, max_timestamp))
|
110
|
-
return datetime.fromtimestamp(value)
|
111
|
-
|
112
102
|
|
113
103
|
class FullManifoldMarket(ManifoldMarket):
|
114
104
|
# Some of these fields are available only in specific cases, see https://docs.manifold.markets/api#get-v0marketmarketid.
|
@@ -137,7 +127,7 @@ class ManifoldUser(BaseModel):
|
|
137
127
|
"""
|
138
128
|
|
139
129
|
id: str
|
140
|
-
createdTime:
|
130
|
+
createdTime: DatetimeUTC
|
141
131
|
name: str
|
142
132
|
username: str
|
143
133
|
url: str
|
@@ -154,7 +144,7 @@ class ManifoldUser(BaseModel):
|
|
154
144
|
userDeleted: t.Optional[bool] = None
|
155
145
|
balance: Mana
|
156
146
|
totalDeposits: Mana
|
157
|
-
lastBetTime: t.Optional[
|
147
|
+
lastBetTime: t.Optional[DatetimeUTC] = None
|
158
148
|
currentBettingStreak: t.Optional[int] = None
|
159
149
|
profitCached: ProfitCached
|
160
150
|
|
@@ -193,7 +183,7 @@ class ManifoldBet(BaseModel):
|
|
193
183
|
loanAmount: Mana | None
|
194
184
|
orderAmount: t.Optional[Mana] = None
|
195
185
|
fills: t.Optional[list[ManifoldBetFills]] = None
|
196
|
-
createdTime:
|
186
|
+
createdTime: DatetimeUTC
|
197
187
|
outcome: Resolution
|
198
188
|
|
199
189
|
def get_resolved_boolean_outcome(self) -> bool:
|
@@ -237,4 +227,4 @@ class ManifoldContractMetric(BaseModel):
|
|
237
227
|
userUsername: str
|
238
228
|
userName: str
|
239
229
|
userAvatarUrl: str
|
240
|
-
lastBetTime:
|
230
|
+
lastBetTime: DatetimeUTC
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime
|
3
2
|
from math import ceil
|
4
3
|
|
5
4
|
from prediction_market_agent_tooling.config import APIKeys
|
@@ -23,6 +22,7 @@ from prediction_market_agent_tooling.markets.manifold.data_models import (
|
|
23
22
|
from prediction_market_agent_tooling.tools.betting_strategies.minimum_bet_to_win import (
|
24
23
|
minimum_bet_to_win,
|
25
24
|
)
|
25
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC
|
26
26
|
|
27
27
|
|
28
28
|
class ManifoldAgentMarket(AgentMarket):
|
@@ -83,7 +83,7 @@ class ManifoldAgentMarket(AgentMarket):
|
|
83
83
|
limit: int,
|
84
84
|
sort_by: SortBy,
|
85
85
|
filter_by: FilterBy = FilterBy.OPEN,
|
86
|
-
created_after: t.Optional[
|
86
|
+
created_after: t.Optional[DatetimeUTC] = None,
|
87
87
|
excluded_questions: set[str] | None = None,
|
88
88
|
) -> t.Sequence["ManifoldAgentMarket"]:
|
89
89
|
sort: t.Literal["newest", "close-date"] | None
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import
|
2
|
+
from datetime import timedelta
|
3
3
|
from enum import Enum
|
4
4
|
|
5
5
|
from prediction_market_agent_tooling.config import APIKeys
|
@@ -26,7 +26,11 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
|
26
26
|
from prediction_market_agent_tooling.markets.polymarket.polymarket import (
|
27
27
|
PolymarketAgentMarket,
|
28
28
|
)
|
29
|
-
from prediction_market_agent_tooling.tools.utils import
|
29
|
+
from prediction_market_agent_tooling.tools.utils import (
|
30
|
+
DatetimeUTC,
|
31
|
+
should_not_happen,
|
32
|
+
utcnow,
|
33
|
+
)
|
30
34
|
|
31
35
|
|
32
36
|
class MarketType(str, Enum):
|
@@ -57,7 +61,7 @@ def get_binary_markets(
|
|
57
61
|
filter_by: FilterBy = FilterBy.OPEN,
|
58
62
|
sort_by: SortBy = SortBy.NONE,
|
59
63
|
excluded_questions: set[str] | None = None,
|
60
|
-
created_after:
|
64
|
+
created_after: DatetimeUTC | None = None,
|
61
65
|
) -> t.Sequence[AgentMarket]:
|
62
66
|
agent_market_class = MARKET_TYPE_TO_AGENT_MARKET[market_type]
|
63
67
|
markets = agent_market_class.get_binary_markets(
|
@@ -1,4 +1,3 @@
|
|
1
|
-
from datetime import datetime
|
2
1
|
from typing import Union
|
3
2
|
|
4
3
|
import requests
|
@@ -9,7 +8,7 @@ from prediction_market_agent_tooling.markets.metaculus.data_models import (
|
|
9
8
|
MetaculusQuestion,
|
10
9
|
MetaculusQuestions,
|
11
10
|
)
|
12
|
-
from prediction_market_agent_tooling.tools.utils import response_to_model
|
11
|
+
from prediction_market_agent_tooling.tools.utils import DatetimeUTC, response_to_model
|
13
12
|
|
14
13
|
METACULUS_API_BASE_URL = "https://www.metaculus.com/api2"
|
15
14
|
|
@@ -65,7 +64,7 @@ def get_questions(
|
|
65
64
|
order_by: str | None = None,
|
66
65
|
offset: int = 0,
|
67
66
|
tournament_id: int | None = None,
|
68
|
-
created_after:
|
67
|
+
created_after: DatetimeUTC | None = None,
|
69
68
|
status: str | None = None,
|
70
69
|
) -> list[MetaculusQuestion]:
|
71
70
|
"""
|