henrydatei / wikifolio-api

A Python API-Wrapper for the unofficial wikifolio API
GNU General Public License v3.0
17 stars 4 forks source link

wf.buy error #17

Open JoeGithub53 opened 9 months ago

JoeGithub53 commented 9 months ago

Hello, have got following error on executing wf.buy script from classes.wikifolio import Wikifolio

wf = Wikifolio("email", "password", "wikifolioID") wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day PS

D:\python_script_depot> python buy-apple.py ... {"success":false,"needsTfaReAuth":true,"needsPasswordReAuth":false} Traceback (most recent call last): File "D:\python_script_depot\buy-apple.py", line 4, in wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\python_script_depot\classes\wikifolio.py", line 579, in buy_limit return OrderResponse(**raw_json) ^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: OrderResponse.init() got an unexpected keyword argument 'needsTfaReAuth'

regards, Josef

henrydatei commented 9 months ago

I can't reproduce this error but it looks like there is a problem with Two-Factor-Authentication. I don't have 2FA enables on my account, there is a discussion going on at https://github.com/henrydatei/wikifolio-api/issues/3 but the conclusion is making orders with 2FA enabled is hard. What you can try is call Wikifolio with

wf = Wikifolio("email", "password", "wikifolioID", "2FA key")

where the 2FA key is the inital seed for TOTP. Do you use 2FA?

From your response I fixed the error happening there but you won't be able to trade. You just get a better error message.

JoeGithub53 commented 9 months ago

you are right. My user uses a 2Fa Key. but error message seems like the test wihout 2Fa Key. But I've also good news. Have tried it also with a user, which doesn't use 2Fa authority. And test was successfully. Do you think there is a way to get it also with 2Fa Keys running ?. Is in wikifolio.py a function to get the stock price ?

this is the output of the 2FA Test from classes.wikifolio import Wikifolio

wf = Wikifolio("email", "password", "wikifolioID", "2FA key") wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day

PS D:\python_script_depot> python buy-apple2fa.py ... {"success":false,"needsTfaReAuth":true,"needsPasswordReAuth":false} Traceback (most recent call last): File "D:\python_script_depot\buy-apple2fa.py", line 5, in wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\python_script_depot\classes\wikifolio.py", line 579, in buy_limit return OrderResponse(**raw_json) ^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: OrderResponse.init() got an unexpected keyword argument 'needsTfaReAuth'

This is the Ouptut of the successful Test without 2FA: PS D:\python_script_depot> python buy-apple2fa.py ... {"success":true,"reason":"","orderGuid":"b8c7b9fa-66b9-40ca-8fb8-89a31ad94175"}

henrydatei commented 9 months ago

For the user without 2FA this is not a error message, it's just a lot of output (mostly the login cookies, but I removed the output, so you should see less text. You just need to pull the lastest commits)

I don't now if there is a way to get this running with 2FA. I don't have a 2FA account so I can't reverse the API and do testing. You can try to implement this and make a pull request, I would be really happy.

Wikifolio doesn't have a function to show stock prices. The way the do it is to use websockets and show the data directly from the L&S exchange. If you want to get some stock prices you can have a look at yfinance

JoeGithub53 commented 9 months ago

hello henrydatei, thank you to remove Login-Cookies. so nobody wonder about this. It has been a privilege to work with you. To solve the 2FA issue, my suggestion would be to contact the founder of Wikifolio to arrange a meeting in Vienna. I am also Austrian and hope to advocate for you, so that together with the founder of Wikifolio, we can obtain the support needed to solve this task collaboratively. Please let me know if such a meeting in Vienna would be possible for you. It would be a great honor for me to have the opportunity to meet you and the founder of Wikifolio on this occasion. regards Josef

henrydatei commented 9 months ago

I don't know if the founder of Wikifolio can help us there and I don't have a contact to him. But if you have, feel free to organise a meeting. Vienna is quite far from me so a in person meeting during the week is problematic, I woul rather prefer a meeting on a weekend or even virtual. And before solving the 2FA issue it would be much more helpful if Wikifolio could offer a public API. 2FA support is then usually included and easy implementable.

JoeGithub53 commented 9 months ago

Der Wikifolio Gründer kann eventuell dann hilfreich sein, wenn wir es über die offiziellen Wege nicht schaffen, an die benötigten Informationen zu kommen. Aber schon mal gut, wenn Sie so einem Treffen, das auch virtuell sein kann, nicht abgeneigt sind. Wären Sie damit einverstanden, wenn ich den Wikifolio Service anschreibe, ob Wikifolio eine öffentliche API anbietet ?. Da die 2FA-Unterstützung über den Wikifolio Dialog im Einsatz ist, muß der Support von Wikifolio dazu eine Dokumentation haben. Bei mir ist es so, ich habe mir am Handy die Google Authenticator App installiert. Handle ich über meinen User der 2FA im Profile aktiviert hat, dann muß ich beim ersten Mal bei einem Kauf oder Verkauf die 6 stellige Zahl vom Google Authenticator eingeben. Danach wird die Authorisierung erst wieder angefordert, wenn ich eine Stunde nichts mache. Das hört sich eigentlich relativ einfach an. Wie ich gesehen habe, kann ich ja auch in Ihrem Script die vom Google Authenticator abgelesene Zahl mitgeben. Aber wir wissen zur Zeit nicht, was nach der Eingabe alles abläuft. Deshalb bin ich auch dafür, daß wir erst mal bei Wikifolio eine Anfrage stellen sollten.

henrydatei commented 8 months ago

Du kannst natürlich gerne nachfragen, ob es mittlerweile eine offizielle API gibt, aber ich glaube das nicht. Schon seit mindestens einem Jahr steht auch im Raum, dass Wikifolio seine Webseite komplett ändern möchte, was mehr oder weniger alle API Wrapper unbrauchbar machen sollte. In dem Atemzug wurde auch erwähnt, dass man über eine offizielle API nachdenkt, aber bis heute ist da nichts passiert. Aber nachfragen schadet nicht.

Den 2FA Code, die du in meinem Skript eingeben musst, ist nicht der Code, den Google Authenticator dir anzeigt. 2FA funktioniert prinzipiell so, dass du bei der Einrichtung einen QR Code scannst, welcher einen Seed enthält. Mit diesem Seed kann dann zu jeder Zeit ein 2FA Code erzeugt werden und das ist auch nur das, was Google Authenticator dir anzeigt. Was mein Skript aber eigentlich braucht ist der Seed, aus dem dann die Library TOTP den Code beim absenden des Requests berechnet. Den Seed aus Google Authenticator wieder rauszuholen ist nicht ganz einfach, aber möglich. Prinzipiell ist das aber auch gut, der Sinn bei 2FA ist ja, dass man zwei Geräte braucht um sich zu authentifizieren. Mein Skript kennt ja schon die Zugangsdaten, es sollte also eigentlich nicht auch noch sich den benötigten Code erzeugen können. Aber da ich keinen 2FA-Account habe, kann ich in dieser Richtung auch nichts weiter programmieren, einfach weil ich es nicht testen kann.

