Gleitenden Durchschnittspreis mit pandas (oder numpy)

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
ytroksit
User
Beiträge: 6
Registriert: Dienstag 6. Juni 2023, 07:42

Moin,

ich möchte den gleitenden Durchschnittspreis (GLD) eines DataFrame berechnen, tue mich aber etwas schwer damit. Definiert ist der GLD:

Code: Alles auswählen

(x_alt * y_alt + x_neu * y_neu) / (x_alt + x_neu)
Als Beispiel sei ein DataFrame mit A (Gewichtung) und B (Wert) gegeben:

Code: Alles auswählen

df = pd.DataFrame({'A': [3, 6, 4, 9, -2, 12], 'B': [12, 29, 27, 15, 34, 29]})
Versucht habe ich schon:

Code: Alles auswählen

>>> df['GM'] = df['B'].rolling(2).mean()
>>> print(df)
    A   B    GM
0   3  12   NaN
1   6  29  20.5
2   4  27  28.0
3   9  15  21.0
4  -2  34  24.5
5  12  29  31.5
Allerdings wird hier nur der einfache Mittelwert gebildet und auch nur von den Einzelwerten.

Code: Alles auswählen

>>> df['GWM'] = sum(df['B'] * df['A']) / sum(df['A'])
>>> print(df)
    A   B    GM       GWM
0   3  12   NaN  22.90625
1   6  29  20.5  22.90625
2   4  27  28.0  22.90625
3   9  15  21.0  22.90625
4  -2  34  24.5  22.90625
5  12  29  31.5  22.90625
So bekomme ich den gewichteten Mittelwert, aber nur für den ganzen DataFrame.

Ich habe schon über pandas.DataFrame.apply() und .agg() sowie .expanding() gelesen, aber ich stehe auf dem Schlauch.

Das Ergebnis soll so aussehen:

Code: Alles auswählen

    A   B    GLD
0   3  12  12.00
1   6  29  23.33
2   4  27  24.47
3   9  15  20.59
4  -2  34  20.59
5  12  29  23.74
Da Zeile 4 eine Entnahme (A: -2) darstellt, sollte dessen GLD keinen Eingang in die Berechnung finden, sondern der vorherige GLD genommen werden (wobei die Änderung der Menge durch Zeile 4 jedoch berücksichtigt wird).

Hat jemand eine Idee, wo ich weiterschauen sollte?
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

Du weißt doch schon, dass der Mittelwert über

Code: Alles auswählen

sum(df['B'] * df['A']) / sum(df['A'])
gebildet wird.
Jetzt willst Du aber nicht die Summe über die gesamte Spalte bilden, sondern nur über das jeweilige rolling Window. Und wie man mit rolling arbeitet, weißt Du auch schon; Du mußt nur beides verbinden.
ytroksit
User
Beiträge: 6
Registriert: Dienstag 6. Juni 2023, 07:42

Im Eingangspost ist mir ein kleiner Fehler unterlaufen; in der Formel des GLD ist der Mittelwert natürlich kumulativ zu bilden:

Code: Alles auswählen

x_GLD_neu = (x_GLD_alt * y_alt + x_neu * y_neu) / (x_alt + x_neu)
Das heißt mit jeder neuen Zeile werden x_neu und y_neu mit dem bisherigen Mittelwerten gemittelt.
Sirius3 hat geschrieben: Dienstag 6. Juni 2023, 17:32 Jetzt willst Du aber nicht die Summe über die gesamte Spalte bilden, sondern nur über das jeweilige rolling Window. Und wie man mit rolling arbeitet, weißt Du auch schon; Du mußt nur beides verbinden.


Mir ist allerdings nicht klar, wie ich im rollenden Fenster eine Formel mit mehr als einer Variable (der aktuellen Zeile) verwende. Versucht habe ich:

Code: Alles auswählen

df['GLD'] = df.rolling(2).apply(sum(df['B'] * df['A']) / sum(df['A']))

Code: Alles auswählen

df['GLD'] = df.rolling(2).apply(lambda x: sum(x.iloc[:,2] * x.iloc[:,1]) / sum(x.iloc[:,1]))
Beides klappt nicht.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@ytroksit: Du summierst jetzt ja auch wieder über die gesamten Daten und nicht nur über das Fenster. Du brauchst ein rollierendes Fenster über A*B und eines nur über A und beide werden getrennt summiert, um dann den Quotienten zu bilden.
ytroksit
User
Beiträge: 6
Registriert: Dienstag 6. Juni 2023, 07:42

Ich denke, es handelt sich eher um ein mathematisches Problem. Meine Frage war vermutlich etwas unklar, weshalb die Tipps auch in die falsche Richtung gingen.

Für die Lösung werden weder .rolling() noch .apply() benötigt, sondern es muss lediglich der kumulative Wert pro Bestand ermittelt werden (ich habe A und B umbenannt, damit es verständlicher ist):

Code: Alles auswählen

>>> import pandas as pd

>>> df = pd.DataFrame({'Stück': [3, 6, 4, 9, -2, 12], 'Kurs': [12, 29, 27, 15, 34, 29]})

>>> df['Bestand'] = df['Stück'].cumsum()
>>> df['Kurswert'] = df['Stück'] * df['Kurs']
>>> df['Bestandswert'] = df['Kurswert'].cumsum()
>>> df['GLD'] = df['Bestandswert'] / df['Bestand']

>>> print(df)
   Stück  Kurs  Bestand  Kurswert  Bestandswert        GLD
0      3    12        3        36            36  12.000000
1      6    29        9       174           210  23.333333
2      4    27       13       108           318  24.461538
3      9    15       22       135           453  20.590909
4     -2    34       20       -68           385  19.250000
5     12    29       32       348           733  22.906250
Antworten