Comparé le temps de calcul de la moyenne mobile écrite en Python
Il est hors de question d'utiliser l'instruction for en Python pour calculer la moyenne mobile, et le résultat est qu'il est préférable d'utiliser les fonctions pandas et scipy. Cependant, dans l'article ci-dessus, il s'agissait d'une moyenne mobile du type de filtre FIR tel que SMA et LWMA, donc cette fois j'ai étudié la moyenne mobile du type de filtre IIR tel que EMA et SMMA.
EMA EMA est une abréviation de moyenne mobile exponentielle et s'exprime par la formule suivante.
Où $ \ alpha $ est un paramètre réel de 0 à 1. Cette formule n'inclut pas de paramètre qui représente la période, mais comme SMA et LWMA utilisent des paramètres qui représentent la période, EMA utilise souvent le paramètre de période en conséquence. En supposant que la période est $ p $, EMA est $ \ alpha = 2 / (p + 1) $ et SMMA est $ \ alpha = 1 / p $. Personnellement, je ne pense pas qu'il soit nécessaire de faire la distinction entre EMA et SMMA car ils ont la même formule, mais je les mentionnerai car ils sont utilisés séparément dans MetaTrader.
Commençons par l'implémenter avec les pandas. Les données à traiter sont les mêmes que dans l'article précédent. Il s'agit d'une série chronologique à quatre valeurs d'environ 370 000 pièces. Puisque les pandas sont pour les données de séries chronologiques, EMA peut aussi être facilement écrit avec les fonctions ʻewm () ʻetmean ()
.
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)
Étant donné que le temps d'exécution sera également comparé cette fois, les résultats de la mesure seront affichés.
10 loops, best of 3: 32.2 ms per loop
Dans le cas du SMA, c'était environ 16 millisecondes, donc c'est environ deux fois plus lent.
Lorsque vous utilisez lfilter ()
de scipy, il ne suffit pas de saisir simplement $ \ alpha $, vous devez passer au format de filtre IIR et entrer le coefficient. Donc, je vais le convertir un peu. (La théorie détaillée est omise. C'est la base du traitement numérique du signal.)
Convertit les deux côtés de l'expression EMA en $ z $.
Si vous mettez $ Y (z) $ sur le côté gauche
Et si $ Y (z) / X (z) $ est $ H (z) $,
Peut être écrit. C'est la fonction système du filtre IIR. Passer l'argument de lfilter ()
est le coefficient du polynôme moléculaire et le coefficient du polynôme dénominateur de cette fonction système.
Dans le cas de l'EMA, la molécule de la fonction système est une constante et le dénominateur est un polynôme du premier ordre, de sorte que la formule générale de la fonction système peut s'écrire comme suit.
En comparant les coefficients, vous pouvez voir que les coefficients de $ b $ et $ a $ sont les suivants.
Donc, si vous implémentez EMA en utilisant lflter ()
de scipy, vous pouvez écrire comme suit. Mettez les $ b $ et $ a $ ci-dessus sous la forme d'une 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)
Le résultat est
100 loops, best of 3: 3.08 ms per loop
Il est devenu presque la même vitesse que le cas de SMA. Après tout, lfilter ()
est rapide.
Le résultat est que lfilter ()
est également rapide cette fois, mais il y a un léger problème avec le résultat du traitement.
En EMA, lors du calcul de la première sortie $ y (0) $, $ y (-1) $ sans données est utilisé, mais dans le cas des pandas, il convient aux données de séries chronologiques, donc $ y Il est traité de sorte que $ y (-1) $ ne soit pas utilisé comme (0) = x (0) $.
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 |
Dans ce cas, le résultat de EMA n'est pas si différent de la série temporelle d'entrée, mais dans le cas de lfilter ()
, il est calculé comme $ y (-1) = 0 $, donc le premier EMA La valeur de s'écartera considérablement de l'entrée.
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 |
Il semble que ce problème puisse être résolu avec l'argument optionnel de lfilter ()
. En écrivant ce qui suit, j'ai presque le même résultat que les pandas.
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)
Ici, zi
est la valeur initiale de la variable d'état, donc ce n'est pas seulement la valeur initiale de l'entrée et de la sortie, mais ici, $ y (0) = \ alpha x (0) + zi = x (0) Si vous mettez un zi
qui devient $, il semble que le résultat sera comme ça.