В одной из предыдущих статей мы разобрали использование библиотеки ta-lib и построение индикаторов. Вот ссылка для освежения памяти. Теперь давайте попробуем получить из этого практическую пользу – научимся анализировать 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()
Теперь наш скрипт каждую секунду (по возможности) получает данные с биржи, строит график и дает нам совет – стоит сейчас торговать или нет. Соответственно, он сам перерисовывает график, и ничего перезапускать не надо.
Всё это весело, но смотреть в экран и что-то делать – это не автоматизация. Давайте теперь, используя новые знания, применим их в существующем коде. Начнем с полоникса (раз уж он тут в статье с самого начала).
Уберем графики и всякое такое – нам же важнее ехать, а не шашечки. Сделаем функцию, которая просто принимает данные с биржи, анализирует их, и возвращает решение – торговать или нет.
Убираем все лишнее, и получаем небольшую, скромную функцию, которая возвращает нам все, что надо:
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 не возвращает архивные данные по торгам… Плохо, придется собирать инфу с биржи, пока не накопится достаточно данных для анализа. Воспользуемся методом 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 линии и гистограмму для торгов на эксмо, оставив логику советчика - покупать или нет. График советчика я заменил на гистограмму, для полноты картины. Так же добавил в скрипт переменную 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()
Вот результат:
О том, как установить библиотеки для запуска, расписано в этой и этой статье, о том, как работать с ботом и всё такое в этой. Ниже под этой статьёй есть ссылки на все статьи цикла, советую прочитать все :)
Удачи вам в торговле и быстрых заработков, пишите в комментариях о результатах работы и найденных недочетах, всем пока!