#![no_std]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
pub mod precision {
pub use precision_core::{ArithmeticError, Decimal, ParseError, RoundingMode};
}
pub mod lending {
pub use risk_metrics::{
collateral_ratio, health_factor, is_healthy, liquidation_price, liquidation_threshold,
max_borrowable, available_liquidity, loan_to_value, utilization_rate,
};
}
pub mod amm {
pub use financial_calc::amm::{
calculate_amounts_from_liquidity, calculate_impermanent_loss, calculate_liquidity_burn,
calculate_liquidity_from_amounts, calculate_liquidity_mint, calculate_position_value,
calculate_price_impact, calculate_spot_price, calculate_swap_input, calculate_swap_output,
sqrt_price_to_tick, tick_spacing_to_fee_bps, tick_to_sqrt_price, ConcentratedPosition,
MAX_TICK, MIN_TICK, TICK_SPACING_HIGH, TICK_SPACING_LOW, TICK_SPACING_MEDIUM,
};
}
pub mod vault {
pub use financial_calc::{
compound_interest, effective_annual_rate, future_value, net_present_value, present_value,
simple_interest,
};
use precision_core::{ArithmeticError, Decimal};
pub fn calculate_share_price(
total_assets: Decimal,
total_supply: Decimal,
) -> Result<Decimal, ArithmeticError> {
if total_supply.is_zero() {
return Ok(Decimal::ONE);
}
total_assets.try_div(total_supply)
}
pub fn calculate_shares_for_deposit(
assets: Decimal,
total_assets: Decimal,
total_supply: Decimal,
) -> Result<Decimal, ArithmeticError> {
if total_supply.is_zero() {
return Ok(assets);
}
assets.try_mul(total_supply)?.try_div(total_assets)
}
pub fn calculate_assets_for_redeem(
shares: Decimal,
total_assets: Decimal,
total_supply: Decimal,
) -> Result<Decimal, ArithmeticError> {
shares.try_mul(total_assets)?.try_div(total_supply)
}
pub fn calculate_apy_from_apr(
apr: Decimal,
compounds_per_year: u32,
) -> Result<Decimal, ArithmeticError> {
let n = Decimal::from(compounds_per_year as i64);
let rate_per_period = apr.try_div(n)?;
let base = Decimal::ONE.try_add(rate_per_period)?;
let mut result = Decimal::ONE;
for _ in 0..compounds_per_year {
result = result.try_mul(base)?;
}
result.try_sub(Decimal::ONE)
}
pub fn calculate_performance_fee(
gains: Decimal,
fee_bps: Decimal,
) -> Result<Decimal, ArithmeticError> {
let bps_base = Decimal::from(10000i64);
gains.try_mul(fee_bps)?.try_div(bps_base)
}
}
pub mod derivatives {
pub use financial_calc::derivatives::{
calculate_average_entry_price, calculate_breakeven_price, calculate_effective_leverage,
calculate_funding_payment, calculate_funding_rate, calculate_liquidation_distance,
calculate_liquidation_price, calculate_margin_ratio, calculate_max_position_size,
calculate_pnl, calculate_pnl_percentage, calculate_required_collateral, calculate_roe,
FundingParams, PerpPosition,
};
}
pub mod options {
pub use financial_calc::options::{
black_scholes_call, black_scholes_put, call_greeks, implied_volatility, normal_cdf,
normal_pdf, put_greeks, Greeks, OptionParams,
};
}
pub mod interest {
pub use financial_calc::{
compound_interest, effective_annual_rate, future_value, net_present_value, present_value,
simple_interest,
};
}
pub mod day_count {
pub use financial_calc::day_count::{Date, DayCountConvention, YearFraction};
}
pub mod term_structure {
pub use financial_calc::term_structure::{
CurveNode, FlatTermStructure, PiecewiseTermStructure, TermStructure, MAX_CURVE_NODES,
};
}
pub mod solver {
pub use financial_calc::solver::{
bisection, brent, default_tolerance, newton_raphson, newton_raphson_numerical, secant,
SolverResult, DEFAULT_MAX_ITER,
};
}
pub mod interpolation {
pub use financial_calc::interpolation::{
CubicSpline, DataPoint, Interpolator, Linear, LogLinear,
};
}
pub mod prelude {
pub use crate::precision::{ArithmeticError, Decimal, RoundingMode};
pub use crate::lending::{
collateral_ratio, health_factor, is_healthy, liquidation_price, max_borrowable,
};
pub use crate::amm::{
calculate_impermanent_loss, calculate_liquidity_mint, calculate_price_impact,
calculate_spot_price, calculate_swap_output, sqrt_price_to_tick, tick_to_sqrt_price,
};
pub use crate::vault::{
calculate_apy_from_apr, calculate_assets_for_redeem, calculate_performance_fee,
calculate_share_price, calculate_shares_for_deposit, compound_interest,
};
pub use crate::derivatives::{
calculate_funding_rate, calculate_liquidation_price, calculate_pnl, FundingParams,
PerpPosition,
};
pub use crate::options::{black_scholes_call, black_scholes_put, call_greeks, OptionParams};
pub use crate::interest::{future_value, net_present_value, present_value, simple_interest};
}
#[cfg(test)]
mod tests {
use super::prelude::*;
use core::str::FromStr;
fn decimal(s: &str) -> Decimal {
Decimal::from_str(s).unwrap()
}
#[test]
fn test_lending_health_factor() {
let hf = health_factor(
decimal("10000"),
decimal("5000"),
decimal("0.8"),
)
.unwrap();
assert_eq!(hf, decimal("1.6"));
}
#[test]
fn test_amm_swap() {
let output = calculate_swap_output(
decimal("1000000"),
decimal("1000000"),
decimal("1000"),
decimal("30"),
)
.unwrap();
assert!(output > decimal("996"));
assert!(output < decimal("1000"));
}
#[test]
fn test_vault_share_price() {
let price = crate::vault::calculate_share_price(
decimal("1050000"),
decimal("1000000"),
)
.unwrap();
assert_eq!(price, decimal("1.05"));
}
#[test]
fn test_derivatives_pnl() {
let position = PerpPosition {
size: decimal("1.0"),
entry_price: decimal("2000"),
is_long: true,
leverage: decimal("10"),
collateral: decimal("200"),
};
let pnl = calculate_pnl(&position, decimal("2100")).unwrap();
assert_eq!(pnl, decimal("100"));
}
#[test]
fn test_options_pricing() {
let params = OptionParams {
spot: decimal("100"),
strike: decimal("100"),
rate: decimal("0.05"),
time: decimal("1.0"),
volatility: decimal("0.2"),
};
let call = black_scholes_call(¶ms).unwrap();
assert!(call > decimal("9"));
assert!(call < decimal("12"));
}
}