JoeGithub53 commented 8 months ago

Ich habe mal folgendes Script ausprobiert:

import base64
import pyotp

def encode_to_base32(number):
    # Wandele die Zahl in Bytes um (kleinste Einheit in Python)
    number_bytes = number.to_bytes((number.bit_length() + 7) // 8, byteorder='big')

    # Kodiere die Bytes in Base32
    base32_encoded = base64.b32encode(number_bytes).decode('utf-8')

    return base32_encoded

# Deine 6-stellige Zahl
numerischer_schluessel = 123456 # sechstellige Zahl vom Google Authenticator

# Kodiere die Zahl in Base32
base32_schluessel = encode_to_base32(numerischer_schluessel)

print('Numerischer Schlüssel (6-stellig):', numerischer_schluessel)
print('Base32-kodierter Schlüssel:', base32_schluessel)

# Geheimer Schlüssel (Base32 kodiert), den du von Google Authenticator erhalten hast
secret_key = base32_schluessel  # Beispielgeheimnis - 6 Zeichen Base32 kodiert

# Erstelle eine TOTP-Instanz mit dem geheimen Schlüssel
totp = pyotp.TOTP(secret_key)

# Generiere den One-Time Passcode (OTP) basierend auf der aktuellen Zeit
otp = totp.now()

print('Generierter One-Time Passcode (OTP):', otp)

so muesste man wahrscheinlich das Google Authenticator Passwort erst in ein Einmalpasswort wandeln. Der Empfangsserver (z.B. dein Backend-Server) kann den generierten OTP vergleichen: Der Empfangsserver sollte den erhaltenen OTP mit dem erwarteten OTP vergleichen, um die Authentizität des Benutzers zu überprüfen. Der erwartete OTP wird auch mit dem geheimen Schlüssel generiert. Die serverseitige Überprüfung sieht in etwa so aus:

# Funktion zur Überprüfung des OTP gegen das erwartete OTP
def verify_otp(secret_key, user_provided_otp):
    totp = pyotp.TOTP(secret_key)
    expected_otp = totp.now()

    return user_provided_otp == expected_otp

# Beispielaufruf der Funktion zur Überprüfung
user_provided_otp = '123456'  # Hier würde der Benutzer den OTP eingeben

# Überprüfe den OTP
if verify_otp(secret_key, user_provided_otp):
    print('OTP ist gültig. Authentifizierung erfolgreich.')
else:
    print('OTP ist ungültig. Authentifizierung fehlgeschlagen.')

In der Funktion verify_otp wird der vom Benutzer eingegebene OTP mit dem erwarteten OTP verglichen, das mit dem geheimen Schlüssel generiert wird. Wenn sie übereinstimmen, ist die Authentifizierung erfolgreich. Andernfalls schlägt die Authentifizierung fehl.

wf = Wikifolio("eMail", "Password, "wikifolioID", "OTP")
wf.buy_limit(amount=1, isin="US0378331005", limit_price=200) # buy 1 apple share, order is valid for 1 day

Bei mir kommt die Abfrage nach dem OTP erst beim Kauf und nicht bei der Anmeldung: Zwei-Faktor-Authentifizierung 6-stelliger 2FA-Code 123456 Der Code wird in deiner App angezeigt. Kein Zugriff auf deine App? Mehr erfahren Da muß ich den Code von Google Authenticator eingeben. Gibt es eine Möglichkeit, den Code auch bei wf.buy mitzugeben?

JoeGithub53 commented 8 months ago

Ja, Sie könnten den Benutzer dazu auffordern, den 2FA-Code aus der Google Authenticator-App (oder einer anderen Authentifizierungs-App) abzulesen und ihn in Ihr System einzugeben. Dies ist eine gängige Methode zur Implementierung von 2FA.

Hier ist, wie der Ablauf normalerweise abläuft:

Der Benutzer richtet 2FA in seinem Profil ein und verknüpft sein Konto mit der Authentifizierungs-App.

Wenn der Benutzer sich anmeldet oder eine Transaktion durchführt, wird er aufgefordert, den aktuellen 2FA-Code aus seiner Authentifizierungs-App abzulesen.

Der Benutzer gibt den abgelesenen Code in Ihr System ein, und Ihr System überprüft die Gültigkeit des Codes.

Wenn der Code korrekt ist, wird der Zugriff gewährt oder die Transaktion durchgeführt.

Dieser Ansatz erfordert keine Übertragung des gemeinsamen geheimen Schlüssels zwischen Ihrem Server und der App des Benutzers. Stattdessen überprüfen Sie nur, ob der von Ihnen empfangene Code mit dem übereinstimmt, den die Authentifizierungs-App des Benutzers generiert.

Beachten Sie jedoch, dass Sie sicherstellen müssen, dass Ihre Implementierung sicher ist und den Code ordnungsgemäß überprüft, um Missbrauch oder Angriffe zu verhindern. Es ist wichtig, sicherzustellen, dass der eingegebene Code nur einmal verwendet werden kann und dass er innerhalb eines bestimmten Zeitraums gültig ist, da 2FA-Codes normalerweise zeitbasiert sind.

Darüber hinaus sollten Sie sicherstellen, dass die gesamte Kommunikation und Datenübertragung zwischen Ihrem Server und dem Benutzer sicher und verschlüsselt ist, um die Sicherheit des 2FA-Prozesses zu gewährleisten.

Somit muesste es möglich sein, im wf.buy_limit den eingegebenen OTP mitzugeben. Der Server prüft ihn dann wie wenn er über den Browser geschickt worden wäre und aktzeptiert ihn oder lehnt in ab. Denken Sie daß es Sinn machen würde wf.buy_limit dahingehend anzupassen, dass es diesen OTP über das API übergibt ?

JoeGithub53 commented 8 months ago

Ein Denkfehler ist im obigen Artikel noch vorhanden. wf.buy prüft nicht den OTP sondern reicht ihn einfach an Wikifolio weiter. Wikifolio kennt den geheimen Schlüssel und kann somit die OTP Prüfung vornehmen.

henrydatei commented 8 months ago

Dein erstes Skript, was den secret_key aus dem generierten Token berechnen will ist völliger Murks, weil so funktioniert 2FA mit TOTP nicht.

Die ChatGPT-Erklärung danach beschreibt den Prozess schon besser. Und ja, man kann das Skript so umschreiben, dass man den 2FA Token erst eingibt, wenn die Transaktion autorisiert werden muss. Allerdings hat das das Problem, dass man das Skript nicht mehr vollautomatisch handeln lassen kann (ich habe dieses Repo genau aus diesem Grund entwickelt, von daher war das auch immer irgendwo der Anspruch, dass alles vollautomatisch geht). In diesem Fall muss das Skript den secret_key kennen, damit es automatisch die Tokens mit pyotp.TOTP().now() erzeugen kann wenn die benötigt werden. Aber klar, aus Sicherheitsgründen sollte der User seinen Token eingeben, der dann direkt zum Server gesendet wird. Da könnte man natürlich einen Check einbauen, der im Fall von fehlender 2FA Autorisierung nach dem Token fragt. Kannst du gerne implementieren und dann einen PR machen.

JoeGithub53 commented 8 months ago

Ich habe vom Service von Wikifolio erfahren, daß sie mit TraderFox ein gemeinsames API entwickelt haben. Dazu habe ich angefragt, ob ich eine Dokumentation dieses API bekommen kann. Wäre schön wenn wir sie bekommen würden. Im Ablauf zu TraderFox stand auch, man muß ein Wikifolio anlegen und dann beim Wikifolio Service einen Schlüssel beantragen. Ich glaube das wäre dann genau dieser Key den wir auch bräuchten, wenn wir über TOTP zugreifen wollen.

henrydatei commented 8 months ago

Das kann sein, dass das der nötige Key ist, aber wir brauchen erstmal die Dokumentation

JoeGithub53 commented 8 months ago

Hallo ich habe folgenden Test gemacht: 1.) Wikifolio.py die original Abfrage auskommentiert und eine abgeänderte eingefügt.

 # if self.twoFA_key != None:
                    #totp = TOTP(self.twoFA_key)
                    # auth = requests.post('https://www.wikifolio.com/api/totp/verify', data = totp.now(), cookies = self.cookie)
                    # cookies = auth.cookies
                # else: 
                    # cookies = self.cookie

                if self.twoFA_key != None:
                    auth = requests.post('https://www.wikifolio.com/api/totp/verify', data = self.twoFA_key, cookies = self.cookie)
                    cookies = auth.cookies
                else: 
                    cookies = self.cookie

2.) Ein Aufruferscript wie folgt erstellt:

from classes.wikifolio import Wikifolio
email = "myeMail"
password = "mypassword"
wikifolioID = "myWikifolioID"
twoFA_key = "None"

def get_six_digit_number():
    while True:
        user_input = input("Geben Sie eine sechsstellige Zahl ein: ")
        if user_input.isdigit() and len(user_input) == 6:
            return int(user_input)
        else:
            print("Ungültige Eingabe. Bitte geben Sie eine sechsstellige Zahl ein.")

# Aufruf der Funktion
twoFA_key = get_six_digit_number()
print("Die eingegebene sechsstellige Zahl ist:", twoFA_key)

wf = Wikifolio(email, password, wikifolioID, twoFA_key)

wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day

3.) Hier der Output: PS D:\python_script_depot> python 2fa_test.py Geben Sie eine sechsstellige Zahl ein: 395797 Die eingegebene sechsstellige Zahl ist: 395797 ... {"success":false,"needsTfaReAuth":true,"needsPasswordReAuth":false} Traceback (most recent call last): File "D:\python_script_depot\2fa_test.py", line 21, in wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\python_script_depot\classes\wikifolio.py", line 579, in buy_limit return OrderResponse(**raw_json) ^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: OrderResponse.init() got an unexpected keyword argument 'needsTfaReAuth'

Die Anmeldung scheint mit dem Token geklappt zu haben. Die Fehlermeldung sieht eher danach aus, als ob er für den Kauf den Token erneut braucht.

Ich probiere nun mal einen Falschen Token einzugeben, was wir dann für eine Fehlermeldung bekommen:

PS D:\python_script_depot> python 2fa_test.py Geben Sie eine sechsstellige Zahl ein: 123456 Die eingegebene sechsstellige Zahl ist: 123456 ... {"success":false,"needsTfaReAuth":true,"needsPasswordReAuth":false} Traceback (most recent call last): File "D:\python_script_depot\2fa_test.py", line 21, in wf.buy_limit(amount=1, isin="US0378331005", limit_price=170) # buy 1 apple share, order is valid for 1 day ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\python_script_depot\classes\wikifolio.py", line 579, in buy_limit return OrderResponse(**raw_json) ^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: OrderResponse.init() got an unexpected keyword argument 'needsTfaReAuth'

Da kommt die gleiche Fehlermeldung. Das heißt die 2FA Authorisierung bei der Anmeldung wird nicht durchgeführt. Sonst würde beim Kauf das nicht angefordert.

JoeGithub53 commented 8 months ago

Ich glaube nun kommen wir einen Schritt weiter. Mein User der bereits einen 2FA Key hat, gibt mir über meine Kontoeinstellungen die Möglichkeit einen weiteren einzurichten. Es wird ein Panel angezeigt wo folgendes steht:

2FA-App einrichten Du kannst 2FA über verschiedene Apps nutzen. Über diese App erhältst du künftig den zusätzlichen 2FA-Code. Wir empfehlen z.B. eine der folgenden Apps: Google Authenticator, Microsoft Authenticator, KeePass, Authy

Installiere eine dieser 2FA-Apps und scanne im Anschluss den QR-Code über die App. Alternativ kannst du auch diesen Text in die App eingeben. Danach wird dir in der App der erste 2FA-Code angezeigt.

Name Python-API Wie möchtest du die verwendete App nennen? (Wenn du mehr als eine App nutzen möchtest, kannst du sie so besser voneinander unterscheiden.) E-Mail Code 123456 Nach Klick auf "Code anfordern" erhältst du einen 6-stelligen Code an fit***@t-online.de. Gib diesen hier ein. Code anfordern App Code 123456 Gib hier den 6-stelligen Code ein, der in deiner App angezeigt wird.

Damit hätten wir den geheimen Schlüssel im Text neben dem QR Code. Dann kann man einen Code anfordern, der einem über eMail geschickt wird. Diesen muß man dann eingeben. Um den 6 stelligen Code der App einzugeben, muessten wir über die Python Methode mit dem geheimen Code eine 6 stelliges Einmalpasswort erstellen und das hier ebenfalls eingeben. Danach sollten über Wikifolio.py und dem beim Aufruf mitgegebenen geheimen Schlüssel ein Einmalpasswort erstellt werden können, das der Server aktzeptiert. Die nächste Aufgabe wäre dann, den API Aufruf um den OTP an den Server zu schicken zu implementieren. Nun die Frage an henrydatei: können Sie mir ein Beispielscript bereit stellen, das mit dem geheimen Schlüssel wie der Google Authenticator ein OTP ausgeben kann, so daß ich damit meine zweite 2FA registrieren kann,.

