Вступление - код для 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, пояса Боллинджера и что-нибудь еще.
Удачи!