кутия с инструменти за търговия

Кутия с инструменти за търговия на Python: Претеглени и експоненциални подвижни средни

В първата публикация от поредицата Инструменти за финансова търговия („Изграждане на кутия с инструменти за финансова търговия в Python: проста подвижна средна“) обсъдихме как да изчислим проста подвижна средна, да я добавим към диаграма на ценови серии и да я използваме за инвестиции и търговски решения. Простата пълзяща средна е само една от няколкото налични пълзящи средни, които могат да се прилагат към ценови серии за изграждане на системи за търговия или рамки за инвестиционни решения. Сред тях две други пълзящи средни се използват често на финансовите пазари:

  • Претеглена подвижна средна (WMA)
  • Експоненциална подвижна средна (EMA)

В тази статия ще проучим как да изчислим тези две средни стойности и как да гарантираме, че резултатите отговарят на определенията, които трябва да приложим.

Претеглена подвижна средна

В някои приложения едно от ограниченията на простата пълзяща средна е, че тя дава еднаква тежест на всяка от дневните цени, включени в прозореца. Например в 10-дневна пълзяща средна най-скорошният ден получава същата тежест като първия ден в прозореца: всяка цена получава 10% тежест.

В сравнение с простата плъзгаща се средна, Линейно претеглената пълзяща средна (или просто Претеглена плъзгаща се средна, WMA) дава по-голяма тежест на най-скорошната цена и постепенно по-малко, когато погледнем назад във времето. При 10-дневна среднопретеглена цена цената на 10-ия ден ще бъде умножена по 10, тази на 9-ия ден по 9, на 8-ия ден по 8 и т.н. След това общата сума ще бъде разделена на сумата от теглата (в този случай: 55). В този конкретен пример най-новата цена получава около 18,2% от общото тегло, втората по-скорошна 16,4% и така нататък до най-старата цена в прозореца, която получава 0,02% от теглото.

Нека приложим това на практика с пример в Python. В допълнение към pandas и Matplotlib, ние ще използваме NumPy:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

Ние прилагаме стил за нашите диаграми. Ако използвате Jupyter, добра идея е да добавите инструкцията %matplotlib inline (и да пропуснете plt.show(), когато създавате диаграми):

plt.style.use('fivethirtyeight')

За следващите примери ще използваме ценови данни от статия на StockCharts.com. Това е отлична образователна статия за пълзящите средни и препоръчвам да я прочетете. Серията от цени, използвана в тази статия, може да принадлежи на всяка акция или финансов инструмент и ще служи за нашите цели за илюстрация.

Промених оригиналния лист на Excel, като включих изчисления за 10-дневната WMA, тъй като изчислението за EMA вече е включено. Можете да получите достъп до моя „файл на Google Таблици“ и да изтеглите данните във формат CSV „тук“.

Винаги е добра практика, когато моделирате данни, да започнете с просто внедряване на нашия модел, което можем да използваме, за да сме сигурни, че резултатите от окончателното ни внедряване са правилни.

Започваме със зареждане на данните в рамка с данни:

datafile = 'cs-movavg.csv'
data = pd.read_csv(datafile, index_col = 'Date')
data.index = pd.to_datetime(data.index)
# We can drop the old index column:
data = data.drop(columns='Unnamed: 0')
data

Засега ще разгледаме само колоните Цена и 10-дневна WMA и ще преминем към EMA по-късно.

Когато става дума за линейно претеглени плъзгащи се средни, библиотеката pandas няма готов готов метод за изчисляването им. Предлага обаче много мощен и гъвкав метод: .apply() Този метод ни позволява да създаваме и предаваме всяка „персонализирана функция“ към подвижния прозорец: така ще изчислим нашата претеглена подвижна средна. За да изчислим 10-дневен WMA, започваме със създаване на масив от тегла - цели числа от 1 до 10:

weights = np.arange(1,11) #this creates an array with integers 1 to 10 included
weights

Което изглежда така:

array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

След това, като използваме метода .apply(), предаваме нашата собствена функция (функция ламбда), за да изчислим точковия продукт на теглата и цените в нашия подвижни прозорец (цените в прозорецът ще бъде умножен по съответното тегло, след което ще бъде сумиран), след което ще бъде разделен на сумата от теглата:

