Java Partielle Gewinnberechnung falsch?

CyborgBeta

Captain
Registriert
Jan. 2021
Beiträge
3.270
Nabend,

hier komme ich nicht weiter und ChatGpt versteht mich gerade auch nicht (oder umgekehrt).

Ich weiß nicht, ob die Formel in der return-Zeile richtig ist ...

Ich möchte wissen, ob ein Teilverkauf an einem festen Preis mit Gewinn wäre oder ohne:

Java:
  private double calculatePartialProfitAtPrice(double price) {
    double totalBase = 0;
    double totalQuote = 0;
    for (Transaction transaction : transactions) {
      if (transaction.isBuy()) {
        totalBase += transaction.quantity() * transaction.price() * 0.9985;
        totalQuote += transaction.quantity() * 0.9985;
      } else {
        totalBase -= transaction.quantity() * transaction.price() * 1.0015;
        totalQuote -= transaction.quantity() * 1.0015;
      }
    }
    if (totalBase == 0 || totalQuote == 0) {
      return 0;
    }
    return getQuoteQuantity(price) * price - totalBase / totalQuote * totalQuote;
  }

fraglich ist Zeile 16. getQuoteQuantity gibt die Menge für einen festen Wert an einem übergebenen Preis zurück.

Beispiel: Der übergebene Preis ist 150 und es sollen immer für 10 Basiswährung Anteile gekauft werden, dann würde die Methode 0.066 zurückgeben.

Der Break-Even-Point wäre eigentlich simpel totalBase / totalQuote, wenn ich mich nicht täusche.

Bitte beachten, dass zwischendurch auch verkauft wurde, also sowohl Käufe als auch Verkäufe in der Transaktionsliste sind.

... Es sollte eigentlich möglich sein, auch unterhalb des BEPs Partialverkäufe mit Gewinn zu tätigen, der BEP würde sich dadurch halt nach oben verschieben, denke ich.

Und bitte jetzt nicht schreiben, dass das Grundschulmathematik sei. :D Manchmal versteht man eben etwas nicht.
 
Ich denke ohne exemplarische Testdaten und weitere Erklärung was hier was ist, ist das zu abstrakt.

CyborgBeta schrieb:
Bitte beachten, dass zwischendurch auch verkauft wurde, also sowohl Käufe als auch Verkäufe in der Transaktionsliste sind.
Gilt dann im Aktienumfeld nicht das FIFO-Prinzip? Da wäre es ja irgendwie nicht ganz richtig über alle Transaktionen zu loopen und dann am Ende einmalig irgendwas zu berechnen. Aber wie gesagt... zu abstrakt... für mich hier ;)
 
  • Gefällt mir
Reaktionen: BeBur und CyborgBeta
CyborgBeta schrieb:
Mich hindert, dass ich das Ergebnis der Berechnung dann quasi nicht "verifizieren" kann.
Genau hier sind wir bei Whitebox und Unittests. Oder auch praktischer gesagt: wenn du was baust, brauchst du eine Möglichkeit das zu verifizieren.

Du baust dir also z.B. eine Tabelle mit Beispielen. Diese jagst du durch deine Methode (Whitebox aka mit Debugger, Unit aka automatisiert mit programmierten Ergebnisauswertung) und schaust ob es passt. Du musst dir jeden sog. Edge Case (Sonderfälle) durchdenken und entsprechend testen.
 
  • Gefällt mir
Reaktionen: CyborgBeta
Ich glaube langsam, der BEP spielt dabei gar keine Rolle, sondern man muss nur den mittleren Einkaufspreis berechnen, und wenn ein partieller Verkauf "weit genug" über diesen liegt, dann ist dieser profitabel.

... Aber eigentlich ist der BEP ja gerade der mittlere Einkaufspreis, abzüglich bereits getätigter Verkäufe. Ich stehe da etwas auf dem Schlauch.

Btw. Ja, das FIFO-Prinzip gilt bei so etwas. Da aber alle Transaktionen die gleichen "Konditionen" oder "Modalitäten" haben (sprich, zum Beispiel 0.15 % Fees anfallen), kann vermutlich auch alles in einen Topf geworfen werden.
 
Habe den Rest vom Thread garnicht gelesen, möchte nur anmerken, dass ich den Ausdruck totalBase / totalQuote * totalQuote richtig schrecklich finde. Klammerung oder wenigstens eine Anpassung der Reihenfolge würde die Lesbarkeit deutlich erhöhen.
Und da ich grade den Teil lese und alle beteiligten Variablen double sind, was soll ... / totalQuote * totalQuote denn überhaupt bewirken? Das ist doch 1.

