Dataframes aus verschiedenen Prozessen zusammenfügen

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.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Es sieht ein bisschen aus als hätte jemand versucht Java in Python zu programmieren.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

@LukeNukem: Wie bau ich da jetzt am besten den Rest vom Script ein?
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Samstag 10. Juli 2021, 11:12

Code: Alles auswählen

rat1 = quote_req.json()['quoteResponse']['result'][0].get('averageAnalystRating', 'None')
rating=''
	for i in rat1:
		if i.isalpha():
rating = "".join([rating, i])
Oder gibts da noch bessere Möglichkeit als nur Buchstaben rausfiltern?
Ungetestet:

Code: Alles auswählen

def filter_alpha(value):
    if isinstance(value, (bytes, str)):
        return ''.join([c for c in value if c.isalpha()])
    return value
    
df.rating = df.rating.apply(filter_alpha).astype('category')
Das ".astype()" muß nicht, konvertiert die Daten aber in kategorische Variablen, siehe dazu [1].

[1] https://pandas.pydata.org/pandas-docs/s ... rical.html
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Sonntag 11. Juli 2021, 19:00 @LukeNukem: Wie bau ich da jetzt am besten den Rest vom Script ein?
Du hast ja mehrere Teile, die die Daten aus unterschiedlichen Quellen ziehen und dann, zumindest teilweise, auf eine recht ähnliche Art konvertieren. Also nimmst Du Dir Deine anderen Programmteile und baust sie nach dem Vorbild um, das ich gezeigt habe. Das baust Du dann in den Hauptteil -- also: dem Teil unter "if __name__ ==..." ein, erzeugst aus Deinen neuen Klassen (bei mir: "YahooFun") also Instanzen (bei mir: "yf = YahooFun()"), übergibst die "Arbeitsfunktionen" (bei mir: "yahoo()") Deinem ThreadPoolExecutor und hängst die future-Objekte mit += an die Variable "futures" an.

Am Ende -- also: wenn alles durchgelaufen ist und alle future-Objekte in "futures" mit "result()" ihre Ergebnisse geliefert haben -- ziehst Du aus den genannten Instanzen dann Deine Pandas-DataFrames, wie ich in der letzten Zeile, baust mit "pandas.concat()" einen großen Ergebnis-Dataframe, und dann kannst Du damit viele lustige Dinge machen: sie einfach schnöde ausgeben, mit Altair oder Bokeh hübsche Plots daraus erzeugen, ganz wie Du lustig bist. Vorher würde ich Dir allerdings empfehlen, mal ein Python- und ein Pandas-Tutorial durchzuarbeiten... Viel Erfolg! ;-)
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

LukeNukem hat geschrieben: Sonntag 11. Juli 2021, 22:40
mirko3107 hat geschrieben: Samstag 10. Juli 2021, 11:12

Code: Alles auswählen

rat1 = quote_req.json()['quoteResponse']['result'][0].get('averageAnalystRating', 'None')
rating=''
	for i in rat1:
		if i.isalpha():
rating = "".join([rating, i])
Oder gibts da noch bessere Möglichkeit als nur Buchstaben rausfiltern?
Ungetestet:

Code: Alles auswählen

def filter_alpha(value):
    if isinstance(value, (bytes, str)):
        return ''.join([c for c in value if c.isalpha()])
    return value
    
df.rating = df.rating.apply(filter_alpha).astype('category')
Das ".astype()" muß nicht, konvertiert die Daten aber in kategorische Variablen, siehe dazu [1].

[1] https://pandas.pydata.org/pandas-docs/s ... rical.html
Wenn ich über die gleiche URL wie die anderen Werte den Wert "averageAnalystRating" abfrage, bekomm ich ab der Hälfte der Ticker-Werte keinen Wert mehr ausgelesen,
alle anderen funktionieren weiterhin. Der Wert verschwindet scheinbar, auch wenn man im Browser den Link zu oft abfragt.
Ich bleib dann bei meiner anderen URL, die scheint dauerhaft zu funktionieren.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Montag 12. Juli 2021, 09:53 Wenn ich über die gleiche URL wie die anderen Werte den Wert "averageAnalystRating" abfrage, bekomm ich ab der Hälfte der Ticker-Werte keinen Wert mehr ausgelesen,
alle anderen funktionieren weiterhin. Der Wert verschwindet scheinbar, auch wenn man im Browser den Link zu oft abfragt.
Möglich, ja. Einige andere Beitragende in diesem Thread haben ja schon angedeutet, daß es da serverseitige Limitierungen gibt -- und bei Yahoo ist es sogar so, daß offenbar auch der User-Agent geprüft wird.