wma10 = data['Price'].rolling(10).apply(lambda prices: np.dot(prices, weights)/weights.sum(), raw=True)
wma10.head(20)

Което дава:

Сега искаме да сравним нашия WMA с този, получен с електронната таблица. За да направим това, можем да добавим колона „Нашият 10-дневен WMA“ към рамката с данни. За да направим визуалното сравнение по-лесно, можем да закръглим серията WMA до три десетични знака, използвайки метода.round() от NumPy. След това избираме колоните за цена и WMA, които да се показват:

data['Our 10-day WMA'] = np.round(wma10, decimals=3)
data[['Price', '10-day WMA', 'Our 10-day WMA']].head(20)

Показани са:

Двете WMA колони изглеждат еднакви. Има няколко разлики в третия знак след десетичната запетая, но можем да сведем това до грешка при закръгляване и да заключим, че нашата реализация на WMA е правилна. В приложение от реалния живот, ако искаме да сме по-строги, трябва да изчислим разликите между двете колони и да проверим дали не са твърде големи. Засега поддържаме нещата прости и можем да бъдем доволни от визуалната проверка.

Би било интересно да сравним на графика новосъздадения WMA с познатия SMA:

sma10 = data['Price'].rolling(10).mean()
plt.figure(figsize = (12,6))
plt.plot(data['Price'], label="Price")
plt.plot(wma10, label="10-Day WMA")
plt.plot(sma10, label="10-Day SMA")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend()
plt.show()

Това показва:

Както виждаме, и двете средни изглаждат движението на цената. WMA е по-реактивен и следва цената по-близо от SMA: ние очакваме това, тъй като WMA дава по-голяма тежест на най-новите ценови наблюдения. Освен това и двете серии с подвижни средни започват на ден 10: първият ден с достатъчно налични данни за изчисляване на средните стойности.

Претеглената подвижна средна може да е по-малко известна от експоненциалния си брат. Въпреки това, той може да бъде допълнителен елемент в нашата кутия с инструменти, когато се опитваме да изградим оригинални решения. Внедряването на WMA в Python ни принуди да търсим начин за създаване на персонализирани подвижни средни с помощта на .apply(): тази техника може да се използва и за внедряване на нови и оригинални подвижни средни.

Експоненциална подвижна средна

Подобно на претеглената пълзяща средна, експоненциалната пълзяща средна (EMA) придава по-голяма тежест на най-новите наблюдения на цените. Въпреки че приписва по-малка тежест на минали данни, той се основава на рекурсивна формула, която включва в своето изчисление всички минали данни в нашите ценови серии.

EMA в момент t се изчислява като текущата цена, умножена по коефициент на изглаждане алфа (положително число, по-малко от 1) плюс EMA в момент 𝑡−1, умножено по 1 минус алфа. Това е основно стойност между предишната EMA и текущата цена:

Коефициентът на изглаждане 𝛼 ( алфа ) се определя като:

където 𝑛 е броят дни в нашия период. Следователно 10-дневната EMA ще има изглаждащ фактор:

Pandas включва метод за изчисляване на плъзгащата се средна EMA на всеки времеви ред: .ewm(). Ще отговори ли този метод на нашите нужди и ще изчисли ли средна стойност, която отговаря на нашата дефиниция? Нека го тестваме:

ema10 = data['Price'].ewm(span=10).mean()
ema10.head(10)

Което дава:

Искаме да сравним тази серия EMA с тази, получена в електронната таблица:

data['Our 10-day EMA'] = np.round(ema10, decimals=3)
data[['Price', '10-day EMA', 'Our 10-day EMA']].head(20)

Кои резултати:

Както вече сте забелязали, тук имаме проблем: 10-дневната EMA, която току-що изчислихме, не съответства на тази, изчислена в изтеглената електронна таблица. Единият започва на 10-ия ден, докато другият започва на 1-вия ден. Освен това стойностите не съвпадат точно.

Грешно ли е изчислението ни? Или изчислението в предоставената електронна таблица е грешно? Нито едно от двете: тези две серии отговарят на две различни дефиниции на EMA. За да бъдем по-конкретни, формулата, използвана за изчисляване на EMA, е същата. Това, което се променя, е само използването на първоначалните стойности.