Edit: Habe jetzt noch den Thread gelesen. Du solltest das FIFO-Prinzip schon berücksichtigen.
Beispiel: am 1.1.24 wurden 20 Anteile zu je 10€ für insgesamt 200€ erworben.
Meinetwegen zzgl. 1€+0,15% Gebühr für dann insgesamt 201,30 €. Einstandspreis (exkl. Gebühren) dann also 10,00€.(Break-Even beim Verkauf mit gleicher Gebühr zu 10,19€.)
Am 1.2. ist der Preis zwischenzeitlich gestiegen. Es werden 40 Anteile zu je 20€ eingekauft. Zzgl. 1€+0,15% Gebühr für insgesamt 802,20€. Einstandspreis (gemittelt) nun also 16,67€.
Am 1.3. ist der Kurs wieder auf 14€ gefallen. Es werden 21 Anteile verkauft. Davon wurden 20 beim niedrigen Kurs und 1 beim hohen Kurs gekauft.
Einkaufswert der verkauften Anteile ist somit 220,00€. Verkaufspreis ist 21×14=297,00€ (zzgl. 4,56€ Gebühren). Der Gewinn (ohne Berücksichtigung der Gebühren) beträgt bei der Transaktion dann also 77€, obwohl der Verkaufspreis am 1.3. unterhalb des (mittleren) Einstandspreises liegt.
Long Story short: FIFO am einfachsten der Reihe nach durchgehen.
 
Zuletzt bearbeitet: (Formatierung (Schriftfarbe) angepasst)
  • Gefällt mir
Reaktionen: Wo bin ich hier und BeBur
@simpsonsfan Ich kann nicht lesen, was da steht, fast alles schwarz, aber stimmt, erst teilen, dann multiplizieren (mit dem gleichen Wert) ist sinnlos. Schon das erst Fettnäpfchen.
 
CyborgBeta schrieb:
fast alles schwarz
Am besten an @Steffen melden, außer du verwendest ein Custom-Theme. Ich hab nur die ganz normale Inline-Code-Formatierung genommen und aus dem Code-Block kopiert. Bei mir am Handy schaut alles richtig aus.

Edit: Ja, im Darkmode war es unlesbar. Beim Kopieren auf dem Handy (aus dem Code-Block) scheint wohl eine schwarze Schriftfarbe übernommen worden zu sein. Hab (im Darkmode) die Formatierung gelöscht und neu als Inline-Code formatiert. Jetzt ist es normal.
 

Anhänge

  • Formatierung_nachKopie_Schriftfarbe.png
    Formatierung_nachKopie_Schriftfarbe.png
    6,8 KB · Aufrufe: 31
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: CyborgBeta
Das ist erstmal keine Programmierfrage, sondern eine Frage zu Aktien. Am besten mal 'drüben' im Aktienthread nachfragen. Da kann das vermutlich besser geklärt werden.
 
  • Gefällt mir
Reaktionen: Marco01_809 und Maviapril2
BeBur schrieb:
Das ist erstmal keine Programmierfrage, sondern eine Frage zu Aktien.
Sehe ich nicht so, ich hatte eine konkrete Frage gestellt.
 
CyborgBeta schrieb:
Der Break-Even-Point wäre eigentlich simpel totalBase / totalQuote, wenn ich mich nicht täusche.
CyborgBeta schrieb:
Bitte beachten, dass zwischendurch auch verkauft wurde, also sowohl Käufe als auch Verkäufe in der Transaktionsliste sind.
CyborgBeta schrieb:
... Es sollte eigentlich möglich sein, auch unterhalb des BEPs Partialverkäufe mit Gewinn zu tätigen, der BEP würde sich dadurch halt nach oben verschieben, denke ich.
CyborgBeta schrieb:
Mich hindert, dass ich das Ergebnis der Berechnung dann quasi nicht "verifizieren" kann.
CyborgBeta schrieb:
Ich glaube langsam, der BEP spielt dabei gar keine Rolle, sondern man muss nur den mittleren Einkaufspreis berechnen, und wenn ein partieller Verkauf "weit genug" über diesen liegt, dann ist dieser profitabel.
Das sind alles Aktien-Themen, keine Programmierthemen.

Du hast gefragt:
CyborgBeta schrieb:
Ich weiß nicht, ob die Formel in der return-Zeile richtig ist ...
Aber da geht es nicht um float-Genauigkeit oder um Operator-Präzedenz, sondern um irgendwas das wohl BEP heißt und break-even-points und Co. Du hast Probleme, die Domäne richtig zu modellieren. Da solltest du am besten mit Domänenexperten sprechen.
 
  • Gefällt mir
