Как устроены индикаторы. Часть 2: EMA + MACD

Вступление - код для SMA

Итак, в прошлой части статьи мы смотрели как устроен SMA (и ничего особо интересного там не увидели), для закрепления материала я покажу, как его можно считать самому с помощью уже не экселя, не талиба, а голого питона.

Это будет невероятно длинный, сложный и запутанный код, вот он полностью:

import math

def SMA(data, period):
    if len(data) == 0:
        raise Exception("Empty data")
    if period <= 0:
        raise Exception("Invalid period")

    interm = 0
    result = []
    for i, v in enumerate(data):
        interm += v
        if (i+1) < period:
            result.append(math.nan)
        else:
            result.append(interm/float(period))
            interm -= data[i+1-period]
    return result

Всё! Мы создали функцию SMA которая принимает на вход массив данных (обычно это close каждой свечи), и кол-во периодов для скользящей средней. Давайте погоняем и посмотрим, что она вернет:

print(SMA([1,2,3,4,5],1))
print(SMA([1,2,3,4,5],2))
print(SMA([1,2,3,4,5],3))
print(SMA([1,2,3,4,5],4))
print(SMA([1,2,3,4,5],5))
print(SMA([1,2,3,4,5],6))

[1.0, 2.0, 3.0, 4.0, 5.0]
[nan, 1.5, 2.5, 3.5, 4.5]
[nan, nan, 2.0, 3.0, 4.0]
[nan, nan, nan, 2.5, 3.5]
[nan, nan, nan, nan, 3.0]
[nan, nan, nan, nan, nan]

Мы указали период 1, скользящяя средняя брала каждое значение, делило на 1 и возвращало.

Указали два - стало брать по два значения и т.п. Ну, в общем всё, как в экселе из прошлой статьи.

И еще там мы затронули вопрос, что SMA не слишком хороша, т.к. нужно не равномерно делить весь указанный период, а больше внимания уделять последним датам, так график будет более чувствительным. И именно для этого было придумано EMA.


Реклама


EMA - экспоненциальная скользящая средняя

Язык сломаешь об такие названия. А если почитаешь Википедию на этот счет, то вообще взгрустнешь. Но опять же, за страшными словами прячутся уши банальных вещей.

Для построения EMA строится всё та же SMA, берется первый непустой результат, он будет первым значением.

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

Третьим значением будет разница цены закрытия и предыдущего результата, помноженная на заранее выбранный коэффициент, и увеличенная на предыдущее значение.

И т.п. Не вникайте, у меня есть ощущение, что я еще больше вас запутал. Вот код самой функции:

def EMA(data, period):
    if period <= 1:
        raise Exception("Invalid period")
    
    sma = SMA(data, period)
    multiplier = 2/(float(period+1))
    result = []
    
    for k, v in enumerate(sma):
        if math.isnan(v):
            result.append(math.nan)
        else:
            prev = result[k-1]
            if math.isnan(prev):
                result.append(v)
                continue
            ema = (data[k]-prev)*multiplier + prev
            result.append(ema)
    return result

Возможно, EMA сложнее понять чем SMA, но в целом тут нет ничего запредельного. Основная мысль, что на текущее значение средней скользящей влияют результаты предыдущих с некоторыми фиксированными значениями (для EMA обычно берется 2/(period+1)).

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


Реклама


Ну и наконец MACD 

MACD - это схождение/расхождение скользящих средних (Moving Average Convergence/Divergence).

Как вы можете понять из названия, берутся скользящие средние и сравниваются. И самое интересное, что у нас уже все для этого есть.  Скопирую кусок из Википедии:

Для расчёта линейного MACD из скользящей средней цены (обычно берётся экспоненциальная скользящая средняя с меньшим периодом) вычитается экспоненциальная скользящая средняя с большим периодом. В большинстве случаев полученный результат сглаживают при помощи экспоненциальной скользящей средней (EМА), чтобы устранить случайные колебания.

1) MACD = ЕМАs(P) − EMAl(P)

2) Signal = EМАa(ЕМАs(P) − EMAl(P)) — (сигнальная линия)

Гистограмма разности = (1) − (2) — (столбики штриховки на рисунке справа-вверху)

где:

  • EMAs(P) — экспоненциальная скользящая средняя с коротким периодом от цены.
  • ЕМАl(P) — экспоненциальная скользящая средняя с длинным периодом от цены.
  • EМАa(P) — сглаживающая скользящая средняя с коротким периодом от разницы двух остальных скользящих.
  • P — цена, обычно берётся цена закрытия периода Close, но возможны и другие варианты (Open, High, Low, Close, Median Price, Typical Price и т. д.)

По умолчанию на дневном графике часто используют следующие настройки MACD:

  • ЕМАs — (короткая) с периодом 12 дней (две недели).
  • ЕМАl — (длинная) с периодом 26 дней (месяц).
  • EМАa — (сглаживающая разницу) с периодом 9 значений.

Перевожу на русский. Нужно посчитать:

EMA за за короткий период (12)

