API- Abruf sehr langsam

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Monjy
User
Beiträge: 15
Registriert: Dienstag 30. August 2022, 14:02

Hallo zusammen,

ich bin Pythonneuling und hab bisher in der Programmierung lediglich erste Gehversuche in HTML CSS und JS gemacht, was aber garnicht der Rede wert ist. Nun möchte mir diverse Finanzkennzahlen aus der yfinace API ziehen, deshalb beschäftige ich mich zur Zeit mit Python. Das klappt auch soweit. Hier unten habe ich mal einen Teil in einem ersten Schritt mit der Microsoft-Aktie dargestellt. Soweit so gut klappt alles prima und schnell. Der Abruf des gesamten Codes ist in 3s fertig.

Code: Alles auswählen

import yfinance as yf 

microsoft = yf.Ticker("MSFT")

# Sammlung Daten

# Land
country = microsoft.info["country"]
print('Land: ' + country)

# aktueller Kurs
kurs = microsoft.info["regularMarketPrice"]
print('aktueller Kurs: ' + str(kurs))

# 52 Wochen Hoch
high52 = microsoft.info['fiftyTwoWeekHigh']
print('52Wochen Hoch: ' + str(high52))

# 52 Wochen Tief
low52 = microsoft.info['fiftyTwoWeekLow']
print('52Wochen Tief: ' + str(low52))

# Anzahl Aktien
sharesOutstanding = microsoft.info["sharesOutstanding"]
print('Anzahl Aktien: ' + str(sharesOutstanding))

# Marktkapitalisierung
marketCap = microsoft.info["marketCap"]
print('Marktkapitalisierung: ' + str(marketCap))

# Gewinn 2022
netIncome = microsoft.info["netIncomeToCommon"]
print('Gewinn 2022: ' + str(netIncome))

# EPS 2023e
erwarteteEPS2023 = microsoft.analysis['Earnings Estimate Avg']['0Y']
print('erwartete EPS 2023: ' + str(erwarteteEPS2023))
Nun möchte ich gerne mehrere Aktien abfragen indem ich eine Variable verändere. Ich habe hier ein Dictionary verwändet weil ich aus einer anderen API weitere Finanzkennzahlen ziehen möchte
und diese die Aktienbezeichung braucht und das Ticker-Symbol nicht funktioniert. Da ich hier nur einen Ausschnitt Poste um es nicht zu unübersichtlich zu machen sieht man das mit dem hier gepusteten Code nicht. Auch diese Abfrage funktioniert aber viel langsamer. Über eine Minute. Gut er fragt 2 Aktien ab aber oben 3s hier über eine Minute.

Kann mir jemand sagen warum das unten so langsam ist und wie ich die Abfrage beschleunigen kann ?