Es ist halt so, daß die Börsenbetreiber und verschiedene andere tatsächlich bemerkt haben, daß man Börsentickerdaten a) für gutes Geld verkaufen kann, man b) mit reinen Datenabfragen keine Werbeeinnahmen generiert, und c), daß der Betrieb eigener Server nun einmal Geld kostet -- und daß diese Server und deren Betrieb immer teurer werden, je häufiger jemand diese Daten abfragt und je mehr Jemande das tun.

Insofern bieten sich Dir nun verschiedene Möglichkeiten: die Anzahl Deiner Abfragen so zu reduzieren und / oder zu verlangsamen, daß sie die serverseitigen Rate Limits nicht auslösen. Das kann Dir allerdings morgen oder übermorgen wieder auf die Füße fallen, wenn der Anbieter Deiner Daten entscheidet, die Rate Limits zu verschärfen. Du kannst natürlich auch Deine aktuelle Strategie weiterverfolgen und Deine Daten aus verschiedenen freien Quellen zusammenstoppeln, aber das birgt verschiedene Gefahren: die Daten könnten veraltet oder auf mehr oder weniger subtile Weise inkonsistent sein, und jeder neue Anbieter könnte von heute auf morgen entscheiden, Deine Quellen abzuschalten, neue oder verschärfte Rate Limits einzuführen, ... oder, und, oder.

Die letzte Möglichkeit ist daher, ich weiß, ein bisschen unkonventionell, langfristig jedoch die vermutlich stabilste Lösung: Du suchst Dir einen kommerziellen Anbieter und bezahlst für Deine Daten... und natürlich für die Server und deren Betreiber, die Dir diese Daten bereitstellen.

HTH, YMMV. ;-)
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

LukeNukem hat geschrieben: Sonntag 11. Juli 2021, 22:51
mirko3107 hat geschrieben: Sonntag 11. Juli 2021, 19:00 @LukeNukem: Wie bau ich da jetzt am besten den Rest vom Script ein?
Du hast ja mehrere Teile, die die Daten aus unterschiedlichen Quellen ziehen und dann, zumindest teilweise, auf eine recht ähnliche Art konvertieren. Also nimmst Du Dir Deine anderen Programmteile und baust sie nach dem Vorbild um, das ich gezeigt habe. Das baust Du dann in den Hauptteil -- also: dem Teil unter "if __name__ ==..." ein, erzeugst aus Deinen neuen Klassen (bei mir: "YahooFun") also Instanzen (bei mir: "yf = YahooFun()"), übergibst die "Arbeitsfunktionen" (bei mir: "yahoo()") Deinem ThreadPoolExecutor und hängst die future-Objekte mit += an die Variable "futures" an.

Am Ende -- also: wenn alles durchgelaufen ist und alle future-Objekte in "futures" mit "result()" ihre Ergebnisse geliefert haben -- ziehst Du aus den genannten Instanzen dann Deine Pandas-DataFrames, wie ich in der letzten Zeile, baust mit "pandas.concat()" einen großen Ergebnis-Dataframe, und dann kannst Du damit viele lustige Dinge machen: sie einfach schnöde ausgeben, mit Altair oder Bokeh hübsche Plots daraus erzeugen, ganz wie Du lustig bist. Vorher würde ich Dir allerdings empfehlen, mal ein Python- und ein Pandas-Tutorial durchzuarbeiten... Viel Erfolg! ;-)
Sollte ich die RSI/SMA-Berechnung auch mit in die yahoo-Funktion einbauen oder eine eigene Funktion dafür erstellen?

Für die Datenabfrage zu IB wäre sicherlich eine neue Class nötig, oder?
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Insofern bieten sich Dir nun verschiedene Möglichkeiten: die Anzahl Deiner Abfragen so zu reduzieren und / oder zu verlangsamen, daß sie die serverseitigen Rate Limits nicht auslösen.
Dass wir jetzt wieder an dem Punkte angelangt sind, auf den so viele schon von Anfang an hingewiesen hatten, ist dann aber doch irgendwie bemerkenswert.
Lösung: Du suchst Dir einen kommerziellen Anbieter und bezahlst für Deine Daten... und natürlich für die Server und deren Betreiber, die Dir diese Daten bereitstellen
Warum eine Tankstelle kaufen, wenn man nur tanken will?

Der kommerzielle Anbieter ist Yahoo (Oder jede andere Plattform, die Finanzdaten zur Verfügung stellt). Nur muss man denen eben ein bischen Geld geben wenn man große Mengen an Daten ziehen will.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Betrifft scheinbar nur diesen einen Wert und ein paar andere unwichtige, daher nicht weiter tragisch.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

