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