На горизонте неизвестный континент - биржа 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 и погонять её. Возьмем за основу код из статьи про Эксмобота, немного адаптируем, ну и запустим.
Скрипт:
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 отрабатывают, правда на моем новеньком голеньком аккаунте нет ни денег, ни истории торгов, так что все выглядит печально, у вас должны быть другие результаты, весь экран в данных.
Заключение
Теперь, когда вы умеете получать информацию по активным парам, курсам, можете создавать , отменять и проверять ордера, вам остается только выстроить логику работы бота – когда что покупать, когда что продавать, и т.п. За основу можете взять алгоритм работы эксмобота из соседней статьи этого цикла, и поменять методы и поля в них – и все у вас получится.
Желаю вам большого, стабильного и легкого заработка!
И еще вопрос как сделать что б все открытые ордера показывались
Причем, для истории торгов не утверждается (хотя подразумевается) что вернутся только ваши торги, так что лучше проверять)
И ого, у них уже под 6 000 пар)
balances = call_api(method="getInfo", funds=['eth'])
Работает. Я понял, что надо считывать в цикле. Но так как не знаю языка, не смог правильно написать код.
Как отфильтровать разделить список ticker_res_obj по парно, чтобы получить цены и сравнить между собой?
Если короче написать , то у меня трудности с сопоставлением и нахождением разности значений.
Из такого:
а={'a3':3,'a1':1,'a2':2,'a4':5,'a5':4}
b={'b1':2,'b3':3,'b2':1}
Надо получить:
{'b1': 1, 'b2': -1, 'b3': 0}
Её там быть не должно
Как вариант запустите бота на стороннем сервере, пример https://bablofil.ru/kak-zapustit-bota-na-servere/.
Можно попробовать выйти в интернет через мобильную сеть, и попробовать через неё.
Можно попробовать работать через proxy, но это будет грустно, медленно и постоянно отваливаться.
https://bablofil.ru/bot-dlya-birgi-yobit/
Замените yobit.net на yobit.io как временное решение
API_KEY = ''
API_SECRET = b''
IP подсунуть можно, но ведь и ответ сервера на этапе установления соединения уйдет на чужой IP, и ничего не выйдет
Действия с публичным API выполняются корректно, с приватным - нет.
Попробуйте создать API ключи новые, должно завестись
запрос отправляю так:
depth = requests.get('https://yobit.io/api/3/depth/ltc_btc?limit=1', timeout=60, proxies=proxies)
Тут выше в комментариях есть ссылка на библиотеку, которая позволяет эту защиту обходить
P.S. Просто мысли в слух.