VIAC Invest in Portfolio Performance einbinden

Hallo zusammen

Ich verfasse diesen Beitrag bewusst auf Deutsch, da meine Frage spezifisch VIAC betrifft.

Bisher hatte ich ein VIAC 3a-Konto, das ich problemlos in Portfolio Performance (PP) integriert habe. Ich habe dazu ein neues Depot mit dem Namen “VIAC 3a” erstellt und konnte alle Wertschriften problemlos finden (z. B. VIAC: SWC (CH) IPF I IEF Japan NT).

Nun gibt es seit “Kurzem” VIAC Invest, und ich habe dort ein Depot eröffnet. Natürlich möchte ich dieses nun ebenfalls in PP einbinden. Ich habe bereits ein neues Depot mit dem Namen “VIAC Invest” erstellt und versucht, die PDFs zu importieren, um die Wertpapierkäufe zu dokumentieren – jedoch ohne Erfolg.

Anschliessend habe ich versucht, die Wertschriften manuell zu finden, aber auch das war nicht möglich. Ist jemand bereits auf dieses Problem gestossen?

Hier ein Beispiel für eine VIAC Invest-Wertschrift, die ich nicht in PP finden kann:
VIAC Equity Emerging Markets, ISIN: CH1336969105.

Vielen Dank im Voraus!

Du kannst über die Watchlist üben rechts über das Plus Symbol leere Wertpapiere anlegen, ohne Suchfunktion.

Das bedeutet also, dass der Kurs nicht getrackt wird und ich somit gar nicht nachvollziehen kann, wie sich meine Investition entwickelt?

Es wird schwierig etwas zu tracken was scheinbar nicht an der Börse gehandelt wird.

Du kannst die historischen Kurse von hier via csv importieren: VIAC Equity Emering Markets | Swiss Fund Data
auf dieser Webseite findest du alle Titel mit der ISIN Nummer.

1 Like

Wenn der Kurs nicht über PP abgerufen werden kann, kannst du das Wertpapier selber anlegen und die historischen Kurse manuell eintragen. Entweder einzeln zu bestimmten Zeiten oder dann über einen manuellen CSV-Import von den SwissFundData.

Hey Leute

Da ich auch die Herausforderung mit den Kursen für die neuen Viac Invest Fonds habe greife ich diesen Post gerne auf.

Bei SwissFundData kann man über einen Endpunkt die Kurshistorie als Excel herunterladen für eine Vielzahl an Fonds. Es braucht einfach die ID am Ende, welche SwissFundData definiert hat. Die ID sieht man in der URL wenn man einen Fonds sucht und die Detailseite öffnet.

Bei SwissFundData sind Daten für mehr als 50’000 Fonds & ETFs verfügbar. Insofern wäre es eine umfangreiche Quelle, welche fix in PP eingebaut werden könnte. Man könnte damit direkt auch den Lieferant VIAC/CS Fonds ersetzen, welcher wegen der Übernahem der CreditSuisse durch die UBS allenfalls gar nicht mehr funktioniert.

Für den Fonds CH1336969105 ist die ID 175977 und der Endpunkt sieht entsprechend so aus:

https://sfd.lbswiss.ch/sfdpub/de/funds/excelData/175977

Die Response sieht dann so aus:

