Бот для Yobit

Андрей К…
Последнее изменение:
143
2
0

В общем всем привет, 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)

Намного более подробной информации в ссылках в начале статьи и в других статьях цикла (см. ниже, над комментариями и НАД КНОПКАМИ ДОНАТА ;))

 

Это статья из цикла "Популярно о бирже"

Текущая статья
Комментарии: (143)
14.01.2018 08:08
Бот работает! Ордера выставляет, снимает, все как надо.

в процессе выполнения пишет: ('Ошибка анализа возвращаемых данных, получена строка', b'')
10.02.2018 18:36
{'asks': [[9.3e-06, 5508.39609503]], 'bids': [[9.28e-06, 268.85775861]]}
{'asks': [[9.3e-06, 5508.39609503]], 'bids': [[9.28e-06, 268.85775861]]}
{'asks': [[9.3e-06, 5508.39609503]], 'bids': [[9.28e-06, 268.85775861]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
Тайм слип равен 1, но цена может обновлятся быстрее чем один раз в секунду, сейчас 'asks' = 9.26 а в следующий проход цикла сразу 28, цена 27 пропущена, и я так понял, что бот ставит фиксированную заявку на 0.3%, и если цена равна 27, то он просто не поставит заявку по этой цене, и упустит прибль. Поэтому бот ставит очень мало корректных заявок на продажу, и в день совершает всего 5-8 зделок, хотя мог бы больше.

{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.26e-06, 1000]], 'bids': [[9.2e-06, 42.92101122]]}
{'asks': [[9.28e-06, 4166.41187387]], 'bids': [[9.26e-06, 74.23802501]]}
{'asks': [[9.26e-06, 1000]], 'bids': [[9.2e-06, 42.92101122]]}
{'asks': [[9.26e-06, 1000]], 'bids': [[9.2e-06, 42.92101122]]}
{'asks': [[9.26e-06, 1000]], 'bids': [[9.2e-06, 42.92101122]]}
{'asks': [[9.26e-06, 1000]], 'bids': [[9.2e-06, 42.92101122]]}
{'asks': [[9.26e-06, 1000]], 'bids': [[9.2e-06, 42.92101122]]}
15.01.2018 08:01
Добрый день. Бот не несомненно рабочий. Но есть пара моментов:
1) в процессе выполнения пишет: ('Ошибка анализа возвращаемых данных, получена строка', b'') - я так понимаю эта ошибка сообщения с API_SECRET
2) по прошествии некоторого времени, ориентировочно 15 мин вылетает вот это (как говорится хороший стук  наружу выйдет) Traceback (most recent call last):
  File "C:\Users\1234\Desktop\боты\yobit max.py", line 178, in <module>
    main_flow()
  File "C:\Users\1234\Desktop\боты\yobit max.py", line 83, in main_flow
    yobit_orders = call_api(method="ActiveOrders", pair=CURR_PAIR)['return']
  File "C:\Users\1234\Desktop\боты\yobit max.py", line 63, in call_api
    response = conn.getresponse().read()
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\http\client.py", line 1331, in getresponse
    response.begin()
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\http\client.py", line 297, in begin
    version, status, reason = self._read_status()
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\http\client.py", line 258, in _read_status
    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\socket.py", line 586, in readinto
    return self._sock.recv_into(b)
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\ssl.py", line 1009, in recv_into
    return self.read(nbytes, buffer)
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\ssl.py", line 871, in read
    return self._sslobj.read(len, buffer)
  File "C:\Users\1234\AppData\Local\Programs\Python\Python36-32\lib\ssl.py", line 631, in read
    v = self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out</module>
15.01.2018 09:14
Да, тоже вижу

('Ошибка анализа возвращаемых данных, получена строка', b'') - это означает, что Yobit вместо нормального ответа вернул пустую страницу
socket.timeout: The read operation timed out - значит что бот не смог дождаться ответа от сервера.

