Руководство: как использовать Python для алгоритмической торговли на бирже. Часть 1. Python биржа


100 строк Python-кода: Автоматизируем биржевую торговлю

Если вы знакомы с финансовым рынком и владеете Python, вы можете легко автоматизировать финансовую торговлю.

Алгоритмическая торговля

Алгоритмическая торговля (алготрейдинг, algorithmic trading) – это автоматизированная с помощью компьютерных средств торговля финансовыми инструментами на основе определенного алгоритма или правила с незначительным участием или без участия человека. Торговать в автоматическом режиме можно почти любыми финансовыми инструментами: акциями, валютами, сырьем, кредитными продуктами или волатильностью. В некоторых сегментах рынка львиная доля сделок совершается именно алгоритмами. Книги «Кванты» («The Quants») Скотта Паттерсона (Scott Patterson) и «More Money Than God» Себастиана Маллаби (Sebastian Mallaby) дают хорошее представление об алгоритмической торговле и личностях, стоявших у ее истоков.

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

  • • Программное обеспечение с открытым исходным кодом. Все инструменты, необходимые трейдеру, чтобы начать алгоритмическую торговлю, доступны под свободными лицензиями. В частности, Python и его экосистема приобрели статус стандарта в этой области.

  • • Открытые источники данных. Появляется все больше открытых источников ценных данных. Благодаря этому, трейдеры получают широкие возможности для проверки гипотез и тестирования торговых стратегий.

  • • Торговые онлайн-платформы. В настоящее время существует множество онлайн-платформ, которые предоставляют простой стандартизированный доступ к историческим данным (посредством RESTful API), данным реального времени (посредством socket API), а также обеспечивают широкий спектр средств для торговли и работы с портфелями (посредством программного API).

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

Рассмотрим основные составляющие проекта:

  • • Стратегия. Мы выбрали моментум-стратегию (momentum strategy), представленную в публикации Moskowitz et al. «Time Series Momentum», 2012. В рамках данной стратегии мы предполагаем, что финансовый инструмент, демонстрировавший в прошлом определенную (позитивную или негативную) тенденцию, в дальнейшем будет следовать этой же тенденции.

  • • Платформа. Мы остановили свой выбор на платформе Oanda. Данная платформа позволяет торговать различные контракты на разницу цен (contract for difference, CFD), что, по сути, позволяет оперировать широким спектром финансовых инструментов, таких как валюты, фондовые индексы, сырье и др.

  • • Данные. Все исторические данные и данные реального времени для нас обеспечит платформа Oanda.

  • • Программное обеспечение. Мы будем использовать Python, мощную аналитическую библиотеку Pandas, а также несколько дополнительных библиотек.

В дальнейшем мы предполагаем, что у вас установлен Python 3.5 и основные библиотеки, такие как NumPy и Pandas. Если у вас еще нет этих средств, вы можете установить все необходимое, используя, например, дистрибутив Anaconda.

Аккаунт Oanda

