Бот для Yobit

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

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

 


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

Последнее изменение:

Не забудьте рассказать друзьям об этой статье.
Чтобы поддержать ресурс Bablofil достаточно просто поделиться с друзьями этой статьей в социальных сетях. Каждый репост - это самая высокая оценка качества материала. Спасибо, что читаете этот блог.



Комментарии
14.01.2018 08:08:16
Бот работает! Ордера выставляет, снимает, все как надо.

в процессе выполнения пишет: ('Ошибка анализа возвращаемых данных, получена строка', b'')
Проголосовать Проголосовать
1 0
10.02.2018 18:36:54
{'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]]}
Проголосовать Проголосовать
0 0
15.01.2018 08:01:24
Добрый день. Бот не несомненно рабочий. Но есть пара моментов:
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>
Проголосовать Проголосовать
0 0
15.01.2018 09:14:22
Да, тоже вижу

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

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

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

 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')
Проголосовать Проголосовать
0 0
21.01.2018 12:16:56
Да, если делать частые запросы Yobit на какое-то время блокирует доступ к сайту. Ну и еще они регулярно заявляют о DDOSe и блокируют вообще всех ботов. Но это само по себе и проходит обычно. Мне много раз так писало.
Обычно это само проходит, если хотите запускать несколько ботов, то каждому дайте свои API ключи, и time.sleep(1) в каждом замените, что бы интервалы были дольше - поставьте time.sleep(3), например.
Проголосовать Проголосовать
0 0
20.01.2018 09:25:14
invalid nonce (has already been used)
 что то не так?
Проголосовать Проголосовать
0 0
21.01.2018 12:17:30
Лучше заведите отдельные новые API ключи и удалите файлик nonce рядом с ботом
Проголосовать Проголосовать
0 0
27.01.2018 14:54:00
што ето за файлик nonce?
Проголосовать Проголосовать
0 0
27.01.2018 16:13:58
В общем каждый раз, когда уходит авторизованный запрос к апи, должна быть переменная, которая каждый раз увеличивается (nonce). Обычно можно брать текущее время в миллисекундах и не париться, но ёбит ограничивает верхную планку. Поэтому бот начинает отчет с единицы и записывает последнее использованное значение в файл nonce, который создается рядом с ботом автоматически.
Эта переменная связана с АПИ ключами - если поменять ключи, то можно опять начинать передавать её с нуля, или с любого другого значения.
Проголосовать Проголосовать
0 0
21.01.2018 13:47:51
Добрый день. Получаю в ответ от сервера на метод 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
Проголосовать Проголосовать
1 0
21.01.2018 15:29:41
Защита от DDOS, маниакальная.
У меня тоже самое, хотя сегодня на Yobit не заходил и ботов не запускал.

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

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>
Проголосовать Проголосовать
0 0
21.01.2018 15:30:24
Защищаются от DDOS, смотрите коммент выше)
Проголосовать Проголосовать
0 0
21.01.2018 19:04:06
Бог программного кода, подскажи:
- как изменить минуты в секунды, при закрытии ордера;
- как сделать счетчик обращений по API к сайту за единицу времени (секунда или минута), что бы это контролировать и не попасть в бан;
- создать звуковое оповещение при выполнении ордера (в процессе модификации кода, под свои запросы, не удобно постоянно на него смотреть).
Проголосовать Проголосовать
0 0
21.01.2018 19:44:27
Если можно, то было бы хорошо получить примеры к твоему же коду)
Проголосовать Проголосовать
0 0
21.01.2018 20:29:57
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)
Проголосовать Проголосовать
0 0
21.01.2018 22:14:23
При много благодарен за информацию))))
По первому пункту, можно было додуматься и самому))))))
Про API в yobit написано, что позволяют 100 обращений в минуту, если мы сделаем 
"def call_api(**kwargs):
добавить
    time.sleep(1)",
