v_exchanges/
other_types.rs1use jiff::Timestamp;
2use serde::Deserialize;
3use v_utils::{Percent, prelude::*, trades::Pair};
4
5#[derive(Clone, Copy, Debug, Default, derive_more::Deref, derive_more::DerefMut, Deserialize, Serialize)]
6pub struct Lsr {
7 pub time: Timestamp,
8 #[deref_mut]
9 #[deref]
10 pub long: Percent,
11}
12impl Lsr {
14 pub fn ratio(&self) -> f64 {
15 *self.long / self.short()
16 }
17
18 pub fn short(&self) -> f64 {
20 1.0 - *self.long
21 }
22
23 pub fn long(&self) -> f64 {
25 *self.long
26 }
27}
28impl From<f64> for Lsr {
29 fn from(f: f64) -> Self {
30 Self {
31 time: Timestamp::default(),
32 long: Percent::from(f),
33 }
34 }
35}
36
37#[derive(Clone, Debug, Default, derive_more::Deref, derive_more::DerefMut, Deserialize, Serialize)]
38pub struct Lsrs {
39 #[deref_mut]
40 #[deref]
41 pub values: Vec<Lsr>,
42 pub pair: Pair,
43}
44impl Lsrs {
45 pub const CHANGE_STR_LEN: usize = 26;
46 const MAX_LEN_BASE: usize = 9;
47
48 pub fn values(&self) -> &[Lsr] {
49 &self.values
50 }
51
52 pub fn last(&self) -> Result<&Lsr> {
53 self.values.last().ok_or_else(|| eyre!("Lsrs is empty"))
54 }
55
56 fn format_pair(&self) -> String {
57 let s = match self.pair.quote().as_ref() {
58 "USDT" => self.pair.base().to_string(),
59 _ => self.pair.to_string(),
60 };
61 format!("{:<width$}", s, width = Self::MAX_LEN_BASE)
62 }
63
64 pub fn display_short(&self) -> Result<String> {
65 Ok(format!("{}: {:.2}", self.format_pair(), self.last()?.long()))
66 }
67
68 pub fn display_change(&self) -> Result<String> {
69 let diff = NowThen::new(*self.last()?.long, *self.first().expect("can't be empty, otherwise `last()` would have had panicked").long);
70 let s = format!("{}: {:<12}", self.format_pair(), diff.to_string()); Ok(format!("{:<width$}", s, width = Self::CHANGE_STR_LEN))
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::sync::OnceLock;
78 static INIT: OnceLock<()> = OnceLock::new();
79 use super::*;
80
81 fn init() -> (Lsrs, Lsrs) {
82 if INIT.get().is_none() {
83 let _ = INIT.set(());
84 color_eyre::install().unwrap();
85 }
86 (
87 Lsrs {
88 values: vec![0.4, 0.5, 0.6, 0.55].into_iter().map(Lsr::from).collect(),
89 pair: Pair::from(("BTC", "USDT")),
90 },
91 Lsrs {
92 values: vec![0.9, 0.6, 0.6, 0.7].into_iter().map(Lsr::from).collect(),
93 pair: Pair::from(("TRUMP", "SOL")),
94 },
95 )
96 }
97
98 #[test]
99 fn display_short_usdt_pair() {
100 let lsrs = init();
101 insta::assert_snapshot!(lsrs.0.display_short().unwrap(), @"BTC : 0.55");
102 }
103
104 #[test]
105 fn display_short_non_usdt_pair() {
106 let lsrs = init();
107 insta::assert_snapshot!(lsrs.1.display_short().unwrap(), @"TRUMP-SOL: 0.70");
108 }
109
110 #[test]
111 fn display_change() {
112 let lsrs = init();
113 insta::assert_snapshot!(lsrs.0.display_change().unwrap(), @"BTC : 0.55+0.15");
114 }
115
116 #[test]
117 fn display_change_non_usdt() {
118 let lsrs = init();
119 insta::assert_snapshot!(lsrs.1.display_change().unwrap(), @"TRUMP-SOL: 0.7-0.2");
120 }
121}