1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
extern crate bytes; extern crate hyper; extern crate hyper_tls; extern crate serde; extern crate serde_json; use bytes::{BufMut, BytesMut}; use hyper::{ body::HttpBody, client::{Client, HttpConnector}, Body, Method, Request, StatusCode, Uri, }; use hyper_tls::HttpsConnector; use serde::Deserialize; type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; const BASE_URL: &str = "https://api-invest.tinkoff.ru/openapi"; #[derive(Deserialize, Debug)] pub enum Currency { RUB, USD, EUR, GBP, HKD, CHF, JPY, CNY, TRY, } #[derive(Deserialize, Debug)] pub enum InstrumentType { Stock, Currency, Bond, Etf, } pub struct TinkoffInvest { client: Client<HttpsConnector<HttpConnector>>, base_url: &'static str, token: &'static str, } #[derive(Deserialize, Debug)] pub struct TinkoffInstrument { pub figi: String, pub ticker: String, pub name: String, pub isin: Option<String>, #[serde(rename(serialize = "minPriceIncrement", deserialize = "minPriceIncrement"))] pub min_price_increment: Option<f64>, pub lot: u64, pub currency: Option<Currency>, #[serde(rename(serialize = "type", deserialize = "type"))] pub type_: InstrumentType, } #[derive(Deserialize, Debug)] struct TinkoffInstruments { instruments: Vec<TinkoffInstrument>, } #[derive(Deserialize, Debug)] struct TinkoffResponseData<P> { #[serde(rename(serialize = "trackingId", deserialize = "trackingId"))] tracking_id: String, payload: P, status: String, error: Option<String>, } async fn request_get( client: &Client<HttpsConnector<HttpConnector>>, uri: &Uri, token: &str, ) -> Result<BytesMut> { let request = Request::builder() .method(Method::GET) .uri(uri) .header("Authorization", "Bearer ".to_owned() + token) .body(Body::empty())?; let mut response = client.request(request).await?; let status = response.status(); if status == StatusCode::UNAUTHORIZED { let error = std::io::Error::new(std::io::ErrorKind::Other, "UNAUTHORIZED"); return Err(Box::new(error)); } let mut body = BytesMut::with_capacity(1024); while let Some(chunk) = response.body_mut().data().await { body.put(chunk?); } Ok(body) } impl TinkoffInvest { pub fn new(token: &'static str) -> TinkoffInvest { let https = HttpsConnector::new(); let client = Client::builder().build(https); TinkoffInvest { client, base_url: BASE_URL, token, } } pub async fn get_stocks(&self) -> Result<Vec<TinkoffInstrument>> { let uri = (self.base_url.to_owned() + "/market/stocks").parse::<Uri>()?; let response_data = request_get(&self.client, &uri, self.token).await?; let data = serde_json::from_slice::<TinkoffResponseData<TinkoffInstruments>>( response_data.as_ref(), )?; Ok(data.payload.instruments) } }