sep=;
VIAC Equity Emering Markets - CH1336969105 (CHF)
Date;CCY Chart Price;Chart Price;CCY Adjusted Chart Price;Adjusted Chart Price;CCY Total Return Chart Price;Total Return Chart Price;Net Asset Value;Issue Price;Redemption Price;Closing Price
2024-12-03;CHF;100;CHF;100;CHF;100;100;100.18;98.75;
2024-12-04;CHF;100;CHF;100;CHF;100;100;100;100;
2024-12-05;CHF;99.76;CHF;99.76;CHF;99.76;99.76;99.93;98.51;
2024-12-06;CHF;99.71;CHF;99.71;CHF;99.71;99.71;;;
2024-12-09;CHF;100.43;CHF;100.43;CHF;100.43;100.43;;;
2024-12-10;CHF;100.87;CHF;100.87;CHF;100.87;100.87;;;
2024-12-11;CHF;100.83;CHF;100.83;CHF;100.83;100.83;;;
2024-12-12;CHF;101.83;CHF;101.83;CHF;101.83;101.83;;;
2024-12-13;CHF;101.83;CHF;101.83;CHF;101.83;101.83;;;
2024-12-16;CHF;101.27;CHF;101.27;CHF;101.27;101.27;;;
2024-12-17;CHF;100.62;CHF;100.62;CHF;100.62;100.62;;;
2024-12-18;CHF;100.61;CHF;100.61;CHF;100.61;100.61;;;
2024-12-19;CHF;99.94;CHF;99.94;CHF;99.94;99.94;;;
2024-12-20;CHF;98.75;CHF;98.75;CHF;98.75;98.75;;;
2024-12-23;CHF;100.25;CHF;100.25;CHF;100.25;100.25;;;
2024-12-27;CHF;100.53;CHF;100.53;CHF;100.53;100.53;;;
2024-12-30;CHF;100.69;CHF;100.69;CHF;100.69;100.69;;;
2024-12-31;CHF;100.37;CHF;100.37;CHF;100.37;100.37;;;
2025-01-03;CHF;100.71;CHF;100.71;CHF;100.71;100.71;;;
2025-01-06;CHF;100.61;CHF;100.61;CHF;100.61;100.61;;;
2025-01-07;CHF;101.07;CHF;101.07;CHF;101.07;101.07;;;
2025-01-08;CHF;100.57;CHF;100.57;CHF;100.57;100.57;;;
2025-01-09;CHF;100.3;CHF;100.3;CHF;100.3;100.3;;;
2025-01-10;CHF;99.86;CHF;99.86;CHF;99.86;99.86;;;
2025-01-13;CHF;98.54;CHF;98.54;CHF;98.54;98.54;;;
2025-01-14;CHF;99.3;CHF;99.3;CHF;99.3;99.3;;;
2025-01-15;CHF;99.25;CHF;99.25;CHF;99.25;99.25;;;
2025-01-16;CHF;100.3;CHF;100.3;CHF;100.3;100.3;;;
2025-01-17;CHF;100.84;CHF;100.84;CHF;100.84;100.84;;;
2025-01-20;CHF;101.2;CHF;101.2;CHF;101.2;101.2;;;
2025-01-21;CHF;101.2;CHF;101.2;CHF;101.2;101.2;;;
2025-01-22;CHF;101.24;CHF;101.24;CHF;101.24;101.24;;;
2025-01-23;CHF;101.4;CHF;101.4;CHF;101.4;101.4;;;
2025-01-24;CHF;101.71;CHF;101.71;CHF;101.71;101.71;;;
2025-01-27;CHF;101.71;CHF;101.71;CHF;101.71;101.71;;;
2025-01-28;CHF;101.71;CHF;101.71;CHF;101.71;101.71;;;
2025-02-03;CHF;101.01;CHF;101.01;CHF;101.01;101.01;;;
2025-02-04;CHF;102.15;CHF;102.15;CHF;102.15;102.15;;;
2025-02-05;CHF;101.83;CHF;101.83;CHF;101.83;101.83;;;
2025-02-06;CHF;102.97;CHF;102.97;CHF;102.97;102.97;;;
2025-02-07;CHF;104.02;CHF;104.02;CHF;104.02;104.02;;;
2025-02-10;CHF;104.17;CHF;104.17;CHF;104.17;104.17;;;
2025-02-11;CHF;104.13;CHF;104.13;CHF;104.13;104.13;;;
2025-02-12;CHF;104.77;CHF;104.77;CHF;104.77;104.77;;;
2025-02-13;CHF;104.04;CHF;104.04;CHF;104.04;104.04;;;
2025-02-14;CHF;104.32;CHF;104.32;CHF;104.32;104.32;;;
2025-02-17;CHF;105.21;CHF;105.21;CHF;105.21;105.21;;;
2025-02-18;CHF;106.06;CHF;106.06;CHF;106.06;106.06;;;
2025-02-19;CHF;106.11;CHF;106.11;CHF;106.11;106.11;;;
2025-02-20;CHF;105.24;CHF;105.24;CHF;105.24;105.24;;;
2025-02-21;CHF;106.58;CHF;106.58;CHF;106.58;106.58;;;
2025-02-24;CHF;105.24;CHF;105.24;CHF;105.24;105.24;;;
2025-02-25;CHF;103.62;CHF;103.62;CHF;103.62;103.62;;;
2025-02-26;CHF;104.86;CHF;104.86;CHF;104.86;104.86;;;
2025-02-27;CHF;104.49;CHF;104.49;CHF;104.49;104.49;;;
2025-02-28;CHF;102.36;CHF;102.36;CHF;102.36;102.36;;;
2025-03-03;CHF;101.93;CHF;101.93;CHF;101.93;101.93;;;
2025-03-04;CHF;100.63;CHF;100.63;CHF;100.63;100.63;;;
2025-03-05;CHF;102.58;CHF;102.58;CHF;102.58;102.58;;;
2025-03-06;CHF;103.72;CHF;103.72;CHF;103.72;103.72;;;
2025-03-07;CHF;102.64;CHF;102.64;CHF;102.64;102.64;;;
2025-03-10;CHF;101.54;CHF;101.54;CHF;101.54;101.54;;;
2025-03-11;CHF;101.06;CHF;101.06;CHF;101.06;101.06;;;
2025-03-12;CHF;101.57;CHF;101.57;CHF;101.57;101.57;;;
2025-03-13;CHF;101.36;CHF;101.36;CHF;101.36;101.36;;;
2025-03-14;CHF;102.63;CHF;102.63;CHF;102.63;102.63;;;
2025-03-17;CHF;103.23;CHF;103.23;CHF;103.23;103.23;;;
2025-03-18;CHF;104.03;CHF;104.03;CHF;104.03;104.03;;;
2025-03-19;CHF;104.2;CHF;104.2;CHF;104.2;104.2;;;
2025-03-20;CHF;104.28;CHF;104.28;CHF;104.28;104.28;;;
2025-03-21;CHF;103.52;CHF;103.52;CHF;103.52;103.52;;;
2025-03-24;CHF;104.04;CHF;104.04;CHF;104.04;104.04;;;
2025-03-25;CHF;103.2;CHF;103.2;CHF;103.2;103.2;;;
2025-03-26;CHF;103.62;CHF;103.62;CHF;103.62;103.62;;;
2025-03-27;CHF;103.3;CHF;103.3;CHF;103.3;103.3;;;
2025-03-28;CHF;102.32;CHF;102.32;CHF;102.32;102.32;;;
2025-03-31;CHF;100.99;CHF;100.99;CHF;100.99;100.99;;;
2025-04-01;CHF;101.61;CHF;101.61;CHF;101.61;101.61;;;
2025-04-02;CHF;101.69;CHF;101.69;CHF;101.69;101.69;;;
2025-04-03;CHF;97.88;CHF;97.88;CHF;97.88;97.88;;;
2025-04-04;CHF;97.88;CHF;97.88;CHF;97.88;97.88;;;
2025-04-07;CHF;89.48;CHF;89.48;CHF;89.48;89.48;;;
2025-04-08;CHF;89.26;CHF;89.26;CHF;89.26;89.26;;;
2025-04-09;CHF;86.7;CHF;86.7;CHF;86.7;86.7;;;
2025-04-10;CHF;89.02;CHF;89.02;CHF;89.02;89.02;;;
2025-04-11;CHF;88.45;CHF;88.45;CHF;88.45;88.45;;;
2025-04-14;CHF;89.99;CHF;89.99;CHF;89.99;89.99;;;
2025-04-15;CHF;90.84;CHF;90.84;CHF;90.84;90.84;;;
2025-04-16;CHF;89.67;CHF;89.67;CHF;89.67;89.67;;;
2025-04-17;CHF;90.85;CHF;90.85;CHF;90.85;90.85;;;
2025-04-22;CHF;90.79;CHF;90.79;CHF;90.79;90.79;;;
2025-04-23;CHF;94.28;CHF;94.28;CHF;94.28;94.28;;;
2025-04-24;CHF;93.89;CHF;93.89;CHF;93.89;93.89;;;
2025-04-25;CHF;94.58;CHF;94.58;CHF;94.58;94.58;;;
2025-04-28;CHF;94.55;CHF;94.55;CHF;94.55;94.55;;;
2025-04-29;CHF;94.72;CHF;94.72;CHF;94.72;94.72;;;
2025-04-30;CHF;95.04;CHF;95.04;CHF;95.04;95.04;;;
2025-05-02;CHF;97.02;CHF;97.02;CHF;97.02;97.02;;;
2025-05-05;CHF;97.02;CHF;97.02;CHF;97.02;97.02;;;
2025-05-06;CHF;97.48;CHF;97.48;CHF;97.48;97.48;;;
2025-05-07;CHF;97.04;CHF;97.04;CHF;97.04;97.04;;;
2025-05-08;CHF;97.51;CHF;97.51;CHF;97.51;97.51;;;
2025-05-09;CHF;98.15;CHF;98.15;CHF;98.15;98.15;;;
2025-05-12;CHF;101.92;CHF;101.92;CHF;101.92;101.92;;;
2025-05-13;CHF;101.25;CHF;101.25;CHF;101.25;101.25;;;
2025-05-14;CHF;102.46;CHF;102.46;CHF;102.46;102.46;;;
2025-05-15;CHF;102.17;CHF;102.17;CHF;102.17;102.17;;;
2025-05-16;CHF;102.39;CHF;102.39;CHF;102.39;102.39;;;
2025-05-19;CHF;101.08;CHF;101.08;CHF;101.08;101.08;;;
2025-05-20;CHF;101.02;CHF;101.02;CHF;101.02;101.02;;;
2025-05-21;CHF;100.82;CHF;100.82;CHF;100.82;100.82;;;
2025-05-22;CHF;100.58;CHF;100.58;CHF;100.58;100.58;;;
2025-05-23;CHF;100.14;CHF;100.14;CHF;100.14;100.14;;;
2025-05-26;CHF;99.93;CHF;99.93;CHF;99.93;99.93;;;
2025-05-27;CHF;100.22;CHF;100.22;CHF;100.22;100.22;;;
2025-05-28;CHF;100.12;CHF;100.12;CHF;100.12;100.12;;;
2025-05-30;CHF;99.04;CHF;99.04;CHF;99.04;99.04;;;
2025-06-02;CHF;98.16;CHF;98.16;CHF;98.16;98.16;;;
2025-06-03;CHF;99.36;CHF;99.36;CHF;99.36;99.36;;;
2025-06-04;CHF;99.99;CHF;99.99;CHF;99.99;99.99;;;
2025-06-05;CHF;101.06;CHF;101.06;CHF;101.06;101.06;;;
2025-06-06;CHF;101.51;CHF;101.51;CHF;101.51;101.51;;;
2025-06-10;CHF;102.9;CHF;102.9;CHF;102.9;102.9;;;
2025-06-11;CHF;103.36;CHF;103.36;CHF;103.36;103.36;;;
2025-06-12;CHF;102.07;CHF;102.07;CHF;102.07;102.07;;;
2025-06-13;CHF;100.86;CHF;100.86;CHF;100.86;100.86;;;
2025-06-16;CHF;101.41;CHF;101.41;CHF;101.41;101.41;;;
2025-06-17;CHF;101.92;CHF;101.92;CHF;101.92;101.92;;;
2025-06-18;CHF;101.9;CHF;101.9;CHF;101.9;101.9;;;
2025-06-19;CHF;100.69;CHF;100.69;CHF;100.69;100.69;;;
2025-06-20;CHF;101.59;CHF;101.59;CHF;101.59;101.59;;;
2025-06-23;CHF;100.49;CHF;100.49;CHF;100.49;100.49;;;
2025-06-24;CHF;101.93;CHF;101.93;CHF;101.93;101.93;;;
2025-06-25;CHF;102.76;CHF;102.76;CHF;102.76;102.76;;;
2025-06-26;CHF;102.53;CHF;102.53;CHF;102.53;102.53;;;
2025-06-27;CHF;102.67;CHF;102.67;CHF;102.67;102.67;;;
2025-06-30;CHF;101.77;CHF;101.77;CHF;101.77;101.77;;;
2025-07-01;CHF;101.76;CHF;101.76;CHF;101.76;101.76;;;
2025-07-02;CHF;102.14;CHF;102.14;CHF;102.14;102.14;;;
2025-07-03;CHF;103.09;CHF;103.09;CHF;103.09;103.09;;;
2025-07-04;CHF;102.34;CHF;102.34;CHF;102.34;102.34;;;
2025-07-07;CHF;102.31;CHF;102.31;CHF;102.31;102.31;;;
2025-07-08;CHF;102.88;CHF;102.88;CHF;102.88;102.88;;;
2025-07-09;CHF;102.2;CHF;102.2;CHF;102.2;102.2;;;
2025-07-10;CHF;102.83;CHF;102.83;CHF;102.83;102.83;;;
2025-07-11;CHF;102.52;CHF;102.52;CHF;102.52;102.52;;;
2025-07-14;CHF;102.56;CHF;102.56;CHF;102.56;102.56;;;
2025-07-15;CHF;104.02;CHF;104.02;CHF;104.02;104.02;;;
2025-07-16;CHF;104.43;CHF;104.43;CHF;104.43;104.43;;;
2025-07-17;CHF;104.7;CHF;104.7;CHF;104.7;104.7;;;
2025-07-18;CHF;104.75;CHF;104.75;CHF;104.75;104.75;;;
2025-07-21;CHF;104.78;CHF;104.78;CHF;104.78;104.78;;;
2025-07-22;CHF;104.07;CHF;104.07;CHF;104.07;104.07;;;
2025-07-23;CHF;105.26;CHF;105.26;CHF;105.26;105.26;;;
2025-07-24;CHF;105.59;CHF;105.59;CHF;105.59;105.59;;;
2025-07-25;CHF;105.04;CHF;105.04;CHF;105.04;105.04;;;
2025-07-28;CHF;105.45;CHF;105.45;CHF;105.45;105.45;;;
2025-07-29;CHF;105.99;CHF;105.99;CHF;105.99;105.99;;;
2025-07-30;CHF;106.43;CHF;106.43;CHF;106.43;106.43;;;
2025-07-31;CHF;105.82;CHF;105.82;CHF;105.82;105.82;;;
2025-08-04;CHF;104.87;CHF;104.87;CHF;104.87;104.87;;;
2025-08-05;CHF;105.49;CHF;105.49;CHF;105.49;105.49;;;
2025-08-06;CHF;105.4;CHF;105.4;CHF;105.4;105.4;;;
2025-08-07;CHF;106.83;CHF;106.83;CHF;106.83;106.83;;;
2025-08-08;CHF;106.22;CHF;106.22;CHF;106.22;106.22;;;
2025-08-11;CHF;107.04;CHF;107.04;CHF;107.04;107.04;;;
2025-08-12;CHF;106.76;CHF;106.76;CHF;106.76;106.76;;;
2025-08-13;CHF;107.77;CHF;107.77;CHF;107.77;107.77;;;
2025-08-14;CHF;107.77;CHF;107.77;CHF;107.77;107.77;;;
2025-08-15;CHF;107.77;CHF;107.77;CHF;107.77;107.77;;;
2025-08-18;CHF;107.89;CHF;107.89;CHF;107.89;107.89;;;
2025-08-19;CHF;107.56;CHF;107.56;CHF;107.56;107.56;;;
2025-08-20;CHF;106.35;CHF;106.35;CHF;106.35;106.35;;;
2025-08-21;CHF;107.08;CHF;107.08;CHF;107.08;107.08;;;
2025-08-22;CHF;106.68;CHF;106.68;CHF;106.68;106.68;;;
2025-08-25;CHF;108.43;CHF;108.43;CHF;108.43;108.43;;;
2025-08-26;CHF;107.65;CHF;107.65;CHF;107.65;107.65;;;
2025-08-27;CHF;107.21;CHF;107.21;CHF;107.21;107.21;;;
2025-08-28;CHF;106.32;CHF;106.32;CHF;106.32;106.32;;;
2025-08-29;CHF;105.68;CHF;105.68;CHF;105.68;105.68;;;
2025-09-01;CHF;106.5;CHF;106.5;CHF;106.5;106.5;;;
2025-09-02;CHF;106.75;CHF;106.75;CHF;106.75;106.75;;;
2025-09-03;CHF;106.99;CHF;106.99;CHF;106.99;106.99;;;
2025-09-04;CHF;106.95;CHF;106.95;CHF;106.95;106.95;;;
2025-09-05;CHF;107.01;CHF;107.01;CHF;107.01;107.01;;;
2025-09-08;CHF;107;CHF;107;CHF;107;107;;;
2025-09-09;CHF;108.31;CHF;108.31;CHF;108.31;108.31;;;
2025-09-10;CHF;109.62;CHF;109.62;CHF;109.62;109.62;;;
2025-09-11;CHF;109.68;CHF;109.68;CHF;109.68;109.68;;;
2025-09-12;CHF;111.07;CHF;111.07;CHF;111.07;111.07;;;
2025-09-15;CHF;111.07;CHF;111.07;CHF;111.07;111.07;;;
2025-09-16;CHF;111.03;CHF;111.03;CHF;111.03;111.03;;;
2025-09-17;CHF;111.57;CHF;111.57;CHF;111.57;111.57;;;
2025-09-18;CHF;112.18;CHF;112.18;CHF;112.18;112.18;;;
2025-09-19;CHF;112.17;CHF;112.17;CHF;112.17;112.17;;;
2025-09-22;CHF;112.2;CHF;112.2;CHF;112.2;112.2;;;
2025-09-23;CHF;112.34;CHF;112.34;CHF;112.34;112.34;;;
2025-09-24;CHF;113.03;CHF;113.03;CHF;113.03;113.03;;;
2025-09-25;CHF;113.15;CHF;113.15;CHF;113.15;113.15;;;

