Skip to content

Commit

Permalink
propagate changes from #894
Browse files Browse the repository at this point in the history
  • Loading branch information
dpaiton committed Sep 25, 2023
1 parent 9310e3e commit 7a2819f
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 61 deletions.
48 changes: 14 additions & 34 deletions lib/agent0/agent0/hyperdrive/exec/execute_agent_trades.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
import logging
from typing import TYPE_CHECKING, NoReturn

from agent0.hyperdrive.state import HyperdriveActionType, HyperdriveMarketAction
from agent0.base import Quantity, TokenType
from agent0.hyperdrive.state import HyperdriveActionType, HyperdriveMarketAction, HyperdriveWalletDeltas, Long, Short
from elfpy import types
from elfpy.markets.hyperdrive import HyperdriveMarket
from elfpy.types import Quantity, TokenType
from elfpy.wallet.wallet import Long, Short
from elfpy.wallet.wallet_deltas import WalletDeltas
from ethpy.base import UnknownBlockError
from ethpy.hyperdrive import HyperdriveInterface, get_hyperdrive_market
from fixedpointmath import FixedPoint
Expand Down Expand Up @@ -58,12 +56,7 @@ async def async_execute_single_agent_trade(
float(trade_object.market_action.trade_amount),
)
try:
wallet_deltas = await async_match_contract_call_to_trade(
agent,
hyperdrive,
hyperdrive_market,
trade_object,
)
wallet_deltas = await async_match_contract_call_to_trade(agent, hyperdrive, trade_object)
agent.wallet.update(wallet_deltas)
except UnknownBlockError as exc:
logging.error(exc)
Expand Down Expand Up @@ -93,9 +86,8 @@ async def async_execute_agent_trades(
async def async_match_contract_call_to_trade(
agent: HyperdriveAgent,
hyperdrive: HyperdriveInterface,
hyperdrive_market: HyperdriveMarket,
trade_envelope: types.Trade[HyperdriveMarketAction],
) -> WalletDeltas:
) -> HyperdriveWalletDeltas:
"""Match statement that executes the smart contract trade based on the provided type.
Arguments
Expand All @@ -104,14 +96,12 @@ async def async_match_contract_call_to_trade(
Object containing a wallet address and Elfpy Agent for determining trades
hyperdrive : HyperdriveInterface
The Hyperdrive API interface object
hyperdrive_market : HyperdriveMarket
The elfpy trading market
trade_object : Trade
A specific trade requested by the given agent
Returns
-------
WalletDeltas
HyperdriveWalletDeltas
Deltas to be applied to the agent's wallet
"""
Expand All @@ -127,7 +117,7 @@ async def async_match_contract_call_to_trade(

case HyperdriveActionType.OPEN_LONG:
trade_result = await hyperdrive.async_open_long(agent, trade.trade_amount, trade.slippage_tolerance)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=-trade_result.base_amount,
unit=TokenType.BASE,
Expand All @@ -141,7 +131,7 @@ async def async_match_contract_call_to_trade(
trade_result = await hyperdrive.async_close_long(
agent, trade.trade_amount, trade.mint_time, trade.slippage_tolerance
)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=trade_result.base_amount,
unit=TokenType.BASE,
Expand All @@ -151,17 +141,12 @@ async def async_match_contract_call_to_trade(

case HyperdriveActionType.OPEN_SHORT:
trade_result = await hyperdrive.async_open_short(agent, trade.trade_amount, trade.slippage_tolerance)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=-trade_result.base_amount,
unit=TokenType.BASE,
),
shorts={
FixedPoint(trade_result.maturity_time_seconds): Short(
balance=trade_result.bond_amount,
open_share_price=hyperdrive_market.market_state.share_price,
)
},
shorts={FixedPoint(trade_result.maturity_time_seconds): Short(balance=trade_result.bond_amount)},
)

case HyperdriveActionType.CLOSE_SHORT:
Expand All @@ -170,24 +155,19 @@ async def async_match_contract_call_to_trade(
trade_result = await hyperdrive.async_close_short(
agent, trade.trade_amount, trade.mint_time, trade.slippage_tolerance
)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=trade_result.base_amount,
unit=TokenType.BASE,
),
shorts={
trade.mint_time: Short(
balance=-trade_result.bond_amount,
open_share_price=agent.wallet.shorts[trade.mint_time].open_share_price,
)
},
shorts={trade.mint_time: Short(balance=-trade_result.bond_amount)},
)

