ooples / OoplesFinance.YahooFinanceAPI

A .NET API wrapper for Yahoo Finance that allows users to get top trending stocks, historical stock data, stock splits, dividends, capital gains, and much much more!
Apache License 2.0
58 stars 13 forks source link

Not working anymore #91

Closed RkNxSR closed 1 month ago

RkNxSR commented 2 months ago

"Historical Data" has not been returning any results since Monday

Exception Message "Yahoo Finance Authentication Error"

davidclayton commented 1 month ago

It appears the previous ability to get a CSV style text response has been removed by Yahoo.

This URL previously worked:

https://query2.finance.yahoo.com/v7/finance/download/MSFT?period1=1640995200&period2=1672531200&interval=1d&events=history

But now it returns:

{"finance":{"result":null,"error":{"code":"unauthorized","description":"User is not logged in"}}}

If you adjust it to:

https://query2.finance.yahoo.com/v8/finance/chart/MSFT?period1=1640995200&period2=1672531200&interval=1d&events=history

It returns JSON that needs to be parsed to get the prices in a CSV style format you expect.

ooples commented 1 month ago

I will be doing an update to this library soon that should address these new changes that Yahoo has made. This URL info is very handy so please let me know if you have any other examples

On Tue, Sep 17, 2024, 9:56 PM David Clayton @.***> wrote:

It appears the previous ability to get a CSV style text response has been removed by Yahoo.

This URL previously worked:

https://query2.finance.yahoo.com/v7/finance/download/MSFT?period1=1640995200&period2=1672531200&interval=1d&events=history

But now it returns:

{"finance":{"result":null,"error":{"code":"unauthorized","description":"User is not logged in"}}}

If you adjust it to:

https://query2.finance.yahoo.com/v8/finance/chart/MSFT?period1=1640995200&period2=1672531200&interval=1d&events=history

It returns JSON that needs to be parsed to get the prices in a CSV style format you expect.

— Reply to this email directly, view it on GitHub https://github.com/ooples/OoplesFinance.YahooFinanceAPI/issues/91#issuecomment-2357332737, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXO4ZOD4SGGJWBEEYUCS63ZXDMUZAVCNFSM6AAAAABN6EVRBKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNJXGMZTENZTG4 . You are receiving this because you are subscribed to this thread.Message ID: @.***>

davidclayton commented 1 month ago

I lightly edited some code ChatGPT generated to extract the historical prices and put them in the text CSV format that was previously expected. I'm going to refactor my code to work directly with the JSON, but this is a quick and dirty helper for code that was expecting the previous CSV format.

public static string ParseJSONPrices(string rawJSON) { try { // Parse the JSON response var jsonObject = JObject.Parse(rawJSON);

     // Extract dates
     var timestamps = jsonObject["chart"]["result"][0]["timestamp"]
         .Select(t => DateTimeOffset.FromUnixTimeSeconds(t.Value<long>()).DateTime.ToString("yyyy-MM-dd"))
         .ToList();

     // Extract different price types
     var openPrices = ExtractPrices(jsonObject, "open");
     var highPrices = ExtractPrices(jsonObject, "high");
     var lowPrices = ExtractPrices(jsonObject, "low");
     var closePrices = ExtractPrices(jsonObject, "close");
     var adjustedClosePrices = ExtractPrices(jsonObject, "adjclose");
     var volumes = ExtractPrices(jsonObject, "volume");

     // Build the output string
     var lines = new List<string>();
     // Header line
     lines.Add("Date,Open,High,Low,Close,AdjClose,Volume"); 

     for (int i = 0; i < timestamps.Count; i++)
     {
         // Create line with date and all prices for that date
         string line = $"{timestamps[i]},{openPrices[i]},{highPrices[i]},{lowPrices[i]},{closePrices[i]},{adjustedClosePrices[i]},{volumes[i]}";
         lines.Add(line);
     }

     // Join all lines into a single string separated by newline characters
     string result = string.Join("\n", lines);

     return result;
 }
 catch
 {
     //ToDo:  Exception handling
     return String.Empty;
 }

}

// Helper method to extract prices by type from JSON static List<decimal?> ExtractPrices(JObject jsonObject, string priceType) { try { //All prices except Adjusted Close are in "quote", use "adjclose" for adjusted close if (priceType == "adjclose") { // Extract prices as nullable decimals return jsonObject["chart"]["result"][0]["indicators"]["adjclose"][0]["adjclose"] .Select(p => p.Value<decimal?>()) .ToList(); } else { // Extract prices as nullable decimals return jsonObject["chart"]["result"][0]["indicators"]["quote"][0][priceType] .Select(p => p.Value<decimal?>()) .ToList(); }

 }
 catch
 {
     // Return an empty list if the price type is not found
     return new List<decimal?>();
 }

}

Codepoet77 commented 1 month ago

@ooples thanks for your input on this issue. I'm encountering this as well.... just like everyone else (I'm assuming). Can you provide an estimated timeline for the update to the library you were referring to that will have the fix?

Codepoet77 commented 1 month ago

@davidclayton just curious how did you figure this out? Are there docs that indicate how to fetch this data from yahoo? If so can you share them?

RkNxSR commented 1 month ago

Thanks @davidclayton and @ooples

