diff --git a/prediction_market_agent_tooling/markets/manifold/api.py b/prediction_market_agent_tooling/markets/manifold/api.py index d3279448..d442a715 100644 --- a/prediction_market_agent_tooling/markets/manifold/api.py +++ b/prediction_market_agent_tooling/markets/manifold/api.py @@ -12,13 +12,17 @@ ResolvedBet, ) from prediction_market_agent_tooling.markets.manifold.data_models import ( + FullManifoldMarket, ManifoldBet, ManifoldContractMetric, ManifoldMarket, ManifoldUser, ) from prediction_market_agent_tooling.tools.parallelism import par_map -from prediction_market_agent_tooling.tools.utils import response_list_to_model +from prediction_market_agent_tooling.tools.utils import ( + response_list_to_model, + response_to_model, +) """ Python API for Manifold Markets @@ -65,9 +69,7 @@ def get_manifold_binary_markets( while True: params["offset"] = offset response = requests.get(url, params=params) - response.raise_for_status() - data = response.json() - markets = [ManifoldMarket.model_validate(x) for x in data] + markets = response_list_to_model(response, ManifoldMarket) if not markets: break @@ -152,11 +154,9 @@ def get_authenticated_user(api_key: str) -> ManifoldUser: wait=tenacity.wait_fixed(1), after=lambda x: logger.debug(f"get_manifold_market failed, {x.attempt_number=}."), ) -def get_manifold_market(market_id: str) -> ManifoldMarket: +def get_manifold_market(market_id: str) -> FullManifoldMarket: url = f"{MANIFOLD_API_BASE_URL}/v0/market/{market_id}" - response = requests.get(url) - response.raise_for_status() - return ManifoldMarket.model_validate(response.json()) + return response_to_model(requests.get(url), FullManifoldMarket) @tenacity.retry( @@ -223,6 +223,6 @@ def manifold_to_generic_resolved_bet( def get_market_positions(market_id: str, user_id: str) -> list[ManifoldContractMetric]: url = f"{MANIFOLD_API_BASE_URL}/v0/market/{market_id}/positions" params = {"userId": user_id} - response = requests.get(url, params=params) - response.raise_for_status() - return [ManifoldContractMetric.model_validate(x) for x in response.json()] + return response_list_to_model( + requests.get(url, params=params), ManifoldContractMetric + ) diff --git a/prediction_market_agent_tooling/markets/manifold/data_models.py b/prediction_market_agent_tooling/markets/manifold/data_models.py index 130b06cb..f27214ad 100644 --- a/prediction_market_agent_tooling/markets/manifold/data_models.py +++ b/prediction_market_agent_tooling/markets/manifold/data_models.py @@ -1,5 +1,6 @@ import typing as t from datetime import datetime, timedelta +from enum import Enum from pydantic import BaseModel, field_validator @@ -19,6 +20,25 @@ class ManifoldPool(BaseModel): YES: float +class ManifoldAnswersMode(str, Enum): + ANYONE = "ANYONE" + ONLY_CREATOR = "ONLY_CREATOR" + DISABLED = "DISABLED" + + +class ManifoldAnswer(BaseModel): + createdTime: datetime + avatarUrl: str + id: str + username: str + number: int + name: str + contractId: str + text: str + userId: str + probability: float + + class ManifoldMarket(BaseModel): """ https://docs.manifold.markets/api#get-v0markets @@ -84,6 +104,20 @@ def clip_timestamp(cls, value: int) -> datetime: return datetime.fromtimestamp(value) +class FullManifoldMarket(ManifoldMarket): + # Some of these fields are available only in specific cases, see https://docs.manifold.markets/api#get-v0marketmarketid. + answers: list[ManifoldAnswer] | None = None + shouldAnswersSumToOne: bool | None = None + addAnswersMode: ManifoldAnswersMode | None = None + options: dict[str, int | str] | None = None + totalBounty: float | None = None + bountyLeft: float | None = None + description: str | dict[str, t.Any] + textDescription: str + coverImageUrl: str | None = None + groupSlugs: list[str] | None = None + + class ProfitCached(BaseModel): daily: Mana weekly: Mana diff --git a/pyproject.toml b/pyproject.toml index fd327dcf..f266dbe0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "prediction-market-agent-tooling" -version = "0.41.2" +version = "0.42.0" description = "Tools to benchmark, deploy and monitor prediction market agents." authors = ["Gnosis"] readme = "README.md" diff --git a/tests/markets/test_manifold.py b/tests/markets/test_manifold.py index 6fd81444..cee0d6ac 100644 --- a/tests/markets/test_manifold.py +++ b/tests/markets/test_manifold.py @@ -9,6 +9,7 @@ from prediction_market_agent_tooling.markets.manifold.api import ( get_manifold_bets, get_manifold_binary_markets, + get_manifold_market, get_one_manifold_binary_market, get_resolved_manifold_bets, manifold_to_generic_resolved_bet, @@ -37,6 +38,13 @@ def test_manifold_markets() -> None: assert len(markets) == limit +def test_manifold_full_market() -> None: + markets = get_manifold_binary_markets(22) + for market in markets: + full_market = get_manifold_market(market.id) + assert market.id == full_market.id, f"{market.id=} != {full_market.id=}" + + def test_manifold_bets(a_user_id: str) -> None: start_time = datetime(2020, 2, 1, tzinfo=pytz.UTC) bets = get_manifold_bets(