fyuz/models/analytics.rs
1//! Token analytics, wallet performance and portfolios.
2//!
3//! Everything in this module is a server-side aggregate and arrives as JSON
4//! numbers, so `f64` is the faithful model here — unlike balances, which stay
5//! decimal strings.
6
7use serde::Deserialize;
8
9/// Holder concentration and flow analytics for one token.
10#[derive(Debug, Clone, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct TokenAnalytics {
13 /// Token contract address.
14 pub token_address: String,
15 /// Chain slug.
16 pub network: String,
17 /// Number of addresses with a positive balance.
18 pub holder_count: i64,
19 /// Share of supply held by the top 10 holders.
20 pub top10_pct: f64,
21 /// Share of supply held by the creator.
22 pub creator_pct: f64,
23 /// Lifetime buy count.
24 pub buys: i64,
25 /// Lifetime sell count.
26 pub sells: i64,
27 /// Lifetime USD volume.
28 pub volume_usd: f64,
29 /// Progress toward the $30,000 graduation target, 0-100.
30 pub graduation_pct: f64,
31 /// `true` once graduated to a DEX pair.
32 pub launched: bool,
33 /// Addresses that bought in the launch block.
34 pub bundle_buyers: i64,
35 /// `true` when the launch-block buying pattern looks like a bundle.
36 pub bundle_flag: bool,
37}
38
39/// One of a token's most profitable traders.
40#[derive(Debug, Clone, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub struct TopTrader {
43 /// Wallet address.
44 pub address: String,
45 /// Display name, when set.
46 #[serde(default)]
47 pub username: Option<String>,
48 /// Avatar URL, when set.
49 #[serde(default)]
50 pub avatar: Option<String>,
51 /// Realised PnL in USD.
52 pub realized_pnl_usd: f64,
53 /// Unrealised PnL in USD on the still-held position.
54 pub unrealized_pnl_usd: f64,
55 /// Realised plus unrealised PnL in USD.
56 pub total_pnl_usd: f64,
57 /// USD volume traded in this token.
58 pub volume_usd: f64,
59 /// `true` when this wallet launched the token.
60 pub is_creator: bool,
61}
62
63/// Average-cost performance for a wallet, rolled up across every token traded.
64#[derive(Debug, Clone, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct WalletStats {
67 /// Wallet address.
68 pub address: String,
69 /// Display name, when set.
70 #[serde(default)]
71 pub username: Option<String>,
72 /// Avatar URL, when set.
73 #[serde(default)]
74 pub avatar: Option<String>,
75 /// Lifetime USD volume.
76 pub volume_usd: f64,
77 /// Realised PnL in USD.
78 pub realized_pnl_usd: f64,
79 /// Unrealised PnL in USD.
80 pub unrealized_pnl_usd: f64,
81 /// Realised plus unrealised PnL in USD.
82 pub total_pnl_usd: f64,
83 /// Return on invested capital, percent.
84 pub roi_pct: f64,
85 /// Share of traded tokens that are net profitable, 0-1.
86 pub win_rate: f64,
87 /// Distinct tokens traded.
88 pub tokens_traded: i64,
89 /// Lifetime trade count.
90 pub trade_count: i64,
91 /// USD value of everything still held.
92 pub holding_value_usd: f64,
93}
94
95/// One token still held by an address.
96///
97/// Average-cost basis. Dust positions (balance <= 1e-6) are omitted by the
98/// server.
99#[derive(Debug, Clone, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct PortfolioPosition {
102 /// Token contract address.
103 pub token_address: String,
104 /// Token name.
105 pub name: String,
106 /// Token ticker.
107 pub symbol: String,
108 /// Token image URL.
109 #[serde(default)]
110 pub image: Option<String>,
111 /// Chain slug.
112 pub network: String,
113 /// Net tokens held (buys minus sells), in whole tokens.
114 pub balance: f64,
115 /// Current token price in USD.
116 pub current_price_usd: f64,
117 /// Current position value in USD.
118 pub value_usd: f64,
119 /// Cost basis of the position in USD.
120 pub cost_usd: f64,
121 /// Average buy price in USD.
122 pub avg_buy_price_usd: f64,
123 /// Unrealised PnL in USD.
124 pub unrealized_pnl_usd: f64,
125 /// Unrealised PnL, percent of cost.
126 pub unrealized_pnl_pct: f64,
127 /// Realised PnL in USD on this token.
128 pub realized_pnl_usd: f64,
129}
130
131/// Live average-cost PnL for an address.
132///
133/// An address with no trades returns zeroed totals and an empty
134/// [`positions`](Self::positions) array rather than a 404.
135#[derive(Debug, Clone, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub struct Portfolio {
138 /// Wallet address.
139 pub address: String,
140 /// USD value of everything held.
141 pub total_value_usd: f64,
142 /// USD cost basis of everything held.
143 pub total_cost_usd: f64,
144 /// Unrealised PnL in USD.
145 pub unrealized_pnl_usd: f64,
146 /// Realised PnL in USD.
147 pub realized_pnl_usd: f64,
148 /// Realised plus unrealised PnL in USD.
149 pub total_pnl_usd: f64,
150 /// Return on invested capital, percent.
151 pub roi_pct: f64,
152 /// Open positions, highest value first.
153 pub positions: Vec<PortfolioPosition>,
154}