Beste Grüsse
Jan

1 Like

Danke. Stimmt, könnte man wahrscheinlich automatisieren. Ich lade einfach einmal im Monat die Kurse so über die ID runter, formattiere die csv ein klein wenig und importiere diese dann bei PP.

Ja, bestimmt. Ich kann zwar nicht mit Eclipse umgehen und Java kann ich auch nicht, aber man kann ja mal mit AI-Hilfe ein wenig rumprobieren.

Ausgangspunkt: name.abuchen.portfolio/src/name/abuchen/portfolio/online/impl/CSQuoteFeed.java ist wahrscheinlich für das alte Credit Suisse Zeug, was jetzt nicht mehr geht.

Das habe ich (bzw. gemini) so verändert, das ein allgemeiner konfigurierbarer CSV-Provider dabei herauskommen soll. Funktioniert auch grundsätzlich


aber ich finde die Stelle nicht wo ich die Felder zum Konfigurieren (Datumsspalte, Kursspalte, Datumsformat, Trennzeichen usw.) in der? dem? UI zum Leben erwecke. In dem (geänderten) Code für CSQuoteFeed.java habe ich gemogelt

indem ich die Standardwerte einfach an lbswiss.ch angepasst habe. Aber so ist das ja eigentlich nicht gedacht, da sollen eigentlich Eingabefelder zu sehen sein, wie