case HyperdriveActionType.ADD_LIQUIDITY:
trade_result = await hyperdrive.async_add_liquidity(
agent, trade.trade_amount, FixedPoint(min_apr), FixedPoint(max_apr)
)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=-trade_result.base_amount,
unit=TokenType.BASE,
Expand All @@ -197,7 +177,7 @@ async def async_match_contract_call_to_trade(

case HyperdriveActionType.REMOVE_LIQUIDITY:
trade_result = await hyperdrive.async_remove_liquidity(agent, trade.trade_amount)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=trade_result.base_amount,
unit=TokenType.BASE,
Expand All @@ -208,7 +188,7 @@ async def async_match_contract_call_to_trade(

case HyperdriveActionType.REDEEM_WITHDRAW_SHARE:
trade_result = await hyperdrive.async_redeem_withdraw_shares(agent, trade.trade_amount)
wallet_deltas = WalletDeltas(
wallet_deltas = HyperdriveWalletDeltas(
balance=Quantity(
amount=trade_result.base_amount,
unit=TokenType.BASE,
Expand Down
96 changes: 91 additions & 5 deletions lib/agent0/agent0/hyperdrive/exec/run_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@
import os
import warnings

import pandas as pd
from agent0 import AccountKeyConfig
from agent0.base import Quantity, TokenType
from agent0.base.config import DEFAULT_USERNAME, AgentConfig, EnvironmentConfig
from agent0.hyperdrive.state import HyperdriveWallet, Long, Short
from chainsync.db.api import balance_of, register_username
from eth_typing import BlockNumber
from ethpy import EthConfig, build_eth_config
from ethpy.base import smart_contract_read
from ethpy.hyperdrive import HyperdriveAddresses, fetch_hyperdrive_address_from_uri
from fixedpointmath import FixedPoint
from hexbytes import HexBytes
from web3.contract.contract import Contract

from .create_and_fund_user_account import create_and_fund_user_account
from .fund_agents import fund_agents
from .setup_experiment import register_username, setup_experiment
from .setup_experiment import setup_experiment
from .trade_loop import trade_if_new_block


Expand Down Expand Up @@ -67,18 +75,32 @@ def run_agents(
hyperdrive, agent_accounts = setup_experiment(
eth_config, environment_config, agent_config, account_key_config, contract_addresses
)
wallet_addrs = [str(agent.checksum_address) for agent in agent_accounts]
# set up database
if not develop:
# Ignore this check if not develop
if environment_config.username == DEFAULT_USERNAME:
# Check for default name and exit if is default
raise ValueError(
"Default username detected, please update 'username' in "
"lib/agent0/agent0/hyperdrive/config/runner_config.py"
)
# Set up postgres to write username to agent wallet addr
# initialize the postgres session
wallet_addrs = [str(agent.checksum_address) for agent in agent_accounts]
register_username(environment_config.username_register_uri, wallet_addrs, environment_config.username)
# Register wallet addresses to username
register_username(environment_config.database_api_uri, wallet_addrs, environment_config.username)
# Load existing balances
# Get existing open positions from db api server
balances = balance_of(environment_config.database_api_uri, wallet_addrs)
# Set balances of wallets based on db and chain
for agent in agent_accounts:
# TODO is this the right location for this to happen?
# On one hand, doing it here makes sense because parameters such as db uri doesn't have to
# be passed in down all the function calls when wallets are initialized.
# On the other hand, we initialize empty wallets just to overwrite here.
# Keeping here for now for later discussion
# TODO maybe this should be optional?
agent.wallet = build_wallet_positions_from_data(
agent.checksum_address, balances, hyperdrive.base_token_contract
)
# run the trades
last_executed_block = BlockNumber(0)
while True:
Expand All @@ -88,3 +110,67 @@ def run_agents(
environment_config.halt_on_errors,
last_executed_block,
)


def build_wallet_positions_from_data(
wallet_addr: str, db_balances: pd.DataFrame, base_contract: Contract
) -> HyperdriveWallet:
"""Builds a wallet position based on gathered data.
Arguments
---------
wallet_addr: str
The checksum wallet address
db_balances: pd.DataFrame
The current positions dataframe gathered from the db (from the `balance_of` api call)
base_contract: Contract
The base contract to query the base amount from
Returns
-------
HyperdriveWallet
The wallet object build from the provided data
"""
# Contract call to get base balance
base_amount: dict[str, int] = smart_contract_read(base_contract, "balanceOf", wallet_addr)
# TODO do we need to do error checking here?
assert "value" in base_amount
base_obj = Quantity(amount=FixedPoint(scaled_value=base_amount["value"]), unit=TokenType.BASE)

# TODO We can also get lp and withdraw shares from chain?
wallet_balances = db_balances[db_balances["walletAddress"] == wallet_addr]

# Get longs
long_balances = wallet_balances[wallet_balances["baseTokenType"] == "LONG"]
long_obj = {}
for _, row in long_balances.iterrows():
long_obj[row["maturityTime"]] = Long(balance=FixedPoint(row["value"]))

short_balances = wallet_balances[wallet_balances["baseTokenType"] == "SHORT"]
short_obj = {}
for _, row in short_balances.iterrows():
short_obj[row["maturityTime"]] = Short(balance=FixedPoint(row["value"]))

lp_balances = wallet_balances[wallet_balances["baseTokenType"] == "LP"]
assert len(lp_balances) <= 1
if len(lp_balances) == 0:
lp_obj = FixedPoint(0)
else:
lp_obj = FixedPoint(lp_balances.iloc[0]["value"])

withdraw_balances = wallet_balances[wallet_balances["baseTokenType"] == "WITHDRAWAL_SHARE"]
assert len(withdraw_balances) <= 1
if len(withdraw_balances) == 0:
withdraw_obj = FixedPoint(0)
else:
withdraw_obj = FixedPoint(withdraw_balances.iloc[0]["value"])
# TODO Build withdraw share object

return HyperdriveWallet(
address=HexBytes(wallet_addr),
balance=base_obj,
lp_tokens=lp_obj,
withdraw_shares=withdraw_obj,
longs=long_obj,
shorts=short_obj,
)
22 changes: 0 additions & 22 deletions lib/agent0/agent0/hyperdrive/exec/setup_experiment.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
"""Setup helper function for running eth agent experiments."""
from __future__ import annotations

from http import HTTPStatus

import numpy as np
import requests
from agent0 import AccountKeyConfig
from agent0.base.config import AgentConfig, EnvironmentConfig
from agent0.hyperdrive.agents import HyperdriveAgent
Expand Down Expand Up @@ -71,22 +68,3 @@ def setup_experiment(
rng,
)
return hyperdrive, agent_accounts


def register_username(register_uri: str, wallet_addrs: list[str], username: str) -> None:
"""Registers the username with the flask server.
Arguments
---------
register_uri: str
The endpoint for the flask server.
wallet_addrs: list[str]
The list of wallet addresses to register.
username: str
The username to register the wallet addresses under.
"""
# TODO: use the json schema from the server.
json_data = {"wallet_addrs": wallet_addrs, "username": username}
result = requests.post(f"{register_uri}/register_agents", json=json_data, timeout=3)
if result.status_code != HTTPStatus.OK:
raise ConnectionError(result)

0 comments on commit 7a2819f

Please sign in to comment.