web-dev-qa-db-fra.com

Pandas calcule-t-il ewm mal?

Lorsque l'on tente de calculer la moyenne mobile exponentielle (EMA) à partir de données financières dans une trame de données, il semble que l'approche ewm de Pandas soit incorrecte.

Les bases sont bien expliquées dans le lien suivant: http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages

En allant à Pandas explication, l'approche adoptée est la suivante (en utilisant le paramètre "adjust" comme False):

   weighted_average[0] = arg[0];
   weighted_average[i] = (1-alpha) * weighted_average[i-1] + alpha * arg[i]

À mon avis, cela est incorrect. Le "arg" doit être (par exemple) les valeurs de fermeture, cependant, arg [0] est la première moyenne (c'est-à-dire la moyenne simple de la première série de données de la longueur de la période sélectionnée), mais PAS la première valeur de fermeture . arg [0] et arg [i] ne peuvent donc jamais provenir des mêmes données. L'utilisation du paramètre "min_periods" ne semble pas résoudre ce problème.

Quelqu'un peut-il m'expliquer comment (ou si) Pandas peut être utilisé pour calculer correctement l'EMA des données?

10
jeronimo

Il existe plusieurs façons d'initialiser une moyenne mobile exponentielle, donc je ne dirais pas pandas le fait mal, juste différent.

Voici un moyen de le calculer comme vous le souhaitez:

In [20]: s.head()
Out[20]: 
0    22.27
1    22.19
2    22.08
3    22.17
4    22.18
Name: Price, dtype: float64

In [21]: span = 10

In [22]: sma = s.rolling(window=span, min_periods=span).mean()[:span]

In [24]: rest = s[span:]

In [25]: pd.concat([sma, rest]).ewm(span=span, adjust=False).mean()
Out[25]: 
0           NaN
1           NaN
2           NaN
3           NaN
4           NaN
5           NaN
6           NaN
7           NaN
8           NaN
9     22.221000
10    22.208091
11    22.241165
12    22.266408
13    22.328879
14    22.516356
15    22.795200
16    22.968800
17    23.125382
18    23.275312
19    23.339801
20    23.427110
21    23.507635
22    23.533520
23    23.471062
24    23.403596
25    23.390215
26    23.261085
27    23.231797
28    23.080561
29    22.915004
Name: Price, dtype: float64
10
chrisb

Vous pouvez calculer l'EWMA en utilisant l'alpha ou le coefficient (span) dans la fonction Pandas ewm.

Formule d'utilisation de l'alpha: (1 - alpha) * previous_val + alpha * current_valalpha = 1 / period

Formule d'utilisation de coeff: ((current_val - previous_val) * coeff) + previous_valcoeff = 2 / (period + 1)

Voici comment utiliser Pandas pour calculer les formules ci-dessus:

con = pd.concat([df[:period][base].rolling(window=period).mean(), df[period:][base]])

if (alpha == True):
    df[target] = con.ewm(alpha=1 / period, adjust=False).mean()
else:
    df[target] = con.ewm(span=period, adjust=False).mean()
6
arkochhar

Voici un exemple de la façon dont Pandas calcule les ewm ajustés et non ajustés:

name = 'closing'
series = pd.Series([1, 2, 3, 5, 8, 13, 21, 34], name=name).to_frame()
period = 4
alpha = 2/(1+period)

series[name+'_ewma'] = np.nan
series.loc[0, name+'_ewma'] = series[name].iloc[0]

series[name+'_ewma_adjust'] = np.nan
series.loc[0, name+'_ewma_adjust'] = series[name].iloc[0]

for i in range(1, len(series)):
    series.loc[i, name+'_ewma'] = (1-alpha) * series.loc[i-1, name+'_ewma'] + alpha * series.loc[i, name]

    ajusted_weights = np.array([(1-alpha)**(i-t) for t in range(i+1)])
    series.loc[i, name+'_ewma_adjust'] = np.sum(series.iloc[0:i+1][name].values * ajusted_weights) / ajusted_weights.sum()

print(series)
print("diff adjusted=False -> ", np.sum(series[name+'_ewma'] - series[name].ewm(span=period, adjust=False).mean()))
print("diff adjusted=True -> ", np.sum(series[name+'_ewma_adjust'] - series[name].ewm(span=period, adjust=True).mean()))

La formule mathématique peut être trouvée à https://github.com/pandas-dev/pandas/issues/8861

3
Ben

Si vous calculez ewm de ewm (comme la formule MACD), vous obtiendrez de mauvais résultats car le deuxième ewm et les suivants utiliseront l'index commençant par 0 et se terminant par la période. J'utilise la solution suivante.

sma = df['Close'].rolling(period, min_periods=period).mean()
#this variable is used to shift index by non null start minus period
idx_start = sma.isna().sum() + 1 - period
idx_end = idx_start + period
sma = sma[idx_start: idx_end]
rest = df[item][idx_end:]
ema = pd.concat([sma, rest]).ewm(span=period, adjust=False).mean()
0
tentativafc