From 3cd5d90f1a5db13959e162866d7d797d1a177547 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Mon, 4 May 2026 02:31:42 +0200 Subject: [PATCH] Boersendchcker update --- .../boersenchecker/assets/boersenchecker.css | 113 +- .../boersenchecker/assets/boersenchecker.js | 8 +- modules/boersenchecker/design.json | 2 +- modules/boersenchecker/pages/chart_data.php | 94 +- modules/boersenchecker/partials/dashboard.php | 1002 ++++++++--------- modules/boersenchecker/partials/home.php | 48 +- .../boersenchecker/partials/instruments.php | 355 +++--- 7 files changed, 822 insertions(+), 800 deletions(-) diff --git a/modules/boersenchecker/assets/boersenchecker.css b/modules/boersenchecker/assets/boersenchecker.css index 6a1bfb2..da5ab8d 100644 --- a/modules/boersenchecker/assets/boersenchecker.css +++ b/modules/boersenchecker/assets/boersenchecker.css @@ -1,4 +1,4 @@ -.bc-app { +.bc-page { --bc-accent: var(--brand-accent); --bc-accent-strong: var(--brand-accent-2); --bc-ink: var(--text); @@ -12,73 +12,9 @@ color: var(--bc-text); } -.bc-grid-bg { - position: relative; - padding: 0 0 8px; -} - -.bc-grid-bg::before { - content: ""; - position: absolute; - inset: 0; - pointer-events: none; - background: - radial-gradient(circle at top right, color-mix(in srgb, var(--brand-accent-2) 10%, transparent), transparent 28%), - radial-gradient(circle at bottom left, color-mix(in srgb, var(--brand-accent-3) 10%, transparent), transparent 30%), - linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0)); -} - -.bc-shell { - position: relative; - display: grid; -} - -.bc-stack { - display: grid; -} - -.bc-hero { - display: grid; - gap: 18px; - padding: 28px; - border-radius: 28px; - background: - radial-gradient(circle at top right, rgba(94, 234, 212, 0.18), transparent 28%), - radial-gradient(circle at bottom left, rgba(121, 215, 255, 0.14), transparent 32%), - linear-gradient(145deg, rgba(15, 23, 42, 0.9), rgba(15, 23, 42, 0.78)); - border: 1px solid var(--bc-line); - box-shadow: 0 18px 40px rgba(2, 6, 23, 0.22); -} - -.bc-hero-top { +.bc-page { display: grid; gap: 16px; - grid-template-columns: minmax(0, 1.6fr) minmax(240px, 0.7fr); - align-items: start; -} - -.bc-hero-copy { - display: grid; - gap: 10px; -} - -.bc-eyebrow { - display: inline-flex; - align-items: center; - width: fit-content; - padding: 8px 12px; - border-radius: 999px; - background: color-mix(in srgb, var(--bc-accent) 18%, transparent); - border: 1px solid color-mix(in srgb, var(--bc-accent) 28%, transparent); - color: var(--bc-text); - font-size: 0.86rem; - font-weight: 700; -} - -.bc-title { - margin: 0; - font-size: clamp(2rem, 3vw, 2.8rem); - line-height: 1.05; } .bc-text { @@ -86,10 +22,23 @@ margin: 0; } -.bc-hero-controls { - display: grid; - gap: 12px; - justify-items: stretch; +.bc-section-head { + display: flex; + align-items: start; + justify-content: space-between; + gap: 16px; +} + +.bc-section-title { + margin: 0; + font-size: 1.45rem; + line-height: 1.15; +} + +.bc-section-head p, +.bc-section-copy { + color: var(--bc-muted); + margin: 8px 0 0; } .bc-form-card, @@ -128,10 +77,10 @@ .bc-button, .bc-tabs a, -.bc-app button, -.bc-app input, -.bc-app select, -.bc-app textarea { +.bc-page button, +.bc-page input, +.bc-page select, +.bc-page textarea { font: inherit; } @@ -352,14 +301,14 @@ background: rgba(255,255,255,0.04); } -.bc-app .setup-field { +.bc-page .setup-field { display: grid; gap: 6px; } -.bc-app input, -.bc-app select, -.bc-app textarea { +.bc-page input, +.bc-page select, +.bc-page textarea { width: 100%; border: 1px solid var(--bc-line); border-radius: 14px; @@ -368,16 +317,16 @@ color: var(--bc-text); } -.bc-app input::placeholder, -.bc-app textarea::placeholder { +.bc-page input::placeholder, +.bc-page textarea::placeholder { color: color-mix(in srgb, var(--bc-muted) 70%, transparent); } -.bc-app a { +.bc-page a { color: inherit; } -.bc-app .muted { +.bc-page .muted { color: var(--bc-muted); } diff --git a/modules/boersenchecker/assets/boersenchecker.js b/modules/boersenchecker/assets/boersenchecker.js index 5a098aa..b53063f 100644 --- a/modules/boersenchecker/assets/boersenchecker.js +++ b/modules/boersenchecker/assets/boersenchecker.js @@ -115,7 +115,13 @@ } currentPayload = payload; renderChart(pointsForRange(payload, activeRange)); - if (statusNode) statusNode.textContent = `Quelle: Alpha Vantage | Symbol ${payload.symbol || ''}`; + if (statusNode) { + const sourceLabel = payload.source_label || payload.source || 'Lokale Kurshistorie'; + const instrumentRef = payload.symbol || payload.isin || ''; + statusNode.textContent = instrumentRef + ? `Quelle: ${sourceLabel} | ${instrumentRef}` + : `Quelle: ${sourceLabel}`; + } } catch (error) { currentPayload = null; chartShell.innerHTML = `
${error.message}
`; diff --git a/modules/boersenchecker/design.json b/modules/boersenchecker/design.json index a761f63..82f6eaf 100644 --- a/modules/boersenchecker/design.json +++ b/modules/boersenchecker/design.json @@ -3,7 +3,7 @@ "title": "Boersenchecker", "description": "Depotverwaltung fuer Aktien, Kaufdaten, Kursverlauf und Waehrungsumrechnung.", "actions": [ - { "label": "Zur Startseite", "href": "/", "variant": "ghost" }, + { "label": "Nexus Übersicht", "href": "/", "variant": "ghost" }, { "label": "Setup", "href": "/modules/setup/boersenchecker", "variant": "secondary" } ], "tabs": [ diff --git a/modules/boersenchecker/pages/chart_data.php b/modules/boersenchecker/pages/chart_data.php index 4f22f4c..e7a6c71 100644 --- a/modules/boersenchecker/pages/chart_data.php +++ b/modules/boersenchecker/pages/chart_data.php @@ -17,9 +17,10 @@ $pdo = module_fn('boersenchecker', 'pdo'); module_fn('boersenchecker', 'ensure_schema'); $instrumentTable = module_fn('boersenchecker', 'table', 'instruments'); $positionTable = module_fn('boersenchecker', 'table', 'positions'); +$quoteTable = module_fn('boersenchecker', 'table', 'quotes'); $stmt = $pdo->prepare( - 'SELECT i.id, i.name, i.symbol, i.isin + 'SELECT i.id, i.name, i.symbol, i.isin, i.quote_currency FROM ' . $instrumentTable . ' i INNER JOIN ' . $positionTable . ' p ON p.instrument_id = i.id WHERE i.id = :id AND p.owner_sub = :owner_sub @@ -37,12 +38,93 @@ if (!is_array($instrument)) { exit; } -$symbol = strtoupper(trim((string) ($instrument['symbol'] ?? ''))); -if ($symbol === '') { - echo json_encode(['ok' => false, 'message' => 'Fuer diese Aktie ist kein Symbol hinterlegt.'], JSON_UNESCAPED_UNICODE); +$quoteStmt = $pdo->prepare( + 'SELECT id, price, currency, quoted_at, source, created_at + FROM ' . $quoteTable . ' + WHERE instrument_id = :instrument_id + ORDER BY quoted_at ASC, created_at ASC, id ASC' +); +$quoteStmt->execute([ + 'instrument_id' => $instrumentId, +]); +$quotes = $quoteStmt->fetchAll(PDO::FETCH_ASSOC) ?: []; + +if ($quotes === []) { + echo json_encode([ + 'ok' => false, + 'message' => 'Keine lokalen Kursdaten fuer diese Aktie vorhanden.', + ], JSON_UNESCAPED_UNICODE); exit; } -$result = module_fn('boersenchecker', 'alpha_vantage_fetch_chart_series', $symbol); -echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); +$dailyMap = []; +foreach ($quotes as $quote) { + $localDate = trim((string) module_fn( + 'boersenchecker', + 'format_datetime_for_display', + (string) ($quote['quoted_at'] ?? ''), + (string) ($quote['source'] ?? ''), + 'Y-m-d' + )); + $localDateTime = trim((string) module_fn( + 'boersenchecker', + 'format_datetime_for_display', + (string) ($quote['quoted_at'] ?? ''), + (string) ($quote['source'] ?? ''), + 'Y-m-d H:i:s' + )); + if ($localDate === '' || !is_numeric($quote['price'] ?? null)) { + continue; + } + + $point = [ + 'date' => $localDate, + 'close' => (float) $quote['price'], + 'currency' => strtoupper(trim((string) ($quote['currency'] ?? ''))), + 'quoted_at' => $localDateTime, + 'source' => (string) ($quote['source'] ?? ''), + ]; + + if (!isset($dailyMap[$localDate]) || strcmp($localDateTime, (string) ($dailyMap[$localDate]['quoted_at'] ?? '')) >= 0) { + $dailyMap[$localDate] = $point; + } +} + +$daily = array_values($dailyMap); +usort($daily, static fn (array $left, array $right): int => strcmp((string) $left['date'], (string) $right['date'])); + +if ($daily === []) { + echo json_encode([ + 'ok' => false, + 'message' => 'Keine gueltigen lokalen Schlusskurse fuer diese Aktie vorhanden.', + ], JSON_UNESCAPED_UNICODE); + exit; +} + +$aggregate = static function (array $points, string $format): array { + $result = []; + $timezone = new DateTimeZone('Europe/Berlin'); + foreach ($points as $point) { + $date = DateTimeImmutable::createFromFormat('Y-m-d', (string) ($point['date'] ?? ''), $timezone); + if (!$date instanceof DateTimeImmutable) { + continue; + } + $bucket = $date->format($format); + $result[$bucket] = $point; + } + return array_values($result); +}; + +echo json_encode([ + 'ok' => true, + 'symbol' => strtoupper(trim((string) ($instrument['symbol'] ?? ''))), + 'isin' => strtoupper(trim((string) ($instrument['isin'] ?? ''))), + 'instrument_name' => (string) ($instrument['name'] ?? ''), + 'currency' => strtoupper(trim((string) ($instrument['quote_currency'] ?? ''))), + 'daily' => $daily, + 'weekly' => $aggregate($daily, 'o-W'), + 'monthly' => $aggregate($daily, 'Y-m'), + 'source' => 'database:quotes', + 'source_label' => 'Lokale Kurshistorie', +], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; diff --git a/modules/boersenchecker/partials/dashboard.php b/modules/boersenchecker/partials/dashboard.php index f6d4706..e8f69e8 100644 --- a/modules/boersenchecker/partials/dashboard.php +++ b/modules/boersenchecker/partials/dashboard.php @@ -2,517 +2,511 @@ 'Depotverwaltung', ]) ?> -
-
-
+
+ +
+ +
+ - -
- -
- - - -
-
-
-

Benutzer-Scope

-

Depots anderer Benutzer sind nur fuer `appadmin` sichtbar und bearbeitbar.

-
-
-
- - -
-
- - -
-
-
-
-

-

Stammdaten und Berichtswahrung fuer ein Depot pflegen.

-
-
-
- - - - - - -
- - - Abbrechen - -
-
-
- -
-
-
-

API / FX

-

Kurs- und Waehrungsdaten zentral aktualisieren.

-
-
-

- Die Umrechnung liest gespeicherte FX-Daten zentral aus dem Modul fx-rates. Eine Aktualisierung wird nur manuell - angestossen und respektiert die dortige Max-Age- und Reuse-Logik. -

-

- Aktienkurse werden ueber Alpha Vantage anhand des hinterlegten Symbols abgerufen. Die ISIN bleibt als Stammdatum erhalten. -

-
-
- - - -
-
- - - -
-
-
- Alpha Vantage Mindestabstand: Min. -
-
- API-Key und Timeout fuer Aktienkurse werden ueber dieses Modul-Setup gepflegt. -
-
- FX-Provider, API-Key und Waehrungskatalog werden im Modul fx-rates gepflegt. -
-
- Standard-Berichtswahrung: · Max. Alter: h -
-
+ +
+
+
+

Benutzer-Scope

+

Depots anderer Benutzer sind nur fuer `appadmin` sichtbar und bearbeitbar.

+
- -
-
-
-

-

Aktienpositionen fuer ein Depot mit Kaufdaten und Kurswaehrung verwalten.

-
-
- -
Bitte zuerst ein Depot anlegen.
- -
- - - - - -
- - - - - - -
-
- - - - - -
- -
- - - Abbrechen - -
-
- -
- -
-
-
-

Wertpapiersuche

-

Alpha-Vantage-Suchergebnisse pruefen und Daten direkt ins Positionsformular uebernehmen.

-
-
-
-
- - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
SymbolNameTypRegionWaehrungMatchAktion
- - In Formular uebernehmen - -
- -
-
Noch keine Symbolsuche ausgefuehrt.
-
- -
- -
-
-
-

Manuellen Kurs erfassen

-

Kurse mit Uhrzeit und Quelle direkt in die Historie schreiben.

-
-
- -
Sobald Positionen vorhanden sind, koennen hier Kurse mit Uhrzeit gespeichert werden.
- -
- - -
- - - - - -
-
- -
-
- -
- -
-
-
-

Depots

-

Uebersicht aller Depots mit Kennzahlen und Schnellaktionen.

-
-
- -
Noch keine Depots vorhanden.
- -
- - 0, 'invested' => 0.0, 'current' => 0.0, 'gain' => null, 'has_invested' => false, 'has_current' => false]; - ?> -
-
-
- -
· Position(en)
-
-
- Bearbeiten -
- - - - -
-
-
- -
- -
-
-
Investiert
- -
-
-
Aktuell
- -
-
-
Gewinn / Verlust
- -
-
-
+
+ + +
+
+ -
-
-
-

Positionen

-

Alle Positionen mit Kaufdaten, letztem Kurs und aktuellen Werten.

-
+
+
+
+
+

+

Stammdaten und Berichtswahrung fuer ein Depot pflegen.

- -
-
Noch keine Positionen vorhanden.
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DepotAktieISIN / WKNStueckKaufLetzter KursWertDeltaAktion
- - -
- -
- -
-
- -
-
- - -
- - kein Kurs - -
- - - - n/a - - - - - - n/a - - -
- Bearbeiten - Kurs erfassen -
- - - - -
-
- - - - -
-
-
- -
+
+
+ + + + + + +
+ + + Abbrechen + +
+
+
-
-
-
-

Kursverlauf

-

Historische Kurse pro Aktie mit Zeitstempel und Quelle.

-
+
+
+
+

API / FX

+

Kurs- und Waehrungsdaten zentral aktualisieren.

- -
Noch keine Kursdaten vorhanden.
- -
- $instrument): ?> - -
-
-
- -
- · -
-
- Neuen Kurs erfassen -
- -
Noch keine historischen Kurse vorhanden.
- -
- - - - - - - - - - - - - - - - - - - -
ZeitpunktKursQuelleAktion
-
- - - - -
-
-
- -
- -
- -
-
+
+

+ Die Umrechnung liest gespeicherte FX-Daten zentral aus dem Modul fx-rates. Eine Aktualisierung wird nur manuell + angestossen und respektiert die dortige Max-Age- und Reuse-Logik. +

+

+ Aktienkurse werden ueber Alpha Vantage anhand des hinterlegten Symbols abgerufen. Die ISIN bleibt als Stammdatum erhalten. +

+
+
+ + + +
+
+ + + +
+
+
+ Alpha Vantage Mindestabstand: Min. +
+
+ API-Key und Timeout fuer Aktienkurse werden ueber dieses Modul-Setup gepflegt. +
+
+ FX-Provider, API-Key und Waehrungskatalog werden im Modul fx-rates gepflegt. +
+
+ Standard-Berichtswahrung: · Max. Alter: h +
+
+ +
+
+
+

+

Aktienpositionen fuer ein Depot mit Kaufdaten und Kurswaehrung verwalten.

+
+
+ +
Bitte zuerst ein Depot anlegen.
+ +
+ + + + + +
+ + + + + + +
+
+ + + + + +
+ +
+ + + Abbrechen + +
+
+ +
+ +
+
+
+

Wertpapiersuche

+

Alpha-Vantage-Suchergebnisse pruefen und Daten direkt ins Positionsformular uebernehmen.

+
+
+
+
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
SymbolNameTypRegionWaehrungMatchAktion
+ + In Formular uebernehmen + +
+ +
+
Noch keine Symbolsuche ausgefuehrt.
+
+ +
+ +
+
+
+

Manuellen Kurs erfassen

+

Kurse mit Uhrzeit und Quelle direkt in die Historie schreiben.

+
+
+ +
Sobald Positionen vorhanden sind, koennen hier Kurse mit Uhrzeit gespeichert werden.
+ +
+ + +
+ + + + + +
+
+ +
+
+ +
+ +
+
+
+

Depots

+

Uebersicht aller Depots mit Kennzahlen und Schnellaktionen.

+
+
+ +
Noch keine Depots vorhanden.
+ +
+ + 0, 'invested' => 0.0, 'current' => 0.0, 'gain' => null, 'has_invested' => false, 'has_current' => false]; + ?> +
+
+
+ +
· Position(en)
+
+
+ Bearbeiten +
+ + + + +
+
+
+ +
+ +
+
+
Investiert
+ +
+
+
Aktuell
+ +
+
+
Gewinn / Verlust
+ +
+
+
+ +
+ +
+ +
+
+
+

Positionen

+

Alle Positionen mit Kaufdaten, letztem Kurs und aktuellen Werten.

+
+
+ +
+
Noch keine Positionen vorhanden.
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DepotAktieISIN / WKNStueckKaufLetzter KursWertDeltaAktion
+ + +
+ +
+ +
+
+ +
+
+ + +
+ + kein Kurs + +
+ + + + n/a + + + + + + n/a + + +
+ Bearbeiten + Kurs erfassen +
+ + + + +
+
+ + + + +
+
+
+ +
+ +
+
+
+

Kursverlauf

+

Historische Kurse pro Aktie mit Zeitstempel und Quelle.

+
+
+ +
Noch keine Kursdaten vorhanden.
+ +
+ $instrument): ?> + +
+
+
+ +
+ · +
+
+ Neuen Kurs erfassen +
+ +
Noch keine historischen Kurse vorhanden.
+ +
+ + + + + + + + + + + + + + + + + + + +
ZeitpunktKursQuelleAktion
+
+ + + + +
+
+
+ +
+ +
+ +
diff --git a/modules/boersenchecker/partials/home.php b/modules/boersenchecker/partials/home.php index 1b966d4..4fbbfb7 100644 --- a/modules/boersenchecker/partials/home.php +++ b/modules/boersenchecker/partials/home.php @@ -1,9 +1,7 @@ 'Depot-Ueberblick', ]) ?> -
-
-
+
-
+
-
+
-
-
+
+
-

Marktueberblick

+

Marktueberblick

Depotauswahl, Aktienfokus und aktueller Kursabruf in einem Bereich.

@@ -76,31 +74,31 @@
-
-
+
+
Positionen
Aktien im aktuell gewaehlten Depot
-
+
Investiert
In Berichtswahrung bewertet
-
+
Aktueller Wert
Basierend auf dem letzten gespeicherten Kurs
-
+
Performance
-
-
+
+
Bester Wert
@@ -110,7 +108,7 @@
-
+
Schwaechster Wert
@@ -121,7 +119,7 @@
-
+
@@ -129,10 +127,10 @@
-
-
+
+
-

Kursverlauf

+

Kursverlauf

Schlusskurse ueber mehrere Zeitfenster fuer das aktuell gewaehlte Instrument.

@@ -161,14 +159,14 @@
-
-
+
+
-

Aktien im Depot

+

Aktien im Depot

Stueckzahl, Kaufdaten, letzter Kurs und Performance auf einen Blick.

-
+
Keine Aktien im ausgewaehlten Depot.
@@ -207,7 +205,5 @@
-
-
diff --git a/modules/boersenchecker/partials/instruments.php b/modules/boersenchecker/partials/instruments.php index 935a8c3..84f7e3d 100644 --- a/modules/boersenchecker/partials/instruments.php +++ b/modules/boersenchecker/partials/instruments.php @@ -1,192 +1,187 @@ 'Aktienverwaltung', ]) ?> -
-
-
+
+ +
+ +
+ - -
- -
- - -
-
-
-
-

