Бот для liqui

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

Кстати говоря да, у меня же есть код бота для liqui.io..

Работает абсолютно идентично боту для эксмо.. Тот же алгоритм и тот же, фактически код.

На этом статья окончена :) Вставляйте API ключи и играйте.

Вот код:

import urllib, http.client
import os
import time
import json
# эти модули нужны для генерации подписи API
import hmac, hashlib

# ключи API, которые предоставила liqui
API_KEY = ''
# обратите внимание, что добавлена 'b' перед строкой
API_SECRET = b''

# Тонкая настройка
CURRENCY_1 = 'ltc' 
CURRENCY_2 = 'btc'

CURRENCY_1_MIN_QUANTITY = 0.0001 # минимальная сумма ставки 

ORDER_LIFE_TIME = 3 # через сколько минут отменять неисполненный ордер на покупку CURRENCY_1
STOCK_FEE = 0.002 # Комиссия, которую берет биржа (0.002 = 0.2%)
AVG_PRICE_PERIOD = 90 # За какой период брать среднюю цену
CAN_SPEND = 0.0002 # Сколько тратить CURRENCY_2 каждый раз при покупке CURRENCY_1
PROFIT_MARKUP = 0.001 # Какой навар нужен с каждой сделки? (0.001 = 0.1%)
DEBUG = True# True - выводить отладочную информацию, False - писать как можно меньше

STOCK_TIME_OFFSET = 0 # Если расходится время биржи с текущим 
NONCE = 1
CURR_DIR = os.path.dirname(os.path.abspath(__file__))

# базовые настройки
API_URL = 'api.liqui.io'
API_VERSION = 'api/3'

# Свой класс исключений
class ScriptError(Exception):
    pass
class ScriptQuitCondition(Exception):
    pass

CURRENT_PAIR = (CURRENCY_1 + '_' + CURRENCY_2).lower()