@LukeNukem: Ich muss mittlerweile zugeben, ich bekomm es nicht so eingebaut, wie du es mir vorgeschlagen hast. Könntest du mir noch einen kleinen Tip geben oder einen Einstieg, wie ich das am besten umsetze?
Momentan hab ich die ganzen Berechnungssachen mit in die yahoo-Funktion eingebaut, aber ich denke mal, so war das von dir nicht gemeint.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Mittwoch 14. Juli 2021, 16:27 @LukeNukem: Ich muss mittlerweile zugeben, ich bekomm es nicht so eingebaut, wie du es mir vorgeschlagen hast.
Könntest du mir noch einen kleinen Tip geben oder einen Einstieg, wie ich das am besten umsetze?
Na klar, gerne... aber ich bin mir nicht ganz sicher, ob er Dir gefallen wird. Mein Tipp ist, erst die Grundlagen von Python und dann die von Pandas zu lernen. Also... nicht den Schlangen und den Bären, wohlgemerkt. ;-)
mirko3107 hat geschrieben: Mittwoch 14. Juli 2021, 16:27 Momentan hab ich die ganzen Berechnungssachen mit in die yahoo-Funktion eingebaut, aber ich denke mal, so war das von dir nicht gemeint.
Naja... mein ganzer Ansatz ist halt ein anderer als Deiner. Und damit meine ich nicht die Datenverarbeitung, sondern vor allem das Laufzeitmodell.

Mein Programmdesign betreibt einen zentralen ThreadPoolExecutor. Der ist dazu da, Deine Funktionen zu verwalten. Dem kannst Du (im Prinzip) so viele Funktionen übergeben, wie Du willst. Das können auch ganz verschiedene Funktionen sein: er wird sie in einem seiner Threads aufrufen, abwarten bis sie durchgelaufen ist und ein Ergebnis produziert hat. Und dann nimmt er sich die nächste wartende Funktion vor... so lange, bis alle Funktionen abgearbeitet sind [1]. Yay! ;-)

Außerdem gibt es einen zweiten großen Designunterschied: ich betrachte jeden Download -- also: jeden Ticker von jeder URL -- als einen Funktionsaufruf. (Eigentlich einen Methodenaufruf, aber geschenkt...) Dagegen betrachtet Deine Software jede Quelle als einen Funktionsaufruf. Anders gesagt: Du hast für jede Quelle eine Funktion, die aufgerufen wird, ich dagegen habe eine Funktion (Methode), die für jede Quelle + Ticker an den ThreadPoolExecutor übergeben wird.

Diese beiden Umstände bedeuten im Umkehrschluß: entweder, Du behältst Dein bisheriges Design bei und kannst dann höchstens meine Ideen zum Auslesen der Daten aus den Responses übernehmen. Oder, Du wirfst Deine bisherigen Lösungsansätze über den Haufen und baust Deine Software nach meinem Design um. Zumindest der zweite Ansatz wird es allerdings erfordern, daß Du Dir die Grundlagen von Python und die von Pandas aneignest. Und genau das möchte ich Dir aller-, aller-, aller- und allerwärmstens empfehlen. Die Idee meiner Idee ist nicht, sie zu kopieren. Sondern, sie zu verstehen und zu adaptieren. ;-)

[1] Für meine pedantisch orientierten Freunde: das ist nur teilweise korrekt, aber sei's drum. ;-)
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Ich hab jetzt alles, was von yahoo kommt, in die Yahoo-Class eingebaut, da lieg ich pro Abruf bei knapp 1s, völlig ausreichend.

Ich habe jetzt versucht, den IB-Part einzubauen, aber da meldet er mir folgendes:

Code: Alles auswählen

 File "/usr/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'ThreadPoolExecutor-0_0'.
sys:1: RuntimeWarning: coroutine 'IB.connectAsync' was never awaited
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Das hatten wir doch schon vor Tagen. Sowohl multiprocessing als auch threads sind falsch. Schreibe asynchron Funktionen. Lass dich von anderen Mitschreibenden nicht in die Irre führen. Am besten fängt man mit einem sehr einfachen Beispiel an, dass z.b. asynchrone Funktionsaufrufen verschiedene Dataframes zurückgeben, die Du dann zu kombinieren versuchst.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@mirko3107,

wenn du nach "ib_insync threading" suchst, wirst du schnell merken, dass du nicht der einzige bist, der es so versucht hat und nicht weiter gekommen ist.
Die Antwort war aber immer: "Benutze asyncio"

Ich kann die Hemmschwelle bzgl. asyncio durchaus verstehen. So schwer ist es aber gar nicht wenn man sich etwas einliest.
ib_insync ist so geschrieben, dass es sich nach außen anfühlt, als ob es einfach synchron abläuft. Das funktioniert bei einzelnen Datenabfragen auch noch problemlos. Wenn man aber mal in den Sourcecode schaut, sieht man, dass alle Datenabfragen asynchron ablaufen.

