coinpaprika_api/coins/
mod.rs

1use crate::client::{Client, Response};
2use crate::error::Error;
3use crate::exchanges::Fiat;
4use chrono::prelude::*;
5use reqwest_middleware::RequestBuilder;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Serialize, Deserialize)]
10/// Basic information about cryptocurrencies on coinpaprika.com
11pub struct Coin {
12    /// ID of coin on coinpaprika.com
13    pub id: String,
14
15    /// Name of the cryptocurrency
16    pub name: String,
17
18    /// Symbol of the cryptocurrency
19    pub symbol: String,
20
21    /// Current ranking of the cryptocurrency. If `is_active` is false the `rank` is 0
22    pub rank: isize,
23
24    /// Flag indicating if the currency was added within the last 5 days
25    pub is_new: bool,
26
27    /// Flag indicating if the currency is active, which means that we can calculate the current
28    /// price and volume
29    pub is_active: bool,
30
31    #[serde(rename = "type")]
32    /// Type of the cryptocurrency. Currently supported values are `coin` and `token`
33    pub coin_type: String,
34}
35
36#[derive(Debug, Serialize, Deserialize)]
37/// Parent coin
38pub struct Parent {
39    pub id: String,
40    pub name: String,
41    pub symbol: String,
42}
43
44#[derive(Debug, Serialize, Deserialize)]
45/// Tag assigned to a coin
46pub struct CoinTag {
47    /// ID of the tag
48    pub id: String,
49
50    /// Name of the tag
51    pub name: String,
52
53    /// Number of coins with this tag
54    pub coin_counter: i32,
55
56    /// Number of ico projects with this tag
57    pub ico_counter: i32,
58}
59
60#[derive(Debug, Serialize, Deserialize)]
61/// The cryptocurrency founding and/or developing team
62pub struct Team {
63    pub id: String,
64    pub name: String,
65    pub position: String,
66}
67
68#[derive(Debug, Serialize, Deserialize)]
69/// Coin contract
70pub struct Contract {
71    /// The contract identifier, which is usually its address
72    pub contract: String,
73
74    /// ID of the contract platform. For Ethereum contracts it is `eth-ethereum`, for Tron
75    /// `trx-tron`, etc.
76    pub platform: String,
77
78    #[serde(rename = "type")]
79    /// Type of the contract. Currently supported values are: `ERC20`, `BEP2`, `TRC10`, `TRC20`,
80    /// `Stellar Asset`, `Other`
81    pub contract_type: String,
82}
83
84#[derive(Debug, Serialize, Deserialize)]
85/// Coin whitepaper
86pub struct Whitepaper {
87    /// The whitepaper URL
88    pub link: String,
89
90    /// Link to the whitepaper thumbnail
91    pub thumbnail: String,
92}
93
94#[derive(Debug, Serialize, Deserialize)]
95/// Detailed, descriptive information about a single coin, without price or volume data.
96pub struct CoinDetails {
97    /// ID of coin on coinpaprika.com
98    pub id: String,
99
100    /// Name of the cryptocurrency
101    pub name: String,
102
103    /// Symbol of the cryptocurrency
104    pub symbol: String,
105
106    /// This field is deprecated. Use `contracts` field instead
107    pub parent: Option<Parent>,
108
109    /// Current coin ranking position on coinpaprika.com
110    pub rank: isize,
111
112    /// Flag indicating if the currency was added within the last 5 days
113    pub is_new: bool,
114
115    /// Flag indicating if the currency is active, which means that we can calculate the current
116    /// price and volume
117    pub is_active: bool,
118
119    #[serde(rename = "type")]
120    /// Type of the cryptocurrency. Currently supported values are `coin` and `token`
121    pub coin_type: String,
122
123    /// Logo image URL
124    pub logo: String,
125
126    /// The array of tags to which this coin was assigned on coinpaprika.com
127    pub tags: Vec<CoinTag>,
128
129    /// The cryptocurrency founding and/or developing team
130    pub team: Vec<Team>,
131
132    /// Text description of the cryptocurrency
133    pub description: Option<String>,
134
135    /// An important message about current status of the cryptocurrency
136    pub message: String,
137
138    /// Set to true if the cryptocurrency is Open Source project
139    pub open_source: bool,
140
141    /// Set to true if the cryptocurrency is supported by any hardware wallet
142    pub hardware_wallet: bool,
143
144    /// Launch date of the cryptocurrency
145    pub started_at: Option<String>,
146
147    /// Development status of the cryptocurrency - if it is a working project, beta version, just
148    /// an idea, etc.
149    pub development_status: Option<String>,
150
151    /// Cryptocurrency proof type: Proof of Work, Proof of Stake, etc.
152    pub proof_type: Option<String>,
153
154    /// The cryptocurrency organization structure: centralized, decentralized, hierarchical, flat,
155    /// etc.
156    pub org_structure: Option<String>,
157
158    /// Name of the hash algorithm used by the cryptocurrency
159    pub hash_algorithm: Option<String>,
160
161    /// This field is deprecated. Use `contracts` field instead
162    pub contract: Option<String>,
163
164    /// This field is deprecated. Use `contracts` field instead
165    pub platform: Option<String>,
166
167    /// Coin contracts
168    pub contracts: Option<Vec<Contract>>,
169
170    /// Social media links for coin
171    pub links: Value,
172
173    /// Contains all links of the `{coin_id}` coin together with statistics for some of them, e.g.
174    /// number of twitter followers, reddit subscribers, telegram members or github repository
175    /// stars and contributors
176    pub links_extended: Value,
177
178    /// Coin whitepaper
179    pub whitepaper: Value,
180
181    /// Date of the first available ticker data for the coin. RFC3999 (ISO-8601) format
182    pub first_data_at: String,
183
184    /// Date of the last available ticker data for the coin. RFC3999 (ISO-8601) format
185    pub last_data_at: String,
186}
187
188#[derive(Debug, Serialize, Deserialize)]
189/// Tweet about given coin
190pub struct Tweet {
191    /// Publish date of the tweet. RFC3999 (ISO-8601) format
192    pub date: String,
193
194    /// Twitter profile user name
195    pub user_name: String,
196
197    /// Twitter profile user image URL
198    pub user_image_link: String,
199
200    /// Tweet content
201    pub status: String,
202
203    /// Flag indicating whether it is a retweet of someone else's tweet
204    pub is_retweet: bool,
205
206    /// Number of retweets of this tweet
207    pub retweet_count: i32,
208
209    /// Number of likes of this tweet
210    pub like_count: i32,
211
212    /// Tweet URL
213    pub status_link: String,
214
215    /// Tweet ID
216    pub status_id: String,
217
218    pub media_link: Option<String>,
219
220    /// Link to Youtube video shared in this tweet
221    pub youtube_link: Option<String>,
222}
223
224#[derive(Debug, Serialize, Deserialize)]
225/// Event regarding given coin
226pub struct CoinEvent {
227    pub id: String,
228    pub date: String,
229    pub date_to: Option<String>,
230    pub name: String,
231    pub description: String,
232    pub is_conference: bool,
233    pub link: Option<String>,
234    pub proof_image_link: Option<String>,
235}
236
237#[derive(Debug, Serialize, Deserialize)]
238/// Exchange where a given coin is traded.
239pub struct CoinExchange {
240    pub id: String,
241    pub name: String,
242    pub fiats: Vec<Fiat>,
243    pub adjusted_volume_24h_share: f64,
244}
245
246#[derive(Debug, Serialize, Deserialize)]
247/// Market for a given coin.
248pub struct CoinMarket {
249    pub exchange_id: String,
250    pub exchange_name: String,
251    pub pair: String,
252    pub base_currency_id: String,
253    pub base_currency_name: String,
254    pub quote_currency_id: String,
255    pub quote_currency_name: String,
256    pub market_url: Option<String>,
257    pub category: String,
258    pub fee_type: String,
259    pub outlier: bool,
260    pub adjusted_volume_24h_share: f64,
261    pub quotes: Value,
262    pub last_updated: String,
263}
264
265#[derive(Debug, Serialize, Deserialize)]
266/// Open/High/Low/Close values with volume and market capitalization for given coin.
267pub struct CoinOHLC {
268    /// RFC3999 (ISO-8601) format
269    pub time_open: String,
270
271    /// RFC3999 (ISO-8601) format
272    pub time_close: String,
273
274    pub open: Option<f64>,
275    pub high: Option<f64>,
276    pub low: Option<f64>,
277    pub close: Option<f64>,
278    pub volume: Option<i64>,
279    pub market_cap: Option<i64>,
280}
281
282/// Request for getting basic information about cryptocurrencies on coinpaprika.com:
283/// [/coins](https://api.coinpaprika.com/#tag/Coins/paths/~1coins/get)
284pub struct GetCoinsRequest<'a> {
285    client: &'a Client,
286}
287
288impl<'a> GetCoinsRequest<'a> {
289    pub fn new(client: &'a Client) -> Self {
290        Self { client }
291    }
292
293    pub async fn send(&self) -> Result<Vec<Coin>, Error> {
294        let request: RequestBuilder = self
295            .client
296            .client
297            .get(format!("{}/coins", self.client.api_url));
298
299        let response: Response = self.client.request(request).await?;
300
301        let data: Vec<Coin> = response.response.json().await?;
302
303        Ok(data)
304    }
305}
306
307/// Request for getting detailed, descriptive information about a single coin, without price or
308/// volume data. For price data, check the `/tickers` and `/tickers/{coin_id}` endpoints.
309/// [/coins/{coin_id}](https://api.coinpaprika.com/#tag/Coins/operation/getCoinById)
310pub struct GetCoinRequest<'a> {
311    client: &'a Client,
312    coin_id: String,
313}
314
315impl<'a> GetCoinRequest<'a> {
316    pub fn new(client: &'a Client, coin_id: &str) -> Self {
317        Self {
318            client,
319            coin_id: String::from(coin_id),
320        }
321    }
322
323    pub async fn send(&self) -> Result<CoinDetails, Error> {
324        let request: RequestBuilder = self
325            .client
326            .client
327            .get(format!("{}/coins/{}", self.client.api_url, self.coin_id));
328
329        let response: Response = self.client.request(request).await?;
330
331        let data: CoinDetails = response.response.json().await?;
332
333        Ok(data)
334    }
335}
336
337/// Request for getting last 50 timeline tweets from the official Twitter profile for a given coin.
338/// [/coins/{coin_id}/twitter](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1twitter/get)
339pub struct GetTwitterRequest<'a> {
340    client: &'a Client,
341    coin_id: String,
342}
343
344impl<'a> GetTwitterRequest<'a> {
345    pub fn new(client: &'a Client, coin_id: &str) -> Self {
346        Self {
347            client,
348            coin_id: String::from(coin_id),
349        }
350    }
351
352    pub async fn send(&self) -> Result<Vec<Tweet>, Error> {
353        let request: RequestBuilder = self.client.client.get(format!(
354            "{}/coins/{}/twitter",
355            self.client.api_url, self.coin_id
356        ));
357
358        let response: Response = self.client.request(request).await?;
359
360        let data: Vec<Tweet> = response.response.json().await?;
361
362        Ok(data)
363    }
364}
365
366/// Request for getting events for a given coin.
367/// [/coins/{coin_id}/events](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1events/get)
368pub struct GetCoinEventsRequest<'a> {
369    client: &'a Client,
370    coin_id: String,
371}
372
373impl<'a> GetCoinEventsRequest<'a> {
374    pub fn new(client: &'a Client, coin_id: &str) -> Self {
375        Self {
376            client,
377            coin_id: String::from(coin_id),
378        }
379    }
380
381    pub async fn send(&self) -> Result<Vec<CoinEvent>, Error> {
382        let request: RequestBuilder = self.client.client.get(format!(
383            "{}/coins/{}/events",
384            self.client.api_url, self.coin_id
385        ));
386
387        let response: Response = self.client.request(request).await?;
388
389        let data: Vec<CoinEvent> = response.response.json().await?;
390
391        Ok(data)
392    }
393}
394
395/// Request for getting exchanges where a given coin is traded.
396/// [/coins/{coin_id}/exchanges](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1exchanges/get)
397pub struct GetCoinExchangesRequest<'a> {
398    client: &'a Client,
399    coin_id: String,
400}
401
402impl<'a> GetCoinExchangesRequest<'a> {
403    pub fn new(client: &'a Client, coin_id: &str) -> Self {
404        Self {
405            client,
406            coin_id: String::from(coin_id),
407        }
408    }
409
410    pub async fn send(&self) -> Result<Vec<CoinExchange>, Error> {
411        let request: RequestBuilder = self.client.client.get(format!(
412            "{}/coins/{}/exchanges",
413            self.client.api_url, self.coin_id
414        ));
415
416        let response: Response = self.client.request(request).await?;
417
418        let data: Vec<CoinExchange> = response.response.json().await?;
419
420        Ok(data)
421    }
422}
423
424/// Request for getting all available markets for a given coin.
425/// [/coins/{coin_id}/markets](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1markets/get)
426pub struct GetCoinMarketsRequest<'a> {
427    client: &'a Client,
428    coin_id: String,
429    quotes: Vec<String>,
430}
431
432impl<'a> GetCoinMarketsRequest<'a> {
433    pub fn new(client: &'a Client, coin_id: &str) -> Self {
434        Self {
435            client,
436            coin_id: String::from(coin_id),
437            quotes: vec![],
438        }
439    }
440
441    /// List of quotes to return. Up to 3 quotes at once. Currently allowed values:
442    /// BTC, ETH, USD, EUR, PLN, KRW, GBP, CAD, JPY, RUB, TRY, NZD, AUD, CHF, UAH, HKD, SGD, NGN,
443    /// PHP, MXN, BRL, THB, CLP, CNY, CZK, DKK, HUF, IDR, ILS, INR, MYR, NOK, PKR, SEK, TWD, ZAR,
444    /// VND, BOB, COP, PEN, ARS, ISK
445    pub fn quotes(&mut self, quotes: Vec<&str>) -> &'a mut GetCoinMarketsRequest {
446        self.quotes = quotes.iter().map(|&q| String::from(q)).collect();
447        self
448    }
449
450    pub async fn send(&self) -> Result<Vec<CoinMarket>, Error> {
451        let query = match self.quotes.len() {
452            0 => vec![],
453            _ => vec![("quotes", self.quotes.join(","))],
454        };
455
456        let request: RequestBuilder = self
457            .client
458            .client
459            .get(format!(
460                "{}/coins/{}/markets",
461                self.client.api_url, self.coin_id
462            ))
463            .query(&query);
464
465        let response: Response = self.client.request(request).await?;
466
467        let data: Vec<CoinMarket> = response.response.json().await?;
468
469        Ok(data)
470    }
471}
472
473/// Request for getting Open/High/Low/Close values with volume and market capitalization for the
474/// last full day.
475/// [/coins/{coin_id}/ohlcv/latest](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1ohlcv~1latest~1/get)
476pub struct GetCoinOHLCLastFullDayRequest<'a> {
477    client: &'a Client,
478    coin_id: String,
479    quote: Option<String>,
480}
481
482impl<'a> GetCoinOHLCLastFullDayRequest<'a> {
483    pub fn new(client: &'a Client, coin_id: &str) -> Self {
484        Self {
485            client,
486            coin_id: String::from(coin_id),
487            quote: None,
488        }
489    }
490    /// Returned data quote (available values: `usd` `btc`)
491    ///
492    /// Default: `"usd"`
493    pub fn quote(&mut self, quote: &str) -> &'a mut GetCoinOHLCLastFullDayRequest {
494        self.quote = Some(String::from(quote));
495        self
496    }
497
498    pub async fn send(&self) -> Result<Vec<CoinOHLC>, Error> {
499        let mut query: Vec<(&str, &str)> = Vec::new();
500
501        if let Some(quote) = &self.quote {
502            query.push(("quote", quote));
503        }
504
505        let request: RequestBuilder = self
506            .client
507            .client
508            .get(format!(
509                "{}/coins/{}/ohlcv/latest",
510                self.client.api_url, self.coin_id
511            ))
512            .query(&query);
513
514        let response: Response = self.client.request(request).await?;
515
516        let data: Vec<CoinOHLC> = response.response.json().await?;
517
518        Ok(data)
519    }
520}
521
522/// Request for getting Open/High/Low/Close values with volume and market capitalization for any
523/// date range. If the end date is the current day, data can change with every request until actual
524/// close of the day at 23:59:59"
525/// [/coins/{coin_id}/ohlcv/historical](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1ohlcv~1historical/get)
526pub struct GetCoinOHLCHistoricalRequest<'a> {
527    client: &'a Client,
528    coin_id: String,
529    start: String,
530    end: Option<String>,
531    limit: Option<String>,
532    quote: Option<String>,
533}
534
535impl<'a> GetCoinOHLCHistoricalRequest<'a> {
536    pub fn new(client: &'a Client, coin_id: &str) -> Self {
537        let now: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
538
539        Self {
540            client,
541            coin_id: String::from(coin_id),
542            start: format!("{}-{}-{}", now.year(), now.month(), now.day()),
543            end: None,
544            limit: None,
545            quote: None,
546        }
547    }
548
549    /// Start point for historical data
550    ///
551    /// Supported formats:
552    /// * RFC3999 (ISO-8601) eg. 2018-02-15T05:15:00Z
553    /// * Simple date (yyyy-mm-dd) eg. 2018-02-15
554    /// * Unix timestamp (in seconds) eg. 1518671700
555    pub fn start(&mut self, start: &str) -> &'a mut GetCoinOHLCHistoricalRequest {
556        self.start = String::from(start);
557        self
558    }
559
560    /// End point for historical data
561    ///
562    /// Default: `"NOW"`
563    ///
564    /// Supported formats:
565    /// RFC3999 (ISO-8601) eg. 2018-02-15T05:15:00Z
566    /// Simple date (yyyy-mm-dd) eg. 2018-02-15
567    /// Unix timestamp (in seconds) eg. 1518671700
568    pub fn end(&mut self, end: &str) -> &'a mut GetCoinOHLCHistoricalRequest {
569        self.end = Some(String::from(end));
570        self
571    }
572
573    /// Limit of result rows (max `366`)
574    ///
575    /// Default: `1`
576    pub fn limit(&mut self, limit: i32) -> &'a mut GetCoinOHLCHistoricalRequest {
577        self.limit = Some(limit.to_string());
578        self
579    }
580
581    /// Returned data quote (available values: `usd` `btc`)
582    ///
583    /// Default: `"usd"`
584    pub fn quote(&mut self, quote: &str) -> &'a mut GetCoinOHLCHistoricalRequest {
585        self.quote = Some(String::from(quote));
586        self
587    }
588
589    pub async fn send(&self) -> Result<Vec<CoinOHLC>, Error> {
590        let mut query: Vec<(&str, &str)> = vec![("start", self.start.as_ref())];
591
592        if let Some(end) = &self.end {
593            query.push(("end", end));
594        }
595
596        if let Some(limit) = &self.limit {
597            query.push(("limit", limit));
598        }
599
600        if let Some(quote) = &self.quote {
601            query.push(("quote", quote));
602        }
603
604        let request: RequestBuilder = self
605            .client
606            .client
607            .get(format!(
608                "{}/coins/{}/ohlcv/historical",
609                self.client.api_url, self.coin_id
610            ))
611            .query(&query);
612
613        let response: Response = self.client.request(request).await?;
614
615        let data: Vec<CoinOHLC> = response.response.json().await?;
616
617        Ok(data)
618    }
619}
620
621/// Request for getting Open/High/Low/Close values with volume and market capitalization for the
622/// current day. Data can change every each request until actual close of the day at 23:59:59.
623/// [/coins/{coin_id}/ohlcv/today](https://api.coinpaprika.com/#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1ohlcv~1today~1/get)
624pub struct GetCoinOHLCTodayRequest<'a> {
625    client: &'a Client,
626    coin_id: String,
627    quote: Option<String>,
628}
629
630impl<'a> GetCoinOHLCTodayRequest<'a> {
631    pub fn new(client: &'a Client, coin_id: &str) -> Self {
632        Self {
633            client,
634            coin_id: String::from(coin_id),
635            quote: None,
636        }
637    }
638
639    /// Returned data quote (available values: `usd` `btc`)
640    ///
641    /// Default: `"usd"`
642    pub fn quote(&mut self, quote: &str) -> &'a mut GetCoinOHLCTodayRequest {
643        self.quote = Some(String::from(quote));
644        self
645    }
646
647    pub async fn send(&self) -> Result<Vec<CoinOHLC>, Error> {
648        let mut query: Vec<(&str, &str)> = Vec::new();
649
650        if let Some(quote) = &self.quote {
651            query.push(("quote", quote));
652        }
653
654        let request: RequestBuilder = self
655            .client
656            .client
657            .get(format!(
658                "{}/coins/{}/ohlcv/today",
659                self.client.api_url, self.coin_id
660            ))
661            .query(&query);
662
663        let response: Response = self.client.request(request).await?;
664
665        let data: Vec<CoinOHLC> = response.response.json().await?;
666
667        Ok(data)
668    }
669}