yahoo_finance_api/
lib.rs

1//! # yahoo! finance API
2//!
3//! This project provides a set of functions to receive data from the
4//! the [yahoo! finance](https://finance.yahoo.com) website via their API. This project
5//! is licensed under Apache 2.0 or MIT license (see files LICENSE-Apache2.0 and LICENSE-MIT).
6//!
7//! Since version 0.3 and the upgrade to ```reqwest``` 0.10, all requests to the yahoo API return futures, using ```async``` features.
8//! Therefore, the functions need to be called from within another ```async``` function with ```.await``` or via functions like ```block_on```.
9//! The examples are based on the ```tokio``` runtime applying the ```tokio-test``` crate.
10//!
11//! Use the `blocking` feature to get the previous behavior back: i.e. `yahoo_finance_api = {"version": "1.0", features = ["blocking"]}`.
12//!
13#![cfg_attr(
14    not(feature = "blocking"),
15    doc = "
16# Get the latest available quote:
17```rust
18use yahoo_finance_api as yahoo;
19use time::OffsetDateTime;
20use tokio_test;
21
22fn main() {
23    let provider = yahoo::YahooConnector::new().unwrap();
24    // get the latest quotes in 1 minute intervals
25    let response = tokio_test::block_on(provider.get_latest_quotes(\"AAPL\", \"1d\")).unwrap();
26    // extract just the latest valid quote summery
27    // including timestamp,open,close,high,low,volume
28    let quote = response.last_quote().unwrap();
29    let time: OffsetDateTime =
30        OffsetDateTime::from_unix_timestamp(quote.timestamp).unwrap();
31    println!(\"At {} quote price of Apple was {}\", time, quote.close);
32}
33```
34# Get history of quotes for given time period:
35```rust
36use yahoo_finance_api as yahoo;
37use time::{macros::datetime, OffsetDateTime};
38use tokio_test;
39
40fn main() {
41    let provider = yahoo::YahooConnector::new().unwrap();
42    let start = datetime!(2020-1-1 0:00:00.00 UTC);
43    let end = datetime!(2020-1-31 23:59:59.99 UTC);
44    // returns historic quotes with daily interval
45    let resp = tokio_test::block_on(provider.get_quote_history(\"AAPL\", start, end)).unwrap();
46    let quotes = resp.quotes().unwrap();
47    println!(\"Apple's quotes in January: {:?}\", quotes);
48}
49```
50# Get the history of quotes for time range
51Another method to retrieve a range of quotes is by requesting the quotes for a given period and
52lookup frequency. Here is an example retrieving the daily quotes for the last month:
53```rust
54use yahoo_finance_api as yahoo;
55use tokio_test;
56
57fn main() {
58    let provider = yahoo::YahooConnector::new().unwrap();
59    let response = tokio_test::block_on(provider.get_quote_range(\"AAPL\", \"1d\", \"1mo\")).unwrap();
60    let quotes = response.quotes().unwrap();
61    println!(\"Apple's quotes of the last month: {:?}\", quotes);
62}
63```
64
65# Search for a ticker given a search string (e.g. company name):
66```rust
67use yahoo_finance_api as yahoo;
68use tokio_test;
69
70fn main() {
71    let provider = yahoo::YahooConnector::new().unwrap();
72    let resp = tokio_test::block_on(provider.search_ticker(\"Apple\")).unwrap();
73
74    let mut apple_found = false;
75    println!(\"All tickers found while searching for 'Apple':\");
76    for item in resp.quotes
77    {
78        println!(\"{}\", item.symbol)
79    }
80}
81```
82Some fields like `longname` are only optional and will be replaced by default
83values if missing (e.g. empty string). If you do not like this behavior,
84use `search_ticker_opt` instead which contains `Option<String>` fields,
85returning `None` if the field found missing in the response.
86"
87)]
88//!
89#![cfg_attr(
90    feature = "blocking",
91    doc = "
92# Get the latest available quote (with blocking feature enabled):
93```rust
94use yahoo_finance_api as yahoo;
95use time::OffsetDateTime;
96
97fn main() {
98    let provider = yahoo::YahooConnector::new().unwrap();
99    // get the latest quotes in 1 minute intervals
100    let response = provider.get_latest_quotes(\"AAPL\", \"1d\").unwrap();
101    // extract just the latest valid quote summery
102    // including timestamp,open,close,high,low,volume
103    let quote = response.last_quote().unwrap();
104    let time: OffsetDateTime =
105        OffsetDateTime::from_unix_timestamp(quote.timestamp).unwrap();
106    println!(\"At {} quote price of Apple was {}\", time, quote.close);
107}
108```
109# Get history of quotes for given time period:
110```rust
111use yahoo_finance_api as yahoo;
112use time::{macros::datetime, OffsetDateTime};
113
114fn main() {
115    let provider = yahoo::YahooConnector::new().unwrap();
116    let start = datetime!(2020-1-1 0:00:00.00 UTC);
117    let end = datetime!(2020-1-31 23:59:59.99 UTC);
118    // returns historic quotes with daily interval
119    let resp = provider.get_quote_history(\"AAPL\", start, end).unwrap();
120    let quotes = resp.quotes().unwrap();
121    println!(\"Apple's quotes in January: {:?}\", quotes);
122}
123
124```
125# Get the history of quotes for time range
126Another method to retrieve a range of quotes is by requesting the quotes for a given period and
127lookup frequency. Here is an example retrieving the daily quotes for the last month:
128```rust
129use yahoo_finance_api as yahoo;
130
131fn main() {
132    let provider = yahoo::YahooConnector::new().unwrap();
133    let response = provider.get_quote_range(\"AAPL\", \"1d\", \"1mo\").unwrap();
134    let quotes = response.quotes().unwrap();
135    println!(\"Apple's quotes of the last month: {:?}\", quotes);
136}
137```
138# Search for a ticker given a search string (e.g. company name):
139```rust
140use yahoo_finance_api as yahoo;
141
142fn main() {
143    let provider = yahoo::YahooConnector::new().unwrap();
144    let resp = provider.search_ticker(\"Apple\").unwrap();
145
146    let mut apple_found = false;
147    println!(\"All tickers found while searching for 'Apple':\");
148    for item in resp.quotes
149    {
150        println!(\"{}\", item.symbol)
151    }
152}
153```
154"
155)]
156
157#[cfg(feature = "debug")]
158extern crate serde_json_path_to_error as serde_json;
159
160use std::sync::Arc;
161use std::time::Duration;
162use time::OffsetDateTime;
163
164#[cfg(feature = "blocking")]
165use reqwest::blocking::{Client, ClientBuilder};
166use reqwest::Proxy;
167#[cfg(not(feature = "blocking"))]
168use reqwest::{Client, ClientBuilder};
169
170// re-export time crate
171pub use quotes::decimal::Decimal;
172pub use time;
173
174mod quotes;
175mod search_result;
176mod yahoo_error;
177pub use quotes::{
178    AdjClose, AssetProfile, CapitalGain, CurrentTradingPeriod, DefaultKeyStatistics, Dividend,
179    ExtendedQuoteSummary, FinancialData, FinancialEvent, PeriodInfo, Quote, QuoteBlock, QuoteList,
180    QuoteType, Split, SummaryDetail, TradingPeriods, YChart, YMetaData, YQuoteBlock, YQuoteSummary,
181    YResponse, YSummaryData,
182};
183pub use search_result::{
184    YNewsItem, YOptionChain, YOptionChainData, YOptionChainResult, YOptionContract, YOptionDetails,
185    YQuote, YQuoteItem, YQuoteItemOpt, YSearchResult, YSearchResultOpt,
186};
187pub use yahoo_error::YahooError;
188
189const YCHART_URL: &str = "https://query1.finance.yahoo.com/v8/finance/chart";
190const YSEARCH_URL: &str = "https://query2.finance.yahoo.com/v1/finance/search";
191const Y_GET_COOKIE_URL: &str = "https://fc.yahoo.com";
192const Y_GET_CRUMB_URL: &str = "https://query1.finance.yahoo.com/v1/test/getcrumb";
193const Y_EARNINGS_URL: &str = "https://query1.finance.yahoo.com/v1/finance/visualization";
194
195// special yahoo hardcoded keys and headers
196const Y_COOKIE_REQUEST_HEADER: &str = "set-cookie";
197const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36";
198
199// Macros instead of constants,
200macro_rules! YCHART_PERIOD_QUERY {
201    () => {
202        "{url}/{symbol}?symbol={symbol}&period1={start}&period2={end}&interval={interval}&events=div|split|capitalGains"
203    };
204}
205macro_rules! YCHART_PERIOD_QUERY_PRE_POST {
206    () => {
207        "{url}/{symbol}?symbol={symbol}&period1={start}&period2={end}&interval={interval}&events=div|split|capitalGains&includePrePost={prepost}"
208    };
209}
210macro_rules! YCHART_RANGE_QUERY {
211  () => {
212    "{url}/{symbol}?symbol={symbol}&interval={interval}&range={range}&events=div|split|capitalGains"
213  };
214}
215macro_rules! YCHART_PERIOD_INTERVAL_QUERY {
216    () => {
217        "{url}/{symbol}?symbol={symbol}&range={range}&interval={interval}&includePrePost={prepost}"
218    };
219}
220macro_rules! YTICKER_QUERY {
221    () => {
222        "{url}?q={name}"
223    };
224}
225macro_rules! YQUOTE_SUMMARY_QUERY {
226    () => {
227        "https://query2.finance.yahoo.com/v10/finance/quoteSummary/{symbol}?modules=financialData,quoteType,defaultKeyStatistics,assetProfile,summaryDetail&corsDomain=finance.yahoo.com&formatted=false&symbol={symbol}&crumb={crumb}"
228    }
229}
230macro_rules! YEARNINGS_QUERY {
231    () => {
232        "{url}?lang={lang}&region={region}&crumb={crumb}"
233    };
234}
235
236/// Container for connection parameters to yahoo! finance server
237pub struct YahooConnector {
238    client: Client,
239    url: &'static str,
240    search_url: &'static str,
241    timeout: Option<Duration>,
242    user_agent: Option<String>,
243    proxy: Option<Proxy>,
244    cookie: Option<String>,
245    crumb: Option<String>,
246}
247
248#[derive(Default)]
249pub struct YahooConnectorBuilder {
250    inner: ClientBuilder,
251    timeout: Option<Duration>,
252    user_agent: Option<String>,
253    proxy: Option<Proxy>,
254}
255
256impl YahooConnector {
257    /// Constructor for a new instance of the yahoo connector.
258    pub fn new() -> Result<YahooConnector, YahooError> {
259        Self::builder().build()
260    }
261
262    pub fn builder() -> YahooConnectorBuilder {
263        YahooConnectorBuilder {
264            inner: Client::builder(),
265            user_agent: Some(USER_AGENT.to_string()),
266            ..Default::default()
267        }
268    }
269
270    /// Internal default implementation used exclusively by the builder.
271    /// Note: This default implementation does not set the user agent in the client,
272    /// so it does not work on its own. The builder will set the user agent.
273    fn default_internal() -> Self {
274        YahooConnector {
275            client: Client::default(),
276            url: YCHART_URL,
277            search_url: YSEARCH_URL,
278            timeout: None,
279            user_agent: Some(USER_AGENT.to_string()),
280            proxy: None,
281            cookie: None,
282            crumb: None,
283        }
284    }
285}
286
287impl YahooConnectorBuilder {
288    pub fn new() -> Self {
289        YahooConnector::builder()
290    }
291
292    pub fn timeout(mut self, timeout: Duration) -> Self {
293        self.timeout = Some(timeout);
294        self
295    }
296
297    pub fn user_agent(mut self, user_agent: &str) -> Self {
298        self.user_agent = Some(user_agent.to_string());
299        self
300    }
301
302    pub fn proxy(mut self, proxy: Proxy) -> Self {
303        self.proxy = Some(proxy);
304        self
305    }
306
307    pub fn build(mut self) -> Result<YahooConnector, YahooError> {
308        if let Some(timeout) = &self.timeout {
309            self.inner = self.inner.timeout(*timeout);
310        }
311        if let Some(user_agent) = &self.user_agent {
312            self.inner = self.inner.user_agent(user_agent.clone());
313        }
314        if let Some(proxy) = &self.proxy {
315            self.inner = self.inner.proxy(proxy.clone());
316        }
317
318        Ok(YahooConnector {
319            client: self.inner.use_rustls_tls().build()?,
320            timeout: self.timeout,
321            user_agent: self.user_agent,
322            proxy: self.proxy,
323            ..YahooConnector::default_internal()
324        })
325    }
326
327    pub fn build_with_client(client: Client) -> Result<YahooConnector, YahooError> {
328        Ok(YahooConnector {
329            client,
330            ..YahooConnector::default_internal()
331        })
332    }
333}
334
335#[cfg(not(feature = "blocking"))]
336pub mod async_impl;
337
338#[cfg(feature = "blocking")]
339pub mod blocking_impl;