Ако разгледаме внимателно дефиницията на експоненциалната подвижна средна на уеб страницата на StockCharts.com, можем да забележим една важна подробност: те започват да изчисляват 10-дневна подвижна средна на ден 10, като пренебрегват предходните дни и заменят цената на деня 10 с неговия 10-дневен SMA. Това е различно определение от приложеното, когато изчислихме EMA, използвайки директно метода .ewm().

Следните редове от код създават нова модифицирана серия от цени, където първите 9 цени (когато SMA не е наличен) се заменят с NaN и цената на 10-та дата става нейната 10-дневна SMA:

modPrice = data['Price'].copy()
modPrice.iloc[0:10] = sma10[0:10]
modPrice.head(20)

Можем да използваме тази модифицирана серия от цени, за да изчислим втора версия на EWM. Като разгледаме документацията, можем да отбележим, че методът .ewm() има параметър adjust, който по подразбиране е True. Този параметър коригира теглата, за да отчете дисбаланса в началните периоди (ако имате нужда от повече подробности, вижте раздела Експоненциално претеглени прозорци в документацията на pandas).

Ако искаме да емулираме EMA, както в нашата електронна таблица, използвайки нашите модифицирани ценови серии, нямаме нужда от тази корекция. След това задаваме adjust=False:

ema10alt = modPrice.ewm(span=10, adjust=False).mean()

Тази новоизчислена EMA ще съвпадне ли с изчислената в електронната таблица? Нека погледнем:

data['Our 2nd 10-Day EMA'] = np.round(ema10alt, decimals=3)
data[['Price', '10-day EMA', 'Our 10-day EMA', 'Our 2nd 10-Day EMA']].head(20)

Сега се справяме много по-добре. Получихме серия EMA, която съответства на изчислената в електронната таблица.

В крайна сметка се оказахме с две различни версии на EMA в ръцете си:

  1. ema10: Тази версия използва обикновен метод .ewm(), започва в началото на нашата история на цените, но не отговаря на определението, използвано в електронната таблица.
  2. ema10alt: Тази версия започва на ден 10 (с първоначална стойност, равна на 10-дневната SMA) и отговаря на определението в нашата електронна таблица.

Кое е най-доброто за използване? Отговорът е: зависи от това какво ни е необходимо за нашето приложение и за изграждането на нашата система. Ако се нуждаем от серия EMA, която започва от ден 1, тогава трябва да изберем първата. От друга страна, ако трябва да използваме нашата средна стойност в комбинация с други средни стойности, които нямат стойности за първите дни (като SMA), тогава втората вероятно е най-добрата.

Второто EMA се използва широко сред анализаторите на финансовите пазари: ако трябва да внедрим вече съществуваща система, трябва да внимаваме да използваме правилната дефиниция. В противен случай резултатите може да не са очакваните от нас и да поставят под въпрос точността на цялата ни работа. Във всеки случай числената разлика между тези две средни стойности е минимална, като въздействието върху нашата система за вземане на търговски или инвестиционни решения е ограничено до първите дни.

Нека да разгледаме всички подвижни средни, които сме използвали досега в диаграма:

plt.figure(figsize = (12,6))
plt.plot(data['Price'], label="Price")
plt.plot(wma10, label="10-Day WMA")
plt.plot(sma10, label="10-Day SMA")
plt.plot(ema10, label="10-Day EMA-1")
plt.plot(ema10alt, label="10-Day EMA-2")
plt.xlabel("Date")
plt.ylabel("Price")
plt.legend()
plt.show()

От всички подвижни средни, WMA изглежда тази, която е по-отзивчива и маркира цената по-точно, докато SMA е тази, която реагира с малко повече забавяне. Двете версии наEMA са склонни да се припокриват, главно през последните дни.

Надявам се, че сте намерили тази публикация за полезна. Въвеждането на Weighted Moving Average ни помогна да научим и внедрим персонализирана средна стойност въз основа на конкретна дефиниция. Работата с експоненциалната подвижна средна ни даде възможност да подчертаем колко е важно да гарантираме, че всяка функция, която използваме за работа върху ценови серии, съответства на определението, което имаме за всяка дадена задача.

Бележка от редакторите на Towards Data Science: Въпреки че позволяваме на независими автори да публикуват статии в съответствие с нашите правила и насоки, ние не одобряваме приноса на всеки автор. Не трябва да разчитате на авторски произведения, без да потърсите професионален съвет. Вижте нашите Условия за четене за подробности.