use crate::Decimal;
pub type Price = Decimal;
pub type Amount = Decimal;
#[derive(Debug, Clone)]
pub struct DepthBasedOffering {
max_exposure: Amount,
target_depth: Amount,
}
impl DepthBasedOffering {
#[must_use]
pub fn new(max_exposure: Amount, target_depth: Amount) -> Self {
Self {
max_exposure,
target_depth,
}
}
#[must_use]
pub fn max_exposure(&self) -> Amount {
self.max_exposure
}
#[must_use]
pub fn target_depth(&self) -> Amount {
self.target_depth
}
#[must_use]
pub fn calculate_ask_size(&self, inventory_position: Decimal) -> Amount {
self.max_exposure + inventory_position
}
#[must_use]
pub fn calculate_bid_size(&self, inventory_position: Decimal) -> Amount {
self.max_exposure - inventory_position
}
#[must_use]
pub fn price_adjustment(
&self,
cumulative_depth_at_level: Amount,
tick_size: Decimal,
is_ask: bool,
) -> Decimal {
if cumulative_depth_at_level >= self.target_depth {
if is_ask {
-tick_size } else {
tick_size }
} else {
Decimal::ZERO
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dec;
#[test]
fn test_depth_based_offering_creation() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
assert_eq!(strategy.max_exposure(), dec!(100.0));
assert_eq!(strategy.target_depth(), dec!(50.0));
}
#[test]
fn test_calculate_ask_size_flat_inventory() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let ask_size = strategy.calculate_ask_size(Decimal::ZERO);
assert_eq!(ask_size, dec!(100.0));
}
#[test]
fn test_calculate_ask_size_long_inventory() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let ask_size = strategy.calculate_ask_size(dec!(20.0));
assert_eq!(ask_size, dec!(120.0));
}
#[test]
fn test_calculate_ask_size_short_inventory() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let ask_size = strategy.calculate_ask_size(dec!(-20.0));
assert_eq!(ask_size, dec!(80.0));
}
#[test]
fn test_calculate_bid_size_flat_inventory() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let bid_size = strategy.calculate_bid_size(Decimal::ZERO);
assert_eq!(bid_size, dec!(100.0));
}
#[test]
fn test_calculate_bid_size_long_inventory() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let bid_size = strategy.calculate_bid_size(dec!(20.0));
assert_eq!(bid_size, dec!(80.0));
}
#[test]
fn test_calculate_bid_size_short_inventory() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let bid_size = strategy.calculate_bid_size(dec!(-20.0));
assert_eq!(bid_size, dec!(120.0));
}
#[test]
fn test_price_adjustment_for_ask() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let adjustment = strategy.price_adjustment(dec!(50.0), dec!(0.01), true);
assert_eq!(adjustment, dec!(-0.01));
let adjustment = strategy.price_adjustment(dec!(40.0), dec!(0.01), true);
assert_eq!(adjustment, Decimal::ZERO);
}
#[test]
fn test_price_adjustment_for_bid() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let adjustment = strategy.price_adjustment(dec!(50.0), dec!(0.01), false);
assert_eq!(adjustment, dec!(0.01));
let adjustment = strategy.price_adjustment(dec!(40.0), dec!(0.01), false);
assert_eq!(adjustment, Decimal::ZERO);
}
#[test]
fn test_symmetric_sizing() {
let strategy = DepthBasedOffering::new(dec!(100.0), dec!(50.0));
let ask = strategy.calculate_ask_size(Decimal::ZERO);
let bid = strategy.calculate_bid_size(Decimal::ZERO);
assert_eq!(ask + bid, dec!(200.0));
let ask = strategy.calculate_ask_size(dec!(30.0));
let bid = strategy.calculate_bid_size(dec!(30.0));
assert_eq!(ask + bid, dec!(200.0));
}
}