Vergleich der Berechnungszeit des in Python geschriebenen gleitenden Durchschnitts
Es kommt nicht in Frage, die for-Anweisung in Python zu verwenden, um den gleitenden Durchschnitt zu berechnen, und das Ergebnis ist, dass es besser ist, die Pandas- und Scipy-Funktionen zu verwenden. Im obigen Artikel war es jedoch ein gleitender Durchschnitt des FIR-Filtertyps wie SMA und LWMA, daher habe ich diesmal den gleitenden Durchschnitt des IIR-Filtertyps wie EMA und SMMA untersucht.
EMA EMA ist eine Abkürzung für Exponential Moving Average und wird durch die folgende Formel ausgedrückt.
Wobei $ \ alpha $ ein realer Parameter von 0 bis 1 ist. Diese Formel enthält keinen Parameter, der die Periode darstellt. Da SMA und LWMA jedoch Parameter verwenden, die die Periode darstellen, verwendet EMA den Periodenparameter häufig entsprechend. Unter der Annahme, dass der Zeitraum $ p $ beträgt, beträgt EMA $ \ alpha = 2 / (p + 1) $ und SMMA $ \ alpha = 1 / p $. Persönlich halte ich es nicht für notwendig, zwischen EMA und SMMA zu unterscheiden, da sie dieselbe Formel haben, aber ich werde sie erwähnen, weil sie in MetaTrader separat verwendet werden.
Lassen Sie es uns zuerst mit Pandas implementieren. Die zu verarbeitenden Daten sind die gleichen wie im vorherigen Artikel. Es ist eine vierwertige Zeitreihe von etwa 370.000 Stück. Da Pandas für Zeitreihendaten bestimmt ist, kann EMA auch einfach mit den Funktionen "ewm ()" und "mean ()" geschrieben werden.
import numpy as np
import pandas as pd
dataM1 = pd.read_csv('DAT_ASCII_EURUSD_M1_2015.csv', sep=';',
names=('Time','Open','High','Low','Close', ''),
index_col='Time', parse_dates=True)
def EMA(s, ma_period):
return s.ewm(span=ma_period).mean()
%timeit MA = EMA(dataM1['Close'], 10)
Da auch diesmal die Ausführungszeit verglichen wird, werden die Messergebnisse angezeigt.
10 loops, best of 3: 32.2 ms per loop
Im Fall von SMA waren es ungefähr 16 Millisekunden, also ungefähr doppelt so langsam.
Wenn Sie "lfilter ()" von scipy verwenden, reicht es nicht aus, nur $ \ alpha $ einzugeben. Sie müssen zum IIR-Filterformat wechseln und den Koeffizienten eingeben. Also werde ich es ein wenig konvertieren. (Eine detaillierte Theorie wird weggelassen. Sie ist die Grundlage der digitalen Signalverarbeitung.)
Konvertieren Sie beide Seiten des EMA-Ausdrucks in $ z $.
Wenn Sie $ Y (z) $ auf die linke Seite setzen
Und wenn $ Y (z) / X (z) $ $ H (z) $ ist,
Kann geschrieben werden. Dies ist die Systemfunktion des IIR-Filters. Das Argument von "lfilter ()" zu übergeben, ist der Koeffizient des molekularen Polynoms und der Koeffizient des Nennerpolynoms dieser Systemfunktion.
Im Fall von EMA ist das Molekül der Systemfunktion eine Konstante und der Nenner ein Polynom erster Ordnung, so dass die allgemeine Formel der Systemfunktion wie folgt geschrieben werden kann.
Wenn Sie die Koeffizienten vergleichen, sehen Sie, dass die Koeffizienten von $ b $ und $ a $ wie folgt sind.
Wenn Sie also EMA mit scipys lflter ()
implementieren, können Sie wie folgt schreiben. Setzen Sie die obigen $ b $ und $ a $ in Form einer Liste.
from scipy.signal import lfilter
def EMAnew(s, ma_period):
alpha = 2/(ma_period+1)
y = lfilter([alpha], [1,alpha-1], s)
return pd.Series(y, index=s.index)
%timeit MA = EMAnew(dataM1['Close'], 10)
Ergebnis ist
100 loops, best of 3: 3.08 ms per loop
Es wurde fast die gleiche Geschwindigkeit wie im Fall von SMA. Immerhin ist lfilter ()
schnell.
Das Ergebnis ist, dass lfilter ()
auch diesmal schnell ist, aber es gibt ein kleines Problem mit dem Verarbeitungsergebnis.
In EMA wird bei der Berechnung der ersten Ausgabe $ y (0) $ $ y (-1) $ ohne Daten verwendet, bei Pandas jedoch für Zeitreihendaten, also $ y Es wird so verarbeitet, dass $ y (-1) $ nicht als (0) = x (0) $ verwendet wird.
pd.DataFrame({'Close':dataM1['Close'],'EMA':MA}).head(10)
Close | EMA | |
---|---|---|
Time | ||
2015-01-01 13:00:00 | 1.20962 | 1.209620 |
2015-01-01 13:01:00 | 1.20962 | 1.209620 |
2015-01-01 13:02:00 | 1.20961 | 1.209616 |
2015-01-01 13:04:00 | 1.20983 | 1.209686 |
2015-01-01 13:05:00 | 1.20988 | 1.209742 |
2015-01-01 13:06:00 | 1.20982 | 1.209762 |
2015-01-01 13:07:00 | 1.20987 | 1.209788 |
2015-01-01 13:08:00 | 1.21008 | 1.209855 |
2015-01-01 13:09:00 | 1.20996 | 1.209878 |
2015-01-01 13:10:00 | 1.20977 | 1.209855 |
In diesem Fall unterscheidet sich das Ergebnis von EMA nicht so sehr von der Eingabezeitreihe, aber im Fall von "lfilter ()" wird es als $ y (-1) = 0 $ berechnet, also die erste EMA Der Wert von wird erheblich von der Eingabe abweichen.
Close | EMA | |
---|---|---|
Time | ||
2015-01-01 13:00:00 | 1.20962 | 0.219931 |
2015-01-01 13:01:00 | 1.20962 | 0.399874 |
2015-01-01 13:02:00 | 1.20961 | 0.547099 |
2015-01-01 13:04:00 | 1.20983 | 0.667596 |
2015-01-01 13:05:00 | 1.20988 | 0.766193 |
2015-01-01 13:06:00 | 1.20982 | 0.846852 |
2015-01-01 13:07:00 | 1.20987 | 0.912855 |
2015-01-01 13:08:00 | 1.21008 | 0.966896 |
2015-01-01 13:09:00 | 1.20996 | 1.011090 |
2015-01-01 13:10:00 | 1.20977 | 1.047213 |
Es scheint, dass dieses Problem mit dem optionalen Argument "lfilter ()" gelöst werden kann. Durch das Schreiben des Folgenden habe ich fast das gleiche Ergebnis wie Pandas erzielt.
def EMAnew(s, ma_period):
alpha = 2/(ma_period+1)
y,zf = lfilter([alpha], [1,alpha-1], s, zi=[s[0]*(1-alpha)])
return pd.Series(y, index=s.index)
Hier ist zi
der Anfangswert der Zustandsvariablen, also nicht nur der Anfangswert von Eingabe und Ausgabe, sondern hier $ y (0) = \ alpha x (0) + zi = x (0) Wenn Sie ein "zi" setzen, das zu "$" wird, scheint das Ergebnis so zu sein.