im JSON-Provider. An welcher Stelle muss man da noch drehen?

Ich vermute irgendwo in name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/wizards/security/ und irgendwo in name.abuchen.portfolio.tests/src/name/abuchen/portfolio/online/impl/ aber ich weiß überhaupt nicht was genau man da tun sollte.

Die veränderte CSQuoteFeed.java

package name.abuchen.portfolio.online.impl;

import java.io.IOException;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

import name.abuchen.portfolio.Messages;
import name.abuchen.portfolio.model.LatestSecurityPrice;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.SecurityProperty;
import name.abuchen.portfolio.money.Values;
import name.abuchen.portfolio.online.QuoteFeed;
import name.abuchen.portfolio.online.QuoteFeedData;
import name.abuchen.portfolio.util.WebAccess;

/**
 * Ein flexibler Feed zur Verarbeitung von CSV-Dateien, dessen Konfiguration
 * über separate Security-Eigenschaften verwaltet wird.
 */
public class CSQuoteFeed implements QuoteFeed
{
    public static final String ID = "CONFIGURABLE_CSV_FEED"; //$NON-NLS-1$

    // Konfigurations-Konstanten, analog zum GenericJSONQuoteFeed
    public static final String SEPARATOR_PROPERTY_NAME = "CSV-SEPARATOR"; //$NON-NLS-1$
    public static final String DATE_COLUMN_PROPERTY_NAME = "CSV-DATE-COLUMN"; //$NON-NLS-1$
    public static final String PRICE_COLUMN_PROPERTY_NAME = "CSV-PRICE-COLUMN"; //$NON-NLS-1$
    public static final String DATE_FORMAT_PROPERTY_NAME = "CSV-DATE-FORMAT"; //$NON-NLS-1$
    public static final String SKIP_LINES_PROPERTY_NAME = "CSV-SKIP-LINES"; //$NON-NLS-1$