то таким образом будем себя ущемлять в более чем полтора раза, а этого бы не хотелось))
Натолкните на мысль или подскажите умные статьи на эту тему, ну уж больно хочется ее использовать для более интересной логике))))
А с музычкой, еще более интереснее стало работать)))))
Проголосовать Проголосовать
0 0
22.01.2018 06:56:49
Ну так кто мешает ставить time.sleep(0.6) например? )
Проголосовать Проголосовать
0 0
22.01.2018 06:57:17
И тогда еще можно все другие time.sleep из кода убрать
Проголосовать Проголосовать
0 0
22.01.2018 18:00:04
Андрей, привет, теперь на Yobit.io тоже поставили clouflare, что с этим делать? Как это обойти можно?
Проголосовать Проголосовать
0 0
23.01.2018 11:36:36
Пока что не знаю, но для начала можно обратиться в техподдержку Yobit, может одумаются или в белые списки добавят?
Проголосовать Проголосовать
0 0
23.01.2018 11:52:18
Добрый день, подскажите пожалуйста, можно ли в одном обращении по API выставить сразу же несколько ордеров 'buy', 'sell' по разным валютным парам, если да, то пожалуйста можно пример или статьи где можно об этом почитать?
Проголосовать Проголосовать
0 0
23.01.2018 19:35:30
Добрый день.
Одним запросом, к сожалению, нельзя
Проголосовать Проголосовать
0 0
24.01.2018 21:15:18
Добрый вечер!
Установил питон, дальше по инструкции -  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
Не подскажите?
Проголосовать Проголосовать
0 0
25.01.2018 07:16:25
Это команду надо выполнять в командной строке windows, а вы, похоже пытаетесь выполнить в python shell.
Win+R -> cmd -> Enter -> pip install requests -> Enter
Проголосовать Проголосовать
1 0
24.01.2018 21:19:48
Сейчас ручками запустил pip, мелькнуло, установился?
Проголосовать Проголосовать
0 0
25.01.2018 08:50:19
Да, действительно через питон. 
Но еще в начале, на первом круге,  делал в командной строке windows - выдало какую-то ошибку, наверно при установке питона выставил не все галочки.
Дальше уже голова закружилась - сообразительности не хватило ).
Щас попробую бот.
Спасибо Вам
Проголосовать Проголосовать
0 0
27.01.2018 14:52:11
invalid nonce (has already been used) что делать где єго надо поменять? подскажите пожалуйста.
Проголосовать Проголосовать
0 0
27.01.2018 16:14:40
Советую завести отдельные ключи АПИ для бота, тогда всё будет работать.
Проголосовать Проголосовать
0 0
27.01.2018 23:01:36
Мне кажется из бота эксмо не вошел кусок про проверку частично исполненного ордера на покупку. Без него бот попадает в ситуацию, когда отменяет не до конца купленный ордер и потом выставляет на продажу CUR1 по дичайшей цене.
Проголосовать Проголосовать
0 0
28.01.2018 13:34:54
Подтверждаю, несколько раз уже сталкивался.

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

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

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

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

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