Скорее всего, это защита Yobit от ддоса, либо какая-то балансировка нагрузки либо отказ серверов на стороне биржи. Может быть, конечно, еще сетевые лаги, но вряд ли у разных людей из разных регионов были бы одинаковые проблемы. Так что предлагаю просто игнорировать эти ошибки, вреда от них вроде как нет..
16.01.2018 06:49
Красава, спасибо, еще не тестил, вот такого бота искал. Допилить его можно, чтобы он анализировал на какой паре лучше купить, а на какой продать. ххх/rur, xxx/usd, xxx/eth, xxx/btc
16.01.2018 08:23
как можно дописать, чтобы покупать с левого стакана, не выставляя ордер по цене правого стакана.
16.01.2018 14:20
Если правильно понял, то нужно
prices = [bid[0] for bid in offers['bids']]
заменить на
prices = [bid[0] for bid in offers['asks]]
16.01.2018 15:33
Не могли бы Вы подсказать как после этих ошибок поставить бот на рестарт, был бы очень признателен.
16.01.2018 15:41
Обновил код, теперь будет рестартовать
19.01.2018 20:37
Андрей, доброго времени суток.

я правильно понял, что свалился бан на меня?? 

 RESTART: C:\Users\volk\AppData\Local\Programs\Python\Python36-32\BTC_USD_YOBIT.py 
('Ошибка анализа возвращаемых данных, получена строка', b'html\n[if lt IE 7]> <html class="no-js ie6 oldie" lang="en-US"> <![endif]\n[if IE 7]>    <html class="no-js ie7 oldie" lang="en-US"> <![endif]\n[if IE 8]>    <html class="no-js ie8 oldie" lang="en-US"> <![endif]\n[if gt IE 8]><! <html class="no-js" lang="en-US"> <!--<![endif]-->\n<head>\n<title>Access denied | yobit.net used Cloudflare to restrict access</title>\n<meta charset="utf-8"/>\n<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>\n<meta content="IE=Edge,chrome=1" http-equiv="X-UA-Compatible"/>\n<meta content="noindex, nofollow" name="robots"/>\n<meta content="width=device-width,initial-scale=1,maximum-scale=1" name="viewport"/>\n<link href="/cdn-cgi/styles/cf.errors.css" id="cf_styles-css" media="screen,projection" rel="stylesheet" type="text/css"/>\n<!--[if lt IE 9]><link rel="stylesheet" id=\'cf_styles-ie-css\' href="/cdn-cgi/styles/cf.errors.ie.css" type="text/css" media="screen,projection" /><![endif]-->\n<style type="text/css">body{margin:0;padding:0}</style>\n<!--[if lte IE 9]><script type="text/javascript" src="/cdn-cgi/scripts/jquery.min.js"></script><![endif]-->\n<!--[if gte IE 10]><!--><script src="/cdn-cgi/scripts/zepto.min.js" type="text/javascript"></script><!--<![endif]-->\n<script src="/cdn-cgi/scripts/cf.common.js" type="text/javascript"></script>\n\n</head>\n<body>\n  <div id="cf-wrapper">\n    <div class="cf-alert cf-alert-error cf-cookie-error" data-translate="enable_cookies" id="cookie-alert">Please enable cookies.</div>\n    <div class="cf-error-details-wrapper" id="cf-error-details">\n      <div class="cf-wrapper cf-header cf-error-overview">\n        <h1>\n          <span class="cf-error-type" data-translate="error">Error</span>\n          <span class="cf-error-code">1015</span>\n          <small class="heading-ray-id">Ray ID: 3dfc8c74ad868e79 • 2018-01-19 20:29:52 UTC</small>\n        </h1>\n        <h2 class="cf-subheadline" data-translate="error_desc">You are being rate limited</h2>\n      </div><!-- /.header -->\n\n      <section></section><!-- spacer -->\n\n      <div class="cf-section cf-wrapper">\n        <div class="cf-columns two">\n          <div class="cf-column">\n            <h2 data-translate="what_happened">What happened?</h2>\n            <p>The owner of this website (yobit.net) has banned you temporarily from accessing this website.</p>\n          </div>\n\n          \n        </div>\n      </div><!-- /.section -->\n\n      <div class="cf-error-footer cf-wrapper">\n  <p>\n    <span class="cf-footer-item">Cloudflare Ray ID: <strong>3dfc8c74ad868e79</strong></span>\n    <span class="cf-footer-separator">•</span>\n    <span class="cf-footer-item"><span data-translate="your_ip">Your IP</span>: 37.235.175.68</span>\n    <span class="cf-footer-separator">•</span>\n    <span class="cf-footer-item"><span data-translate="performance_security_by">Performance & security by</span> <a data-orig-proto="https" data-orig-ref="www.cloudflare.com/5xx-error-landing?utm_source=error_footer" id="brand_link" target="_blank">Cloudflare</a></span>\n    \n  </p>\n</div><!-- /.error-footer -->\n\n\n    </div><!-- /#cf-error-details -->\n  </div><!-- /#cf-wrapper -->\n\n  <script type="text/javascript">\n  window._cf_translation = {};\n  \n  \n</script>\n\n</body>\n</html>\n')
21.01.2018 12:16
Да, если делать частые запросы Yobit на какое-то время блокирует доступ к сайту. Ну и еще они регулярно заявляют о DDOSe и блокируют вообще всех ботов. Но это само по себе и проходит обычно. Мне много раз так писало.
Обычно это само проходит, если хотите запускать несколько ботов, то каждому дайте свои API ключи, и time.sleep(1) в каждом замените, что бы интервалы были дольше - поставьте time.sleep(3), например.
20.08.2018 04:50
Кажется это похоже на нтмл код страницы с капчей. Эту страницу бы привести в читаемый вид, и решить капчу..
20.01.2018 09:25
invalid nonce (has already been used)
 что то не так?
21.01.2018 12:17
Лучше заведите отдельные новые API ключи и удалите файлик nonce рядом с ботом
27.01.2018 14:54
што ето за файлик nonce?
27.01.2018 16:13
В общем каждый раз, когда уходит авторизованный запрос к апи, должна быть переменная, которая каждый раз увеличивается (nonce). Обычно можно брать текущее время в миллисекундах и не париться, но ёбит ограничивает верхную планку. Поэтому бот начинает отчет с единицы и записывает последнее использованное значение в файл nonce, который создается рядом с ботом автоматически.
Эта переменная связана с АПИ ключами - если поменять ключи, то можно опять начинать передавать её с нуля, или с любого другого значения.
21.01.2018 13:47
Добрый день. Получаю в ответ от сервера на метод getInfo , html ответ вот такого вида. При чём происходит это не тлько для getInfo, а вообще для всех методов, даже публичных. Из браузера всё хорошо показывает. А вот когда пробую через скрипт или даже postman, выбивает такую ошибку + 503 error, и так целый день сегодня. Вчера же всё было отлично, изменения в код не вносил.


HTML
<html lang="en-US">
<head>
  <meta charset="utf-8"/>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
  <meta content="IE=Edge,chrome=1" http-equiv="X-UA-Compatible"/>
  <meta content="noindex, nofollow" name="robots"/>
  <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"/>
  <title>Just a moment...</title>
  <style type="text/css">
    html, body {width: 100%; height: 100%; margin: 0; padding: 0;}
    body {background-color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 100%;}
    h1 {font-size: 1.5em; color: #404040; text-align: center;}
    p {font-size: 1em; color: #404040; text-align: center; margin: 10px 0 0 0;}
    #spinner {margin: 0 auto 30px auto; display: block;}
    .attribution {margin-top: 20px;}
    @-webkit-keyframes bubbles { 33%: { -webkit-transform: translateY(10px); transform: translateY(10px); } 66% { -webkit-transform: translateY(-10px); transform: translateY(-10px); } 100% { -webkit-transform: translateY(0); transform: translateY(0); } }
    @keyframes bubbles { 33%: { -webkit-transform: translateY(10px); transform: translateY(10px); } 66% { -webkit-transform: translateY(-10px); transform: translateY(-10px); } 100% { -webkit-transform: translateY(0); transform: translateY(0); } }
    .bubbles { background-color: #404040; width:15px; height: 15px; margin:2px; border-radius:100%; -webkit-animation:bubbles 0.6s 0.07s infinite ease-in-out; animation:bubbles 0.6s 0.07s infinite ease-in-out; -webkit-animation-fill-mode:both; animation-fill-mode:both; display:inline-block; }
  </style>

    <script type="text/javascript">
  //<![CDATA[
  (function(){
    var a = function() {try{return !!window.addEventListener} catch(e) {return !1} },
    b = function(b, c) {a() ? document.addEventListener("DOMContentLoaded", b, c) : document.attachEvent("onreadystatechange", b)};
    b(function(){
      var a = document.getElementById('cf-content');a.style.display = 'block';
      setTimeout(function(){
        var s,t,o,p,b,r,e,a,k,i,n,g,f, MGlTAXz={"hrQSDD":+((!+[]+!![]+!![]+[])+(!+[]+!![]+!![]))};
        t = document.createElement('div');
        t.innerHTML="<a href='/'>x</a>";
        t = t.firstChild.href;r = t.match(/https?:\/\//)[0];
        t = t.substr(r.length); t = t.substr(0,t.length-1);
        a = document.getElementById('jschl-answer');
        f = document.getElementById('challenge-form');
        ;MGlTAXz.hrQSDD-=+((!+[]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]));MGlTAXz.hrQSDD*=+((!+[]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]));MGlTAXz.hrQSDD-=+((!+[]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]));MGlTAXz.hrQSDD+=+((!+[]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]));MGlTAXz.hrQSDD-=+((!+[]+!![]+[])+(!+[]+!![]));MGlTAXz.hrQSDD+=!+[]+!![]+!![]+!![];MGlTAXz.hrQSDD+=+((!+[]+!![]+!![]+[])+(+[]));a.value = parseInt(MGlTAXz.hrQSDD, 10) + t.length; '; 121'
        f.action += location.hash;
        f.submit();
      }, 4000);
    }, false);
  })();
  //]]>
</script>


</head>
<body>
  <table cellpadding="20" height="100%" width="100%">
    <tr>
      <td align="center" valign="middle">
          <div class="cf-browser-verification cf-im-under-attack">
  <noscript><h1 data-translate="turn_on_js" style="color:#bd2426;">Please turn JavaScript on and reload the page.</h1></noscript>
  <div id="cf-content" style="display:none">
    
    <div>
      <div class="bubbles"></div>
      <div class="bubbles"></div>
      <div class="bubbles"></div>
    </div>
    <h1><span data-translate="checking_browser">Checking your browser before accessing</span> yobit.net.</h1>
    
    <p data-translate="process_is_automatic">This process is automatic. Your browser will redirect to your requested content shortly.</p>
    <p data-translate="allow_5_secs">Please allow up to 5 seconds</p>
  </div>
   
  <form action="/cdn-cgi/l/chk_jschl" id="challenge-form" method="get">
    <input name="jschl_vc" type="hidden" value="3735564ce109c150d9c3fbced9ace785"/>
    <input name="pass" type="hidden" value="1516542060.16-X39OgvxYjR"/>
    <input id="jschl-answer" name="jschl_answer" type="hidden"/>
  </form>
</div>

          
          <div class="attribution">
            <a href="https://www.cloudflare.com/5xx-error-landing?utm_source=iuam" style="font-size: 12px;" target="_blank">DDoS protection by Cloudflare</a>
            <br/>
            Ray ID: 3e0ab02afa3d97e6
          </div>
      </td>
     
    </tr>
  </table>
</body>
</html>


Process finished with exit code 0
21.01.2018 15:29
Защита от DDOS, маниакальная.
У меня тоже самое, хотя сегодня на Yobit не заходил и ботов не запускал.

Лайфхак: Если заменить в коде все yobit.net на yobit.io то все прекрасно работает ))
21.01.2018 16:17
Действительно, но ваш лайфхак работает только для публичного апи, для трэйд апи к сожалению нет)
21.01.2018 16:21
сори, всё работает , я плохо продебажил)
21.01.2018 14:42
Добрый день. На этот код начал выдавать ошибки, подскажите, в чем причина

import json
import requests

res = requests.get('https://yobit.net/api/3/ticker/sys_rur') # получаем данные ticker'а
res_obj = json.loads(res.text) # переводим полученный текст в объект с данными

print("SELL: %0.8f" % res_obj['sys_rur']['sell'])
print("BUY: %0.8f" % res_obj['sys_rur']['buy'])



Traceback (most recent call last):
  File "D:\pyton\1.py", line 5, in <module>
    res_obj = json.loads(res.text) # переводим полученный текст в объект с данными
  File "C:\Users\yunvy\AppData\Local\Programs\Python\Python36-32\lib\json\__init__.py", line 354, in loads
    return _default_decoder.decode(s)
  File "C:\Users\yunvy\AppData\Local\Programs\Python\Python36-32\lib\json\decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Users\yunvy\AppData\Local\Programs\Python\Python36-32\lib\json\decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)



</module>
21.01.2018 15:30
Защищаются от DDOS, смотрите коммент выше)
21.01.2018 19:04
Бог программного кода, подскажи:
- как изменить минуты в секунды, при закрытии ордера;
- как сделать счетчик обращений по API к сайту за единицу времени (секунда или минута), что бы это контролировать и не попасть в бан;
- создать звуковое оповещение при выполнении ордера (в процессе модификации кода, под свои запросы, не удобно постоянно на него смотреть).
21.01.2018 19:44
Если можно, то было бы хорошо получить примеры к твоему же коду)
21.01.2018 20:29
1. заменить 
if time_passed > ORDER_LIFE_TIME * 60:
на 
if time_passed > ORDER_LIFE_TIME:
2. Самое простое, что можно сделать - это после
def call_api(**kwargs):
добавить
    time.sleep(1)
Тогда ни один запрос к апи не будет выполняться чаще одного раза в секунду. 
Счетчик обращений к API - это переменная nonce, которая увеличивается при каждом обращении к API. Можно использовать её для более интересной логики.
3. Вот эту строчку нужно вставить в самый верх

