use crate::error::{Error, Result};
use crate::types::PriceLevel;
use crate::Side;
use rust_decimal::Decimal;
pub fn calculate_market_price(
positions: &[PriceLevel],
shares_to_match: Decimal,
side: Side,
) -> Result<Decimal> {
let mut remaining = shares_to_match;
let mut total_cost = Decimal::ZERO;
let positions = match side {
Side::Buy => {
let mut asks = positions.to_vec();
asks.sort_by(|a, b| a.price.cmp(&b.price));
asks
}
Side::Sell => {
let mut bids = positions.to_vec();
bids.sort_by(|a, b| b.price.cmp(&a.price));
bids
}
};
for p in positions {
let filled = remaining.min(p.size);
total_cost += filled * p.price;
remaining -= filled;
if remaining.is_zero() {
return Ok(total_cost / shares_to_match); }
}
Err(Error::InvalidOrder(format!(
"Not enough liquidity to create market order with amount {}",
shares_to_match
)))
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal_macros::dec;
fn order(price: Decimal, size: Decimal) -> PriceLevel {
PriceLevel { price, size }
}
#[test]
fn test_weighted_avg_price_buy() {
let positions = vec![order(dec!(0.50), dec!(10)), order(dec!(0.55), dec!(20))];
let price = calculate_market_price(&positions, dec!(25), Side::Buy).unwrap();
assert_eq!(price, dec!(0.53));
}
#[test]
fn test_weighted_avg_price_sell() {
let positions = vec![order(dec!(0.50), dec!(10)), order(dec!(0.55), dec!(20))];
let price = calculate_market_price(&positions, dec!(25), Side::Sell).unwrap();
assert_eq!(price, dec!(0.54));
}
#[test]
fn test_single_tick() {
let positions = vec![order(dec!(0.50), dec!(100))];
let price = calculate_market_price(&positions, dec!(50), Side::Buy).unwrap();
assert_eq!(price, dec!(0.50));
}
#[test]
fn test_insufficient_liquidity() {
let positions = vec![order(dec!(0.50), dec!(10))];
let result = calculate_market_price(&positions, dec!(20), Side::Buy);
assert!(result.is_err());
}
}