Reaktionen: Marco01_809, Maviapril2, Myron und eine weitere Person
Die Domäne ist richtig modelliert. 🤷‍♂️ Warum soll ich dann mit Leuten sprechen, die davon nichts verstehen?
 
Die Domäne ist OFFENBAR nicht richtig modelliert, da du genau zu deren Berechnung fragen hattest.
Der Hinweis auf Experten ist da schon korrekt.

Dann sind wir wieder bei der Programmierung, wo z.B. ich vorgeschlagen habe, die jeweiligen Fälle zu durchdenken (Domäne) und sie abzutesten (Programmierung). Mit Unittest kannst du dann auch langfristig sicherstellen, dass alle bekannten Fälle auch bei der Änderung der Funktion weiterhin korrekt funktionieren.

Deswegen besteht der Ablauf in der Regel aus
1. Domäne verstehen
2. Bekannten Fälle durchdenken
3. Implementierung, unter Berücksichtigung der bekannten Fälle
 
  • Gefällt mir
Reaktionen: Marco01_809, simpsonsfan, Maviapril2 und 3 andere
Na ja, es wird nicht richtiger, auch wenn man es doppelt schreibt. Die Domäne (Transaktionsliste) ist bereits gegeben. Es fehlt noch ein wenig Mathematik. Aber ich sehe schon, man muss alles selber machen und dieses Unterforum hat eigentlich keinen Nutzen.
Ergänzung ()

Ich denke schon, dass wir hier von einer gleichen Definition sprechen:

https://en.wikipedia.org/wiki/Domain_(software_engineering)

Domain in the realm of software engineering commonly refers to the subject area on which the application is intended to apply. In other words, during application development, the domain is the "sphere of knowledge and activity around which the application logic revolves." —Andrew Powell-Morse

Der Begriff Domäne bezieht sich im Bereich der Softwareentwicklung im Allgemeinen auf das Fachgebiet, auf das die Anwendung angewendet werden soll. Mit anderen Worten: Bei der Anwendungsentwicklung ist die Domäne der „Wissens- und Tätigkeitsbereich, um den sich die Anwendungslogik dreht“.

Domain: A sphere of knowledge, influence, or activity. The subject area to which the user applies a program is the domain of the software. —Eric Evans

Domäne: Ein Bereich des Wissens, des Einflusses oder der Aktivität. Der Themenbereich, auf den der Benutzer ein Programm anwendet, ist die Domäne der Software.

Also noch einmal, es geht um die Anwendungslogik. Weshalb ist meine Frage also angeblich deplatziert, und sollte besser in einem Unterforum gestellt werden, das nichts mit Programmierung, Softwareentwicklung oder Mathematik zu hat?

Das möchte ich jetzt wissen, sonst melde ich die Beiträge.
 
Zuletzt bearbeitet:
CyborgBeta schrieb:
besser in einem Unterforum gestellt werden, das nichts mit Programmierung, Softwareentwicklung oder Mathematik
Also mir ist z.B. bis jetzt nicht klar, ob hier FIFO Anwendung finden soll oder nicht. Das ist eine rein fachliche Frage und hat nichts mit Programmierung/Softwareentwicklung zu tun. Ohne ein genaues fachliches Verständnis oder den genauen fachlichen Anforderungen tappen wir hier im Dunkeln. FIFO ist nur eine der offenen Fragen.
Mathematik werden sie im Aktienthread auch können ;)

Aus meiner Sicht ist hier FIFO ein Muss. Das bedeutet aber auch, dass du den Gewinn gar nicht nur anhand des Preises ermitteln kannst (calculatePartialProfitAtPrice(double price)), sondern auch die Anzahl brauchst.
Wenn ich ChatGPT konsultiere kommt als Basis sowas heraus (ungetestet):

Java:
import java.util.LinkedList;
import java.util.Queue;

class Transaction {
    int quantity; // Anzahl der Aktien
    double price; // Preis pro Aktie

    public Transaction(int quantity, double price) {
        this.quantity = quantity;
        this.price = price;
    }
}

public class FifoProfitCalculator {
    private Queue < Transaction > purchases = new LinkedList < > ();

    public void buy(int quantity, double price) {
        purchases.add(new Transaction(quantity, price));
        System.out.println("Gekauft: " + quantity + " Aktien zu " + price + " EUR.");
    }