JoeGithub53 commented 8 months ago

Ich habe mal folgendes Script erstellt und getestet. Der generierte Schlüssel wird allerdings von Wikifolio nicht als gültig erkannt. Eine Idee, woran es liegen könnte ?

import hmac
import base64
import struct
import hashlib
import time

def generate_otp(secret_key, time_step=30):
    """
    Generiere ein One-Time Password (OTP) basierend auf dem geheimen Schlüssel und dem aktuellen Zeitstempel.

    Args:
        secret_key (str): Der geheime Schlüssel als Basis32-kodierter String.
        time_step (int): Die Dauer (in Sekunden) für die Gültigkeit eines OTP. Standardmäßig 30 Sekunden.

    Returns:
        str: Das generierte OTP.
    """
    # Konvertiere den Zeitstempel in die entsprechende Zeitschlitznummer
    time_counter = int(time.time()) // time_step

    # Kodiere den Zeitstempel als 8-Byte-Array im Netzwerkformat (Big Endian)
    time_counter_bytes = struct.pack('>Q', time_counter)

    # Dekodiere den geheimen Schlüssel aus Base32
    secret_key_bytes = base64.b32decode(secret_key)

    # Berechne den HMAC-SHA1 des Zeitstempels mit dem geheimen Schlüssel
    hmac_result = hmac.new(secret_key_bytes, time_counter_bytes, hashlib.sha1).digest()

    # Ermittle den Offset und die 4-Byte-Zahl aus dem HMAC-Ergebnis
    offset = hmac_result[-1] & 0x0F
    truncated_hash = hmac_result[offset:offset+4]

    # Setze das MSB auf 0 und interpretiere die 4-Byte-Zahl als Integer
    binary = struct.unpack(">I", truncated_hash)[0] & 0x7fffffff

    # Modulo 10^6, um eine 6-stellige Zahl zu erhalten
    otp = str(binary % 1000000).zfill(6)

    return otp

# Beispiel für die Verwendung
secret_key = 'Inhalt-geheimer-Schluessel'  # Beispiel für einen geheimen Schlüssel (Base32-kodiert)
otp = generate_otp(secret_key)
print('Generiertes OTP:', otp)

Ausgabe: PS D:\python_script_depot> python Generate_otp.py Generiertes OTP: 914993

JoeGithub53 commented 8 months ago

Wir haben es geschafft. Ich habe mir von der KI folgendes Google Authenticator Script erstellen lassen und siehe da, ich konnte unter Wikifolio einen weiteren 2FA Key erstellen.

Hier das erfolgreiche Google Authenticator Script unter Python:

import base64
import struct
import hashlib
import hmac
import time

def generate_google_authenticator_code(secret):
    key = base64.b32decode(secret)
    message = int(time.time()) // 30  # Zeit in 30-Sekunden-Intervallen
    message_bytes = struct.pack('>Q', message)  # Nachricht als 8-Byte-Array im Netzwerkformat (Big Endian)
    hash = hmac.new(key, message_bytes, hashlib.sha1).digest()
    offset = hash[-1] & 0x0F
    truncated_hash = hash[offset:offset+4]
    truncated_hash = struct.unpack('>I', truncated_hash)[0] & 0x7FFFFFFF
    code = truncated_hash % 1000000
    return str(code).zfill(6)

# Beispiel für die Verwendung mit einem geheimen Schlüssel
secret_key = 'mein Secret Key'  # Beispiel für einen geheimen Schlüssel (Base32-kodiert)
otp = generate_google_authenticator_code(secret_key)
print('Generiertes OTP (Google Authenticator-Stil):', otp)

PS D:\python_script_depot> python Generate_otp.py Generiertes OTP (Google Authenticator-Stil): 825320

Als ich den OTP bei der 2FA Anlage in Wikifolio eingegeben habe, wurde der Key akzeptiert.

Und hier nochmal zum Beweis die Übersicht meiner 2FA aktiven Apps. Zwei-Faktor-Authentifizierung (2FA) 2FA ist eine zusätzliche Sicherheitsebene für dein Benutzerkonto und das Traden auf wikifolio.com.

App hinzufügen Füge bitte mindestens eine App zur Generierung von 2FA-Codes hinzu, um 2FA für dein Benutzerkonto zu aktivieren.

Aktive Apps fits4u@wikifolio

Wikifolio-API

Als nächsten Test versuche ich über den Dialog mit dem Key aus Python eine Apple Aktie zu kaufen. Und Ihr werdet es nicht glauben, es kommt die Erfolgsmeldung: Geschafft! Deine Verifizierung ist für die nächsten 60 Minuten gültig. Viel Erfolg beim Traden!

Jetzt müssen wir nur noch die Wikifolio-API mit diesem Algorithmus ans laufen bringen.

henrydatei commented 8 months ago

Ich habe mal folgendes Script erstellt und getestet. Der generierte Schlüssel wird allerdings von Wikifolio nicht als gültig erkannt. Eine Idee, woran es liegen könnte ?

Keine Ahnung was du da gemacht hast, aber ChatGPT schreibt leider sehr häufig nicht direkt funktionalen Code, was aber nicht direkt erkannt wird, wenn man sich nicht gut mit der Materie auskennt. Für die Erstellung der Einmalcodes, wie sie dir Google Authenticator anzeigt, gibt es die Bibliothek TOTP:

from pyotp import TOTP

totp = TOTP(secret_key)
einmalcode = totp.now()
print(einmalcode)

Der secret_key steckt in dem QR-Code bzw. in dem Text, den du anstelle des QR-Codes eingeben kannst.

JoeGithub53 commented 8 months ago

danke für den bereitgestellten Code, ich probiere es gleich aus.

JoeGithub53 commented 8 months ago

Es kommt schon mal ein OTP. Ich muss nur eine Stunde warten, weil der vorhergehende OTP noch aktiv ist. Aber dann wissen, wir mehr!

JoeGithub53 commented 8 months ago

ja, funktioniert genauso, wie mein zuvor getestetes script. Ich habe auch beide scripte laufen lassen und beide erzeugen den gleichen Nummercode.

PS D:\python_script_depot> python Generate_otp.py Generiertes OTP (Google Authenticator-Stil): 143785 PS D:\python_script_depot> python generateTOTP.py 143785

Wenn man nun den Secret Key bei der Anmeldung mitgibt, klappt die TOTP Authorisierung leider noch nicht. Die Fehlermeldung sagt auch, 'needsTfaReAuth'. Es braucht vor der wf.buy_limit eine 2FA Authorisierung. Vielleicht klappt es ja auch noch, daß wir die API Dokumentation bekommen, damit sollte es dann auf alle Fälle lösbar sein. Zumindest wissen wir jetzt, der generierte Key aus TOTP funktioniert mit dem sicheren Schlüssel. @henrydatei: Falls diesbezüglich was geändert werden kann, ich stehe zum Test bereit.

