1use std::collections::HashMap;
2
3use crate::models::skyblock::{Bazaar, BazaarProduct, SkyBlockAuction};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct BazaarSpread {
8 pub buy_order_price: f64,
10 pub sell_order_price: f64,
12}
13
14impl BazaarSpread {
15 pub fn margin(&self) -> f64 {
17 (self.sell_order_price - self.buy_order_price).abs()
18 }
19}
20
21pub fn bazaar_spread(product: &BazaarProduct) -> Option<BazaarSpread> {
26 let sell_order_price = product.sell_summary.first()?.price_per_unit;
27 let buy_order_price = product.buy_summary.first()?.price_per_unit;
28 Some(BazaarSpread {
29 buy_order_price,
30 sell_order_price,
31 })
32}
33
34pub fn bazaar_spreads(bazaar: &Bazaar) -> HashMap<String, BazaarSpread> {
36 bazaar
37 .products
38 .iter()
39 .filter_map(|(id, product)| bazaar_spread(product).map(|s| (id.clone(), s)))
40 .collect()
41}
42
43pub fn lowest_bin(auctions: &[SkyBlockAuction]) -> HashMap<String, i64> {
48 let mut lowest: HashMap<String, i64> = HashMap::new();
49 for auction in auctions {
50 if !auction.bin {
51 continue;
52 }
53 lowest
54 .entry(auction.item_name.clone())
55 .and_modify(|price| {
56 if auction.starting_bid < *price {
57 *price = auction.starting_bid;
58 }
59 })
60 .or_insert(auction.starting_bid);
61 }
62 lowest
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::models::skyblock::{BazaarOrder, BazaarProduct};
69
70 fn product(sell: f64, buy: f64) -> BazaarProduct {
71 BazaarProduct {
72 product_id: "X".into(),
73 sell_summary: vec![BazaarOrder {
74 amount: 1,
75 price_per_unit: sell,
76 orders: 1,
77 }],
78 buy_summary: vec![BazaarOrder {
79 amount: 1,
80 price_per_unit: buy,
81 orders: 1,
82 }],
83 quick_status: None,
84 }
85 }
86
87 #[test]
88 fn computes_spread_margin() {
89 let spread = bazaar_spread(&product(5.0, 4.2)).unwrap();
90 assert_eq!(spread.sell_order_price, 5.0);
91 assert_eq!(spread.buy_order_price, 4.2);
92 assert!((spread.margin() - 0.8).abs() < 1e-9);
93 }
94}