用 Python 實現股票指數移動平均線
移動平均指標用于各種交易策略,以發現價格數據的長期趨勢。簡單移動平均線策略的一個潛在缺點是它們對所有價格的權重相同,而您可能希望最近的價格占有更大的比重。指數移動平均線 (EMA) 是實現這一目標的一種方法。
下面我們通過代碼示例詳細介紹 EMA 的實現,并將其與 SMA (簡單移動平均)進行比較。
EMA 通過加權乘數賦予最新價格更多權重。這個乘數應用于最后一個價格,因此它比其他數據點占移動平均線更大的部分。EMA 是通過采用最近的價格(我們將其稱為“時間 t 的價格”) 減去前一個時間段 (EMA_{t-1})。此差異由您將 EMA 設置為 (N) 并加回到 EMA_{t-1}的時間段數加權。在數學上,我們可以這樣寫:
你可能已經注意到上面的等式有一個小問題,它是如何開始的?它參考了最后一個時期的 EMA,所以如果你進行第一次計算,它參考的是什么?這通常可以通過替換簡單移動平均線 (SMA) 來初始化計算來延緩,這樣您就可以在第一次之后的所有時間段構建 EMA。
讓我們通過導入我們的包,用 Python 中的一個簡單示例來展示它是如何工作的。
- import numpy as np
- import pandas as pd
- import yfinance as yf
- import matplotlib.pyplot as plt
從這里開始,我們將構建兩個函數來協同工作并計算我們的指標。第一個函數將是我們上面概述的公式的簡單實現:
- def _calcEMA(P, last_ema, N):
- return (P - last_ema) * (2 / (N + 1)) + last_ema
第二個函數將計算我們所有數據的 EMA,首先使用 SMA 對其進行初始化,然后迭代我們的數據以使用我們的 SMA 列中的值更新每個后續條目,或者調用我們上面定義的 _calcEMA 函數來處理大于 N的值。
- def calcEMA(data, N):
- # Initialize series
- data['SMA_' + str(N)] = data['Close'].rolling(N).mean()
- ema = np.zeros(len(data))
- for i, _row in enumerate(data.iterrows()):
- row = _row[1]
- if i < N:
- ema[i] += row['SMA_' + str(N)]
- else:
- ema[i] += _calcEMA(row['Close'], ema[i-1], N)
- data['EMA_' + str(N)] = ema.copy()
- return data
現在,讓我們獲取一些數據,看看它是如何工作的。我們將拉出比回測更短的時間段,并比較 EMA 和 SMA 的 10、50 和 100 天。
- ticker = 'BABA'
- yfyfObj = yf.Ticker(ticker)
- data = yfObj.history(ticker, start='2018-01-01', end='2020-12-31')
- N = [10, 50, 100]
- _ = [calcEMA(data, n) for n in N]
- colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
- fig, ax = plt.subplots(figsize=(18, 8))
- ax.plot(data['Close'], label='Close')
- for i, n in enumerate(N, 1):
- ax.plot(data[f'EMA_{n}'], label=f'EMA-{n}', color=colors[i])
- ax.plot(data[f'SMA_{n}'], label=f'SMA-{n}', color=colors[i], linestyle=':')
- ax.legend()
- ax.set_title(f'EMA and Closing Price Comparison for {ticker}')
- plt.show()
您可以在上圖中看到,EMA 比 SMA 對最近的變化更敏感。較短的時間范圍也比較長的時間范圍更具響應性,較長的時間范圍具有可以追溯到幾個月或更長時間的價格“記憶”。
所有類型的移動平均線都是滯后指標,這意味著它們只能告訴您價格中已經發生了什么。然而,這并不意味著它們不能用于識別趨勢和制定使用一個或多個移動平均指標的策略。如果您有想法,請繼續進行測試,看看如何結合 EMA、SMA 和其他值來開發新的盈利交易策略。