henrydatei commented 8 months ago

Ok, das klingt schon mal gut. Wir müssen exakt rausfinden, wann und wie die 2FA stattfindet, wie sichergestellt wird, dass man sich nur einmal pro Stunde 2FAen muss und was passiert, wenn die Stunde rum ist und 2FA wieder nötig ist.

Ich glaube ich komme nicht drum herum, mir selber mal eine 2FA Account zu machen und das alles zu testen. Du kannst ja mal schauen, ob du an die API Dokumentation ran kommst

JoeGithub53 commented 8 months ago

ich habe mal die KI gefragt, wie wir auf den Server reagieren sollen, wenn er uns auffordert eine 2FA Authorisierung zu senden:
Wenn Sie beim Kauf einer Aktie über das Wikifolio-API die Aufforderung 'needsTfaReAuth' erhalten, bedeutet dies, dass der Server eine erneute Two-Factor Authentication (2FA) Autorisierung erfordert, um die Transaktion durchzuführen. Um diese Anforderung zu erfüllen, müssen Sie den 2FA-Code (One-Time Password, OTP) im API-Anforderungstoken einschließen.

Hier ist ein Beispiel, wie Sie dies mit dem requests-Modul in Python tun können:

import requests

# URL für den Kaufvorgang über das Wikifolio API
buy_url = 'https://api.wikifolio.com/v1/trades/buy'

# Ihre API-Zugangsdaten und andere erforderliche Informationen
api_key = 'Ihr_API_Schlüssel'
api_secret = 'Ihr_API_Geheimnis'
wikifolio_id = 'Ziel_Wikifolio_ID'
symbol = 'Aktiensymbol'
quantity = 10  # Anzahl der zu kaufenden Aktien
price_limit = 100.0  # Preislimit für den Kauf
tfa_code = 'Ihr_2FA_Code'  # Hier geben Sie Ihren aktuellen 2FA-Code ein

# Erstellen Sie die erforderlichen Header für die API-Anfrage
headers = {
    'Content-Type': 'application/json',
    'X-API-Key': api_key,
    'X-API-Signature': api_secret
}

# Erstellen Sie das JSON-Datenobjekt für den Kaufauftrag
buy_data = {
    'wikifolioId': wikifolio_id,
    'symbol': symbol,
    'quantity': quantity,
    'priceLimit': price_limit,
    'tfaCode': tfa_code  # Fügen Sie hier Ihren 2FA-Code ein
}

# Senden Sie die API-Anfrage mit den erforderlichen Daten und dem 2FA-Code
response = requests.post(buy_url, json=buy_data, headers=headers)

# Überprüfen Sie die Antwort des Servers
if response.status_code == 200:
    print('Aktie erfolgreich gekauft!')
else:
    print('Fehler beim Kauf der Aktie:', response.status_code)

Wenn Sie 2FA (Two-Factor Authentication) für das Wikifolio-API einrichten, erhalten Sie normalerweise zwei Schlüssel:

API-Schlüssel: Dies ist der Schlüssel, der Ihre Anwendung oder Ihren Benutzeraccount gegenüber dem Wikifolio-API identifiziert. Er wird normalerweise von Wikifolio bereitgestellt und dient dazu, den Absender der API-Anfragen zu authentifizieren und zu autorisieren. Dieser Schlüssel wird in Ihren API-Anfragen verwendet.

Geheimer Schlüssel für 2FA: Dies ist der geheime Schlüssel, den Sie bei der Einrichtung der 2FA-Authentifizierung in Ihrer Authentifizierungs-App (wie Google Authenticator) erhalten. Dieser geheime Schlüssel wird verwendet, um OTPs (One-Time Passwords) für die 2FA-Authentifizierung zu generieren. Dieser Schlüssel wird normalerweise nicht in API-Anfragen übertragen, sondern bleibt in Ihrer 2FA-Authentifizierungs-App.

Im zuvor gegebenen Beispiel zum Kauf von Aktien über das Wikifolio-API sollten Sie den API-Schlüssel verwenden, um Ihre Anwendung oder Ihr Benutzerkonto gegenüber dem Wikifolio-API zu authentifizieren. Dieser API-Schlüssel ist normalerweise öffentlich und wird in den Anfrage-Headern oder -Parametern mitgesendet.

Der geheime Schlüssel für 2FA, den Sie bei der Einrichtung der 2FA erhalten, sollte nicht im API-Code aufgeführt sein. Stattdessen verwenden Sie diesen geheimen Schlüssel in Ihrer Authentifizierungs-App, um OTPs zu generieren, wenn Sie zur 2FA-Authentifizierung aufgefordert werden.

In Ihrem API-Code sollten Sie den API-Schlüssel verwenden, um sich gegenüber dem Wikifolio-API zu authentifizieren, und den geheimen Schlüssel für 2FA in Ihrer 2FA-Authentifizierungs-App belassen.

So wie es aussieht, muß ich von Wikifolio auch noch einen API-Schluessel beantragen, damit 2FA klappt. Werde mal beim Service nachfragen. @henrydatei : bitte prüfen, ob wir den KI Vorschlag in die Kauf/Verkauf so implementieren können.

henrydatei commented 8 months ago

Völliger Bullshit. Wie ich schon sagte, ChatGPT halluziniert oft beim Programmieren; man sollte ChatGPT nicht unbedingt zum Programmieren nutzen, wenn man nicht exakt weiß, was man tut. Den API Endpoint https://api.wikifolio.com/v1/trades/buy gibt es nämlich gar nicht, den hat sich ChatGPT ausgedacht.

JoeGithub53 commented 8 months ago

Nun hat ChatGPT auch auf die Dokumentation verwiesen, auf die wir auch warten. << Ich glaube ich komme nicht drum herum, mir selber mal eine 2FA Account zu machen und das alles zu testen. >> Dafür muß eines der Wikifolios zumindest in der Beantragung zu investierbar sein. Bis es dann investierbar ist, vergehen in der Regel nochmal ein paar Monate. Ich denke bis dahin, werden wir über meinen Account testen muessen. Aber dauerhaft ist es auf alle Fälle sinvoll. Wenn Du mir den Namen mitteilst, kann ich mich schon mal vormerken lassen.

henrydatei commented 8 months ago

Ich habe dieses Wikifolio https://www.wikifolio.com/de/de/w/wf00tips01 bei welchem ich heute Nachmittag die Publizierung beantragt habe.

JoeGithub53 commented 8 months ago

Obiger Link kann nicht aufgelöst werden, sollte wahrscheinlich der sein: https://www.wikifolio.com/de/de/w/wf000ips01

