Skip to content

Commit

Permalink
Option to change deployed agent's bet strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
kongzii committed Feb 26, 2024
1 parent b5a119c commit a46fc5c
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 13 deletions.
9 changes: 8 additions & 1 deletion prediction_market_agent_tooling/deploy/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
)
from prediction_market_agent_tooling.deploy.gcp.utils import gcp_function_is_active
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
from prediction_market_agent_tooling.markets.data_models import BetAmount
from prediction_market_agent_tooling.markets.markets import (
MarketType,
get_binary_markets,
Expand Down Expand Up @@ -112,6 +113,12 @@ def {entrypoint_function_name}(request) -> str:
if cron_schedule:
schedule_deployed_gcp_function(fname, cron_schedule=cron_schedule)

def calculate_bet_amount(self, answer: bool, market: AgentMarket) -> BetAmount:
"""
Calculate the bet amount. By default, it returns the minimum bet amount.
"""
return market.get_tiny_bet_amount()

def run(self, market_type: MarketType, _place_bet: bool = True) -> None:
available_markets = get_binary_markets(market_type)
markets = self.pick_markets(available_markets)
Expand All @@ -120,7 +127,7 @@ def run(self, market_type: MarketType, _place_bet: bool = True) -> None:
if _place_bet:
print(f"Placing bet on {market} with result {result}")
market.place_bet(
amount=market.get_tiny_bet_amount(),
amount=self.calculate_bet_amount(result, market),
outcome=result,
)

Expand Down
17 changes: 16 additions & 1 deletion prediction_market_agent_tooling/markets/agent_market.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from pydantic import BaseModel

from prediction_market_agent_tooling.gtypes import Probability
from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency


Expand All @@ -12,10 +13,16 @@ class AgentMarket(BaseModel):
Contains everything that is needed for an agent to make a prediction.
"""

currency: t.ClassVar[Currency]

id: str
question: str
outcomes: list[str]
currency: t.ClassVar[Currency]
p_yes: Probability

@property
def p_no(self) -> float:
return 1 - self.p_yes

def get_bet_amount(self, amount: Decimal) -> BetAmount:
return BetAmount(amount=amount, currency=self.currency)
Expand All @@ -30,6 +37,14 @@ def place_bet(self, outcome: bool, amount: BetAmount) -> None:
def get_binary_markets(limit: int) -> list["AgentMarket"]:
raise NotImplementedError("Subclasses must implement this method")

def get_outcome_str(self, outcome_index: int) -> str:
try:
return self.outcomes[outcome_index]
except IndexError:
raise IndexError(
f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
)

def get_outcome_index(self, outcome: str) -> int:
try:
return self.outcomes.index(outcome)
Expand Down
24 changes: 24 additions & 0 deletions prediction_market_agent_tooling/markets/bets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from math import ceil

from prediction_market_agent_tooling.markets.agent_market import AgentMarket
from prediction_market_agent_tooling.markets.data_models import Currency


def minimum_bet_to_win(
answer: bool, amount_to_win: float, market: AgentMarket
) -> float:
"""
Estimates the minimum bet amount to win the given amount based on the current market price.
"""
share_price = market.p_yes if answer else market.p_no
bet_amount = amount_to_win / (1 / share_price - 1)
return bet_amount


def minimum_bet_to_win_manifold(
answer: bool, amount_to_win: float, market: AgentMarket
) -> int:
if market.currency != Currency.Mana:
raise ValueError(f"Manifold bets are made in Mana. Got {market.currency}.")
# Manifold lowest bet is 1 Mana, so we need to ceil the result.
return ceil(minimum_bet_to_win(answer, amount_to_win, market))
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ManifoldMarket(BaseModel):
https://docs.manifold.markets/api#get-v0markets
"""

BET_AMOUNT_CURRENCY: Currency = Currency.Mana
BET_AMOUNT_CURRENCY: t.ClassVar[Currency] = Currency.Mana

id: str
question: str
Expand Down
3 changes: 2 additions & 1 deletion prediction_market_agent_tooling/markets/manifold/manifold.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import typing as t
from decimal import Decimal

from prediction_market_agent_tooling.gtypes import Mana
from prediction_market_agent_tooling.gtypes import Mana, Probability
from prediction_market_agent_tooling.markets.agent_market import AgentMarket
from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency
from prediction_market_agent_tooling.markets.manifold.api import (
Expand Down Expand Up @@ -36,6 +36,7 @@ def from_data_model(model: ManifoldMarket) -> "ManifoldAgentMarket":
id=model.id,
question=model.question,
outcomes=model.outcomes,
p_yes=Probability(model.pool.YES),
)

@staticmethod
Expand Down
21 changes: 12 additions & 9 deletions prediction_market_agent_tooling/markets/omen/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class OmenMarket(BaseModel):
https://aiomen.eth.limo
"""

BET_AMOUNT_CURRENCY: t.ClassVar[Currency] = Currency.xDai

id: HexAddress
title: str
collateralVolume: Wei
Expand Down Expand Up @@ -73,15 +75,16 @@ def outcomeTokenProbabilities(self) -> t.Optional[list[Probability]]:
else None
)

def get_outcome_str(self, outcome_index: int) -> str:
n_outcomes = len(self.outcomes)
if outcome_index >= n_outcomes:
raise ValueError(
f"Outcome index `{outcome_index}` not valid. There are only "
f"`{n_outcomes}` outcomes."
)
else:
return self.outcomes[outcome_index]
@property
def p_yes(self) -> Probability:
return check_not_none(
self.outcomeTokenProbabilities,
"outcomeTokenProbabilities not available",
)[self.outcomes.index(OMEN_TRUE_OUTCOME)]

@property
def p_no(self) -> Probability:
return Probability(1 - self.p_yes)

def __repr__(self) -> str:
return f"Omen's market: {self.title}"
Expand Down
1 change: 1 addition & 0 deletions prediction_market_agent_tooling/markets/omen/omen.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def from_data_model(model: OmenMarket) -> "OmenAgentMarket":
outcomes=model.outcomes,
collateral_token_contract_address_checksummed=model.collateral_token_contract_address_checksummed,
market_maker_contract_address_checksummed=model.market_maker_contract_address_checksummed,
p_yes=model.p_yes,
)

@staticmethod
Expand Down
70 changes: 70 additions & 0 deletions tests/markets/test_bets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import pytest
from web3 import Web3

from prediction_market_agent_tooling.gtypes import Probability
from prediction_market_agent_tooling.markets.bets import (
minimum_bet_to_win,
minimum_bet_to_win_manifold,
)
from prediction_market_agent_tooling.markets.manifold.manifold import (
ManifoldAgentMarket,
)
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
from prediction_market_agent_tooling.tools.web3_utils import WXDAI_CONTRACT_ADDRESS


@pytest.mark.parametrize(
"outcome, market_p_yes, amount_to_win",
[
(True, 0.68, 1),
(False, 0.68, 1),
(True, 0.7, 10),
],
)
def test_minimum_bet_to_win(
outcome: bool, market_p_yes: Probability, amount_to_win: float
) -> None:
min_bet = minimum_bet_to_win(
outcome,
amount_to_win,
OmenAgentMarket(
id="id",
question="question",
outcomes=["Yes", "No"],
p_yes=market_p_yes,
collateral_token_contract_address_checksummed=WXDAI_CONTRACT_ADDRESS,
market_maker_contract_address_checksummed=Web3.to_checksum_address(
"0xf3318C420e5e30C12786C4001D600e9EE1A7eBb1"
),
),
)
assert (
min_bet / (market_p_yes if outcome else 1 - market_p_yes)
>= min_bet + amount_to_win
)


@pytest.mark.parametrize(
"outcome, market_p_yes, amount_to_win, expected_min_bet",
[
(True, 0.68, 1, 3),
(False, 0.68, 1, 1),
],
)
def test_minimum_bet_to_win_manifold(
outcome: bool,
market_p_yes: Probability,
amount_to_win: float,
expected_min_bet: int,
) -> None:
min_bet = minimum_bet_to_win_manifold(
outcome,
amount_to_win,
ManifoldAgentMarket(
id="id",
question="question",
outcomes=["Yes", "No"],
p_yes=market_p_yes,
),
)
assert min_bet == expected_min_bet, f"Expected {expected_min_bet}, got {min_bet}."

0 comments on commit a46fc5c

Please sign in to comment.