`public class StockPriceFetcher { private static readonly HttpClient client = new HttpClient();

public StockPriceFetcher()
{
    client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3");
}

public static DateTime UnixTimeStampToDateTime(long unixTimeStamp)
{
    DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(unixTimeStamp);
    return dateTimeOffset.DateTime;
}

public static long DateTimeToUnixTimeStamp(DateTime dateTime)
{
    DateTimeOffset dateTimeOffset = new DateTimeOffset(dateTime);
    return dateTimeOffset.ToUnixTimeSeconds();
}

public async Task<List<StockPriceItem>> FetchStockPrices(DateTime startDate, DateTime endDate, string stockName)
{        
    long period1 = DateTimeToUnixTimeStamp(startDate);
    long period2 = DateTimeToUnixTimeStamp(endDate);

    string url = $"https://query2.finance.yahoo.com/v8/finance/chart/{stockName}?period1={period1}&period2={period2}&interval=1d&events=history";

    List<StockPriceItem> stockPrices = new List<StockPriceItem>();

    try
    {
        string response = await client.GetStringAsync(url);

        JObject json = JObject.Parse(response);

        var timestamp = json["chart"]["result"][0]["timestamp"];
        var indicators = json["chart"]["result"][0]["indicators"]["quote"][0];
        var adjClose = json["chart"]["result"][0]["indicators"]["adjclose"][0]["adjclose"];

        for (int i = 0; i < timestamp.Count(); i++)
        {
            DateTime date = UnixTimeStampToDateTime((long)timestamp[i]);
            double? open = indicators["open"][i]?.ToObject<double?>();
            double? high = indicators["high"][i]?.ToObject<double?>();
            double? low = indicators["low"][i]?.ToObject<double?>();
            double? close = indicators["close"][i]?.ToObject<double?>();
            double? adjCloseVal = adjClose[i]?.ToObject<double?>();
            long? volume = indicators["volume"][i]?.ToObject<long?>();

            if (open.HasValue && high.HasValue && low.HasValue && close.HasValue && adjCloseVal.HasValue && volume.HasValue)
            {
                StockPriceItem stockPrice = new StockPriceItem
                {
                    Symbol = stockName,
                    Date = date,
                    Open = open.Value,
                    High = high.Value,
                    Low = low.Value,
                    Close = close.Value,
                    AdjClose = adjCloseVal.Value,
                    Volume = volume.Value
                };

                stockPrices.Add(stockPrice);
            }
        }
    }
    catch (HttpRequestException ex)
    {
        MessageBox.Show($"Error fetching data: {ex.Message}");
    }

    return stockPrices;
}

}

public class StockPriceItem { public string Symbol { get; set; }
public DateTime Date { get; set; }
public double Open { get; set; }
public double High { get; set; }
public double Low { get; set; }
public double Close { get; set; }
public double AdjClose { get; set; }
public double Volume { get; set; }
}`

`private async void btn_fill_list_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(txtB_stocks.Text)) { MessageBox.Show("Empty stock name"); return; }

StockPriceFetcher fetcher = new StockPriceFetcher();
DateTime startDate = date_Start.Value;
DateTime endDate = date_end.Value;

try
{
    List<StockPriceItem> stockPrices = await fetcher.FetchStockPrices(startDate, endDate, txtB_stocks.Text);

    dgw_stockPriceList.DataSource = stockPrices;
}
catch (Exception ex)
{
    MessageBox.Show(ex.ToString());
}

}`

img1

davidclayton commented 1 month ago

@davidclayton just curious how did you figure this out? Are there docs that indicate how to fetch this data from yahoo? If so can you share them?

I googled the error I was getting, found this redit thread, and then started experimenting with the URL, I too am looking for some kind of official documentation or statement

https://www.reddit.com/r/sheets/comments/1farvxr/broken_yahoo_finance_url/?rdt=46309

ooples commented 1 month ago

@Codepoet77 @davidclayton @RkNxSR I have a new version 1.6.5 that fixes the issues with the 4 csv methods: GetCapitalGainsAsync(), GetStockSplitDataAsync(), GetHistoricalDataAsync(), and GetDividendDataAsync()

If you guys can please help test and let me know if you run into any issues

davidclayton commented 1 month ago

I can confirm GetHistoricalDataAsync() is working as expected

ooples commented 1 month ago

Great to hear the new version is working for everyone. I will go ahead and close this ticket then

oscar811 commented 1 month ago

@ooples thank you for your great work. Most of quote work good but some of quote like "BUMI.JK" which volume would overflow. I believe the volume type should change to long instead of integer.

chart.result[0].indicators.quote[0].volume[10]

ooples commented 1 month ago

Can you do me a favor and send me the method you are using? I thought I changed all volumes to long but clearly I missed something

Get Outlook for Androidhttps://aka.ms/AAb9ysg


From: Oscar Iong @.> Sent: Saturday, October 5, 2024 9:49:45 PM To: ooples/OoplesFinance.YahooFinanceAPI @.> Cc: Franklin Moormann @.>; Mention @.> Subject: Re: [ooples/OoplesFinance.YahooFinanceAPI] Not working anymore (Issue #91)

@oopleshttps://github.com/ooples thank you for your great work. Most of quote work good but some of quote like "BUMI.JK" which volume would overflow. I believe the volume type should change to long instead of integer.

chart.result[0].indicators.quote[0].volume[10]

— Reply to this email directly, view it on GitHubhttps://github.com/ooples/OoplesFinance.YahooFinanceAPI/issues/91#issuecomment-2395255487, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAXO4ZPNBXV3DDSNXSSXUFDZ2CJLTAVCNFSM6AAAAABN6EVRBKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGOJVGI2TKNBYG4. You are receiving this because you were mentioned.Message ID: @.***>

oscar811 commented 1 month ago

@ooples , Here is the same code.

var historicalDataList = await yahooClient.GetHistoricalDataAsync("BUMI.JK", DataFrequency.Daily, startDate, null, true);

ooples commented 1 month ago

Please check out version 1.6.9 and confirm that this version fixes your issue

oscar811 commented 1 month ago

Works good to me