Skip to content

Commit

Permalink
code cleanup (#967)
Browse files Browse the repository at this point in the history
* removes redundant import

* moves long functions

Functions that need to be shared by targeted_short are moved to a more
accessile location.
Functions that hae duplicate purpose are consolidated.

* adds calc_rate_after_short

* fixes return types

* fix subscript

Co-authored-by: Ryan Goree <[email protected]>

* update docstring

Co-authored-by: Ryan Goree <[email protected]>

* fixes comment

---------

Co-authored-by: Ryan Goree <[email protected]>
  • Loading branch information
dpaiton and ryangoree authored Apr 15, 2024
1 parent e95dcac commit 3360daa
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 99 deletions.
3 changes: 1 addition & 2 deletions crates/fixed-point/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,7 @@ impl UniformSampler for UniformFixedPoint {
mod tests {
use std::panic;

use eyre::Result;
use rand::{thread_rng, Rng};
use rand::thread_rng;
use test_utils::{chain::TestChain, constants::FAST_FUZZ_RUNS};

use super::*;
Expand Down
53 changes: 53 additions & 0 deletions crates/hyperdrive-math/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,59 @@ impl State {
}
}

/// Calculates the pool reserve levels to achieve a target interest rate.
/// This calculation does not take Hyperdrive's solvency constraints or exposure
/// into account and shouldn't be used directly.
///
/// The price for a given fixed-rate is given by $p = 1 / (r \cdot t + 1)$, where
/// $r$ is the fixed-rate and $t$ is the annualized position duration. The
/// price for a given pool reserves is given by $p = \frac{\mu z}{y}^{t_{s}}$,
/// where $\mu$ is the initial share price and $t_{s}$ is the time stretch
/// constant. By setting these equal we can solve for the pool reserve levels
/// as a function of a target rate.
///
/// For some target rate, $r_t$, the pool share reserves, $z_t$, must be:
///
/// $$
/// z_t = \frac{1}{\mu} \left(
/// \frac{k}{\frac{c}{\mu} + \left(
/// (r_t \cdot t + 1)^{\frac{1}{t_{s}}}
/// \right)^{1 - t_{s}}}
/// \right)^{\frac{1}{1 - t_{s}}}
/// $$
///
/// and the pool bond reserves, $y_t$, must be:
///
/// $$
/// y_t = \left(
/// \frac{k}{ \frac{c}{\mu} + \left(
/// \left( r_t \cdot t + 1 \right)^{\frac{1}{t_{s}}}
/// \right)^{1 - t_{s}}}
/// \right)^{1 - t_{s}} \left( r_t t + 1 \right)^{\frac{1}{t_{s}}}
/// $$
fn reserves_given_rate_ignoring_exposure<F: Into<FixedPoint>>(
&self,
target_rate: F,
) -> (FixedPoint, FixedPoint) {
let target_rate = target_rate.into();

// First get the target share reserves
let c_over_mu = self
.vault_share_price()
.div_up(self.initial_vault_share_price());
let scaled_rate = (target_rate.mul_up(self.annualized_position_duration()) + fixed!(1e18))
.pow(fixed!(1e18) / self.time_stretch());
let inner = (self.k_down()
/ (c_over_mu + scaled_rate.pow(fixed!(1e18) - self.time_stretch())))
.pow(fixed!(1e18) / (fixed!(1e18) - self.time_stretch()));
let target_share_reserves = inner / self.initial_vault_share_price();

// Then get the target bond reserves.
let target_bond_reserves = inner * scaled_rate;

(target_share_reserves, target_bond_reserves)
}

/// Config ///
fn position_duration(&self) -> FixedPoint {
Expand Down
11 changes: 11 additions & 0 deletions crates/hyperdrive-math/src/long/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ impl State {

/// Calculate the spot rate after a long has been opened.
/// If a bond_amount is not provided, then one is estimated using `calculate_open_long`.
///
/// We calculate the rate for a fixed length of time as:
/// $$
/// r(x) = (1 - p(x)) / (p(x) t)
/// $$
///
/// where $p(x)$ is the spot price after a long for `delta_base`$= x$ and
/// t is the normalized position druation.
///
/// In this case, we use the resulting spot price after a hypothetical long
/// for `base_amount` is opened.
pub fn calculate_spot_rate_after_long(
&self,
base_amount: FixedPoint,
Expand Down
109 changes: 21 additions & 88 deletions crates/hyperdrive-math/src/long/targeted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ impl State {
let (target_share_reserves, target_bond_reserves) =
self.reserves_given_rate_ignoring_exposure(target_rate);
let (mut target_base_delta, target_bond_delta) =
self.trade_deltas_from_reserves(target_share_reserves, target_bond_reserves);
self.long_trade_deltas_from_reserves(target_share_reserves, target_bond_reserves);

// Determine what rate was achieved.
let resulting_rate = self.rate_after_long(target_base_delta, Some(target_bond_delta))?;
let resulting_rate =
self.calculate_spot_rate_after_long(target_base_delta, Some(target_bond_delta))?;

// The estimated long will usually underestimate because the realized price
// should always be greater than the spot price.
Expand Down Expand Up @@ -112,8 +113,10 @@ impl State {
let possible_target_bond_delta = self
.calculate_open_long(possible_target_base_delta)
.unwrap();
let resulting_rate =
self.rate_after_long(possible_target_base_delta, Some(possible_target_bond_delta))?;
let resulting_rate = self.calculate_spot_rate_after_long(
possible_target_base_delta,
Some(possible_target_bond_delta),
)?;

// We assume that the loss is positive only because Newton's
// method will always underestimate.
Expand All @@ -122,6 +125,9 @@ impl State {
"We overshot the zero-crossing during Newton's method.",
));
}
// We choose the difference between the rates as the loss because it
// is convex given the above check, differentiable almost everywhere,
// and has a simple derivative.
let loss = resulting_rate - target_rate;

// If we've done it (solvent & within error), then return the value.
Expand Down Expand Up @@ -171,8 +177,10 @@ impl State {
let possible_target_bond_delta = self
.calculate_open_long(possible_target_base_delta)
.unwrap();
let resulting_rate =
self.rate_after_long(possible_target_base_delta, Some(possible_target_bond_delta))?;
let resulting_rate = self.calculate_spot_rate_after_long(
possible_target_base_delta,
Some(possible_target_bond_delta),
)?;
if target_rate > resulting_rate {
return Err(eyre!(
"We overshot the zero-crossing after Newton's method.",
Expand All @@ -189,29 +197,6 @@ impl State {
Ok(possible_target_base_delta)
}

/// The fixed rate after a long has been opened.
///
/// We calculate the rate for a fixed length of time as:
/// $$
/// r(x) = (1 - p(x)) / (p(x) t)
/// $$
///
/// where $p(x)$ is the spot price after a long for `delta_bonds`$= x$ and
/// t is the normalized position druation.
///
/// In this case, we use the resulting spot price after a hypothetical long
/// for `base_amount` is opened.
fn rate_after_long(
&self,
base_amount: FixedPoint,
maybe_bond_amount: Option<FixedPoint>,
) -> Result<FixedPoint> {
let resulting_price =
self.calculate_spot_price_after_long(base_amount, maybe_bond_amount)?;
Ok((fixed!(1e18) - resulting_price)
/ (resulting_price * self.annualized_position_duration()))
}

/// The derivative of the equation for calculating the rate after a long.
///
/// For some $r = (1 - p(x)) / (p(x) \cdot t)$, where $p(x)$
Expand Down Expand Up @@ -327,7 +312,8 @@ impl State {
* (inner_denominator / inner_numerator).pow(fixed!(1e18) - self.time_stretch()))
}

/// Calculate the base & bond deltas from the current state given desired new reserve levels.
/// Calculate the base & bond deltas for a long trade that moves the current
/// state to the given desired ending reserve levels.
///
/// Given a target ending pool share reserves, $z_t$, and bond reserves, $y_t$,
/// the trade deltas to achieve that state would be:
Expand All @@ -339,70 +325,17 @@ impl State {
///
/// where $c$ is the vault share price and
/// $c(\Delta x)$ is the (open_long_curve_fee)[long::fees::open_long_curve_fees].
fn trade_deltas_from_reserves(
fn long_trade_deltas_from_reserves(
&self,
share_reserves: FixedPoint,
bond_reserves: FixedPoint,
ending_share_reserves: FixedPoint,
ending_bond_reserves: FixedPoint,
) -> (FixedPoint, FixedPoint) {
let base_delta =
(share_reserves - self.effective_share_reserves()) * self.vault_share_price();
(ending_share_reserves - self.effective_share_reserves()) * self.vault_share_price();
let bond_delta =
(self.bond_reserves() - bond_reserves) - self.open_long_curve_fees(base_delta);
(self.bond_reserves() - ending_bond_reserves) - self.open_long_curve_fees(base_delta);
(base_delta, bond_delta)
}

/// Calculates the pool reserve levels to achieve a target interest rate.
/// This calculation does not take Hyperdrive's solvency constraints or exposure
/// into account and shouldn't be used directly.
///
/// The price for a given fixed-rate is given by $p = 1 / (r \cdot t + 1)$, where
/// $r$ is the fixed-rate and $t$ is the annualized position duration. The
/// price for a given pool reserves is given by $p = \frac{\mu z}{y}^t_{s}$,
/// where $\mu$ is the initial share price and $t_{s}$ is the time stretch
/// constant. By setting these equal we can solve for the pool reserve levels
/// as a function of a target rate.
///
/// For some target rate, $r_t$, the pool share reserves, $z_t$, must be:
///
/// $$
/// z_t = \frac{1}{\mu} \left(
/// \frac{k}{\frac{c}{\mu} + \left(
/// (r_t \cdot t + 1)^{\frac{1}{t_{s}}}
/// \right)^{1 - t_{s}}}
/// \right)^{\tfrac{1}{1 - t_{s}}}
/// $$
///
/// and the pool bond reserves, $y_t$, must be:
///
/// $$
/// y_t = \left(
/// \frac{k}{ \frac{c}{\mu} + \left(
/// \left( r_t \cdot t + 1 \right)^{\frac{1}{t_{s}}}
/// \right)^{1 - t_{s}}}
/// \right)^{1 - t_{s}} \left( r_t t + 1 \right)^{\frac{1}{t_{s}}}
/// $$
fn reserves_given_rate_ignoring_exposure<F: Into<FixedPoint>>(
&self,
target_rate: F,
) -> (FixedPoint, FixedPoint) {
let target_rate = target_rate.into();

// First get the target share reserves
let c_over_mu = self
.vault_share_price()
.div_up(self.initial_vault_share_price());
let scaled_rate = (target_rate.mul_up(self.annualized_position_duration()) + fixed!(1e18))
.pow(fixed!(1e18) / self.time_stretch());
let inner = (self.k_down()
/ (c_over_mu + scaled_rate.pow(fixed!(1e18) - self.time_stretch())))
.pow(fixed!(1e18) / (fixed!(1e18) - self.time_stretch()));
let target_share_reserves = inner / self.initial_vault_share_price();

// Then get the target bond reserves.
let target_bond_reserves = inner * scaled_rate;

(target_share_reserves, target_bond_reserves)
}
}

#[cfg(test)]
Expand Down
27 changes: 26 additions & 1 deletion crates/hyperdrive-math/src/short/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use eyre::{eyre, Result};
use fixed_point::FixedPoint;
use fixed_point_macros::fixed;

use crate::{State, YieldSpace};
use crate::{calculate_rate_given_fixed_price, State, YieldSpace};

impl State {
/// Calculates the amount of base the trader will need to deposit for a short of
Expand Down Expand Up @@ -84,6 +84,31 @@ impl State {
Ok(state.calculate_spot_price())
}

/// Calculate the spot rate after a short has been opened.
/// If a base_amount is not provided, then one is estimated using `calculate_open_short`.
///
/// We calculate the rate for a fixed length of time as:
/// $$
/// r(y) = (1 - p(y)) / (p(y) t)
/// $$
///
/// where $p(y)$ is the spot price after a short for `delta_bonds`$= y$ and
/// t is the normalized position druation.
///
/// In this case, we use the resulting spot price after a hypothetical short
/// for `bond_amount` is opened.
pub fn calculate_spot_rate_after_short(
&self,
bond_amount: FixedPoint,
base_amount: Option<FixedPoint>,
) -> Result<FixedPoint> {
let price = self.calculate_spot_price_after_short(bond_amount, base_amount)?;
Ok(calculate_rate_given_fixed_price(
price,
self.position_duration(),
))
}

/// Calculates the amount of short principal that the LPs need to pay to back a
/// short before fees are taken into consideration, $P(x)$.
///
Expand Down
1 change: 0 additions & 1 deletion crates/hyperdrive-math/src/yield_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,6 @@ pub trait YieldSpace {
mod tests {
use std::panic;

use eyre::Result;
use rand::{thread_rng, Rng};
use test_utils::{chain::TestChain, constants::FAST_FUZZ_RUNS};

Expand Down
5 changes: 1 addition & 4 deletions crates/test-utils/src/bin/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ use std::{

use ethers::signers::LocalWallet;
use eyre::Result;
use test_utils::{
chain::{Chain, TestChainConfig},
constants::ALICE,
};
use test_utils::chain::{Chain, TestChainConfig};

#[tokio::main]
async fn main() -> Result<()> {
Expand Down
4 changes: 1 addition & 3 deletions crates/test-utils/src/bin/testnet_deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
///
/// After deploying these contracts and setting up the deployer coordinators,
/// this script will transfer ownership of the factory to a specified address.
use std::fs::{create_dir_all, File};
use std::{env, sync::Arc};

use ethers::{
core::utils::keccak256,
middleware::Middleware,
signers::{LocalWallet, Signer},
signers::LocalWallet,
types::{Address, U256},
};
use eyre::Result;
Expand Down

0 comments on commit 3360daa

Please sign in to comment.