Skip to content

Commit

Permalink
Merge branch 'peter/gcp-secrets-labels' into peter/refactor-deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
kongzii committed Feb 15, 2024
2 parents f0ded87 + 56fe592 commit 8a659a5
Show file tree
Hide file tree
Showing 20 changed files with 933 additions and 66 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/python_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,23 @@ jobs:
- uses: ./.github/actions/python_prepare
- name: Check with black
run: poetry run black --check .

autoflake:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/python_prepare
- name: Check with autoflake
run: |
poetry run autoflake --in-place --remove-all-unused-imports --remove-unused-variables --recursive .
git diff --exit-code --quiet || exit 1
isort:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/python_prepare
- name: Check with isort
run: |
poetry run isort --profile black .
git diff --exit-code --quiet || exit 1
21 changes: 21 additions & 0 deletions examples/monitor/monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

import streamlit as st

from prediction_market_agent_tooling.markets.manifold import get_authenticated_user
from prediction_market_agent_tooling.monitor.markets.manifold import (
DeployedManifoldAgent,
)
from prediction_market_agent_tooling.monitor.monitor import monitor_agent

if __name__ == "__main__":
start_time = datetime.now() - timedelta(weeks=1)
agent = DeployedManifoldAgent(
name="foo",
start_time=start_time.astimezone(ZoneInfo("UTC")),
manifold_user_id=get_authenticated_user().id,
)
st.set_page_config(layout="wide") # Best viewed with a wide screen
st.title(f"Monitoring Agent: '{agent.name}'")
monitor_agent(agent)
535 changes: 533 additions & 2 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion prediction_market_agent_tooling/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import typing as t

from pydantic_settings import BaseSettings, SettingsConfigDict

from prediction_market_agent_tooling.gtypes import ChecksumAddress, PrivateKey
from prediction_market_agent_tooling.tools.utils import check_not_none
from prediction_market_agent_tooling.tools.web3_utils import verify_address
from prediction_market_agent_tooling.gtypes import ChecksumAddress, PrivateKey


class APIKeys(BaseSettings):
Expand Down
7 changes: 6 additions & 1 deletion prediction_market_agent_tooling/deploy/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
import tempfile
import inspect
from decimal import Decimal
from prediction_market_agent_tooling.markets.data_models import AgentMarket