JoeGithub53 commented 8 months ago

Ich habe eines gekauft, sehe aber noch keine Änderung in der Investitionssumme. Das wird wahrscheinlich zeitverzögert aktualisiert. Dann müsste ja bereits in den Kontoeinstellungen der Menüpunkt Zwei-Faktor-Authentifizierung angezeigt werden. Und es müßte möglich sein, daß Du Dir deinen eigenen 2FA Key erstellen kannst.

henrydatei commented 8 months ago

Interessant dass du schon "investieren" kannst. Vorhin kam die Ablehnung auf Publizierung von Wikifolio wegen dem Namen und Links in der Beschreibung. Muss das ändern und dann erneut beantragen

JoeGithub53 commented 8 months ago

Nun haben wir vom Wikifolio Service Team den Link zur API Dokumentation bekommen: https://trading-api.wikifolio.com/swagger/index.html Auf die schnelle habe ich dort noch keinen Punkt zu 2FA gefunden. Aber irgendwo in der Dokumentation, sollten wir es schon finden.

henrydatei commented 8 months ago

Perfekt ich schaue mir das heute Abend mal an

JoeGithub53 commented 8 months ago

die Dokumentation, auf die wir über den erhaltenen Link Zugriff erhalten haben, sieht nicht so aus als wäre es die gleiche, die wir über die Wikifolio-API verwenden. In der erhaltenen ist eine Anmeldung nur über Zertifikate und nicht über die alte Form (Userid und Passwort) möglich. Für mich sieht es so aus, als wäre es die mit TraderFox erstellte für deren Handel. Wenn man Traderfox nutzen will, muß man sich erst dort registrieren und der Service von Wikifolio richtet dann die für den Zugriff auf Wikifolio benötigten Zertifikate ein. Wir bräuchten allerdings nach wie vor die alte Form der Anmeldung über Userid,Password. Soll ich nochmal nachfragen, ob der Service eventuell auch noch die ältere Form der Wikifolio-API Dokumentation anbieten kann ?.

henrydatei commented 8 months ago

Ja wäre gut. Ich kam leider noch nicht dazu mir die API Dokumentation mal genauer anzusehen, ich bin aktuell sehr beschäftigt. Vielleicht habe ich am Wochenende mehr Zeit, da kann ich auch mal ein anderes Repo aufsetzen für diese API Dokumentation

JoeGithub53 commented 8 months ago

Vom Wikifolio Service Team, habe ich leider die Information bekommen, daß es nur das neue API geben wird. Und dieses darf nur ein ausgewählter Kreis nutzen. Da ich den Wunsch geäußert habe, voll automatisiert zu handeln, gehöre ich damit nicht zum ausgewählten Kreis. Anscheinend wurde das Verbot eines voll automatischen Handels, vertraglich so mit Lang & Schwarz vereinbart. Ich habe dann beschwichtigt, daß es mir nicht nur um den vollautomatischen Handel geht, sondern es auch gerne für Auswertungen nutzen würde. Sehen wir mal, ob ich noch in den ausgewählten Kreis kommen kann. Ansonsten, werde ich mich an den Wikifolio Gründer wenden und mit ihm klären, ob das in seinem Sinn ist, seinen Wikifolio Kunden so ein hervorragendes Werkzeug zu nehmen, bzw. sie auszuschließen.

JoeGithub53 commented 8 months ago

Vor ein paar Tagen habe ich ein Telefonat mit dem Wikifolio Service über das API Thema geführt. Die gute Nachricht, das alte inoffizielle Api wird weiterhin bestehen bleiben. Beim neuen API will man allderdings nicht, daß es über die Open Source für die breite Nutzung freigegeben wird. Aber das Thema hat zumindest für Diskussion gesorgt und man will intern klären, ob man nicht doch das inoffizielle API in das offizelle implementieren könnte. @Henrydatei: eventuell kommt noch eine Anfrage nach einer Videositzung um sich die inoffizielle API vorstellen zu lassen und vielleicht dann doch gemeinsam auf eine einheitliche Lösung hinzuarbeiten. Ich habe auch den Gründer Andreas Kern darüber informiert und ihm den Standpunkt eines Wikifolio Kunden beschrieben und wie wertvoll so eine perfekte Lösung von Github für die Pflege von Wikifolios ist. Über die hohe Qualität dieses API ist auch weitestgehend für Wikifolio gesichert, daß der Service Level für alle Nutzer gehalten werden kann. Es wäre auch schön, wenn es eine kostenlose Wikifolio Anwender Oberfläche, wie die von Trader Fox geben würde, wo man Einstiegs und Ausstiegsregeln definieren, oder aus bestehenden auswählen kann und sein Wikifolio generiert bekommt. Für so eine Anwendung wäre auch die Nutzung einer Wikifolio API unverzichtbar. Da wäre es für die Planung so einer Anwendung auch gut, wenn man bis dahin mit der Nutzung einer offiziellen API die alle Belange abdeckt rechnen könnte. Lassen wir uns überraschen, was draus wird.

henrydatei commented 8 months ago

Ich hatte vor ein paar Tagen auch das Gespräch und habe den API Nutzungsvertrag mit wikifolio unterschrieben. Die senden mir dann die Keys zu und dann baue ich mal einen Python Wrapper drum herum. Aber es wurde auch mehrfach erwähnt dass automatisches Handeln unerwünscht sei, aber das will ich sowieso nicht machen. Was geht, sei dass ein Mensch manuell einen Button drückt und dann wird vollautomatisch das Wikifolio umgeschichtet.

henrydatei commented 8 months ago

Das klingt doch gut, was du da erreicht hast. Mal schauen was draus wird

JoeGithub53 commented 8 months ago

Das ist ja wirklich eine erfreuliche Nachricht, wenn sie Dich kontaktiert und Dir einen Vertrag geschickt haben. Genau das wollten wir erreichen!. Wenn Du den Python Wrapper drum herum entwickelt hast, wird das Wikifolio Team bestimmt daran interessiert sein, wie Deine Lösung aussieht. Ich bin auch schon gespannt darauf! Vielleicht ergibt sich ein Video Meeting, an dem ich auch teilnehmen darf ?

JoeGithub53 commented 5 months ago