Daher würde jeder, der sich da ein wenig auskennt, die Applikation drum herum auch asynchron schreiben.

Ich kann es bei mir leider nicht ausprobieren, da man dafür anscheinen einen Zugang braucht.
Das ist das Beispiel aus der ib_insync Dokumentation mit asynchronem Aufruf für mehrere Symbole.
So würde ich anfangen, wenn ich es ausprobieren könnte. Nur ein Versuch und ungetestet.

(Wie man sich die Daten von Yahoo holt, hatte ich ja schon in einem früheren Post gezeigt)

Code: Alles auswählen

import asyncio
from ib_insync import IB, Forex

ib = IB()
ib.connect('127.0.0.1', 7497, clientId=1)

async def get_data(symbol):
    contract = Forex(symbol)
    bars = await ib.reqHistoricalData(
        contract, endDateTime='', durationStr='30 D',
        barSizeSetting='1 hour', whatToShow='MIDPOINT', useRTH=True)
    return bars


async def main():
    # holt die historischen Daten der angegebenen Symbole
    all_data = await asyncio.gather(*[get_data(symbol) for symbol in ["FOREX", "AMZN", "GOOG"]])
    # all_data enthält Liste aller Daten


loop = asyncio.get_event_loop()
loop.run_until_complete(main())
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Es muss ja nicht unbedingt async sein bei ib_insync, mir reicht es, wenn der Teil so mal mit durchläuft. Die Werte, die da ausgespuckt werden, ändern sich nicht stündlich.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Ich habe jetzt einiges probiert, aber mit wenig Erfolg.
Ich hab mein Script vom Beginn mal wieder rausgekramt und wollte da Dinge einbauen, die in den letzten Tage denke gelernt zu haben, besser gesagt gelesen.

Das Script funktioniert im Grunde, nicht schön wie ihr durchaus bemerkt habt, aber läuft.

Wenn ich aus meiner CSV-Datei per

Code: Alles auswählen

yf_df = pd.read_csv(file, sep=',')
tickers = yf_df.Ticker.tolist()
eine Ticker-Liste erstelle und diese dann per

Code: Alles auswählen

p1 = multiprocessing.Process(target=rsi, args = (tickers, ))
an den jeweiligen Prozess schicke, läuft zb. dieser hier durch:

Code: Alles auswählen

def rsi(tickers):
    rsilist = []
    while True:
        for ticker in tickers:
            try:
                rsi = pdr.get_data_yahoo(ticker, dt.datetime(2021, 5, 1), dt.datetime.now(),session=session)
                delta = rsi['Close'].diff()
                up = delta.clip(lower=0)
                down = -1 * delta.clip(upper=0)
                ema_up = up.ewm(com=13, adjust=False).mean()
                ema_down = down.ewm(com=13, adjust=False).mean()
                rs = ema_up / ema_down
                rsi['RSI'] = 100 - (100 / (1 + rs))
                rsi_value = rsi.iloc[-1]['RSI']
                print(ticker, rsi_value)
                rsilist.append(rsi_value)
            except Exception as e:
                logging.exception('Something went terribly wrong')
                return [0.0] * 4
Wie bekomm ich die Ergebnisse dieses Prozesses wieder aus diesem heraus?

Vielen Dank
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Freitag 16. Juli 2021, 18:39 Wie bekomm ich die Ergebnisse dieses Prozesses wieder aus diesem heraus?
Naja, wenn Du es mit Multiprocessing machst, dann müssen die Daten natürlich vom sendenden Prozeß serialisiert werden -- etwa mit pandas.DataFrame.to_pickle() und vom lesenden Prozeß wieder deserialisiert werden, beispielsweise mit pandas.read_pickle(). Aber die Kollegen haben natürlich Recht: wenn dieses ib-Dingsi asynchron arbeitet, solltest Du beim Rest ebenfalls asynchron arbeiten. Man kann die Ansätze zwar mischen, aber... dazu sollte man UNBEDINGT und sehr GENAU wissen, was man tut und warum man es tut. ;-)
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Also mit async und ib_insync hatte ich bisher keinen Erfolg, per multiprocessing läuft es aber, reicht mir völlig.

MIt multiprocessing.Pool ist es aber nicht möglich, Funktionen parallel auszuführen, oder?
Hab einen Pool für yahoo mit 4 Prozessen und einen Pool für ib_insync mit 1 Prozess, welche aber nacheinander laufen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sinn und Zweck von multiprocessing *ist* Parallelitaet. Wenn das bei dir nicht parallel laeuft, ist da was komisch.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

die Funktionen, die ich aufrufe, laufen auch parallel in mehreren Prozessen (ausser ib_insync), aber ich hab es nicht hinbekommen, während Funktion "Yahoo" läuft, parallel dazu die Funktion "IB" laufen zu lassen.
Antworten