from prediction_market_agent_tooling.markets.data_models import (
AgentMarket,
BetAmount,
Currency,
)
from prediction_market_agent_tooling.markets.markets import (
MarketType,
get_binary_markets,
Expand Down
10 changes: 6 additions & 4 deletions prediction_market_agent_tooling/deploy/gcp/deploy.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import os
import requests
import shutil
import subprocess
import tempfile
import typing as t
from prediction_market_agent_tooling.tools.utils import export_requirements_from_toml


import requests
from cron_validator import CronValidator

from prediction_market_agent_tooling.deploy.gcp.utils import (
gcloud_create_topic_cmd,
gcloud_delete_function_cmd,
Expand All @@ -14,8 +17,7 @@
get_gcloud_function_uri,
get_gcloud_id_token,
)
from prediction_market_agent_tooling.markets.markets import MarketType
from cron_validator import CronValidator
from prediction_market_agent_tooling.tools.utils import export_requirements_from_toml


def deploy_to_gcp(
Expand Down
1 change: 1 addition & 0 deletions prediction_market_agent_tooling/deploy/gcp/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import subprocess
import sys

from google.cloud.functions_v2.services.function_service.client import (
FunctionServiceClient,
)
Expand Down
11 changes: 6 additions & 5 deletions prediction_market_agent_tooling/gtypes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from decimal import Decimal
from typing import NewType, Union
from web3.types import Wei
from eth_typing.evm import (

from eth_typing.evm import ( # noqa: F401 # Import for the sake of easy importing with others from here.
Address,
HexStr,
HexAddress,
ChecksumAddress,
) # noqa: F401 # Import for the sake of easy importing with others from here.
HexAddress,
HexStr,
)
from web3.types import Wei

Wad = Wei # Wei tends to be referred to as `wad` variable in contracts.
USD = NewType(
Expand Down
133 changes: 126 additions & 7 deletions prediction_market_agent_tooling/markets/data_models.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import typing as t
from datetime import datetime
from decimal import Decimal
from enum import Enum
import typing as t

from pydantic import BaseModel
from web3 import Web3

from prediction_market_agent_tooling.gtypes import (
USD,
HexAddress,
ChecksumAddress,
Probability,
HexAddress,
Mana,
OmenOutcomeToken,
xDai,
Probability,
Wei,
xDai,
)
from datetime import datetime


class Currency(str, Enum):
Expand All @@ -26,6 +28,28 @@ class BetAmount(BaseModel):
currency: Currency


class ProfitAmount(BaseModel):
amount: Decimal
currency: Currency


class Bet(BaseModel):
amount: BetAmount
outcome: bool
created_time: datetime
market_question: str


class ResolvedBet(Bet):
market_outcome: bool
resolved_time: datetime
profit: ProfitAmount

@property
def is_correct(self) -> bool:
return self.outcome == self.market_outcome


class AgentMarket(BaseModel):
"""
Common market class that can be created from vendor specific markets.
Expand Down Expand Up @@ -116,7 +140,7 @@ class ManifoldPool(BaseModel):

class ManifoldMarket(BaseModel):
"""
https://manifold.markets
https://docs.manifold.markets/api#get-v0markets
"""

BET_AMOUNT_CURRENCY: Currency = Currency.Mana
Expand All @@ -130,8 +154,10 @@ class ManifoldMarket(BaseModel):
creatorName: str
creatorUsername: str
isResolved: bool
resolution: t.Optional[str] = None
resolutionTime: t.Optional[datetime] = None
lastBetTime: datetime
lastCommentTime: t.Optional[datetime] = None # Not always present
lastCommentTime: t.Optional[datetime] = None
lastUpdatedTime: datetime
mechanism: str
outcomeType: str
Expand Down Expand Up @@ -160,3 +186,96 @@ def to_agent_market(self) -> "AgentMarket":

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


class ProfitCached(BaseModel):
daily: Mana
weekly: Mana
monthly: Mana
allTime: Mana


class ManifoldUser(BaseModel):
"""
https://docs.manifold.markets/api#get-v0userusername
"""

id: str
createdTime: datetime
name: str
username: str
url: str
avatarUrl: t.Optional[str] = None
bio: t.Optional[str] = None
bannerUrl: t.Optional[str] = None
website: t.Optional[str] = None
twitterHandle: t.Optional[str] = None
discordHandle: t.Optional[str] = None
isBot: t.Optional[bool] = None
isAdmin: t.Optional[bool] = None
isTrustworthy: t.Optional[bool] = None
isBannedFromPosting: t.Optional[bool] = None
userDeleted: t.Optional[bool] = None
balance: Mana
totalDeposits: Mana
lastBetTime: t.Optional[datetime] = None
currentBettingStreak: t.Optional[int] = None
profitCached: ProfitCached


class ManifoldBetFills(BaseModel):
amount: Mana
matchedBetId: t.Optional[str]
shares: Decimal
timestamp: int


class ManifoldBetFees(BaseModel):
platformFee: Decimal
liquidityFee: Decimal
creatorFee: Decimal


class ManifoldBet(BaseModel):
"""
https://docs.manifold.markets/api#get-v0bets
"""

shares: Decimal
probBefore: Probability
isFilled: t.Optional[bool] = None
probAfter: Probability
userId: str
amount: Mana
contractId: str
id: str
fees: ManifoldBetFees
isCancelled: t.Optional[bool] = None
loanAmount: Mana
orderAmount: t.Optional[Mana] = None
fills: t.Optional[list[ManifoldBetFills]] = None
createdTime: datetime
outcome: str


class ManifoldContractMetric(BaseModel):
"""
https://docs.manifold.markets/api#get-v0marketmarketidpositions
"""

contractId: str
hasNoShares: bool
hasShares: bool
hasYesShares: bool
invested: Decimal
loan: Decimal
maxSharesOutcome: t.Optional[str]
payout: Decimal
profit: Decimal
profitPercent: Decimal
totalShares: dict[str, Decimal]
userId: str
userUsername: str
userName: str
userAvatarUrl: str
lastBetTime: datetime
81 changes: 79 additions & 2 deletions prediction_market_agent_tooling/markets/manifold.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import requests
import typing as t
from prediction_market_agent_tooling.gtypes import Mana
from datetime import datetime

import requests

from prediction_market_agent_tooling.config import APIKeys
from prediction_market_agent_tooling.gtypes import Mana
from prediction_market_agent_tooling.markets.data_models import (
BetAmount,
Currency,
ManifoldBet,
ManifoldContractMetric,
ManifoldMarket,
ManifoldUser,
ProfitAmount,
ResolvedBet,
)

"""
Expand Down Expand Up @@ -69,3 +79,70 @@ def place_bet(amount: Mana, market_id: str, outcome: bool) -> None:
raise Exception(
f"Placing bet failed: {response.status_code} {response.reason} {response.text}"
)


def get_authenticated_user() -> ManifoldUser:
url = "https://api.manifold.markets/v0/me"
headers = {
"Authorization": f"Key {APIKeys().manifold_api_key}",
"Content-Type": "application/json",
}
response = requests.get(url, headers=headers)
response.raise_for_status()
return ManifoldUser.model_validate(response.json())


def get_manifold_market(market_id: str) -> ManifoldMarket:
url = f"https://api.manifold.markets/v0/market/{market_id}"
response = requests.get(url)
response.raise_for_status()
return ManifoldMarket.model_validate(response.json())


def get_resolved_manifold_bets(
user_id: str,
start_time: datetime,
end_time: t.Optional[datetime],
) -> list[ManifoldBet]:
url = "https://api.manifold.markets/v0/bets"

params: dict[str, str] = {"userId": user_id}
response = requests.get(url, params=params)
response.raise_for_status()
bets = [ManifoldBet.model_validate(x) for x in response.json()]
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]
return bets


def manifold_to_generic_resolved_bet(bet: ManifoldBet) -> ResolvedBet:
market = get_manifold_market(bet.contractId)
if not market.isResolved:
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

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


def get_market_positions(market_id: str, user_id: str) -> list[ManifoldContractMetric]:
url = f"https://api.manifold.markets/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()]
Loading

0 comments on commit 8a659a5

Please sign in to comment.