@@ -237,7 +237,7 @@
= e((string) ($result['currency'] ?? '')) ?> |
= e((string) ($result['match_score'] ?? '')) ?> |
-
+
In Formular uebernehmen
|
@@ -428,7 +428,7 @@
Bearbeiten
Kurs erfassen
diff --git a/modules/boersenchecker/src/Support/DashboardPage.php b/modules/boersenchecker/src/Support/DashboardPage.php
index b257ecb..3c51643 100644
--- a/modules/boersenchecker/src/Support/DashboardPage.php
+++ b/modules/boersenchecker/src/Support/DashboardPage.php
@@ -15,7 +15,7 @@ final class DashboardPage
private array $moduleSettings;
private string $defaultReportCurrency;
private float $fxMaxAgeHours;
- private int $alphaMinIntervalMinutes;
+ private int $marketDataMinIntervalMinutes;
private int $editPortfolioId;
private int $editPositionId;
private string $portfolioTable;
@@ -49,9 +49,9 @@ final class DashboardPage
$this->fxMaxAgeHours = 6.0;
}
- $this->alphaMinIntervalMinutes = (int) ($this->moduleSettings['alpha_vantage_min_interval_minutes'] ?? 60);
- if ($this->alphaMinIntervalMinutes <= 0) {
- $this->alphaMinIntervalMinutes = 60;
+ $this->marketDataMinIntervalMinutes = (int) ($this->moduleSettings['bavest_min_interval_minutes'] ?? 60);
+ if ($this->marketDataMinIntervalMinutes <= 0) {
+ $this->marketDataMinIntervalMinutes = 60;
}
$this->editPortfolioId = (int) ($_GET['edit_portfolio'] ?? 0);
@@ -100,7 +100,7 @@ final class DashboardPage
'availableOwners' => array_values($this->availableOwners),
'defaultReportCurrency' => $this->defaultReportCurrency,
'fxMaxAgeHours' => $this->fxMaxAgeHours,
- 'alphaMinIntervalMinutes' => $this->alphaMinIntervalMinutes,
+ 'marketDataMinIntervalMinutes' => $this->marketDataMinIntervalMinutes,
'symbolSearchKeywords' => $this->symbolSearchKeywords,
'symbolSearchResults' => $this->symbolSearchResults,
'editPortfolio' => $state['editPortfolio'],
@@ -127,8 +127,8 @@ final class DashboardPage
'save_position' => $this->savePosition(),
'delete_position' => $this->deletePosition(),
'save_quote' => $this->saveQuote(),
- 'refresh_alpha_vantage_position' => $this->refreshAlphaVantagePosition(),
- 'refresh_alpha_vantage_all' => $this->refreshAlphaVantageAll(),
+ 'refresh_market_data_position' => $this->refreshMarketDataPosition(),
+ 'refresh_market_data_all' => $this->refreshMarketDataAll(),
'search_symbol' => $this->searchSymbol(),
'delete_quote' => $this->deleteQuote(),
'refresh_fx' => $this->refreshFx(),
@@ -172,6 +172,7 @@ final class DashboardPage
$candidateName = trim((string) ($_GET['instrument_name_candidate'] ?? ''));
$candidateSymbol = trim((string) ($_GET['symbol_candidate'] ?? ''));
+ $candidateIsin = trim((string) ($_GET['isin_candidate'] ?? ''));
$candidateMarket = trim((string) ($_GET['market_candidate'] ?? ''));
$candidateCurrency = $this->normalizeCurrency((string) ($_GET['quote_currency_candidate'] ?? $this->defaultReportCurrency));
@@ -179,6 +180,7 @@ final class DashboardPage
$editPosition = [
'instrument_name' => $candidateName,
'symbol' => $candidateSymbol,
+ 'isin' => $candidateIsin,
'market' => $candidateMarket,
'quote_currency' => $candidateCurrency,
'purchase_currency' => $this->defaultReportCurrency,
@@ -191,6 +193,9 @@ final class DashboardPage
if ($candidateSymbol !== '') {
$editPosition['symbol'] = $candidateSymbol;
}
+ if ($candidateIsin !== '') {
+ $editPosition['isin'] = $candidateIsin;
+ }
if ($candidateMarket !== '') {
$editPosition['market'] = $candidateMarket;
}
@@ -378,7 +383,7 @@ final class DashboardPage
return 'Kurs gespeichert.';
}
- private function refreshAlphaVantagePosition(): string
+ private function refreshMarketDataPosition(): string
{
$positionId = (int) ($_POST['position_id'] ?? 0);
$stmt = $this->pdo->prepare(
@@ -386,6 +391,7 @@ final class DashboardPage
p.instrument_id,
i.name AS instrument_name,
i.symbol,
+ i.isin,
i.quote_currency
FROM ' . $this->positionTable . ' p
INNER JOIN ' . $this->instrumentTable . ' i ON i.id = p.instrument_id
@@ -402,19 +408,19 @@ final class DashboardPage
}
$instrumentId = (int) $row['instrument_id'];
- $symbol = strtoupper(trim((string) ($row['symbol'] ?? '')));
+ $isin = strtoupper(trim((string) ($row['isin'] ?? '')));
$quoteCurrency = $this->normalizeCurrency((string) ($row['quote_currency'] ?? $this->defaultReportCurrency));
- if ($symbol === '') {
- throw new RuntimeException('Fuer diese Aktie ist noch kein API-Symbol / Ticker hinterlegt.');
+ if ($isin === '') {
+ throw new RuntimeException('Fuer diese Aktie ist noch keine ISIN hinterlegt.');
}
$latestApiQuote = $this->latestApiQuoteForInstrument($instrumentId);
$latestTimestamp = is_array($latestApiQuote) ? strtotime((string) ($latestApiQuote['quoted_at'] ?? '')) : false;
- if ($latestTimestamp !== false && (time() - $latestTimestamp) < ($this->alphaMinIntervalMinutes * 60)) {
- return 'Vorhandener Alpha-Vantage-Kurs fuer ' . (string) $row['instrument_name'] . ' wiederverwendet.';
+ if ($latestTimestamp !== false && (time() - $latestTimestamp) < ($this->marketDataMinIntervalMinutes * 60)) {
+ return 'Vorhandener Bavest-Kurs fuer ' . (string) $row['instrument_name'] . ' wiederverwendet.';
}
- $apiResult = \module_fn('boersenchecker', 'alpha_vantage_fetch_quote', $symbol);
+ $apiResult = \module_fn('boersenchecker', 'bavest_fetch_quote_by_isin', $isin);
if (empty($apiResult['ok'])) {
throw new RuntimeException((string) ($apiResult['message'] ?? 'API-Abruf fehlgeschlagen.'));
}
@@ -426,16 +432,17 @@ final class DashboardPage
(string) $apiResult['fetched_at'],
(string) $apiResult['source']
);
- return 'API-Kurs fuer ' . (string) $row['instrument_name'] . ' gespeichert.';
+ return 'Bavest-Kurs fuer ' . (string) $row['instrument_name'] . ' gespeichert.';
}
- private function refreshAlphaVantageAll(): string
+ private function refreshMarketDataAll(): string
{
$stmt = $this->pdo->prepare(
'SELECT DISTINCT
i.id,
i.name,
i.symbol,
+ i.isin,
i.quote_currency
FROM ' . $this->positionTable . ' p
INNER JOIN ' . $this->instrumentTable . ' i ON i.id = p.instrument_id
@@ -454,58 +461,68 @@ final class DashboardPage
$failed = 0;
$errors = [];
+ $bulkRows = [];
foreach ($rows as $row) {
$instrumentId = (int) ($row['id'] ?? 0);
- $symbol = strtoupper(trim((string) ($row['symbol'] ?? '')));
- $quoteCurrency = $this->normalizeCurrency((string) ($row['quote_currency'] ?? $this->defaultReportCurrency));
-
- if ($instrumentId <= 0 || $symbol === '') {
+ $isin = strtoupper(trim((string) ($row['isin'] ?? '')));
+ if ($instrumentId <= 0 || $isin === '') {
$skipped++;
continue;
}
$latestApiQuote = $this->latestApiQuoteForInstrument($instrumentId);
$latestTimestamp = is_array($latestApiQuote) ? strtotime((string) ($latestApiQuote['quoted_at'] ?? '')) : false;
- if ($latestTimestamp !== false && (time() - $latestTimestamp) < ($this->alphaMinIntervalMinutes * 60)) {
+ if ($latestTimestamp !== false && (time() - $latestTimestamp) < ($this->marketDataMinIntervalMinutes * 60)) {
$reused++;
continue;
}
- $apiResult = \module_fn('boersenchecker', 'alpha_vantage_fetch_quote', $symbol);
- if (empty($apiResult['ok'])) {
- $failed++;
- $errors[] = (string) ($row['name'] ?? $symbol) . ': ' . (string) ($apiResult['message'] ?? 'API-Abruf fehlgeschlagen.');
- if (stripos((string) ($apiResult['message'] ?? ''), 'limit') !== false) {
- break;
- }
- continue;
+ $bulkRows[] = $row;
+ }
+
+ if ($bulkRows !== []) {
+ $bulkResult = \module_fn('boersenchecker', 'bavest_fetch_bulk_quotes', $bulkRows);
+ if (empty($bulkResult['ok'])) {
+ throw new RuntimeException((string) ($bulkResult['message'] ?? 'Bavest Bulk-Abruf fehlgeschlagen.'));
}
- $this->storeQuote(
- $instrumentId,
- (float) $apiResult['price'],
- $quoteCurrency,
- (string) $apiResult['fetched_at'],
- (string) $apiResult['source']
- );
- $fetched++;
+ $bulkQuotes = is_array($bulkResult['quotes'] ?? null) ? $bulkResult['quotes'] : [];
+ foreach ($bulkRows as $row) {
+ $instrumentId = (int) ($row['id'] ?? 0);
+ $quoteCurrency = $this->normalizeCurrency((string) ($row['quote_currency'] ?? $this->defaultReportCurrency));
+ $apiResult = $bulkQuotes[$instrumentId] ?? null;
+ if (!is_array($apiResult) || !is_numeric($apiResult['price'] ?? null)) {
+ $failed++;
+ $errors[] = (string) ($row['name'] ?? $instrumentId) . ': kein Preis in der Bavest-Antwort.';
+ continue;
+ }
+
+ $this->storeQuote(
+ $instrumentId,
+ (float) $apiResult['price'],
+ $quoteCurrency,
+ (string) $apiResult['fetched_at'],
+ (string) $apiResult['source']
+ );
+ $fetched++;
+ }
}
if ($errors !== []) {
throw new RuntimeException(
- 'Alpha Vantage: ' . $fetched . ' neu, ' . $reused . ' wiederverwendet, ' . $skipped . ' ohne Symbol, ' . $failed . ' Fehler. '
+ 'Bavest: ' . $fetched . ' neu, ' . $reused . ' wiederverwendet, ' . $skipped . ' ohne ISIN, ' . $failed . ' Fehler. '
. implode(' | ', array_slice($errors, 0, 3))
);
}
- return 'Alpha Vantage: ' . $fetched . ' neu, ' . $reused . ' wiederverwendet, ' . $skipped . ' ohne Symbol, ' . $failed . ' Fehler.';
+ return 'Bavest: ' . $fetched . ' neu, ' . $reused . ' wiederverwendet, ' . $skipped . ' ohne ISIN, ' . $failed . ' Fehler.';
}
private function searchSymbol(): string
{
$keywords = trim((string) ($_POST['search_keywords'] ?? ''));
$this->symbolSearchKeywords = $keywords;
- $result = \module_fn('boersenchecker', 'alpha_vantage_search_symbols', $keywords);
+ $result = \module_fn('boersenchecker', 'bavest_search_symbols', $keywords);
$this->symbolSearchResults = is_array($result['results'] ?? null) ? $result['results'] : [];
if (empty($result['ok'])) {
@@ -708,7 +725,7 @@ final class DashboardPage
);
$stmt->execute([
'instrument_id' => $instrumentId,
- 'source' => 'alpha_vantage:%',
+ 'source' => 'bavest:%',
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return is_array($row) ? $row : null;
diff --git a/modules/boersenchecker/src/Support/HomePage.php b/modules/boersenchecker/src/Support/HomePage.php
index b614043..3eec9cd 100644
--- a/modules/boersenchecker/src/Support/HomePage.php
+++ b/modules/boersenchecker/src/Support/HomePage.php
@@ -15,7 +15,7 @@ final class HomePage
private string $positionTable;
private string $quoteTable;
private string $defaultReportCurrency;
- private int $alphaMinIntervalMinutes;
+ private int $marketDataMinIntervalMinutes;
public function __construct()
{
@@ -26,9 +26,9 @@ final class HomePage
$settings = \modules()->settings('boersenchecker');
$this->defaultReportCurrency = strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR';
- $this->alphaMinIntervalMinutes = (int) ($settings['alpha_vantage_min_interval_minutes'] ?? 60);
- if ($this->alphaMinIntervalMinutes <= 0) {
- $this->alphaMinIntervalMinutes = 60;
+ $this->marketDataMinIntervalMinutes = (int) ($settings['bavest_min_interval_minutes'] ?? 60);
+ if ($this->marketDataMinIntervalMinutes <= 0) {
+ $this->marketDataMinIntervalMinutes = 60;
}
$table = static fn (string $name): string => \module_fn('boersenchecker', 'table', $name);
@@ -118,7 +118,7 @@ final class HomePage
}
$stmt = $this->pdo->prepare(
- 'SELECT DISTINCT i.id, i.name, i.symbol, i.quote_currency
+ 'SELECT DISTINCT i.id, i.name, i.symbol, i.isin, i.quote_currency
FROM ' . $this->positionTable . ' p
INNER JOIN ' . $this->instrumentTable . ' i ON i.id = p.instrument_id
WHERE p.owner_sub = :owner_sub AND p.portfolio_id = :portfolio_id'
@@ -134,37 +134,47 @@ final class HomePage
$updated = 0;
$reused = 0;
+ $bulkCandidates = [];
foreach ($rows as $row) {
$instrumentId = (int) ($row['id'] ?? 0);
- $symbol = strtoupper(trim((string) ($row['symbol'] ?? '')));
- if ($instrumentId <= 0 || $symbol === '') {
+ $isin = strtoupper(trim((string) ($row['isin'] ?? '')));
+ if ($instrumentId <= 0 || $isin === '') {
continue;
}
$latest = $this->latestApiQuoteForInstrument($instrumentId);
$latestTimestamp = is_array($latest) ? strtotime((string) ($latest['quoted_at'] ?? '')) : false;
- if ($latestTimestamp !== false && (time() - $latestTimestamp) < ($this->alphaMinIntervalMinutes * 60)) {
+ if ($latestTimestamp !== false && (time() - $latestTimestamp) < ($this->marketDataMinIntervalMinutes * 60)) {
$reused++;
continue;
}
- $apiResult = \module_fn('boersenchecker', 'alpha_vantage_fetch_quote', $symbol);
- if (empty($apiResult['ok'])) {
- continue;
- }
+ $bulkCandidates[] = $row;
+ }
+ if ($bulkCandidates !== []) {
+ $bulkResult = \module_fn('boersenchecker', 'bavest_fetch_bulk_quotes', $bulkCandidates);
+ $quotes = is_array($bulkResult['quotes'] ?? null) ? $bulkResult['quotes'] : [];
$stmtInsert = $this->pdo->prepare(
'INSERT INTO ' . $this->quoteTable . ' (instrument_id, price, currency, quoted_at, source)
VALUES (:instrument_id, :price, :currency, :quoted_at, :source)'
);
- $stmtInsert->execute([
- 'instrument_id' => $instrumentId,
- 'price' => (float) $apiResult['price'],
- 'currency' => strtoupper(trim((string) ($row['quote_currency'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency,
- 'quoted_at' => (string) $apiResult['fetched_at'],
- 'source' => (string) $apiResult['source'],
- ]);
- $updated++;
+
+ foreach ($bulkCandidates as $row) {
+ $instrumentId = (int) ($row['id'] ?? 0);
+ $quote = $quotes[$instrumentId] ?? null;
+ if (!is_array($quote) || !is_numeric($quote['price'] ?? null)) {
+ continue;
+ }
+ $stmtInsert->execute([
+ 'instrument_id' => $instrumentId,
+ 'price' => (float) $quote['price'],
+ 'currency' => strtoupper(trim((string) ($quote['currency'] ?? $row['quote_currency'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency,
+ 'quoted_at' => (string) ($quote['fetched_at'] ?? date('Y-m-d H:i:s')),
+ 'source' => (string) ($quote['source'] ?? 'bavest:quote'),
+ ]);
+ $updated++;
+ }
}
return 'Aktuelle Kurse: ' . $updated . ' aktualisiert, ' . $reused . ' wiederverwendet.';
@@ -241,7 +251,7 @@ final class HomePage
);
$stmt->execute([
'instrument_id' => $instrumentId,
- 'source' => 'alpha_vantage:%',
+ 'source' => 'bavest:%',
]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return is_array($row) ? $row : null;
diff --git a/modules/boersenchecker/src/Support/InstrumentPage.php b/modules/boersenchecker/src/Support/InstrumentPage.php
index 579e7ea..efb2794 100644
--- a/modules/boersenchecker/src/Support/InstrumentPage.php
+++ b/modules/boersenchecker/src/Support/InstrumentPage.php
@@ -72,6 +72,7 @@ final class InstrumentPage
$candidateName = trim((string) ($_GET['instrument_name_candidate'] ?? ''));
$candidateSymbol = trim((string) ($_GET['symbol_candidate'] ?? ''));
+ $candidateIsin = trim((string) ($_GET['isin_candidate'] ?? ''));
$candidateMarket = trim((string) ($_GET['market_candidate'] ?? ''));
$candidateCurrency = strtoupper(trim((string) ($_GET['quote_currency_candidate'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency;
if ($selectedInstrument === null && ($candidateName !== '' || $candidateSymbol !== '' || $candidateMarket !== '')) {
@@ -79,9 +80,9 @@ final class InstrumentPage
'id' => 0,
'name' => $candidateName,
'symbol' => $candidateSymbol,
+ 'isin' => $candidateIsin,
'market' => $candidateMarket,
'quote_currency' => $candidateCurrency,
- 'isin' => '',
'wkn' => '',
];
}
@@ -106,7 +107,7 @@ final class InstrumentPage
'save_instrument' => $this->saveInstrument(),
'save_quote' => $this->saveQuote(),
'delete_quote' => $this->deleteQuote(),
- 'refresh_alpha_vantage_instrument' => $this->refreshInstrumentQuote(),
+ 'refresh_market_data_instrument' => $this->refreshInstrumentQuote(),
'search_symbol' => $this->searchSymbol(),
default => '',
};
@@ -211,12 +212,12 @@ final class InstrumentPage
{
$instrumentId = (int) ($_POST['instrument_id'] ?? 0);
$instrument = $this->assertInstrumentAccessible($instrumentId);
- $symbol = trim((string) ($instrument['symbol'] ?? ''));
- if ($symbol === '') {
- throw new RuntimeException('Fuer diese Aktie ist kein Symbol hinterlegt.');
+ $isin = strtoupper(trim((string) ($instrument['isin'] ?? '')));
+ if ($isin === '') {
+ throw new RuntimeException('Fuer diese Aktie ist keine ISIN hinterlegt.');
}
- $apiResult = \module_fn('boersenchecker', 'alpha_vantage_fetch_quote', $symbol);
+ $apiResult = \module_fn('boersenchecker', 'bavest_fetch_quote_by_isin', $isin);
if (empty($apiResult['ok'])) {
throw new RuntimeException((string) ($apiResult['message'] ?? 'API-Abruf fehlgeschlagen.'));
}
@@ -233,13 +234,13 @@ final class InstrumentPage
'source' => (string) $apiResult['source'],
]);
- return 'API-Kurs gespeichert.';
+ return 'Bavest-Kurs gespeichert.';
}
private function searchSymbol(): string
{
$this->searchKeywords = trim((string) ($_POST['search_keywords'] ?? ''));
- $result = \module_fn('boersenchecker', 'alpha_vantage_search_symbols', $this->searchKeywords);
+ $result = \module_fn('boersenchecker', 'bavest_search_symbols', $this->searchKeywords);
$this->searchResults = is_array($result['results'] ?? null) ? $result['results'] : [];
if (empty($result['ok'])) {
throw new RuntimeException((string) ($result['message'] ?? 'Symbolsuche fehlgeschlagen.'));