EMA(close, 12)

EMA за длинный период (26)

EMA(close, 26)

Получим два массива. Из первого вычтем второй (найдем разницу каждого элемента для двух массивов).

Это будет линия macd.

Теперь посчитаем EMA, передав ему не цены закрытия, а только что посчитанную MACD, и сглаживающую (9)

EMA(macd, 9)

Это будет линия macdsignal.

Теперь найдем разницу между каждым элементом массивов macd и macdsignal, и получим что? Правильно, macdhist.

Гистограмму, которая отображается обычно столбиками.

Вот вам код:

def MACD(data, fastperiod, slowperiod, signalperiod):
    macd, macdsignal, macdhist = [], [], []

    fast_ema = EMA(data, fastperiod)
    slow_ema = EMA(data, slowperiod)
    
    diff = []

    for k, fast in enumerate(fast_ema):
        if math.isnan(fast) or math.isnan(slow_ema[k]):
            macd.append(math.nan)
            macdsignal.append(math.nan)
        else:
            macd.append(fast-slow_ema[k])
            diff.append(macd[k])

    diff_ema = EMA(diff, signalperiod)
    macdsignal = macdsignal + diff_ema

    for k, ms in enumerate(macdsignal):
        if math.isnan(ms) or math.isnan(macd[k]):
            macdhist.append(math.nan)
        else:
            macdhist.append(macd[k] - macdsignal[k])

    return macd, macdsignal, macdhist

Реклама


Заключение

Вот код полностью для SMA, EMA и MACD. Он занимает 64 строки и еще 30 строк для демонстрации запуска.

import math

def SMA(data, period):
    if len(data) == 0:
        raise Exception("Empty data")
    if period <= 0:
        raise Exception("Invalid period")

    interm = 0
    result = []
    for i, v in enumerate(data):
        interm += v
        if (i+1) < period:
            result.append(math.nan)
        else:
            result.append(interm/float(period))
            interm -= data[i+1-period]
    return result

def EMA(data, period):
    if period <= 1:
        raise Exception("Invalid period")
    
    sma = SMA(data, period)
    multiplier = 2/(float(period+1))
    result = []
    
    for k, v in enumerate(sma):
        if math.isnan(v):
            result.append(math.nan)
        else:
            prev = result[k-1]
            if math.isnan(prev):
                result.append(v)
                continue
            ema = (data[k]-prev)*multiplier + prev
            result.append(ema)
    return result

def MACD(data, fastperiod, slowperiod, signalperiod):
    macd, macdsignal, macdhist = [], [], []

    fast_ema = EMA(data, fastperiod)
    slow_ema = EMA(data, slowperiod)
    
    diff = []

    for k, fast in enumerate(fast_ema):
        if math.isnan(fast) or math.isnan(slow_ema[k]):
            macd.append(math.nan)
            macdsignal.append(math.nan)
        else:
            macd.append(fast-slow_ema[k])
            diff.append(macd[k])

    diff_ema = EMA(diff, signalperiod)
    macdsignal = macdsignal + diff_ema

    for k, ms in enumerate(macdsignal):
        if math.isnan(ms) or math.isnan(macd[k]):
            macdhist.append(math.nan)
        else:
            macdhist.append(macd[k] - macdsignal[k])

    return macd, macdsignal, macdhist

print("SMA", SMA([1,2,3,4,5],1))

print("EMA", EMA([22.2734, 22.1940, 22.0847, 22.1741,
            22.1840, 22.1344, 22.2337, 22.4323,
            22.2436, 22.2933, 22.1542, 22.3926,
            22.3816, 22.6109, 23.3558, 24.0519,
            23.7530, 23.8324, 23.9516, 23.6338,
            23.8225, 23.8722, 23.6537, 23.1870,
            23.0976, 23.3260, 22.6805, 23.0976,
            22.4025, 22.1725],10
    )
)

macd, macdsignal, macdhist = MACD([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                               ],
                                12,
                                26,
                                9
                          )
print("MACD", macd)
print("MACDSIGNAL", macdsignal)
print("MACDHIST", macdhist)

Это те самые линии, в которые трейдеры напряженно всматриваются в поисках точки входа или выхода. Магии тут нет, код и математика перед глазами. В следующих статьях затронем RSI, пояса Боллинджера и что-нибудь еще. 

Удачи!


Это статья из цикла "Теханализ + Разработка индикаторов"
Все статьи цикла:

Последнее изменение:

Не забудьте рассказать друзьям об этой статье.
Чтобы поддержать ресурс Bablofil достаточно просто поделиться с друзьями этой статьей в социальных сетях. Каждый репост - это самая высокая оценка качества материала. Спасибо, что читаете этот блог.



Комментарии
Пожалуйста, авторизуйтесь, что бы оставить свой комментарий
Крипто-кошельки для помощи и благодарности проекту:

Bitcoin адрес проекта: [[address]]

Перевод на сумму [[value]] BTC получен. Спасибо!.
[[error]]

Ethereum адрес проекта: [[address]]