    // Standardwerte
    private static final String DEFAULT_SEPARATOR = ";"; //$NON-NLS-1$
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; //$NON-NLS-1$
    private static final int DEFAULT_DATE_COLUMN = 0;
    private static final int DEFAULT_PRICE_COLUMN = 7;
    private static final int DEFAULT_SKIP_LINES = 4;

    @Override
    public String getId()
    {
        return ID;
    }

    @Override
    public String getName()
    {
        return "Konfigurierbarer CSV Feed"; //$NON-NLS-1$
    }

    @Override
    public QuoteFeedData getHistoricalQuotes(Security security, boolean collectRawResponse)
    {
        // Da es keine Unterscheidung zwischen Latest/Historic wie beim
        // JSON-Feed gibt,
        // verwenden wir nur einen Satz von Konstanten.
        return internalGetQuotes(security, security.getFeedURL(), collectRawResponse);
    }

    private QuoteFeedData internalGetQuotes(Security security, String feedURL, boolean collectRawResponse)
    {
        try
        {
            if (feedURL == null || feedURL.length() == 0)
                throw new IOException(MessageFormat.format(Messages.MsgMissingFeedURL, security.getName()));

            // 1. Konfiguration aus den Security-Eigenschaften abrufen
            QuoteConfig config = getConfigFromSecurity(security);

            // 2. CSV-Inhalt herunterladen (hier wird nur die FeedURL verwendet,
            // keine Parameter)
            String content = new WebAccess(feedURL).get();
            QuoteFeedData result = new QuoteFeedData();

            if (collectRawResponse)
            {
                result.addResponse(feedURL, content);
            }

            // 3. Inhalt zeilenweise verarbeiten
            String[] lines = content.lines().toArray(String[]::new);

            // Schleife beginnt nach den zu überspringenden Zeilen
            for (int i = config.getSkipLines(); i < lines.length; i++)
            {
                String line = lines[i];
                if (line.trim().isEmpty())
                    continue;

                try
                {
                    result.addPrice(getPrice(line, config));
                }
                catch (Exception e)
                {
                    // Fehler beim Parsen einer Zeile protokollieren und
                    // fortfahren
                    System.err.println(MessageFormat.format("Fehler beim Parsen der Zeile für {0}: {1}. Fehler: {2}", //$NON-NLS-1$
                                    security.getName(), line, e.getMessage()));
                }
            }

            return result;
        }
        catch (Exception e)
        {
            return QuoteFeedData.withError(e);
        }
    }

