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

add interface functions for calculating market estimates #1622

Merged
merged 5 commits into from
Aug 5, 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
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ _now, we have completed our feature, so we create a PR to merge the branch into

Once the PR is approved, we perform a final rebase, if necessary, and then a _squash merge_.
This means each PR results in a single commit to `main`.
Please provide a brief description of the PR in the summary, as opposed to a list of commit strings.
Please provide a brief description of the PR in the summary instead of a list of commit strings.

## Building with new Hyperdrive contracts

If you wish to incorporate modifications made to the Hyperdrive contracts, you must update agent0 by:

- Replace the Solidity contract ABIs in `packages/hyperdrive/src/abis` with the newly compiled files from Hyperdrive.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ dependencies = [
"flask",
"flask-expects-json",
"hexbytes",
"hyperdrivepy==0.16.10",
"hyperdrivepy==0.17.0",
"ipython",
"matplotlib",
"mplfinance",
Expand Down
32 changes: 9 additions & 23 deletions src/agent0/chainsync/analysis/calc_position_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,9 @@ def calc_single_closeout(
# Rust Panic Exceptions are base exceptions, not Exceptions
except BaseException as exception: # pylint: disable=broad-except
logging.info(
"Chainsync: Exception caught in calculating close long: %s\nApproximating with spot price.", exception
"Chainsync: Exception caught in calculating close long: %s\nUsing an approximation.", exception
)

# TODO: We can use the rust `calculate_market_value_*` functions once
# https://github.com/delvtech/hyperdrive-rs/pull/153 is merged.
# Long value = users_longs * spot_price * term_remaining
normalized_time_remaining = _calc_scaled_normalized_time_remaining(
FixedPoint(maturity),
FixedPoint(hyperdrive_state.checkpoint_time),
FixedPoint(hyperdrive_state.pool_config.position_duration),
)
fp_out_value = amount * interface.calc_spot_price(hyperdrive_state) * normalized_time_remaining
fp_out_value = interface.calc_market_value_long(amount, maturity, hyperdrive_state)

elif position["token_type"] == "SHORT":
# Get the open share price from the checkpoint lookup
Expand Down Expand Up @@ -178,19 +169,14 @@ def calc_single_closeout(
# Rust Panic Exceptions are base exceptions, not Exceptions
except BaseException as exception: # pylint: disable=broad-except
logging.info(
"Chainsync: Exception caught in calculating close short: %s\nApproximating with spot price.", exception
)

# TODO: We can use the rust `calculate_market_value_*` functions once
# https://github.com/delvtech/hyperdrive-rs/pull/153 is merged.
# Short value = users_shorts * ( 1 - spot_price ) * term_remaining
normalized_time_remaining = _calc_scaled_normalized_time_remaining(
FixedPoint(maturity),
FixedPoint(hyperdrive_state.checkpoint_time),
FixedPoint(hyperdrive_state.pool_config.position_duration),
"Chainsync: Exception caught in calculating close short: %s\nUsing an approximation.", exception
)
fp_out_value = (
amount * (FixedPoint(1) - interface.calc_spot_price(hyperdrive_state)) * normalized_time_remaining
fp_out_value = interface.calc_market_value_short(
amount,
open_share_price,
close_share_price,
maturity,
hyperdrive_state,
)

# For PNL, we assume all withdrawal shares are redeemable
Expand Down
66 changes: 43 additions & 23 deletions src/agent0/ethpy/hyperdrive/interface/_mock_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,14 @@ def _calc_open_long(pool_state: PoolState, base_amount: FixedPoint) -> FixedPoin
return FixedPoint(scaled_value=int(long_amount))


def _calc_pool_deltas_after_open_long(pool_state: PoolState, base_amount: FixedPoint) -> FixedPoint:
def _calc_pool_deltas_after_open_long(pool_state: PoolState, base_amount: FixedPoint) -> tuple[FixedPoint, FixedPoint]:
"""See API for documentation."""
return FixedPoint(
scaled_value=int(
hyperdrivepy.calculate_pool_deltas_after_open_long(
fixedpoint_to_pool_config(pool_state.pool_config),
fixedpoint_to_pool_info(pool_state.pool_info),
str(base_amount.scaled_value),
)
)
deltas = hyperdrivepy.calculate_pool_deltas_after_open_long(
fixedpoint_to_pool_config(pool_state.pool_config),
fixedpoint_to_pool_info(pool_state.pool_info),
str(base_amount.scaled_value),
)
return (FixedPoint(scaled_value=int(deltas[0])), FixedPoint(scaled_value=int(deltas[1])))


def _calc_spot_price_after_long(
Expand Down Expand Up @@ -226,6 +223,20 @@ def _calc_close_long(
return FixedPoint(scaled_value=int(long_returns))


def _calc_market_value_long(
pool_state: PoolState, bond_amount: FixedPoint, maturity_time: int, current_time: int
) -> FixedPoint:
"""See API for documentation."""
long_returns = hyperdrivepy.calculate_market_value_long(
fixedpoint_to_pool_config(pool_state.pool_config),
fixedpoint_to_pool_info(pool_state.pool_info),
str(bond_amount.scaled_value),
str(maturity_time),
str(current_time),
)
return FixedPoint(scaled_value=int(long_returns))


def _calc_open_short(
pool_state: PoolState,
bond_amount: FixedPoint,
Expand All @@ -246,24 +257,12 @@ def _calc_open_short(
return FixedPoint(scaled_value=int(short_deposit))


def _calculate_pool_deltas_after_open_short(pool_state: PoolState, bond_amount: FixedPoint) -> FixedPoint:
return FixedPoint(
scaled_value=int(
hyperdrivepy.calculate_pool_deltas_after_open_short(
fixedpoint_to_pool_config(pool_state.pool_config),
fixedpoint_to_pool_info(pool_state.pool_info),
str(bond_amount.scaled_value),
)
)
)


def _calc_pool_deltas_after_open_short(
def _calc_pool_share_delta_after_open_short(
pool_state: PoolState,
short_amount: FixedPoint,
) -> FixedPoint:
"""See API for documentation."""
short_deposit = hyperdrivepy.calculate_pool_deltas_after_open_short(
short_deposit = hyperdrivepy.calculate_pool_share_delta_after_open_short(
fixedpoint_to_pool_config(pool_state.pool_config),
fixedpoint_to_pool_info(pool_state.pool_info),
str(short_amount.scaled_value),
Expand Down Expand Up @@ -327,6 +326,27 @@ def _calc_close_short(
return FixedPoint(scaled_value=int(short_returns))


def _calc_market_value_short(
pool_state: PoolState,
bond_amount: FixedPoint,
open_vault_share_price: FixedPoint,
close_vault_share_price: FixedPoint,
maturity_time: int,
) -> FixedPoint:
"""See API for documentation."""
current_block_time = pool_state.block_time
short_returns = hyperdrivepy.calculate_market_value_short(
fixedpoint_to_pool_config(pool_state.pool_config),
fixedpoint_to_pool_info(pool_state.pool_info),
str(bond_amount.scaled_value),
str(open_vault_share_price.scaled_value),
str(close_vault_share_price.scaled_value),
str(maturity_time),
str(current_block_time),
)
return FixedPoint(scaled_value=int(short_returns))


def _calc_present_value(pool_state: PoolState, current_block_timestamp: int) -> FixedPoint:
"""See API for documentation."""
return FixedPoint(
Expand Down
77 changes: 70 additions & 7 deletions src/agent0/ethpy/hyperdrive/interface/read_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@
_calc_close_short,
_calc_effective_share_reserves,
_calc_idle_share_reserves_in_base,
_calc_market_value_long,
_calc_market_value_short,
_calc_max_long,
_calc_max_short,
_calc_max_spot_price,
_calc_open_long,
_calc_open_short,
_calc_pool_deltas_after_open_long,
_calc_pool_deltas_after_open_short,
_calc_pool_share_delta_after_open_short,
_calc_position_duration_in_years,
_calc_present_value,
_calc_shares_in_given_bonds_out_down,
Expand Down Expand Up @@ -1157,7 +1159,7 @@ def calc_open_long(self, base_amount: FixedPoint, pool_state: PoolState | None =

def calc_pool_deltas_after_open_long(
self, base_amount: FixedPoint, pool_state: PoolState | None = None
) -> FixedPoint:
) -> tuple[FixedPoint, FixedPoint]:
"""Calculate the bond deltas to be applied to the pool after opening a long.

Arguments
Expand All @@ -1170,8 +1172,8 @@ def calc_pool_deltas_after_open_long(

Returns
-------
FixedPoint
The amount of bonds to remove from the pool reserves.
tuple[FixedPoint, FixedPoint]
The amount of (shares, bonds) to remove from the pool reserves.
"""
if pool_state is None:
pool_state = self.current_pool_state
Expand Down Expand Up @@ -1280,6 +1282,30 @@ def calc_close_long(
pool_state = self.current_pool_state
return _calc_close_long(pool_state, bond_amount, maturity_time, int(pool_state.block_time))

def calc_market_value_long(
self, bond_amount: FixedPoint, maturity_time: int, pool_state: PoolState | None = None
) -> FixedPoint:
"""Calculate the amount of shares that will be returned after fees for closing a long.

Arguments
---------
bond_amount: FixedPoint
The amount of bonds to sell.
maturity_time: int
The maturity time of the bond.
pool_state: PoolState | None, optional
The state of the pool, which includes block details, pool config, and pool info.
If not given, use the current pool state.

Returns
-------
FixedPoint
An estimate of the amount of shares returned upon closing the long.
"""
if pool_state is None:
pool_state = self.current_pool_state
return _calc_market_value_long(pool_state, bond_amount, maturity_time, int(pool_state.block_time))

def calc_targeted_long(
self,
budget: FixedPoint,
Expand Down Expand Up @@ -1338,7 +1364,7 @@ def calc_open_short(self, bond_amount: FixedPoint, pool_state: PoolState | None
pool_state = self.current_pool_state
return _calc_open_short(pool_state, bond_amount, pool_state.pool_info.vault_share_price)

def calc_pool_deltas_after_open_short(
def calc_pool_share_delta_after_open_short(
self, bond_amount: FixedPoint, pool_state: PoolState | None = None
) -> FixedPoint:
"""Calculate the amount of shares the pool will add after opening a short.
Expand All @@ -1354,11 +1380,11 @@ def calc_pool_deltas_after_open_short(
Returns
-------
FixedPoint
The amount of base to add to the pool share reserves.
The amount of shares to add to the pool reserves.
"""
if pool_state is None:
pool_state = self.current_pool_state
return _calc_pool_deltas_after_open_short(pool_state, bond_amount)
return _calc_pool_share_delta_after_open_short(pool_state, bond_amount)

def calc_spot_price_after_short(
self, bond_amount: FixedPoint, base_amount: FixedPoint | None = None, pool_state: PoolState | None = None
Expand Down Expand Up @@ -1448,6 +1474,43 @@ def calc_close_short(
pool_state, bond_amount, open_vault_share_price, close_vault_share_price, maturity_time
)

def calc_market_value_short(
self,
bond_amount: FixedPoint,
open_vault_share_price: FixedPoint,
close_vault_share_price: FixedPoint,
maturity_time: int,
pool_state: PoolState | None = None,
) -> FixedPoint:
"""Estimates the current market value of an open short position.

Arguments
---------
bond_amount: FixedPoint
The amount to of bonds provided.
open_vault_share_price: FixedPoint
The checkpoint share price when the short was opened.
close_vault_share_price: FixedPoint
The share price when the short was closed.
If the short isn't mature, this is the current share price.
If the short is mature, this is the share price of the maturity checkpoint.
maturity_time: int
The maturity time of the short.
pool_state: PoolState | None, optional
The state of the pool, which includes block details, pool config, and pool info.
If not given, use the current pool state.

Returns
-------
FixedPoint
An estimate of the amount of shares returned upon closing the short.
"""
if pool_state is None:
pool_state = self.current_pool_state
return _calc_market_value_short(
pool_state, bond_amount, open_vault_share_price, close_vault_share_price, maturity_time
)

def calc_present_value(self, pool_state: PoolState | None = None) -> FixedPoint:
"""Calculate the present value of LPs capital in the pool.

Expand Down
4 changes: 2 additions & 2 deletions src/agent0/ethpy/hyperdrive/interface/read_interface_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def test_calc_short(self, hyperdrive_read_interface_fixture: HyperdriveReadInter
bond_amount = FixedPoint(100)
price_with_default = hyperdrive_read_interface_fixture.calc_spot_price_after_short(bond_amount)
base_amount = (
hyperdrive_read_interface_fixture.calc_pool_deltas_after_open_short(bond_amount)
hyperdrive_read_interface_fixture.calc_pool_share_delta_after_open_short(bond_amount)
* hyperdrive_read_interface_fixture.current_pool_state.pool_info.vault_share_price
)
price_with_base_amount = hyperdrive_read_interface_fixture.calc_spot_price_after_short(bond_amount, base_amount)
Expand All @@ -140,7 +140,7 @@ def test_calc_short(self, hyperdrive_read_interface_fixture: HyperdriveReadInter
close_vault_share_price=hyperdrive_read_interface_fixture.current_pool_state.pool_info.vault_share_price,
maturity_time=current_time + 100,
)
_ = hyperdrive_read_interface_fixture.calc_pool_deltas_after_open_short(bond_amount)
_ = hyperdrive_read_interface_fixture.calc_pool_share_delta_after_open_short(bond_amount)

def test_misc(self, hyperdrive_read_interface_fixture: HyperdriveReadInterface):
"""Miscellaneous tests only verify that the attributes exist and functions can be called.
Expand Down
2 changes: 1 addition & 1 deletion tests/bot_wallet_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest
from fixedpointmath import FixedPoint
from utils import run_with_funded_bot
from utils import run_with_funded_bot # type: ignore

from agent0.core.base import Trade
from agent0.core.hyperdrive import HyperdriveMarketAction, HyperdriveWallet
Expand Down
2 changes: 1 addition & 1 deletion tests/invalid_balance_trade_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pytest
from fixedpointmath import FixedPoint
from utils import expect_failure_with_funded_bot, expect_failure_with_non_funded_bot
from utils import expect_failure_with_funded_bot, expect_failure_with_non_funded_bot # type: ignore
from web3.exceptions import ContractCustomError, ContractPanicError

from agent0.core.base import Trade
Expand Down
2 changes: 1 addition & 1 deletion tests/min_txn_amount_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pytest
from fixedpointmath import FixedPoint
from utils import expect_failure_with_funded_bot
from utils import expect_failure_with_funded_bot # type: ignore
dpaiton marked this conversation as resolved.
Show resolved Hide resolved
from web3.exceptions import ContractCustomError

from agent0.core.base import Trade
Expand Down
2 changes: 1 addition & 1 deletion tests/slippage_warning_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pytest
from fixedpointmath import FixedPoint
from utils import expect_failure_with_funded_bot, run_with_funded_bot
from utils import expect_failure_with_funded_bot, run_with_funded_bot # type: ignore

from agent0.core.hyperdrive.agent import (
add_liquidity_trade,
Expand Down
Loading