Aktie waehlen

-

Systemweit vorhandene Aktie aus allen Depots auswaehlen.

-
-
-
- -
-
- -
-
-
-

Wertpapiersuche

-

Alpha-Vantage-Suchergebnisse finden und direkt fuer die Aktie uebernehmen.

-
-
-
-
- - - - -
-
- - - - - - - - - - - - - - - - - - - - - - -
SymbolNameRegionWaehrungAktion
- - Uebernehmen - -
- -
-
Noch keine Symbolsuche ausgefuehrt.
-
- -
+
+
+
+
+

Aktie waehlen

+

Systemweit vorhandene Aktie aus allen Depots auswaehlen.

+
+
+ +
+
-
-
-
-

Aktie bearbeiten

-

Stammdaten, Markt und Kurswaehrung zentral fuer die Aktie pflegen.

-
+
+
+
+

Wertpapiersuche

+

Alpha-Vantage-Suchergebnisse finden und direkt fuer die Aktie uebernehmen.

- -
Keine Aktie vorhanden.
- -
- - -
- - - - - - -
-
- -
-
-
- - - -
- -
- -
-
-
-

Manuellen Kurs eingeben

-

Einzelne Kurse mit Zeitstempel und Quelle fuer die ausgewaehlte Aktie speichern.

-
-
- -
Keine Aktie vorhanden.
- -
- - -
- - - - -
-
- -
-
- -
- -
-
-
-

