diff --git a/name.abuchen.portfolio/META-INF/services/name.abuchen.portfolio.online.QuoteFeed b/name.abuchen.portfolio/META-INF/services/name.abuchen.portfolio.online.QuoteFeed index 24b035f4a0..fc330331ac 100644 --- a/name.abuchen.portfolio/META-INF/services/name.abuchen.portfolio.online.QuoteFeed +++ b/name.abuchen.portfolio/META-INF/services/name.abuchen.portfolio.online.QuoteFeed @@ -1,5 +1,6 @@ name.abuchen.portfolio.online.impl.ManualQuoteFeed name.abuchen.portfolio.online.impl.AlphavantageQuoteFeed +name.abuchen.portfolio.online.impl.AMFIIndiaQuoteFeed name.abuchen.portfolio.online.impl.BitfinexQuoteFeed name.abuchen.portfolio.online.impl.BinanceQuoteFeed name.abuchen.portfolio.online.impl.CoinGeckoQuoteFeed @@ -12,8 +13,8 @@ name.abuchen.portfolio.online.impl.LeewayQuoteFeed name.abuchen.portfolio.online.impl.TwelveDataQuoteFeed name.abuchen.portfolio.online.impl.PortfolioReportQuoteFeed name.abuchen.portfolio.online.impl.QuandlQuoteFeed -name.abuchen.portfolio.online.impl.HTMLTableQuoteFeed name.abuchen.portfolio.online.impl.CSQuoteFeed name.abuchen.portfolio.online.impl.YahooFinanceQuoteFeed name.abuchen.portfolio.online.impl.YahooFinanceAdjustedCloseQuoteFeed +name.abuchen.portfolio.online.impl.HTMLTableQuoteFeed name.abuchen.portfolio.online.impl.GenericJSONQuoteFeed \ No newline at end of file diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/Messages.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/Messages.java index 20109728cf..13fac38945 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/Messages.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/Messages.java @@ -267,6 +267,7 @@ public class Messages extends NLS public static String MsgErrorInvestmentPlanMissingSecurityPricesToGenerateTransaction; public static String MsgErrorLeewayAPIKeyMissing; public static String MsgErrorMissingAPIKey; + public static String MsgErrorMissingIdentifierForSecurity; public static String MsgErrorMissingKeyValueInJSON; public static String MsgErrorMissingOnlineId; public static String MsgErrorMissingPathToDateOrClose; diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages.properties index 30619d7904..0895b362f3 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages.properties @@ -524,6 +524,8 @@ MsgErrorLeewayAPIKeyMissing = PWP Leeway UG API key is missing. Configure in pre MsgErrorMissingAPIKey = API key is missing. Configure API key in preferences. +MsgErrorMissingIdentifierForSecurity = Missing identifier {0} for instrument {1} + MsgErrorMissingKeyValueInJSON = Missing value for key ''{0}'' MsgErrorMissingOnlineId = Missing Portfolio Report OnlineId for security {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_cs.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_cs.properties index 66ef9df746..f1f75427d7 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_cs.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_cs.properties @@ -524,6 +524,8 @@ MsgErrorLeewayAPIKeyMissing = PWP Leeway UG API key is missing. Nastavte v p\u01 MsgErrorMissingAPIKey = Chyb\u00ED kl\u00ED\u010D API. Nastavte kl\u00ED\u010D API v p\u0159edvolb\u00E1ch. +MsgErrorMissingIdentifierForSecurity = Chyb\u011Bj\u00EDc\u00ED identifik\u00E1tor {0} pro p\u0159\u00EDstroj {1} + MsgErrorMissingKeyValueInJSON = Chyb\u011Bj\u00EDc\u00ED hodnota pro kl\u00ED\u010D ''{0}'' MsgErrorMissingOnlineId = Chyb\u00ED zpr\u00E1va o portfoliu OnlineId pro cenn\u00FD pap\u00EDr {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_da.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_da.properties index 32980d3e80..112c2b677e 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_da.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_da.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = PWP Leeway UG API-n\u00F8gle mangler. Konfigurer i MsgErrorMissingAPIKey = API-n\u00F8gle mangler. Konfigurer API-n\u00F8gle i pr\u00E6ferencer. +MsgErrorMissingIdentifierForSecurity = Manglende identifikation {0} for instrument {1}. + MsgErrorMissingKeyValueInJSON = Manglende v\u00E6rdi for n\u00F8gle ''{0}'' MsgErrorMissingOnlineId = Manglende online portef\u00F8ljerapport for v\u00E6rdipapir {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_de.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_de.properties index 607484fbd8..afc7a98913 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_de.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_de.properties @@ -524,6 +524,8 @@ MsgErrorLeewayAPIKeyMissing = PWP Leeway UG API-Schl\u00FCssel ist nicht in den MsgErrorMissingAPIKey = API-Schl\u00FCssel fehlt. Konfigurieren Sie den API-Schl\u00FCssel in den Einstellungen. +MsgErrorMissingIdentifierForSecurity = Fehlender Bezeichner {0} f\u00FCr Instrument {1} + MsgErrorMissingKeyValueInJSON = Schl\u00FCssel ''{0}'' nicht gefunden MsgErrorMissingOnlineId = Fehlende Portfolio Report OnlineId f\u00FCr Wertpapier {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_es.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_es.properties index 59dfa97ef6..74f856fbf3 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_es.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_es.properties @@ -524,6 +524,8 @@ MsgErrorLeewayAPIKeyMissing = No hay clave API definida para PWP Leeway UG, conf MsgErrorMissingAPIKey = Falta la clave API. Config\u00FArela en el men\u00FA 'Preferencias'. +MsgErrorMissingIdentifierForSecurity = Falta identificador {0} para instrumento {1} + MsgErrorMissingKeyValueInJSON = No se ha encontrado la clave \u201B{0}\u2019 MsgErrorMissingOnlineId = Falta Portfolio Report OnlineId para el valor {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_fr.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_fr.properties index 32f937f09e..03ab56e03d 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_fr.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_fr.properties @@ -524,6 +524,8 @@ MsgErrorLeewayAPIKeyMissing = Clef API PWP Leeway UG manquante. \u00C0 configure MsgErrorMissingAPIKey = La cl\u00E9 API est manquante. Configurez la cl\u00E9 API dans les pr\u00E9f\u00E9rences. +MsgErrorMissingIdentifierForSecurity = Identifiant manquant {0} pour l'instrument {1} + MsgErrorMissingKeyValueInJSON = Valeur manquante pour clef ''{0}'' MsgErrorMissingOnlineId = Portfolio Report OnlineId manquant pour titre {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_it.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_it.properties index 57ca7ca0c9..96a83413e3 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_it.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_it.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = La chiave API di PWP Leeway UG \u00E8 assente. Con MsgErrorMissingAPIKey = Manca la chiave API. Configura la chiave API nelle preferenze. +MsgErrorMissingIdentifierForSecurity = Identificatore mancante {0} per lo strumento {1} + MsgErrorMissingKeyValueInJSON = Valore mancante per la chiave ''{0}'' MsgErrorMissingOnlineId = Report Portfolio, manca OnlineID del titolo {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_nl.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_nl.properties index 752993d837..6df1993d83 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_nl.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_nl.properties @@ -524,6 +524,8 @@ MsgErrorLeewayAPIKeyMissing = De PWP Leeway UG API-sleutel ontbreekt. Configuree MsgErrorMissingAPIKey = De API-sleutel ontbreekt. Configureer deze in Help/Instellingen/API-sleutels. +MsgErrorMissingIdentifierForSecurity = Identificatiecode {0} voor instrument {1} ontbreekt. + MsgErrorMissingKeyValueInJSON = Ontbrekende waarde voor sleutel ''{0}'' MsgErrorMissingOnlineId = Ontbrekend Portfolio Report OnlineID voor effect {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pl.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pl.properties index c011086a55..06994e2b62 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pl.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pl.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = Brak klucza API PWP Leeway UG. Skonfiguruj w prefe MsgErrorMissingAPIKey = Brakuje klucza API. Skonfiguruj klucz API w ustawieniach. +MsgErrorMissingIdentifierForSecurity = Brakuj\u0105cy identyfikator {0} dla instrumentu {1} + MsgErrorMissingKeyValueInJSON = Brakuj\u0105ca warto\u015B\u0107 dla klucza ''{0}'' MsgErrorMissingOnlineId = Brakuj\u0105cy identyfikator online raportu portfela dla waloru {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt.properties index 948aa2aac1..b7a2969f6f 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt.properties @@ -510,6 +510,8 @@ MsgErrorLeewayAPIKeyMissing = A chave da API PWP Leeway UG est\u00E1 ausente. Co MsgErrorMissingAPIKey = Falta a chave API. Configure a chave API nas prefer\u00EAncias. +MsgErrorMissingIdentifierForSecurity = Identificador {0} em falta para o instrumento {1} + MsgErrorMissingKeyValueInJSON = Valor ausente para a chave ''{0}'' MsgErrorMissingOnlineId = Falta Portfolio Report OnlineId para o ativo {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt_BR.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt_BR.properties index 87259aed78..37bd41c68c 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt_BR.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_pt_BR.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = A chave da API PWP Leeway UG est\u00E1 ausente. Co MsgErrorMissingAPIKey = Falta a chave API. Configure a chave API nas prefer\u00EAncias. +MsgErrorMissingIdentifierForSecurity = Falta o identificador {0} para o instrumento {1} + MsgErrorMissingKeyValueInJSON = Valor ausente para a chave ''{0}'' MsgErrorMissingOnlineId = Falta Portfolio Report OnlineId para o ativo {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_ru.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_ru.properties index 4bd880f5ad..8fa13e72e7 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_ru.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_ru.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = PWP Leeway UG API key \u043E\u0442\u0441\u0443\u04 MsgErrorMissingAPIKey = \u041A\u043B\u044E\u0447 API \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442. \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u0442\u0435 \u043A\u043B\u044E\u0447 API \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445. +MsgErrorMissingIdentifierForSecurity = \u041E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 {0} \u0434\u043B\u044F \u043F\u0440\u0438\u0431\u043E\u0440\u0430 {1} + MsgErrorMissingKeyValueInJSON = \u041E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043A\u043B\u044E\u0447\u0430 "{0}" MsgErrorMissingOnlineId = \u041E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043E\u043D\u043B\u0430\u0439\u043D-\u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043E\u0442\u0447\u0435\u0442\u0430 \u043E \u043F\u043E\u0440\u0442\u0444\u0435\u043B\u0435 \u0434\u043B\u044F \u0430\u043A\u0442\u0438\u0432\u0430 {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_sk.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_sk.properties index c87cc71801..364e76da6d 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_sk.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_sk.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = Ch\u00FDba k\u013E\u00FA\u010D API PWP Leeway UG. MsgErrorMissingAPIKey = Ch\u00FDba k\u013E\u00FA\u010D API. Nakonfigurujte k\u013E\u00FA\u010D API v nastaveniach. +MsgErrorMissingIdentifierForSecurity = Ch\u00FDbaj\u00FAci identifik\u00E1tor {0} pre pr\u00EDstroj {1} + MsgErrorMissingKeyValueInJSON = Ch\u00FDbaj\u00FAca hodnota pre k\u013E\u00FA\u010D ''{0}'' MsgErrorMissingOnlineId = Ch\u00FDba spr\u00E1va o portf\u00F3liu OnlineId pre cenn\u00FD papier {0} diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh.properties index 4adf80d1ad..778cdd1e55 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = \u7F3A\u5C11 PWP Leeway UG API \u5BC6\u94A5\u3002\ MsgErrorMissingAPIKey = \u7F3A\u5C11 API \u5BC6\u94A5\u3002\u8BF7\u4E8E\u504F\u597D\u8BBE\u7F6E\u4E2D\u8BBE\u7F6E API \u5BC6\u94A5\u3002 +MsgErrorMissingIdentifierForSecurity = \u4EEA\u5668 {1} \u7F3A\u5C11\u6807\u8BC6\u7B26 {0} + MsgErrorMissingKeyValueInJSON = \u952E \u201C{0}\u201D \u7F3A\u5C11\u503C MsgErrorMissingOnlineId = \u8BC1\u5238 {0} \u7F3A\u5C11 Portfolio Report OnlineId diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh_TW.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh_TW.properties index 8913b8a25f..a97f16f617 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh_TW.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/messages_zh_TW.properties @@ -523,6 +523,8 @@ MsgErrorLeewayAPIKeyMissing = PWP Leeway\u7684UG API\u91D1\u9470\u4E1F\u5931\u30 MsgErrorMissingAPIKey = API\u91D1\u9470\u4E1F\u5931\u3002\u5728\u504F\u597D\u8A2D\u5B9A\u4E2D\u8A2D\u7F6EAPI\u91D1\u9470\u3002 +MsgErrorMissingIdentifierForSecurity = \u5100\u5668 {1} \u7F3A\u5C11\u8B58\u5225\u78BC {0} + MsgErrorMissingKeyValueInJSON = \u9375\u503C\u300C{0}\u300D\u7F3A\u5C11\u503C MsgErrorMissingOnlineId = \u7F3A\u5C11\u8B49\u5238{0}\u7684\u6295\u8CC7\u5831\u544A\u7DDA\u4E0AID diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/online/impl/AMFIIndiaQuoteFeed.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/online/impl/AMFIIndiaQuoteFeed.java new file mode 100644 index 0000000000..1c96ab1a43 --- /dev/null +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/online/impl/AMFIIndiaQuoteFeed.java @@ -0,0 +1,159 @@ +package name.abuchen.portfolio.online.impl; + +import java.io.IOException; +import java.text.MessageFormat; +import java.text.ParseException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +import name.abuchen.portfolio.Messages; +import name.abuchen.portfolio.PortfolioLog; +import name.abuchen.portfolio.model.LatestSecurityPrice; +import name.abuchen.portfolio.model.Security; +import name.abuchen.portfolio.model.SecurityPrice; +import name.abuchen.portfolio.online.QuoteFeed; +import name.abuchen.portfolio.online.QuoteFeedData; +import name.abuchen.portfolio.util.WebAccess; + +/** + * Load NAV prices from AMFI India. + */ +public class AMFIIndiaQuoteFeed implements QuoteFeed +{ + private record MutualFund(String schemeCode, String isin1, String isin2, LatestSecurityPrice price) + { + } + + public static final String ID = "AMFIINDIA"; //$NON-NLS-1$ + + private final PageCache> cache = new PageCache<>(); + + @Override + public String getId() + { + return ID; + } + + @Override + public String getName() + { + return "AMFI India"; //$NON-NLS-1$ + } + + @Override + public Optional getLatestQuote(Security security) + { + QuoteFeedData data = getHistoricalQuotes(security, false); + + if (!data.getErrors().isEmpty()) + PortfolioLog.abbreviated(data.getErrors()); + + List prices = data.getLatestPrices(); + + if (prices.isEmpty()) + return Optional.empty(); + + Collections.sort(prices, new SecurityPrice.ByDate()); + + return Optional.of(prices.get(prices.size() - 1)); + } + + @Override + public QuoteFeedData previewHistoricalQuotes(Security security) + { + return getHistoricalQuotes(security, true); + } + + @Override + public QuoteFeedData getHistoricalQuotes(Security security, boolean collectRawResponse) + { + if (security.getIsin() == null) + { + return QuoteFeedData.withError( + new IOException(MessageFormat.format(Messages.MsgErrorMissingIdentifierForSecurity, + Messages.CSVColumn_ISIN, security.getName()))); + } + + QuoteFeedData data = new QuoteFeedData(); + + try + { + var funds = cache.lookup(AMFIIndiaQuoteFeed.class.toString()); + + if (funds == null) + { + WebAccess webaccess = new WebAccess("www.amfiindia.com", "/spages/NAVAll.txt"); //$NON-NLS-1$ //$NON-NLS-2$ + String content = webaccess.get(); + + if (collectRawResponse) + data.addResponse("https://www.amfiindia.com/spages/NAVAll.txt", content); //$NON-NLS-1$ + + funds = parse(content); + cache.put(AMFIIndiaQuoteFeed.class.toString(), funds); + } + + for (MutualFund fund : funds) + { + if (security.getIsin().equals(fund.isin1) || security.getIsin().equals(fund.isin2)) + { + data.addPrice(fund.price); + break; + } + } + } + catch (IOException e) + { + data.addError(e); + } + + return data; + } + + private List parse(String content) + { + var answer = new ArrayList(); + + String[] lines = content.split("\\r?\\n"); //$NON-NLS-1$ + + var dateFormat = DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.US); //$NON-NLS-1$ + + for (String line : lines) + { + // @formatter:off + // Scheme Code;ISIN Div Payout/ ISIN Growth;ISIN Div Reinvestment;Scheme Name;Net Asset Value;Date + // @formatter:on + String[] words = line.split(";"); //$NON-NLS-1$ + if (words.length < 6) + continue; + + String schemeCode = words[0]; + String isin1 = words[1]; + String isin2 = words[2]; + String nav = words[4]; + String date = words[5]; + + try + { + var price = new LatestSecurityPrice(LocalDate.parse(date, dateFormat), YahooHelper.asPrice(nav)); + price.setHigh(LatestSecurityPrice.NOT_AVAILABLE); + price.setLow(LatestSecurityPrice.NOT_AVAILABLE); + price.setVolume(LatestSecurityPrice.NOT_AVAILABLE); + + if (price.getValue() > 0) + answer.add(new MutualFund(schemeCode, isin1, isin2, price)); + } + catch (ParseException | DateTimeParseException ignore) + { + // ignore this record + } + } + + return answer; + } +}