Zum Inhalt springen
Vorschau auf das nächste Semester, noch nicht veröffentlicht.
Zur aktuellen Seite

Finanzen

Aufgabe

So funktioniert der Aktienhandel in der App

  1. Symbol: Jede Aktie hat ein Kürzel, etwa NFLX für Netflix.
  2. Kurs: lookup(symbol) aus helpers.py fragt den aktuellen Preis bei einem Dienst von CS50 ab (https://finance.cs50.io/quote?symbol=NFLX, ohne API-Schlüssel). Die Kurse sind nicht in Echtzeit, ändern sich aber mit der Zeit.
  3. Startkapital: Wer sich registriert, bekommt 10.000 US-Dollar Bargeld.
  4. Kaufen: Anzahl × aktueller Kurs wird vom Bargeld abgezogen. Reicht das Bargeld nicht, findet der Kauf nicht statt.
  5. Verkaufen: Anzahl × aktueller Kurs wird dem Bargeld gutgeschrieben. Verkaufen kann man nur Aktien, die man besitzt.
  6. Gesamtwert: Bargeld plus, für jede Aktie, Anzahl × aktueller Kurs.

Beispiel (Kurse erfunden):

SchrittKurs NFLXNFLX im BesitzBargeldWert der AktienGesamt
registrieren–0$10,000.00$0.00$10,000.00
10 NFLX kaufen$72.0010$9,280.00$720.00$10,000.00
Kurs steigt$75.0010$9,280.00$750.00$10,030.00
4 NFLX verkaufen$75.006$9,580.00$450.00$10,030.00

Der Dienst antwortet im JSON-Format, einer Liste von Schlüssel-Wert-Paaren in geschweiften Klammern:

{"companyName":"Netflix Inc. Common Stock","latestPrice":71.35,"symbol":"NFLX"}

Für ein unbekanntes Symbol antwortet er mit einer Fehlermeldung ({"error":"Invalid symbol"}). lookup macht daraus ein dict mit den Schlüsseln name (str), price (float) und symbol (str, immer großgeschrieben, egal wie es übergeben wurde). Ist das Symbol unbekannt oder der Dienst nicht erreichbar, gibt lookup None zurück. Im selben JSON-Format speichert die App auch ihre eigenen Daten, etwa die Benutzer in users.json.

Was Aktien überhaupt sind, steht am Ende der Seite unter „Zum Weiterlesen“.

Demo

finance

So sieht die Lösung von CS50 aus: finance.cs50.net. Registrieren Sie sich gern und probieren Sie die App aus, verwenden Sie aber kein Passwort, das Sie auch anderswo nutzen. Sie dürfen sich den HTML- und CSS-Code dieser Lösung ansehen; Ihre App darf aber auch anders aussehen.

Aufgabenmaterial

Für diese Aufgabe werden Sie ein von uns zur Verfügung gestelltes Codegerüst vervollständigen.

Aufgabenmaterial herunterladen

Öffnen Sie VS CodeVisual Studio Code, der kostenlose Code-Editor von Microsoft, in dem Sie im Kurs programmieren. Glossar → entsprechend Ihrem Setup, klicken Sie auf Ihr Terminalfenster und führen Sie cd aus. Die EingabeaufforderungDas Zeichen am Anfang der Zeile im Terminal, etwa $ oder me/ $. Es zeigt, dass das Terminal auf Ihren nächsten Befehl wartet. Steht davor ein Ordnername wie me/, befinden Sie sich gerade in diesem Ordner. Glossar → Ihres Terminalfensters sollte ungefähr wie folgt aussehen:

$

Führen Sie den Befehl

wget https://dev.inf.zone/download/exercises/13/finance.zip

aus, um in Ihrem Codespace die ZIP finance.zip herunterzuladen.

Nun können Sie

unzip finance.zip

ausführen, um die ZIP in den Ordner finance zu entpacken.

Sie benötigen die ZIP-Datei nicht mehr, daher können Sie den Befehl

rm finance.zip

ausführen und bei der Aufforderung mit „y“ gefolgt von der Eingabetaste antworten, um die heruntergeladene ZIP-Datei zu entfernen.

Geben Sie nun den Befehl

cd finance

ein und drücken Sie anschließend die Eingabetaste, um in dieses Verzeichnis zu wechseln (d. h., es zu öffnen). Ihre Eingabeaufforderung sollte nun wie folgt aussehen:

finance/ $

Falls alles erfolgreich war, führen Sie den folgenden Befehl aus:

ls

Sie sollten nun die folgenden Dateien und Ordner sehen:

app.py  helpers.py  static/  templates/  users.json

Falls es zu Problemen kommt, wiederholen Sie dieselben Schritte und versuchen Sie herauszufinden, wo ein Fehler aufgetreten sein könnte!

Starten Sie im Ordner finance den eingebauten Webserver von Flask und öffnen Sie die URL, die er ausgibt:

flask run

Registrieren und Einloggen funktionieren bereits; alle übrigen Seiten zeigen vorerst nur eine Entschuldigung („TODO“).

DateiInhalt
app.pydie Routen. Fertig: login, logout, register. Noch apology("TODO"): index, quote, buy, sell, history. Dazu read_users und write_users für users.json.
helpers.pyapology(message, code=400), login_required, lookup(symbol), usd(value)
users.jsondie Benutzer, anfangs leer ({})
static/styles.css – dürfen Sie beliebig ändern
templates/layout.html (Navigationsleiste, Block main, Flash-Meldungen), login.html, register.html, apology.html
Überblick über das Codegerüst

app.py: Nach den Importen (darunter json und die Hilfsfunktionen aus helpers.py) wird Jinja mit dem Filter usd konfiguriert, der Werte als US-Dollar formatiert. Flask speichert Sitzungen im lokalen Dateisystem statt, wie sonst üblich, in (digital signierten) Cookies. after_request schaltet das Caching von Antworten ab, damit der Browser Änderungen immer sieht.

  • read_users liest users.json ein und gibt den Inhalt als Dictionary zurück (oder ein leeres Dictionary, falls die Datei fehlt); write_users schreibt ein solches Dictionary zurück in die Datei.
  • login vergleicht mit check_password_hash das eingegebene Passwort mit dem gespeicherten Hash. Nach erfolgreichem Login merkt sich die Route die ID des Benutzers (den Schlüssel seines Eintrags in users.json) als session["user_id"] und leitet auf / weiter. So können andere Routen prüfen, wer eingeloggt ist.
  • logout leert die Sitzung.
  • register legt einen neuen Eintrag in users.json an, mit 10.000 US-Dollar Startkapital. Der Benutzername muss eindeutig sein, das Passwort muss zweimal gleich eingegeben werden. Danach ist der Benutzer eingeloggt und landet auf der Startseite.
  • Die Routen index, quote, buy, sell und history sind mit @login_required dekoriert: Wer nicht eingeloggt ist, landet auf der Login-Seite.

helpers.py: apology rendert die Vorlage apology.html; die innere Funktion escape ersetzt dafür Sonderzeichen in der Meldung und ist nur innerhalb von apology sichtbar. login_required ist ein Beispiel für eine Funktion, die eine Funktion zurückgibt – keine Sorge, wenn sie kryptisch wirkt. lookup ist oben unter „So funktioniert …“ beschrieben. usd formatiert einen float als US-Dollar, etwa 1234.56 als $1,234.56.

templates/: login.html und register.html enthalten im Wesentlichen ein HTML-Formular, gestaltet mit Bootstrap. apology übergibt message als bottom und code als top an apology.html; schauen Sie sich an, wie die Vorlage die beiden Werte verwendet. layout.html enthält eine Navigationsleiste für Handy und Desktop und definiert den Block main, in den die anderen Vorlagen ihren Inhalt einfügen. Außerdem zeigt es Flasks Flash-Meldungen an, mit denen Sie eine Nachricht von einer Route zur nächsten weitergeben können.

Spezifikation

Daten speichern

users.json ist ein JSON-Objekt. Die Schlüssel sind die IDs der Benutzer (als Strings), die Werte Objekte mit diesen Feldern:

FeldTypInhalt
"username"StringBenutzername
"hash"Stringgehashtes Passwort
"cash"Floatverfügbares Bargeld in US-Dollar
{
    "1": {
        "username": "Alice",
        "hash": "hashed_password_1",
        "cash": 10000.0
    },
    "2": {
        "username": "Bob",
        "hash": "hashed_password_2",
        "cash": 10000.0
    }
}

read_users liest die Datei mit json.load() als Dictionary ein. JSON kennt als Schlüssel nur Strings: Nach dem Einlesen heißt der Schlüssel "1", nicht 1. session["user_id"] enthält genau diesen String, Sie können also direkt users[session["user_id"]] schreiben. Änderungen landen erst in der Datei, wenn Sie write_users aufrufen.

Das Codegerüst speichert bisher nur die Benutzer mit ihrem Bargeld. Für die folgenden Routen müssen Sie außerdem festhalten, welche Aktien ein Benutzer besitzt und welche Käufe und Verkäufe er getätigt hat:

  • Speichern Sie diese Daten in JSON-Dateien, mit möglichst wenig Redundanz.
  • Sie können die Einträge in users.json um weitere Felder ergänzen oder neue JSON-Dateien anlegen; read_users und write_users zeigen, wie man solche Dateien liest und schreibt.
  • Jeder gespeicherte Eintrag muss sich über die ID einem Benutzer zuordnen lassen.

Routen im Überblick

RouteMethodenZweck
/quoteGET, POSTKurs einer Aktie anzeigen
/buyGET, POSTAktien kaufen
/ (index)GETPortfolio des Benutzers als Tabelle
/sellGET, POSTAktien verkaufen
/historyGETalle Käufe und Verkäufe als Tabelle

Wo unten „Entschuldigung“ steht, rufen Sie apology mit einer passenden Meldung auf.

quote

  • GET: Rendern Sie eine Vorlage (z. B. quote.html) mit einem Formular, das ein Textfeld symbol per POST an /quote sendet.
  • POST: Entschuldigung, wenn das Symbol leer ist oder nicht existiert (lookup gibt None zurück). Sonst rendern Sie eine zweite Vorlage (z. B. quoted.html), in die Sie einen oder mehrere Werte aus lookup einbetten.

buy

  • GET: Formular mit einem Textfeld symbol und einem Textfeld shares (Anzahl der Aktien), das per POST an /buy sendet.
  • POST, Entschuldigung
    • wenn symbol leer ist oder das Symbol nicht existiert (Rückgabewert von lookup),
    • wenn shares keine positive Ganzzahl ist,
    • wenn sich der Benutzer die Aktien zum aktuellen Kurs nicht leisten kann; der Kauf findet dann nicht statt.
  • POST, sonst: Ziehen Sie den Preis vom Bargeld ab und schreiben Sie den neuen Bargeldbestand mit write_users zurück. Speichern Sie genug, um zu wissen, wer was zu welchem Preis und wann gekauft hat. Leiten Sie danach auf die Startseite um.

index

Eine HTML-Tabelle, die für den eingeloggten Benutzer zusammenfasst:

  • welche Aktien er besitzt und wie viele jeweils,
  • den aktuellen Kurs jeder Aktie,
  • den Gesamtwert jeder Position (Anzahl × Kurs),
  • sein aktuelles Bargeld und den Gesamtwert (Aktien + Bargeld).

sell

  • GET: Formular mit einem Dropdown-Menü symbol für die Aktien des Benutzers und einem Textfeld shares, das per POST an /sell sendet.
  • POST, Entschuldigung
    • wenn keine Aktie ausgewählt ist oder der Benutzer (irgendwie, nach dem Absenden) keine Anteile an dieser Aktie besitzt,
    • wenn shares keine positive Ganzzahl ist oder der Benutzer nicht so viele Anteile besitzt.
  • POST, sonst: Schreiben Sie den Erlös zum aktuellen Kurs dem Bargeld gut, verringern Sie den Bestand und speichern Sie den Verkauf. Leiten Sie danach auf die Startseite um.

history

Eine HTML-Tabelle mit allen Transaktionen des Benutzers, eine Zeile pro Transaktion:

  • gekauft oder verkauft,
  • Symbol,
  • Kauf- oder Verkaufspreis,
  • Anzahl der Aktien,
  • Datum und Uhrzeit.

Persönliche Anpassung

Implementieren Sie mindestens eine dieser Erweiterungen:

  • Benutzer können ihr Passwort ändern.
  • Benutzer können zusätzliches Bargeld auf ihr Konto laden.
  • Benutzer können Aktien direkt auf der Startseite (index) kaufen oder verkaufen, ohne das Symbol eintippen zu müssen.
  • eine andere Funktion mit vergleichbarem Umfang.

Hilfestellung

Klicken Sie auf die folgenden Tipps, um einige Ratschläge zu erhalten. Versuchen Sie aber zunächst, selbst so weit wie möglich zu kommen.

Daten in JSON-Dateien
  • Für eine neue JSON-Datei können Sie sich zwei Funktionen nach dem Vorbild von read_users und write_users schreiben.
  • Rufen Sie die Schreibfunktion nach jeder Änderung auch wirklich auf. Prüfen Sie nach einem Kauf oder Verkauf in der Datei selbst, ob der neue Eintrag angekommen ist.
  • Für buy werden Sie den aktuellen Kurs mit lookup und das Bargeld mit read_users lesen; für index rufen Sie lookup für jede Aktie auf.
Templates und Gestaltung

Testen

Testen Sie Ihre Web-App von Hand. Es ist völlig in Ordnung, andere zu bitten, Ihre Seite auszuprobieren (und Fehler zu provozieren).

Normalfälle

  • Registrieren Sie einen neuen Benutzer und prüfen Sie, ob die Portfolio-Seite mit den richtigen Angaben lädt.
  • Fragen Sie den Kurs zu einem gültigen Aktiensymbol ab.
  • Kaufen Sie dieselbe Aktie mehrmals und prüfen Sie, ob das Portfolio die richtigen Summen anzeigt.
  • Verkaufen Sie alle oder einige Anteile einer Aktie und prüfen Sie das Portfolio erneut.
  • Prüfen Sie, ob der Verlauf alle Transaktionen des eingeloggten Benutzers zeigt.

Sonderfälle – jeweils sollte eine Entschuldigung erscheinen:

  • Buchstaben, wo nur Zahlen erwartet werden,
  • 0 oder negative Zahlen, wo nur positive Zahlen erwartet werden,
  • Gleitkommazahlen, wo nur Ganzzahlen erwartet werden,
  • mehr Bargeld ausgeben, als der Benutzer hat,
  • mehr Anteile verkaufen, als der Benutzer besitzt,
  • ein ungültiges Aktiensymbol.

Korrektheit

Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:

check50 -l inf-zone/exercises/2026/finance

HTML-Validierung

Prüfen Sie Ihr HTML mit dem „I ♥ VALIDATOR“-Knopf im Footer jeder Seite. Er schickt das HTML der Seite an validator.w3.org.

Style

Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit style50Prüfprogramm von CS50, das zeigt, wo Ihr Code von den Formatierungsregeln abweicht. In Grün zeigt es, was Sie ergänzen sollten, in Rot, was wegfallen soll. Glossar → zu analysieren:

style50 app.py

Abgeben

Geben Sie im Ordner finance ab:

inf upload finance

Danach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie inf installieren und sich anmelden, steht unter Abgeben mit inf.

Zum Weiterlesen: Aktien und Bootstrap

Für die Lösung der Aufgabe nicht nötig.

Eine Aktie ist ein Anteil an einem Unternehmen. Wenn Sie sich nicht ganz sicher sind, was es bedeutet, Aktien zu kaufen und zu verkaufen, finden Sie hier ein Tutorial. C$50 Finance bildet das nach: Sie sehen echte Kurse und den Wert Ihres Portfolios, „kaufen“ und „verkaufen“ aber nur zum Schein.

Die Kurse kommen über eine API (Application Programming Interface): eine Schnittstelle, die ein Programm über URLs abfragt, statt eine Webseite für Menschen auszuliefern. Das Symbol steckt dabei in der URL; so weiß der Dienst, welche Daten er liefern soll.

Bootstrap ist ein beliebtes Open-Source-Framework für Websites, die sich an verschiedene Bildschirmgrößen anpassen. Es bringt fertige Gestaltungselemente mit – Layouts, Buttons, Formulare, Navigationselemente –, die auf HTML, CSS und JavaScript aufbauen und auf Handy wie Desktop gut funktionieren.

Diese Seite als Markdown: ansehen herunterladen