Ich habe vor sehr viele Kennzahlen von sehr vielen Aktien abzufragen... das würde ja Stunden dauern :(

Code: Alles auswählen

import yfinance as yf 
 
stockDic = {"MSFT": "Microsoft" , "AAPL": "Apple"}



def get_stockdata(ticker, aktienname):
  yfstock = yf.Ticker(ticker)
  estimates = stocks.get_estimates(stock=aktienname)

  # Land
  country = yf.Ticker(ticker).info["country"]
  print('Land: ' + country)

  # aktueller Kurs
  stock_price = yf.Ticker(ticker).info["regularMarketPrice"]
  print('aktueller Kurs: ' + str(stock_price))

  # 52 Wochen Hoch
  high52 = yf.Ticker(ticker).info['fiftyTwoWeekHigh']
  print('52Wochen Hoch: ' + str(high52))

  # 52 Wochen Tief
  low52 = yf.Ticker(ticker).info['fiftyTwoWeekLow']
  print('52Wochen Tief: ' + str(low52))

  # Anzahl Aktien
  shares_outstanding = yf.Ticker(ticker).info["sharesOutstanding"]
  print('Anzahl Aktien: ' + str(shares_outstanding))

  # Marktkapitalisierung
  market_cap = yf.Ticker(ticker).info["marketCap"]
  print('Marktkapitalisierung: ' + str(market_cap))

  # Gewinn letztes Jahr
  net_income_last_year = yf.Ticker(ticker).info["netIncomeToCommon"]
  print('Gewinn 2022: ' + str(net_income_last_year))

  # EPS laufendes Jahr
  expEPS_current_year = yf.Ticker(ticker).analysis['Earnings Estimate Avg']['0Y']
  print('erwartete EPS laufendes Jahr: ' + str(expEPS_current_year))

for ticker, aktienname in stockDic.items():
  get_stockdata(ticker, aktienname)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 2. Variablennamen schreibt man komplett klein, Konstanten komplett GROSS.
Strings stückelt man nicht mit + zusammen, sondern nutzt Formatstrings.
Im oberen Beispiel fragst Du den Ticker einmal ab, unten bei jedem einzelnen Wert, also insgesamt 9mal pro Aktie.
Monjy
User
Beiträge: 15
Registriert: Dienstag 30. August 2022, 14:02

Hallo Sirius,

ganz lieben Dank für deine Hilfe!
Auch für die Hinweise zu den korrekten Gepflogenheiten bei der Codeeingabe.

In dem Beispielcode komme ich nun auf 4 Secs. Alle Daten erscheinen nun zeitgleich in der Console, woraus ich mal schließe dass nur eine Abfrage stattfindet.
Ich habe verstanden dass die zeitliche Verzögerung durch die Vielzahl an Abfragen entstanden ist. Diese sollte dann also logischerweise auf das nötigste begrenzt werden..

Was ist noch nicht ganz verstanden habe ist, was da denn nun konkret im Programm passiert. Ich stelle mir die Frage:
Warum startet er mit yf.Ticker eine Abfrage und mit yfstock.info"MSFT" nicht ? die Variable yfstock ist doch nur ein "Platzhalter" für yf.Ticker und ist gleich yf.Ticker.info"MSFT" ?
Oder werden mit der abfrage yf.Ticker sämtliche Informationen die hier zur Verfügung gestellt werden in diese Variable gespeichert ?
Das müssen Millionen von Werten sein. Yahoo Finance führt ja tausende Aktien. Jede für sich hat tausende einzelne Infos ... Das hatte ich in meiner Denke erstmal
ausgeschlossen. Aber es muss ja so sein , sonst müssen ja weitere Abfragen erfolgen... oder wo ist mein Denkfehler ?

Code: Alles auswählen

import yfinance as yf 
 
stockDic = {"MSFT": "Microsoft" , "AAPL": "Apple"}
yfstock = yf.Ticker(ticker)


def get_stockdata(ticker, aktienname):
  

  # Land
  country = yfstock.info["country"]
  print(f'Land: {country}')

  # aktueller Kurs
  stock_price = yfstock.info["regularMarketPrice"]
  print(f'aktueller Kurs:  {stock_price}')

  # 52 Wochen Hoch
  high52 = yfstock.info['fiftyTwoWeekHigh']
  print(f'52Wochen Hoch: {high52}')

  # 52 Wochen Tief
  low52 = yfstock.info['fiftyTwoWeekLow']
  print(f'52Wochen Tief: {low52}')

  # Anzahl Aktien
  shares_outstanding = yfstock.info["sharesOutstanding"]
  print(f'Anzahl Aktien: {shares_outstanding}')

  # Marktkapitalisierung
  market_cap = yfstock.info["marketCap"]
  print(f'Marktkapitalisierung: {market_cap}')

  # Gewinn letztes Jahr
  net_income_last_year = yfstock.info["netIncomeToCommon"]
  print(f'Gewinn 2022: has {net_income_last_year}')

  # EPS laufendes Jahr
  exp_eps_current_year = yfstock.analysis['Earnings Estimate Avg']['0Y']
  print(f'erwartete EPS laufendes Jahr: {exp_eps_current_year}')

for ticker, aktienname in stockDic.items():
  get_stockdata(ticker, aktienname)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Monjy: Der Code den Du da jetzt zeigst läuft nicht, weil `ticker` in Zeile 4 nicht definiert ist.

Die Abfrage wird beim Erstellen des `Ticker`-Objekts gemacht. Und das muss man für jede Aktie einzeln machen, also ein Abruf der Daten pro Aktie. Wenn Du Dein Programm korrigierst, sind das dann also zwei Abfragen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Monjy: Bei einem Namen nach dem Muster `get_*()` erwartet der Leser, dass diese Funktion Daten als Rückgabewert liefert, und nicht das sie nur etwas ausgibt.

Kommentare sollten dem Leser einen Mehrwert über den Code bieten. Faustregel: Ein Kommentar beschreibt nicht was der Code macht, denn das steht dort bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in der Regel was in der Dokumentation von Python oder den verwendeten Bibliotheken steht. Du hast da immer nur noch mal das als Kommentar hin geschrieben was zwei Zeilen später im `print()` noch mal genau so steht.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from datetime import datetime as DateTime

import yfinance as yf

SHORT_NAME_TO_NAME = {"MSFT": "Microsoft", "AAPL": "Apple"}


def get(mapping, path):
    if isinstance(path, str):
        path = [path]
    result = mapping
    for path_element in path:
        result = result[path_element]
    return result


def print_stockdata(abkuerzung, aktienname):
    print(aktienname)
    now = DateTime.now()
    stock = yf.Ticker(abkuerzung)
    for beschreibung, pfad in [
        ("Land", "country"),
        ("aktueller Kurs", "regularMarketPrice"),
        ("52 Wochen Hoch", "fiftyTwoWeekHigh"),
        ("52 Wochen Tief", "fiftyTwoWeekLow"),
        ("Anzahl Aktien", "sharesOutstanding"),
        ("Marktkapitalisierung", "marketCap"),
        (f"Gewinn {now.year}", "netIncomeToCommon"),
        ("erwartete EPS laufendes Jahr", ["Earnings Estimate Avg", "0Y"]),
    ]:
        print(f"{beschreibung}: {get(stock.info, pfad)}")


def main():
    for abkuerzung, aktienname in SHORT_NAME_TO_NAME.items():
        print_stockdata(abkuerzung, aktienname)


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Monjy
User
Beiträge: 15
Registriert: Dienstag 30. August 2022, 14:02

Sorry für das späte bedanken. Meine Tochter hat ein Bazillus aus der KITA mitgebracht was mich 2 Wochen ausser Gefecht gesetzt hat.

Also besser spät als nie @__blackjack__ herzlichen Dank für deine Mühe !

Ich muss gestehen dass ich auf den ersten Blick nur Bahnhof von deinem Code verstehe ... was ist path ? was ist datatime ... da muss ich mich erst einlesen.

Komischerweise hat mein Code so wie ich Ihn oben aus Colaboratory kopiert habe tatsächlich nach einem Umbau funktioniert ... daher hab ich ja auch die 4sec. KA ob da irgendwas vorabgespeichtert wurde...
Jetzt ist es genau wie du sagst und er sagt mir das ticker nicht definiert ist.

Ist sicher nicht leicht einem Neuling der so dummes Zeug schreibt wie ich zu erklären wies laufen soll, umso höher ist euch anzurechnen dass ihr es versucht.

Also, du sagst dass es nicht möglich ist mehrere Aktien Gleichzeitig abzufragen... ich muss jede einzeln abfragen und das dauert dann eben pro abfrage 3-4 Sekunden ?

Also wenn ich 100 Aktien abfragen möchte muss ich 2 Minuten warten ? Hab ich das richtig verstanden ?

2. Frage ist ... wenn ich die Daten aus der API in eine eigene SQL Datenbank übertragen würde ... sind Abfragen aus einer eigenen SQL Datenbank schneller ?
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Ein Blick in die Doku lohnt sich immer. Hier wird das Vorgehen für die Abfrage von mehreren Tickern beschrieben. Unter anderem gibt es auch die Möglichkeit Threads zu nutzen, wes dein Vorhaben deutlich beschleunigt.
Abfragen an eine eigene Datenbank ist in der Regel schneller - allerdings müssen ja auch die Daten dann von der API in die Datenbank gelangen, was also mindestens genauso lange dauert.
Monjy
User
Beiträge: 15
Registriert: Dienstag 30. August 2022, 14:02

ach ja, die Beschreibung die ich mit Print ausgebe sind nicht wichtig und nur für mich gewesen ... ich will erstmal die Daten möglichst effizient auslesen, dann muss ich mir überlegen wir ich mir die Daten am übersichtlichsten ausgeben lasse , dass ich auch Sortierungen und Filter anwenden kann... ich denke ich werde versuchen die Daten via html,css und JS auszugeben ...jetzt bin ich aber auch auf das Thema GUI gestoßen ... damit will ich mich auch mal beschäftigen ob das eine Alternative sein kann.

Wahrscheinlich mag man meine Einstellung zum Programmieren in solch einem Forum nicht. Ich möchte mir mit möglichst wenig Programmieraufwand ein Aktienanalysetool wie ich es brauche bauen.
Jetzt sagt ihr wahrscheinlich (verständlicherweise) lern erstmal anständig programmieren bevor du sowas bauen willst. Leider hat der Tag nur 24h und es ist schon sehr aufwendig neben dem Job im Aktienthema informiert zu bleiben ... dabei soll das tool helfen, also Zeit ersparen... für eine umfängliche Programmierausbildung fehlt da einfach die Zeit so gern ich das auch machen würde um mir auch ausserhalb der Geldanlage kleine Helferlein zu bauen.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

@Monjy: Ich sehe da eine oft zu beobachtende Fehlannahme. Wie du selbst schreibst, willst du etwas tun, das dir Zeit spart. Dann solltest du auch die Zeit investieren, die dafür nötig ist, um das vernünftig umsetzen zu können. Am Ende zahlt sich das ja aus.
Monjy
User
Beiträge: 15
Registriert: Dienstag 30. August 2022, 14:02

@sparrow ist absolut richtig was du schreibst ! Dazu kommt noch dass es mir echt Spaß macht zu "programmieren" (also die kläglichen Versuche das zu tun).
Aber mit einem de facto 50 Std. Job und zwei Kleinkindern (1 und 2,5 Jahre) bleibt einem ohnehin für nichts mehr Zeit. Dann habe ich den "Fehler" gemacht und habe mich mal etwas
intensiver mit meiner Altersvorsorge beschäftig und festgestellt dass ein wesentlicher Teil für Bank oder Versicherungskosten drauf gehen und entschieden das Thema in die eigenen Hände zu nehmen. Das läuft auch sehr gut und hat mir allein in den letzten 2 Jahren einen sehr hohen Profit im Vergleich dazu gebracht wie ich es vorher laufen hatte. Mit Fehler meine ich... mir macht es das Wissen um die hohen Kosten und der suboptimalen Anlage unmöglich mich nicht mehr selber um das Thema zu kümmern. Leider ist auch dieses Thema ein riesen Zeitfresser wenn man es richtig macht. Die Frage ist nun wie viel Zeit investiert man für das Mittel zum Zweck ?

@blackjack
ich habe nun die Zuweisung der yfstock Variable in die Funktion gepackt. So wird jede Aktie einzeln abgefragt. Bevor ich mich mit deinem Vorschlag beschäftige wozu ich wahrscheinlich so schnell nicht zu kommen werde, welchen Nachteil hat denn meine Variante ? Also so:

Code: Alles auswählen

import yfinance as yf 
 
stockDic = {"MSFT": "Microsoft" , "AAPL": "Apple"}



def get_stockdata(ticker, aktienname):
  yfstock = yf.Ticker(ticker)  

  # Land
  country = yfstock.info["country"]
  print(f'Land: {country}')

  # aktueller Kurs
  stock_price = yfstock.info["regularMarketPrice"]
  print(f'aktueller Kurs:  {stock_price}')

  # 52 Wochen Hoch
  high52 = yfstock.info['fiftyTwoWeekHigh']
  print(f'52Wochen Hoch: {high52}')

  # 52 Wochen Tief
  low52 = yfstock.info['fiftyTwoWeekLow']
  print(f'52Wochen Tief: {low52}')

  # Anzahl Aktien
  shares_outstanding = yfstock.info["sharesOutstanding"]
  print(f'Anzahl Aktien: {shares_outstanding}')

  # Marktkapitalisierung
  market_cap = yfstock.info["marketCap"]
  print(f'Marktkapitalisierung: {market_cap}')

  # Gewinn letztes Jahr
  net_income_last_year = yfstock.info["netIncomeToCommon"]
  print(f'Gewinn 2022: has {net_income_last_year}')

  # EPS laufendes Jahr
  exp_eps_current_year = yfstock.analysis['Earnings Estimate Avg']['0Y']
  print(f'erwartete EPS laufendes Jahr: {exp_eps_current_year}')

for ticker, aktienname in stockDic.items():
  get_stockdata(ticker, aktienname)
Im Colaboratory funktioniert der Code so... nur wird halt jede Aktie einzeln abgefragt.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Monjy: Ist halt länger und hat einen schlechten Funktionsnamen. *Der* ist IMHO wirklich ein Problem. Wie gesagt, bei `get_*()` erwartet man einen Rückgabewert, nicht das die Funktion was ausgibt.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Monjy
User
Beiträge: 15
Registriert: Dienstag 30. August 2022, 14:02

Okay den ändere ich noch ... stimmt hattest du oben schon erwähnt... danke für den Tipp ... ich habe halt wegen keiner Erfahrung keine Ahnung von
den Gepflogenheiten was Namensgebung von Variablen oder Funktionen betrifft.
Antworten