Проголосовать Проголосовать
0 0
29.01.2018 12:44:34
Я код не буду менять на сайте, т.к. моменты спорные, но если взять вот эту строчку
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 или сколько там изначально было, что бы после продажи вернуться на правильную сумму торгов..
Проголосовать Проголосовать
0 0
29.01.2018 15:21:49
Может проще было бы разветвить продажу в зависимости от исполнения ордера покупки? (полностью или частично). Да и новая переменная CAN_SPEND_part1 при частичном исполнении покупки напрашивается :)
Проголосовать Проголосовать
0 0
29.01.2018 16:02:22
Всё в ваших руках ;)
Проголосовать Проголосовать
0 0
29.01.2018 17:31:59
К сожалению, крайний опыт Бэйсика 25-летней давности, более навыков нет. :)
Проголосовать Проголосовать
0 0
29.01.2018 15:37:14
Андрей, добрый день!
Что прописать в коде для получения информации по количеству (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))
Проголосовать Проголосовать
0 0
29.01.2018 16:06:43
Уточните, пожалуйста, вопрос, кол-во (покупаемый amount) и так уже пишется после слова "кол-во"
Проголосовать Проголосовать
0 0
29.01.2018 17:20:42
Вопрос с приложением отравил Вам на почту)
Проголосовать Проголосовать
0 0
30.01.2018 07:04:31
Здравствуйте. Что в коде нужно поменять, что бы покупал по нынешней цене, а не ставил ордер по цене из правого стакана?
Проголосовать Проголосовать
0 0
30.01.2018 07:30:32
Здравствуйте.
Нужно
prices = [bid[0] for bid in offers['bids']]
заменить на 
prices = [ask[0] for ask in offers['asks']]
Проголосовать Проголосовать
1 0
31.01.2018 02:53:50
Здравствуйте!
Не очень понятны моменты:
- почему не использовать Int(time.time()) в nonce? Там вроде до 2038 года лимит на бирже
- у меня бот первый делом начинает продавать, почему не закупать ?) Причём продавать весь баланс Валюты 1)))) или какая логика?
- В моменте продажи (если до этого руками, что-то купил, ибо первым делом он хочет продавать, и закупиться проблемно), ценник он выставляет раз в 10 больше текущей цены))) Как так происходит, не могу понять...
- в wanna_get у меня получилась немного иная формула (CAN_SPEND*(1+STOCK_FEE) + CAN_SPEND * PROFIT_MARKUP) / (
1 - STOCK_FEE), в таком случае она сожрёт комиссию покупки...
Проголосовать Проголосовать
0 0
31.01.2018 05:49:38
Здравствуйте.
- Потому что требуется делать чаще одного запроса в секунду, а мили микро нано и т.п. биржа не принимает
- Да, сначала надо скинуть баланс альта в 0. К примеру, торгуя liza_rur нужно сначала продать всю лизу за рубли или другую валюту. А сейчас получается всю лизу, что он нашел, он пытается продать так, что бы выручить 5$ или 500$ или сколько вы там указали. Отсюда и ценник. Алгоритм работы аналогичен этому - https://bablofil.ru/bot-dlya-birjy-exmo/
- Цена покупки и так съедается за счет того, что на баланс падает меньше. Купив на 10$ вы получаете X = 10/Y-0.2%. Продавая X в ноль за 10$ Вы УЖЕ задираете цену за счет того, что валюты в наличии меньше на 0.2%. Ваша же формула даст повышенный курс еще на 0.2%
Проголосовать Проголосовать
0 0
31.01.2018 22:04:23
Всё-таки не понимаю, зачем нужно предварительно продавать все альты, тем более по очень экзотической цене :)
Как выйти в прибыль я так и не понял :(
Движение бота в 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 (рублям)
Проголосовать Проголосовать
0 0
01.02.2018 05:12:33
У меня тоже баланс почему то понизился незначительно.... Странно
Проголосовать Проголосовать
0 0
01.02.2018 08:21:40
Всем спасибо)
Я обновил код бота, теперь комиссия считается по новой формуле, а так же добавил проверку на наличие первой валюты на балансе и запрос на действие, если валюта есть.

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

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>
Проголосовать Проголосовать
0 0
01.02.2018 14:56:49
Да, именно от бана. Бывает, что нужно быстро выполнить несколько запросов, типичный случай - получить содержимое стаканов и выставить ордер по лучшей цене - тут явно секунду ждать во вред себе, т.к. на активной паре курс кардинально сменится.
Про кеш в 2 секунды я видел у Liqui, у Yobita не видел, по наблюдениям отдает корректно.
Ну и про бан - по правилам разрешено 100 запросов в минуту, но минуту они ждать не будут - опять же по наблюдениям банят через 10 секунд если кол-во запросов за этот период больше 15, но тут может быть динамический расчет.
В общем, время от времени можно 5 запросов подряд делать, но постоянно так делать не получится, надо где-то ускоряться, где-то замедляться.
Проголосовать Проголосовать
0 0
02.02.2018 06:13:07
Действительно, спасибо.
Вообще для таких ситуаций есть метод get(), который либо получает ключ словаря, либо возвращает None или указанное значение, без генерации исключения.
Но и в целом отлов исключения не помешает, т.к. могут быть проблемы сети, невалидные ключи и т.п.
Обновил код
Проголосовать Проголосовать
0 0
01.02.2018 01:26:13
А unixtime с секундами корректно сравниваются?
Проголосовать Проголосовать
0 0
01.02.2018 08:30:40
Не совсем понимаю вопроса..
unixtime - это и есть кол-во секунд, прошедших с начала эпохи UNIX.
Биржа отдает UTC время, и питон работает по UTC, ордера (по крайней мере, у меня) отменяются через 3 минуты - значит, корректно
Проголосовать Проголосовать
0 0
02.02.2018 16:03:26
Подскажите пожалуйста почему выходит это сообщение каждую 2 секунду
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)
Проголосовать Проголосовать
0 0
02.02.2018 16:56:48
Бот выставил ордер на продажу, и теперь ждет его исполнения. Когда кто-то купит всю валюту, что вы продаете, бот начнет новый круг купли/продажи
Проголосовать Проголосовать
0 0
03.02.2018 13:42:19
Здравствуйте, Андрей!
Все же, прошу еще раз вернуться к вопросу - что делать с частично исполненным ордером на покупку?
Ну вот нельзя боту довериться :) Да по полдня висит, хочет закупить, у курс уже тю-тю... Приходится постоянно его останавливать и отменять ордер на покупку, вручную продавать кусочек, какой уже закупил. И заново запускать. 
Пример:
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))
Проголосовать Проголосовать
0 0
08.02.2018 06:31:17
Что-то вот уже пару дней АПИ практически не работает. Тормозит дико. И на .ио домене тоже. У всех так?
Проголосовать Проголосовать
0 0
10.02.2018 17:11:10
Так а что автор пишет что 
"Еще убрал параметры минимальной цены для торгов - бот будет стараться продать все, что купил, и купить на сколько сказали, не работает, значит не работает. Оно в принципе так и было, только с параметром."

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

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

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

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

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'
))

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