Hallo henrydatei, ich habe dank Deiner hervorragenden Schnittstellen einen Flask Server bei 1&1 eingerichtet. Aus den Wikifolio Seiten "wikifolios nach Investmentthemen" habe ich die dort aufgeführten Wikifolios in je ein Universum zusammengefasst. Immer ab 22:00h hole ich mir die Wikifolio Daten über Deine Schnittstelle. Beim Holen werden diese noch um die Backtest Daten Performance 1 Jahr, 1 Monat und 1 Woche angereichert. Dann kann man aus Speichertabellen am Tag superschnell darauf zugreifen. Mich würde interessieren, was Du davon hälst. Der Link lautet: https://e21fa77.online-server.cloud/ Was z.B. noch schön wäre, wenn ich die Ø-Perf.Jahr und Volatilität der ausgewählten Wikifolios maschinell abrufen könnte. Gibt es da bereits eventuell eine Funktion ? Ach ja und einen Kauf der ausgewählten Aktien kann man auch bereits per Knopfdruck machen. Wenn man welche aus der Auswahl nicht kaufen will, dann braucht man bloß den Limit-Preis auf Null setzen. Schön ist auch, daß man ihn aus dem Chart direkt setzen kann. Ich freue mich auf Deine Antwort.

henrydatei commented 5 months ago

Hab mir das gerade angeschaut, das sieht doch gut aus. Ist sicher noch in der Entwicklung, aber warum braucht man denn einen Account? Für das bloße Anzeigen der Wikifolios und Aktien sollte ja eigentlich kein Account nötig sein.

Mir ist noch aufgefallen, dass das Datum bei den Wikifolios nicht zu stimmen scheint, bzw. ist mir nicht klar, welches Datum da gemeint sein soll: image Ist das das Auflagedatum? Das aktuelle Datum oder der Stand der Daten wird es ja kaum sein. Dann gibt es bei dem angezeigtn Link immer noch ein ?wikifolio=wf000larry. Dieser Parameter ist doch gar nicht notwendig, um auf die Webseite zu kommen, wo Wikifolio die Aktie vorstellt. Und wenn wir schon bei Links sind, ich denke ein Link auf das Wikifolio in der oberen Tabelle wäre auch ganz nützlich.

Die durchschnittliche Performance und Volatilität pro Jahr solltest du für ein Wikifolio doch mit

from wikifolio import Wikifolio

wf = Wikifolio("email", "password", "wikifolioID")

print(wf.performance_annualized_ever, wf.volatility_annualized_ever)

bestimmen können. Aber wie ich sehe, gibst du die Zahlen doch in deiner Tabelle schon mit aus.

henrydatei commented 5 months ago

Und du scheinst auch die Option zum Kaufen der Aktien anzubieten. Das Problem was ich hier sehe, ist, dass der Nutzer dir vertrauen muss, wenn er seine Zugangsdaten zu Wikifolio eingibt. In diesem Szenario wäre es besser auf den Standard OAUTH zu setzen, den aber Wikifolio meines Wissens nach nicht anbietet. Mit OAUTH kann der Nutzer sich über eine weitere Platform, z.B. Google, Apple, etc. bei dir einloggen und du kannst dann bestimmte Aktionen im Namen des Users machen, ohne dass du das Passwort des Users kennst. Wenn Wikifolio OAUTH anbieten würde, könnte sich der Nutzer über Wikifolio bei dir anmelden und du kannst dann für den User handeln. Wäre sicherheitstechnisch deutlich besser, aber, wie gesagt, Wikifolio bietet es im Moment nicht an.

JoeGithub53 commented 5 months ago

Hallo henrydatei, danke für die konstruktiven Vorschläge. zur durchschnittlichen Performance und Volatilität: Beim Wikifolio meiner Frau werden folgende Werte angezeigt: 0.02473945817543 0.25964432106071 Wenn ich mir die Werte über Wikifolio ansehen: +2,5 % Ø-Perf. pro Jahr und 17,0 %Volatilität (1 J). wf.performance_annualized_ever gerundet sieht gut aus, aber bei wf.volatility_annualized_ever wüsste ich nicht, wie ich da auf die 17,0% komme ?. Zum Datum bei der Anzeige, daß habe ich einfach das Datum eingetragen, wo ich die Werte manuell ausgelesen habe. Diese varieren ja, und sind falsch, wenn man den Zeitpunkt nicht berücksichtigt. Aber wenn ich diese Werte maschniell mit Deiner Funktion auslesen kann, werde ich es ganz rausnehmen. Warum ich eine Authorisierung eingebaut habe: zum einen wollte ich das mal programmieren. Zum anderen habe ich mir gedacht, wenn später einmal Daten einem User zugeordnet werden muessten. Z.B. die Selektionskriterien könnte man damit einem Benutzer zuordnen. Wenn jemand eine Einstiegsregel hat, über die er immer die gleichen Selektionskriterien verwenden will, könnte ich ihm diese nach der Anmeldung vorgeben. Aber Du hast recht, die meisten wollen wahrscheinlich nur eine Auswahl machen und sonst nichts. Für die wäre eine Anmeldung nicht notwendig. Ich schaue mal, ob ich die Anmeldung für den Anfang rausnehmen kann und die erst dann anfordern, wenn es unbedingt notwendig ist. Das Problem mit der Wikifolio Anmeldung sehe ich auch, weil die Anmeldedaten als Variablen auslesbar wären. Vielleicht ist da tatsächlich die OAUTH Möglichkeit eine Lösung. Bei Wikifolio kann man sich nämlich über Google, Facebook etc anmelden. Dann wäre die Frage ob wir es über das API auch schaffen ?. Danke für die Hilfe!

henrydatei commented 5 months ago

Wenn du Volatilität für 1 Jahr haben willst, musst du folgende Eigenschaft nehmen

from wikifolio import Wikifolio

wf = Wikifolio("email", "password", "wikifolioID")

print(wf.volatility_one_year)

wf.volatility_annualized_ever berechnet die durchschnittliche jährliche Volatilität seit Beginn des Portfolios. volatility_one_year berechnet nur die Volatilität des letzten Jahres.

Zum Datum: Du hast die Werte aber nicht am 23.12.2001 ausgelesen, oder? Vielleicht ist auch deine Formatierung falsch und du meinst den 01.12.2023?

Dass man sich bei Wikifolio mit Google oder Facebook anmelden kann, hilft leider nichts, da in diesem Fall Google/Facebook der Anbieter von OAUTH ist. Wikifolio wird das nutzen um die E-Mail-Adresse eines Nutzers und den Benutzernamen zu übernehmen und in die eigene Datenbank zu schreiben. Ohne dass Wikifolio Anbieter von OAUTH ist wirst du auch immer in der API Benutzername und Passwort eines Users brauchen.

JoeGithub53 commented 5 months ago

Danke für die Lösung: mit den Werten wf.performance_annualized_ever und wf.volatility_one_year kommt genau das Ergebnis, das in den Wikifolios angezeigt wird. Das von mir angegebene Datum JJ.MM.TT kann damit auch entfallen. Wegen der Wikifolio Userid Passwort Authentifizierung muß ich mir noch was überlegen. Die Abschaffung der Registrierung für die Abfragen überlege ich mir auch noch.

JoeGithub53 commented 5 months ago