    /**
     * Ruft die Konfigurationswerte aus den Security-Eigenschaften ab.
     */
    private QuoteConfig getConfigFromSecurity(Security security)
    {
        // Trennzeichen
        String separator = security.getPropertyValue(SecurityProperty.Type.FEED, SEPARATOR_PROPERTY_NAME)
                        .orElse(DEFAULT_SEPARATOR);

        // Datumsspalte
        int dateCol = security.getPropertyValue(SecurityProperty.Type.FEED, DATE_COLUMN_PROPERTY_NAME)
                        .map(Integer::parseInt).orElse(DEFAULT_DATE_COLUMN);

        // Kursspalte
        int priceCol = security.getPropertyValue(SecurityProperty.Type.FEED, PRICE_COLUMN_PROPERTY_NAME)
                        .map(Integer::parseInt).orElse(DEFAULT_PRICE_COLUMN);

        // Datumsformat
        String dateFormat = security.getPropertyValue(SecurityProperty.Type.FEED, DATE_FORMAT_PROPERTY_NAME)
                        .orElse(DEFAULT_DATE_FORMAT);

        // Zu überspringende Zeilen
        int skipLines = security.getPropertyValue(SecurityProperty.Type.FEED, SKIP_LINES_PROPERTY_NAME)
                        .map(Integer::parseInt).orElse(DEFAULT_SKIP_LINES);

        // Hier könnte man auch DATE_TIMEZONE und DATE_LOCALE übernehmen, falls
        // nötig
        // (analog zum JSON-Feed)

        return new QuoteConfig(separator, dateCol, priceCol, dateFormat, skipLines);
    }