- убрал:
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)  # сколько хотим получить за наше кол-во

Надеюсь это работает и кому нибудь поможет. 
Всем профит!
Проголосовать Проголосовать
1 0
03.03.2018 17:21:18
Помогите пожалуйста! Как сделать так чтобы бот после первой продажи прекращал работу?
Проголосовать Проголосовать
0 0
12.05.2018 15:27:22
Хороший вопрос)
Думаю, тут надо вставить
 
# Реализация алгоритма
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)

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

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. А бот остался ждать пока цена в левом стакане не станет больше чем в правом.

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

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

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

Например
print('Проверяем, что происходит с отложенным ордером', order['order_id'])
Замените на 
print('Проверяем, что происходит с отложенным ордером', order['order_id'], file=open("C:\log.txt", "a"))
Можете оставить две строки - и ту и эту, что бы и на экране видеть и в файл писать
Проголосовать Проголосовать
0 0
26.04.2018 10:05:19
Спасибо, как временное может и сгодится. Но хочется правильно и сразу :)
Чтобы создавался файл при его отсутствии в текущем каталоге, компрессию и ротацию если невозможно своими средствами организовать, возложу на средства linux.
Проголосовать Проголосовать
0 0
26.04.2018 10:13:31
Файл создаваться будет, а вообще ротацию и прочее логгинг умеет, поищите logging.handlers.RotatingFileHandler
Вот вроде бы неплохой пример (см. выбранный ответ)
https://stackoverflow.com/questions/40088496/how-to-use-pythons-rotatingfilehandler
Проголосовать Проголосовать
0 0
26.04.2018 10:20:52
в вашем примере много закрывающих скобок :)
Проголосовать Проголосовать
0 0
26.04.2018 10:23:08
Спасибо, поправил))
Проголосовать Проголосовать
0 0
26.04.2018 10:52:06
Спасибо, к сожалению,я не программист. Смогу только что-то поправить в готовом блоке для нашего бота. Если это нужно. А жизнь показывает, что нужно. Буду ждать Вашей реализации.
Проголосовать Проголосовать
0 0
09.05.2018 14:47:23
Можно-ли отменить время для отмены ордера на отмену ордера по удалению цены от ордера? как для bay так и для sell
Проголосовать Проголосовать
0 0
17.05.2018 15:06:33
Всем привет.
Кто ловит ошибку с Cloudflare - вот решение https://github.com/Anorov/cloudflare-scrape#usage
Проголосовать Проголосовать
0 0
19.05.2018 17:24:18
Приветствую всех!  Андрей, премного благодарен за ваши труды. Уже некоторое время вынашивал мысль, что надо как-то разобраться с API, но не знал, с какого боку подобраться. В программировании не силён, так, немного на РНР карябал лет несколько назад, ну и на Баше изредка по мере необходимости что либо простенькое рисую, а тут ну прямо в цвет всё, осталось хоть немного Python освоить, чтобы хоть по мелочи под себя можно было подстроить. 
  Второй день пять ботов одновременно гоняю на копейках пока, тьфу-тьфу полёт нормальный. Тут в посте о каком-то боте видел, что существует мультивалютный вариант. Вопрос: как заиметь? Ответить можно в мыл andr_aleks@mail.ru
  Кстати, лишнюю CURRENCY_1 можно от бота спрятать в ордер на продажу по нереальной цене, но только в другую пару.
Проголосовать Проголосовать
0 0
04.06.2018 18:13:10
Здравствуйте, у меня на балансе есть USD, я хочу торговать к DOGE, как мне настроить бота?
Проголосовать Проголосовать
0 0
Пожалуйста, авторизуйтесь, что бы оставить свой комментарий
Крипто-кошельки для помощи и благодарности проекту:

Bitcoin адрес проекта: [[address]]

Перевод на сумму [[value]] BTC получен. Спасибо!.
[[error]]

Ethereum адрес проекта: [[address]]