    public double sell(int quantity, double price) {
        int remainingToSell = quantity;
        double totalProfit = 0.0;

        System.out.println("Verkauft: " + quantity + " Aktien zu " + price + " EUR.");

        while (remainingToSell > 0 && !purchases.isEmpty()) {
            Transaction firstPurchase = purchases.peek();

            if (firstPurchase.quantity <= remainingToSell) {
                // Alle Aktien aus diesem Kauf verkaufen
                totalProfit += firstPurchase.quantity * (price - firstPurchase.price);
                remainingToSell -= firstPurchase.quantity;
                purchases.poll(); // Entfernen, da vollständig verkauft
            } else {
                // Teilweise Aktien aus diesem Kauf verkaufen
                totalProfit += remainingToSell * (price - firstPurchase.price);
                firstPurchase.quantity -= remainingToSell;
                remainingToSell = 0; // Alle benötigten Aktien verkauft
            }
        }

        if (remainingToSell > 0) {
            System.out.println("Warnung: Nicht genügend Aktien zum Verkauf vorhanden.");
        }

        System.out.println("Berechneter Gewinn: " + totalProfit + " EUR.");
        return totalProfit;
    }

    public static void main(String[] args) {
        FifoProfitCalculator calculator = new FifoProfitCalculator();

        calculator.buy(100, 50.0); // 100 Aktien zu 50 EUR
        calculator.buy(50, 60.0); // 50 Aktien zu 60 EUR
        double profit = calculator.sell(120, 70.0); // 120 Aktien zu 70 EUR

        System.out.println("Gesamtgewinn: " + profit + " EUR.");
    }
}
 
  • Gefällt mir
Reaktionen: CyborgBeta
@Wo bin ich hier Also, ein paar Anmerkungen:

  • Das Ding rechnet mit ganzen Zahlen.
  • Ich müsste doch quasi nur das "Aktienpaket" aus der Liste suchen, das zum niedrigsten Preis gekauft wurde und das später noch nicht wieder verkauft wurde, oder nicht?
 
CyborgBeta schrieb:
Ich müsste doch quasi nur das "Aktienpaket" aus der Liste suchen, das zum niedrigsten Preis gekauft wurde und das später noch nicht wieder verkauft wurde, oder nicht?
Nein, der Preis spielt bei FIFO keine Rolle. Die zeitliche Abfolge ist das Relevante.
Du musst das "Aktienpaket" verkaufen, welches zeitlich als nächstes dran ist.
 
Hab es inzwischen herausgefunden:

Java:
  private double[] calculatePartialProfitAtPrice(double price) {
    final double quoteToFind = getQuoteQuantity(price); // 10 usd in quote at price
    double sellQuote = 0;
    double buyQuote = 0;
    for (Transaction transaction : transactions) {
      if (transaction.isBuy()) {
        buyQuote += transaction.quantity();
      } else {
        sellQuote += transaction.quantity();
      }
    }
    if (quoteToFind + sellQuote > buyQuote) {
      return null;
    }
    double openQuote = quoteToFind + sellQuote;
    ArrayList<Transaction> tempList = new ArrayList<>(transactions);
    tempList.sort(Comparator.comparing(Transaction::price));
    for (Transaction transaction : tempList) {
      if (transaction.isBuy()) {
        openQuote -= transaction.quantity();
        if (openQuote <= 0) {
          double profit = quoteToFind * price - quoteToFind * transaction.price();
          double percentage = (price / transaction.price() - 1.0) * 100.0;
          return new double[] {profit, percentage};
        }
      }
    }
    return null;
  }

  • Zuerst alle Verkäufe bestimmen und summieren
  • Dann so lange die nach Preis sortierte Temp-Liste durchgehen, bis der gesuchte Betrag erreicht wurde
  • Profit und Prozent zurückgeben

An einer Zeichnung könnte man jetzt vielleicht besser erklären, wie das gemeint ist ... prinzipiell löschen sich Käufe und Verkäufe gegenseitig aus, und der kleinste verbleibende Kauf ist dann gesucht.

Richtig?
 
CyborgBeta schrieb:
Nein, (vermutlich) fachlich falsch. Weshalb sollte man irgendwas nach Preis sortieren?
Die Hinweise darauf, dass das allerdings keine softwareseitige, sondern eine thematische Frage ist, ignorierst du dabei auch.

Solltest du (wider Erwarten) berechnen wollen, was für ein Gewinn nach einer selbstdefinierten, steuerlich irrelevanten, Gweinndefinition rauskommt, die sich eben grade bspw. durch Verkauf immer der günstigsten Anteile ergibt, dann mag dein Verfahren korrekt (aber weitgehend sinnlos) sein.
 
  • Gefällt mir
Reaktionen: Micha-, BeBur und Wo bin ich hier
Zurück
Oben