В одной из предыдущих статей мы разобрали использование библиотеки ta-lib и построение индикаторов. Вот ссылка для освежения памяти. Теперь давайте попробуем получить из этого практическую пользу – научимся анализировать MACD программно, и напишем функцию для принятия решения – стоит ли торговать в данный момент времени или не стоит. Ну и воткнем индикатор в бота.
Если вы помните, в прошлой статье мы получали данные с биржи, и строили три графика – сами свечи, линии MACD и гистограмму. Возьмем этот пример, и на его основе разберемся дальше, что же с этим делать.
Строим MACD и ставим задачу
Итак, вот код – с некоторыми изменениями – мы построим свечи, построим линии, а третий пока что оставим пустым – он нам пригодится для демонстрации работы алгоритма.
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import datetime as datetime
PAIR = 'BTC_ETH' # Пара, по которой работаем
start_time = time.time() - 15*60*60 # за какой период брать данные - 15 часов в данном случае
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
xdate=[datetime.datetime.fromtimestamp(item['date']) for item in data]
fig, ax = plt.subplots(3, sharex=True)
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
plt.show()
После запуска получим примерно такую картину:
Но, раз уж смотреть на графики скучно, давайте попробуем их проанализировать? Методик много, но мы воспользуемся подсказкой от википедии:
Обычно сигналом «Покупать» считают, когда скользящая с меньшим периодом (на рисунке синяя линия) в нижней зоне пересекает снизу вверх скользящую с бо́льшим периодом (красная линия). Сигналом «Продавать» считают, когда скользящая с меньшим периодом в верхней зоне пересекает сверху вниз скользящую с бо́льшим периодом.
Конечно, в жизни не все так просто – и сам по себе индикатор предназначен для недельных и месячных периодов, и объем влияет на принятие решений, и, по хорошему, надо заодно проверить еще пяток индикаторов… Но не будем заморачиваться! Мы хотим научиться понимать тенденцию рынка – давайте научимся.
Значит, первое, что нам нужно сделать, это найти пересечение линий на графике. Можно вспомнить курс математики, а можно воспользоваться возможностями библиотеки numpy – там уже кто-то всё вспомнил, запрограммировал и оптимизировал. Нам, по сути, нужно добавить в код пару строчек:
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import datetime as datetime
PAIR = 'BTC_ETH' # Пара, по которой работаем
start_time = time.time() - 15*60*60 # за какой период брать данные - 15 часов в данном случае
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
xdate=[datetime.datetime.fromtimestamp(item['date']) for item in data]
fig, ax = plt.subplots(3, sharex=True)
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
print(idx)
plt.show()
Запускаем. Кое-где ругнулось, это связано с отсутствием данных в начале, построился тот же график, что и выше, и в консоль вывелись точки пересечения – в начале много, потому что, опять же, мы берем мало данных с биржи.
Эти цифры обозначают положение по оси X, в которых произошли пересечения. Давайте наложим их на график?
Еще немного меняем код:
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import datetime as datetime
PAIR = 'BTC_ETH' # Пара, по которой работаем
start_time = time.time() - 15*60*60 # за какой период брать данные - 15 часов в данном случае
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
xdate=[datetime.datetime.fromtimestamp(item['date']) for item in data]
fig, ax = plt.subplots(3, sharex=True)
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
print(idx)
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
ax[1].plot(inters, 'ro')
plt.show()
Запускаем, и видим, как на графике отображаются точки пересечения:
Похоже на истину, кое-где может быть выглядит не прямо идеально, но тут играют сглаживание линий, позиционирование центра окружности красной точки и т.п. Для наших целей достаточно.
Итак, задача: давайте теперь научимся понимать, когда график растет, а когда падает. Но сначала нужно разобраться с данными, которые мы получаем.
Как это работает?
Уберем (временно) все, что связано с кодом, и пристально взглянем на то, с чем работаем.
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import datetime as datetime
PAIR = 'BTC_ETH' # Пара, по которой работаем
start_time = time.time() - 15*60*60 # за какой период брать данные - 15 часов в данном случае
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
print('=====MACD=======', macd)
print('*'*80)
print('=====MACDSIGNAL=======', macdsignal)
print('*'*80)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
print('======IDX=======',idx)
print('*'*80)
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
print('======INTERS=======',inters)
print('*'*80)
Вот что, примерно, будет выведено у нас в консоль:
Для построения графика мы используем несколько наборов данных. На шкале X мы используем время – для всех графиков в данной статье используется время, которое мы получили с биржи.
В первом графике мы рисуем свечи, используя готовые данные.
На втором мы получаем данные сами, с помощью вызова вот этой функции:
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
Как я писал в прошлой статье, мы в результате получаем три набора данных – macd, macdsignal, macdhist – это три массива значений, каждое из которых является значением для оси Y. Т.е. первый элемент будет отрисован в X(0), второй в X(1), на высоте Y, указанной в самом элементе. На скриншоте выше вы видите, я вывел в консоль полученные значения
=====MACDSIGNAL======= [ nan nan nan nan
nan nan nan nan
nan nan nan nan
nan nan nan nan
nan nan nan nan
nan nan nan nan
nan nan nan nan
nan nan nan nan
nan 7.91964156e-06 4.14538137e-06 2.60110672e-08
-4.07798778e-06 -7.95972541e-06 -1.00486163e-05 -1.19658012e-05
-1.41967407e-05 -1.91172395e-05 -2.54980919e-05 -3.30112736e-05
-4.08438995e-05 -4.82626516e-05 -5.56410407e-05 -6.42993299e-05
-7.47543657e-05 -8.34476354e-05 -9.08057290e-05 -9.68925398e-05
-1.01468395e-04 -1.04801729e-04 -1.06813122e-04 -1.05755025e-04
-1.00109101e-04 -9.20068930e-05 -8.33780421e-05 -7.33571885e-05
Nan – это значит Not a number, и это то, чего не видно на графике (в начале, линии рисуются с отступом). Дальше идут значения – они крутятся вокруг нуля, именно эти значения мы и отрисовываем на графике и используем для анализа. В данном случае первые 33 элемента отрисованы не будут, потом (X=34, Y=7.91964156e-06), (X=35, Y=4.14538137e-06) и т.п.
То же самое справедливо и для macd.
В этой строке:
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
я получаю пересечение значений (или графиков, как удобнее), и получаю на выходе массив индексов.
======IDX======= [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 54 75 86
110 111 114 116 125 154 156 157 161 162]
Что это значит – это значит, если я возьму массив значений macd, то пересечение будет в точках 0, 1, 2, …. 75, 86… 161, 162. То же самое справедливо и для массива macdsignal. Можно сказать и так, что IDX указывает на точки X на графике.
Для отображения пересечений (красных точек на графике) я создаю новый набор данных – он по длине такой же, как macd и macdsignal, но там, где есть пересечение macd и macdsignal, я вставляю значение macd, там где пересечения нет, вставляю nan. Таким образом, я отрисовываю все точки X, но красная точка появится только там, где есть пересечение.
Давайте ловить тренд
Теперь, зная суть вещей, давайте научимся понимать, в какую сторону движется рынок. Вернем отрисовку графиков, и на третьем будем отображать результаты нашего анализа.
В принципе, ничего принципиально нового мы тут не будем изобретать, для начала будем просто сравнивать, какая линия сверху, а какая снизу, и рисовать свой график – растет или падает.
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import datetime as datetime
PAIR = 'BTC_ETH' # Пара, по которой работаем
start_time = time.time() - 15*60*60 # за какой период брать данные - 15 часов в данном случае
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
xdate=[datetime.datetime.fromtimestamp(item['date']) for item in data]
fig, ax = plt.subplots(3, sharex=True)
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
print(idx)
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
ax[1].plot(inters, 'ro')
hist_data = []
max_v = 0
for offset, elem in enumerate(macdhist):
if macd[offset] > macdsignal[offset]: # восходящий тренд
v = 1 #perc
else:
v = -1
hist_data.append(v*1000)
ax[2].fill_between([x for x in range(len(macdhist))], 0, hist_data, facecolor='green', interpolate=True)
plt.show()
Тут наглядно видно, когда график рос, а когда падал. Давайте добавим логику чуть-чуть поумнее.
Мне кажется, боту будет выгоднее торговать в такие моменты:
- Когда график падал, но начал расти (в самом конце падения)
- Когда график начал расти, растет, и не наметил тенденцию к падению (торговать во время роста, прекратить ближе к концу)
Вот как то так (нарисовал в пейнте. Обвел не всё, но самое показательное):
Понятно, что текущая логика с этим не справляется. Значит, надо её поменять!
Вот какой алгоритм пришел мне в голову: Мы берем разницу между двумя линиями MACD, и сравниваем с максимальной разницей линий MACD за период от одного изменения тренда до другого. Каждый раз, когда тренд разворачивается (пересечение линий) мы обнуляем этот максимум в ноль.
Таким образом, после разворота тренда, первая разница между двумя линиями будет максимальной (100% разницей). После этого если линии еще сильнее разойдутся, то уже следующая разница между линиями станет максимальной. В тот момент, когда линии начнут сходиться, разница уже не будет максимальной, а будет убывать (90%, 80%) и т.п.
Поэтому для торгов мы будем использовать две настроечные переменные:
Если рынок бычий (растет) то торгуй, пока разница больше X%. В таком случае он будет торговать на бычьем рынке с момента разворота тренда до тех пор, пока линии не начнут плотно сходиться.
Если рынок медвежий (падает) то торгуй тогда, когда разница между линиями меньше Y%. В таком случае он включится в торговлю только после того как тренд развернется, линии разойдутся на максимум, потом сойдутся почти полностью.
Вот так определим эти переменные:
BEAR_PERC = 70 # При падении рынка
BULL_PERC = 30 # При росте рынка
Каждое из этих значений может колебаться от 0 до 100, не обязательно, что бы они в сумме что-то давали, они независимые.
Давайте внесем изменения в код и посмотрим, что получится:
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import datetime as datetime
PAIR = 'BTC_ETH' # Пара, по которой работаем
BEAR_PERC = 70
BULL_PERC = 10
start_time = time.time() - 15*60*60 # за какой период брать данные - 15 часов в данном случае
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
xdate=[datetime.datetime.fromtimestamp(item['date']) for item in data]
fig, ax = plt.subplots(3, sharex=True)
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
print(idx)
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
ax[1].plot(inters, 'ro')
hist_data = []
max_v = 0
for offset, elem in enumerate(macdhist):
curr_v = macd[offset] - macdsignal[offset]
if abs(curr_v) > abs(max_v):
max_v = curr_v
perc = curr_v/max_v
if ( (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
or (
macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
)
):
v = 1
else:
v = 0
if offset in idx and not numpy.isnan(elem):
# тренд изменился
max_v = curr_v = 0 # обнуляем пик спреда между линиями
hist_data.append(v*1000)
ax[2].fill_between([x for x in range(len(macdhist))], 0, hist_data, facecolor='green', interpolate=True)
plt.show()
Работает. Правда, сработало в середине, там где тренд почти развернулся, но передумал, но это от настроек зависит, если выставить BEAR_PERC = 99, например, то такого не будет, но и на падении он не срубит… А если выставить 100 то будет всегда играть только на росте.. В общем это сами решайте. Но по мне, на графике наглядно видно, что в самые печальные моменты бот торговать не будет)
Окей, окей, но это же прошлые периоды, тут и самому можно смотреть, как сделать так, что бы бот в реальном времени смотрел и делал прогнозы?????
Смотрим в будущее
Давайте, как обычно, изменим код, и сделаем (для демонстрации), советчика – он будет говорить нам, торговать или нет, сейчас, в данную минуту. Вынесем часть кода в функцию, и добавим анимацию.
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from datetime import datetime
BEAR_PERC = 70
BULL_PERC = 30
PAIR = 'BTC_ETH'
fig, ax = plt.subplots(3, sharex=True)
fig.comment = plt.figtext(.7,.05, '')
def update_graph(interval):
start_time = time.time() - 15*60*60
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (PAIR, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['open']=numpy.asarray([item['open'] for item in data])
quotes['close']=numpy.asarray([item['close'] for item in data])
quotes['high']=numpy.asarray([item['high'] for item in data])
quotes['low']=numpy.asarray([item['low'] for item in data])
xdate=[datetime.fromtimestamp(item['date']) for item in data]
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].clear()
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
# print(ax[0].get_xdata())
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].clear()
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
ax[1].plot(inters, 'ro')
#ax[1].scatter(x=ax[0].get_xdata(), y=inters, c='b')
hist_data = []
max_v = 0
for offset, elem in enumerate(macdhist):
activity_time = False
curr_v = macd[offset] - macdsignal[offset]
if abs(curr_v) > abs(max_v):
max_v = curr_v
perc = curr_v/max_v
if ( (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
or (
macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
)
):
v = 1
activity_time = True
else:
v = 0
if offset in idx and not numpy.isnan(elem):
# тренд изменился
max_v = curr_v = 0 # обнуляем пик спреда между линиями
hist_data.append(v*1000)
ax[2].clear()
ax[2].fill_between([x for x in range(len(macdhist))], 0, hist_data, facecolor='green', interpolate=True)
plt.gcf().texts.remove(fig.comment)
fig.comment = plt.figtext(.7,.05, '%s %s%s' % (PAIR, time.ctime(), ' ТОРГУЕМ!!!! ' if activity_time else ''), style='italic', bbox={'facecolor':'red' if activity_time else 'green', 'alpha':0.5, 'pad':10})
ani = animation.FuncAnimation(fig, update_graph, interval=1000)
plt.show()
Теперь наш скрипт каждую секунду (по возможности) получает данные с биржи, строит график и дает нам совет – стоит сейчас торговать или нет. Соответственно, он сам перерисовывает график, и ничего перезапускать не надо.
Прикручиваем к боту
Всё это весело, но смотреть в экран и что-то делать – это не автоматизация. Давайте теперь, используя новые знания, применим их в существующем коде. Начнем с полоникса (раз уж он тут в статье с самого начала).
Poloniex
Уберем графики и всякое такое – нам же важнее ехать, а не шашечки. Сделаем функцию, которая просто принимает данные с биржи, анализирует их, и возвращает решение – торговать или нет.
Убираем все лишнее, и получаем небольшую, скромную функцию, которая возвращает нам все, что надо:
import numpy
import talib
import requests
import json
import time
from datetime import datetime
BEAR_PERC = 70
BULL_PERC = 30
PAIR = 'BTC_ETH'
def should_buy(pair):
start_time = time.time() - 15*60*60
resource = requests.get("https://poloniex.com/public?command=returnChartData¤cyPair=%s&start=%s&end=9999999999&period=300" % (pair, start_time))
data = json.loads(resource.text)
quotes = {}
quotes['close']=numpy.asarray([item['close'] for item in data])
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
hist_data = []
max_v = 0
for offset, elem in enumerate(macdhist):
activity_time = False
curr_v = macd[offset] - macdsignal[offset]
if abs(curr_v) > abs(max_v):
max_v = curr_v
perc = curr_v/max_v
if ( (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
or (
macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
)
):
v = 1
activity_time = True
else:
v = 0
if offset in idx and not numpy.isnan(elem):
# тренд изменился
max_v = curr_v = 0 # обнуляем пик спреда между линиями
hist_data.append(v*1000)
return activity_time
while True:
print("Покупать?", should_buy(PAIR))
time.sleep(1)
Как добавить в код бота для полоникс? В бота на сайте я добавил, вот ссылка на обновленную версию. В какого-то другого – скопируйте функцию, подключите numpy, talib и requests, выставьте параметры BEAR_PERC и BULL_PERC, ну и вызывайте, когда надо.
Exmo
Тут немного сложнее, т.к. Exmo не возвращает архивные данные по торгам… Плохо, придется собирать инфу с биржи, пока не накопится достаточно данных для анализа. Воспользуемся методом API trades. Он возвращает 100 последних торгов, и, судя по документации к API, ничего больше.. Но если не следовать документации, а добавить на свой страх и риск не указанный параметр limit, то можно получать до 10 000 записей :) Пишем функцию:
import numpy
import talib
import requests
import json
import time
from datetime import datetime
BEAR_PERC = 70
BULL_PERC = 30
PAIR = 'BTC_USD'
def should_buy(pair):
resource = requests.get('https://api.exmo.com/v1/trades/?pair=%s&limit=10000' % pair)
data = json.loads(resource.text)
close_prices = {} # сформируем словарь с ценой закрытия по 5 минут
for item in reversed(data[pair]):
d = int(float(item['date'])/300)*300 # Округляем время сделки до 5 минут
close_prices[d] = float(item['price'])
macd, macdsignal, macdhist = talib.MACD(numpy.asarray([close_prices[item] for item in sorted(close_prices)]), fastperiod=12, slowperiod=26, signalperiod=9)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
hist_data = []
max_v = 0
activity_time = False
for offset, elem in enumerate(macdhist):
activity_time = False
curr_v = macd[offset] - macdsignal[offset]
if abs(curr_v) > abs(max_v):
max_v = curr_v
perc = curr_v/max_v
if ( (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
or (
macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
)
):
v = 1
activity_time = True
else:
v = 0
if offset in idx and not numpy.isnan(elem):
# тренд изменился
max_v = curr_v = 0 # обнуляем пик спреда между линиями
hist_data.append(v*1000)
return activity_time
while True:
print("Покупать?", should_buy(PAIR))
time.sleep(1)
Её можно прикрутить, опять же, к любому боту, но я прикрутил к нашему, можете скачать обновленную версию. Здесь бот будет покупать реже, но уже с некоторым проблеском интеллекта.
Теперь о том, как строить MACD для эксмо
Давайте наш скрипт модифицируем так, что бы видеть MACD линии и гистограмму для торгов на эксмо, оставив логику советчика - покупать или нет. График советчика я заменил на гистограмму, для полноты картины. Так же добавил в скрипт переменную PERIOD, это время в минутах, за которое строим свечи. Для демострации, я выставил 30 минут - именно так строит эксмо, и так вы можете сравнить точность работы скрипта. Но я советую поменять вам это число на 5 - тогда вы сможете видеть пятиминутные свечи, более оперативно оценивать обстановку, и, возможно, получите преимущество перед теми, кто торгует на эксмо через родной интерфейс биржи.
Вот код:
import numpy
import talib
import requests
import json
import time
from matplotlib.finance import candlestick2_ohlc
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from datetime import datetime
BEAR_PERC = 70
BULL_PERC = 30
PERIOD = 5 # Период в минутах для построения свечей
PAIR = 'BTC_USD'
SHOW_BOT_CHART = False
fig, ax = plt.subplots(3, sharex=True)
fig.comment = plt.figtext(.7,.05, '')
def update_graph(interval):
resource = requests.get('https://api.exmo.com/v1/trades/?pair=%s&limit=10000' % PAIR)
print(resource.text[:100])
#return
data = json.loads(resource.text)
chart_data = {} # сформируем словарь с ценой закрытия по PERIOD минут
for item in reversed(data[PAIR]):
d = int(float(item['date'])/(PERIOD*60))*(PERIOD*60) # Округляем время сделки до PERIOD минут
if not d in chart_data:
chart_data[d] = {'open':0, 'close':0, 'high':0, 'low':0}
chart_data[d]['close'] = float(item['price'])
if not chart_data[d]['open']:
chart_data[d]['open'] = float(item['price'])
if not chart_data[d]['high'] or chart_data[d]['high'] < float(item['price']):
chart_data[d]['high'] = float(item['price'])
if not chart_data[d]['low'] or chart_data[d]['low'] > float(item['price']):
chart_data[d]['low'] = float(item['price'])
quotes = {}
quotes['open']=numpy.asarray([chart_data[item]['open'] for item in sorted(chart_data)])
quotes['close']=numpy.asarray([chart_data[item]['close'] for item in sorted(chart_data)])
quotes['high']=numpy.asarray([chart_data[item]['high'] for item in sorted(chart_data)])
quotes['low']=numpy.asarray([chart_data[item]['low'] for item in sorted(chart_data)])
xdate=[datetime.fromtimestamp(item) for item in sorted(chart_data)]
ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
def chart_date(x,pos):
try:
return xdate[int(x)]
except IndexError:
return ''
ax[0].clear()
ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
fig.autofmt_xdate()
fig.tight_layout()
macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
ax[1].clear()
ax[1].plot(macd, color="y")
ax[1].plot(macdsignal)
idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
inters = []
for offset, elem in enumerate(macd):
if offset in idx:
inters.append(elem)
else:
inters.append(numpy.nan)
ax[1].plot(inters, 'ro')
max_v = 0
hist_data = []
for offset, elem in enumerate(macdhist):
activity_time = False
curr_v = macd[offset] - macdsignal[offset]
if abs(curr_v) > abs(max_v):
max_v = curr_v
perc = curr_v/max_v
if ( (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
or (
macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
)
):
v = 1
activity_time = True
else:
v = 0
if offset in idx and not numpy.isnan(elem):
# тренд изменился
max_v = curr_v = 0 # обнуляем пик спреда между линиями
hist_data.append(v*1000)
ax[2].fill_between([x for x in range(len(macdhist))], 0, hist_data if SHOW_BOT_CHART else macdhist, facecolor='gray', interpolate=True)
plt.gcf().texts.remove(fig.comment)
fig.comment = plt.figtext(.6,.05, '%s %s%s' % (PAIR, time.ctime(), ' ТОРГУЕМ!!!! ' if activity_time else ''), style='italic', bbox={'facecolor':'red' if activity_time else 'green', 'alpha':0.5, 'pad':10})
ani = animation.FuncAnimation(fig, update_graph, interval=1000)
plt.show()
Вот результат:
О том, как установить библиотеки для запуска, расписано в этой и этой статье, о том, как работать с ботом и всё такое в этой. Ниже под этой статьёй есть ссылки на все статьи цикла, советую прочитать все :)
Удачи вам в торговле и быстрых заработков, пишите в комментариях о результатах работы и найденных недочетах, всем пока!
Но ошибка при старте бота так и есть.
Есть вопросы: ссылка на данные в коде на фолоникс, эта ссылка должны быть и для биржи эксмо?)
И правильно ли я понимаю, что достаточно установить базу талиб и скачать обновленную версию бота?
Так же в обновленной версии ORDER_LIFE_TIME = 0.5, тобишь ордер живет пол минуты?
Можете у себя удалить, ничего не изменится)
.Дополнил статью, выложил пример.
Мы можем получить только 10 000 последних торгов.
Если для ZEC_RUB 10 000 это грубо говоря три дня, то для ходовой пары BTC_USD час-два.. Боту для принятия решения такого набора данных хватит, а вот красивый график из этого не построишь.
Поставьте период в 5 минут - будут и точки, и графики
1. Бот очень хорошо работает на биткоинах (за ночь при игре на 8 долларах поднял 30 центов), но на альтах при игре на понижение он часто просчитывается и потом не может продать. Думаю, если прикрутить какой-нибудь скрипт, который при игре на альтах учитывает ещё и курс битка (от которого очень сильно альты зависят), он сможет так же хорошо торговать, как и на битках
2. Бот, как уже сказал, изредка выставляет слишком высокие цены за криптовалюту, когда в альтах
3. Было бы интересно посмотреть, как бот будет работать при учете нескольких индикаторов (например, облака Ишимоку Кинко Хё), и будет ли от этого увеличиваться точность
Ну да, чем осторожнее бот, тем меньше сделок (удобных ситуаций на рынке по паре), нарастить можно, увеличив количество пар. Вот тут в конце статьи есть отчет по работе подобного бота на другой бирже https://bablofil.ru/bot-dlya-birgi-bittrex/.
Еще один вопрос, если можно.
Можно ли майнить на DigitalOcean.com сервере через VPS на базе Ubuntu?
Попробуйте переустановить matplotlib
Если создался ордер на покупку при существующем ордере на продажу, то да, какой то глюк)
Я так понял , что бот если видит в данный момент , что можно брать, то покупает еще . Пока вроде динамика нормальная. Подскажите, как в боте для poloniex добавить пары? По аналогии с ботом exmo не работает, как только не пробовал... Или только разных ботов по каждой паре?
Вроде написано пары... Подскажите пожалуйста. Спасибо.
# Пары, по которым собираемся торговать
PAIRS = {
'BTC_VTC' : {
'ORDER_AMOUNT': '0.001', # Сколько валюты 1 использовать в ордере ( в данном случае, 0.002 Btc),
'ORDER_LIFE_TIME': 3, # через сколько минут отменять неисполненный ордер на покупку CURR_1
'PROFIT_MARKUP_DOWN': 0.001, # Какой навар нужен с каждой сделки при покупке (поверх комиссии)? (0.001 = 0.1%). Можно ставить 0
'PROFIT_MARKUP_UP': 0.002, # Какой навар нужен с каждой сделки при продаже (поверх комиссии)? (0.002 = 0.2%)
'MED_PRICE_PERIOD': 20, # За какой период брать среднюю цену (в минутах)
},
Про Полоникс - спасибо, действительно была недоработка, поправил - скачайте код по новой.
Про эксмо смотрите комменты, как обойти запреты https://bablofil.ru/bot-dlya-birjy-exmo/
Про полоникс я выкладывал как использовать прокси, тоже в комментариях https://bablofil.ru/bot-dlya-birjy-poloniex
Я думал -это Ваш бот, но доработанный на торговлю несколькими парами.
А как в нем ставить ограничение, чтобы по одной паре выставлялись только ордера на сумму не превышающие сумму заданную в этом параметре?
CAN_SPEND = 20 (например)
У меня он как раз сейчас в паре DASH_USD подвесил все же купленные на пике 3 ордера . Я так понимаю, что именно из-за отсутствия ограничения. С ограничением был бы один наверно. До падения курсов последнего работал очень даже не плохо, профит около 3% в день составил.
Или талиб встал как-то криво.
Попробуйте снести все, что ставили - и поставьте питон для всех пользователей - вот все галочки прям, при установке выберите расширенный режим, там все галочки.
Потом скачайте whl файл, и установите его.
Должно работать.
Ну и если не будет, на диск C скачайте и распакуйте талиб
Специально взял несколько скриптов со статьи, запустил, графики построились.
Если вы видите какие-то матюки, то выкладывайте - может быть версия питона не та, либо с часовым поясом заморочки, либо сетевые какие-то вопросы.
Строка со сдвигом по времени от биржи не нужна (вернее нужно ставить STOCK_SHIFT = 0), т.к. вы используете время unix а оно согласуется с UTC также как и биржа.
Рынок в медвежьей фазе и не растет, бот не покупает..
BEAR_PERC = 99.9
BULL_PERC = 99.9
BEAR_PERC = 1
BULL_PERC = 1
BEAR_PERC = 1
BULL_PERC = 99.9
BEAR_PERC = 99.9
BULL_PERC = 1
при таких настройках как будет выглядеть работа бота, в какие моменты он будет торговать?
Берите любой и запускайте
Спасибо огромное!
Подскажи пожалуйста, как убрать в индикаторе "грязь"
[url=https://radikal.ru][img]https://a.radikal.ru/a41/1802/62/7d9a3bcbf86f.png[/img][/url]
Из за чего ложные срабатывания бота. Хотелось бы желтая линия сверху однозначно торговать, снизу не торговать, т.е. переключение строго по красным точкам ???
https://a.radikal.ru/a41/1802/62/7d9a3bcbf86f.png
"Грязь" это то что между второй и третьей точкой справа, провал на уровне возле 150.
В боте из за такого идет ложное срабатывание.
Честно говоря не знаю, а что бы человек сделал, если бы видел, что macd сближаются и намечается разворот?
Как вариант можно страховать другим индикатором, RSI например, и решать на основе двух значений.
Если с ним мудрить - покупать руками и подсовывать валюту то да, последствия будут соответствующими, и это не ошибка.
Предлагаю вопросы, не относящиеся к теме статьи, обсуждать на форуме)
В принципе в боте уже есть свечи и цены их закрытия, вам всего лишь надо перенести функцию update_graph и передавать ей на вход данные свечей. Но это надо тут поменять, там прописать..
https://bablofil.ru/static/macd_bots/poloniex_macd.py
https://bablofil.ru/static/macd_bots/exmo_macd.py
функция анализа по MACD и прогнозирование тренда уже реализованы и боты сами себе советчики, сами размещают ордера, просто делают это молча без визуализации? А советчик с графиками работает независимо от бота и служит исключительно для визуализации работы бота и демонстрации его логики?
Да, всё именно так.
- есть CURRENCY_1, а CURRENCY_2 нет;
- есть CURRENCY_1 и CURRENCY_2 для закупки CURRENCY_1_MIN_QUANTITY ;
Вопрос сводится к тому, будет ли бот сливать CURRENCY_1, если его запустить при указанных условиях т.е. продаст ли он всю имеющуюся CURRENCY_1 и по какому курсу.
Практически полный алгоритм расписан тут https://bablofil.ru/bot-dlya-birjy-exmo/, даже есть блок-схема - вот прямая ссылка https://bablofil.ru/media/uploads/2017/04/11/bot1.png
- есть CURRENCY_1, а CURRENCY_2 нет;
Бот продаст CURRENCY_1 на сумму CAN_SPEND + PROFIT
- есть CURRENCY_1 и CURRENCY_2
Бот продаст CURRENCY_1 на сумму CAN_SPEND + PROFIT
В любом случае, если есть что продать, бот сначала продаст.
Потом купит и снова вернется к предыдущему шагу, если вкратце.
CURRENCY_1_MIN_QUANTITY - это не сумма торгов, а минимальный размер лота на бирже, биржа не даст купить/продать меньше, чем CURRENCY_1_MIN_QUANTITY
Т е покупаем при переходе гистограммы из "-" в "+", продаем в обратном порядке
Тут ошибочка. Бот продаст CURRENCY_1 всю.
Есть форум, там можно создать любую тему
Сделайте так - запустите cmd, там напишите python и путь к файлу, и посмотрите, что пишет
Спасибо.
Будет ли разобран пример с ботом для wex.nz?
Не совсем понимаю вопроса, бот для wex выложен - https://bablofil.ru/bot-dlya-birgi-wex/, алгоритм и прочее примерно как тут.
Уточните, что именно нужно разобрать?
Насчет проверок все очень просто - код (покупок/продаж) не вызовется, пока не доработает предыдущий кусок кода. Т.е. индикатор может как угодно моргать, скрипеть, жужжать, но когда он прекратит и даст последние (актуальные) значения, вся процессия двинется дальше.
KDJ я не реализовывал, но глянул краем глаза, это пересечения нескольких скользящих (*MA). Скользящюю среднюю вы можете строить и без каких-либо библиотек, можете взять готовые из Talib, вызывать с разными параметрами и проверять на пересечение.
Открытых ордеров нет
buy 0.0005675477515121432 8809.83139599844
Выход, не хватает денег на создание ордера
Открытых ордеров нет
Номер два пишет что нет какой то библиотеки хот я выполнял все действия что били описаны. Вы не могли бы подсказать что я неправильно делаю. Я понимаю что не по теме просто не знаю где еще можно спросить совета. Заранее признателен.
Спасибо за ответ, попробую увеличить сумму . И ещё раз внимательно изучу статью.
Изменил сумму указал 15 дол. вот что пишет:
Выход, продолжаем надеяться купить валюту по указанному ранее курсу, со времени создания ордера прошло 79.48328137397766 секунд
Проверяем, что происходит с отложенным ордером 778802291
Частично исполненных ордеров нет
Изменения вносил в эту строку CAN_SPEND = 15 # Сколько тратить CURRENCY_2 каждый раз при покупке CURRENCY_1
По MACD - как вариант можно брать не свечи, а истории сделок.
Из этих историй сделок формировать свои свечи, например 11-секундные (просто для примера)
Т.е. берете за 11 секунд все сделки, самая большая цена за период будет high, самая низкая - low, самая ранняя open, самая поздняя - close, объемы торгов складываете... Если сделок за период не было берете значение предыдущей, как на многих биржах ставят. В итоге у вас будет МНОГО свечей :)
Правда, нужно учитывать, что чем короче свечи тем чаще ошибается macd..
Вообще я пару раз писал в саппорт что бы добавили полноценные свечи в апи, но там кивают да да, и ниче не делается)
my_need_price = avg_price - avg_price * (STOCK_FEE+PROFIT_MARKUP)
my_amount = CAN_SPEND/my_need_price
на
# my_need_price = avg_price - avg_price * (STOCK_FEE+PROFIT_MARKUP)
my_need_price = round((avg_price - avg_price * (STOCK_FEE+PROFIT_MARKUP)), 8)
# my_amount = CAN_SPEND/my_need_price
my_amount = round((CAN_SPEND/my_need_price), 8)
Всё вроде пошло работать дальше.
Аналогично исправил чуть код и при продаже.