Finanzen
Aufgabe
So funktioniert der Aktienhandel in der App
- Symbol: Jede Aktie hat ein Kürzel, etwa
NFLXfür Netflix. - Kurs:
lookup(symbol)aushelpers.pyfragt 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. - Startkapital: Wer sich registriert, bekommt 10.000 US-Dollar Bargeld.
- Kaufen: Anzahl × aktueller Kurs wird vom Bargeld abgezogen. Reicht das Bargeld nicht, findet der Kauf nicht statt.
- Verkaufen: Anzahl × aktueller Kurs wird dem Bargeld gutgeschrieben. Verkaufen kann man nur Aktien, die man besitzt.
- Gesamtwert: Bargeld plus, für jede Aktie, Anzahl × aktueller Kurs.
Beispiel (Kurse erfunden):
| Schritt | Kurs NFLX | NFLX im Besitz | Bargeld | Wert der Aktien | Gesamt |
|---|---|---|---|---|---|
| registrieren | – | 0 | $10,000.00 | $0.00 | $10,000.00 |
| 10 NFLX kaufen | $72.00 | 10 | $9,280.00 | $720.00 | $10,000.00 |
| Kurs steigt | $75.00 | 10 | $9,280.00 | $750.00 | $10,030.00 |
| 4 NFLX verkaufen | $75.00 | 6 | $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

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.zipaus, um in Ihrem Codespace die ZIP finance.zip herunterzuladen.
Nun können Sie
unzip finance.zipausfü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.zipausfü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 financeein 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:
lsSie sollten nun die folgenden Dateien und Ordner sehen:
app.py helpers.py static/ templates/ users.jsonFalls 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 runRegistrieren und Einloggen funktionieren bereits; alle übrigen Seiten zeigen vorerst nur eine Entschuldigung („TODO“).
| Datei | Inhalt |
|---|---|
app.py | die Routen. Fertig: login, logout, register. Noch apology("TODO"): index, quote, buy, sell, history. Dazu read_users und write_users für users.json. |
helpers.py | apology(message, code=400), login_required, lookup(symbol), usd(value) |
users.json | die 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_usersliestusers.jsonein und gibt den Inhalt als Dictionary zurück (oder ein leeres Dictionary, falls die Datei fehlt);write_usersschreibt ein solches Dictionary zurück in die Datei.loginvergleicht mitcheck_password_hashdas eingegebene Passwort mit dem gespeicherten Hash. Nach erfolgreichem Login merkt sich die Route die ID des Benutzers (den Schlüssel seines Eintrags inusers.json) alssession["user_id"]und leitet auf/weiter. So können andere Routen prüfen, wer eingeloggt ist.logoutleert die Sitzung.registerlegt einen neuen Eintrag inusers.jsonan, 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,sellundhistorysind mit@login_requireddekoriert: 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:
| Feld | Typ | Inhalt |
|---|---|---|
"username" | String | Benutzername |
"hash" | String | gehashtes Passwort |
"cash" | Float | verfü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.jsonum weitere Felder ergänzen oder neue JSON-Dateien anlegen;read_usersundwrite_userszeigen, wie man solche Dateien liest und schreibt. - Jeder gespeicherte Eintrag muss sich über die ID einem Benutzer zuordnen lassen.
Routen im Überblick
| Route | Methoden | Zweck |
|---|---|---|
/quote | GET, POST | Kurs einer Aktie anzeigen |
/buy | GET, POST | Aktien kaufen |
/ (index) | GET | Portfolio des Benutzers als Tabelle |
/sell | GET, POST | Aktien verkaufen |
/history | GET | alle 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 Textfeldsymbolper POST an/quotesendet. - POST: Entschuldigung, wenn das Symbol leer ist oder nicht existiert (
lookupgibtNonezurück). Sonst rendern Sie eine zweite Vorlage (z. B.quoted.html), in die Sie einen oder mehrere Werte auslookupeinbetten.
buy
- GET: Formular mit einem Textfeld
symbolund einem Textfeldshares(Anzahl der Aktien), das per POST an/buysendet. - POST, Entschuldigung
- wenn
symbolleer ist oder das Symbol nicht existiert (Rückgabewert vonlookup), - wenn
shareskeine positive Ganzzahl ist, - wenn sich der Benutzer die Aktien zum aktuellen Kurs nicht leisten kann; der Kauf findet dann nicht statt.
- wenn
- POST, sonst: Ziehen Sie den Preis vom Bargeld ab und schreiben Sie den neuen Bargeldbestand mit
write_userszurü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ü
symbolfür die Aktien des Benutzers und einem Textfeldshares, das per POST an/sellsendet. - POST, Entschuldigung
- wenn keine Aktie ausgewählt ist oder der Benutzer (irgendwie, nach dem Absenden) keine Anteile an dieser Aktie besitzt,
- wenn
shareskeine 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_usersundwrite_usersschreiben. - 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
buywerden Sie den aktuellen Kurs mitlookupund das Bargeld mitread_userslesen; fürindexrufen Sielookupfür jede Aktie auf.
Templates und Gestaltung
- Beträge in US-Dollar mit zwei Nachkommastellen geben Sie in Jinja mit dem Filter
usdaus:{{ value | usd }}statt{{ value }}. - Die Jinja-Dokumentation hilft beim Schreiben der Vorlagen.
- Zusätzliche statische Dateien legen Sie in
static/ab. - Die mitgelieferten Vorlagen verwenden Bootstrap; Sie müssen aber nicht damit arbeiten und können wie gewohnt eigenes HTML, CSS und JavaScript schreiben.
- Das Aussehen können Sie anpassen, z. B. mit:
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/financeHTML-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.pyAbgeben
Geben Sie im Ordner finance ab:
inf upload financeDanach 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