tokenomics_simulator/
report.rs

1//! # Report module
2//!
3//! This module contains the simulation report struct and its methods.
4//! The simulation report contains the results of a simulation.
5
6use chrono::Utc;
7use rust_decimal::Decimal;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::User;
12
13/// Report containing the results of a simulation.
14#[derive(Debug)]
15#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
16pub struct SimulationReport {
17    /// Timestamp of the simulation interval.
18    pub interval: i64,
19
20    /// List of users and their balances, behaviors, etc.
21    /// Only available in the final report.
22    #[cfg_attr(feature = "serde", serde(skip))]
23    pub users: Option<Vec<User>>,
24
25    /// Profit or loss for the interval.
26    /// Positive value indicates profit, negative value indicates loss.
27    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
28    pub profit_loss: Decimal,
29
30    /// Number of trades made in the interval.
31    /// This includes both successful and failed trades.
32    pub trades: u64,
33
34    /// Number of successful trades made in the interval.
35    /// A trade is considered successful if the user has a positive balance.
36    pub successful_trades: u64,
37
38    /// Number of failed trades made in the interval.
39    /// A trade is considered failed if the user has a zero balance.
40    pub failed_trades: u64,
41
42    /// Market volatility during the simulation.
43    /// This is the standard deviation of token prices.
44    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
45    pub market_volatility: Decimal,
46
47    /// Liquidity of the token during the simulation.
48    /// Liquidity is the number of trades per second.
49    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
50    pub liquidity: Decimal,
51
52    /// Adoption rate of the token.
53    /// Adoption rate is the percentage of users who have a positive balance.
54    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
55    pub adoption_rate: Decimal,
56
57    /// Total number of tokens burned during the simulation.
58    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
59    pub total_burned: Decimal,
60
61    /// Burn rate of the token.
62    /// Burn rate is the number of tokens burned per user.
63    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
64    pub burn_rate: Decimal,
65
66    /// Inflation rate of the token.
67    /// Inflation rate is the number of new tokens created per user.
68    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
69    pub inflation_rate: Decimal,
70
71    /// User retention rate.
72    /// User retention rate is the percentage of users who have a positive balance.
73    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
74    pub user_retention: Decimal,
75
76    /// Network activity (e.g., transactions per second).
77    /// This is the number of transactions made in the interval.
78    pub network_activity: u64,
79
80    /// Actual token price during the simulation.
81    /// This is the price of the token at the end of the simulation.
82    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
83    pub token_price: Decimal,
84
85    /// Total number of new tokens created during the simulation.
86    #[cfg_attr(feature = "serde", serde(with = "rust_decimal::serde::float"))]
87    pub total_new_tokens: Decimal,
88}
89
90impl Default for SimulationReport {
91    /// Create a new simulation report with default values.
92    ///
93    /// # Returns
94    ///
95    /// A new simulation report with default values.
96    fn default() -> Self {
97        Self {
98            users: None,
99            interval: Utc::now().timestamp(),
100            profit_loss: Decimal::default(),
101            trades: 0,
102            successful_trades: 0,
103            failed_trades: 0,
104            market_volatility: Decimal::default(),
105            liquidity: Decimal::default(),
106            adoption_rate: Decimal::default(),
107            total_burned: Decimal::default(),
108            burn_rate: Decimal::default(),
109            inflation_rate: Decimal::default(),
110            user_retention: Decimal::default(),
111            token_price: Decimal::default(),
112            total_new_tokens: Decimal::default(),
113            network_activity: 0,
114        }
115    }
116}
117
118impl SimulationReport {
119    /// Calculate the liquidity of the token.
120    /// Liquidity is the number of trades per second.
121    ///
122    /// # Arguments
123    ///
124    /// * `trades` - Number of trades made in the interval.
125    /// * `interval_duration` - Duration of the interval in seconds.
126    /// * `decimals` - Number of decimal places to round to.
127    ///
128    /// # Returns
129    ///
130    /// The liquidity of the token as trades per second.
131    pub fn calculate_liquidity(
132        &self,
133        trades: Decimal,
134        interval_duration: Decimal,
135        decimals: u32,
136    ) -> Decimal {
137        #[cfg(feature = "log")]
138        log::debug!(
139            "Calculating liquidity: trades={}, interval_duration={}",
140            trades,
141            interval_duration
142        );
143
144        (trades / interval_duration).round_dp(decimals)
145    }
146
147    /// Calculate the adoption rate.
148    /// Adoption rate is the percentage of users who have a positive balance.
149    ///
150    /// # Arguments
151    ///
152    /// * `users` - A list of users.
153    /// * `decimals` - Number of decimal places to round to.
154    ///
155    /// # Returns
156    ///
157    /// The adoption rate as a percentage.
158    pub fn calculate_adoption_rate(&self, users: &[User], decimals: u32) -> Decimal {
159        #[cfg(feature = "log")]
160        log::debug!("Calculating adoption rate: users={:?}", users.len());
161
162        let total_users = Decimal::new(users.len() as i64, 0);
163        let new_users = Decimal::new(
164            users
165                .iter()
166                .filter(|u| u.balance > Decimal::default())
167                .count() as i64,
168            0,
169        );
170
171        (new_users / total_users).round_dp(decimals)
172    }
173
174    /// Calculate the burn rate.
175    /// Burn rate is the number of tokens burned per user.
176    ///
177    /// # Arguments
178    ///
179    /// * `total_burned` - Total number of tokens burned.
180    /// * `total_users` - Total number of users.
181    /// * `decimals` - Number of decimal places to round to.
182    ///
183    /// # Returns
184    ///
185    /// The burn rate as a percentage.
186    pub fn calculate_burn_rate(
187        &self,
188        total_burned: Decimal,
189        total_users: Decimal,
190        decimals: u32,
191    ) -> Decimal {
192        #[cfg(feature = "log")]
193        log::debug!(
194            "Calculating burn rate: total_burned={}, total_users={}",
195            total_burned,
196            total_users
197        );
198
199        (total_burned / total_users).round_dp(decimals)
200    }
201
202    /// Calculate the inflation rate.
203    /// Inflation rate is the number of new tokens created per user.
204    ///
205    /// # Arguments
206    ///
207    /// * `total_new_tokens` - Total number of new tokens created.
208    /// * `total_users` - Total number of users.
209    /// * `decimals` - Number of decimal places to round to.
210    ///
211    /// # Returns
212    ///
213    /// The inflation rate as a percentage.
214    pub fn calculate_inflation_rate(
215        &self,
216        total_new_tokens: Decimal,
217        total_users: Decimal,
218        decimals: u32,
219    ) -> Decimal {
220        #[cfg(feature = "log")]
221        log::debug!(
222            "Calculating inflation rate: total_new_tokens={}, total_users={}",
223            total_new_tokens,
224            total_users
225        );
226
227        (total_new_tokens / total_users).round_dp(decimals)
228    }
229
230    /// Calculate the user retention rate.
231    /// User retention rate is the percentage of users who have a positive balance.
232    ///
233    /// # Arguments
234    ///
235    /// * `users` - A list of users.
236    /// * `decimals` - Number of decimal places to round to.
237    ///
238    /// # Returns
239    ///
240    /// The user retention rate as a percentage.
241    pub fn calculate_user_retention(&self, users: &[User], decimals: u32) -> Decimal {
242        #[cfg(feature = "log")]
243        log::debug!("Calculating user retention rate: users={:?}", users.len());
244
245        let total_users = Decimal::new(users.len() as i64, 0);
246        let retained_users = Decimal::new(
247            users
248                .iter()
249                .filter(|u| u.balance > Decimal::default())
250                .count() as i64,
251            0,
252        );
253
254        (retained_users / total_users).round_dp(decimals)
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use uuid::Uuid;
261
262    use super::*;
263
264    #[test]
265    fn test_default() {
266        let report = SimulationReport::default();
267
268        assert!(report.users.is_none());
269        assert_eq!(report.profit_loss, Decimal::default());
270        assert_eq!(report.trades, 0);
271        assert_eq!(report.successful_trades, 0);
272        assert_eq!(report.failed_trades, 0);
273        assert_eq!(report.market_volatility, Decimal::default());
274        assert_eq!(report.liquidity, Decimal::default());
275        assert_eq!(report.adoption_rate, Decimal::default());
276        assert_eq!(report.burn_rate, Decimal::default());
277        assert_eq!(report.inflation_rate, Decimal::default());
278        assert_eq!(report.user_retention, Decimal::default());
279        assert_eq!(report.network_activity, 0);
280    }
281
282    #[test]
283    fn test_calculate_liquidity() {
284        let report = SimulationReport::default();
285        let trades = Decimal::new(100, 0);
286        let interval_duration = Decimal::new(10, 0);
287
288        assert_eq!(
289            report.calculate_liquidity(trades, interval_duration, 4),
290            Decimal::new(10, 0)
291        );
292    }
293
294    #[test]
295    fn test_calculate_adoption_rate() {
296        let report = SimulationReport::default();
297        let users = vec![
298            User::new(Uuid::new_v4(), Decimal::default()),
299            User::new(Uuid::new_v4(), Decimal::new(10, 0)),
300            User::new(Uuid::new_v4(), Decimal::default()),
301            User::new(Uuid::new_v4(), Decimal::new(5, 0)),
302        ];
303
304        assert_eq!(
305            report.calculate_adoption_rate(&users, 4),
306            Decimal::new(5, 1),
307        );
308    }
309
310    #[test]
311    fn test_calculate_burn_rate() {
312        let report = SimulationReport::default();
313        let total_burned = Decimal::new(100, 0);
314        let total_users = Decimal::new(10, 0);
315
316        assert_eq!(
317            report.calculate_burn_rate(total_burned, total_users, 4),
318            Decimal::new(10, 0)
319        );
320    }
321
322    #[test]
323    fn test_calculate_inflation_rate() {
324        let report = SimulationReport::default();
325        let total_new_tokens = Decimal::new(100, 0);
326        let total_users = Decimal::new(10, 0);
327
328        assert_eq!(
329            report.calculate_inflation_rate(total_new_tokens, total_users, 4),
330            Decimal::new(10, 0)
331        );
332    }
333
334    #[test]
335    fn test_calculate_user_retention() {
336        let report = SimulationReport::default();
337        let users = vec![
338            User::new(Uuid::new_v4(), Decimal::default()),
339            User::new(Uuid::new_v4(), Decimal::new(10, 0)),
340            User::new(Uuid::new_v4(), Decimal::default()),
341            User::new(Uuid::new_v4(), Decimal::new(5, 0)),
342        ];
343
344        assert_eq!(
345            report.calculate_user_retention(&users, 4),
346            Decimal::new(5, 1),
347        );
348    }
349}