На платформе Oanda (http://oanda.com) любой желающий может бесплатно зарегистрировать демо аккаунт, обеспечивающий доступ к имитации торгового процесса. После того, как аккаунт зарегистрирован, чтобы получить программный доступ к Oanda API, необходимо установить соответствующую Python-библиотеку:

pip install oandapy

Перед началом работы с библиотекой необходимо создать файл конфигурации oanda.cfg со следующим содержимым:

[oanda] account_id = YOUR_ACCOUNT_ID access_token = YOUR_ACCESS_TOKEN

Укажите в файле конфигурации ваш идентификатор и токен, значения которых вы можете узнать в своем аккаунте.

Выполнив следующий код, мы получаем основной объект для программного взаимодействия с платформой:

In [1]: import configparser # 1 import oandapy as opy # 2 config = configparser.ConfigParser() # 3 config.read('oanda.cfg') # 4 oanda = opy.API(environment='practice', access_token=config['oanda']['access_token']) # 5

У нас уже есть все необходимое, чтобы начать тестирование моментум-стратегии. В частности, мы можем получить исторические данные, предоставляемые платформой. Мы будем использовать инструмент EUR_USD, основанный на обменном курсе EUR/USD.

Первым делом мы загружаем набор исторических данных и преобразуем его в DataFrame. Мы получаем дынные за два дня: 8 и 9 декабря 2016 года. Дискретность данных составляет 1 минуту. Выполним следующий код:

In [2]: import pandas as pd # 6 data = oanda.get_history(instrument='EUR_USD', # our instrument start='2016-12-08', # start data end='2016-12-10', # end date granularity='M1') # minute bars # 7 df = pd.DataFrame(data['candles']).set_index('time') # 8 df.index = pd.DatetimeIndex(df.index) # 9 df.info() # 10

В результате получим подробную характеристику набора данных:

<class 'pandas.core.frame.DataFrame'> DatetimeIndex: 2658 entries, 2016-12-08 00:00:00 to 2016-12-09 21:59:00 Data columns (total 10 columns): closeAsk 2658 non-null float64 closeBid 2658 non-null float64 complete 2658 non-null bool highAsk 2658 non-null float64 highBid 2658 non-null float64 lowAsk 2658 non-null float64 lowBid 2658 non-null float64 openAsk 2658 non-null float64 openBid 2658 non-null float64 volume 2658 non-null int64 dtypes: bool(1), float64(8), int64(1) memory usage: 210.3 KB

Далее мы формализуем моментум-стратегию, вычисляя для каждого момента времени среднее логарифма доходности (mean log return) за последние 15, 30, 60 и 120 минут. Например, среднее логарифма доходности за последние 15 минут – это среднее 15 последних значений логарифма доходности. Если эта величина положительна, мы играем на повышение (go/stay long), если отрицательна – на понижение (go/stay short). Чтобы не усложнять код, мы полагаемся лишь на значение столбца closeAsk.

In [3]: import numpy as np # 11 df['returns'] = np.log(df['closeAsk'] / df['closeAsk'].shift(1)) # 12 cols = [] # 13 for momentum in [15, 30, 60, 120]: # 14 col = 'position_%s' % momentum # 15 df[col] = np.sign(df['returns'].rolling(momentum).mean()) # 16 cols.append(col) # 17

Затем, чтобы вычислить абсолютную результативность моментум-стратегий, основанных на различных временных интервалах, необходимо умножить доходность на величины, полученные выше (предварительно выполнив сдвиг). Вот как мы это сделаем:

In [4]: %matplotlib inline import seaborn as sns; sns.set() # 18 strats = ['returns'] # 19 for col in cols: # 20 strat = 'strategy_%s' % col.split('_')[1] # 21 df[strat] = df[col].shift(1) * df['returns'] # 22 strats.append(strat) # 23 df[strats].dropna().cumsum().apply(np.exp).plot() # 24

Получим следующую диаграмму:

Проанализировав диаграмму, мы видим, что в течение рассматриваемого периода, сам инструмент имеет отрицательную доходность около -2%. Моментум-стратегия, основанная на 120-минутных интервалах, показывает наилучший результат, демонстрируя положительную доходность около 1.5% (без учета разницы между спросом и предложением (bid/ask spread)). По сути, данная стратегия показывает «реальную альфу»: она обеспечивает положительную доходность даже тогда, когда сам инструмент имеет отрицательную доходность.

Автоматическая торговля

Выбрав торговую стратегию, мы можем полностью автоматизировать торговые операции. Чтобы ускорить процесс, мы используем данные с дискретностью 5 секунд, вместо 1 минуты, как было при тестировании. Автоматизировать торговлю можно с помощью одного достаточно компактного класса:

In [5]: class MomentumTrader(opy.Streamer): # 25 def __init__(self, momentum, *args, **kwargs): # 26 opy.Streamer.__init__(self, *args, **kwargs) # 27 self.ticks = 0 # 28 self.position = 0 # 29 self.df = pd.DataFrame() # 30 self.momentum = momentum # 31 self.units = 100000 # 32 def create_order(self, side, units): # 33 order = oanda.create_order(config['oanda']['account_id'], instrument='EUR_USD', units=units, side=side, ENGINE='market') # 34 print('\n', order) # 35 def on_success(self, data): # 36 self.ticks += 1 # 37 # print(self.ticks, end=', ') # appends the new tick data to the DataFrame object self.df = self.df.append(pd.DataFrame(data['tick'], index=[data['tick']['time']])) # 38 # transforms the time information to a DatetimeIndex object self.df.index = pd.DatetimeIndex(self.df['time']) # 39 # resamples the data set to a new, homogeneous interval dfr = self.df.resample('5s').last() # 40 # calculates the log returns dfr['returns'] = np.log(dfr['ask'] / dfr['ask'].shift(1)) # 41 # derives the positioning according to the momentum strategy dfr['position'] = np.sign(dfr['returns'].rolling( self.momentum).mean()) # 42 if dfr['position'].ix[-1] == 1: # 43 # go long if self.position == 0: # 44 self.create_order('buy', self.units) # 45 elif self.position == -1: # 46 self.create_order('buy', self.units * 2) # 47 self.position = 1 # 48 elif dfr['position'].ix[-1] == -1: # 49 # go short if self.position == 0: # 50 self.create_order('sell', self.units) # 51 elif self.position == 1: # 52 self.create_order('sell', self.units * 2) # 53 self.position = -1 # 54 if self.ticks == 250: # 55 # close out the position if self.position == 1: # 56 self.create_order('sell', self.units) # 57 elif self.position == -1: # 58 self.create_order('buy', self.units) # 59 self.disconnect() # 60

Следующий фрагмент кода запускает класс MomentumTrader на выполнение. Расчет моментум-стратегии выполняется на основе интервалов по 12 наблюдений. Класс автоматически прекращает торговлю после получения 250 блоков данных. Это значение выбрано произвольно, чтобы быстро продемонстрировать работу класса MomentumTrader.

In [6]: mt = MomentumTrader(momentum=12, environment='practice', access_token=config['oanda']['access_token']) mt.rates(account_id=config['oanda']['account_id'], instruments=['DE30_EUR'], ignore_heartbeat=True)

Вывод, представленный ниже, показывает отдельные торговые операции, выполняемые классом MomentumTrader, в процессе демонстрации:

{'price': 1.04858, 'time': '2016-12-15T10:29:31.000000Z', 'tradeReduced': {}, 'tradesClosed': [], 'tradeOpened': {'takeProfit': 0, 'id': 10564874832, 'trailingStop': 0, 'side': 'buy', 'stopLoss': 0, 'units': 100000}, 'instrument': 'EUR_USD'} {'price': 1.04805, 'time': '2016-12-15T10:29:46.000000Z', 'tradeReduced': {}, 'tradesClosed': [{'side': 'buy', 'id': 10564874832, 'units': 100000}], 'tradeOpened': {'takeProfit': 0, 'id': 10564875194, 'trailingStop': 0, 'side': 'sell', 'stopLoss': 0, 'units': 100000}, 'instrument': 'EUR_USD'} {'price': 1.04827, 'time': '2016-12-15T10:29:46.000000Z', 'tradeReduced': {}, 'tradesClosed': [{'side': 'sell', 'id': 10564875194, 'units': 100000}], 'tradeOpened': {'takeProfit': 0, 'id': 10564875229, 'trailingStop': 0, 'side': 'buy', 'stopLoss': 0, 'units': 100000}, 'instrument': 'EUR_USD'} {'price': 1.04806, 'time': '2016-12-15T10:30:08.000000Z', 'tradeReduced': {}, 'tradesClosed': [{'side': 'buy', 'id': 10564875229, 'units': 100000}], 'tradeOpened': {'takeProfit': 0, 'id': 10564876308, 'trailingStop': 0, 'side': 'sell', 'stopLoss': 0, 'units': 100000}, 'instrument': 'EUR_USD'} {'price': 1.04823, 'time': '2016-12-15T10:30:10.000000Z', 'tradeReduced': {}, 'tradesClosed': [{'side': 'sell', 'id': 10564876308, 'units': 100000}], 'tradeOpened': {'takeProfit': 0, 'id': 10564876466, 'trailingStop': 0, 'side': 'buy', 'stopLoss': 0, 'units': 100000}, 'instrument': 'EUR_USD'} {'price': 1.04809, 'time': '2016-12-15T10:32:27.000000Z', 'tradeReduced': {}, 'tradesClosed': [{'side': 'buy', 'id': 10564876466, 'units': 100000}], 'tradeOpened': {}, 'instrument': 'EUR_USD'}

На рисунке ниже показано приложение Oanda fxTrade Practice, где мы видим класс MomentumTrader в действии.

Все результаты, представленные в данной статье, получены с помощью демонстрационного аккаунта, в котором не используются настоящие деньги. Этот аккаунт является симулятором для пробной реализации алгоритмической торговли. Чтобы перейти к реальным операциям с реальными деньгами, необходимо настроить полноценный аккаунт Oanda, внести необходимые средства, и изменить параметры аккаунта в коде. Сам код изменять не нужно.

Заключение

В данной статье мы рассмотрели быстрый старт в алгоритмической торговле, для которого требуется менее 100 строк Python-кода. Мы обсудили все основные этапы реализации подобных проектов: получение исторических данных для тестирования, тестирование стратегии, автоматизация торговых операций на основе выбранной стратегии. Представленный код является отправной точкой, откуда можно двигаться в различных направлениях. Например, можно использовать различные стратегии, задействовать различные инструменты или работать с несколькими инструментами одновременно.

О популярности алгоритмической торговли свидетельствует появление различных типов торговых платформ. Например, создатели Quantopian – онлайн-платформы для тестирования стратегий алгоритмической торговли – сообщили в конце 2016 года о привлечении более 100000 пользователей. Торговые онлайн-платформы, подобные Oanda, а также специализирующиеся на криптовалюте, такие как Gemini, позволяют очень быстро начать торговлю на реальном рынке, присоединившись к тысячам трейдеров, живущих во всех точках земного шара.

datareview.info

как использовать Python для алгоритмической торговли на бирже. Часть 1 / Блог компании ITI Capital / Хабр

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

Среди наиболее популярных в сфере финансов языков программирования можно отметить R и Python, также часто используются C++, C# и Java. В опубликованном на сайте DataCamp руководстве речь идет о том, как начать использовать Python для создания финансовых приложений — мы представляем вам серию статей-адаптаций глав этого материала.

Структура руководства:

  • Первая часть предназначена для новичков на рынке, в ней речь пойдет об устройстве финансовых рынков, акциях и торговых стратегиях, данных временных рядов, а также о том, что понадобится для начала разработки.
  • Во второй части будет представлено введение в работу с данными временных рядов и инструментами финансового анализа, вроде подсчета волатильности и скользящих средних, с помощью Python-библиотеки Pandas.
  • Затем мы перейдем к непосредственной разработке простой импульсной торговой стратегии.
  • В четвертой части речь пойдет о том, как проводить бэктест стратегии на исторических данных.
  • В завершение, будут затронуты вопросы оптимизации стратегии для повышения ее производительности, а также оценки ее работы и надежности.

Введение: простым языком об устройстве сферы финансов

Прежде чем окунуться в мир торговых стратегий, имеет смысл коснуться базовых понятий. Однако это не означает, что то, о чем пойдет речь ниже, рассчитано совсем на новичков. Будет здорово, если вы предварительно ознакомитесь с курсом по использованию Python для работы с данными, а также представляете себе, как работать со списками и пакетами Python, а также хотя бы на базовом уровне знакомы с NumPy и Pandas.

Акции и торговля на бирже

Когда компания хочет продолжить развитие бизнеса, запускать новые проекты или расширяться, то в качестве инструмента финансирования может использовать акции. Акция представляет долю во владении компании, акции обменивают на деньги. Акции можно покупать и продавать: участники таких сделок проводят операции с уже существующими, ранее выпущенными акциями.

Цена, по которой конкретная акция будет продана или куплена, может постоянно меняться, вне зависимости от бизнес-показателей выпустившей акции компании: все определяется спросом и предложением. Важно понимать различие между акциями и, например, облигациями (бондами), которые используются для привлечения именно заемных средств.

Когда речь идет о трейдинге, то рассматриваться может не только продажа и покупка акций — сделка может быть заключена по разным активам, в числе которых могут быть как финансовые инструменты, так и, например, драгоценные металлы или ресурсы вроде нефти.

При покупке акций инвестор получает определенную долю в компании, из чего может в будущем извлечь финансовую выгоду, продав эту долю. Стратегии могут различаться: существуют длинные сделки (long), заключаемые в надежде на дальнейший рост акций, и короткие, когда инвестор предполагает, что акции будут дешеветь, поэтому продает акции в надежде в будущем «откупить» их обратно по более низкой цене.

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

Торговые стратегии обычно проверяют с помощью бэктестинга: это подход, при которым стратегия «прогоняется» на исторических данных о торгах — на их основе программа генерирует сделки. Это позволяет понять, принесла бы такая стратегия доход при том развитии рыночной ситуации, что наблюдалось в прошлом. Таким образом, можно предварительно оценить перспективность стратегии в торгах в режиме реального времени. При этом, нет гарантий того, что хорошие показатели на исторических данных повторятся при работе на реальном рынке.

Данные временных рядов

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

По оcи X расположены даты, а цена — на оси Y. «Последовательные равные промежутки времени» в данном случае означает, что по временной оси даты расположены с двухнедельным интервалом: можно сравнить 3/7/2005 и 3/31/2005, а также 4/5/2005 и 4/19/2005 (здесь даты записаны в принятом в США формате, когда сначала идет месяц, а затем день).

Однако финансовые данные обычно включают не два параметра (цена и дата), а пять — помимо величины торгового периода, это цена открытия торгового периода, наивысшая и низшая цена в его рамках, а также цена в момент закрытия периода. Это значит, что если мы рассматривает дневной период, то анализ данных даст нам информацию о том, на каком уровне находилась цена в момент старта и завершения торгов в выбранный день, а также какой была максимальная и минимальная цена в ходе торгов.

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

Основы Python для сферы финансов: Pandas

Одним из самых востребованных инструментов при использовании Python для разработки финансовых приложений является пакет Pandas. Он нужен уже в самом начале, но по мере углубления в процесс разработки понадобятся и такие пакеты как NumPy, SciPy, Matplotlib.

Для начала сфокусируемся на Pandas и применении этого инструмента к анализу временных рядов. Ниже речь пойдет о том, как с помощью данного пакета импортировать данные, анализировать и манипулировать ими.

Импорт финансовых данных

Пакет pandas-datareader позволяет получать данные из таких источников, как Google, Yahoo! Finance или Всемирный банк — подробнее о доступных источниках данных написано в документации. В этом руководстве будет рассматриваться получение данных с сервиса Yahoo! Finance. Для начала работы необходимо установить последнюю версию пакета с помощью pip:pip install pandas-datareader Инструкции по установке версии в разработке представлены здесь.import pandas_datareader as pdr import datetime aapl = pdr.get_data_yahoo('AAPL', start=datetime.datetime(2006, 10, 1), end=datetime.datetime(2012, 1, 1)) Не так давно в Yahoo API произошли изменения, так что для начала самостоятельной работы с библиотекой нужно установить исправления, который позволит дождаться официального патча. Подробнее проблема описана здесь. Однако для этого руководства данные были скачаны заранее, так что проблем с его изучением не возникнет.

Важно также понимать, что несмотря на то, что pandas-datareader — это удобный инструмент для загрузки данных, он далеко не единственный для Python. Также можно использовать библиотеки вроде Quandl, которая позволяет получать данные с сервиса Google Finance:

import quandl aapl = quandl.get("WIKI/AAPL", start_date="2006-10-01", end_date="2012-01-01") Также многим известно, что в сфере финансов для анализа данных очень популярен Excel. Для удобства будущей работы можно интегрировать этот инструмент с Python (подробнее по ссылке).

Работа с данными временных рядов

Для импортирования данных мы использовали pandas_datareader. В результате возник объект aapl — это DataFrame, то есть двумерная именованная структура данных со столбцами потенциально разных типов. Первое, что следует сделать при работе с таким фреймом — запустить функции head() и tail() для того, чтобы взглянуть на первый и последний столбцы датафрейма. Для получения полезной статистической сводки по скачанным данным можно воспользоваться функцией describe().

Пример этого кода можно найти на странице исходного материала.

Данные содержат четыре столбца с ценой открытия и закрытия торгового периода, а также максимальной и минимальной ценой — мы рассматриваем дневные интервалы и акции Apple. Также мы получаем два дополнительных столбца: Volume и Adj Close. Первый из них используется для того, чтобы зафиксировать количество акций, с которыми совершались сделки в торговый день. Второй столбец — это «отрегулированная» цена закрытия (adjusted closing price), это значит, что в цену закрытия периода были добавлены все действия с акциями, которые могли быть совершены до момента открытия следующего торгового дня.

Если нужно сохранить данные в CSV-файл, это можно сделать с помощью функции to_csv(), а прочитать файл можно с помощью read_csv() — это полезно для ситуаций, когда источник данных меняется и доступ к ним временно теряется.

import pandas as pd aapl.to_csv('data/aapl_ohlc.csv') df = pd.read_csv('data/aapl_ohlc.csv', header=0, index_col='Date', parse_dates=True) После базового анализа скачанных данных, пора двигаться дальше. Для этого можно, к примеру, изучить индексы и столбцы, выбрав, например, десять последних строк конкретного столбца. Это называется разбиением (subsetting), поскольку берется лишь небольшой набор имеющихся данных. Получившееся подмножество — это ряд, то есть одноразмерный именованный массив.

Для того, чтобы посмотреть на индекс и столбцы данных следует использовать атрибуты index и columns. Затем можно выделить подмножество из десяти последних наблюдений в столбце column. Для изолирования этих значений следует использовать квадратные скобки. Последнее значение помещается в переменную ts, а проверка ее типа осуществляется с помощью функции type().

# Inspect the index aapl.index # Inspect the columns aapl.columns # Select only the last 10 observations of `Close` ts = aapl['Close'][-10:] # Check the type of `ts` type(ts) Использование квадратных скобок удобно, но это не самый характерный способ при работе с Pandas. Поэтому также стоит рассмотреть функции loc() и iloc(): первая из них используется для label-based индексирования, а последняя для позиционального индексирования.

На практике, это значит, что можно передать ярлык ряда вроде 2007 или 2006-11-01 в функцию loc(), а целые числа вроде 22 или 43 передаются функции iloc().

# Inspect the first rows of November-December 2006 print(aapl.loc[pd.Timestamp('2006-11-01'):pd.Timestamp('2006-12-31')].head()) # Inspect the first rows of 2007 print(aapl.loc['2007'].head()) # Inspect November 2006 print(aapl.iloc[22:43]) # Inspect the 'Open' and 'Close' values at 2006-11-01 and 2006-12-01 print(aapl.iloc[[22,43], [0, 3]]) Если внимательно взглянуть на результаты процедуры разбиения, то станет видно, что в данных пропущены определенные дни. Дальнейший анализ паттерна покажет, что обычно не хватает двух или трех дней. Это выходные дни и государственные праздники, во время которых нет биржевых торгов.

Помимо индексирования есть несколько способов узнать о данных больше. Можно, к примеру, попробовать создать семпл из 20 строк данных, а затем переформатировать их таким образом, чтобы appl стал не дневным значением и месячным. Сделать это можно с помощью функций sample() и resample():

# Sample 20 rows sample = aapl.sample(20) # Print `sample` print(sample) # Resample to monthly level monthly_aapl = aapl.resample('M').mean() # Print `monthly_aapl` print(monthly_aapl) Прежде чем перейти к визуализации данных и проведению финансового анализа, можно начать вычислять разницу между ценами открытия и закрытия торгового периода. Эту арифметическую операцию можно с помощью Pandas — нужно вычесть значения столбца Open данных appl из столбца Close. Или, другими словами, вычесть aapl.Close из aapl.Open. Получившийся результат будет храниться в новом столбце датафрейма aapl под названием diff, который можно удалить с помощью функции del:# Add a column `diff` to `aapl` aapl['diff'] = aapl.Open - aapl.Close # Delete the new `diff` column del aapl['diff'] Получившиеся абсолютные значения уже могут быть полезны при разработке финансовой стратегии, однако обычно требуется и более глубокий анализ, например, процентных величин роста или падения цены определенной акции.

Визуализация данных временных рядов

Помимо анализа данных с помощью функций head(), tail() и индексирования, также возможна их визуализация. Благодаря интеграции Pandas с инструментом для создания графиков Matplotlib это можно сделать довольно легко. Нужно лишь использовать функцию plot() и передать ей релевантные параметры. Кроме того, если добавить параметр grid, то получившийся график будет наложен на сетку.# Import Matplotlib's `pyplot` module as `plt` import matplotlib.pyplot as plt # Plot the closing prices for `aapl` aapl['Close'].plot(grid=True) # Show the plot plt.show() Этот код дает вот такой график:

В следующей части руководства речь пойдет о финансовом анализе данных временных рядов с помощью Python.

Продолжение следует…..

Другие материалы по теме финансов и фондового рынка от ITinvest:

habr.com

Парный трейдинг: описание стратегии на Python

Стратегия парного🎏 трейдинга очень популярна на рынке. Она основана на чистой статистике📊, что делает ее привлекательной для алгоритмической🤖 торговли. Общий смысл сводится к нескольким шагам: найти пару, проверить ее поведение, определить границы входа в позицию и направление (лонг/шорт).

Пары ищут с помощью корреляции, но корреляция в чистом виде может сослужить плохую службу. Спред пар должен быть стационарным и обладать коинтегрированностью. Весь представленный код на Python🐍.

В статье рассмотрены:

  • Введение в корреляцию/коинтеграцию на простом примере.
  • Корреляция без коинтеграции.
  • Коинтеграция без корреляции.

Времянной ряд — статистические данные исследуемого процесса собранные в разные моменты времени.

Корреляция — статистическая взаимосвязь двух и более случайных величин. В нашем случае времянных рядов.

Коинтеграция — свойство нескольких нестационарных временных рядов, заключающееся в существовании некоторой их стационарной линейной комбинации.

Стационарность — свойство процесса не менять свои характеристики со временем.

P-значение — величина используемая при тестировании статистических гипотез.

Простыми словами: две акции будут коинтегрированы, когда спред разницы их истории цен будет находиться в пределах фиксированных границ и не будет обладать трендом.

Есть две акции, которые имеют глубокую экономическую связь и их цены всегда меняются в одном направлении со скоростью, характерной для каждой акции.

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

Пример стационарного спреда пары

Сигналами являются минимальные и максимальные отклонения спреда от нуля. Когда спред на минимуме — необходимо купить акцию А и продать в шорт акцию Б. Когда спред на максимуме — покупаем акцию Б и продаем в шорт акцию А. В обоих случаях закрываем позицию около нуля.

Здесь будут рассмотрены два временных ряда сформированных из набора произвольных значений нормального распределения. Считаем, что произвольный набор — это данные о прибыли/убытке в определенный момент времени. Для получения графика изменений посчитаем кумулятивную сумму значений элементов — это первый времянной ряд.

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

Нарисуем графики обоих рядов:

%matplotlib inline import numpy as np import pandas as pd import statsmodels from statsmodels.tsa.stattools import coint import seaborn import matplotlib.pyplot as plt # устанавливаем зерно для повторимости случайных чисел np.random.seed(107) X_returns = np.random.normal(0, 1, 100) # генерируем историю доходности X = pd.Series(np.cumsum(X_returns), name='X') + 50 # суммируем и смещаем на произвольную величину some_noise = np.random.normal(0, 1, 100) # немного шума для второго ряда Y = X + 5 + some_noise Y.name = 'Y' pd.concat([X, Y], axis=1).plot() # рисуем оба ряда

Нарисуем график спреда:

(Y-X).plot() # рисуем спрэд plt.axhline((Y-X).mean(), color='red', linestyle='--') # добавляем среднее График изменений Спред

Проверив значения коинтеграции и корреляции, видим что спред пары стационарен, p-значение рядом с нулем, и имеет высокую корреляцию около 95%. Код для получения значений оценки ниже:

def check_coint_corr(X, Y): # проверим стационарность двух рядов score, pvalue, _ = coint(X,Y) # проверим корреляцию двух рядов corr = X.corr(Y) print("p-значение: %.3f" % pvalue, "корреляция: %s" % corr) check_coint_corr(X, Y)

Однако, есть случаи, когда ряды имеют высокую корреляцию без коинтеграции. Пара с таким свойством не подходит для парного трейдинга. Для изучения сформируем два независимых произвольных времянных ряда:

X_returns = np.random.normal(1, 1, 100) Y_returns = np.random.normal(2, 1, 100) X_diverging = pd.Series(np.cumsum(X_returns), name='X') Y_diverging = pd.Series(np.cumsum(Y_returns), name='Y') pd.concat([X_diverging, Y_diverging], axis=1).plot() check_coint_corr(X_diverging, Y_diverging)

И нарисуем спред:

(Y_diverging-X_diverging).plot() # рисуем спрэд plt.axhline((Y_diverging-X_diverging).mean(), color='red', linestyle='--') # добавляем среднее График изменений Спред

Видим, что оба ряда имеют высокую корреляцию около 99% и проваливают тест на коинтеграцию с p-значением 0.885 (рядом с единицей). Мы видим, что спред имеет тенденцию роста со временем.

Завершим знакомство примером стационарной пары без корреляции. Возьмем набор данных из нормального распределения и сравним его с квадратной волной:

Y2 = pd.Series(np.random.normal(0, 1, 1000), name='Y2') * 3 + 20 Y3 = pd.Series([30 if not (i//100)%2 else 10 for i in range(1, 1001)], name='Y3') pd.concat([Y2, Y3], axis=1).plot() plt.ylim([0, 40]) check_coint_corr(Y2, Y3) График изменений Спред

Теперь тесты показывают p-значение равное нулю, что говорит о наличии коинтеграции пары и корреляцию рядом с нулем, а также указывает на отсутствие корреляции между ними. Эти две пары подходят для парного трейдинга, так как спред всякий раз возвращается к нулю.

Здесь раскрыт общий смысл стратегии Парного трейдинга, а примеры показывают, какие пары надо искать и какие инструменты для этого можно использовать. Конечно, для торговли этого не достаточно, но уже позволяет избежать грубых ошибок. В следующих статьях я опишу 3 способа поиска пар для торговли: проверка коинтеграции, тест Дики-Фуллера и положение средних.

💬В комментариях задавайте вопросы и напишите, где не хватает подробностей, а что можно описать иначе.

VKontakte

Facebook

Twitter

Google+

quantrum.me

Талиб, Питон и индикаторы. Всё просто

О чем пойдет речь

В этой статье мы мельком коснемся индикаторов, и, на примере биржи Полоникс, построим такие же, используя язык программирования Python и библиотеку TA-Lib.

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

Вступление. Индикаторы

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

Тысячи людей пытались найти закономерности рынка, выстроить математические модели для расчета падения и роста курса, кое-кто потратил на это почти всю жизнь, и многие более-менее приблизились к решению. Более-менее - это значит что они теперь знают о бирже немного больше других. Но, когда вопрос касается денег, даже 0.5% знания могут принести серьезные дивиденды... Если эти знания истинны, конечно.

Существуют такие вещи, как индикаторы. Если говорить упрощенно, то индикатор - это математическая функция, которая принимает на вход историю торгов за определенный период, и возвращает некоторый прогноз. Некоторые индикаторы оценивают объемы торгов, некоторые - цены сделок, в любом случае за каждым проверенным индикатором стоит выверенная математическая модель. А так же для каждого индикатора есть определенные инструкции по трактованию результатов.

Довольно воды - давайте рассмотрим на простых примерах, и подумаем, как мы можем это использовать в автоматизированной торговле.

Вот перед нами окно Полоникса – сейчас выбрана пара BTC-ETH, период 24 часа, свечи по 5 минут. Мы видим соответственно сами свечи (как они строятся, мы уже разбирали), за ними затененные столбики то ли серого, то ли светло-зеленого цвета – объемы торгов по паре (сколько было вбухано денег при том курсе), и кучу разноцветных полос тут и там. Так вот эти полосы как раз отображают посчитанные данные некоторых индикаторов, которые можно использовать при торговле.

EMA, SMA и прочие MA – это скользящие средние (упрощение графика). На любом трейдерском сайте вы найдете тысячу и один способ по их использованию, я процитирую пример из википедии:

Наиболее простой является стратегия, при которой инструмент покупается при условии, что график цены пересекает свою скользящую среднюю снизу вверх, и продаётся, когда график цены пересекает график скользящей средней сверху вниз. И то и другое явление называют пробоем.

Кроме того, полагают, что если линия графика цены находится выше скользящей средней, то рынок считается «бычьим», на котором можно покупать, а если наоборот — «медвежьим», предпочтительным для продажи.

MACD тоже неплохо разобран везде – и на вики, и на нашем сайте есть видео, тоже немного процитирую:

Чаще всего индикатор используют для выявления торговых сигналов при боковом движении цен — периоды стабилизации курса после повышения или понижения (консолидация).

Обычно сигналом «Покупать» считают, когда скользящая с меньшим периодом (на рисунке синяя линия) в нижней зоне пересекает снизу вверх скользящую с бо́льшим периодом (красная линия). Сигналом «Продавать» считают, когда скользящая с меньшим периодом в верхней зоне пересекает сверху вниз скользящую с бо́льшим периодом.

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

Я не буду сейчас заострять внимание на каждом индикаторе – это тема для отдельных будущих статей, но покажу, как их получать, строить и использовать для своих целей.

Так же маленький хинт – на графике полоникса сверху слева есть шестеренка – вы можете нажать её и поиграться с настройкой индикаторов.

Реклама

Пишем код

Подготовка

Для работы нам потребуется библиотека для научных расчетов numpy, библиотека TA-Lib и модуль requests. Так же понадобится пакет matplotlib, с его помощью мы будем рисовать графики. Ну и установленный Python, конечно. У меня установлен Python 3.5.2 разрядность 32.

  1. В командной строке пишем и выполняем команды
pip install numpy pip install requests pip install matplotlib
  1. Скачиваем и устанавливаем набор для TA-Lib (вот прямая ссылка на архив, вот ссылка на страницу, где можно скачать последнюю версию в разделе Windows). После скачивания архива нужно его распаковать на диск C в папку ta-lib. Должно получиться так – C:\ta-lib\, а в этой папке уже папки C, Excel, java и т.п.
  2. В командной строке выполнить pip install ta-lib

Подготовка окончена.

Для начала построим график

Полоникс предоставляет публичный API-метод для построения графика – дает цены открытия, закрытия, минимальной и максимальной цены за период, сгруппированный по указанным периодам. Метод returnChartData, пример можно посмотреть по ссылке. Мы будем использовать эти данные, что бы

  1. Построить график
  2. Сформировать индикаторы

График будем строить по паре BTC-ETH, за прошедшие 24 часа с группировкой по 5 минут. Для этого напишем небольшой скрипт. Если нужны данные по другим парам, меняйте в коде BTC_ETH на что вам угодно.

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 start_time = time.time() - 24*60*60 resource = requests.get("https://poloniex.com/public?command=returnChartData&currencyPair=BTC_ETH&start=%s&end=9999999999&period=300" % 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() candlestick2_ohlc(ax,quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6) ax.xaxis.set_major_locator(ticker.MaxNLocator(6)) def chart_date(x,pos): try: return xdate[int(x)] except IndexError: return '' ax.xaxis.set_major_formatter(ticker.FuncFormatter(chart_date)) fig.autofmt_xdate() fig.tight_layout() plt.show()

При запуске он будет выдавать предупреждение MatplotlibDeprecationWarning: The finance module has been deprecated in mpl 2.0 and will be removed in mpl 2.2. Please use the module mpl_finance instead., но не обращайте на него внимание, он просто предупреждает, что в будущем код надо писать по другому. Но что бы писать прямо сейчас такой код, как он хочет, надо скачивать отдельные библиотеки, подключать их.. В общем, не берите в голову.

Итак, мы запустили код, и что мы видим? Хм, график! Совпадают данные с полониксом? Ага)

Добавим индикаторов

Но в самом графике смысла нет – его можно и так смотреть на полониксе. Давайте попробуем построить данные для индикатора?

На Полониксе в верхней части используются SMA и EMA – давайте начнем с чего-то одного. Значит так, SMA строится с интервалом в 50 минут? Оок. Немного меняем скрипт:

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 start_time = time.time() - 24*60*60 resource = requests.get("https://poloniex.com/public?command=returnChartData&currencyPair=BTC_ETH&start=%s&end=9999999999&period=300" % 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() candlestick2_ohlc(ax,quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6) ax.xaxis.set_major_locator(ticker.MaxNLocator(6)) def chart_date(x,pos): try: return xdate[int(x)] except IndexError: return '' ax.xaxis.set_major_formatter(ticker.FuncFormatter(chart_date)) fig.autofmt_xdate() fig.tight_layout() sma = talib.SMA(quotes['close'], timeperiod=50) print(sma) ax.plot(sma) plt.show()

Работает… Так же обратили внимание, что в коде есть строка print(sma)? Я специально в отладочную консоль вывел посчитанные данные:

В «нормальной» жизни вам (наверное) не нужны графики – вам нужны именно эти цифры. Т.е. ваш бот может получать такой массив данных, и понимать – тренд растет, либо тренд падает, цена биржи пересекает тренд снизу вверх или сверху вниз и т.п… Но в этой статье мы рисуем графики для визуализации принципа, поэтому пока-что мы их анализировать не будем.. И print() писать тоже)

Так же график начинается не с начала, а с некоторым интервалом – но тут все просто. Мы берем данные за 24 часа, поэтому в начале работы скрипту нечего считать – мы же ведь за 50 минут строим? Это легко поправить, но я не хочу усложнять код и объяснять, почему и как усложнил, сути это не поменяет.

Теперь надо добавить EMA с периодом в 30 минут. Дополняем код:

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 start_time = time.time() - 24*60*60 resource = requests.get("https://poloniex.com/public?command=returnChartData&currencyPair=BTC_ETH&start=%s&end=9999999999&period=300" % 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() candlestick2_ohlc(ax,quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6) ax.xaxis.set_major_locator(ticker.MaxNLocator(6)) def chart_date(x,pos): try: return xdate[int(x)] except IndexError: return '' ax.xaxis.set_major_formatter(ticker.FuncFormatter(chart_date)) fig.autofmt_xdate() fig.tight_layout() sma = talib.SMA(quotes['close'], timeperiod=50) ax.plot(sma) ema = talib.EMA(quotes['close'], timeperiod=20) ax.plot(ema) plt.show()

Мне, в целом, нравится. Но надо больше графиков!

Разделяем графики

Теперь я хочу добавить MACD, но он на этот график не ложится – там совсем другие значения, слишком маленькие. Все просто, сейчас мы будем рисовать сразу ТРИ графика с одной и той-же осью X. На первом графике мы оставим всё как есть, на второй вынесем линию 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 start_time = time.time() - 24*60*60 resource = requests.get("https://poloniex.com/public?command=returnChartData&currencyPair=BTC_ETH&start=%s&end=9999999999&period=300" % 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] f

bablofil.ru

Как торговать через API на Yobit.net

На горизонте неизвестный континент - биржа Yobit.net. Больше 1000 активных (порой весьма экзотических) пар. Отличное место что бы что-то автоматизировать. Хотя бы даже без денег, так, поизучать. Погнали)

Public API

Как и большинство конкурентов, биржа предоставляет для роботов публичный и приватный методы получения данных. Прочитать детальное описание можно тут: https://www.yobit.net/ru/api/.

ХИНТ: у тех, кто работает с ноута без мыши, может не работать скролл текста. На самом деле там есть маленький серый скролл справа сверху, придется его двигать.

Попробуем для начала вытащить текущие пары и их активные курсы покупок и продаж?

Обычно именно для этого и используется метод ticker, но увы, тут он не предусмотрен выводить информацию по всем парам, можно лишь указывать нужные - например https://yobit.net/api/3/ticker/ltc_btc-nmc_btc.

Жаль, но давайте посмотрим, какую информацию можно получить. Для начала перейдем по этой ссылке сами, в браузере, что видим? Полезную информацию:

Отобразим в более удобном виде для чтения (jsoneditoronline.org):

Почитаем, что где что означает..

  • high: макcимальная цена
  • low: минимальная цена
  • avg: средняя цена
  • vol: объем торгов
  • vol_cur: объем торгов в валюте
  • last: цена последней сделки
  • buy: цена покупки
  • sell: цена продажи
  • updated: последнее обновление кэша

Судя по всему, поля buy и sell – это лучшие текущие цены покупки и продажи соответственно. Попробуем их вывести сначала для отдельно взятой пары. Пусть это будет ltc_btc.

 (Тем, кто не установил себе Python, нужно это сделать, что бы повторять написанное далее – как установить питон и модули к нему, я писал в этой статье).

Запускаем Idle, и пишем туда текст скрипта по получению данных. Сохраняем (под любым именем, у меня он называется yobit0.py) и запускаем (f5).

Скрипт:

import json import requests res = requests.get('https://yobit.net/api/3/ticker/ltc_btc') # получаем данные ticker'а res_obj = json.loads(res.text) # переводим полученный текст в объект с данными print("SELL: %0.8f" % res_obj['ltc_btc']['sell']) print("BUY: %0.8f" % res_obj['ltc_btc']['buy'])

Результат:

Теперь нам нужно научиться получать название пары, buy и sell для всех пар – но всего на бирже больше 1000 пар – задолбаешься проверять каждую, даже прописывая по несколько штук… хотя… 

Активные пары можно посмотреть в методе info - https://yobit.net/api/3/info. Давайте ради интереса вытащим все активные пары и попробуем подставить их в URL? Раньше, я помню, было ограничение на длину 1024 символа при отправке данных методом GET, но потом вроде бы ограничение ушло из браузеров и переместилось на сторону конфигурации веб-серверов, так что заодно и проверим что получилось.

Получение списка активных пар

Скрипт:

import json import requests res = requests.get('https://yobit.net/api/3/info') # получаем данные res_obj = json.loads(res.text) # переводим полученный текст в объект с данными print("Получено %d пар(ы)!" % len(res_obj['pairs'])) pairs = '-'.join(res_obj['pairs']) # Формируем строку в нужном формате print(pairs)

Результат:

Все пары получены, и сформированы в строку, разделенную дефисами – за это отвечает функция join – но не заостряйте пока на этом внимание. Попробуем теперь эту строку отдать на вход ticker`у и посмотрим, что получится. Юхуу.

Меняем этот скрипт (или создаем новый файл, yobit2.py), запускаем и смотрим ответ сервера…

Скрипт:

import json import requests res = requests.get('https://yobit.net/api/3/info') # получаем данные res_obj = json.loads(res.text) # переводим полученный текст в объект с данными print("Получено %d пар(ы)!" % len(res_obj['pairs'])) pairs = '-'.join(res_obj['pairs']) # Формируем строку в нужном формате print(pairs)

О, горе, горе… Yobit использует веб-фронтенд Nginx версии 1.11.4, которому очень не нравится ссылка, которую мы сформировали.

  Ладно, ладно…. Плохой Yobit, тогда будем разбивать массив полученных данных на кусочки по 50 30 пар, и получать инфу, потом следующие 30 пар.. Можно было бы взять по 100 пар, но тогда  Yobit их отрезает и считает что ничего не передали) Можно просто идти в цикле по каждой паре, но это будет 1000 запросов к сайту, на месте Yobit’а я бы насторожился. Итак, режем список по 30 пар и выводим значения:

Скрипт:

import json import math import requests res = requests.get('https://yobit.net/api/3/info') # получаем данные info res_obj = json.loads(res.text) # переводим полученный текст в объект с данными pairs = [pair for pair in res_obj['pairs']] # создадим массив названий пар cnt = 1 # Проходим в цикле, отбирая каждый раз по 100 пар (или меньше, в хвосте) for i in range(0, int(math.ceil(len(pairs)/30))): pairs_str = '-'.join(pairs[i*30:(i+1)*30]) # формируем строку для передачи тикеру ticker_res = requests.get('https://yobit.net/api/3/ticker/'+pairs_str) # получаем данные info ticker_res_obj = json.loads(ticker_res.text) # переводим полученный текст в объект с данными for pair in ticker_res_obj: print( cnt, pair, '%0.8f' % ticker_res_obj[pair]['buy'], '%0.8f' % ticker_res_obj[pair]['sell'] ) cnt += 1

Результат:

Bingo, Yobit побежден, все 1002 пары получены.. Двигаемся дальше.

Реклама

Private API

Методы, которыми мы пользовались выше, относились к публичному API – т.е. данные доступны для всех без регистрации. Сейчас посмотрим приватное API – методы, доступные только для авторизованного пользователя, позволяющие выполнять действия от его имени.

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

Нажать Create new key

Обратите внимание - слева от кнопки есть выпадающий список (но менять его не надо).

Info only – Вы сможете запрашивать баланс, открытые ордера, историю торгов и т.п., но не сможете торговать, отменять ордера и выполнять прочие активные действия.

Info & trade & deposits (по умолчанию, оставляем как есть) – помимо информации вы сможете торговать а так же пополнять баланс. Если хакер украдет ваши ключи API, он не сможет вывести у вас деньги (но сможет их проиграть).

Info & trade & deposits & withdrawals – вы сможете выполнять  все доступные на сегодня методы API, включая вывод денег. Я бы не стал такое включать, пока в этом нет действительной нужды (но если пишете бота для межбиржевого арбитража, наверное стоит включить)

После этого получите ключи и сохраните их, они нам понадобятся.

Ну а теперь пора наваять простецкую функцию для доступа по private API и погонять её. Возьмем за основу код из статьи про Эксмобота, немного адаптируем, ну и запустим.

Впишите API-ключи в API_KEY и API_SECRET!

Скрипт:

import os import json # import requests import urllib, http.client import hmac, hashlib # Вписываем свои ключи API_KEY = '' API_SECRET = b'' """ Каждый новый запрос к серверу должен содержать увеличенное число в диапазоне 1-2147483646 Поэтому храним число в файле поблизости, каждый раз обновляя его """ nonce_file = "./nonce" if not os.path.exists(nonce_file): with open(nonce_file, "w") as out: out.write('1') # Будем перехватывать все сообщения об ошибках с биржи class YobitException(Exception): pass def call_api(**kwargs): # При каждом обращении к торговому API увеличиваем счетчик nonce на единицу with open(nonce_file, 'r+') as inp: nonce = int(inp.read()) inp.seek(0) inp.write(str(nonce+1)) inp.truncate() payload = {'nonce': nonce} if kwargs: payload.update(kwargs) payload = urllib.parse.urlencode(payload) H = hmac.new(key=API_SECRET, digestmod=hashlib.sha512) H.update(payload.encode('utf-8')) sign = H.hexdigest() headers = {"Content-type": "application/x-www-form-urlencoded", "Key":API_KEY, "Sign":sign} conn = http.client.HTTPSConnection("yobit.net", timeout=60) conn.request("POST", "/tapi/", payload, headers) response = conn.getresponse().read() conn.close() try: obj = json.loads(response.decode('utf-8')) if 'error' in obj and obj['error']: raise YobitException(obj['error']) return obj except json.decoder.JSONDecodeError: raise YobitException('Ошибка анализа возвращаемых данных, получена строка', response) print ('Получаем информацию по аккаунту', '*'*30) print( call_api(method="getInfo") ) try: print ('Создаем ордер на покупку', '*'*30) print( call_api(method="Trade", pair="ltc_btc", type="buy", rate="0.1", amount=0.01) ) except YobitException as e: print("Облом:", e) try: print ('Создаем ордер на продажу', '*'*30) print( call_api(method="Trade", pair="ltc_btc", type="sell", rate="0.1", amount=0.01) ) except YobitException as e: print("Облом:", e) try: print ('Получаем список активных ордеров', '*'*30) print( call_api(method="ActiveOrders", pair="ltc_btc") ) except YobitException as e: print("Облом:", e) try: print ('Получаем информацию по ордеру', '*'*30) print( call_api(method="OrderInfo", order_id="123") ) except YobitException as e: print("Облом:", e) try: print ('Отменяем ордер', '*'*30) print( call_api(method="CancelOrder", order_id="123") ) except YobitException as e: print("Облом:", e) try: print ('Получаем историю торгов', '*'*30) print( call_api(method="TradeHistory", pair="ltc_btc") ) except YobitException as e: print("Облом:", e) try: print ('Получаем кошель для пополнения (BTC)', '*'*30) print( call_api(method="GetDepositAddress", coinName="BTC") ) except YobitException as e: print("Облом:", e)

Результат:

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

Реклама

Заключение

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

Желаю вам большого, стабильного и легкого заработка!

bablofil.ru

как использовать Python для алгоритмической торговли на бирже. Часть 1

Руководство: как использовать Python для алгоритмической торговли на бирже. Часть 1 - 1

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

Среди наиболее популярных в сфере финансов языков программирования можно отметить R и Python, также часто используются C++, C# и Java. В опубликованном на сайте DataCamp руководстве речь идет о том, как начать использовать Python для создания финансовых приложений — мы представляем вам серию статей-адаптаций глав этого материала.

Структура руководства:

  • Первая часть предназначена для новичков на рынке, в ней речь пойдет об устройстве финансовых рынков, акциях и торговых стратегиях, данных временных рядов, а также о том, что понадобится для начала разработки.
  • Во второй части будет представлено введение в работу с данными временных рядов и инструментами финансового анализа, вроде подсчета волатильности и скользящих средних, с помощью Python-библиотеки Pandas.
  • Затем мы перейдем к непосредственной разработке простой импульсной торговой стратегии.
  • В четвертой части речь пойдет о том, как проводить бэктест стратегии на исторических данных.
  • В завершение, будут затронуты вопросы оптимизации стратегии для повышения ее производительности, а также оценки ее работы и надежности.

Введение: простым языком об устройстве сферы финансов

Прежде чем окунуться в мир торговых стратегий, имеет смысл коснуться базовых понятий. Однако это не означает, что то, о чем пойдет речь ниже, рассчитано совсем на новичков. Будет здорово, если вы предварительно ознакомитесь с курсом по использованию Python для работы с данными, а также представляете себе, как работать со списками и пакетами Python, а также хотя бы на базовом уровне знакомы с NumPy и Pandas.

Акции и торговля на бирже

Когда компания хочет продолжить развитие бизнеса, запускать новые проекты или расширяться, то в качестве инструмента финансирования может использовать акции. Акция представляет долю во владении компании, акции обменивают на деньги. Акции можно покупать и продавать: участники таких сделок проводят операции с уже существующими, ранее выпущенными акциями.

Цена, по которой конкретная акция будет продана или куплена, может постоянно меняться, вне зависимости от бизнес-показателей выпустившей акции компании: все определяется спросом и предложением. Важно понимать различие между акциями и, например, облигациями (бондами), которые используются для привлечения именно заемных средств.

Когда речь идет о трейдинге, то рассматриваться может не только продажа и покупка акций — сделка может быть заключена по разным активам, в числе которых могут быть как финансовые инструменты, так и, например, драгоценные металлы или ресурсы вроде нефти.

При покупке акций инвестор получает определенную долю в компании, из чего может в будущем извлечь финансовую выгоду, продав эту долю. Стратегии могут различаться: существуют длинные сделки (long), заключаемые в надежде на дальнейший рост акций, и короткие, когда инвестор предполагает, что акции будут дешеветь, поэтому продает акции в надежде в будущем «откупить» их обратно по более низкой цене.

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

Торговые стратегии обычно проверяют с помощью бэктестинга: это подход, при которым стратегия «прогоняется» на исторических данных о торгах — на их основе программа генерирует сделки. Это позволяет понять, принесла бы такая стратегия доход при том развитии рыночной ситуации, что наблюдалось в прошлом. Таким образом, можно предварительно оценить перспективность стратегии в торгах в режиме реального времени. При этом, нет гарантий того, что хорошие показатели на исторических данных повторятся при работе на реальном рынке.

Данные временных рядов

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

Руководство: как использовать Python для алгоритмической торговли на бирже. Часть 1 - 2

По оcи X расположены даты, а цена — на оси Y. «Последовательные равные промежутки времени» в данном случае означает, что по временной оси даты расположены с двухнедельным интервалом: можно сравнить 3/7/2005 и 3/31/2005, а также 4/5/2005 и 4/19/2005 (здесь даты записаны в принятом в США формате, когда сначала идет месяц, а затем день).

Однако финансовые данные обычно включают не два параметра (цена и дата), а пять — помимо величины торгового периода, это цена открытия торгового периода, наивысшая и низшая цена в его рамках, а также цена в момент закрытия периода. Это значит, что если мы рассматривает дневной период, то анализ данных даст нам информацию о том, на каком уровне находилась цена в момент старта и завершения торгов в выбранный день, а также какой была максимальная и минимальная цена в ходе торгов.

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

Основы Python для сферы финансов: Pandas

Одним из самых востребованных инструментов при использовании Python для разработки финансовых приложений является пакет Pandas. Он нужен уже в самом начале, но по мере углубления в процесс разработки понадобятся и такие пакеты как NumPy, SciPy, Matplotlib.

Для начала сфокусируемся на Pandas и применении этого инструмента к анализу временных рядов. Ниже речь пойдет о том, как с помощью данного пакета импортировать данные, анализировать и манипулировать ими.

Импорт финансовых данных

Пакет pandas-datareader позволяет получать данные из таких источников, как Google, Yahoo! Finance или Всемирный банк — подробнее о доступных источниках данных написано в документации. В этом руководстве будет рассматриваться получение данных с сервиса Yahoo! Finance. Для начала работы необходимо установить последнюю версию пакета с помощью pip:

pip install pandas-datareader

Инструкции по установке версии в разработке представлены здесь.

import pandas_datareader as pdr import datetime aapl = pdr.get_data_yahoo('AAPL', start=datetime.datetime(2006, 10, 1), end=datetime.datetime(2012, 1, 1))

Не так давно в Yahoo API произошли изменения, так что для начала самостоятельной работы с библиотекой нужно установить исправления, который позволит дождаться официального патча. Подробнее проблема описана здесь. Однако для этого руководства данные были скачаны заранее, так что проблем с его изучением не возникнет.

Важно также понимать, что несмотря на то, что pandas-datareader — это удобный инструмент для загрузки данных, он далеко не единственный для Python. Также можно использовать библиотеки вроде Quandl, которая позволяет получать данные с сервиса Google Finance:

import quandl aapl = quandl.get("WIKI/AAPL", start_date="2006-10-01", end_date="2012-01-01")

Также многим известно, что в сфере финансов для анализа данных очень популярен Excel. Для удобства будущей работы можно интегрировать этот инструмент с Python (подробнее по ссылке).

Работа с данными временных рядов

Для импортирования данных мы использовали pandas_datareader. В результате возник объект aapl — это DataFrame, то есть двумерная именованная структура данных со столбцами потенциально разных типов. Первое, что следует сделать при работе с таким фреймом — запустить функции head() и tail() для того, чтобы взглянуть на первый и последний столбцы датафрейма. Для получения полезной статистической сводки по скачанным данным можно воспользоваться функцией describe().

Пример этого кода можно найти на странице исходного материала.

Данные содержат четыре столбца с ценой открытия и закрытия торгового периода, а также максимальной и минимальной ценой — мы рассматриваем дневные интервалы и акции Apple. Также мы получаем два дополнительных столбца: Volume и Adj Close. Первый из них используется для того, чтобы зафиксировать количество акций, с которыми совершались сделки в торговый день. Второй столбец — это «отрегулированная» цена закрытия (adjusted closing price), это значит, что в цену закрытия периода были добавлены все действия с акциями, которые могли быть совершены до момента открытия следующего торгового дня.

Если нужно сохранить данные в CSV-файл, это можно сделать с помощью функции to_csv(), а прочитать файл можно с помощью read_csv() — это полезно для ситуаций, когда источник данных меняется и доступ к ним временно теряется.

import pandas as pd aapl.to_csv('data/aapl_ohlc.csv') df = pd.read_csv('data/aapl_ohlc.csv', header=0, index_col='Date', parse_dates=True)

После базового анализа скачанных данных, пора двигаться дальше. Для этого можно, к примеру, изучить индексы и столбцы, выбрав, например, десять последних строк конкретного столбца. Это называется разбиением (subsetting), поскольку берется лишь небольшой набор имеющихся данных. Получившееся подмножество — это ряд, то есть одноразмерный именованный массив.

Для того, чтобы посмотреть на индекс и столбцы данных следует использовать атрибуты index и columns. Затем можно выделить подмножество из десяти последних наблюдений в столбце column. Для изолирования этих значений следует использовать квадратные скобки. Последнее значение помещается в переменную ts, а проверка ее типа осуществляется с помощью функции type().

# Inspect the index aapl.index # Inspect the columns aapl.columns # Select only the last 10 observations of `Close` ts = aapl['Close'][-10:] # Check the type of `ts` type(ts)

Использование квадратных скобок удобно, но это не самый характерный способ при работе с Pandas. Поэтому также стоит рассмотреть функции loc() и iloc(): первая из них используется для label-based индексирования, а последняя для позиционального индексирования.

На практике, это значит, что можно передать ярлык ряда вроде 2007 или 2006-11-01 в функцию loc(), а целые числа вроде 22 или 43 передаются функции iloc().

# Inspect the first rows of November-December 2006 print(aapl.loc[pd.Timestamp('2006-11-01'):pd.Timestamp('2006-12-31')].head()) # Inspect the first rows of 2007 print(aapl.loc['2007'].head()) # Inspect November 2006 print(aapl.iloc[22:43]) # Inspect the 'Open' and 'Close' values at 2006-11-01 and 2006-12-01 print(aapl.iloc[[22,43], [0, 3]])

Если внимательно взглянуть на результаты процедуры разбиения, то станет видно, что в данных пропущены определенные дни. Дальнейший анализ паттерна покажет, что обычно не хватает двух или трех дней. Это выходные дни и государственные праздники, во время которых нет биржевых торгов.

Помимо индексирования есть несколько способов узнать о данных больше. Можно, к примеру, попробовать создать семпл из 20 строк данных, а затем переформатировать их таким образом, чтобы appl стал не дневным значением и месячным. Сделать это можно с помощью функций sample() и resample():

# Sample 20 rows sample = aapl.sample(20) # Print `sample` print(sample) # Resample to monthly level monthly_aapl = aapl.resample('M').mean() # Print `monthly_aapl` print(monthly_aapl)

Прежде чем перейти к визуализации данных и проведению финансового анализа, можно начать вычислять разницу между ценами открытия и закрытия торгового периода. Эту арифметическую операцию можно с помощью Pandas — нужно вычесть значения столбца Open данных appl из столбца Close. Или, другими словами, вычесть aapl.Close из aapl.Open. Получившийся результат будет храниться в новом столбце датафрейма aapl под названием diff, который можно удалить с помощью функции del:

# Add a column `diff` to `aapl` aapl['diff'] = aapl.Open - aapl.Close # Delete the new `diff` column del aapl['diff']

Получившиеся абсолютные значения уже могут быть полезны при разработке финансовой стратегии, однако обычно требуется и более глубокий анализ, например, процентных величин роста или падения цены определенной акции.

Визуализация данных временных рядовПомимо анализа данных с помощью функций head(), tail() и индексирования, также возможна их визуализация. Благодаря интеграции Pandas с инструментом для создания графиков Matplotlib это можно сделать довольно легко. Нужно лишь использовать функцию plot() и передать ей релевантные параметры. Кроме того, если добавить параметр grid, то получившийся график будет наложен на сетку.

# Import Matplotlib's `pyplot` module as `plt` import matplotlib.pyplot as plt # Plot the closing prices for `aapl` aapl['Close'].plot(grid=True) # Show the plot plt.show()

Этот код дает вот такой график:

Руководство: как использовать Python для алгоритмической торговли на бирже. Часть 1 - 3

В следующей части руководства речь пойдет о финансовом анализе данных временных рядов с помощью Python.

Продолжение следует…..

Другие материалы по теме финансов и фондового рынка от ITinvest:

Автор: ITinvest

Источник

www.pvsm.ru

Пишем торгового робота для биржи Exmo

Теперь, когда мы разобрались, что такое биржа биткоинов, отложенные ордера и API, пришло время написать своего собственного бота.

Тем, кто попал сюда впервые, и не знает о чем идет речь, советую прочитать предыдущие статьи цикла – вы сможете найти перечень внизу этой статьи.

Итак, что это за бот и как он будет работать

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

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

Если сделка на покупку не реализуется в течении какого-то времени (у меня это три минуты) бот отменяет ордер и создает новый, с новым курсом.

Если сделка на покупку прошла, то бот создает ордер на продажу, и держит этот ордер до тех пор, пока он не будет целиком исполнен.

Бот берет среднюю цену по рынку за некоторый период (из-за ограничений exmo, за последние 100 сделок, на других биржах я действовал по другому), и создает ордера на покупку с указанной наценкой – т.е. ниже текущей цены рынка, после чего создает ордера на покупку – опять же с указанной наценкой – получается выше цены рынка. В сумму продаж/покупок закладывается комиссия биржи и, таким образом, нивелируется. Совершая сделки, бот отдает бирже требуемый ею кусок, но прибыль для владельца бота остается неизменной.

Бот отслеживает частичное исполнение ордера – он не будет создавать новых ордеров, пока предыдущий не был полностью исполнен или отменен. Если предыдущий ордер был исполнен частично, бот будет ждать завершения всех сделок по этому ордеру.

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

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

Сколько зарабатывает:

В рамках подготовки этой статьи (и отладки алгоритма), я играл на сумму 1 доллар 49 центов – и вот какие он сделки совершал (читать снизу вверх):

Сколько приносит бот на бирже

Если проанализировать доход/расход, то бот принес 3.5 цента за день – при том, что я играл на полтора доллара – это 2.4% со вклада в день.

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

Недостатки бота:

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

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

Так же для упрощения вес не пишется в локальную базу данных, а делается запрос к API. С одной стороны, это хорошо для бота, так как информация всегда приходит актуальная, с другой стороны – плохо, так как эта биржа ограничивает количество API запросов до 180 в минуту. Код, который написан здесь, будет работать без проблем, но если вы запустите параллельно несколько экземпляров бота, с разными валютными парами, например, вполне можете наткнуться на это ограничение.

Реклама

Алгоритм работы:

Для наглядности составлена блок-схема алгоритма работы – полностью транслировать её в текст я смысла не вижу, поясню основные принципы.

Бот играет на сумму которую вы указали – в данном случае для примера выбрана сумма 10 долларов США. На эту сумму бот старается купить биткойнов по курсу, чуть ниже текущего курса рынка. Если в течении некоторого времени (три минуты в примере) купить не получается, этот ордер на покупку отменяется, и создается новый, чуть ниже текущей цены уже на этот момент времени.

Если же ордер на покупку исполняется, то бот создает отложенный ордер на продажу этой валюты – он старается продать купленную валюту, и получить за это условные 10 долларов + желаемую наценку.

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

Все, что бот зарабатывает, не тратится – бот играет на указанную сумму, а полученный излишек просто копится на балансе.

Вот блок-схема работы:

алгоритм работы торгового робота

Реклама

Как начать пользоваться:

1. Регистрируйтесь на бирже (если еще этого не сделали):

2. Перейдите в Account-settings-API, нажмите “Generate and save”, и получите ключ и подпись: 

получить API ключи для робота

3. Установите интерпретатор Python 3.4 и выше (описано в этой статье)

4. Создайте файл с названием exmo.py и скопируйте туда код, указанный ниже

5. В коде, в строках 11 и 13, укажите ключи API, полученные в шаге 2

6. В строке  24 укажите сумму, на которую будет играть бот - CAN_SPEND = 1.45 – сейчас указано 1.45 доллара.

7. На балансе не должно быть currency_1 - например, если играете на паре BTC_USD, то BTC заранее переведите в доллары или в другую валюту, а то продаст в минус.

8. Сохраните и запускайте (F5) – бот начнет работать.

Вы можете его запустить, даже если на бирже сейчас нет денег – бот вас предупредит, и просто ничего не купит. Но, конечно, для успешной работы нужно, что бы деньги были :) На 11.04.2017 минимальная сумма на балансе должна составлять примерно 1.5 доллара – это примерно равно минимальной сумме сделки на бирже, 0.001 Btc.

Реклама

Сам код бота:

import urllib, http.client import time import json # эти модули нужны для генерации подписи API import hmac, hashlib # ключи API, которые предоставила exmo API_KEY = 'YOUR API KEY' # обратите внимание, что добавлена 'b' перед строкой API_SECRET = b'YOUR API SECRET' # Тонкая настройка CURRENCY_1 = 'BTC' CURRENCY_2 = 'USD' CURRENCY_1_MIN_QUANTITY = 0.001 # минимальная сумма ставки - берется из https://api.exmo.com/v1/pair_settings/ ORDER_LIFE_TIME = 3 # через сколько минут отменять неисполненный ордер на покупку CURRENCY_1 STOCK_FEE = 0.002 # Комиссия, которую берет биржа (0.002 = 0.2%) AVG_PRICE_PERIOD = 15 # За какой период брать среднюю цену (мин) CAN_SPEND = 5 # Сколько тратить CURRENCY_2 каждый раз при покупке CURRENCY_1 PROFIT_MARKUP = 0.001 # Какой навар нужен с каждой сделки? (0.001 = 0.1%) DEBUG = True # True - выводить отладочную информацию, False - писать как можно меньше STOCK_TIME_OFFSET = 0 # Если расходится время биржи с текущим # базовые настройки API_URL = 'api.exmo.com' API_VERSION = 'v1' # Свой класс исключений class ScriptError(Exception): pass class ScriptQuitCondition(Exception): pass CURRENT_PAIR = CURRENCY_1 + '_' + CURRENCY_2 # все обращения к API проходят через эту функцию def call_api(api_method, http_method="POST", **kwargs): # Составляем словарь {ключ:значение} для отправки на биржу   # пока что в нём {'nonce':123172368123} payload = {'nonce': int(round(time.time()*1000))}   # Если в ф-цию переданы параметры в формате ключ:значение if kwargs:   # добавляем каждый параметр в словарь payload   # Получится {'nonce':123172368123, 'param1':'val1', 'param2':'val2'} payload.update(kwargs)   # Переводим словарь payload в строку, в формат для отправки через GET/POST и т.п. payload = urllib.parse.urlencode(payload)   # Из строки payload получаем "подпись", хешируем с помощью секретного ключа API   # sing - получаемый ключ, который будет отправлен на биржу для проверки H = hmac.new(key=API_SECRET, digestmod=hashlib.sha512) H.update(payload.encode('utf-8')) sign = H.hexdigest()   # Формируем заголовки request для отправки запроса на биржу.   # Передается публичный ключ API и подпись, полученная с помощью hmac headers = {"Content-type": "application/x-www-form-urlencoded", "Key":API_KEY, "Sign":sign}   # Создаем подключение к бирже, если в течении 60 сек не удалось подключиться, обрыв соединения conn = http.client.HTTPSConnection(API_URL, timeout=60)   # После установления связи, запрашиваем переданный адрес   # В заголовке запроса уходят headers, в теле - payload conn.request(http_method, "/"+API_VERSION + "/" + api_method, payload, headers) # Получаем ответ с биржи и читаем его в переменную response   response = conn.getresponse().read() # Закрываем подключение conn.close() try:   # Полученный ответ переводим в строку UTF, и пытаемся преобразовать из текста в объект Python obj = json.loads(response.decode('utf-8'))   # Смотрим, есть ли в полученном объекте ключ "error" if 'error' in obj and obj['error']:   # Если есть, выдать ошибку, код дальше выполняться не будет raise ScriptError(obj['error'])   # Вернуть полученный объект как результат работы ф-ции return obj except ValueError:   # Если не удалось перевести полученный ответ (вернулся не JSON) raise ScriptError('Ошибка анализа возвращаемых данных, получена строка', response) # Реализация алгоритма def main_flow(): try: # Получаем список активных ордеров try: opened_orders = call_api('user_open_orders')[CURRENCY_1 + '_' + CURRENCY_2] except KeyError: if DEBUG: print('Открытых ордеров нет') opened_orders = [] sell_orders = [] # Есть ли неисполненные ордера на продажу CURRENCY_1? for order in opened_orders: if order['type'] == 'sell': # Есть неисполненные ордера на продажу CURRENCY_1, выход raise ScriptQuitCondition('Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)') else: # Запоминаем ордера на покупку CURRENCY_1 sell_orders.append(order)         # Проверяем, есть ли открытые ордера на покупку CURRENCY_1         if sell_orders: # открытые ордера есть             for order in sell_orders:                 # Проверяем, есть ли частично исполненные                 if DEBUG:                     print('Проверяем, что происходит с отложенным ордером', order['order_id'])                 try:                     order_history = call_api('order_trades', order_id=order['order_id'])                     # по ордеру уже есть частичное выполнение, выход                     raise ScriptQuitCondition('Выход, продолжаем надеяться докупить валюту по тому курсу, по которому уже купили часть')                 except ScriptError as e:                     if 'Error 50304' in str(e):                         if DEBUG:                             print('Частично исполненных ордеров нет')                                              time_passed = time.time() + STOCK_TIME_OFFSET*60*60 - int(order['created'])                         if time_passed > ORDER_LIFE_TIME * 60:                             # Ордер уже давно висит, никому не нужен, отменяем                             call_api('order_cancel', order_id=order['order_id'])                             raise ScriptQuitCondition('Отменяем ордер -за ' + str(ORDER_LIFE_TIME) + ' минут не удалось купить '+ str(CURRENCY_1))                         else:                             raise ScriptQuitCondition('Выход, продолжаем надеяться купить валюту по указанному ранее курсу, со времени создания ордера прошло %s секунд' % str(time_passed))                     else:                         raise ScriptQuitCondition(str(e)) else: # Открытых ордеров нет balances = call_api('user_info')['balances'] if float(balances[CURRENCY_1]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать? """ Высчитываем курс для продажи. Нам надо продать всю валюту, которую купили, на сумму, за которую купили + немного навара и минус комиссия биржи При этом важный момент, что валюты у нас меньше, чем купили - бирже ушла комиссия 0.00134345 1.5045 """ wanna_get = CAN_SPEND + CAN_SPEND * (STOCK_FEE+PROFIT_MARKUP) # сколько хотим получить за наше кол-во print('sell', balances[CURRENCY_1], wanna_get, (wanna_get/float(balances[CURRENCY_1]))) new_order = call_api( 'order_create', pair=CURRENT_PAIR, quantity = balances[CURRENCY_1], price=wanna_get/float(balances[CURRENCY_1]), type='sell' ) print(new_order) if DEBUG: print('Создан ордер на продажу', CURRENCY_1, new_order['order_id']) else: # CURRENCY_1 нет, надо докупить # Достаточно ли денег на балансе в валюте CURRENCY_2 (Баланс >= CAN_SPEND) if float(balances[CURRENCY_2]) >= CAN_SPEND: # Узнать среднюю цену за AVG_PRICE_PERIOD, по которой продают CURRENCY_1 """ Exmo не предоставляет такого метода в API, но предоставляет другие, к которым можно попробовать привязаться. У них есть метод required_total, который позволяет подсчитать курс, но, во-первых, похоже он берет текущую рыночную цену (а мне нужна в динамике), а во-вторых алгоритм расчета скрыт и может измениться в любой момент. Сейчас я вижу два пути - либо смотреть текущие открытые ордера, либо последние совершенные сделки. Оба варианта мне не слишком нравятся, но завершенные сделки покажут реальные цены по которым продавали/покупали, а открытые ордера покажут цены, по которым только собираются продать/купить - т.е. завышенные и заниженные. Так что берем информацию из завершенных сделок. """ deals = call_api('trades', pair=CURRENT_PAIR) prices = [] for deal in deals[CURRENT_PAIR]: time_passed = time.time() + STOCK_TIME_OFFSET*60*60 - int(deal['date']) if time_passed < AVG_PRICE_PERIOD*60: prices.append(float(deal['price'])) try: avg_price = sum(prices)/len(prices) """ Посчитать, сколько валюты CURRENCY_1 можно купить. На сумму CAN_SPEND за минусом STOCK_FEE, и с учетом PROFIT_MARKUP ( = ниже средней цены рынка, с учетом комиссии и желаемого профита) """ # купить больше, потому что биржа потом заберет кусок my_need_price = avg_price - avg_price * (STOCK_FEE+PROFIT_MARKUP) my_amount = CAN_SPEND/my_need_price print('buy', my_amount, my_need_price) # Допускается ли покупка такого кол-ва валюты (т.е. не нарушается минимальная сумма сделки) if my_amount >= CURRENCY_1_MIN_QUANTITY: new_order = call_api( 'order_create', pair=CURRENT_PAIR, quantity = my_amount, price=my_need_price, type='buy' ) print(new_order) if DEBUG: print('Создан ордер на покупку', new_order['order_id']) else: # мы можем купить слишком мало на нашу сумму raise ScriptQuitCondition('Выход, не хватает денег на создание ордера') except ZeroDivisionError: print('Не удается вычислить среднюю цену', prices) else: raise ScriptQuitCondition('Выход, не хватает денег') except ScriptError as e: print(e) except ScriptQuitCondition as e: if DEBUG: print(e) pass except Exception as e: print("!!!!",e) while(True): main_flow() time.sleep(1)

Примечания по коду:

Строки 16 и 17  обозначают валютную пару. В данном примере это BTC_USD, но вы можете поменять на любую другую.

Строка 19  - CURRENCY_1_MIN_QUANTITY = 0.001. Это минимальная ставка, которая допускается на бирже. Для разных валют она разная, и, вообще, стоило бы получать её автоматически через API запрос. Но это усложнит код, поэтому я указал её как константу. Тем не менее, если вы планируете торговать другой валютой, вам следует поменять это значение, иначе торговля может затрудниться.

Строка 21 - ORDER_LIFE_TIME = 3. Если ордер на покупку не сыграл, то через сколько минут отменить его и создать новый, с новой ценой, более приближенной к текущим реалиям.

Строка 22 - STOCK_FEE = 0.002. Комиссия биржи за совершенную сделку. Непохоже, что бы она когда-то менялась, но, тем не менее, вы, при необходимости, сможете поменять её здесь если понадобится.

Строка 23 - AVG_PRICE_PERIOD = 90. Бот, в идеале, смотрит сделки за последние 90 минут, что бы узнать среднюю цену, в данной реализации он получает список совершенных сделок, и берет те из них, кто моложе 90 минут. Другой вопрос, что биржа не возвращает больше 100 записей, так что в данном случае число 90 сильно завышено.

Строка 24 - CAN_SPEND = 1.45. Важный параметр – сумма денег, которую вы доверяете боту для игры. В данном случае – 1 доллар 45 центов. Это удобно в том случае, когда бот играет на одну валютную пару, а вы – на другую, ну и еще гарантирует, что бот не проиграет всё, что нажито. В общем, чем больше эта сумма, тем больше денег он может заработать.

Строка 25  - PROFIT_MARKUP = 0.001. Это сумма наценки, которую вы хотите получить. В данном случае – это 0.1% от ставки. Чем больше это число, тем больше вы заработаете, но и курс будет раздуваться больше – т.е. вам придется дольше ждать исполнения сделки. Допускается дальнейшее дробление – например, число 0.00111 подходит. Если указать ноль, то бот будет работать вхолостую, обогащая биржу. Вы при этом, терять и зарабатывать не будете.

В строке 26 указано DEBUG = True. С этим параметром будет очень «разговорчивым», он будет комментировать каждое свое действие. Когда вам это надоест, советую вместо True написать False – тогда бот будет писать только по делу.

Так же не помешало бы в код добавить обработку некоторых исключительных ситуаций, перевести на ООП и так далее – но я не вижу смысла усложнять учебный код. Тот, кто заинтересуется, сможет сделать всё это и сам. Ну, или не делать, а просто пользоваться ботом как он есть :)

Реклама

Заключение

Надеюсь, этот бот будет для вас полезен – и буду признателен обратной связи. Расскажите, каких результатов вы добились при использовании, с какими трудностями столкнулись и какие моменты показались вам непонятными.

Желаю вам стабильных, хороших заработков!

bablofil.ru


Смотрите также

.