import winsound

А эти две - в любое место где нужен звук

soundfile = "c:/Windows/Media/chimes.wav"
winsound.PlaySound(soundfile, winsound.SND_FILENAME | winsound.SND_ASYNC)
21.01.2018 22:14
При много благодарен за информацию))))
По первому пункту, можно было додуматься и самому))))))
Про API в yobit написано, что позволяют 100 обращений в минуту, если мы сделаем 
"def call_api(**kwargs):
добавить
    time.sleep(1)",
то таким образом будем себя ущемлять в более чем полтора раза, а этого бы не хотелось))
Натолкните на мысль или подскажите умные статьи на эту тему, ну уж больно хочется ее использовать для более интересной логике))))
А с музычкой, еще более интереснее стало работать)))))
22.01.2018 06:56
Ну так кто мешает ставить time.sleep(0.6) например? )
22.01.2018 06:57
И тогда еще можно все другие time.sleep из кода убрать
22.01.2018 18:00
Андрей, привет, теперь на Yobit.io тоже поставили clouflare, что с этим делать? Как это обойти можно?
23.01.2018 11:36
Пока что не знаю, но для начала можно обратиться в техподдержку Yobit, может одумаются или в белые списки добавят?
23.01.2018 11:52
Добрый день, подскажите пожалуйста, можно ли в одном обращении по API выставить сразу же несколько ордеров 'buy', 'sell' по разным валютным парам, если да, то пожалуйста можно пример или статьи где можно об этом почитать?
23.01.2018 19:35
Добрый день.
Одним запросом, к сожалению, нельзя
24.01.2018 21:15
Добрый вечер!
Установил питон, дальше по инструкции -  pip install requests + Enter, выдало ошибку "SyntaxError: invalid syntax".
Погуглил, переустановил проставил галочки, тажа ошибка, указал местоположение - C:\Users\user\AppData\Local\Programs\Python\Python36-32\Scripts\pip.exe install requests. 
Опять не нравится:
SyntaxError: unexpected character after line continuation character
Менял слеши - C:\\ /  -- SyntaxError: invalid syntax
Не подскажите?
25.01.2018 07:16
Это команду надо выполнять в командной строке windows, а вы, похоже пытаетесь выполнить в python shell.
Win+R -> cmd -> Enter -> pip install requests -> Enter
24.01.2018 21:19
Сейчас ручками запустил pip, мелькнуло, установился?
25.01.2018 08:50
Да, действительно через питон. 
Но еще в начале, на первом круге,  делал в командной строке windows - выдало какую-то ошибку, наверно при установке питона выставил не все галочки.
Дальше уже голова закружилась - сообразительности не хватило ).
Щас попробую бот.
Спасибо Вам
27.01.2018 14:52
invalid nonce (has already been used) что делать где єго надо поменять? подскажите пожалуйста.
27.01.2018 16:14
Советую завести отдельные ключи АПИ для бота, тогда всё будет работать.
27.01.2018 23:01
Мне кажется из бота эксмо не вошел кусок про проверку частично исполненного ордера на покупку. Без него бот попадает в ситуацию, когда отменяет не до конца купленный ордер и потом выставляет на продажу CUR1 по дичайшей цене.
28.01.2018 13:34
Подтверждаю, несколько раз уже сталкивался.

Пример: выставляем ордер на покупку 10 ед. по цене 100

Ордер исполняется частично, например, куплено 1 ед.

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

Подскажите, как заставить его все же закупить недостающие части для выставления полных 10 единиц на продажу?
28.01.2018 16:47
Ваша правда, часть логики потерялась.
Изменил статью, добавил проверку частично исполненной покупки.
29.01.2018 06:28
Спасибо, эта часть логики появилась. Но, может добавим еще чуть логики? ;)
Ситуация та же :) Сегодня столкнулся.
выставляем ордер на покупку 10 ед. по цене 100
ордер исполняется частично, куплено 9,5 ед.
бот ждет докупки, но курс уже ушел вверх и вернется когда к закупочному - не известно.
уже можно было бы продать с хорошим наваром, но бот ждет цены докупить 0,5 ед, которой нет и возможно долго не будет.
Варианты? (ИМХО)
1. Оценить вес остатка на докупку - менее 50-75%, фиг на него, продаем уже купленное по цене закупки + STOCK_FEE + PROFIT_MARKUP
Отменяем ордер на закупку недостающих 0.5
2. Отменяем ордер на закупку недостающих 0.5 по старой цене, остаток на докупку помещаем в стакан по лучшей цене покупки, после покупки, продаем все с учетом закупочных цен + STOCK_FEE + PROFIT_MARKUP

ИМХО, так он будет работать шустрее, а не ждать у моря погоды ;)


29.01.2018 12:44
Я код не буду менять на сайте, т.к. моменты спорные, но если взять вот эту строчку
if order_info ['status'] == 0 and order_info['start_amount'] == order_info['amount']:
и заменить на, скажем
if order_info ['status'] == 0 and float(order_info['amount'])/float(order_info['start_amount']) <= 0.05 :
то бот будет отменять хвост ордера, при условии что ордер выполнен на 95% или выше (и прошло 3 минуты или больше)
При этом надо понимать, что он и цену задерет на 5% или больше, т.к. купил меньше.
Теоретически, вы можете после 
call_api(method="CancelOrder", order_id=order['order_id']) 
добавить 
CAN_SPEND = CAN_SPEND - CAN_SPEND*float(order_info['amount'])/float(order_info['start_amount']) 
что бы он продавал без вот этих наценок, но тогда после
new_order = call_api(method="Trade", pair=CURR_PAIR, type="sell", rate=wanna_get/float(balances[CURRENCY_1]), amount=balances[CURRENCY_1])['return']
нужно сделать снова
CAN_SPEND = 100 или сколько там изначально было, что бы после продажи вернуться на правильную сумму торгов..
29.01.2018 15:21
Может проще было бы разветвить продажу в зависимости от исполнения ордера покупки? (полностью или частично). Да и новая переменная CAN_SPEND_part1 при частичном исполнении покупки напрашивается :)
29.01.2018 16:02
Всё в ваших руках ;)
29.01.2018 17:31
К сожалению, крайний опыт Бэйсика 25-летней давности, более навыков нет. :)
29.01.2018 15:37
Андрей, добрый день!
Что прописать в коде для получения информации по количеству (amount) криптовалюты по ордеру при известной цене (my_need_price)???

OFFERS_AMOUNT=1
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)
                        my_need_price = avg_price# - avg_price * (STOCK_FEE+PROFIT_MARKUP) 
                        my_amount = CAN_SPEND/my_need_price
print('sell: кол-во {amount:0.8f}, курс: {rate:0.8f}'.format(amount=my_amount, rate=my_need_price))
29.01.2018 16:06
Уточните, пожалуйста, вопрос, кол-во (покупаемый amount) и так уже пишется после слова "кол-во"
29.01.2018 17:20
Вопрос с приложением отравил Вам на почту)
30.01.2018 07:04
Здравствуйте. Что в коде нужно поменять, что бы покупал по нынешней цене, а не ставил ордер по цене из правого стакана?
30.01.2018 07:30
Здравствуйте.
Нужно
prices = [bid[0] for bid in offers['bids']]
заменить на 
prices = [ask[0] for ask in offers['asks']]
31.01.2018 02:53
Здравствуйте!
Не очень понятны моменты:
- почему не использовать Int(time.time()) в nonce? Там вроде до 2038 года лимит на бирже
- у меня бот первый делом начинает продавать, почему не закупать ?) Причём продавать весь баланс Валюты 1)))) или какая логика?
- В моменте продажи (если до этого руками, что-то купил, ибо первым делом он хочет продавать, и закупиться проблемно), ценник он выставляет раз в 10 больше текущей цены))) Как так происходит, не могу понять...
- в wanna_get у меня получилась немного иная формула (CAN_SPEND*(1+STOCK_FEE) + CAN_SPEND * PROFIT_MARKUP) / (
1 - STOCK_FEE), в таком случае она сожрёт комиссию покупки...
31.01.2018 05:49
Здравствуйте.
- Потому что требуется делать чаще одного запроса в секунду, а мили микро нано и т.п. биржа не принимает
- Да, сначала надо скинуть баланс альта в 0. К примеру, торгуя liza_rur нужно сначала продать всю лизу за рубли или другую валюту. А сейчас получается всю лизу, что он нашел, он пытается продать так, что бы выручить 5$ или 500$ или сколько вы там указали. Отсюда и ценник. Алгоритм работы аналогичен этому - https://bablofil.ru/bot-dlya-birjy-exmo/
- Цена покупки и так съедается за счет того, что на баланс падает меньше. Купив на 10$ вы получаете X = 10/Y-0.2%. Продавая X в ноль за 10$ Вы УЖЕ задираете цену за счет того, что валюты в наличии меньше на 0.2%. Ваша же формула даст повышенный курс еще на 0.2%
31.01.2018 22:04
Всё-таки не понимаю, зачем нужно предварительно продавать все альты, тем более по очень экзотической цене :)
Как выйти в прибыль я так и не понял :(
Движение бота в 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 (рублям)
01.02.2018 05:12
У меня тоже баланс почему то понизился незначительно.... Странно
01.02.2018 08:21
Всем спасибо)
Я обновил код бота, теперь комиссия считается по новой формуле, а так же добавил проверку на наличие первой валюты на балансе и запрос на действие, если валюта есть.

Касаемо алгоритма, поясню - бот простой, и делает то, что ему сказано, подробности и блок-схема тут https://bablofil.ru/bot-dlya-birjy-exmo/.
Когда вы говорите ему - я хочу со ста рублей получить 101 рубль, он смотрит, есть ли чего продать - и, если есть на балансе есть валюта1, он продает так, что бы выручить 101 рубль. Если нет - он покупает на 100 рублей и идет снова к шагу 1.
Поэтому при запуске бота, особенно в первый раз, если на балансе была лиза и её было мало - он продавал дорого что бы получить 101 рубль. Если много - дешево, что бы уложиться в 101 рубль. Отсюда и экзотические цены.
Идеальная ситуация для бота - если лизы вообще нет, тогда он купит сколько надо и продаст как надо, и так до бесконечности. Именно поэтому от альтов лучше избавиться перед запуском.
Теперь, с новым кодом, если при запуске лиза есть, то бот выведет её количество, курс и желаемую цену, и попросит удостовериться, что вы желаете её продать.
01.02.2018 14:00
Спасибо!!
А вот Вы говорите, nonce записывается в файл, т.е. количество запросов чаще, чем 1 секунда. В данном вариант time.sleep() - 1 секунда, чисто для подстраховки (от бана)? И, не знаете, как реально отображает биржа? они пишут, что кэширование раз в 2 секунды. Бан, если не ошибают - 10 минут.
02.02.2018 05:37
Тогда для метода проверки баланса надо добавить исключение, баланс-то нулевой.

Traceback (most recent call last):
  File "yobit_bot.py", line 190, in <module>
    alt_balance = call_api(method="getInfo")['return']['funds'][CURRENCY_1.lower()]
KeyError: 'liza'


{code}
try:
    alt_balance = call_api(method="getInfo")['return']['funds'][CURRENCY_1.lower()]
    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:
    pass
{code}</module>
01.02.2018 14:56
Да, именно от бана. Бывает, что нужно быстро выполнить несколько запросов, типичный случай - получить содержимое стаканов и выставить ордер по лучшей цене - тут явно секунду ждать во вред себе, т.к. на активной паре курс кардинально сменится.
Про кеш в 2 секунды я видел у Liqui, у Yobita не видел, по наблюдениям отдает корректно.
Ну и про бан - по правилам разрешено 100 запросов в минуту, но минуту они ждать не будут - опять же по наблюдениям банят через 10 секунд если кол-во запросов за этот период больше 15, но тут может быть динамический расчет.
В общем, время от времени можно 5 запросов подряд делать, но постоянно так делать не получится, надо где-то ускоряться, где-то замедляться.
02.02.2018 06:13
Действительно, спасибо.
Вообще для таких ситуаций есть метод get(), который либо получает ключ словаря, либо возвращает None или указанное значение, без генерации исключения.
Но и в целом отлов исключения не помешает, т.к. могут быть проблемы сети, невалидные ключи и т.п.
Обновил код
01.02.2018 01:26
А unixtime с секундами корректно сравниваются?
01.02.2018 08:30
Не совсем понимаю вопроса..
unixtime - это и есть кол-во секунд, прошедших с начала эпохи UNIX.
Биржа отдает UTC время, и питон работает по UTC, ордера (по крайней мере, у меня) отменяются через 3 минуты - значит, корректно
02.02.2018 16:03
Подскажите пожалуйста почему выходит это сообщение каждую 2 секунду
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
02.02.2018 16:56
Бот выставил ордер на продажу, и теперь ждет его исполнения. Когда кто-то купит всю валюту, что вы продаете, бот начнет новый круг купли/продажи
03.02.2018 13:42
Здравствуйте, Андрей!
Все же, прошу еще раз вернуться к вопросу - что делать с частично исполненным ордером на покупку?
Ну вот нельзя боту довериться :) Да по полдня висит, хочет закупить, у курс уже тю-тю... Приходится постоянно его останавливать и отменять ордер на покупку, вручную продавать кусочек, какой уже закупил. И заново запускать. 
Пример:
start_amount 1.08
amount 1.06
Вот какая-то гадость отщипнула кусочек и мы ждем исполнения ордера вечность.
Ведь у нас уже есть по не исполненному ордеру информация:
amount
rate
т.е. есть количество и цена покупки хвостика. 
Помогите в этом случае просто тупо реализовать
1. Отменить затянувшийся ордер на покупку.
2. Создать ордер на продажу купленного хвостика
Количество есть у нас -  amount
А цену просто тупо добавить к  rate 0,6% (0,4 бирже, остальное чтобы не уйти в минус)
Вот где-то в этом кусочке надо, да?

        # Проверяем, есть ли открытые ордера на покупку 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))
08.02.2018 06:31
Что-то вот уже пару дней АПИ практически не работает. Тормозит дико. И на .ио домене тоже. У всех так?
10.02.2018 17:11
Так а что автор пишет что 
"Еще убрал параметры минимальной цены для торгов - бот будет стараться продать все, что купил, и купить на сколько сказали, не работает, значит не работает. Оно в принципе так и было, только с параметром."

Не ну не убрал же ничего. По минималке считает как и раньше

 alt_balance = call_api(method="getInfo")['return']['funds'].get(CURRENCY_1.lower(), 0)
10.02.2018 20:49
Речь идет о минимально допустимой сумме торгов - пропала CURRENCY_1_MIN_QUANTITY и связанная с ней логика
18.02.2018 12:10
Пользуюсь Вашим ботом, классная штука)
Правда столкнулся с проблемой, второй раз бот закупил валюту и поставил ордер на х2 больше чем сумма покупки, хоть PROFIT_MARKUP меньше и далее по кругу гоняет надпись мол Выход, ждём пока не исполняться/закроются, все ордера на продажу.
18.02.2018 13:30
Это потому что баланс крипты у вас внезапно уменьшился.
Например, бот играл rur_liza на 100 рублей, купил по 2 рубля 50 лиз. Потом он собирается взять всю лизу на балансе и продать так, что бы получить 101 рубль (курс будет по 2 рубля с копейками).
НО в это время кто-то (другой бот?) взял и продал половину лиз за USD, и на балансе теперь 25 лиз. Первый бот по прежнему пытается продать всю лизу на балансе, что бы выручить 101 рубль, но лиз всего 25 штук, и ему приходится продавать их по 4 рубля.
18.02.2018 14:52
Спасибо большое за информацию и оперативность!
18.02.2018 21:42
Так же хотел поинтересоваться, а какую логику можно прописать боту, что бы при спреде между торговыми стаканами меньше чем указанная(к примеру 1%) он не выставлял ордер?
19.02.2018 07:02
Полагаю, как то так - заменить

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('Выход, не хватает денег')

--------

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]
    if float(offers['asks'][0][0])/float(offers['bids'][0][0]) < 1.01:
        print("Слишком маленький спред, пропускаем")
    else:
        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('Выход, не хватает денег')

Не проверял, но должно работать )
19.02.2018 15:25
Огромное спасибо! Данная функция очень выручает!
19.02.2018 15:16
Добрый день Андрей!
Юзаю бота, спасибо вам за такую возможность - очень интересно.
Можете ли вы добавить в скрипт настройку : 1)чтобы она каким то образом определяла разницу между только что проданным и ещё не купленным по прошлым к примеру 30 последним сделкам биржи - для выставления ордера на покупку не на мини хаях , а чуть ниже?
2)чтобы бот мог менять свой навар (присматриваясь пару секунд к тренду после покупки, чтобы мог улавливать прыжок цены и пониал что не надо сейчас ордер на продажу ставить , и ставил чуть позже?
Скомкано написано, но думаю у вас подобные вопросы возникали.
25.02.2018 12:54
А возможно ли вписать в него код, чтоб присылал отчет о купле\продаже ордеров?
25.02.2018 13:40
Присылал куда? Если в телеграм, то легко, смотрите тут https://bablofil.ru/telegram-easy-way/
25.02.2018 13:59
Да, в телеграм. Но проблема в том что по этому коду:

import urllib.request
urllib.request.urlopen("""
    https://api.telegram.org/bot{API_TOKEN}/sendMessage?chat_id={CHAT_ID}&text={TEXT}
""".format(
    API_TOKEN = '515479662:AAF1wTj2Xcb3HVURbc_5DbZ9qiz4ddds9iQ',
    CHAT_ID = '303964420',
    TEXT = 'TEST TEST TEST'
))

он мне ничего не присылает, а как это вставить в сам скрипт бота, я понятия не имею
27.02.2018 08:36
Здравствуйте! А как изменить код чтоб можно было торговать на всю имеющуюся сумму не забивая ее вручную в код?
28.02.2018 10:45
Здравствуйте, тут много придется менять, т.к. бот сейчас продает с баланса всё купленное, что бы получить нужную сумму. Не будет нужной суммы - бот не будет знать, какую цену ставить.
03.05.2018 11:48
Андрей К.  - спасибо за труды! Сам вообще не программист, но благодаря Вам пришлось включить мозг и осваивать питон.
Видимо не так уж и много нужно изменить, для себя сегодня прикрутил (скорее всего криво - но это крутится) вместо фиксированной суммы процент от баланса базовой валюты:

- убрал:
CAN_SPEND = 'x'
и по аналогии с PROFIT_MARKUP = 0.01 # Какой навар нужен с каждой сделки? (0.001 = 0.1%)

+ добавил:
PERC_SPEND = 0.01 # Какой процент от депозита можем использовать для покупки? (0.001 = 0.1%):

- заменил:
def wanna_get():
    return  (CAN_SPEND*(1+STOCK_FEE) + CAN_SPEND * PROFIT_MARKUP) / (1 - STOCK_FEE)  # сколько хотим получить за наше кол-во

+ на:
def can_spend():
    balances = call_api(method="getInfo")['return']['funds']
    funds = float(balances.get(CURRENCY_2, 0))
    perc = PERC_SPEND*100
    spend = funds*PERC_SPEND
    return (spend)  # на сколько хотим закупать
def wanna_get():
    return (can_spend()*(1+STOCK_FEE) + can_spend() * PROFIT_MARKUP) / (1 - STOCK_FEE)  # сколько хотим получить за наше кол-во

- естественно далее заменил по коду все:
 CAN_SPEND

+ на:
can_spend()

если нужно можно в консоль сделать вывод, а-ля:
#Информация
print(' * Работаем по паре {pair} Баланс: {funds:0.8f} {curr} Используем {perc}%: {spend:0.8f} {curr}'.format(pair=CURR_PAIR, funds=float(balances.get(CURRENCY_2, 0)), curr=CURRENCY_2, perc=PERC_SPEND*100, spend=can_spend()))

Иван Г.  в вашем случае если поставить PERC_SPEND = 1 - т.е. 100%, не проверял и если не ошибаюсь получите
raise ScriptQuitCondition('Выход, не хватает денег')
т.к нужно учесть еще и комиссию которая вычитается в базовой валюты,  не уверен что ниже все будет все верно, но чую пятой точкой, что истина где-то рядом:
def can_spend():
    balances = call_api(method="getInfo")['return']['funds']
    funds = float(balances.get(CURRENCY_2, 0))
    perc = PERC_SPEND*100
    spend = (funds*PERC_SPEND)-(funds*STOCK_FEE)
    return (spend)  # на сколько хотим закупать

В случае если устойчивое желание стать долгосрочным инвестором Вас не покидает, то можно попробовать:
def can_spend():
    balances = call_api(method="getInfo")['return']['funds']
    funds = float(balances.get(CURRENCY_2, 0))
    spend = funds-(funds*STOCK_FEE)
    return (spend)  # замазываться в альты на всю котлету :)
def wanna_get():
    return (can_spend()*(1+STOCK_FEE) + can_spend() * PROFIT_MARKUP) / (1 - STOCK_FEE)  # сколько хотим получить за наше кол-во

Надеюсь это работает и кому нибудь поможет. 
Всем профит!
11.11.2018 21:34
С % бот работает в минус, основной баланс уменьшается с каждой сделкой. Бот не выставляет правильный ордер на продажу чтоб оправдать купленное, а продаёт по цене закупа.
03.03.2018 17:21
Помогите пожалуйста! Как сделать так чтобы бот после первой продажи прекращал работу?
12.05.2018 15:27
Хороший вопрос)
Думаю, тут надо вставить
 
# Реализация алгоритма
sell_created = False
def main_flow():

Тут вот так
if DEBUG:
                 print('Создан ордер на продажу', CURRENCY_1, new_order['order_id'])
                 sell_created=True

И вот тут поменять условие
while not sell_created:
    main_flow()
    time.sleep(1)

Тогда он выставит ордер на покупку, будет отслеживать, и выйдет как только будет куплено и выставлено на продажу. Дальнейшее ему все равно не интересно
12.03.2018 16:56
Здравствуйте! Выше есть кусок кода про СПЕРД :

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]
    if float(offers['asks'][0][0])/float(offers['bids'][0][0]) < 1.01:
        print("Слишком маленький спред, пропускаем")
    else:
        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('Выход, не хватает денег')
_____________________________________________________________________________

Попытались произвести замену как указано в комменте. Бот не запускается. Ругается на строку:
    offers = json.loads(requests.get("https://yobit.io/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT)).text)[CURR_PAIR]

Данная функция полезна при резком обвале курса, т.е. при искусственном занижении курса (когда в левый стакан выкидывают цены ниже чем в правом стакане), другими словами "Дампят" валютную пару.
Хотелось бы чтобы бот при резком падении курса не оставлял ордер на верхах, а закрывал его и выставлял новый с меньшим профитом, но не в минус.
Либо, бот должен фиксировать прибыль с каждой сделки, чтобы знать сколько заработано. Затем при ситуации с резким обвалом цены закрывал последний ордер не в минус относительно суммарной прибыли за время работы бота.
Например, за день заработали $10. Бот видит обвал цены и должен выставить ордер так, чтобы 80% прибыли за время работы бота остались у нас. Т.е. потеряли $2 на закрытии последнего ордера, но сохранили $8. А бот остался ждать пока цена в левом стакане не станет больше чем в правом.

Заранее спасибо!
26.04.2018 09:42
Здравствуйте, как именно ругается?
Питон чувствителен к отступам слева, проверьте, что число пробелов/табуляций правильное
26.04.2018 09:39
Здравствуйте всем!

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

Спасибо!
26.04.2018 09:50
Добрый день!
Вообще, у питона есть замечательный модуль logging, возможно попозже я обновлю код с его использованием.
Но как быстрое временное решение вы можете изменить вывод print, допишите в конец file=open("C:\log.txt", "a")

Например
print('Проверяем, что происходит с отложенным ордером', order['order_id'])
Замените на 
print('Проверяем, что происходит с отложенным ордером', order['order_id'], file=open("C:\log.txt", "a"))
Можете оставить две строки - и ту и эту, что бы и на экране видеть и в файл писать
26.04.2018 10:05
Спасибо, как временное может и сгодится. Но хочется правильно и сразу :)
Чтобы создавался файл при его отсутствии в текущем каталоге, компрессию и ротацию если невозможно своими средствами организовать, возложу на средства linux.
26.04.2018 10:13
Файл создаваться будет, а вообще ротацию и прочее логгинг умеет, поищите logging.handlers.RotatingFileHandler
Вот вроде бы неплохой пример (см. выбранный ответ)
https://stackoverflow.com/questions/40088496/how-to-use-pythons-rotatingfilehandler
26.04.2018 10:20
в вашем примере много закрывающих скобок :)
26.04.2018 10:23
Спасибо, поправил))
26.04.2018 10:52
Спасибо, к сожалению,я не программист. Смогу только что-то поправить в готовом блоке для нашего бота. Если это нужно. А жизнь показывает, что нужно. Буду ждать Вашей реализации.
09.05.2018 14:47
Можно-ли отменить время для отмены ордера на отмену ордера по удалению цены от ордера? как для bay так и для sell
17.05.2018 15:06
Всем привет.
Кто ловит ошибку с Cloudflare - вот решение https://github.com/Anorov/cloudflare-scrape#usage
19.05.2018 17:24
Приветствую всех!  Андрей, премного благодарен за ваши труды. Уже некоторое время вынашивал мысль, что надо как-то разобраться с API, но не знал, с какого боку подобраться. В программировании не силён, так, немного на РНР карябал лет несколько назад, ну и на Баше изредка по мере необходимости что либо простенькое рисую, а тут ну прямо в цвет всё, осталось хоть немного Python освоить, чтобы хоть по мелочи под себя можно было подстроить. 
  Второй день пять ботов одновременно гоняю на копейках пока, тьфу-тьфу полёт нормальный. Тут в посте о каком-то боте видел, что существует мультивалютный вариант. Вопрос: как заиметь? Ответить можно в мыл andr_aleks@mail.ru
  Кстати, лишнюю CURRENCY_1 можно от бота спрятать в ордер на продажу по нереальной цене, но только в другую пару.
