Как торговать через 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):

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

Судя по всему, поля 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 отрабатывают, правда на моем новеньком голеньком аккаунте нет ни денег, ни истории торгов, так что все выглядит печально, у вас должны быть другие результаты, весь экран в данных.

Заключение

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

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

Тэги: