1#![no_std]
74#![forbid(unsafe_code)]
75#![deny(missing_docs)]
76
77pub mod precision {
79 pub use precision_core::{ArithmeticError, Decimal, ParseError, RoundingMode};
80}
81
82pub mod lending {
84 pub use risk_metrics::{
85 collateral_ratio, health_factor, is_healthy, liquidation_price, liquidation_threshold,
86 max_borrowable, available_liquidity, loan_to_value, utilization_rate,
87 };
88}
89
90pub mod amm {
92 pub use financial_calc::amm::{
93 calculate_amounts_from_liquidity, calculate_impermanent_loss, calculate_liquidity_burn,
94 calculate_liquidity_from_amounts, calculate_liquidity_mint, calculate_position_value,
95 calculate_price_impact, calculate_spot_price, calculate_swap_input, calculate_swap_output,
96 sqrt_price_to_tick, tick_spacing_to_fee_bps, tick_to_sqrt_price, ConcentratedPosition,
97 MAX_TICK, MIN_TICK, TICK_SPACING_HIGH, TICK_SPACING_LOW, TICK_SPACING_MEDIUM,
98 };
99}
100
101pub mod vault {
103 pub use financial_calc::{
104 compound_interest, effective_annual_rate, future_value, net_present_value, present_value,
105 simple_interest,
106 };
107
108 use precision_core::{ArithmeticError, Decimal};
109
110 pub fn calculate_share_price(
112 total_assets: Decimal,
113 total_supply: Decimal,
114 ) -> Result<Decimal, ArithmeticError> {
115 if total_supply.is_zero() {
116 return Ok(Decimal::ONE);
117 }
118 total_assets.try_div(total_supply)
119 }
120
121 pub fn calculate_shares_for_deposit(
123 assets: Decimal,
124 total_assets: Decimal,
125 total_supply: Decimal,
126 ) -> Result<Decimal, ArithmeticError> {
127 if total_supply.is_zero() {
128 return Ok(assets);
129 }
130 assets.try_mul(total_supply)?.try_div(total_assets)
131 }
132
133 pub fn calculate_assets_for_redeem(
135 shares: Decimal,
136 total_assets: Decimal,
137 total_supply: Decimal,
138 ) -> Result<Decimal, ArithmeticError> {
139 shares.try_mul(total_assets)?.try_div(total_supply)
140 }
141
142 pub fn calculate_apy_from_apr(
144 apr: Decimal,
145 compounds_per_year: u32,
146 ) -> Result<Decimal, ArithmeticError> {
147 let n = Decimal::from(compounds_per_year as i64);
148 let rate_per_period = apr.try_div(n)?;
149 let base = Decimal::ONE.try_add(rate_per_period)?;
150
151 let mut result = Decimal::ONE;
152 for _ in 0..compounds_per_year {
153 result = result.try_mul(base)?;
154 }
155
156 result.try_sub(Decimal::ONE)
157 }
158
159 pub fn calculate_performance_fee(
161 gains: Decimal,
162 fee_bps: Decimal,
163 ) -> Result<Decimal, ArithmeticError> {
164 let bps_base = Decimal::from(10000i64);
165 gains.try_mul(fee_bps)?.try_div(bps_base)
166 }
167}
168
169pub mod derivatives {
171 pub use financial_calc::derivatives::{
172 calculate_average_entry_price, calculate_breakeven_price, calculate_effective_leverage,
173 calculate_funding_payment, calculate_funding_rate, calculate_liquidation_distance,
174 calculate_liquidation_price, calculate_margin_ratio, calculate_max_position_size,
175 calculate_pnl, calculate_pnl_percentage, calculate_required_collateral, calculate_roe,
176 FundingParams, PerpPosition,
177 };
178}
179
180pub mod options {
182 pub use financial_calc::options::{
183 black_scholes_call, black_scholes_put, call_greeks, implied_volatility, normal_cdf,
184 normal_pdf, put_greeks, Greeks, OptionParams,
185 };
186}
187
188pub mod interest {
190 pub use financial_calc::{
191 compound_interest, effective_annual_rate, future_value, net_present_value, present_value,
192 simple_interest,
193 };
194}
195
196pub mod day_count {
198 pub use financial_calc::day_count::{Date, DayCountConvention, YearFraction};
199}
200
201pub mod term_structure {
203 pub use financial_calc::term_structure::{
204 CurveNode, FlatTermStructure, PiecewiseTermStructure, TermStructure, MAX_CURVE_NODES,
205 };
206}
207
208pub mod solver {
210 pub use financial_calc::solver::{
211 bisection, brent, default_tolerance, newton_raphson, newton_raphson_numerical, secant,
212 SolverResult, DEFAULT_MAX_ITER,
213 };
214}
215
216pub mod interpolation {
218 pub use financial_calc::interpolation::{
219 CubicSpline, DataPoint, Interpolator, Linear, LogLinear,
220 };
221}
222
223pub mod prelude {
225 pub use crate::precision::{ArithmeticError, Decimal, RoundingMode};
226
227 pub use crate::lending::{
229 collateral_ratio, health_factor, is_healthy, liquidation_price, max_borrowable,
230 };
231
232 pub use crate::amm::{
234 calculate_impermanent_loss, calculate_liquidity_mint, calculate_price_impact,
235 calculate_spot_price, calculate_swap_output, sqrt_price_to_tick, tick_to_sqrt_price,
236 };
237
238 pub use crate::vault::{
240 calculate_apy_from_apr, calculate_assets_for_redeem, calculate_performance_fee,
241 calculate_share_price, calculate_shares_for_deposit, compound_interest,
242 };
243
244 pub use crate::derivatives::{
246 calculate_funding_rate, calculate_liquidation_price, calculate_pnl, FundingParams,
247 PerpPosition,
248 };
249
250 pub use crate::options::{black_scholes_call, black_scholes_put, call_greeks, OptionParams};
252
253 pub use crate::interest::{future_value, net_present_value, present_value, simple_interest};
255}
256
257#[cfg(test)]
258mod tests {
259 use super::prelude::*;
260 use core::str::FromStr;
261
262 fn decimal(s: &str) -> Decimal {
263 Decimal::from_str(s).unwrap()
264 }
265
266 #[test]
267 fn test_lending_health_factor() {
268 let hf = health_factor(
269 decimal("10000"),
270 decimal("5000"),
271 decimal("0.8"),
272 )
273 .unwrap();
274
275 assert_eq!(hf, decimal("1.6"));
277 }
278
279 #[test]
280 fn test_amm_swap() {
281 let output = calculate_swap_output(
282 decimal("1000000"),
283 decimal("1000000"),
284 decimal("1000"),
285 decimal("30"),
286 )
287 .unwrap();
288
289 assert!(output > decimal("996"));
290 assert!(output < decimal("1000"));
291 }
292
293 #[test]
294 fn test_vault_share_price() {
295 let price = crate::vault::calculate_share_price(
296 decimal("1050000"),
297 decimal("1000000"),
298 )
299 .unwrap();
300
301 assert_eq!(price, decimal("1.05"));
302 }
303
304 #[test]
305 fn test_derivatives_pnl() {
306 let position = PerpPosition {
307 size: decimal("1.0"),
308 entry_price: decimal("2000"),
309 is_long: true,
310 leverage: decimal("10"),
311 collateral: decimal("200"),
312 };
313
314 let pnl = calculate_pnl(&position, decimal("2100")).unwrap();
315 assert_eq!(pnl, decimal("100"));
316 }
317
318 #[test]
319 fn test_options_pricing() {
320 let params = OptionParams {
321 spot: decimal("100"),
322 strike: decimal("100"),
323 rate: decimal("0.05"),
324 time: decimal("1.0"),
325 volatility: decimal("0.2"),
326 };
327
328 let call = black_scholes_call(¶ms).unwrap();
329 assert!(call > decimal("9"));
330 assert!(call < decimal("12"));
331 }
332}