04.06.2018 18:13
Здравствуйте, у меня на балансе есть USD, я хочу торговать к DOGE, как мне настроить бота?
19.10.2018 20:45
здравствуйте, после запуска  в командной строке появляется бесконечно кол-во раз строка
!!!! key: expected bytes ore bytearray, but got 'str'
что это может означать?
19.10.2018 20:54
и второе:
если в коде 
API_SECRET = b''
в кавычка вставляю свою секретный api, то получаю вот такую ошибку
!!! [SSL: certificate_verify_failed] certificate verify failed: unable to get local issuer certificate ssl.c 1045

если кавычки оставляю пустыми, то из сообщения выше получаю ошибку
!!!! key: expected bytes ore bytearray, but got 'str'
20.10.2018 09:01
Здравствуйте, то, что вы вставляете  b'', это правильно, но ругается у вас на SSL сертификат, этому может быть несолько объяснений, например вы запускаете бота в корпоративной сети с собственным центром сертификации, или провайдер что-то мудрит, либо на бирже забыли продлить сертификат и он просрочен. 
Вот что попробуйте сделать (не обязательно всё, лучше делать по одному по порядку):
1. Заменить в коде все вхождения yobit.io на yobit.net
2. Заменить строку
    conn = http.client.HTTPSConnection("yobit.io", timeout=60)
на
    conn = http.client.HTTPConnection("yobit.io", timeout=60)
