acta_maker_sdk/types/
amounts.rs1use super::PRICE_SCALE;
4use super::ids::{Decimals, Price, Quantity, Strike};
5
6#[allow(clippy::cast_precision_loss)]
7fn scaled_to_f64(value: u64) -> f64 {
8 value as f64 / PRICE_SCALE as f64
9}
10
11impl Strike {
12 #[must_use]
14 pub fn as_decimal(self) -> f64 {
15 scaled_to_f64(self.value())
16 }
17}
18
19impl Price {
20 #[must_use]
22 pub fn as_decimal(self) -> f64 {
23 scaled_to_f64(self.value())
24 }
25}
26
27impl Quantity {
28 #[must_use]
30 #[allow(clippy::cast_precision_loss)]
31 pub fn to_units(self, underlying_decimals: Decimals) -> f64 {
32 self.value() as f64 / 10f64.powi(i32::from(underlying_decimals.value()))
33 }
34}
35
36#[must_use]
40pub fn total_premium_quote_atoms(
41 price: Price,
42 quantity: Quantity,
43 underlying_decimals: Decimals,
44 quote_decimals: Decimals,
45) -> Option<u64> {
46 scaled_quote_amount(
47 price.value(),
48 quantity.value(),
49 quote_decimals.value(),
50 underlying_decimals.value(),
51 )
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct PhysicalPremium {
57 pub gross_premium: u64,
58 pub protocol_fee: u64,
59 pub net_premium: u64,
60}
61
62#[must_use]
66pub fn physical_premium(
67 price: Price,
68 strike: Strike,
69 quantity: Quantity,
70 underlying_decimals: Decimals,
71 quote_decimals: Decimals,
72 premium_bps: u16,
73 volume_bps: u16,
74) -> Option<PhysicalPremium> {
75 if premium_bps > 10_000 || volume_bps > 10_000 {
76 return None;
77 }
78 let quote = |per_unit: u64| {
79 scaled_quote_amount(
80 per_unit,
81 quantity.value(),
82 quote_decimals.value(),
83 underlying_decimals.value(),
84 )
85 };
86 let fee =
87 |amount: u64, bps: u16| u64::try_from(u128::from(amount) * u128::from(bps) / 10_000).ok();
88 let gross_premium = quote(price.value())?;
89 let premium_fee = gross_premium - quote(price.value() - fee(price.value(), premium_bps)?)?;
90 let protocol_fee = premium_fee.min(fee(quote(strike.value())?, volume_bps)?);
91 Some(PhysicalPremium {
92 gross_premium,
93 protocol_fee,
94 net_premium: gross_premium - protocol_fee,
95 })
96}
97
98pub(crate) fn scaled_quote_amount(
101 per_unit: u64,
102 quantity: u64,
103 quote_decimals: u8,
104 underlying_decimals: u8,
105) -> Option<u64> {
106 if quote_decimals > 18 || underlying_decimals > 18 {
107 return None;
108 }
109 let quote_scale = 10u128.pow(u32::from(quote_decimals));
110 let denominator = u128::from(PRICE_SCALE) * 10u128.pow(u32::from(underlying_decimals));
111 let numerator = u128::from(per_unit) * u128::from(quantity);
112 let amount = if quote_scale <= denominator {
113 numerator / (denominator / quote_scale)
114 } else {
115 numerator.checked_mul(quote_scale / denominator)?
116 };
117 u64::try_from(amount).ok()
118}
119
120#[cfg(test)]
121#[path = "amounts_tests.rs"]
122mod tests;