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 short::open tests #970

Merged
merged 3 commits into from
Apr 24, 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
124 changes: 0 additions & 124 deletions crates/hyperdrive-math/src/short/max.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,37 +394,6 @@ impl State {
+ self.governance_lp_fee() * self.curve_fee() * (fixed!(1e18) - spot_price))
}

/// Calculates the derivative of the short deposit function with respect to the
/// short amount. This allows us to use Newton's method to approximate the
/// maximum short that a trader can open.
///
/// Using this, calculating $D'(x)$ is straightforward:
///
/// $$
/// D'(x) = \tfrac{c}{c_0} - (c \cdot P'(x) - \phi_{curve} \cdot (1 - p)) + \phi_{flat}
/// $$
///
/// $$
/// P'(x) = \tfrac{1}{c} \cdot (y + x)^{-t_s} \cdot \left(\tfrac{\mu}{c} \cdot (k - (y + x)^{1 - t_s}) \right)^{\tfrac{t_s}{1 - t_s}}
/// $$
fn short_deposit_derivative(
&self,
bond_amount: FixedPoint,
spot_price: FixedPoint,
open_vault_share_price: FixedPoint,
) -> FixedPoint {
// NOTE: The order of additions and subtractions is important to avoid underflows.
let payment_factor = (fixed!(1e18)
/ (self.bond_reserves() + bond_amount).pow(self.time_stretch()))
* self
.theta(bond_amount)
.pow(self.time_stretch() / (fixed!(1e18) - self.time_stretch()));
(self.vault_share_price() / open_vault_share_price)
+ self.flat_fee()
+ self.curve_fee() * (fixed!(1e18) - spot_price)
- payment_factor
}

/// Calculates the pool's solvency after opening a short.
///
/// We can express the pool's solvency after opening a short of $x$ bonds as:
Expand Down Expand Up @@ -514,21 +483,6 @@ impl State {
None
}
}

/// A helper function used in calculating the short deposit.
///
/// This calculates the inner component of the `short_principal` calculation,
/// which makes the `short_principal` and `short_deposit_derivative` calculations
/// easier. $\theta(x)$ is defined as:
///
/// $$
/// \theta(x) = \tfrac{\mu}{c} \cdot (k - (y + x)^{1 - t_s})
/// $$
fn theta(&self, bond_amount: FixedPoint) -> FixedPoint {
(self.initial_vault_share_price() / self.vault_share_price())
* (self.k_down()
- (self.bond_reserves() + bond_amount).pow(fixed!(1e18) - self.time_stretch()))
}
}

#[cfg(test)]
Expand Down Expand Up @@ -616,84 +570,6 @@ mod tests {
Ok(())
}

/// This test empirically tests the derivative of `short_deposit_derivative`
/// by calling `calculate_open_short` at two points and comparing the empirical
/// result with the output of `short_deposit_derivative`.
#[traced_test]
#[tokio::test]
async fn fuzz_short_deposit_derivative() -> Result<()> {
let mut rng = thread_rng();
// We use a relatively large epsilon here due to the underlying fixed point pow
// function not being monotonically increasing.
let empirical_derivative_epsilon = fixed!(1e12);
// TODO pretty big comparison epsilon here
let test_comparison_epsilon = fixed!(1e15);

for _ in 0..*FAST_FUZZ_RUNS {
let state = rng.gen::<State>();
let amount = rng.gen_range(fixed!(10e18)..=fixed!(10_000_000e18));

let p1_result = std::panic::catch_unwind(|| {
state.calculate_open_short(
amount - empirical_derivative_epsilon,
state.vault_share_price(),
)
});
let p1;
let p2;
match p1_result {
// If the amount results in the pool being insolvent, skip this iteration
Ok(p) => match p {
Ok(p) => p1 = p,
Err(_) => continue,
},
Err(_) => continue,
}

let p2_result = std::panic::catch_unwind(|| {
state.calculate_open_short(
amount + empirical_derivative_epsilon,
state.vault_share_price(),
)
});
match p2_result {
// If the amount results in the pool being insolvent, skip this iteration
Ok(p) => match p {
Ok(p) => p2 = p,
Err(_) => continue,
},
Err(_) => continue,
}
// Sanity check
assert!(p2 > p1);

let empirical_derivative = (p2 - p1) / (fixed!(2e18) * empirical_derivative_epsilon);
let short_deposit_derivative = state.short_deposit_derivative(
amount,
state.calculate_spot_price(),
state.vault_share_price(),
);

let derivative_diff;
if short_deposit_derivative >= empirical_derivative {
derivative_diff = short_deposit_derivative - empirical_derivative;
} else {
derivative_diff = empirical_derivative - short_deposit_derivative;
}
assert!(
derivative_diff < test_comparison_epsilon,
"expected (derivative_diff={}) < (test_comparison_epsilon={}), \
calculated_derivative={}, emperical_derivative={}",
derivative_diff,
test_comparison_epsilon,
short_deposit_derivative,
empirical_derivative
);
}

Ok(())
}

/// Tests that the absolute max short can be executed on chain.
#[traced_test]
#[tokio::test]
Expand Down
Loading
Loading