# все обращения к API проходят через эту функцию
def call_api(api_method, http_method="POST", public=True, **kwargs):

    payload = {}
    if not public:
        try:
            nonce_file = open(CURR_DIR + "/nonce", "r+")
        except FileNotFoundError:
            nonce_file = open(CURR_DIR + "/nonce", "w+")
        try:
            nonce = int(nonce_file.read())
        except ValueError:
            nonce = 0
            
        nonce = (NONCE if not nonce else nonce)+1
        nonce_file.close()

        nonce_file = open(CURR_DIR + "/nonce", "w")
        nonce_file.write(str(nonce))
        nonce_file.close()
        
        payload = {'nonce': nonce}
    
    if kwargs:
        payload.update(kwargs)
        
    payload =  urllib.parse.urlencode(payload)
    #print("payload", 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(API_URL)
    conn.request(http_method, ("" if public else '/tapi') + ("/"+API_VERSION if public else '')  + ("/"+ api_method  if public else ''), payload, headers)
    response = conn.getresponse().read()
    
    #print("response", response)
    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 main_flow():
    
    try:
        # Получаем список активных ордеров
        try:
            opened_orders_dict = call_api(api_method="", http_method="POST", public=False, method="ActiveOrders", pair=CURRENT_PAIR )['return']###
            opened_orders = []
            for order in opened_orders_dict:
                curr_order = opened_orders_dict[order]
                curr_order['order_id'] = order
                opened_orders.append(curr_order)
        except (KeyError, ScriptError):
            if DEBUG:
                print('Открытых ордеров нет')
            opened_orders = []
            
        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_history = call_api(api_method="", http_method="POST", public=False, method="OrderInfo", order_id=order['order_id'])['return'][order['order_id']]

                if order_history['status'] == 0 and order_history['start_amount'] != order_history['amount']:
                    
                    # по ордеру уже есть частичное выполнение, выход
                    raise ScriptQuitCondition('По ордеру уже есть частичное выполнение, выход, продолжаем надеяться докупить валюту по тому курсу, по которому уже купили часть')
                else:
                    if DEBUG:
                        print('Частично исполненных ордеров нет')
                    
                    time_passed = time.time() + STOCK_TIME_OFFSET*60*60 - int(order['timestamp_created'])

                    if time_passed > ORDER_LIFE_TIME * 60:
                        # Ордер уже давно висит, никому не нужен, отменяем
                        call_api(api_method="", http_method="POST", public=False, method="CancelOrder", order_id=order['order_id'])
                        raise ScriptQuitCondition('Отменяем ордер -за ' + str(ORDER_LIFE_TIME) + ' минут не удалось купить '+ str(CURRENCY_1))
                    else:
                        raise ScriptQuitCondition('Выход, продолжаем надеяться купить валюту по указанному ранее курсу')
                

        else: # Открытых ордеров нет
            balances = call_api(api_method="", http_method="POST", public=False, method="getInfo")['return']['funds']
            if float(balances[CURRENCY_1]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать?
                """
                    Высчитываем курс для продажи.
                    Нам надо продать всю валюту, которую купили, на сумму, за которую купили + немного навара и минус комиссия биржи
                    При этом важный момент, что валюты у нас меньше, чем купили - бирже ушла комиссия
                    0.00134345 1.5045
                """
                wanna_get =  "%.8f" % (CAN_SPEND + CAN_SPEND * (STOCK_FEE+PROFIT_MARKUP))  # сколько хотим получить за наше кол-во
                rate = "%.8f" % (float(wanna_get)/float(balances[CURRENCY_1]))
                print('sell', balances[CURRENCY_1], wanna_get, rate)
                
                new_order = call_api(api_method="", http_method="POST", public=False, method="Trade", pair=CURRENT_PAIR, type="sell", rate=rate, amount="%.8f" % (float(balances[CURRENCY_1]) - 0.0000001))['return']
            
                print(new_order)
                if DEBUG:
                    print('Создан ордер на продажу', CURRENCY_1, new_order['order_id'])
            else:
                # CURRENCY_1 нет, надо докупить
                # Достаточно ли денег на балансе в валюте CURRENCY_2 (Баланс >= CAN_SPEND)
                if float(balances[CURRENCY_2]) >= CAN_SPEND:
                    # Узнать среднюю цену за AVG_PRICE_PERIOD, по которой продают CURRENCY_1

                    deals =  call_api(api_method="trades/"+CURRENT_PAIR)
                    prices = []
                    for deal in deals[CURRENT_PAIR]:
                        time_passed = time.time() + STOCK_TIME_OFFSET*60*60 - int(deal['timestamp'])
                        if time_passed < AVG_PRICE_PERIOD*60:
                            prices.append(float(deal['price']))
                    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', my_amount, my_need_price)
                        
                        # Допускается ли покупка такого кол-ва валюты (т.е. не нарушается минимальная сумма сделки)
                        if my_amount >= CURRENCY_1_MIN_QUANTITY:

                            new_order = call_api(api_method="", http_method="POST", public=False, method="Trade", pair=CURRENT_PAIR, type="buy", rate=my_need_price, amount=round(my_amount,8))['return']

                            print(new_order)
                            if DEBUG:
                                print('Создан ордер на покупку', new_order['order_id'])
                            
                        else: # мы можем купить слишком мало на нашу сумму
                            raise ScriptQuitCondition('Выход, не хватает денег на создание ордера')
                    except ZeroDivisionError:
                        print('Не удается вычислить среднюю цену', prices)
                else:
                    raise ScriptQuitCondition('Выход, не хватает денег')
        
    except ScriptError as e:
        print(e)
    except ScriptQuitCondition as e:
        if DEBUG:
            print(e)
        pass

while(True):
    main_flow()
    print('*'*80)
    time.sleep(1)

 

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

Текущая статья
Комментарии: (2)
18.12.2017 22:46
Замучался с авторизацией. Ключ на бирже создан, разрешения даны, но бот ругается 
btc_usdt Открытых ордеров нет
invalid api key
Андрей, помогите, пожалуйста. С другими биржами все было ок. Заранее спасибо.
19.12.2017 07:40
Спать надо больше, перепутал ключ с сикретом :D
Пожалуйста, Авторизуйтесь что бы оставить свой комментарий