Hallo henrydatei, Dank Deiner guten Vorschläge habe ich heute den Link der Wikifolios aufgenommen. In dem Wikifolio Block wird nun nur noch das Universum und die wikifolioID vorgegeben, alles andere wird Dank Deiner API Schnittstelle voll automatisiert abends ab 22:00h geholt. Mir gefällt es jetzt sehr gut. Ich werde als nächstes versuchen den Limit Verkauf für das Wikifolio möglich zu machen. Damit könnte man als erstes sein Wikiofolio von schlecht gelaufenen Aktien bereinigen und dann über die Kauf Funktion mit neuen auffüllen. Dann schwebt mir ein Rotate System vor. Die Anzeige ist ja auf die Rangliste ausgerichtet. Da würde es sich anbieten, ein Depot nach Rangliste zu reorganisieren. Aktien im Depot die aus der Rangliste rausgefallen sind, werden durch Aktien aus der Rangliste ersetzt. Diese Funktion liese sich sogar völlig automatisiert um 8:00h bei Börsenstart durchführen. Die Regel nach der ausgewählt wird könnte vorgegeben werden, dann kann das Python Programm das Wikifolio in eine Tabelle laden und die Aktien nach den Regeln auswählen. Dann einen Abgleich machen und anhand des Ergebnisses voll automatisiert die Limit Verkaufs und Kauforders einstellen. Was mir auch bereits jetzt sehr gefällt, ist die Möglichkeit über den Limit Preis ein Reverse System anzuwenden. Man kann den Limit Preis bereits jetzt nach Chart einstellen, oder was ich mir auch noch überlegt habe, eine Auwahlmöglichkeit nach Prozent anzugeben. Über die könnte man über einen Prozentwert den Preis unter dem aktuellen steuern. Ebenso könnte man einen Prozentwert über Preis angeben, damit der Kauf sicher stattfindet. Wenn Du noch weitere tolle Ideen hast, gib mir jederzeit gerne Bescheid. Vielleicht kannst Du mir auch eine Vorgabe liefern, wie wir uns in der HomePage vorstellen können. Ohne Deine Unterstützung würde es diese Lösung nicht geben.

JoeGithub53 commented 5 months ago

Hallo henrydatei, kann es sein, daß in portfolio_details die Werte von ask= und bid= falsch zugeordnet wurden? Ask ist doch der Verkaufspreis, der niedriger ist als der Kaufpreis(bid)? Bei der Anzeige von portfolio_details ist es genau anders rum.

portfolio_details: [PortfolioDetail(name='Palo Alto Networks', isin='US6974351057', quantity=32.0, averagePurchasePrice=306.2, ask=299.8, bid=299.4, close=305.725, percentage=0.10329754989727688, link='/de/de/s/us6974351057?wikifolio=wf00wtop15', openLinkInSameTab=True, issuer=None, mid=299.6, isLeveraged=False, isTicking=True, partnerName='aktienguide', isLtsuActive=False)

henrydatei commented 5 months ago

Bid ist der höchste Preis den ein Käufer zahlen will und ask ist der kleinste Preis, den ein Verkäufer akzeptiert. Ask > bid

JoeGithub53 commented 5 months ago

Hallo henrydatei, wenn ich über Portfolio_details abfrage und unmittelbar mir die Werte Aktien.de ansehe, kommen die Portfolio Detail werte verdreht raus. Und es ist auch logisch, daß man bei einem Kauf mehr zahlen muß, als man bei einem Verkauf bekommt. Ich werde in mein Programm eine Abfrage einbauen, wenn der ask höher ist, als der bid dann tausche ich die Werte aus.

Werte aus Link: Palo Alto Networks | wikifolio.com https://www.wikifolio.com/de/de/s/us6974351057?wikifolio=wfimatrend

304,700 EUR Verkauf

305,700 EUR Kauf

portfolio_details: [PortfolioDetail(name='Palo Alto Networks', isin='US6974351057', quantity=32.0, averagePurchasePrice=306.2, ask=305.7, bid=304.7, close=303.7, percentage=0.10420550753501433, link='/de/de/s/us6974351057?wikifolio=wf00wtop15'

Gruß Josef

-----Original-Nachricht----- Betreff: Re: [henrydatei/wikifolio-api] wf.buy error (Issue #17) Datum: 2024-01-17T17:23:34+0100 Von: "henrydatei" @.> An: "henrydatei/wikifolio-api" @.>

Bid ist der höchste Preis den ein Käufer zahlen will und ask ist der kleinste Preis, den ein Verkäufer akzeptiert. Ask > bid — Reply to this email directly, view it on GitHub https://github.com/henrydatei/wikifolio-api/issues/17#issuecomment-1896155767 , or unsubscribe https://github.com/notifications/unsubscribe-auth/BC4ZQKO2GFJYDMV5TKN6RFTYO73IHAVCNFSM6AAAAAA5PC27JSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQOJWGE2TKNZWG4 . You are receiving this because you authored the thread.Message ID: @.> [ { @.": "http://schema.org", @.": "EmailMessage", "potentialAction": { @.": "ViewAction", "target": "https://github.com/henrydatei/wikifolio-api/issues/17#issuecomment-1896155767", "url": "https://github.com/henrydatei/wikifolio-api/issues/17#issuecomment-1896155767", "name": "View Issue" }, "description": "View this Issue on GitHub", "publisher": { @.***": "Organization", "name": "GitHub", "url": "https://github.com" } } ] 

henrydatei commented 5 months ago

Ask und bid sind immer aus der Sicht des Marktes. Dein Kaufpreis ist der Verkaufspreis eines Markteilnehmers. Es macht schon so Sinn, wie das die API zurückgibt, du musst es nur dem User richtig darstellen.

JoeGithub53 commented 4 months ago

@henrydatei, ich probiere zur Zeit unter Python Selenium mit dem Chrome Webdriver aus. Dabei versuche ich im Trading Masters Börsenspiel die Aktienseiten auszulesen. Bis zur Ranking Seite bin ich auch ganz gut hingekommen. Auf der Ranking Seite werden die Spieler aufgelistet und neben den Namen ist ein Lupen Symbol. Wenn man nun auf den Namen oder das Symbol cklickt wir man zum Depot des Spielers geleitet und kann sich dort sein Aktiendepot ansehen. Nun habe ich mir den HTML Code schon zig mal angesehen, schaffe aber nicht das richtige Schlüsselwort zu finden um über Selenium den Click an der richtigen Stelle auszuführen. Ich stelle mal den Bereich des HTML hier rein, Vielleicht kannst Du mir einen Tipp geben, wie der Click klappen könnte. Das Testbeispiel ist der User Winter:

1
<!----><!---->
Winter
<!---->
Aktiendepot
49.558,90 €
<!----><!---->
98,24 %
<!---->