three_commas_types/bots/
stats.rs1use rust_decimal::Decimal;
2use serde::Deserialize;
3use smol_str::SmolStr;
4use std::{
5 collections::{hash_map, HashMap},
6 iter::FusedIterator,
7};
8
9#[derive(Debug, Deserialize, Clone)]
10pub struct BotStats {
11 overall_stats: HashMap<SmolStr, Decimal>,
12 today_stats: HashMap<SmolStr, Decimal>,
13 profits_in_usd: ProfitsInUsd,
14}
15
16impl BotStats {
17 pub fn overall(&self) -> TokenValues {
18 TokenValues {
19 values: &self.overall_stats,
20 }
21 }
22
23 pub fn today(&self) -> TokenValues {
24 TokenValues {
25 values: &self.today_stats,
26 }
27 }
28
29 pub fn overall_usd_profit(&self) -> Decimal {
30 self.profits_in_usd.overall_usd_profit
31 }
32
33 pub fn today_usd_profit(&self) -> Decimal {
34 self.profits_in_usd.today_usd_profit
35 }
36
37 pub fn active_deals_usd_profit(&self) -> Decimal {
38 self.profits_in_usd.active_deals_usd_profit
39 }
40}
41
42#[derive(Clone)]
43pub struct TokenValues<'a> {
44 values: &'a HashMap<SmolStr, Decimal>,
45}
46
47impl<'a> TokenValues<'a> {
48 pub fn get(&self, token: &str) -> Option<Decimal> {
49 self.values.get(token).copied()
50 }
51
52 pub fn tokens(&self) -> impl Iterator<Item = &'a str> {
53 self.values.keys().map(|k| &**k)
54 }
55
56 pub fn iter(&self) -> TokenValuesIter<'a> {
57 TokenValuesIter {
58 iter: self.values.iter(),
59 }
60 }
61}
62
63impl<'a> IntoIterator for TokenValues<'a> {
64 type Item = (&'a str, Decimal);
65 type IntoIter = TokenValuesIter<'a>;
66
67 fn into_iter(self) -> Self::IntoIter {
68 TokenValuesIter {
69 iter: self.values.iter(),
70 }
71 }
72}
73
74#[derive(Clone)]
75pub struct TokenValuesIter<'a> {
76 iter: hash_map::Iter<'a, SmolStr, Decimal>,
77}
78
79impl<'a> Iterator for TokenValuesIter<'a> {
80 type Item = (&'a str, Decimal);
81
82 fn next(&mut self) -> Option<Self::Item> {
83 self.iter.next().map(|(k, v)| (&**k, *v))
84 }
85
86 #[inline]
87 fn size_hint(&self) -> (usize, Option<usize>) {
88 self.iter.size_hint()
89 }
90}
91
92impl<'a> ExactSizeIterator for TokenValuesIter<'a> {
93 #[inline]
94 fn len(&self) -> usize {
95 self.iter.len()
96 }
97}
98
99impl<'a> FusedIterator for TokenValuesIter<'a> {}
100
101#[derive(Debug, Deserialize, Clone)]
102struct ProfitsInUsd {
103 overall_usd_profit: Decimal,
104 today_usd_profit: Decimal,
105 active_deals_usd_profit: Decimal,
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use std::str::FromStr;
112
113 #[test]
114 fn deserialize() {
115 let json = r###"
116 {
117 "overall_stats": {
118 "USDT": "79.77234945",
119 "BTC": "0.00074279"
120 },
121 "today_stats": {
122 "BTC": "0.00006813",
123 "USDT": "10.96327719"
124 },
125 "profits_in_usd": {
126 "overall_usd_profit": 123.49,
127 "today_usd_profit": 14.97,
128 "active_deals_usd_profit": -8.52
129 }
130 }
131 "###;
132
133 let stats: BotStats = serde_json::from_str(json).expect("deserialize successfully");
134 assert_eq!(stats.overall_stats.len(), 2);
135 assert_eq!(
136 stats.overall_stats.get("USDT"),
137 Some(&Decimal::from_str("79.77234945").unwrap())
138 );
139 assert_eq!(
140 stats.overall_stats.get("BTC"),
141 Some(&Decimal::from_str("0.00074279").unwrap())
142 );
143 assert_eq!(
144 stats.today_stats.get("USDT"),
145 Some(&Decimal::from_str("10.96327719").unwrap())
146 );
147 assert_eq!(
148 stats.today_stats.get("BTC"),
149 Some(&Decimal::from_str("0.00006813").unwrap())
150 );
151 assert_eq!(
152 stats.profits_in_usd.overall_usd_profit,
153 Decimal::from_str("123.49").unwrap()
154 );
155 assert_eq!(
156 stats.profits_in_usd.today_usd_profit,
157 Decimal::from_str("14.97").unwrap()
158 );
159 assert_eq!(
160 stats.profits_in_usd.active_deals_usd_profit,
161 Decimal::from_str("-8.52").unwrap()
162 );
163 }
164}