xemwebe / yahoo_finance_api

Simple wrapper to yahoo! finance API to retrieve latest quotes and end-of-day quote histories
Apache License 2.0
69 stars 35 forks source link

Not showing minute-by-minute data #38

Closed satvikpendem closed 8 months ago

satvikpendem commented 9 months ago

When I run:

use yahoo_finance_api::YahooConnector;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let provider = YahooConnector::new();
    let response = provider.get_quote_range("AAPL", "1d", "1mo").await.unwrap();
    // or even the equivalent:
    // let response = provider.get_latest_quotes("AAPL", "1d").await.unwrap();
    println!("{:#?}", response.quotes().unwrap());
}

I get daily data for one month. However, if I run

curl https://query1.finance.yahoo.com/v8/finance/chart/AAPL&interval=1d,range=1mo

I get minute-by-minute data, for one day at least (output here: aapl.json). This is also the case in Python's yfinance package:

import yfinance as yf

stock = yf.Ticker("AAPL")
x = stock.history(interval="1m", period="1d")
print(x)

Do you know why there's a discrepancy? It seems like yahoo-finance-api is just using the same query string that I curled above but it's being returned different results from the endpoint. Is there a way to get minute-by-minute data for several days or an entire month?

satvikpendem commented 9 months ago

Got the solution, tldr:

let response = provider.get_quote_range("AAPL", "1m", "1d").await.unwrap();

Interval means how fine-grained you want your updates to be, so in my initial comment, I used "1d" which predictably gave 1 day responses.

Range (or period in Python's yfinance) is how far back you want to look.

Seems like the interval needs to be "1m" (1 minute, not to be confused with "1mo", one month) and the range needs to be "1d" (1 day, also works with "5d", 5 days). If the range is greater than "5d" it throws an error, 422 Unprocessable Entity. It seems like the valid ranges from the JSON response are:

"validRanges": [
    "1d",
    "5d",
    "1mo",
    "3mo",
    "6mo",
    "1y",
    "2y",
    "5y",
    "10y",
    "ytd",
    "max"
]

I made a Markdown table of valid and invalid interval and range pairs via the following code:

use std::error::Error;
use yahoo_finance_api::YahooConnector;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let mut valid = Vec::new();
    let mut invalid = Vec::new();
    for interval in [
        "1m", "2m", "5m", "10m", "15m", "30m", "90m", "1h", "1d", "5d", "1wk", "1mo", "3mo", "6mo",
        "1y",
    ] {
        for range in [
            "1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max",
        ] {
            let response = provider.get_quote_range("AAPL", interval, range).await;

            match response {
                Ok(_) => valid.push((interval, range)),
                Err(_) => invalid.push((interval, range)),
            }
        }
    }

    println!("|Valid||");
    println!("|---|---|");
    println!("|Interval|Range|");
    for (interval, range) in valid {
        println!("|{}|{}|", interval, range)
    }

    println!("|Invalid||");
    println!("|---|---|");
    println!("|Interval|Range|");
    for (interval, range) in invalid {
        println!("|{}|{}|", interval, range);
    }
}
Valid
Interval Range
1m 1d
1m 5d
1m ytd
1m max
2m 1d
2m 5d
2m 1mo
2m ytd
2m max
5m 1d
5m 5d
5m 1mo
5m ytd
5m max
15m 1d
15m 5d
15m 1mo
15m ytd
15m max
30m 1d
30m 5d
30m 1mo
30m ytd
30m max
90m 1d
90m 5d
90m 1mo
90m ytd
90m max
1h 1d
1h 5d
1h 1mo
1h 3mo
1h 6mo
1h 1y
1h 2y
1h ytd
1h max
1d 1d
1d 5d
1d 1mo
1d 3mo
1d 6mo
1d 1y
1d 2y
1d 5y
1d 10y
1d ytd
1d max
5d 1d
5d 5d
5d 1mo
5d 3mo
5d 6mo
5d 1y
5d 2y
5d 5y
5d 10y
5d ytd
5d max
1wk 1d
1wk 5d
1wk 1mo
1wk 3mo
1wk 6mo
1wk 1y
1wk 2y
1wk 5y
1wk 10y
1wk ytd
1wk max
1mo 1d
1mo 5d
1mo 1mo
1mo 3mo
1mo 6mo
1mo 1y
1mo 2y
1mo 5y
1mo 10y
1mo ytd
1mo max
3mo 1d
3mo 5d
3mo 1mo
3mo 3mo
3mo 6mo
3mo 1y
3mo 2y
3mo 5y
3mo 10y
3mo ytd
3mo max
Invalid
Interval Range
1m 1mo
1m 3mo
1m 6mo
1m 1y
1m 2y
1m 5y
1m 10y
2m 3mo
2m 6mo
2m 1y
2m 2y
2m 5y
2m 10y
5m 3mo
5m 6mo
5m 1y
5m 2y
5m 5y
5m 10y
10m 1d
10m 5d
10m 1mo
10m 3mo
10m 6mo
10m 1y
10m 2y
10m 5y
10m 10y
10m ytd
10m max
15m 3mo
15m 6mo
15m 1y
15m 2y
15m 5y
15m 10y
30m 3mo
30m 6mo
30m 1y
30m 2y
30m 5y
30m 10y
90m 3mo
90m 6mo
90m 1y
90m 2y
90m 5y
90m 10y
1h 5y
1h 10y
6mo 1d
6mo 5d
6mo 1mo
6mo 3mo
6mo 6mo
6mo 1y
6mo 2y
6mo 5y
6mo 10y
6mo ytd
6mo max
1y 1d
1y 5d
1y 1mo
1y 3mo
1y 6mo
1y 1y
1y 2y
1y 5y
1y 10y
1y ytd
1y max

Note that while some pairs might say they are supported, in reality it seems like the Yahoo Finance endpoints just ignore them. For example, a "1h" interval for a "max" range will definitely not give you actual 1 hour data all the way back to when a stock was first public.

xemwebe commented 8 months ago

As you have figured out by yourself already, this is a feature of the Yahoo Finance API and not really something that can be changed by the crate. However, I think it would be worthwhile to somehow document this feature for other users of the crate, so maybe we could put this table in the README.md. What do you suggest?

satvikpendem commented 8 months ago

Yeah that sounds good to put it in the README. An explanation of interval vs range with some examples would work well also.

xemwebe commented 8 months ago

Your results are now included in the readme in some compressed form.