Skip to main content

chainstream_sdk/stream/
models.rs

1//! Data models for the ChainStream WebSocket Stream API
2//!
3//! This module contains all the data structures used by the stream API
4//! for real-time data subscriptions.
5
6use serde::{Deserialize, Serialize};
7
8// Re-export Resolution from openapi for convenience
9pub use crate::openapi::types::Resolution;
10
11/// Token activity type
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(rename_all = "snake_case")]
14pub enum TokenActivityType {
15    Sell,
16    Buy,
17    AddLiquidity,
18    RemoveLiquidity,
19}
20
21/// Channel type for subscriptions
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
23#[serde(rename_all = "snake_case")]
24pub enum ChannelType {
25    New,
26    #[serde(rename = "trending")]
27    Hot,
28    UsStocks,
29    Completed,
30    Graduated,
31}
32
33/// Metric type for measurements
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35#[serde(rename_all = "snake_case")]
36pub enum MetricType {
37    LiquidityInUsd,
38    MigratedRatio,
39}
40
41/// Ranking type for token rankings
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "snake_case")]
44pub enum RankingType {
45    New,
46    #[serde(rename = "trending")]
47    Hot,
48    Stocks,
49    #[serde(rename = "completed")]
50    FinalStretch,
51    #[serde(rename = "graduated")]
52    Migrated,
53}
54
55/// DEX (Decentralized Exchange) type
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
57#[serde(rename_all = "snake_case")]
58pub enum Dex {
59    PumpFun,
60    RaydiumLaunchpad,
61    MeteoraDynamicBoundingCurve,
62    BonkFun,
63    BoopFun,
64    MoonitFun,
65}
66
67/// Token activity data
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct TokenActivity {
71    pub address: String,
72    pub price_usd: String,
73    pub amount: String,
74    #[serde(rename = "type")]
75    pub activity_type: TokenActivityType,
76    pub tx_hash: String,
77    pub timestamp: i64,
78}
79
80/// Token statistics with multi-timeframe data
81/// Time windows: 1m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 24h, 1W, 1M
82#[derive(Debug, Clone, Serialize, Deserialize, Default)]
83pub struct TokenStat {
84    #[serde(rename = "a")]
85    pub address: String,
86    #[serde(rename = "t")]
87    pub timestamp: i64,
88
89    // Current price (first positive closeInUsd across windows, or from cache)
90    #[serde(rename = "p", skip_serializing_if = "Option::is_none")]
91    pub price: Option<String>,
92
93    // 1-minute data
94    #[serde(rename = "b1m", skip_serializing_if = "Option::is_none")]
95    pub buys_1m: Option<i64>,
96    #[serde(rename = "s1m", skip_serializing_if = "Option::is_none")]
97    pub sells_1m: Option<i64>,
98    #[serde(rename = "be1m", skip_serializing_if = "Option::is_none")]
99    pub buyers_1m: Option<i64>,
100    #[serde(rename = "se1m", skip_serializing_if = "Option::is_none")]
101    pub sellers_1m: Option<i64>,
102    #[serde(rename = "bviu1m", skip_serializing_if = "Option::is_none")]
103    pub buy_volume_in_usd_1m: Option<String>,
104    #[serde(rename = "sviu1m", skip_serializing_if = "Option::is_none")]
105    pub sell_volume_in_usd_1m: Option<String>,
106    #[serde(rename = "p1m", skip_serializing_if = "Option::is_none")]
107    pub price_1m: Option<String>,
108    #[serde(rename = "oiu1m", skip_serializing_if = "Option::is_none")]
109    pub open_in_usd_1m: Option<String>,
110    #[serde(rename = "ciu1m", skip_serializing_if = "Option::is_none")]
111    pub close_in_usd_1m: Option<String>,
112    #[serde(rename = "vpc1m", skip_serializing_if = "Option::is_none")]
113    pub volume_change_ratio_1m: Option<String>,
114    #[serde(rename = "tr1m", skip_serializing_if = "Option::is_none")]
115    pub trades_1m: Option<i64>,
116    #[serde(rename = "dpc1m", skip_serializing_if = "Option::is_none")]
117    pub dapp_program_count_1m: Option<i32>,
118    #[serde(rename = "pc1m", skip_serializing_if = "Option::is_none")]
119    pub pool_count_1m: Option<i32>,
120    #[serde(rename = "liq1m", skip_serializing_if = "Option::is_none")]
121    pub liquidity_in_usd_1m: Option<String>,
122    #[serde(rename = "lpc1m", skip_serializing_if = "Option::is_none")]
123    pub liquidity_change_ratio_1m: Option<String>,
124
125    // 5-minute data
126    #[serde(rename = "b5m", skip_serializing_if = "Option::is_none")]
127    pub buys_5m: Option<i64>,
128    #[serde(rename = "s5m", skip_serializing_if = "Option::is_none")]
129    pub sells_5m: Option<i64>,
130    #[serde(rename = "be5m", skip_serializing_if = "Option::is_none")]
131    pub buyers_5m: Option<i64>,
132    #[serde(rename = "se5m", skip_serializing_if = "Option::is_none")]
133    pub sellers_5m: Option<i64>,
134    #[serde(rename = "bviu5m", skip_serializing_if = "Option::is_none")]
135    pub buy_volume_in_usd_5m: Option<String>,
136    #[serde(rename = "sviu5m", skip_serializing_if = "Option::is_none")]
137    pub sell_volume_in_usd_5m: Option<String>,
138    #[serde(rename = "p5m", skip_serializing_if = "Option::is_none")]
139    pub price_5m: Option<String>,
140    #[serde(rename = "oiu5m", skip_serializing_if = "Option::is_none")]
141    pub open_in_usd_5m: Option<String>,
142    #[serde(rename = "ciu5m", skip_serializing_if = "Option::is_none")]
143    pub close_in_usd_5m: Option<String>,
144    #[serde(rename = "vpc5m", skip_serializing_if = "Option::is_none")]
145    pub volume_change_ratio_5m: Option<String>,
146    #[serde(rename = "tr5m", skip_serializing_if = "Option::is_none")]
147    pub trades_5m: Option<i64>,
148    #[serde(rename = "dpc5m", skip_serializing_if = "Option::is_none")]
149    pub dapp_program_count_5m: Option<i32>,
150    #[serde(rename = "pc5m", skip_serializing_if = "Option::is_none")]
151    pub pool_count_5m: Option<i32>,
152    #[serde(rename = "liq5m", skip_serializing_if = "Option::is_none")]
153    pub liquidity_in_usd_5m: Option<String>,
154    #[serde(rename = "lpc5m", skip_serializing_if = "Option::is_none")]
155    pub liquidity_change_ratio_5m: Option<String>,
156
157    // 15-minute data
158    #[serde(rename = "b15m", skip_serializing_if = "Option::is_none")]
159    pub buys_15m: Option<i64>,
160    #[serde(rename = "s15m", skip_serializing_if = "Option::is_none")]
161    pub sells_15m: Option<i64>,
162    #[serde(rename = "be15m", skip_serializing_if = "Option::is_none")]
163    pub buyers_15m: Option<i64>,
164    #[serde(rename = "se15m", skip_serializing_if = "Option::is_none")]
165    pub sellers_15m: Option<i64>,
166    #[serde(rename = "bviu15m", skip_serializing_if = "Option::is_none")]
167    pub buy_volume_in_usd_15m: Option<String>,
168    #[serde(rename = "sviu15m", skip_serializing_if = "Option::is_none")]
169    pub sell_volume_in_usd_15m: Option<String>,
170    #[serde(rename = "p15m", skip_serializing_if = "Option::is_none")]
171    pub price_15m: Option<String>,
172    #[serde(rename = "oiu15m", skip_serializing_if = "Option::is_none")]
173    pub open_in_usd_15m: Option<String>,
174    #[serde(rename = "ciu15m", skip_serializing_if = "Option::is_none")]
175    pub close_in_usd_15m: Option<String>,
176    #[serde(rename = "vpc15m", skip_serializing_if = "Option::is_none")]
177    pub volume_change_ratio_15m: Option<String>,
178    #[serde(rename = "tr15m", skip_serializing_if = "Option::is_none")]
179    pub trades_15m: Option<i64>,
180    #[serde(rename = "dpc15m", skip_serializing_if = "Option::is_none")]
181    pub dapp_program_count_15m: Option<i32>,
182    #[serde(rename = "pc15m", skip_serializing_if = "Option::is_none")]
183    pub pool_count_15m: Option<i32>,
184    #[serde(rename = "liq15m", skip_serializing_if = "Option::is_none")]
185    pub liquidity_in_usd_15m: Option<String>,
186    #[serde(rename = "lpc15m", skip_serializing_if = "Option::is_none")]
187    pub liquidity_change_ratio_15m: Option<String>,
188
189    // 30-minute data
190    #[serde(rename = "b30m", skip_serializing_if = "Option::is_none")]
191    pub buys_30m: Option<i64>,
192    #[serde(rename = "s30m", skip_serializing_if = "Option::is_none")]
193    pub sells_30m: Option<i64>,
194    #[serde(rename = "be30m", skip_serializing_if = "Option::is_none")]
195    pub buyers_30m: Option<i64>,
196    #[serde(rename = "se30m", skip_serializing_if = "Option::is_none")]
197    pub sellers_30m: Option<i64>,
198    #[serde(rename = "bviu30m", skip_serializing_if = "Option::is_none")]
199    pub buy_volume_in_usd_30m: Option<String>,
200    #[serde(rename = "sviu30m", skip_serializing_if = "Option::is_none")]
201    pub sell_volume_in_usd_30m: Option<String>,
202    #[serde(rename = "p30m", skip_serializing_if = "Option::is_none")]
203    pub price_30m: Option<String>,
204    #[serde(rename = "oiu30m", skip_serializing_if = "Option::is_none")]
205    pub open_in_usd_30m: Option<String>,
206    #[serde(rename = "ciu30m", skip_serializing_if = "Option::is_none")]
207    pub close_in_usd_30m: Option<String>,
208    #[serde(rename = "vpc30m", skip_serializing_if = "Option::is_none")]
209    pub volume_change_ratio_30m: Option<String>,
210    #[serde(rename = "tr30m", skip_serializing_if = "Option::is_none")]
211    pub trades_30m: Option<i64>,
212    #[serde(rename = "dpc30m", skip_serializing_if = "Option::is_none")]
213    pub dapp_program_count_30m: Option<i32>,
214    #[serde(rename = "pc30m", skip_serializing_if = "Option::is_none")]
215    pub pool_count_30m: Option<i32>,
216    #[serde(rename = "liq30m", skip_serializing_if = "Option::is_none")]
217    pub liquidity_in_usd_30m: Option<String>,
218    #[serde(rename = "lpc30m", skip_serializing_if = "Option::is_none")]
219    pub liquidity_change_ratio_30m: Option<String>,
220
221    // 1-hour data
222    #[serde(rename = "b1h", skip_serializing_if = "Option::is_none")]
223    pub buys_1h: Option<i64>,
224    #[serde(rename = "s1h", skip_serializing_if = "Option::is_none")]
225    pub sells_1h: Option<i64>,
226    #[serde(rename = "be1h", skip_serializing_if = "Option::is_none")]
227    pub buyers_1h: Option<i64>,
228    #[serde(rename = "se1h", skip_serializing_if = "Option::is_none")]
229    pub sellers_1h: Option<i64>,
230    #[serde(rename = "bviu1h", skip_serializing_if = "Option::is_none")]
231    pub buy_volume_in_usd_1h: Option<String>,
232    #[serde(rename = "sviu1h", skip_serializing_if = "Option::is_none")]
233    pub sell_volume_in_usd_1h: Option<String>,
234    #[serde(rename = "p1h", skip_serializing_if = "Option::is_none")]
235    pub price_1h: Option<String>,
236    #[serde(rename = "oiu1h", skip_serializing_if = "Option::is_none")]
237    pub open_in_usd_1h: Option<String>,
238    #[serde(rename = "ciu1h", skip_serializing_if = "Option::is_none")]
239    pub close_in_usd_1h: Option<String>,
240    #[serde(rename = "vpc1h", skip_serializing_if = "Option::is_none")]
241    pub volume_change_ratio_1h: Option<String>,
242    #[serde(rename = "tr1h", skip_serializing_if = "Option::is_none")]
243    pub trades_1h: Option<i64>,
244    #[serde(rename = "dpc1h", skip_serializing_if = "Option::is_none")]
245    pub dapp_program_count_1h: Option<i32>,
246    #[serde(rename = "pc1h", skip_serializing_if = "Option::is_none")]
247    pub pool_count_1h: Option<i32>,
248    #[serde(rename = "liq1h", skip_serializing_if = "Option::is_none")]
249    pub liquidity_in_usd_1h: Option<String>,
250    #[serde(rename = "lpc1h", skip_serializing_if = "Option::is_none")]
251    pub liquidity_change_ratio_1h: Option<String>,
252
253    // 2-hour data
254    #[serde(rename = "b2h", skip_serializing_if = "Option::is_none")]
255    pub buys_2h: Option<i64>,
256    #[serde(rename = "s2h", skip_serializing_if = "Option::is_none")]
257    pub sells_2h: Option<i64>,
258    #[serde(rename = "be2h", skip_serializing_if = "Option::is_none")]
259    pub buyers_2h: Option<i64>,
260    #[serde(rename = "se2h", skip_serializing_if = "Option::is_none")]
261    pub sellers_2h: Option<i64>,
262    #[serde(rename = "bviu2h", skip_serializing_if = "Option::is_none")]
263    pub buy_volume_in_usd_2h: Option<String>,
264    #[serde(rename = "sviu2h", skip_serializing_if = "Option::is_none")]
265    pub sell_volume_in_usd_2h: Option<String>,
266    #[serde(rename = "p2h", skip_serializing_if = "Option::is_none")]
267    pub price_2h: Option<String>,
268    #[serde(rename = "oiu2h", skip_serializing_if = "Option::is_none")]
269    pub open_in_usd_2h: Option<String>,
270    #[serde(rename = "ciu2h", skip_serializing_if = "Option::is_none")]
271    pub close_in_usd_2h: Option<String>,
272    #[serde(rename = "vpc2h", skip_serializing_if = "Option::is_none")]
273    pub volume_change_ratio_2h: Option<String>,
274    #[serde(rename = "tr2h", skip_serializing_if = "Option::is_none")]
275    pub trades_2h: Option<i64>,
276    #[serde(rename = "dpc2h", skip_serializing_if = "Option::is_none")]
277    pub dapp_program_count_2h: Option<i32>,
278    #[serde(rename = "pc2h", skip_serializing_if = "Option::is_none")]
279    pub pool_count_2h: Option<i32>,
280    #[serde(rename = "liq2h", skip_serializing_if = "Option::is_none")]
281    pub liquidity_in_usd_2h: Option<String>,
282    #[serde(rename = "lpc2h", skip_serializing_if = "Option::is_none")]
283    pub liquidity_change_ratio_2h: Option<String>,
284
285    // 4-hour data
286    #[serde(rename = "b4h", skip_serializing_if = "Option::is_none")]
287    pub buys_4h: Option<i64>,
288    #[serde(rename = "s4h", skip_serializing_if = "Option::is_none")]
289    pub sells_4h: Option<i64>,
290    #[serde(rename = "be4h", skip_serializing_if = "Option::is_none")]
291    pub buyers_4h: Option<i64>,
292    #[serde(rename = "se4h", skip_serializing_if = "Option::is_none")]
293    pub sellers_4h: Option<i64>,
294    #[serde(rename = "bviu4h", skip_serializing_if = "Option::is_none")]
295    pub buy_volume_in_usd_4h: Option<String>,
296    #[serde(rename = "sviu4h", skip_serializing_if = "Option::is_none")]
297    pub sell_volume_in_usd_4h: Option<String>,
298    #[serde(rename = "p4h", skip_serializing_if = "Option::is_none")]
299    pub price_4h: Option<String>,
300    #[serde(rename = "oiu4h", skip_serializing_if = "Option::is_none")]
301    pub open_in_usd_4h: Option<String>,
302    #[serde(rename = "ciu4h", skip_serializing_if = "Option::is_none")]
303    pub close_in_usd_4h: Option<String>,
304    #[serde(rename = "vpc4h", skip_serializing_if = "Option::is_none")]
305    pub volume_change_ratio_4h: Option<String>,
306    #[serde(rename = "tr4h", skip_serializing_if = "Option::is_none")]
307    pub trades_4h: Option<i64>,
308    #[serde(rename = "dpc4h", skip_serializing_if = "Option::is_none")]
309    pub dapp_program_count_4h: Option<i32>,
310    #[serde(rename = "pc4h", skip_serializing_if = "Option::is_none")]
311    pub pool_count_4h: Option<i32>,
312    #[serde(rename = "liq4h", skip_serializing_if = "Option::is_none")]
313    pub liquidity_in_usd_4h: Option<String>,
314    #[serde(rename = "lpc4h", skip_serializing_if = "Option::is_none")]
315    pub liquidity_change_ratio_4h: Option<String>,
316
317    // 6-hour data
318    #[serde(rename = "b6h", skip_serializing_if = "Option::is_none")]
319    pub buys_6h: Option<i64>,
320    #[serde(rename = "s6h", skip_serializing_if = "Option::is_none")]
321    pub sells_6h: Option<i64>,
322    #[serde(rename = "be6h", skip_serializing_if = "Option::is_none")]
323    pub buyers_6h: Option<i64>,
324    #[serde(rename = "se6h", skip_serializing_if = "Option::is_none")]
325    pub sellers_6h: Option<i64>,
326    #[serde(rename = "bviu6h", skip_serializing_if = "Option::is_none")]
327    pub buy_volume_in_usd_6h: Option<String>,
328    #[serde(rename = "sviu6h", skip_serializing_if = "Option::is_none")]
329    pub sell_volume_in_usd_6h: Option<String>,
330    #[serde(rename = "p6h", skip_serializing_if = "Option::is_none")]
331    pub price_6h: Option<String>,
332    #[serde(rename = "oiu6h", skip_serializing_if = "Option::is_none")]
333    pub open_in_usd_6h: Option<String>,
334    #[serde(rename = "ciu6h", skip_serializing_if = "Option::is_none")]
335    pub close_in_usd_6h: Option<String>,
336    #[serde(rename = "vpc6h", skip_serializing_if = "Option::is_none")]
337    pub volume_change_ratio_6h: Option<String>,
338    #[serde(rename = "tr6h", skip_serializing_if = "Option::is_none")]
339    pub trades_6h: Option<i64>,
340    #[serde(rename = "dpc6h", skip_serializing_if = "Option::is_none")]
341    pub dapp_program_count_6h: Option<i32>,
342    #[serde(rename = "pc6h", skip_serializing_if = "Option::is_none")]
343    pub pool_count_6h: Option<i32>,
344    #[serde(rename = "liq6h", skip_serializing_if = "Option::is_none")]
345    pub liquidity_in_usd_6h: Option<String>,
346    #[serde(rename = "lpc6h", skip_serializing_if = "Option::is_none")]
347    pub liquidity_change_ratio_6h: Option<String>,
348
349    // 8-hour data
350    #[serde(rename = "b8h", skip_serializing_if = "Option::is_none")]
351    pub buys_8h: Option<i64>,
352    #[serde(rename = "s8h", skip_serializing_if = "Option::is_none")]
353    pub sells_8h: Option<i64>,
354    #[serde(rename = "be8h", skip_serializing_if = "Option::is_none")]
355    pub buyers_8h: Option<i64>,
356    #[serde(rename = "se8h", skip_serializing_if = "Option::is_none")]
357    pub sellers_8h: Option<i64>,
358    #[serde(rename = "bviu8h", skip_serializing_if = "Option::is_none")]
359    pub buy_volume_in_usd_8h: Option<String>,
360    #[serde(rename = "sviu8h", skip_serializing_if = "Option::is_none")]
361    pub sell_volume_in_usd_8h: Option<String>,
362    #[serde(rename = "p8h", skip_serializing_if = "Option::is_none")]
363    pub price_8h: Option<String>,
364    #[serde(rename = "oiu8h", skip_serializing_if = "Option::is_none")]
365    pub open_in_usd_8h: Option<String>,
366    #[serde(rename = "ciu8h", skip_serializing_if = "Option::is_none")]
367    pub close_in_usd_8h: Option<String>,
368    #[serde(rename = "vpc8h", skip_serializing_if = "Option::is_none")]
369    pub volume_change_ratio_8h: Option<String>,
370    #[serde(rename = "tr8h", skip_serializing_if = "Option::is_none")]
371    pub trades_8h: Option<i64>,
372    #[serde(rename = "dpc8h", skip_serializing_if = "Option::is_none")]
373    pub dapp_program_count_8h: Option<i32>,
374    #[serde(rename = "pc8h", skip_serializing_if = "Option::is_none")]
375    pub pool_count_8h: Option<i32>,
376    #[serde(rename = "liq8h", skip_serializing_if = "Option::is_none")]
377    pub liquidity_in_usd_8h: Option<String>,
378    #[serde(rename = "lpc8h", skip_serializing_if = "Option::is_none")]
379    pub liquidity_change_ratio_8h: Option<String>,
380
381    // 24-hour data
382    #[serde(rename = "b24h", skip_serializing_if = "Option::is_none")]
383    pub buys_24h: Option<i64>,
384    #[serde(rename = "s24h", skip_serializing_if = "Option::is_none")]
385    pub sells_24h: Option<i64>,
386    #[serde(rename = "be24h", skip_serializing_if = "Option::is_none")]
387    pub buyers_24h: Option<i64>,
388    #[serde(rename = "se24h", skip_serializing_if = "Option::is_none")]
389    pub sellers_24h: Option<i64>,
390    #[serde(rename = "bviu24h", skip_serializing_if = "Option::is_none")]
391    pub buy_volume_in_usd_24h: Option<String>,
392    #[serde(rename = "sviu24h", skip_serializing_if = "Option::is_none")]
393    pub sell_volume_in_usd_24h: Option<String>,
394    #[serde(rename = "p24h", skip_serializing_if = "Option::is_none")]
395    pub price_24h: Option<String>,
396    #[serde(rename = "oiu24h", skip_serializing_if = "Option::is_none")]
397    pub open_in_usd_24h: Option<String>,
398    #[serde(rename = "ciu24h", skip_serializing_if = "Option::is_none")]
399    pub close_in_usd_24h: Option<String>,
400    #[serde(rename = "vpc24h", skip_serializing_if = "Option::is_none")]
401    pub volume_change_ratio_24h: Option<String>,
402    #[serde(rename = "tr24h", skip_serializing_if = "Option::is_none")]
403    pub trades_24h: Option<i64>,
404    #[serde(rename = "dpc24h", skip_serializing_if = "Option::is_none")]
405    pub dapp_program_count_24h: Option<i32>,
406    #[serde(rename = "pc24h", skip_serializing_if = "Option::is_none")]
407    pub pool_count_24h: Option<i32>,
408    #[serde(rename = "liq24h", skip_serializing_if = "Option::is_none")]
409    pub liquidity_in_usd_24h: Option<String>,
410    #[serde(rename = "lpc24h", skip_serializing_if = "Option::is_none")]
411    pub liquidity_change_ratio_24h: Option<String>,
412
413    // 1-week data (note: JSON keys use uppercase W)
414    #[serde(rename = "b1W", skip_serializing_if = "Option::is_none")]
415    pub buys_1w: Option<i64>,
416    #[serde(rename = "s1W", skip_serializing_if = "Option::is_none")]
417    pub sells_1w: Option<i64>,
418    #[serde(rename = "be1W", skip_serializing_if = "Option::is_none")]
419    pub buyers_1w: Option<i64>,
420    #[serde(rename = "se1W", skip_serializing_if = "Option::is_none")]
421    pub sellers_1w: Option<i64>,
422    #[serde(rename = "bviu1W", skip_serializing_if = "Option::is_none")]
423    pub buy_volume_in_usd_1w: Option<String>,
424    #[serde(rename = "sviu1W", skip_serializing_if = "Option::is_none")]
425    pub sell_volume_in_usd_1w: Option<String>,
426    #[serde(rename = "p1W", skip_serializing_if = "Option::is_none")]
427    pub price_1w: Option<String>,
428    #[serde(rename = "oiu1W", skip_serializing_if = "Option::is_none")]
429    pub open_in_usd_1w: Option<String>,
430    #[serde(rename = "ciu1W", skip_serializing_if = "Option::is_none")]
431    pub close_in_usd_1w: Option<String>,
432    #[serde(rename = "vpc1W", skip_serializing_if = "Option::is_none")]
433    pub volume_change_ratio_1w: Option<String>,
434    #[serde(rename = "tr1W", skip_serializing_if = "Option::is_none")]
435    pub trades_1w: Option<i64>,
436    #[serde(rename = "dpc1W", skip_serializing_if = "Option::is_none")]
437    pub dapp_program_count_1w: Option<i32>,
438    #[serde(rename = "pc1W", skip_serializing_if = "Option::is_none")]
439    pub pool_count_1w: Option<i32>,
440    #[serde(rename = "liq1W", skip_serializing_if = "Option::is_none")]
441    pub liquidity_in_usd_1w: Option<String>,
442    #[serde(rename = "lpc1W", skip_serializing_if = "Option::is_none")]
443    pub liquidity_change_ratio_1w: Option<String>,
444
445    // 1-month data (note: JSON keys use uppercase M)
446    #[serde(rename = "b1M", skip_serializing_if = "Option::is_none")]
447    pub buys_1mo: Option<i64>,
448    #[serde(rename = "s1M", skip_serializing_if = "Option::is_none")]
449    pub sells_1mo: Option<i64>,
450    #[serde(rename = "be1M", skip_serializing_if = "Option::is_none")]
451    pub buyers_1mo: Option<i64>,
452    #[serde(rename = "se1M", skip_serializing_if = "Option::is_none")]
453    pub sellers_1mo: Option<i64>,
454    #[serde(rename = "bviu1M", skip_serializing_if = "Option::is_none")]
455    pub buy_volume_in_usd_1mo: Option<String>,
456    #[serde(rename = "sviu1M", skip_serializing_if = "Option::is_none")]
457    pub sell_volume_in_usd_1mo: Option<String>,
458    #[serde(rename = "p1M", skip_serializing_if = "Option::is_none")]
459    pub price_1mo: Option<String>,
460    #[serde(rename = "oiu1M", skip_serializing_if = "Option::is_none")]
461    pub open_in_usd_1mo: Option<String>,
462    #[serde(rename = "ciu1M", skip_serializing_if = "Option::is_none")]
463    pub close_in_usd_1mo: Option<String>,
464    #[serde(rename = "vpc1M", skip_serializing_if = "Option::is_none")]
465    pub volume_change_ratio_1mo: Option<String>,
466    #[serde(rename = "tr1M", skip_serializing_if = "Option::is_none")]
467    pub trades_1mo: Option<i64>,
468    #[serde(rename = "dpc1M", skip_serializing_if = "Option::is_none")]
469    pub dapp_program_count_1mo: Option<i32>,
470    #[serde(rename = "pc1M", skip_serializing_if = "Option::is_none")]
471    pub pool_count_1mo: Option<i32>,
472    #[serde(rename = "liq1M", skip_serializing_if = "Option::is_none")]
473    pub liquidity_in_usd_1mo: Option<String>,
474    #[serde(rename = "lpc1M", skip_serializing_if = "Option::is_none")]
475    pub liquidity_change_ratio_1mo: Option<String>,
476}
477
478/// Token holder information
479#[derive(Debug, Clone, Serialize, Deserialize)]
480#[serde(rename_all = "camelCase")]
481pub struct TokenHolder {
482    pub token_address: String,
483    #[serde(skip_serializing_if = "Option::is_none")]
484    pub holders: Option<i32>,
485    #[serde(skip_serializing_if = "Option::is_none")]
486    pub top100_amount: Option<String>,
487    #[serde(skip_serializing_if = "Option::is_none")]
488    pub top10_amount: Option<String>,
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub top100_holders: Option<i32>,
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub top10_holders: Option<i32>,
493    #[serde(skip_serializing_if = "Option::is_none")]
494    pub top100_ratio: Option<String>,
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub top10_ratio: Option<String>,
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub creators_holders: Option<i32>,
499    #[serde(skip_serializing_if = "Option::is_none")]
500    pub creators_amount: Option<String>,
501    #[serde(skip_serializing_if = "Option::is_none")]
502    pub creators_ratio: Option<String>,
503    pub timestamp: i64,
504}
505
506/// Wallet balance data
507#[derive(Debug, Clone, Serialize, Deserialize)]
508#[serde(rename_all = "camelCase")]
509pub struct WalletBalance {
510    pub wallet_address: String,
511    pub token_address: String,
512    pub token_price_in_usd: String,
513    pub balance: String,
514    pub timestamp: i64,
515}
516
517/// Wallet profit and loss data
518#[derive(Debug, Clone, Serialize, Deserialize)]
519#[serde(rename_all = "camelCase")]
520pub struct WalletPnl {
521    pub wallet_address: String,
522    pub buys: i32,
523    pub buy_amount: String,
524    pub buy_amount_in_usd: String,
525    pub average_buy_price_in_usd: String,
526    pub sell_amount: String,
527    pub sell_amount_in_usd: String,
528    pub sells: i32,
529    pub wins: i32,
530    pub win_ratio: String,
531    pub pnl_in_usd: String,
532    pub average_pnl_in_usd: String,
533    pub pnl_ratio: String,
534    pub profitable_days: i32,
535    pub losing_days: i32,
536    pub tokens: i32,
537    pub resolution: String,
538}
539
540/// DEX protocol information
541#[derive(Debug, Clone, Serialize, Deserialize)]
542#[serde(rename_all = "camelCase")]
543pub struct DexProtocol {
544    #[serde(skip_serializing_if = "Option::is_none")]
545    pub program_address: Option<String>,
546    #[serde(skip_serializing_if = "Option::is_none")]
547    pub protocol_family: Option<String>,
548    #[serde(skip_serializing_if = "Option::is_none")]
549    pub protocol_name: Option<String>,
550}
551
552/// New token data
553#[derive(Debug, Clone, Serialize, Deserialize)]
554#[serde(rename_all = "camelCase")]
555pub struct NewToken {
556    pub token_address: String,
557    pub name: String,
558    pub symbol: String,
559    #[serde(skip_serializing_if = "Option::is_none")]
560    pub decimals: Option<i32>,
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub image_url: Option<String>,
563    #[serde(skip_serializing_if = "Option::is_none")]
564    pub description: Option<String>,
565    #[serde(skip_serializing_if = "Option::is_none")]
566    pub social_media: Option<SocialMedia>,
567    #[serde(skip_serializing_if = "Option::is_none")]
568    pub coingecko_coin_id: Option<String>,
569    #[serde(skip_serializing_if = "Option::is_none")]
570    pub launch_from: Option<DexProtocol>,
571    #[serde(skip_serializing_if = "Option::is_none")]
572    pub migrated_to: Option<DexProtocol>,
573    pub created_at_ms: i64,
574}
575
576/// Token supply data
577#[derive(Debug, Clone, Serialize, Deserialize)]
578#[serde(rename_all = "camelCase")]
579pub struct TokenSupply {
580    pub token_address: String,
581    #[serde(skip_serializing_if = "Option::is_none")]
582    pub supply: Option<String>,
583    #[serde(skip_serializing_if = "Option::is_none")]
584    pub market_cap_in_usd: Option<String>,
585    pub timestamp: i64,
586}
587
588/// DEX pool balance data
589#[derive(Debug, Clone, Serialize, Deserialize)]
590#[serde(rename_all = "camelCase")]
591pub struct DexPoolBalance {
592    pub pool_address: String,
593    pub token_a_address: String,
594    pub token_a_liquidity_in_usd: String,
595    pub token_b_address: String,
596    pub token_b_liquidity_in_usd: String,
597}
598
599/// Token liquidity data
600#[derive(Debug, Clone, Serialize, Deserialize)]
601#[serde(rename_all = "camelCase")]
602pub struct TokenLiquidity {
603    pub token_address: String,
604    pub metric_type: MetricType,
605    pub value: String,
606    pub timestamp: i64,
607}
608
609/// Token max liquidity data (max liquidity in a single pool)
610#[derive(Debug, Clone, Serialize, Deserialize)]
611#[serde(rename_all = "camelCase")]
612pub struct TokenMaxLiquidity {
613    pub token_address: String,
614    pub pool_address: String,
615    pub liquidity_in_usd: String,
616    pub liquidity_in_native: String,
617    pub timestamp: i64,
618}
619
620/// Token total liquidity data (total liquidity across all pools)
621#[derive(Debug, Clone, Serialize, Deserialize)]
622#[serde(rename_all = "camelCase")]
623pub struct TokenTotalLiquidity {
624    pub token_address: String,
625    pub liquidity_in_usd: String,
626    pub liquidity_in_native: String,
627    pub pool_count: i32,
628    pub timestamp: i64,
629}
630
631/// Token bonding curve data
632#[derive(Debug, Clone, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub struct TokenBondingCurve {
635    #[serde(skip_serializing_if = "Option::is_none")]
636    pub token_address: Option<String>,
637    #[serde(skip_serializing_if = "Option::is_none")]
638    pub progress_ratio: Option<String>,
639}
640
641/// Social media links
642#[derive(Debug, Clone, Serialize, Deserialize, Default)]
643#[serde(rename_all = "camelCase")]
644pub struct SocialMedia {
645    #[serde(skip_serializing_if = "Option::is_none")]
646    pub twitter: Option<String>,
647    #[serde(skip_serializing_if = "Option::is_none")]
648    pub telegram: Option<String>,
649    #[serde(skip_serializing_if = "Option::is_none")]
650    pub website: Option<String>,
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub tiktok: Option<String>,
653    #[serde(skip_serializing_if = "Option::is_none")]
654    pub discord: Option<String>,
655    #[serde(skip_serializing_if = "Option::is_none")]
656    pub facebook: Option<String>,
657    #[serde(skip_serializing_if = "Option::is_none")]
658    pub github: Option<String>,
659    #[serde(skip_serializing_if = "Option::is_none")]
660    pub instagram: Option<String>,
661    #[serde(skip_serializing_if = "Option::is_none")]
662    pub linkedin: Option<String>,
663    #[serde(skip_serializing_if = "Option::is_none")]
664    pub medium: Option<String>,
665    #[serde(skip_serializing_if = "Option::is_none")]
666    pub reddit: Option<String>,
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub youtube: Option<String>,
669    #[serde(skip_serializing_if = "Option::is_none")]
670    pub bitbucket: Option<String>,
671}
672
673/// Token metadata
674#[derive(Debug, Clone, Serialize, Deserialize)]
675#[serde(rename_all = "camelCase")]
676pub struct TokenMetadata {
677    pub token_address: String,
678    #[serde(skip_serializing_if = "Option::is_none")]
679    pub name: Option<String>,
680    #[serde(skip_serializing_if = "Option::is_none")]
681    pub decimals: Option<i32>,
682    #[serde(skip_serializing_if = "Option::is_none")]
683    pub symbol: Option<String>,
684    #[serde(skip_serializing_if = "Option::is_none")]
685    pub image_url: Option<String>,
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub description: Option<String>,
688    #[serde(skip_serializing_if = "Option::is_none")]
689    pub social_media: Option<SocialMedia>,
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub created_at_ms: Option<i64>,
692    #[serde(skip_serializing_if = "Option::is_none")]
693    pub coingecko_coin_id: Option<String>,
694    #[serde(skip_serializing_if = "Option::is_none")]
695    pub launch_from: Option<DexProtocol>,
696    #[serde(skip_serializing_if = "Option::is_none")]
697    pub migrated_to: Option<DexProtocol>,
698}
699
700/// Price type for candle data
701#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
702pub enum PriceType {
703    #[default]
704    Usd,
705    Native,
706}
707
708/// Candlestick data for Token/Pool/Pair
709#[derive(Debug, Clone, Serialize, Deserialize)]
710#[serde(rename_all = "camelCase")]
711pub struct Candle {
712    pub address: String,
713    pub open: String,
714    pub close: String,
715    pub high: String,
716    pub low: String,
717    pub volume: String,
718    pub resolution: String,
719    pub time: i64,
720    pub number: i32,
721}
722
723/// Alias for backward compatibility
724pub type TokenCandle = Candle;
725
726/// Trade activity data
727#[derive(Debug, Clone, Serialize, Deserialize)]
728#[serde(rename_all = "camelCase")]
729pub struct TradeActivity {
730    pub token_address: String,
731    pub timestamp: i64,
732    pub kind: String,
733    pub buy_amount: String,
734    pub buy_amount_in_usd: String,
735    pub buy_token_address: String,
736    pub buy_token_name: String,
737    pub buy_token_symbol: String,
738    pub buy_wallet_address: String,
739    pub sell_amount: String,
740    pub sell_amount_in_usd: String,
741    pub sell_token_address: String,
742    pub sell_token_name: String,
743    pub sell_token_symbol: String,
744    pub sell_wallet_address: String,
745    pub tx_hash: String,
746}
747
748/// Wallet token profit and loss data
749#[derive(Debug, Clone, Serialize, Deserialize)]
750#[serde(rename_all = "camelCase")]
751pub struct WalletTokenPnl {
752    pub wallet_address: String,
753    pub token_address: String,
754    pub token_price_in_usd: String,
755    pub timestamp: i64,
756    pub open_time: i64,
757    pub last_time: i64,
758    pub close_time: i64,
759    pub buy_amount: String,
760    pub buy_amount_in_usd: String,
761    pub buy_count: i32,
762    pub buy_count_30d: i32,
763    pub buy_count_7d: i32,
764    pub sell_amount: String,
765    pub sell_amount_in_usd: String,
766    pub sell_count: i32,
767    pub sell_count_30d: i32,
768    pub sell_count_7d: i32,
769    pub held_duration_timestamp: i64,
770    pub average_buy_price_in_usd: String,
771    pub average_sell_price_in_usd: String,
772    pub unrealized_profit_in_usd: String,
773    pub unrealized_profit_ratio: String,
774    pub realized_profit_in_usd: String,
775    pub realized_profit_ratio: String,
776    pub total_realized_profit_in_usd: String,
777    pub total_realized_profit_ratio: String,
778}
779
780/// Ranking token list data
781#[derive(Debug, Clone, Serialize, Deserialize)]
782#[serde(rename_all = "camelCase")]
783pub struct RankingTokenList {
784    #[serde(skip_serializing_if = "Option::is_none")]
785    pub metadata: Option<TokenMetadata>,
786    #[serde(skip_serializing_if = "Option::is_none")]
787    pub holder: Option<TokenHolder>,
788    #[serde(skip_serializing_if = "Option::is_none")]
789    pub supply: Option<TokenSupply>,
790    #[serde(skip_serializing_if = "Option::is_none")]
791    pub stat: Option<TokenStat>,
792    #[serde(skip_serializing_if = "Option::is_none")]
793    pub bonding_curve: Option<TokenBondingCurve>,
794}