Skip to content

Commit

Permalink
Fix calculate_implied_rate to return an annualized rate and scale tol…
Browse files Browse the repository at this point in the history
…erance up with implied_rate (#112)

# Description
`calculate_implied_rate` was meant to return an annualized rate, but it
was returning an HPR instead. This fixes it to return an annualized
rate, and adds helper functions for turning annualized rates into HPR.

This also makes the test tolerance for `calculate_implied_rate` scale
with the total implied rate, to ensure the tests don't fail while the
error is still within reasonable bounds for larger results.

Adds `calculate_hpr_given_apr` and `calculate_hpr_given_apy` functions.

# Review Checklists

Please check each item **before approving** the pull request. While
going
through the checklist, it is recommended to leave comments on items that
are
referenced in the checklist to make sure that they are reviewed.

- [ ] **Testing**
    - [ ] Are there new or updated unit or integration tests?
    - [ ] Do the tests cover the happy paths?
    - [ ] Do the tests cover the unhappy paths?
- [ ] Are there an adequate number of fuzz tests to ensure that we are
          covering the full input space?
- [ ] If matching Solidity behavior, are there differential fuzz tests
that
          ensure that Rust matches Solidity?
  • Loading branch information
MazyGio authored May 22, 2024
1 parent 830cf49 commit e6eaebe
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 7 deletions.
30 changes: 23 additions & 7 deletions crates/hyperdrive-math/src/short/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,13 @@ impl State {
(fixed!(1e18) + variable_apy).pow(self.annualized_position_duration()) - fixed!(1e18);
let base_proceeds = bond_amount * tpy;
if base_proceeds > base_paid {
Ok(I256::try_from((base_proceeds - base_paid) / base_paid)?)
Ok(I256::try_from(
(base_proceeds - base_paid) / (base_paid * self.annualized_position_duration()),
)?)
} else {
Ok(-I256::try_from((base_paid - base_proceeds) / base_paid)?)
Ok(-I256::try_from(
(base_paid - base_proceeds) / (base_paid * self.annualized_position_duration()),
)?)
}
}

Expand Down Expand Up @@ -754,20 +758,32 @@ mod tests {

// Bob closes his short.
let base_proceeds = bob.close_short(maturity_time, bond_amount, None).await?;
let annualized_position_duration =
bob.get_state().await?.annualized_position_duration();

// Ensure that the implied rate matches the realized rate from
// holding the short to maturity.
let realized_rate = if base_proceeds > base_paid {
I256::try_from((base_proceeds - base_paid) / base_paid)?
I256::try_from(
(base_proceeds - base_paid) / (base_paid * annualized_position_duration),
)?
} else {
-I256::try_from((base_paid - base_proceeds) / base_paid)?
-I256::try_from(
(base_paid - base_proceeds) / (base_paid * annualized_position_duration),
)?
};
let error = (implied_rate - realized_rate).abs();
let scaled_tolerance = if implied_rate > int256!(1e18) {
I256::from(tolerance * implied_rate)
} else {
tolerance
};
assert!(
error < tolerance,
"error {:?} exceeds tolerance of {}",
error < scaled_tolerance,
"error {:?} exceeds tolerance of {} (scaled to {})",
error,
tolerance
tolerance,
scaled_tolerance
);

// Revert to the snapshot and reset the agent's wallets.
Expand Down
32 changes: 32 additions & 0 deletions crates/hyperdrive-math/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,38 @@ pub fn calculate_rate_given_fixed_price(
(fixed!(1e18) - price) / (price * fixed_price_duration_in_years)
}

/// Calculate the holding period return (HPR) given a non-compounding, annualized rate (APR).
///
/// Since the rate is non-compounding, we calculate the hpr as:
///
/// $$
/// hpr = apr * t
/// $$
///
/// where $t$ is the holding period, in units of years. For example, if the
/// holding period is 6 months, then $t=0.5$.
pub fn calculate_hpr_given_apr(apr: FixedPoint, position_duration: FixedPoint) -> FixedPoint {
let holding_period_in_years =
position_duration / FixedPoint::from(U256::from(60 * 60 * 24 * 365));
apr * holding_period_in_years
}

/// Calculate the holding period return (HPR) given a compounding, annualized rate (APY).
///
/// Since the rate is compounding, we calculate the hpr as:
///
/// $$
/// hpr = (1 + apy) ^ (t) - 1
/// $$
///
/// where $t$ is the holding period, in units of years. For example, if the
/// holding period is 6 months, then $t=0.5$.
pub fn calculate_hpr_given_apy(apy: FixedPoint, position_duration: FixedPoint) -> FixedPoint {
let holding_period_in_years =
position_duration / FixedPoint::from(U256::from(60 * 60 * 24 * 365));
(fixed!(1e18) + apy).pow(holding_period_in_years) - fixed!(1e18)
}

#[cfg(test)]
mod tests {
use std::panic;
Expand Down

0 comments on commit e6eaebe

Please sign in to comment.