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

Fix manifold bet profit calculation #19

Merged
merged 6 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 42 additions & 1 deletion prediction_market_agent_tooling/markets/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pydantic import BaseModel
from web3 import Web3

from prediction_market_agent_tooling.benchmark.utils import should_not_happen
from prediction_market_agent_tooling.gtypes import (
USD,
ChecksumAddress,
Expand All @@ -23,6 +24,12 @@ class Currency(str, Enum):
Mana = "Mana"


class Resolution(str, Enum):
YES = "YES"
NO = "NO"
CANCEL = "CANCEL"


class BetAmount(BaseModel):
amount: Decimal
currency: Currency
Expand Down Expand Up @@ -156,7 +163,7 @@ class ManifoldMarket(BaseModel):
isResolved: bool
resolution: t.Optional[str] = None
resolutionTime: t.Optional[datetime] = None
lastBetTime: datetime
lastBetTime: t.Optional[datetime] = None
lastCommentTime: t.Optional[datetime] = None
lastUpdatedTime: datetime
mechanism: str
Expand Down Expand Up @@ -184,6 +191,16 @@ def to_agent_market(self) -> "AgentMarket":
original_market=self,
)

def get_resolution_enum(self) -> Resolution:
return Resolution(self.resolution)

def is_resolved_non_cancelled(self) -> bool:
return (
self.isResolved
and self.resolutionTime is not None
and self.get_resolution_enum() != Resolution.CANCEL
)

def __repr__(self) -> str:
return f"Manifold's market: {self.question}"

Expand Down Expand Up @@ -235,6 +252,9 @@ class ManifoldBetFees(BaseModel):
liquidityFee: Decimal
creatorFee: Decimal

def get_total(self) -> Decimal:
return Decimal(sum([self.platformFee, self.liquidityFee, self.creatorFee]))
Comment on lines +255 to +256
Copy link

Choose a reason for hiding this comment

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

The implementation of get_total in the ManifoldBetFees class correctly sums up the fees. However, consider using Decimal directly on the list comprehension for precision in financial calculations.

- return Decimal(sum([self.platformFee, self.liquidityFee, self.creatorFee]))
+ return sum([self.platformFee, self.liquidityFee, self.creatorFee], start=Decimal(0))

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def get_total(self) -> Decimal:
return Decimal(sum([self.platformFee, self.liquidityFee, self.creatorFee]))
def get_total(self) -> Decimal:
return sum([self.platformFee, self.liquidityFee, self.creatorFee], start=Decimal(0))



class ManifoldBet(BaseModel):
"""
Expand All @@ -257,6 +277,27 @@ class ManifoldBet(BaseModel):
createdTime: datetime
outcome: str

def get_resolved_boolean_outcome(self) -> bool:
outcome = Resolution(self.outcome)
if outcome == Resolution.YES:
return True
elif outcome == Resolution.NO:
return False
else:
should_not_happen(f"Unexpected bet outcome string, '{outcome.value}'.")

def get_profit(self, market_outcome: bool) -> ProfitAmount:
profit = (
self.shares - self.amount
if self.get_resolved_boolean_outcome() == market_outcome
else -self.amount
)
profit -= self.fees.get_total()
return ProfitAmount(
amount=profit,
currency=Currency.Mana,
)


class ManifoldContractMetric(BaseModel):
"""
Expand Down
20 changes: 9 additions & 11 deletions prediction_market_agent_tooling/markets/manifold.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
ManifoldContractMetric,
ManifoldMarket,
ManifoldUser,
ProfitAmount,
Resolution,
ResolvedBet,
)

Expand Down Expand Up @@ -113,30 +113,28 @@ def get_resolved_manifold_bets(
bets = [b for b in bets if b.createdTime >= start_time]
if end_time:
bets = [b for b in bets if b.createdTime < end_time]
bets = [b for b in bets if get_manifold_market(b.contractId).isResolved]
bets = [
b for b in bets if get_manifold_market(b.contractId).is_resolved_non_cancelled()
]
return bets


def manifold_to_generic_resolved_bet(bet: ManifoldBet) -> ResolvedBet:
market = get_manifold_market(bet.contractId)
if not market.isResolved:
if not market.is_resolved_non_cancelled():
raise ValueError(f"Market {market.id} is not resolved.")
if not market.resolutionTime:
raise ValueError(f"Market {market.id} has no resolution time.")

# Get the profit for this bet from the corresponding position
positions = get_market_positions(market.id, bet.userId)
bet_position = next(p for p in positions if p.contractId == bet.contractId)
profit = bet_position.profit

market_outcome = market.get_resolution_enum() == Resolution.YES
return ResolvedBet(
amount=BetAmount(amount=bet.amount, currency=Currency.Mana),
outcome=bet.outcome == "YES",
outcome=bet.get_resolved_boolean_outcome(),
created_time=bet.createdTime,
market_question=market.question,
market_outcome=market.resolution == "YES",
market_outcome=market_outcome,
resolved_time=market.resolutionTime,
profit=ProfitAmount(amount=profit, currency=Currency.Mana),
profit=bet.get_profit(market_outcome=market_outcome),
)


Expand Down
Loading