bitvavo_api/
types.rs

1use std::fmt;
2
3use serde::de::{Error, SeqAccess, Unexpected, Visitor};
4use serde::{Deserialize, Deserializer};
5
6/// Time interval between each candlestick.
7#[derive(Debug)]
8pub enum CandleInterval {
9    OneMinute,
10    FiveMinutes,
11    FifteenMinutes,
12    ThirtyMinutes,
13    OneHour,
14    TwoHours,
15    FourHours,
16    SixHours,
17    EightHours,
18    TwelveHours,
19    OneDay,
20}
21
22impl fmt::Display for CandleInterval {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            CandleInterval::OneMinute => write!(f, "1m"),
26            CandleInterval::FiveMinutes => write!(f, "5m"),
27            CandleInterval::FifteenMinutes => write!(f, "15m"),
28            CandleInterval::ThirtyMinutes => write!(f, "30m"),
29            CandleInterval::OneHour => write!(f, "1h"),
30            CandleInterval::TwoHours => write!(f, "2h"),
31            CandleInterval::FourHours => write!(f, "4h"),
32            CandleInterval::SixHours => write!(f, "6h"),
33            CandleInterval::EightHours => write!(f, "8h"),
34            CandleInterval::TwelveHours => write!(f, "12h"),
35            CandleInterval::OneDay => write!(f, "1d"),
36        }
37    }
38}
39
40/// A candlestick for a given market over a given time interval.
41#[derive(Debug)]
42pub struct OHLCV {
43    pub time: u64,
44    pub open: String,
45    pub high: String,
46    pub low: String,
47    pub close: String,
48    pub volume: String,
49}
50
51macro_rules! next_seq_element {
52    ($seq:ident, $name:ident) => {
53        $seq.next_element()?
54            .ok_or_else(|| A::Error::missing_field(stringify!($name)))?
55    };
56}
57
58impl<'de> Deserialize<'de> for OHLCV {
59    fn deserialize<D>(deserializer: D) -> crate::Result<Self, D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        struct OHLCVVisitor;
64
65        impl<'de> Visitor<'de> for OHLCVVisitor {
66            type Value = OHLCV;
67
68            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
69                write!(f, "OHLCV")
70            }
71
72            fn visit_seq<A>(self, mut seq: A) -> crate::Result<Self::Value, A::Error>
73            where
74                A: SeqAccess<'de>,
75            {
76                Ok(OHLCV {
77                    time: next_seq_element!(seq, time),
78                    open: next_seq_element!(seq, open),
79                    high: next_seq_element!(seq, high),
80                    low: next_seq_element!(seq, low),
81                    close: next_seq_element!(seq, close),
82                    volume: next_seq_element!(seq, volume),
83                })
84            }
85        }
86
87        deserializer.deserialize_seq(OHLCVVisitor)
88    }
89}
90
91/// Asset supported by Bitvavo.
92#[derive(Debug, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct Asset {
95    pub symbol: String,
96    pub name: String,
97    pub decimals: u64,
98    pub deposit_fee: String,
99    pub deposit_confirmations: u64,
100    pub deposit_status: AssetStatus,
101    pub withdrawal_fee: String,
102    pub withdrawal_min_amount: String,
103    pub withdrawal_status: AssetStatus,
104    pub networks: Vec<String>,
105    pub message: Option<String>,
106}
107
108/// The status of an asset.
109#[derive(Debug)]
110pub enum AssetStatus {
111    Ok,
112    Maintenance,
113    Delisted,
114}
115
116impl<'de> Deserialize<'de> for AssetStatus {
117    fn deserialize<D>(deserializer: D) -> crate::Result<AssetStatus, D::Error>
118    where
119        D: Deserializer<'de>,
120    {
121        let s = String::deserialize(deserializer)?;
122
123        match s.as_str() {
124            "OK" => Ok(AssetStatus::Ok),
125            "MAINTENANCE" => Ok(AssetStatus::Maintenance),
126            "DELISTED" => Ok(AssetStatus::Delisted),
127            s => Err(D::Error::invalid_value(
128                Unexpected::Str(s),
129                &"[OK, MAINTENANCE, DELISTED]",
130            )),
131        }
132    }
133}
134
135/// Information about a market on Bitvavo.
136#[derive(Debug, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct Market {
139    #[serde(rename = "market")]
140    pub pair: String,
141    pub status: MarketStatus,
142    pub base: String,
143    pub quote: String,
144    pub price_precision: u64,
145    pub min_order_in_base_asset: String,
146    pub min_order_in_quote_asset: String,
147    pub max_order_in_base_asset: String,
148    pub max_order_in_quote_asset: String,
149    pub order_types: Vec<String>,
150}
151
152/// The status of a market.
153#[derive(Debug)]
154pub enum MarketStatus {
155    Trading,
156    Halted,
157    Auction,
158}
159
160impl<'de> Deserialize<'de> for MarketStatus {
161    fn deserialize<D>(deserializer: D) -> crate::Result<MarketStatus, D::Error>
162    where
163        D: Deserializer<'de>,
164    {
165        let s = String::deserialize(deserializer)?;
166
167        match s.as_str() {
168            "trading" => Ok(MarketStatus::Trading),
169            "halted" => Ok(MarketStatus::Halted),
170            "auction" => Ok(MarketStatus::Auction),
171            s => Err(D::Error::invalid_value(
172                Unexpected::Str(s),
173                &"[trading, halted, auction]",
174            )),
175        }
176    }
177}
178
179/// Order book for a particular market.
180#[derive(Debug, Deserialize)]
181pub struct OrderBook {
182    pub market: String,
183    pub nonce: u64,
184    pub bids: Vec<Quote>,
185    pub asks: Vec<Quote>,
186}
187
188/// A quote in the order book.
189#[derive(Debug)]
190pub struct Quote {
191    pub price: String,
192    pub amount: String,
193}
194
195impl<'de> Deserialize<'de> for Quote {
196    fn deserialize<D>(deserializer: D) -> crate::Result<Self, D::Error>
197    where
198        D: Deserializer<'de>,
199    {
200        struct QuoteVisitor;
201
202        impl<'de> Visitor<'de> for QuoteVisitor {
203            type Value = Quote;
204
205            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
206                write!(f, "Quote")
207            }
208
209            fn visit_seq<A>(self, mut seq: A) -> crate::Result<Self::Value, A::Error>
210            where
211                A: SeqAccess<'de>,
212            {
213                Ok(Quote {
214                    price: next_seq_element!(seq, price),
215                    amount: next_seq_element!(seq, amount),
216                })
217            }
218        }
219
220        deserializer.deserialize_seq(QuoteVisitor)
221    }
222}
223
224/// A trade performed on the exchange for a particular market.
225#[derive(Debug, Deserialize)]
226pub struct Trade {
227    pub id: String,
228    pub timestamp: u64,
229    pub amount: String,
230    pub price: String,
231    pub side: TradeSide,
232}
233
234/// The side of a trade.
235#[derive(Debug)]
236pub enum TradeSide {
237    Buy,
238    Sell,
239}
240
241impl<'de> Deserialize<'de> for TradeSide {
242    fn deserialize<D>(deserializer: D) -> crate::Result<TradeSide, D::Error>
243    where
244        D: Deserializer<'de>,
245    {
246        let s = String::deserialize(deserializer)?;
247
248        match s.as_str() {
249            "buy" => Ok(TradeSide::Buy),
250            "sell" => Ok(TradeSide::Sell),
251            s => Err(D::Error::invalid_value(Unexpected::Str(s), &"[buy, sell]")),
252        }
253    }
254}
255
256/// A ticker for a given market pair.
257#[derive(Debug, Deserialize)]
258pub struct TickerPrice {
259    pub market: String,
260    pub price: Option<String>,
261}
262
263/// Highest buy and lowest sell prices currently available for a market.
264#[derive(Debug, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct TickerBook {
267    pub market: Option<String>,
268    pub bid: Option<String>,
269    pub bid_size: Option<String>,
270    pub ask: Option<String>,
271    pub ask_size: Option<String>,
272}
273
274/// High, low, open, last, and volume information for trades for a given market over the previous 24h.
275#[derive(Debug, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct Ticker24h {
278    pub market: String,
279    pub start_timestamp: Option<u64>,
280    pub timestamp: Option<u64>,
281    pub open: Option<String>,
282    pub open_timestamp: Option<u64>,
283    pub high: Option<String>,
284    pub low: Option<String>,
285    pub last: Option<String>,
286    pub close_timestamp: Option<u64>,
287    pub bid: Option<String>,
288    pub bid_size: Option<String>,
289    pub ask: Option<String>,
290    pub ask_size: Option<String>,
291    pub volume: Option<String>,
292    pub volume_quote: Option<String>,
293}