Skip to main content

fyuz/models/
trade.rs

1//! Trades and OHLCV candles.
2
3use serde::{Deserialize, Deserializer};
4
5use super::token::Token;
6
7/// Side of a trade.
8///
9/// Deserialisation is tolerant in two ways. A value the API adds later lands in
10/// [`TradeSide::Other`] instead of failing the whole response, and the casing is
11/// folded: `/trades/recent` sends `"buy"`/`"sell"` while the trades embedded in
12/// `/tokens/{network}/{token_address}` send `"BUY"`/`"SELL"`, and both map to
13/// [`TradeSide::Buy`] and [`TradeSide::Sell`].
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum TradeSide {
16    /// Bought the token (BNB in, tokens out).
17    Buy,
18    /// Sold the token (tokens in, BNB out).
19    Sell,
20    /// A side this SDK version does not know about, kept verbatim.
21    Other(String),
22}
23
24impl TradeSide {
25    /// The wire representation of this side.
26    pub fn as_str(&self) -> &str {
27        match self {
28            TradeSide::Buy => "buy",
29            TradeSide::Sell => "sell",
30            TradeSide::Other(s) => s,
31        }
32    }
33}
34
35impl std::fmt::Display for TradeSide {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        f.write_str(self.as_str())
38    }
39}
40
41impl<'de> Deserialize<'de> for TradeSide {
42    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
43        // Matched case-insensitively: `/trades/recent` sends "buy"/"sell" while
44        // the trades embedded in `/tokens/{network}/{token_address}` send
45        // "BUY"/"SELL". Folding the case here is what lets callers match on
46        // `TradeSide::Buy` at all; anything genuinely unrecognised still keeps
47        // its exact wire text in `Other`.
48        let raw = String::deserialize(deserializer)?;
49        Ok(match raw.to_ascii_lowercase().as_str() {
50            "buy" => TradeSide::Buy,
51            "sell" => TradeSide::Sell,
52            _ => TradeSide::Other(raw),
53        })
54    }
55}
56
57/// A single swap against the bonding curve or the graduated pair.
58#[derive(Debug, Clone, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct Trade {
61    /// Internal row id. Pass the highest one you have seen as `latest_trade_id`
62    /// to [`RecentTradesParams`](crate::RecentTradesParams) to poll incrementally.
63    pub id: i64,
64    /// Token name.
65    pub token_name: String,
66    /// Token ticker.
67    pub token_symbol: String,
68    /// Token contract address.
69    pub token_address: String,
70    /// Token image URL.
71    #[serde(default)]
72    pub token_image: Option<String>,
73    /// Wallet that made the trade.
74    pub swapper_address: String,
75    /// Trader's display name, when set.
76    #[serde(default)]
77    pub swapper_username: Option<String>,
78    /// Trader's avatar URL, when set.
79    #[serde(default)]
80    pub swapper_avatar: Option<String>,
81    /// Buy or sell.
82    #[serde(rename = "type")]
83    pub trade_side: TradeSide,
84    /// BNB amount. Decimal string — do not parse as `f64`.
85    pub eth_amount: String,
86    /// Token amount. Decimal string — do not parse as `f64`.
87    pub token_amount: String,
88    /// Chain slug.
89    pub network: String,
90    /// Trade time, UNIX **seconds**.
91    pub date: i64,
92    /// Transaction hash.
93    pub tx_hash: String,
94    /// Token price in USD at trade time. Decimal string.
95    pub token_price: String,
96    /// BNB price in USD at trade time. Decimal string.
97    pub eth_price: String,
98}
99
100/// Response of `GET /trades/recent`.
101#[derive(Debug, Clone, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct RecentTrades {
104    /// Platform-wide trades, newest first.
105    pub trades: Vec<Trade>,
106    /// The tokens referenced by [`trades`](Self::trades), so a poller does not
107    /// have to fetch them one at a time.
108    pub tokens: Vec<Token>,
109}
110
111/// One OHLCV candle.
112///
113/// These are already-aggregated chart numbers and are floats on the wire.
114#[derive(Debug, Clone, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct Candle {
117    /// Candle open time, UNIX **seconds**.
118    pub time: i64,
119    /// Open price.
120    pub open: f64,
121    /// High price.
122    pub high: f64,
123    /// Low price.
124    pub low: f64,
125    /// Close price.
126    pub close: f64,
127    /// Volume over the candle.
128    pub volume: f64,
129}