Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BuyYes and BuyNo functions with Omen API #54

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 43 additions & 41 deletions prediction_market_agent/agents/microchain_agent/functions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import pprint
import typing as t
from decimal import Decimal

from microchain import Function
from prediction_market_agent_tooling.markets.agent_market import (
AgentMarket,
FilterBy,
SortBy,
)
from prediction_market_agent_tooling.markets.data_models import BetAmount, Currency
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket

from prediction_market_agent.agents.microchain_agent.utils import (
get_omen_binary_market_from_question,
get_omen_binary_markets,
get_omen_market_token_balance,
)

balance = 50
outcomeTokens = {}
outcomeTokens["Will Joe Biden get reelected in 2024?"] = {"yes": 0, "no": 0}
Expand Down Expand Up @@ -51,13 +54,7 @@ def example_args(self) -> list[str]:
return []

def __call__(self) -> list[str]:
# Get the 5 markets that are closing soonest
markets: list[AgentMarket] = OmenAgentMarket.get_binary_markets(
filter_by=FilterBy.OPEN,
sort_by=SortBy.CLOSING_SOONEST,
limit=5,
)

markets: list[OmenAgentMarket] = get_omen_binary_markets()
market_questions_and_prices = []
for market in markets:
market_questions_and_prices.append(market.question)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just a style preference, but I would prefer having some kind of tuple structure, like [(market_price, market_question)] instead of a list containing different item types. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, is there a reason why we can't use some Pydantic model here?

This seems like it could work:

from pydantic import BaseModel


class MicroMarket(BaseModel):
    question: str
    p_yes: float

    def __str__(self) -> str:
        return f"'{self.question}' with probability of yes: {self.p_yes:.2%}"


mm = MicroMarket(question="Will it rain tomorrow?", p_yes=0.7)

prompt = f"""
You have following markets:

- {mm}
"""

print(prompt)

prints:

> python test.py                                                                                                                             [9:44:12]

You have following markets:

- 'Will it rain tomorrow?' with probability of yes: 70.00%

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, will do this!

I think this is still a starting point though, and we might want to do some thinking/experimenting how we want the api to look like for the agent. We could expose a bunch more things (opening time, closing time, liquidity, ...), either as extra fields of this pydantic class, or as separate Functions the agent can call.

My hunch is that separate functions makes more sense for token efficiency, but need to check.

Also, we could expose just the market Id here, and have the agent pass that into the other getter functions, so that we don't need get_omen_market_by_title here https://github.com/gnosis/prediction-market-agent-tooling/pull/172/files.

Expand Down Expand Up @@ -98,44 +95,49 @@ def __call__(self) -> float:
return balance


class BuyYes(Function):
class BuyTokens(Function):
def __init__(self, outcome: str):
self.outcome = outcome
super().__init__()

@property
def description(self) -> str:
return "Use this function to buy yes outcome tokens of a prediction market. The second parameter specifies how much $ you spend."
return f"Use this function to buy {self.outcome} outcome tokens of a prediction market. The second parameter specifies how much $ you spend."

@property
def example_args(self) -> list[t.Union[str, float]]:
return ["Will Joe Biden get reelected in 2024?", 2]

def __call__(self, market: str, amount: int) -> str:
global balance
if amount > balance:
return (
f"Your balance of {balance} $ is not large enough to spend {amount} $."
)

balance -= amount
return "Bought " + str(amount * 2) + " yes outcome token of: " + market

return ["Will Joe Biden get reelected in 2024?", 2.3]

def __call__(self, market: str, amount: float) -> str:
if self.outcome == "yes":
Copy link
Contributor

@kongzii kongzii Apr 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, fixed.

outcome_bool = True
elif self.outcome == "no":
outcome_bool = False
else:
raise ValueError(f"Invalid outcome: {self.outcome}")

market_obj: OmenAgentMarket = get_omen_binary_market_from_question(market)
before_balance = get_omen_market_token_balance(
market=market_obj, outcome=outcome_bool
)
market_obj.place_bet(
outcome_bool, BetAmount(amount=Decimal(amount), currency=Currency.xDai)
)
tokens = (
get_omen_market_token_balance(market=market_obj, outcome=outcome_bool)
- before_balance
)
return f"Bought {tokens} {self.outcome} outcome tokens of: {market}"

class BuyNo(Function):
@property
def description(self) -> str:
return "Use this function to buy no outcome tokens of a prdiction market. The second parameter specifies how much $ you spend."

@property
def example_args(self) -> list[t.Union[str, float]]:
return ["Will Joe Biden get reelected in 2024?", 4]
class BuyYes(BuyTokens):
def __init__(self) -> None:
super().__init__("yes")

def __call__(self, market: str, amount: int) -> str:
global balance
if amount > balance:
return (
f"Your balance of {balance} $ is not large enough to spend {amount} $."
)

balance -= amount
return "Bought " + str(amount * 2) + " no outcome token of: " + market
class BuyNo(BuyTokens):
def __init__(self) -> None:
super().__init__("no")


class SellYes(Function):
Expand Down
31 changes: 31 additions & 0 deletions prediction_market_agent/agents/microchain_agent/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import List, cast

from prediction_market_agent_tooling.markets.agent_market import (
AgentMarket,
FilterBy,
SortBy,
)
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket


def get_omen_binary_markets() -> list[OmenAgentMarket]:
# Get the 5 markets that are closing soonest
markets: list[AgentMarket] = OmenAgentMarket.get_binary_markets(
filter_by=FilterBy.OPEN,
sort_by=SortBy.CLOSING_SOONEST,
limit=5,
)
return cast(List[OmenAgentMarket], markets)


def get_omen_binary_market_from_question(market: str) -> OmenAgentMarket:
markets = get_omen_binary_markets()
for m in markets:
if m.question == market:
return m
raise ValueError(f"Market '{market}' not found")


def get_omen_market_token_balance(market: OmenAgentMarket, outcome: bool) -> float:
# TODO implement this
return 7.3
Loading