Skip to content

Commit

Permalink
Allow dynamic bet amounts based on market - remove 'adjust_bet_amount' (
Browse files Browse the repository at this point in the history
  • Loading branch information
evangriffiths authored Sep 25, 2024
1 parent c01d2ef commit ca6d9cb
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 43 deletions.
17 changes: 14 additions & 3 deletions prediction_market_agent_tooling/deploy/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ class DeployableTraderAgent(DeployableAgent):
bet_on_n_markets_per_run: int = 1
min_required_balance_to_operate: xDai | None = xdai_type(1)
min_balance_to_keep_in_native_currency: xDai | None = xdai_type(0.1)
strategy: BettingStrategy = MaxAccuracyBettingStrategy()

def __init__(
self,
Expand All @@ -292,6 +291,15 @@ def __init__(
super().__init__(enable_langfuse=enable_langfuse)
self.place_bet = place_bet

def get_betting_strategy(self, market: AgentMarket) -> BettingStrategy:
user_id = market.get_user_id(api_keys=APIKeys())

total_amount = market.get_user_balance(user_id=user_id) * 0.1
if existing_position := market.get_position(user_id=user_id):
total_amount += existing_position.total_amount.amount

return MaxAccuracyBettingStrategy(bet_amount=total_amount)

def initialize_langfuse(self) -> None:
super().initialize_langfuse()
# Auto-observe all the methods where it makes sense, so that subclassses don't need to do it manually.
Expand Down Expand Up @@ -410,7 +418,8 @@ def build_trades(
answer: ProbabilisticAnswer,
existing_position: Position | None,
) -> list[Trade]:
trades = self.strategy.calculate_trades(existing_position, answer, market)
strategy = self.get_betting_strategy(market=market)
trades = strategy.calculate_trades(existing_position, answer, market)
BettingStrategy.assert_trades_currency_match_markets(market, trades)
return trades

Expand Down Expand Up @@ -443,7 +452,9 @@ def process_market(

existing_position = market.get_position(user_id=APIKeys().bet_from_address)
trades = self.build_trades(
market=market, answer=answer, existing_position=existing_position
market=market,
answer=answer,
existing_position=existing_position,
)

if self.place_bet:
Expand Down
40 changes: 5 additions & 35 deletions prediction_market_agent_tooling/deploy/betting_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ def calculate_trades(
def build_zero_token_amount(self, currency: Currency) -> TokenAmount:
return TokenAmount(amount=0, currency=currency)

@abstractmethod
def adjust_bet_amount(
self, existing_position: Position | None, market: AgentMarket
) -> float:
pass

@staticmethod
def assert_trades_currency_match_markets(
market: AgentMarket, trades: list[Trade]
Expand Down Expand Up @@ -99,20 +93,7 @@ def _build_rebalance_trades_from_positions(


class MaxAccuracyBettingStrategy(BettingStrategy):
def adjust_bet_amount(
self, existing_position: Position | None, market: AgentMarket
) -> float:
existing_position_total_amount = (
existing_position.total_amount.amount if existing_position else 0
)
bet_amount = (
market.get_tiny_bet_amount().amount
if self.bet_amount is None
else self.bet_amount
)
return bet_amount + existing_position_total_amount

def __init__(self, bet_amount: float | None = None):
def __init__(self, bet_amount: float):
self.bet_amount = bet_amount

def calculate_trades(
Expand All @@ -121,13 +102,11 @@ def calculate_trades(
answer: ProbabilisticAnswer,
market: AgentMarket,
) -> list[Trade]:
adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)

direction = self.calculate_direction(market.current_p_yes, answer.p_yes)

amounts = {
market.get_outcome_str_from_bool(direction): TokenAmount(
amount=adjusted_bet_amount,
amount=self.bet_amount,
currency=market.currency,
),
}
Expand Down Expand Up @@ -155,24 +134,15 @@ def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool:


class KellyBettingStrategy(BettingStrategy):
def __init__(self, max_bet_amount: float = 10):
def __init__(self, max_bet_amount: float):
self.max_bet_amount = max_bet_amount

def adjust_bet_amount(
self, existing_position: Position | None, market: AgentMarket
) -> float:
existing_position_total_amount = (
existing_position.total_amount.amount if existing_position else 0
)
return self.max_bet_amount + existing_position_total_amount

def calculate_trades(
self,
existing_position: Position | None,
answer: ProbabilisticAnswer,
market: AgentMarket,
) -> list[Trade]:
adjusted_bet_amount = self.adjust_bet_amount(existing_position, market)
outcome_token_pool = check_not_none(market.outcome_token_pool)
kelly_bet = (
get_kelly_bet_full(
Expand All @@ -183,12 +153,12 @@ def calculate_trades(
market.get_outcome_str_from_bool(False)
],
estimated_p_yes=answer.p_yes,
max_bet=adjusted_bet_amount,
max_bet=self.max_bet_amount,
confidence=answer.confidence,
)
if market.has_token_pool()
else get_kelly_bet_simplified(
adjusted_bet_amount,
self.max_bet_amount,
market.current_p_yes,
answer.p_yes,
answer.confidence,
Expand Down
3 changes: 2 additions & 1 deletion prediction_market_agent_tooling/jobs/omen/omen_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ def compute_job_reward(
market: OmenAgentMarket, max_bond: float, web3: Web3 | None = None
) -> float:
# Because jobs are powered by prediction markets, potentional reward depends on job's liquidity and our will to bond (bet) our xDai into our job completion.
required_trades = KellyBettingStrategy(max_bet_amount=max_bond).calculate_trades(
strategy = KellyBettingStrategy(max_bet_amount=max_bond)
required_trades = strategy.calculate_trades(
existing_position=None,
# We assume that we finish the job and so the probability of the market happening will be 100%.
answer=ProbabilisticAnswer(p_yes=Probability(1.0), confidence=1.0),
Expand Down
8 changes: 8 additions & 0 deletions prediction_market_agent_tooling/markets/agent_market.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,11 @@ def get_pool_tokens(self, outcome: str) -> float:
raise ValueError("Outcome token pool is not available.")

return self.outcome_token_pool[outcome]

@staticmethod
def get_user_balance(user_id: str) -> float:
raise NotImplementedError("Subclasses must implement this method")

@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
raise NotImplementedError("Subclasses must implement this method")
4 changes: 4 additions & 0 deletions prediction_market_agent_tooling/markets/manifold/manifold.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,7 @@ def get_binary_markets(
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
return get_authenticated_user(keys.manifold_api_key.get_secret_value()).url

@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
return api_keys.manifold_user_id
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing as t
from datetime import datetime

from prediction_market_agent_tooling.config import APIKeys
from prediction_market_agent_tooling.gtypes import Probability
from prediction_market_agent_tooling.markets.agent_market import (
AgentMarket,
Expand Down Expand Up @@ -104,3 +105,7 @@ def get_binary_markets(
def submit_prediction(self, p_yes: Probability, reasoning: str) -> None:
make_prediction(self.id, p_yes)
post_question_comment(self.id, reasoning)

@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
return str(api_keys.metaculus_user_id)
8 changes: 8 additions & 0 deletions prediction_market_agent_tooling/markets/omen/omen.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,14 @@ def get_new_p_yes(self, bet_amount: BetAmount, direction: bool) -> Probability:
)
return Probability(new_p_yes)

@staticmethod
def get_user_balance(user_id: str) -> float:
return float(get_balances(Web3.to_checksum_address(user_id)).total)

@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
return api_keys.bet_from_address


def get_omen_user_url(address: ChecksumAddress) -> str:
return f"https://gnosisscan.io/address/{address}"
Expand Down
4 changes: 4 additions & 0 deletions prediction_market_agent_tooling/monitor/markets/polymarket.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ def from_all_gcp_functions(
== MarketType.POLYMARKET.value,
) -> t.Sequence["DeployedPolymarketAgent"]:
return super().from_all_gcp_functions(filter_=filter_)

@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
return api_keys.bet_from_address
8 changes: 4 additions & 4 deletions tests/test_betting_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@
def test_answer_decision(
estimate_p_yes: float, market_p_yes: float, expected_direction: bool
) -> None:
betting_strategy = MaxAccuracyBettingStrategy()
betting_strategy = MaxAccuracyBettingStrategy(bet_amount=0.1)
direction: bool = betting_strategy.calculate_direction(market_p_yes, estimate_p_yes)
assert direction == expected_direction


def test_rebalance() -> None:
strategy = MaxAccuracyBettingStrategy()

mock_amount = TokenAmount(amount=5, currency=Currency.xDai)
tiny_amount = TokenAmount(amount=0.0001, currency=Currency.xDai)
mock_amount = TokenAmount(amount=5, currency=Currency.xDai)
mock_existing_position = Position(
market_id="0x123",
amounts={
OmenAgentMarket.get_outcome_str_from_bool(True): mock_amount,
OmenAgentMarket.get_outcome_str_from_bool(False): mock_amount,
},
)
bet_amount = tiny_amount.amount + mock_existing_position.total_amount.amount
strategy = MaxAccuracyBettingStrategy(bet_amount=bet_amount)
mock_answer = ProbabilisticAnswer(p_yes=Probability(0.9), confidence=0.5)
mock_market = Mock(OmenAgentMarket, wraps=OmenAgentMarket)
mock_market.get_tiny_bet_amount.return_value = tiny_amount
Expand Down

0 comments on commit ca6d9cb

Please sign in to comment.