В общем всем привет, again.
Сегодня хочу выложить вам бота для биржи Yobit.net, собранного на основе бота для эксмо и материала из статьи про API для Yobita.
Это простой бот, картинку я выбрал что-то черезчур крутую :)
Немного упростил бота, и чуть-чуть изменил логику выставления цены. Этот бот, когда входит в торги, берет предложения из стакана, первые несколько штук (параметр OFFERS_AMOUNT) и выставляет цену по средней цене этих предложений.
Т.е. если указать OFFERS_AMOUNT = 3, то бот возьмет 3 цены с верхушки стакана, из них среднюю, и по ней выставит свою. Если указать OFFERS_AMOUNT=1, то выставит по текущей лучшей. Ну и т.п. Такая вот бешеная аналитика :)
Еще убрал параметры минимальной цены для торгов - бот будет стараться продать все, что купил, и купить на сколько сказали, не работает, значит не работает. Оно в принципе так и было, только с параметром.
В общем бот выставляет ордер на покупку, отслеживает, отменяет через указанное количество минут ORDER_LIFE_TIME если он все еще не выполнен. Если исполнен, то выставляет ордер на продажу. Тут небольшая хитрость с математикой, раз уж я её убрал при покупке (для возможности покупки по лучшей цене), то при продаже она возвращается и цена увеличивается сильнее чем для эксмо. Если там бот старался купить дешевле, что бы потом продать тоже дешевле, то тут бот покупает как есть, а при продаже учитывает обе комиссии + желаемый навар.
Не скажу, что бы я его очень сильно и долго гонял, но проверил, что ордера создаются, отменяются и продаются с наваром.. И решил с вами поделиться. Если что-то там не так, пишите, придумаем куда чего прикрутить.
В общем создавайте новые API ключи, вписывайте, запускайте, хорошего вам навара :)
Вот код:
import os
import sys
import json
import requests
import urllib, http.client
import hmac, hashlib
import time
# Вписываем свои ключи
API_KEY = ''
API_SECRET = b''
# Тонкая настройка
CURRENCY_1 = 'liza'
CURRENCY_2 = 'rur'
ORDER_LIFE_TIME = 3 # через сколько минут отменять неисполненный ордер на покупку CURRENCY_1
STOCK_FEE = 0.002 # Комиссия, которую берет биржа (0.002 = 0.2%)
OFFERS_AMOUNT = 1 # Сколько предложений из стакана берем для расчета средней цены
CAN_SPEND = 100 # Сколько тратить CURRENCY_2 каждый раз при покупке CURRENCY_1 (100р в моем случае)
PROFIT_MARKUP = 0.001 # Какой навар нужен с каждой сделки? (0.001 = 0.1%)
DEBUG = True # True - выводить отладочную информацию, False - писать как можно меньше
CURR_PAIR = CURRENCY_1.lower() + "_" + CURRENCY_2.lower()
"""
Каждый новый запрос к серверу должен содержать увеличенное число в диапазоне 1-2147483646
Поэтому храним число в файле поблизости, каждый раз обновляя его
"""
nonce_file = "./nonce"
if not os.path.exists(nonce_file):
with open(nonce_file, "w") as out:
out.write('1')
# Будем перехватывать все сообщения об ошибках с биржи
class ScriptError(Exception):
pass
class ScriptQuitCondition(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.io", 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 ScriptError(obj['error'])
return obj
except json.decoder.JSONDecodeError:
raise ScriptError('Ошибка анализа возвращаемых данных, получена строка', response)
def wanna_get():
return (CAN_SPEND*(1+STOCK_FEE) + CAN_SPEND * PROFIT_MARKUP) / (1 - STOCK_FEE) # сколько хотим получить за наше кол-во
# Реализация алгоритма
def main_flow():
try:
# Получаем список активных ордеров
opened_orders = []
try:
yobit_orders = call_api(method="ActiveOrders", pair=CURR_PAIR)['return']
for order in yobit_orders:
o = yobit_orders[order]
o['order_id']=order
opened_orders.append(o)
except KeyError:
if DEBUG:
print('Открытых ордеров нет')
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'])
# Получаем состояние ордера, если он еще не исполнен, отменяем
order_info = call_api(method="OrderInfo", order_id=order['order_id'])['return'][str(order['order_id'])]
if order_info ['status'] == 0 and order_info['start_amount'] == order_info['amount']: # ордер не исполнен, по нему ничего не куплено
time_passed = time.time() - int(order['timestamp_created'])
if time_passed > ORDER_LIFE_TIME * 60:
# Ордер уже давно висит, никому не нужен, отменяем
call_api(method="CancelOrder", 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(order_info))
else: # Открытых ордеров нет
balances = call_api(method="getInfo")['return']['funds']
if float(balances.get(CURRENCY_1, 0)) > 0: # Есть ли в наличии CURRENCY_1, которую можно продать?
"""
Высчитываем курс для продажи.
Нам надо продать всю валюту, которую купили, на сумму, за которую купили + немного навара и минус комиссия биржи
При этом важный момент, что валюты у нас меньше, чем купили - бирже ушла комиссия
0.00134345 1.5045
Поэтому курс продажи может получиться довольно высоким
"""
print('sell', balances[CURRENCY_1], wanna_get(), (wanna_get()/float(balances[CURRENCY_1])))
new_order = call_api(method="Trade", pair=CURR_PAIR, type="sell", rate="{rate:0.8f}".format(rate=wanna_get()/float(balances[CURRENCY_1])), amount="{amount:0.8f}".format(amount=balances[CURRENCY_1]))['return']
print(new_order)
if DEBUG:
print('Создан ордер на продажу', CURRENCY_1, new_order['order_id'])
else:
# CURRENCY_1 нет, надо докупить
# Достаточно ли денег на балансе в валюте CURRENCY_2 (Баланс >= CAN_SPEND)
if float(balances.get(CURRENCY_2, 0)) >= CAN_SPEND:
# Получаем информацию по предложениям из стакана
offers = json.loads(requests.get("https://yobit.io/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT)).text)[CURR_PAIR]
prices = [bid[0] for bid in offers['bids']]
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: кол-во {amount:0.8f}, курс: {rate:0.8f}'.format(amount=my_amount, rate=my_need_price))
# Допускается ли покупка такого кол-ва валюты (т.е. не нарушается минимальная сумма сделки)
new_order = call_api(method="Trade", pair=CURR_PAIR, type="buy", rate="{rate:0.8f}".format(rate=my_need_price), amount="{amount:0.8f}".format(amount=my_amount))['return']
print(new_order)
if DEBUG:
print('Создан ордер на покупку', new_order['order_id'])
except ZeroDivisionError:
print('Не удается вычислить среднюю цену', prices)
else:
raise ScriptQuitCondition('Выход, не хватает денег')
except ScriptError as e:
print(e)
except ScriptQuitCondition as e:
print(e)
except Exception as e:
print("!!!!",e)
try:
alt_balance = call_api(method="getInfo")['return']['funds'].get(CURRENCY_1.lower(),0)
if alt_balance > 0:
decision = input("""
У вас на балансе есть {amount:0.8f} {curr1}
Вы действительно хотите, что бы бот продал все это по курсу {rate:0.8f}, выручив {wanna_get:0.8f} {curr2}?
Введите Д/Y или Н/N
""".format(
amount=alt_balance,
curr1=CURRENCY_1,
curr2=CURRENCY_2,
wanna_get=wanna_get(),
rate=wanna_get()/alt_balance
))
if decision in ('N','n','Н','н'):
print("Тогда избавьтесь от {curr} и перезапустите бота".format(curr=CURRENCY_1))
sys.exit(0)
except Exception as e:
print(str(e))
while(True):
main_flow()
time.sleep(1)
Для тех, кто недавно присоединился:
1. Нужно скачать питон версии 3.6+ с официального сайта
2. После установки запустить командную строку (cmd) вбить туда pip install requests + Enter
3. Код, выложенный на сайте, скопировать в блокнот и сохранить (например yobit_bot.py)
3. Получить на Yobite ключи API и прописать в файл бота.
4. В настройках указать на какую пару хотите играть и на какую сумму
5. Запустить бота (в командной строке введите python путь_к_файлу_yobit_bot.py)
Намного более подробной информации в ссылках в начале статьи и в других статьях цикла (см. ниже, над комментариями и НАД КНОПКАМИ ДОНАТА ;))
в процессе выполнения пишет: ('Ошибка анализа возвращаемых данных, получена строка', b'')
('Ошибка анализа возвращаемых данных, получена строка', b'') - это означает, что Yobit вместо нормального ответа вернул пустую страницу
socket.timeout: The read operation timed out - значит что бот не смог дождаться ответа от сервера.
Скорее всего, это защита Yobit от ддоса, либо какая-то балансировка нагрузки либо отказ серверов на стороне биржи. Может быть, конечно, еще сетевые лаги, но вряд ли у разных людей из разных регионов были бы одинаковые проблемы. Так что предлагаю просто игнорировать эти ошибки, вреда от них вроде как нет..
Обычно это само проходит, если хотите запускать несколько ботов, то каждому дайте свои API ключи, и time.sleep(1) в каждом замените, что бы интервалы были дольше - поставьте time.sleep(3), например.
Эта переменная связана с АПИ ключами - если поменять ключи, то можно опять начинать передавать её с нуля, или с любого другого значения.
У меня тоже самое, хотя сегодня на Yobit не заходил и ботов не запускал.
Лайфхак: Если заменить в коде все yobit.net на yobit.io то все прекрасно работает ))
- как изменить минуты в секунды, при закрытии ордера;
- как сделать счетчик обращений по API к сайту за единицу времени (секунда или минута), что бы это контролировать и не попасть в бан;
- создать звуковое оповещение при выполнении ордера (в процессе модификации кода, под свои запросы, не удобно постоянно на него смотреть).
Одним запросом, к сожалению, нельзя
Пример: выставляем ордер на покупку 10 ед. по цене 100
Ордер исполняется частично, например, куплено 1 ед.
Бот, вместо того, чтобы подождать закупить оставшиеся 9 ед, выставляет ордер на продажу 1 ед. по конскому курсу 1000.
Подскажите, как заставить его все же закупить недостающие части для выставления полных 10 единиц на продажу?
Изменил статью, добавил проверку частично исполненной покупки.
Ситуация та же :) Сегодня столкнулся.
выставляем ордер на покупку 10 ед. по цене 100
ордер исполняется частично, куплено 9,5 ед.
бот ждет докупки, но курс уже ушел вверх и вернется когда к закупочному - не известно.
уже можно было бы продать с хорошим наваром, но бот ждет цены докупить 0,5 ед, которой нет и возможно долго не будет.
Варианты? (ИМХО)
1. Оценить вес остатка на докупку - менее 50-75%, фиг на него, продаем уже купленное по цене закупки + STOCK_FEE + PROFIT_MARKUP
Отменяем ордер на закупку недостающих 0.5
2. Отменяем ордер на закупку недостающих 0.5 по старой цене, остаток на докупку помещаем в стакан по лучшей цене покупки, после покупки, продаем все с учетом закупочных цен + STOCK_FEE + PROFIT_MARKUP
ИМХО, так он будет работать шустрее, а не ждать у моря погоды ;)
Не очень понятны моменты:
- почему не использовать Int(time.time()) в nonce? Там вроде до 2038 года лимит на бирже
- у меня бот первый делом начинает продавать, почему не закупать ?) Причём продавать весь баланс Валюты 1)))) или какая логика?
- В моменте продажи (если до этого руками, что-то купил, ибо первым делом он хочет продавать, и закупиться проблемно), ценник он выставляет раз в 10 больше текущей цены))) Как так происходит, не могу понять...
- в wanna_get у меня получилась немного иная формула (CAN_SPEND*(1+STOCK_FEE) + CAN_SPEND * PROFIT_MARKUP) / (
1 - STOCK_FEE), в таком случае она сожрёт комиссию покупки...
- Потому что требуется делать чаще одного запроса в секунду, а мили микро нано и т.п. биржа не принимает
- Да, сначала надо скинуть баланс альта в 0. К примеру, торгуя liza_rur нужно сначала продать всю лизу за рубли или другую валюту. А сейчас получается всю лизу, что он нашел, он пытается продать так, что бы выручить 5$ или 500$ или сколько вы там указали. Отсюда и ценник. Алгоритм работы аналогичен этому - https://bablofil.ru/bot-dlya-birjy-exmo/
- Цена покупки и так съедается за счет того, что на баланс падает меньше. Купив на 10$ вы получаете X = 10/Y-0.2%. Продавая X в ноль за 10$ Вы УЖЕ задираете цену за счет того, что валюты в наличии меньше на 0.2%. Ваша же формула даст повышенный курс еще на 0.2%
Как выйти в прибыль я так и не понял :(
Движение бота в 1 круг, взял Ваш пример LIZA/RUR:
Покупка: по цене 33,90615568 куплено 0,88479509 лиз = 30,00000006+комиссия = 30,06000006
Продажа: по цене 34,00807792 продано 0,88479509 лиз = 30,09018036-комиссия = 30.03000000
Итого: Было 30,06 рублей, стало 30,03 = -0,1%
И после периода работы боты итоговый баланс по RUR упал с 60,962 до 60,632 ... Было около 12 сделок (кругов), т.е. играл на полбаланса, и как раз получается примерно по -0,1% со сделки...
Все настройки такие же, только can_spend = 30 (рублям)
Я обновил код бота, теперь комиссия считается по новой формуле, а так же добавил проверку на наличие первой валюты на балансе и запрос на действие, если валюта есть.
Касаемо алгоритма, поясню - бот простой, и делает то, что ему сказано, подробности и блок-схема тут https://bablofil.ru/bot-dlya-birjy-exmo/.
Когда вы говорите ему - я хочу со ста рублей получить 101 рубль, он смотрит, есть ли чего продать - и, если есть на балансе есть валюта1, он продает так, что бы выручить 101 рубль. Если нет - он покупает на 100 рублей и идет снова к шагу 1.
Поэтому при запуске бота, особенно в первый раз, если на балансе была лиза и её было мало - он продавал дорого что бы получить 101 рубль. Если много - дешево, что бы уложиться в 101 рубль. Отсюда и экзотические цены.
Идеальная ситуация для бота - если лизы вообще нет, тогда он купит сколько надо и продаст как надо, и так до бесконечности. Именно поэтому от альтов лучше избавиться перед запуском.
Теперь, с новым кодом, если при запуске лиза есть, то бот выведет её количество, курс и желаемую цену, и попросит удостовериться, что вы желаете её продать.
А вот Вы говорите, nonce записывается в файл, т.е. количество запросов чаще, чем 1 секунда. В данном вариант time.sleep() - 1 секунда, чисто для подстраховки (от бана)? И, не знаете, как реально отображает биржа? они пишут, что кэширование раз в 2 секунды. Бан, если не ошибают - 10 минут.
Про кеш в 2 секунды я видел у Liqui, у Yobita не видел, по наблюдениям отдает корректно.
Ну и про бан - по правилам разрешено 100 запросов в минуту, но минуту они ждать не будут - опять же по наблюдениям банят через 10 секунд если кол-во запросов за этот период больше 15, но тут может быть динамический расчет.
В общем, время от времени можно 5 запросов подряд делать, но постоянно так делать не получится, надо где-то ускоряться, где-то замедляться.
unixtime - это и есть кол-во секунд, прошедших с начала эпохи UNIX.
Биржа отдает UTC время, и питон работает по UTC, ордера (по крайней мере, у меня) отменяются через 3 минуты - значит, корректно
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Правда столкнулся с проблемой, второй раз бот закупил валюту и поставил ордер на х2 больше чем сумма покупки, хоть PROFIT_MARKUP меньше и далее по кругу гоняет надпись мол Выход, ждём пока не исполняться/закроются, все ордера на продажу.
Например, бот играл rur_liza на 100 рублей, купил по 2 рубля 50 лиз. Потом он собирается взять всю лизу на балансе и продать так, что бы получить 101 рубль (курс будет по 2 рубля с копейками).
НО в это время кто-то (другой бот?) взял и продал половину лиз за USD, и на балансе теперь 25 лиз. Первый бот по прежнему пытается продать всю лизу на балансе, что бы выручить 101 рубль, но лиз всего 25 штук, и ему приходится продавать их по 4 рубля.
Юзаю бота, спасибо вам за такую возможность - очень интересно.
Можете ли вы добавить в скрипт настройку : 1)чтобы она каким то образом определяла разницу между только что проданным и ещё не купленным по прошлым к примеру 30 последним сделкам биржи - для выставления ордера на покупку не на мини хаях , а чуть ниже?
2)чтобы бот мог менять свой навар (присматриваясь пару секунд к тренду после покупки, чтобы мог улавливать прыжок цены и пониал что не надо сейчас ордер на продажу ставить , и ставил чуть позже?
Скомкано написано, но думаю у вас подобные вопросы возникали.
Питон чувствителен к отступам слева, проверьте, что число пробелов/табуляций правильное
Подскажите, как организовать вывод сообщений в отдельный лог-файл. Столкнулся недавно с исчезновением средств с баланса. Благо, наблюдал за работой робота, смог скопировать значения баланса до и после исчезновения средств и написать тикет администрации.
Спасибо!
Пользую стаканного бота на yobit. Ещё по весне приносил до 2-3т.р. в день. Сейчас yobit (с лета) слился, похоже. Торгов нет практически. И стали те боты приносить по 100-200 р в день всего. Вопросы такие:
1. Кто-нибудь может [похвастаться] поделиться - сколько ваших ботов запускать и с какими суммами надо заходить, чтобы получалось хотя бы 1 т.р. в день (лучше 2-3 т.р.)? Есть такие?
Просто пора браться за дело, не совсем лёгкое (0 в программировании, стаканные боты подарил бойфренд).
2. Есть ли задумки или реализация какой-либо визуальной отчётности по разным ботам и разным биржам и вообще - как они все работают. Типа такой - картинки не знаю как вставлять, потому ссылка - http://eftel.ru/cron/primer_bota.jpg - здесь чётко всё так организовано было..
Или так и придётся каждого бота запускать ручками и смотреть [экран] логи? И как это всё контролировать, если всё будет на хостинге с поддержкой питона?
Спасибо всем ответившим!!!
Объемы упали везде, это не только на Yobit, это рынок в целом, ушли многие из тех, кто узнал про биткойин из телевизора прошлой осенью.
Что бы строить графики и сводить отчеты, нужно что бы каждый бот писал в базу, и брать суммарную информацию оттуда, анализировать логи грустное занятие.
Вы либо в корпоративной сети со злыми админами, либо вирус у вас какой то..
Попробуйте, впрочем, заменить все вхождения yobit.net на yobit.io или наоборот
всё работает по адресу yobitex.net
Нужно одного запускать, тогда все будет ок
По поводу запуска нескольких мысль была, но я не знаю как биржа на это отреагирует. Биржа учитывает соединения с IP или соединения на аккаунт? Чтобы не забанили за Ddos
Попробуйте сразу все вхождения yobit.net и yobit.io заменить на yobitex.net
Что то они мудрят там...
В HTTPSConnection нужно указать просто хост, без слешей и прочего, например yobitex.net
Если в чем то другом, сначала сохраните в файл, потом запускайте его питоном
Подскажите, пожалуйста, сделал небольшой ввод в размере 250 рублей.
Пробовал запускать с разными парами USD/RUR, ZEC/BTC, ETH/RUR (делая при этом перевод в нужную валюту), с суммой CAN_SPEND=200, а он мне каждый раз циклом пишет:
Открытых ордеров нет
Выход, не хватает денег
Открытых ордеров нет
Выход, не хватает денег
......................................
......................................
Открытых ордеров нет
Выход, не хватает денег
и так далее.
Я новичок в этом, неужели на эту сумму действительно нет ордеров? Самую последнюю пару ставил USD/RUR, видел что были ордера на продажу с более мелкими суммами, как быть?
Спасибо.
Нужно нажать New File, вставить туда код, сохранить, потом только запускать (F5)
Попробуйте сменить домен (у йобита их три)
Самое простое - создайте ордер по другой паре с этой монетой с заведомо несбыточным курсом, тогда бот их не увидит и будет нормально покупать и продавать
Есть 2 аккаунта сумма в битках на втором в рублях
При падении цены в паре BTC-RUR закупаем битки и тут же выставляем ордер на продажу BTC за USD
т.е. при повышении или понижении всё-равно в профите.
Такое сможет сработать ?
И попутный вопрос: можно как-то в коде реализовать чтобы разные запросы к бирже с разными ключами API шли через разные прокси/vpn т.е. приходили на биржу с разных IP для уменьшения риска бана.
По поводу разных ключей и VPN не могу сказать как отреагирует биржа, не пробовал, но технически конечно такое можно сделать
Открытых ордеров нет
Выход, не хватает денег
Открытых ордеров нет
Выход, не хватает денег
CAN_SPEND=100
На балансе более 150 к. ???????????