Kursverlauf

-

Gespeicherte Kursdaten der ausgewaehlten Aktie mit Quelle und Loeschoption.

-
-
- -
-
Keine Kursdaten vorhanden.
-
- - - + +
+
+ + + + + +
+ +
+ + + + + + + + + + + - - - - + + + + + - - - - - - - - - - - -
SymbolNameRegionWaehrungAktion
ZeitpunktKursQuelleAktion + + Uebernehmen + +
-
- - - - -
-
- -
-
+ + + + +
+
Noch keine Symbolsuche ausgefuehrt.
+
+ +
+ +
+
+
+

Aktie bearbeiten

+

Stammdaten, Markt und Kurswaehrung zentral fuer die Aktie pflegen.

+
+
+ +
Keine Aktie vorhanden.
+ +
+ + +
+ + + + + + +
+
+ +
+
+
+ + + +
+ +
+ +
+
+
+

Manuellen Kurs eingeben

+

Einzelne Kurse mit Zeitstempel und Quelle fuer die ausgewaehlte Aktie speichern.

+
+
+ +
Keine Aktie vorhanden.
+ +
+ + +
+ + + + +
+
+ +
+
+ +
+ +
+
+
+

Kursverlauf

+

Gespeicherte Kursdaten der ausgewaehlten Aktie mit Quelle und Loeschoption.

+
+
+ +
+
Keine Kursdaten vorhanden.
+
+ + + + + + + + + + + + + + + + + + + + +
ZeitpunktKursQuelleAktion
+
+ + + + +
+
+ +