(убрана буква S)
3. Если ни то, ни другое не помогло, попробуйте строку
                    offers = json.loads(requests.get("https://yobit.io/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT)).text)[CURR_PAIR]
заменить на
                    offers = json.loads(requests.get("https://yobit.io/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT), verify=False).text)[CURR_PAIR]
В этом последнем случае он будет ворчать, но работать.
29.10.2018 19:15
Everything is great!!! I ordered rather easy bot for Yobit and Andrey did it for free!!! Succeed in everything
31.10.2018 21:48
Здравствуйте Андрей и все все все!
Пользую стаканного бота на yobit. Ещё по весне приносил до 2-3т.р. в день. Сейчас yobit (с лета) слился, похоже. Торгов нет практически. И стали те боты приносить по 100-200 р в день всего. Вопросы такие:
1. Кто-нибудь может [похвастаться] поделиться - сколько ваших ботов запускать и с какими суммами надо заходить, чтобы получалось хотя бы 1 т.р. в день (лучше 2-3 т.р.)? Есть такие?
Просто пора браться за дело, не совсем лёгкое (0 в программировании, стаканные боты подарил бойфренд).
2. Есть ли задумки или реализация какой-либо визуальной отчётности по разным ботам и разным биржам и вообще - как они все работают. Типа такой - картинки не знаю как вставлять, потому ссылка - http://eftel.ru/cron/primer_bota.jpg - здесь чётко всё так организовано было..
Или так и придётся каждого бота запускать ручками и смотреть [экран] логи? И как это всё контролировать, если всё будет на хостинге с поддержкой питона?

Спасибо всем ответившим!!!
12.11.2018 21:05
Друзья, выложил измененную версию этого бота на форум, вот ссылка
https://forum.bablofil.ru/topic/36-%D0%B1%D0%BE%D1%82-%D0%B4%D0%BB%D1%8F-yobit/?tab=comments#comment-38
12.11.2018 21:11
Добрый день.
Объемы упали везде, это не только на Yobit, это рынок в целом, ушли многие из тех, кто узнал про биткойин из телевизора прошлой осенью.
Что бы строить графики и сводить отчеты, нужно что бы каждый бот писал в базу, и брать суммарную информацию оттуда, анализировать логи грустное занятие.
25.12.2018 16:51
File "C:\Users\Gamers\Music\python\immov\doubl\Works - копия.py", line 78, in call_api
    conn.request("POST", "/tapi/", payload, headers)
  File "C:\Program Files\Python37\lib\http\client.py", line 1229, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "C:\Program Files\Python37\lib\http\client.py", line 1275, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "C:\Program Files\Python37\lib\http\client.py", line 1224, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "C:\Program Files\Python37\lib\http\client.py", line 1016, in _send_output
    self.send(msg)
  File "C:\Program Files\Python37\lib\http\client.py", line 956, in send
    self.connect()
  File "C:\Program Files\Python37\lib\http\client.py", line 1392, in connect
    server_hostname=server_hostname)
  File "C:\Program Files\Python37\lib\ssl.py", line 412, in wrap_socket
    session=session
  File "C:\Program Files\Python37\lib\ssl.py", line 850, in _create
    self.do_handshake()
  File "C:\Program Files\Python37\lib\ssl.py", line 1108, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1045)

поискал и нашел, что нужно через pip обновить модули. Обновил все, но ошибка так и осталась. Куда копать?
25.12.2018 19:22
Почему сертификат самоподписанный?
Вы либо в корпоративной сети со злыми админами, либо вирус у вас какой то..
Попробуйте, впрочем, заменить все вхождения yobit.net на yobit.io или наоборот
28.12.2018 13:55
админы :) провайдер внес ёбит в банлист
всё работает по адресу yobitex.net
29.12.2018 14:20
получаю ошибку 
ScriptError: ('Ошибка анализа возвращаемых данных, получена строка', b'')
что сделать, чтобы после такой ошибки бот не останавливался, а пытался рестартовать через какое то время (5-10 секунд)?
И вообще после какой то ошибки авторестарт как сделать?
30.12.2018 19:04
Вообще он не должен останавливаться, он просто на следующий круг должен пойти
01.01.2019 15:54
останавливается зараза.
09.01.2019 11:52
У вас может быть несколько ботов запущено, и вас биржа блокирует?
Нужно одного запускать, тогда все будет ок
13.01.2019 04:59
Запущен 1 бот. Время между вызовами call_api увеличил до 1 секунды.
По поводу запуска нескольких мысль была, но я не знаю как биржа на это отреагирует. Биржа учитывает соединения с IP или соединения на аккаунт? Чтобы не забанили за Ddos
13.01.2019 07:23
Как минимум IP
31.12.2018 07:39
{'success': 1}
!!!! 'int' object does not support item assignment
как  победить такую ошибку?
31.12.2018 11:41
А вы код не правили?
Попробуйте сразу все вхождения yobit.net и yobit.io заменить на yobitex.net
Что то они мудрят там...
03.01.2019 01:57
Вышла вот такая ошибка. Как я понимаю две ошибки (или всё-таки одна). Тут ему не нравится json?
Если ScriptError поместить не в raise a в except  ошибки не будут останавливать программу?
 
Traceback (most recent call last):
  File "C:\Users\Gamers\Music\python\immov\doubl\Works.py", line 84, in call_api
    obj = json.loads(response.decode('utf-8'))
  File "C:\Program Files\Python37\lib\json\__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "C:\Program Files\Python37\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Program Files\Python37\lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Gamers\Music\python\immov\doubl\Works.py", line 437, in <module>
    proverka_otkritih_orders()
  File "C:\Users\Gamers\Music\python\immov\doubl\Works.py", line 219, in proverka_otkritih_orders
    alt_balance = call_api(method="getInfo")['return']['funds_incl_orders'].get(CURRENCY_1.lower(),0)
  File "C:\Users\Gamers\Music\python\immov\doubl\Works.py", line 90, in call_api
    raise ScriptError('Ошибка анализа возвращаемых данных, получена строка', response)
ScriptError: ('Ошибка анализа возвращаемых данных, получена строка', b'')</module>
09.01.2019 11:52
Это одна ошибка, просто в результате срабатывания возникает другая и образуется каскад ошибок.
Биржа возвращает пустую строку, бот не может распарсить JSON, ругается.
Вы можете, в принципе, заменить
raise ScriptError..

на 
pass

Если хотите игнорировать ошибки..
18.01.2019 18:58
плиииз
18.01.2019 18:17
nonnumeric port: '//yobitex.net/ru/api/keys/'
как решить? подскажите пожалуста
21.01.2019 06:39
Где вы это используете?
В HTTPSConnection нужно указать просто хост, без слешей и прочего, например yobitex.net
07.05.2019 20:00
Приветствую. Подскажите будьте так добры. Немного переделывал бота и столкнулся с вопросами.
1.Как обратиться к первому ордеру в стакане? 
2.Как обратиться к величине первого ордера?
float(offers['asks'][0][1]) верно ли это обращение?
3. ['asks'][0][1] что означает цифра 0 и цифра 1? обращение к  величине и месту ордера в стакане?
4. По какому параметру можно сравнить свой ордер и ордер в стакане?
Суть такова: нужно узнать является ли мой ордер первым или нет. Да - оставляем, Нет - отменяем.
В коде сравнение по величине, но как-то не всегда проходит
Часть кода: 
 
elif sell_orders:
            for order in sell_orders:
                order_info = call_api(method="OrderInfo", order_id=order['order_id'])['return'][str(order['order_id'])]
                if order_info ['status'] == 0 or float(order_info['amount']) < float(order_info['start_amount']):
                    time_passed = time.time() - int(order['timestamp_created'])
                    if time_passed > ORDER_LIFE_TIME * 60:
                        offers = json.loads(requests.get("https://yobit.net/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT)).text)[CURR_PAIR]
                        if float(offers['asks'][0][1]) == float(order_info['amount']):
                            print('Ордер на ПРОДАЖУ в топе, прошло %s секунд' % int(time_passed))
                        else:
                            call_api(method="CancelOrder", order_id=order['order_id'])
                            print('Отменяем ордер на продажу ' + str(CURRENCY_1))
                    else:
                        raise ScriptQuitCondition('Ждем, прошло %s секунд' % int(time_passed))
09.05.2019 09:50
Добрый день, всё правильно, asks[0] - это первый ордер в стакане
 
asks[0][0] - цена первого ордера
asks[0][1] - объем первого ордера
 
asks[1][0] - цена второго ордера
asks[1][1] - объем второго ордера
и т.п.
 
Добавьте вывод этих величин в лог
float(offers['asks'][0][1])
float(order_info['amount'])
что бы понимать, что и почему не срабатывает
 
Возможно, нужно будет оба значения округлять до 8 символов после запятой
А может быть ордер исполняется частично
А может быть к вашему ордеру присоседился кто-то и суммарный объем вырос
А может быть это чужой ордер с таким же объемом...
Видите сколько всего может быть?
 
Мне кажется вам нужно проверять не объем первого ордера, а цену, если цена вдруг выше, то это уже не ваш ордер
14.05.2019 10:36
Приветствую! Сходу такая проблема, и как её вылечить подскажите пожалуйста. :-)
  File "C:\yobit_bot.py", line 7
    import time
               ^
SyntaxError: invalid character in identifier
18.05.2019 08:14
Если делаете в IDLE, выполните file->new file, вставьте весь код туда и запускайте
Если в чем то другом, сначала сохраните в файл, потом запускайте его питоном
22.05.2019 17:29
Здравствуйте, попытался запустить бота...

C:\Users\RIP>python C:\Users\RIP\Desktop\yobit_bot\go.py
Открытых ордеров нет, начинаем новый цикл
Баланс основной валюты 0.00354212, % для торгов 100.00, итого на создание ордера будет потрачено 0.00354212
Traceback (most recent call last):
  File "C:\Users\RIP\Desktop\yobit_bot\go.py", line 183, in <module>
    main_flow()
  File "C:\Users\RIP\Desktop\yobit_bot\go.py", line 109, in main_flow
    offers = json.loads(requests.get("https://yobitex.net/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT)).text)[CURR_PAIR]
KeyError: 'usd_btc'

вот такая штука вылезает....</module>
25.05.2019 17:44
Здравствуйте, такой пары видимо нет, попробуйте местами поменять названия
30.07.2019 13:16
Здравствуйте, спасибо за бота!
Подскажите, пожалуйста, сделал небольшой ввод в размере 250 рублей.
Пробовал запускать с разными парами USD/RUR, ZEC/BTC, ETH/RUR (делая при этом перевод в нужную валюту), с суммой CAN_SPEND=200, а он мне каждый раз циклом пишет:
Открытых ордеров нет
Выход, не хватает денег
Открытых ордеров нет
Выход, не хватает денег
......................................
......................................
Открытых ордеров нет
Выход, не хватает денег
и так далее.
Я новичок в этом, неужели на эту сумму действительно нет ордеров? Самую последнюю пару ставил USD/RUR, видел что были ордера на продажу с более мелкими суммами, как быть?
Спасибо.
31.07.2019 12:45
Это сообщение значит, что у вас на балансе нет указанной суммы. В настройках бота стоит сумма, на которую нужно покупать, и на балансе её не хватает
03.09.2019 14:26
Приветствую Вас, подскажите пожалуйста, так как я еще "тот" юзер, я копирую код бота в питон и нажимаю энтер, после чего выходит такое сообщение:
SyntaxError: invalid syntax
      
   File "<stdin>", line 1
main_flow<>
IdentationError: unexpected indent
      time.sleep<1>
File "<stdin>", line 1
IdentationError: unexpected indent

Я что то не так сделал?
</stdin></stdin>
05.09.2019 13:33
Здравствуйте
Нужно нажать New File, вставить туда код, сохранить, потом только запускать (F5)
25.09.2019 19:48
со старта ругается на сертификат - [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'yobit.io'. (_ssl.c:1076)
27.09.2019 13:22
решено)
13.02.2020 07:26
С 12:00 мск вчера что-то не работает бот.
!!!! Expecting value: line 1 column 1 (char 0)
Что/где поправить?
13.02.2020 08:05
Очень похоже, что проблема тут:
offers = json.loads(requests.get("https://"+NET+"/api/3/depth/"+CURR_PAIR+"?limit="+str(OFFERS_AMOUNT)).text)[CURR_PAIR]
При этом, на сайте так:
{"ltc_rur":{"asks":[[5114.77289052,0.00045592],[5133.98446116,0.05815978],[5134.44346051,0.04067607],[5137,0.1],[5137.15246468,0.02526792],[5147.57321613,0.02166463],[5148.596376,0.0042888],[5160.26965078,0.04214037],[5169.69223052,0.01693753],[5183.79882538,0.00704865],[5184,0.00401],[5184.02285763,0.1027268],[5189,0.001],[5189.81740389,0.04542424],[5190,0.2],[5190.01595028,0.0084],[5200,0.44913681],[5200.45550267,0.00264508],[5206.8165731,0.01796306],[5208.05411049,0.0470664],[5226.01561441,0.00442371],[5234.66114782,0.01845164],[5273.4331337,0.01],[5294.84999969,0.00037931],[5298.48198769,0.00012],[5299.48198769,0.07],[5300,4.03375362],[5300.3717324,0.40395198],[5310.03381657,0.0004001],[5320.86496894,0.0002],[5350,0.1],[5357,0.0001],[5387,0.0001],[5400,0.10134],[5406.10400001,0.00324952],[5410,0.20608971],[5450,0.1],[5467.00000008,0.02236722],[5487,0.02874777],[5488.869,0.00054],[5495.05890121,0.0032418],[5499.48198769,0.07],[5500,1.67617336],[5506.66484724,0.00021],[5515.62712709,0.31316138],[5525.58280911,0.8184383],[5551,0.02280437],[5555,0.00500113],[5584.34886553,0.729],[5592.47270561,0.00327566],[5599,0.1],[5599.9999,0.99887],[5600,0.01738431],[5655.99920358,0.1],[5700,0.50135],[5703,0.012],[5738.88829858,0.00012],[5750,6.46564418],[5757.33000003,0.01919361],[5757.36,0.0001004],[5757.8801588,0.00321946],[5759.99999999,0.05345232],[5800,0.0015683],[5867.653,0.00053755],[5898.70566504,0.01705987],[5899.48198769,0.07],[5900,0.0661],[5915.00000001,0.06104895],[5985.58,0.0001015],[5989.99470415,0.5],[5995.53,0.05371413],[5999.9999,7.64657],[6000,3.89886705],[6020,0.1],[6026.1599664,0.00310764],[6029.9282736,0.00435858],[6040.30035454,0.0003331],[6040.4800087,0.00113212],[6050,0.1000001],[6070.91922061,0.00033142],[6074,0.000101],[6077.64325938,0.0001],[6080.64325938,0.00043089],[6091.3204205,0.00033031],[6092.79607575,0.00033023],[6100,0.0014],[6102.19042999,0.16806722],[6113.7472084,0.0001],[6114.74720841,0.0001],[6124.74720841,0.0001],[6131.7472084,0.0012],[6134.74720841,0.0001],[6144.89829306,0.00032743],[6149.40569118,0.00032719],[6151,0.0001],[6152,0.0001],[6153,0.0001],[6154,0.0001],[6155,0.0002],[6156,0.0001],[6157,0.0001],[6200,3.53026615],[6204.26000636,0.53090195],[6220,0.0001017],[6232.19043007,0.16806722],[6250,0.00016],[6250.79875186,0.00740496],[6300,1.15018978],[6300.56029132,0.89167987],[6316.19880516,0.16562849],[6341.70594678,0.01576862],[6399,0.0005],[6400,0.00129915],[6400.1682806,0.0554253],[6400.70480003,0.01390297],[6406.14789536,0.19214642],[6429,0.004],[6440,0.15],[6447,0.0005],[6463.27345309,0.0001044],[6464.869,0.00054],[6470,0.01277294],[6490,0.02],[6499,0.0004616],[6500,1.0011],[6506.00000003,0.01985919],[6515.42040713,0.2],[6525.08592877,0.0001],[6590,0.00012664],[6600,0.0011],[6677,0.03150836],[6700,0.08720296],[6725.08592877,0.00010002],[6740,0.01175624],[6750,0.00516155],[6760.32646705,0.01492415],[6800,0.0001],[6830,0.00012754],[6831,0.03093013],[6832,0.0001],[6833,0.0001],[6834,0.0001],[6835,0.0001],[6836,0.0001],[6837,0.0001],[6838,0.0001],[6839,0.0001],[6840,0.0001],[6841,0.0001],[6842,0.0001]],"bids":[[5095.17551958,0.02823319],[5086.02566789,1.08936273],[5086.02566734,1.09167891],[5086.02565623,0.1025633],[5068,0.002],[5067.79551117,0.03923838],[5067.1969246,0.00414984],[5042,0.002],[5040,0.99206349],[5038.35112,0.01914169],[5009.80186274,0.16630528],[4971.01287791,0.0002303],[4948,0.00222],[4928,0.00122],[4919,0.00121],[4918.02,0.00222],[4917.43069312,0.01985754],[4915,0.008056],[4909,0.0011],[4906,0.0001002],[4905,0.0001002],[4904.02,0.002462],[4904,0.0001002],[4903,0.0001002],[4902,0.0001002],[4901,0.0001002],[4900,0.1001002],[4899,0.0012002],[4898,0.0011002],[4897,0.0001002],[4896,0.0001002],[4895,0.0001002],[4894,0.0001002],[4893,0.0001002],[4892,0.0001002],[4891,0.1001002],[4890,1.8756545],[4889,0.0001002],[4888,0.0001002],[4887,0.0001002],[4885,0.00010028],[4884,0.00010028],[4883,0.00010028],[4882,0.00010028],[4881,0.00010028],[4880,0.00010028],[4879,0.00010028],[4878,0.00010028],[4877,0.00010028],[4876,0.51580653],[4801.13670634,0.00384023],[4800,0.74833333],[4799.41235649,0.02060017],[4796.99999999,0.10138204],[4796.67,0.64500077],[4750,0.38510622],[4710,0.00234042],[4707,0.002525],[4700,0.21611137],[4693.11942963,0.00011088],[4690,0.00639658],[4684.22645994,0.02137056],[4675.70630894,0.00042827],[4662,0.002106],[4657,0.002525],[4654,0.00211],[4650,0.21698924],[4638.61,0.71000059],[4607,0.002525],[4600,0.2673913],[4577,0.00505],[4571.80502491,0.02216977],[4557.1792873,0.0007103],[4555.1,0.05],[4555,0.01],[4550,0.18096472],[4515,0.02],[4500,0.1749621],[4498.94289092,0.00045539],[4490,0.02227171],[4477,0.00505],[4470.54796987,0.00011114],[4462.08170432,0.02299886],[4415,0.02],[4357,0.00505],[4355.99999897,1.03641484],[4354.99174342,0.02385896],[4350.12569341,0.02298784],[4350,0.0045977],[4340,0.01],[4320.77604152,0.95994672],[4309.99999897,1],[4305.99999897,1],[4305.00000334,0.0005],[4301,0.1],[4300.99999999,0.93382908],[4300,3.46100869],[4250.47194158,0.02475123],[4250,1.3965547],[4211.00000001,8.36377402],[4210.99999999,1.39209459],[4206.8165731,1.20931751],[4205,0.13055885],[4201,4.45336089],[4200,0.23714476],[4148.46061499,0.02567686],[4124.36,1.76667624],[4121.09397699,0.05175406],[4120.92731032,0.05123977],[4117.0000789,0.97571046],[4111,1.002],[4100,3.47466917],[4099.59221333,33.16473582],[4050.99999999,1.13989796],[4050.999,2],[4048.89756024,0.02663712],[4017.0000789,1.49184188],[4012.26,0.016],[4011.00000011,1],[4001.091,2],[4001,0.05],[4000,6.13090975],[3999,1],[3951.7240188,0.02763328],[3950.99999999,1.47700378],[3910,0.2714],[3906.28449293,0.01287271],[3906,3.62416794],[3905.32,0.48256814],[3900.18074558,0.0005],[3900,0.05472391],[3890.56124497,0.0087],[3888,1],[3879.50001,0.000101],[3856.88264235,0.02866669],[3839.10688593,0.00053267],[3800,0.14140189],[3799,0.01],[3772.80320082,0.01121111],[3764.31745894,0.02973876],[3764.09845096,0.00053146],[3738.30782289,0.00052163],[3736,0.00010117],[3735,0.0001012],[3734,0.00010123],[3733,0.00010125],[3732,0.00010128],[3731,0.00020262],[3730,0.00010134],[3728,0.00010139]]}}
01.03.2020 09:11
Такое бывает с йобитом, он прячется за проксирующими серверами, которые в какие-то моменты начинают не пускать ботов, особенно если их запущено несколько и т.п.
Попробуйте сменить домен (у йобита их три)
29.04.2020 20:27
Здравствуйте! Весьма признателен вам за вашу проделанную работу!
У меня возникла проблема при старте бота, пишет:
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
!!!! key: expected bytes or bytearray, but got 'str'
И так постоянно
29.04.2020 20:41
Сам поправил, все норм. Спасибо!
07.05.2020 00:17
Добрый вечер ребята. Что то я не пойму.
В строке CAN_SPEND = по умолчанию в коде стоит 100.
Ордер выставляется верно. Но как только я ставлю сумму 300, бот перед стартом пишет и главное выставляет ордер по огромному курсу.
У вас на балансе есть 0.02933799 ltc
 Вы действительно хотите, что бы бот продал все это по курсу 10276.88011387, выручив 301.50300601 rur?
Ну и мои настройки. Где ошибка?
ORDER_LIFE_TIME = 3 # через сколько минут отменять неисполненный ордер на покупку CURRENCY_1
STOCK_FEE = 0.002 # Комиссия, которую берет биржа (0.002 = 0.2%)
OFFERS_AMOUNT = 3 # Сколько предложений из стакана берем для расчета средней цены
CAN_SPEND = 300 # Сколько тратить CURRENCY_2 каждый раз при покупке CURRENCY_1 (300р в моем случае)
PROFIT_MARKUP = 0.001 # Какой навар нужен с каждой сделки? (0.001 = 0.1%)
DEBUG = False # True - выводить отладочную информацию, False - писать как можно меньше


У вас на балансе есть 0.02933799 ltc
            Вы действительно хотите, что бы бот продал все это по курсу 10276.88011387, выручив 301.50300601 rur?
            Введите Д/Y или Н/N
28.06.2020 13:05
Нужно, что бы при начале работы на балансе не было монет, иначе бот захочет их продать.
Самое простое - создайте ордер по другой паре с этой монетой с заведомо несбыточным курсом, тогда бот их не увидит и будет нормально покупать и продавать
11.05.2020 10:11
Добрый день Андрей.
Вы отвечали человеку по поводу создания звукового оповещения.
import winsound
А эти две - в любое место где нужен звук
soundfile = "c:/Windows/Media/chimes.wav"
winsound.PlaySound(soundfile, winsound.SND_FILENAME | winsound.SND_ASYNC)
Как я не пытался "прикрутить" звук на создание ордера - ни чего не выходит. (Синтаксическая ошибка)
Покажите пожалуйста живой пример. 
Последняя моя попытка вставить звук.
# Купить как есть, потом продать с учетом комиссии
                        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'])
							
			winsound.PlaySound("SystemExit", winsound.SND_ALIAS)
						
                                                
                    except ZeroDivisionError:
                        print('Не удается вычислить среднюю цену', prices)
                else:
                    raise ScriptQuitCondition('Выход, не хватает денег')
28.06.2020 13:09
Тут, судя по всему, какие то проблемы с отступами - для питона важно, сколько пробелов или табуляций слева от строки, и на строках выше/ниже.
Вы можете открыть файл в каком-то редакторе, например visual studio code или notepad++, и включить отображение непечатаемых символов.
15.05.2020 09:28
Кто может подсказать по такой стратегии:
Есть 2 аккаунта сумма в битках на втором в рублях
При падении цены в паре BTC-RUR закупаем битки и тут же выставляем ордер на продажу BTC за USD
т.е. при повышении или понижении всё-равно в профите.
Такое сможет сработать ?
И попутный вопрос: можно как-то в коде реализовать чтобы разные запросы к бирже с разными ключами API шли через разные прокси/vpn т.е. приходили на биржу с разных IP для уменьшения риска бана.
28.06.2020 13:13
Все различия курсов, которые могли бы дать прибыль, нещадно эксплуатируются ботами и конкурентами, поэтому купив монеты (и уплатив комиссии, кстати), вы как правило столкнетесь с тем, что продавать в данный момент уже невыгодно. Тем не менее это не значит, что не нужно пробовать.
По поводу разных ключей и VPN не могу сказать как отреагирует биржа, не пробовал, но технически конечно такое можно сделать

04.08.2020 17:48
Здравствуйте! Действительно каждый раз циклом пишет, как у пользователя выше:
Открытых ордеров нет
Выход, не хватает денег
Открытых ордеров нет
Выход, не хватает денег

CAN_SPEND=100
На балансе более 150 к. ???????????
10.04.2021 07:45
а если ордер будет частично исполнен то какой ответ "status: 0 - активен, 1 - исполнен и закрыт, 2 - отменен, 3 - отменен, но был частично исполнен" будет?
Пожалуйста, Авторизуйтесь что бы оставить свой комментарий