Moving Average im Kursdiagram falsch?


#1

Hallo!

ich habe das Moving Average im Kursdiagram über 200 Tage nun mit mehreren anderen SMA-200 von anderen Chartseiten verglichen und irgendwie habe ich den Eindruck das etwas falsch läuft. Es scheint als würde das Moving Average 50 auch nicht wirklich stimmen.

Kann es sein, dass hier etwas falsch läuft? Wird eventuell bei der Berechnung des SMA auch börsenfreie Tage mitgezählt?

Grüsse
Chris


#2

Klar, kann es sein, dass etwas falsch läuft. Du machst es jedem, der sich das näher anschauen möchte, viel einfacher, wenn Du (anhand eines Beispiels) beschreibst, was Du siehst und was Du denkst, was stattdessen richtig wäre.

Siehe auch: Was sollte ich beim Melden eines Fehlers beachten?


#3

Ok, here we go:

FB2A.DE stand heute (26.03.2018):

Portfolio Performance SMA200: 148,0217 EUR
Tradingview SMA200: 145,4787 EUR

Manuelle Berechnung:
(200 letzte Closes von Yahoo https://finance.yahoo.com/quote/FB2A.DE/history?p=FB2A.DE)

|Date|Close*|
|Mar 26, 2018|122.8|
|Mar 23, 2018|132.2|
|Mar 22, 2018|134.53|
|Mar 21, 2018|140.25|
|Mar 20, 2018|134.53|
|Mar 19, 2018|139.58|
|Mar 16, 2018|149.85|
|Mar 15, 2018|148.11|
|Mar 14, 2018|147.82|
|Mar 13, 2018|146.4|
|Mar 12, 2018|150.4|
|Mar 09, 2018|150.08|
|Mar 08, 2018|148.07|
|Mar 07, 2018|146.04|
|Mar 06, 2018|145.06|
|Mar 05, 2018|145.19|
|Mar 02, 2018|141.46|
|Mar 01, 2018|145.46|
|Feb 28, 2018|148.77|
|Feb 27, 2018|149.63|
|Feb 26, 2018|149.8|
|Feb 23, 2018|147.87|
|Feb 22, 2018|145.55|
|Feb 21, 2018|146.86|
|Feb 20, 2018|143.56|
|Feb 19, 2018|143.67|
|Feb 16, 2018|143.49|
|Feb 15, 2018|143.1|
|Feb 14, 2018|142.8|
|Feb 13, 2018|141|
|Feb 12, 2018|142.4|
|Feb 09, 2018|142|
|Feb 08, 2018|144.25|
|Feb 07, 2018|149.8|
|Feb 06, 2018|145.2|
|Feb 05, 2018|152.2|
|Feb 02, 2018|153.6|
|Feb 01, 2018|155.81|
|Jan 31, 2018|152|
|Jan 30, 2018|151.01|
|Jan 29, 2018|151.8|
|Jan 26, 2018|150.8|
|Jan 25, 2018|149.8|
|Jan 24, 2018|151.2|
|Jan 23, 2018|153.6|
|Jan 22, 2018|150.2|
|Jan 19, 2018|147.8|
|Jan 18, 2018|145.6|
|Jan 17, 2018|144.4|
|Jan 16, 2018|147.6|
|Jan 15, 2018|148.2|
|Jan 12, 2018|148.6|
|Jan 11, 2018|155.6|
|Jan 10, 2018|156.8|
|Jan 09, 2018|157.4|
|Jan 08, 2018|157.2|
|Jan 05, 2018|154.2|
|Jan 04, 2018|152.8|
|Jan 03, 2018|152.8|
|Jan 02, 2018|149.4|
|Dec 29, 2017|148.8|
|Dec 28, 2017|149.55|
|Dec 27, 2017|149.45|
|Dec 22, 2017|149.35|
|Dec 21, 2017|150.05|
|Dec 20, 2017|149.5|
|Dec 19, 2017|151.6|
|Dec 18, 2017|152.4|
|Dec 15, 2017|153|
|Dec 14, 2017|152.8|
|Dec 13, 2017|151.8|
|Dec 12, 2017|152.15|
|Dec 11, 2017|151.95|
|Dec 08, 2017|152.7|
|Dec 07, 2017|151.4|
|Dec 06, 2017|148.85|
|Dec 05, 2017|147.75|
|Dec 04, 2017|144.9|
|Dec 01, 2017|146|
|Nov 30, 2017|148.3|
|Nov 29, 2017|147.7|
|Nov 28, 2017|154.5|
|Nov 27, 2017|152.8|
|Nov 24, 2017|152.9|
|Nov 23, 2017|152.75|
|Nov 22, 2017|153.3|
|Nov 21, 2017|154.45|
|Nov 20, 2017|152.25|
|Nov 17, 2017|152.55|
|Nov 16, 2017|152.5|
|Nov 15, 2017|151.25|
|Nov 14, 2017|151.65|
|Nov 13, 2017|153.15|
|Nov 10, 2017|152.8|
|Nov 09, 2017|153.25|
|Nov 08, 2017|155.35|
|Nov 07, 2017|155.15|
|Nov 06, 2017|154.6|
|Nov 03, 2017|153.5|
|Nov 02, 2017|153.15|
|Nov 01, 2017|155.95|
|Oct 31, 2017|154.45|
|Oct 30, 2017|154.45|
|Oct 27, 2017|152.7|
|Oct 26, 2017|146.15|
|Oct 25, 2017|144.05|
|Oct 24, 2017|147.15|
|Oct 23, 2017|147.4|
|Oct 20, 2017|148.6|
|Oct 19, 2017|147.3|
|Oct 18, 2017|149.5|
|Oct 17, 2017|149.25|
|Oct 16, 2017|147.55|
|Oct 13, 2017|147|
|Oct 12, 2017|146.4|
|Oct 11, 2017|145.15|
|Oct 10, 2017|145.4|
|Oct 09, 2017|148.3|
|Oct 06, 2017|145.55|
|Oct 05, 2017|144.4|
|Oct 04, 2017|145|
|Oct 03, 2017|144.8|
|Oct 02, 2017|144.8|
|Sep 29, 2017|144.45|
|Sep 28, 2017|143|
|Sep 27, 2017|141.6|
|Sep 26, 2017|138.55|
|Sep 25, 2017|138.5|
|Sep 22, 2017|142.55|
|Sep 21, 2017|144|
|Sep 20, 2017|143.15|
|Sep 19, 2017|143.4|
|Sep 18, 2017|143.4|
|Sep 15, 2017|143.45|
|Sep 14, 2017|144.4|
|Sep 13, 2017|145.15|
|Sep 12, 2017|144.6|
|Sep 11, 2017|144.45|
|Sep 08, 2017|143.4|
|Sep 07, 2017|142.9|
|Sep 06, 2017|142.5|
|Sep 05, 2017|143.5|
|Sep 04, 2017|143.9|
|Sep 01, 2017|145.3|
|Aug 31, 2017|143.9|
|Aug 30, 2017|141.4|
|Aug 29, 2017|139.25|
|Aug 28, 2017|140|
|Aug 25, 2017|140.15|
|Aug 24, 2017|141.3|
|Aug 23, 2017|142.9|
|Aug 22, 2017|143.7|
|Aug 21, 2017|141.55|
|Aug 18, 2017|143.45|
|Aug 17, 2017|143.8|
|Aug 16, 2017|145.35|
|Aug 15, 2017|145.7|
|Aug 14, 2017|144.75|
|Aug 11, 2017|142.05|
|Aug 10, 2017|143.2|
|Aug 09, 2017|145.5|
|Aug 08, 2017|146.35|
|Aug 07, 2017|145.75|
|Aug 04, 2017|144.55|
|Aug 03, 2017|142.3|
|Aug 02, 2017|141.55|
|Aug 01, 2017|143.85|
|Jul 31, 2017|144.35|
|Jul 28, 2017|146.65|
|Jul 27, 2017|149.55|
|Jul 26, 2017|141.25|
|Jul 25, 2017|141.2|
|Jul 24, 2017|141.7|
|Jul 21, 2017|141.05|
|Jul 20, 2017|140.55|
|Jul 19, 2017|143.3|
|Jul 18, 2017|139.65|
|Jul 17, 2017|139.05|
|Jul 14, 2017|139.7|
|Jul 13, 2017|139.85|
|Jul 12, 2017|138.3|
|Jul 11, 2017|134.45|
|Jul 10, 2017|134.55|
|Jul 07, 2017|132.75|
|Jul 06, 2017|131.05|
|Jul 05, 2017|132.15|
|Jul 04, 2017|131.35|
|Jul 03, 2017|130.95|
|Jun 30, 2017|132.55|
|Jun 29, 2017|131.45|
|Jun 28, 2017|133.1|
|Jun 27, 2017|135.15|
|Jun 26, 2017|137.05|
|Jun 23, 2017|137.7|
|Jun 22, 2017|138|
|Jun 21, 2017|137.45|
|Jun 20, 2017|137.45|
|Jun 19, 2017|136.65|
|Jun 16, 2017|134.2|
|Jun 15, 2017|132.8|
===================
SMA200:	        145.01875
===================

Auch bei anderen Quellen liegt der SMA200 immer irgendwo bei 145 EUR. Wieso aber 148 EUR bei PP? Selbiges Phänomen ist auch bei anderen Aktien zu beobachten.


#5

Welche Quelle ist denn in PP für die historischen Kurse gewählt? Nicht nur aktuell, sondern verlässlich für die letzten 250 Tage?
Habe grade mal per Auge yahoo und onvista verglichen: An Feiertagen gibt es Unterschiede, zB 03.10.17 oder 31.10.17 steht bei yahoo ein Kurs mit 0 Volumen während bei onvista für diese Tage kein Kurs auftaucht.
Wie bestimmt PP ob ein Tag ein Handelstag ist? Anhand eines vorhandenen Kurses?

Sowas könnte Einfluss haben.


#6

Das Ding ist, dass ich für die manuelle Berechnung Yahoo verwendet habe und für die Kursermittlung im PP ebenfalls Yahoo verwendet wird.

Also ich glaube, dass hier nur ein PP Entwickler richtig Einblick hat und für Aufklärung sorgen kann. Andernfalls muss man alle möglichen Dinge durchkalkulieren bis man mittels brute force dann auf die selben Zahlen wie PP kommt und dann weiss was dazu führt. Schwierig.


#7

Das von Chris_Tian geschriebene ist nachvollziehbar.
Bei der Berechnung des SimpleMovingAverage (SMA) in PortfolioPerformance (PP) stimmt was nicht.

Bei der Berechnung des SMA50 sollten die letzten 50 Schlusskurse addiert und anschließend durch 50 geteilt werden (analoges vorgehen beim SMA200).

Tut man dies mit den Kursdaten von PP nach einem .csv-Export und anschließendem Import dieser Kursdaten in ein Tabellenkalkulationsprogramm, kommt was anderes raus als PP intern berechnet. Wobei der Wert aus dem Tabellenkalkulationsprogramm mit den Werten aus z. B. dem Chartprogramm Tradingview gut übereinstimmt, der Wert aus PP aber nicht.

Habe versucht den Fehler zu finden.
Da ich kein Java kann, habe ich Probleme den Quelltext zu verstehen.

Vielleicht kann sich ein Java-Programmierer mal den Quelltext ansehen.

Den Programmcode zur Berechnung des SimpleMovingAverage findet man im Quelltext hier:

portfolio-master\name.abuchen.portfolio.ui\src\name\abuchen\portfolio\ui\views\SimpleMovingAverage.java

Dieser ist wie folgt:

    package name.abuchen.portfolio.ui.views;

    import java.time.LocalDate;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.stream.Collectors;
    import org.apache.commons.lang3.ArrayUtils;
    import name.abuchen.portfolio.model.Security;
    import name.abuchen.portfolio.model.SecurityPrice;
    import name.abuchen.portfolio.money.Values;
    import name.abuchen.portfolio.ui.util.chart.TimelineChart;

    public class SimpleMovingAverage
    {

        public static final int MIN_AVERAGE_PRICES_PER_WEEK = 2;
        private int rangeSMA;
        private Security security;
        private LocalDate startDate;
        private ChartLineSeriesAxes SMA;
        private int calculatedMinimumDays;
        private List<SecurityPrice> prices;
        private List<LocalDate> datesSMA;
        private List<Double> valuesSMA;

        public SimpleMovingAverage(int rangeSMA, Security security, LocalDate startDate)
        {
            this.rangeSMA = rangeSMA;
            this.security = security;
            this.startDate = startDate;
            this.SMA = new ChartLineSeriesAxes();
            this.datesSMA = new ArrayList<>();
            this.valuesSMA = new ArrayList<>();
            this.calculatedMinimumDays = getMinimumDaysForSMA();
            this.calculateSMA();
        }

        /**
         * Returns the calculated Simple Moving Average
         * 
         * @return The ChartLineSeriesAxes contains the X and Y Axes of the
         *         generated SMA
         */
        public ChartLineSeriesAxes getSMA()
        {
            return this.SMA;
        }

        public SimpleMovingAverage calculateSMA(int rangeSMA, Security security, LocalDate startDate)
        {
            return new SimpleMovingAverage(rangeSMA, security, startDate);
        }

        /**
         * Calculates the Simple Moving Average for the given range of days from the
         * given startDate on The method returns an object containing the X and Y
         * Axes of the generated SMA
         */
        private void calculateSMA()
        {
            if (security == null)
                return;

            this.prices = security.getPricesIncludingLatest();
            int index;

            SecurityPrice startPrice = null;

            if (prices == null || prices.size() < calculatedMinimumDays)
                return;

            if (startDate == null)
            {
                startPrice = this.getStartPrice();
                // in case no valid start date could be determined, return null
                if (startPrice == null)
                    return;
                index = prices.indexOf(startPrice);
                if (index >= prices.size())
                    return;
            }
            else
            {
                startPrice = this.getStartPriceFromStartDate();
                // in case no valid start date could be determined, return null
                if (startPrice == null)
                    return;
                index = prices.indexOf(startPrice);
                if (index >= prices.size())
                    return;
            }

            for (; index < prices.size(); index++)
            {
                LocalDate nextDate = prices.get(index).getDate();
                LocalDate isBefore = nextDate.plusDays(1);
                LocalDate isAfter = isBefore.minusDays(rangeSMA + 1L);
                List<SecurityPrice> filteredPrices = this.getFilteredList(isBefore, isAfter);

                if (filteredPrices.size() < calculatedMinimumDays)
                    continue; // skip this date and try to calculate SMA for next
                              // entry

                double sum = filteredPrices.stream().mapToLong(SecurityPrice::getValue).sum();

                valuesSMA.add(sum / Values.Quote.divider() / filteredPrices.size());
                datesSMA.add(prices.get(index).getDate());
            }
            LocalDate[] tmpDates = datesSMA.toArray(new LocalDate[0]);
            Double[] tmpPrices = valuesSMA.toArray(new Double[0]);

            this.SMA.setDates(TimelineChart.toJavaUtilDate(tmpDates));
            this.SMA.setValues(ArrayUtils.toPrimitive(tmpPrices));
        }

        public int getMinimumDaysForSMA()
        {
            int weeks = rangeSMA / 7;
            int minDays = weeks * MIN_AVERAGE_PRICES_PER_WEEK;
            return minDays > 0 ? minDays : 1;
        }

        public SecurityPrice getStartPriceFromStartDate()
        {
            // get Date of first possible SMA calculation beginning from startDate
            int index = Math.abs(
                            Collections.binarySearch(prices, new SecurityPrice(startDate, 0), new SecurityPrice.ByDate()));

            if (index >= prices.size())
                return null;
            return determineStartPrice(startDate);
        }

        public SecurityPrice getStartPrice()
        {
            // get Date of first possible SMA calculation
            LocalDate smaPeriodEnd = prices.get(0).getDate().plusDays(rangeSMA - 1L);
            int index = Math.abs(Collections.binarySearch(prices, new SecurityPrice(smaPeriodEnd, 0),
                            new SecurityPrice.ByDate()));
            if (index >= prices.size())
                return null;

            return determineStartPrice(smaPeriodEnd);
        }

        private SecurityPrice determineStartPrice(LocalDate smaPeriodEnd)
        {
            // check if an SMA can be calculated for this Date
            List<SecurityPrice> filteredPrices = null;
            LocalDate isBefore = smaPeriodEnd.plusDays(1);
            LocalDate isAfter = smaPeriodEnd.minusDays(rangeSMA);
            LocalDate lastDate = prices.get(prices.size() - 1).getDate();
            filteredPrices = this.getFilteredList(isBefore, isAfter);

            int i = 1;
            while (!this.checkListIsValidForSMA(filteredPrices))
            {
                if (isBefore.plusDays(i).isAfter(lastDate) || isAfter.plusDays(i).isAfter(lastDate))
                    return null;

                filteredPrices = this.getFilteredList(isBefore.plusDays(i), isAfter.plusDays(i));
                i++;
            }

            return filteredPrices.get(filteredPrices.size() - 1);
        }

        private boolean checkListIsValidForSMA(List<SecurityPrice> filteredPrices)
        {
            return filteredPrices.size() < calculatedMinimumDays ? false : true;
        }

        private List<SecurityPrice> getFilteredList(LocalDate isBefore, LocalDate isAfter)
        {
            return prices.stream().filter(p -> p.getDate().isAfter(isAfter) && p.getDate().isBefore(isBefore))
                            .collect(Collectors.toList());
        }
    }

Die Funktion zum Zeichnen des SMA im Chart findet man hier:

portfolio-master\name.abuchen.portfolio.ui\src\name\abuchen\portfolio\ui\views\SecuritiesChart.java

Dieser ist wie folgt:

private void addSMAMarkerLines(int smaDays)
    {
        ChartLineSeriesAxes smaLines = new SimpleMovingAverage(smaDays, this.security, chartPeriod).getSMA();
        if (smaLines == null || smaLines.getValues() == null || smaLines.getDates() == null)
            return;

        String lineID = smaDays == 200 ? Messages.LabelChartDetailSMA200 : Messages.LabelChartDetailSMA50;

        ILineSeries lineSeriesSMA = (ILineSeries) chart.getSeriesSet().createSeries(SeriesType.LINE, lineID);
        lineSeriesSMA.setXDateSeries(smaLines.getDates());
        lineSeriesSMA.setLineWidth(2);
        lineSeriesSMA.enableArea(false);
        lineSeriesSMA.setSymbolType(PlotSymbolType.NONE);
        lineSeriesSMA.setYSeries(smaLines.getValues());
        lineSeriesSMA.setAntialias(SWT.ON);
        lineSeriesSMA.setLineColor(smaDays == 200 ? colorSMA200 : colorSMA50);
        lineSeriesSMA.setYAxisId(0);
        lineSeriesSMA.setVisibleInLegend(true);
    }

#8

Danke für die detaillierte Rückmeldung!

In der Tat rechnet PP den SMA200 anders. Und zwar werden die Kurse der letzten 200 Tage verwendet. In dem Beispiel sind das die Kurse vom 7.9.2017 bis zum 26.3.2018. Damit kommt man auf den Wert von 148,02.

Wenn man die letzten 200 Kurse nimmt, kommt man (für den Bereich vom 15.6.2017 bis 26.3.2018) auf den Wert von 145,63 (trotzdem leicht anders was Tradingview berechnet).

Warum haben wir die letzten 200 Tage genommen und nicht die letzten 200 Kurse? Einerseits sicherlich Unwissen um die genaue Definition. Andererseits kann PP nie davon ausgehen, dass die Kurse in irgendeiner Weise vollständig sind. Wenn es Lücken von mehrere Wochen gibt, dann will man nicht mehrere Jahre im SMA200 sehen.

Was ist die richtige Definition?

Tradesignal schreibt:

Berechnung und Parameter: Die Berechnung ist einfach durchzuführen. Es wird schlicht die Summe der Kurse oder Indikatorwerte des Berechnungszeitraumes durch die Anzahl an Handelstagen im Berechnungszeitraum geteilt.

Auch bei Investopedia lese ich das als Zeitraum, nicht Anzahl der Kurse:

A simple moving average is customizable in that it can be calculated for a different number of time periods, simply by adding the closing price of the security for a number of time periods and then dividing this total by the number of time periods, which gives the average price of the security over the time period.

Andrerseits sind natürlich andere Werte anderer Seiten auch ein Indikator. Die Berechnung kann man an sich relativ einfach umstellen. Was meint Ihr?


#9

Hallo Andreas,
bezüglich der Investopia-Definition bin ich anderer Meinung. Ich lese es ganz klar als Anzahl der Kurse und nicht als Zeitraum.
Es heißt da: “time periods” also Zeiträume und nicht Zeitraum.
Ich denke “time periods” kann man auch mit “Zeiteinheit” übersetzen.
Damit sagt die Definition auf Investopia:
Berechne den SimpleMovingAverage ganz einfach in dem du die Schlusskurse eines Wertpapiers für eine bestimmte Anzahl an Zeiträumen addierst und dann das Ergebnis durch die Anzahl an Zeiträumen dividierst.

Auch würde ich davon ausgehen, dass die Kurse in PP keine Lücken haben. Denn es macht generell keinen Sinn auf der Basis von unvollständigen Kursdaten irgendwas zu berechnen.

Um dem Namen SimpleMovingAverage (keep it simple) gerecht zu werden und da der 50er und der 200er SMA doch eine gewisse Beachtung am Markt erfahren, würde ich es begrüßen, wenn die Berechnungsmethode umgestellt werden würde.

Vieleicht kann man die jetzige Berechnungsmethode ja beibehalten und umbenennen (wobei zu bedenken ist, dass diese Berechnungsmethode am Markt keine Beachtung findet).

Grüße Guntur


#10

Das können wir sicherlich umstellen. @Ragas - was meinst Du?

Entweder geht man davon aus. Oder man unterbricht die Berechnung wenn man - sagen wir mal - für 14 Tage keine Kurse hat.

Nee, wenn dann nur die richtige Methode.


#11

In der dt. Wikipedia wird ebenfalls von Datenpunkten gesprochen vgl. https://de.wikipedia.org/wiki/Gleitender_Mittelwert#Einfacher_gleitender_Mittelwert


#12

Das mit den Datenpunkten klingt logisch, vielleicht kann ich am WE ein Update als Pull einstellen.


#13

@AndreasB

also im ersten Durchlauf schaut es gut bei mir aus, allerdings fliegen mir noch die Testcases um die Ohren. Ich schau mir das heute Nachmittag noch einmal an :roll_eyes:

Aktuelle SMA200 Darstellung in PP

Neue SMA200 Darstellung in PP

Darstellung vom SMA200 bei ARIVA

Gruß
Marco


#14

Deutlicher Unterschied! Danke schonmal!


#15

@Ragas Sieht gut aus!


#16

Mit Version 0.30.0 ist die Korrektur drin.