    /**
     * Parsed eine einzelne Datenzeile basierend auf der Konfiguration.
     */
    private LatestSecurityPrice getPrice(String line, QuoteConfig config)
                    throws DateTimeParseException, NumberFormatException
    {
        // Entfernt führende 'sep=;' falls vorhanden (für das SFD-Beispiel
        // relevant)
        String cleanedLine = line.trim().replaceFirst("sep=;", ""); //$NON-NLS-1$//$NON-NLS-2$

        // Split mit dem konfigurierten Separator
        // Regex.quote schützt vor Sonderzeichen im Separator
        String regexSeparator = java.util.regex.Pattern.quote(config.getSeparator());
        String[] tokens = cleanedLine.split(regexSeparator);

        if (tokens.length <= config.getDateColumn() || tokens.length <= config.getPriceColumn())
        {
            throw new IllegalArgumentException("Nicht genügend Spalten in der Zeile."); //$NON-NLS-1$
        }

        // Datum parsen
        String dateString = tokens[config.getDateColumn()].trim();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(config.getDateFormat());
        LocalDate localDate = LocalDate.parse(dateString, formatter);

        // Preis parsen: Ersetzt Kommas durch Punkte (für deutsche/schweizer
        // Zahlenformate)
        String priceString = tokens[config.getPriceColumn()].trim().replace(',', '.');
        double rawPrice = Double.parseDouble(priceString);

        // Faktorisiert den Preis
        long price = Values.Quote.factorize(rawPrice);

        return new LatestSecurityPrice(localDate, price);
    }
}

/**
 * Hilfsklasse zur Speicherung der Konfigurationsparameter. Die baseURL
 * entfällt, da sie die normale FeedURL ist.
 */
class QuoteConfig
{
    private final String separator;
    private final int dateColumn;
    private final int priceColumn;
    private final String dateFormat;
    private final int skipLines;

    public QuoteConfig(String separator, int dateColumn, int priceColumn, String dateFormat, int skipLines)
    {
        this.separator = separator;
        this.dateColumn = dateColumn;
        this.priceColumn = priceColumn;
        this.dateFormat = dateFormat;
        this.skipLines = skipLines;
    }

    public String getSeparator()
    {
        return separator;
    }

    public int getDateColumn()
    {
        return dateColumn;
    }

    public int getPriceColumn()
    {
        return priceColumn;
    }

    public int getSkipLines()
    {
        return skipLines;
    }

    public String getDateFormat()
    {
        return dateFormat;
    }
}