Обновленный бот для Exmo

Добрый день.

Пытаюсь разобраться в чем различие в логике между ботом для Bittrex и Exmo (оба с подключенным MACD).

Первый упрощенный, но пока есть возможность потесить только его. На Битрексе закрыта регистрация. 

Первое, что заметил, это разница в проверке момента выхода на продажу, для первого бота (Эксмо)

BEAR_PERC = 70
BULL_PERC = 30

для второго бота (Битрекс)

BEAR_PERC = 70
BULL_PERC = 98

Но я так понял, что есть еще вторая часть изменений, когда берется минимальная цена покупки?

Или для обоих ботов алгоритм выбора цены покупки одинаков?

И еще вопрос, во втором боте (Битрекс) берется минимальный профит (0,1%) и, если есть такая возможность, продажа по максимальной цене, верно?

Можно ли эту часть кода прикрутить к первому боту?

Ну и вопрос по доп модификациям.

Прочитал вашу статью по стоп-лоссам. Понял, что при работе с криптовалютой вы к ним не очень хорошо относитесь.

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

Может изобретаю велосипед.. и это и так реализовано в вашем механизме при использовании МАСД, но тем не менее вопрос заинтересовал.

Спасибо за бота, что уже выложен.

 

 

 

16.01.2018 13:42:49

Добрый день!

Алгоритм покупки у всех местных ботов с macd одинаковый, но алгоритм выхода из торгов отличается. У того бота для эксмо, что выкладывал я, ордер выставляется после покупки с наценкой, и на этом всё.

Но умельцы на сайте сделали бота который как для биттрекса, но для Эксмо - т.е. прикрутили торговлю по нескольким парам, выход при цене побольше, и т.п. Спасибо им за это)

Вот код:

import time
import json
import requests
import urllib, http.client
import hmac, hashlib
 
# Если нет нужных пакетов - читаем тут: https://bablofil.ru/python-indicators/
import numpy 
import talib 
 
from datetime import datetime
 
# ключи API, которые предоставила exmo
API_KEY = 'K-            кей'
# обратите внимание, что добавлена 'b' перед строкой
API_SECRET = b'S-секрет'
 
# Список пар, на которые торгуем
MARKETS = [
    'BCH_USD', 'ETC_USD', 'ETH_USD',
    'ZEC_USD', 'DASH_USD' 
]
 
CAN_SPEND = 20 # Сколько USD готовы вложить в бай
MARKUP = 0.001 # 0.001 = 0.1% - Какой навар со сделки хотим получать
 
STOCK_FEE = 0.002 # Какую комиссию берет биржа
PERIOD = 5 # Период в минутах для построения свечей
ORDER_LIFE_TIME = 0.5 # Через сколько минут отменять неисполненный ордер на покупку 0.5 = 30 сек.
 
USE_MACD = True # True - оценивать тренд по MACD, False - покупать и продавать невзирая ни на что
 
BEAR_PERC = 70  # % что считаем поворотом при медведе (подробности - https://bablofil.ru/macd-python-stock-bot/
BULL_PERC = 99.9  # % что считаем поворотом при быке
 
#BEAR_PERC = 70  # % что считаем поворотом при медведе
#BULL_PERC = 100  # Так он будет продавать по минималке, как только курс пойдет вверх
 
API_URL = 'api.exmo.me'
API_VERSION = 'v1'
 
USE_LOG = False
DEBUG = False # True - выводить отладочную информацию, False - писать как можно меньше
 
numpy.seterr(all='ignore')
 
 
 
curr_pair = None
 
# Свой класс исключений
class ScriptError(Exception):
    pass
class ScriptQuitCondition(Exception):
    pass
     
# все обращения к API проходят через эту функцию
def call_api(api_method, http_method="POST", **kwargs):
     
    payload = {'nonce': int(round(time.time()*1000))}
 
    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(API_URL, timeout=90)
    conn.request(http_method, "/"+API_VERSION + "/" + api_method, 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 get_ticks(pair):
    resource = requests.get('https://api.exmo.me/v1/trades/?pair=%s&limit=10000' % pair)
    data = json.loads(resource.text)
     
    chart_data = {} # сформируем словарь с ценой закрытия по 5 минут
    for item in reversed(data[pair]):
        d = int(float(item['date'])/(PERIOD*60))*(PERIOD*60) # Округляем время сделки до PERIOD минут
        chart_data[d] = float(item['price']) 
    return chart_data
 
# С помощью MACD делаем вывод о целесообразности торговли в данный момент (https://bablofil.ru/macd-python-stock-bot/)
def get_macd_advice(chart_data):   
     
    macd, macdsignal, macdhist = talib.MACD(numpy.asarray([chart_data[item] for item in sorted(chart_data)]), fastperiod=12, slowperiod=26, signalperiod=9)
     
    idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
    inters = []
 
    for offset, elem in enumerate(macd):
        if offset in idx:
            inters.append(elem)
        else:
            inters.append(numpy.nan)
    trand = 'BULL' if macd[-1] > macdsignal[-1] else 'BEAR'
    hist_data = []
    max_v = 0
    growing = False
    for offset, elem in enumerate(macdhist):
        growing = False       
        curr_v = macd[offset] - macdsignal[offset]
        if abs(curr_v) > abs(max_v):
            max_v = curr_v
        perc = curr_v/max_v
         
        if       (   (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
                     or      (
                                 macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
                            )
 
                ):
            v = 1
            growing = True
        else:
            v = 0
             
        if offset in idx and not numpy.isnan(elem):
            # тренд изменился
            max_v = curr_v = 0 # обнуляем пик спреда между линиями
        hist_data.append(v*1000)
  
    return ({'trand':trand, 'growing':growing})
 
# Выводит всякую информацию на экран, самое важное скидывает в Файл log.txt
def log(*args):
     
    if USE_LOG:
        l = open("./log.txt", 'a', encoding='utf-8')
        print(datetime.now(), *args, file=l)
        l.close()
    print(datetime.now(),' ', *args)
 
# Ф-ция для создания ордера на покупку 
def create_buy(pair):
    global USE_LOG
    USE_LOG = True
    log(pair, 'Создаем ордер на покупку')
    log(pair, 'Получаем текущие курсы')
     
    offers = call_api('order_book', pair=pair)[pair]
    try:
        #current_rate =  float(offers['ask'][0][0]) # покупка по лучшей цене
        current_rate = sum([float(item[0]) for item in offers['ask'][:3]])/3 # покупка по средней цене из трех лучших в стакане
        can_buy = CAN_SPEND/current_rate
        print('buy', can_buy, current_rate)                            
        log(pair, """
            Текущая цена - %0.8f
            На сумму %0.8f %s можно купить %0.8f %s
            Создаю ордер на покупку
            """ % (current_rate, CAN_SPEND, pair[0], can_buy, pair[1])
        )       
        new_order = call_api(
            'order_create',
            pair = pair,
            quantity = can_buy,
            price = current_rate,
            type = 'buy'
            )                
        log("Создан ордер на покупку %s" % new_order['order_id'] )
    except ZeroDivisionError:
        print('Не удается вычислить цену', prices)
    USE_LOG = False   
     
# Ф-ция для создания ордера на продажу
def create_sell(pair):
    global USE_LOG
    USE_LOG = True
    balances = call_api('user_info')['balances']
    #if float(balances[pair[:-4]]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать?
    wanna_get = CAN_SPEND + CAN_SPEND * (STOCK_FEE + MARKUP)
    order_amount = float(balances[pair[:-4]])
    new_rate = wanna_get/order_amount
    new_rate_fee = new_rate/(1-STOCK_FEE)   
    offers = call_api('order_book', pair=pair)[pair]       
    current_rate = float(offers['bid'][0][0]) # Берем верхнюю цену, по которой кто-то покупает
    choosen_rate = current_rate if current_rate > new_rate_fee else new_rate_fee        
    print('sell', balances[pair[:-4]], wanna_get, choosen_rate)
    log(pair, """
    Итого на этот ордер было потрачено %0.8f %s, получено %0.8f %s
    Что бы выйти в плюс, необходимо продать купленную валюту по курсу %0.8f
    Тогда, после вычета комиссии %0.4f останется сумма %0.8f %s
    Итоговая прибыль составит %0.8f %s
    Текущий курс продажи %0.8f
    Создаю ордер на продажу по курсу %0.8f
    """
    % (
        wanna_get, pair[0], order_amount, pair[1],
        new_rate_fee,
        STOCK_FEE, (new_rate_fee*order_amount - new_rate_fee * order_amount * STOCK_FEE), pair[0],
        (new_rate_fee*order_amount - new_rate_fee*order_amount * STOCK_FEE)-wanna_get, pair[0],
        current_rate,
        choosen_rate,
        )
    )
    new_order = call_api(
        'order_create',
        pair=pair,
        quantity = balances[pair[:-4]],
        price= choosen_rate,
        type='sell'
    )
    log(pair, "Создан ордер на продажу %s" % new_order['order_id'])
    print(new_order)
    if DEBUG:
        print('Создан ордер на продажу', pair[:-4], new_order['order_id']) 
    USE_LOG = False
 
# Бесконечный цикл процесса - основная логика
while True:
    try:
        for pair in MARKETS: # Проходим по каждой паре из списка в начале\            
            try:
            # Получаем список активных ордеров
                try:
                    opened_orders = call_api('user_open_orders')[pair]
                except KeyError:
                    if DEBUG:
                        print('Открытых ордеров нет')
                        log(pair, "Открытых ордеров нет")    
                    opened_orders = []
                sell_orders = []
                # Есть ли неисполненные ордера на продажу CURRENCY_1?
                log(pair, " Обработка...")
                for order in opened_orders:
                    if order['type'] == 'sell':
                        # Есть неисполненные ордера на продажу CURRENCY_1, выход
                        raise ScriptQuitCondition('Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)')
                        # пропуск продажи
                        # pass
                    else:
                        # Запоминаем ордера на покупку CURRENCY_1
                        sell_orders.append(order)
                # Проверяем, есть ли открытые ордера на покупку CURRENCY_1
                if sell_orders: # открытые ордера есть
                    for order in sell_orders:
                        # Проверяем, есть ли частично исполненные
                        if DEBUG:
                            print('Проверяем, что происходит с отложенным ордером', order['order_id'])
                        try:
                            order_history = call_api('order_trades', order_id=order['order_id'])
                            # по ордеру уже есть частичное выполнение, выход
                            raise ScriptQuitCondition('Выход, продолжаем надеяться докупить валюту по тому курсу, по которому уже купили часть')                               
                        except ScriptError as e:
                            if 'Error 50304' in str(e):
                                if DEBUG:
                                    print('Частично исполненных ордеров нет')
                             
                                time_passed = time.time() + STOCK_TIME_OFFSET*60*60 - int(order['created'])
 
                                if time_passed > ORDER_LIFE_TIME * 60:
                                    log('Пора отменять ордер %s' % order)
                                    # Ордер уже давно висит, никому не нужен, отменяем
                                    call_api('order_cancel', order_id=order['order_id'])  
                                    log('Ордер %s отменен' % order)
                                    raise ScriptQuitCondition('Отменяем ордер -за ' + str(ORDER_LIFE_TIME) + ' минут не удалось купить '+ str(CURRENCY_1))
                                else:
                                    raise ScriptQuitCondition('Выход, продолжаем надеяться купить валюту по указанному ранее курсу, со времени создания ордера прошло %s секунд' % str(time_passed))
                            else:
                                raise ScriptQuitCondition(str(e))
                else: # Открытых ордеров нет
                    balances = call_api('user_info')['balances']
                    reserved = call_api('user_info')['reserved']
                    min_quantityy = call_api('pair_settings',pair=pair)[pair]
                    CURRENCY_1_MIN_QUANTITY = float(min_quantityy['min_quantity'])                    
                    if float(balances[pair[:-4]]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать? 
                        print('Баланс: '+str(float(balances[pair[:-4]]))+' '+str(pair[:-4]))
                        if USE_MACD:
                                macd_advice = get_macd_advice(chart_data=get_ticks(pair)) # проверяем, можно ли создать sell
                                if macd_advice['trand'] == 'BEAR' or (macd_advice['trand'] == 'BULL' and macd_advice['growing']):
                                    print('Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд '+str(macd_advice['trand'])+'; Рост '+str(macd_advice['growing']))
                                    #log(pair, 'Для ордера %s не создаем ордер на продажу, т.к. ситуация на рынке неподходящая' % order['oreder_id'] )
                                else:
                                    print('Выставляем ордер на продажу, т.к ситуация подходящая: '+str(macd_advice['trand'])+' '+str(macd_advice['growing']))
                                    log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                                    create_sell(pair=pair)
                        else: # создаем sell если тенденция рынка позволяет
                            log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                            create_sell(pair=pair)
                    else:                    
                        if float(balances[pair[-3:]]) >= CAN_SPEND: 
                            #log(pair, "Неисполненных ордеров нет, пора ли создать новый?")
                            # Проверяем MACD, если рынок в нужном состоянии, выставляем ордер на покупку
                            if USE_MACD:
                                macd_advice = get_macd_advice(chart_data=get_ticks(pair))
                                if macd_advice['trand'] == 'BEAR' and macd_advice['growing']:
                                    log(pair, "Создаем ордер на покупку")
                                    create_buy(pair=pair)
                                else:
                                    log(pair, "Условия рынка не подходят для торговли", macd_advice)
                            else:
                                log(pair, "Создаем ордер на покупку")
                                create_buy(pair=pair)
                        else:
                            order = str(' В ордере :' + str(float(reserved[pair[:-4]])) + '. ' + str(pair[:-4])) if float(reserved[pair[:-4]]) > 0.0 else ''
                            raise ScriptQuitCondition('Не хватает денег для торговли: баланс ' + str(round(float(balances[pair[-3:]]))) + ' ' + str(pair[-3:]) + order)
            except ScriptError as e:
                    print(e)
            except ScriptQuitCondition as e:
                print(e)
            except Exception as e:
                print("!!!!",e)
        time.sleep(1)
    except Exception as e:
        print(e)

Стоп-лосс, о котором вы говорите, вещь хорошая, называется trailng stop, но тут его нет. В принципе все можно прикрутить, было бы время)
 

ПроголосоватьПроголосовать
2 0
17.01.2018 11:44:11

Спасибо, уже пробую, сам соорудил за это время нечто похожее, правда для одной пары. Но этот скрипт более интеллектуальный. Еще и о той технологии. что вы упомянули почитаю. Так, по крайней мере на пиковых взлетах, с резким падением должно хоть как-то защитить, мне так кажется.

ПроголосоватьПроголосовать
0 0
18.01.2018 11:50:58

Добрый день. Еще вопрос. По выложенному скрипту. Переделала его на пары работающие с рублем, поменял только два параметра, собствено сами пары и увеличил спред до 1200 руб. (примерно 20$). Почему-то за 5 дней не было ни одной сделки. Или он настолько осторозный? или это такая редкая комбинация при торгах?

ПроголосоватьПроголосовать
1 0
22.01.2018 08:48:05
Всем привет, СПАСИБО за скрипт, если не сложно, растолкуйте эти значения 
BEAR_PERC = 70 
BULL_PERC = 99.9

СПАСИБО

ПроголосоватьПроголосовать
0 0
24.01.2018 04:04:00

Тут, бот торгует с тех пор, кока линии тренда долгосрочного и краткосрочного пересеклись и начался подьем тренда вверх, именно поэтому выставлено 99,9 процента (т.е. сразу после точки пересечения трентовых линий большего и меньшего периодов). А вот первая переменная говорит о том, что можешь держать ордер до тех пор, пока тренд снижения не скатился более чем на семдесят процентов от максимум (100). 

По идее все верно. Он покупает ордер в первое мгновение (торитически мин цена) и продает его в тот момент, когда проскочили максимум (так же теоретически цена ордера близка к максимальной).

 

Но у меня почему-то представленный выше алгоритм за несколько дней не совершил ни одной сделки, хотя если проверить по ранее предложенному автором скрипту. для биржи ЭКМО, который рисет графики, то такие периоды бывали.

import numpy
import talib
import requests
import json
import time

from matplotlib.finance import candlestick2_ohlc
import matplotlib.animation as animation

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from datetime import datetime

BEAR_PERC = 70
BULL_PERC = 98

PERIOD = 5 # Период в минутах для построения свечей

# Список пар, на которые торгуем

       
PAIR = 'BTC_RUB'
#PAIR = 'BCH_RUB'
#PAIR = 'ETC_RUB'
#PAIR = 'ETH_RUB'
#PAIR = 'ZEC_RUB'
#PAIR = 'DASH_RUB'
#PAIR = 'XRP_RUB'
#PAIR = 'WAVES_RUB'
#PAIR = 'LTC_RUB'
#PAIR = 'USD_RUB'

fig, ax = plt.subplots(3, sharex=True)
fig.comment = plt.figtext(.7,.05, '')


def update_graph(interval):


    resource = requests.get('https://api.exmo.com/v1/trades/?pair=%s&limit=10000' % PAIR)
    data = json.loads(resource.text)

    chart_data = {} # сформируем словарь с ценой закрытия по PERIOD минут
    
    for item in reversed(data[PAIR]):
        d = int(float(item['date'])/(PERIOD*60))*(PERIOD*60) # Округляем время сделки до PERIOD минут
        if not d in chart_data:
            chart_data[d] = {'open':0, 'close':0, 'high':0, 'low':0}

        chart_data[d]['close'] = float(item['price'])

        if not chart_data[d]['open']:
            chart_data[d]['open'] = float(item['price'])

        if not chart_data[d]['high'] or chart_data[d]['high'] < float(item['price']):
            chart_data[d]['high'] = float(item['price'])

        if not chart_data[d]['low'] or chart_data[d]['low'] > float(item['price']):
            chart_data[d]['low'] = float(item['price'])
        

    quotes = {}
    quotes['open']=numpy.asarray([chart_data[item]['open'] for item in sorted(chart_data)])
    quotes['close']=numpy.asarray([chart_data[item]['close'] for item in sorted(chart_data)])
    quotes['high']=numpy.asarray([chart_data[item]['high'] for item in sorted(chart_data)])
    quotes['low']=numpy.asarray([chart_data[item]['low'] for item in sorted(chart_data)])

    xdate=[datetime.fromtimestamp(item) for item in sorted(chart_data)]

    ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))

    def chart_date(x,pos):
        try:
            return xdate[int(x)]
        except IndexError:
            return ''
        
    ax[0].clear()
    ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
    
    candlestick2_ohlc(ax[0], quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
    
    fig.autofmt_xdate()
    fig.tight_layout()

    macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
    ax[1].clear()
    ax[1].plot(macd, color="y")
    ax[1].plot(macdsignal)

    idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0

    inters = []

    for offset, elem in enumerate(macd):
        if offset in idx:
            inters.append(elem)
        else:
            inters.append(numpy.nan)
    ax[1].plot(inters, 'ro')

    max_v = 0

    for offset, elem in enumerate(macdhist):
        activity_time = False
        curr_v = macd[offset] - macdsignal[offset]
        if abs(curr_v) > abs(max_v):
            max_v = curr_v
        perc = curr_v/max_v
        
        if       (   (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
                     or      (
                                 macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
                            )

                ):
            v = 1
            activity_time = True
        else:
            v = 0
            
        if offset in idx and not numpy.isnan(elem):
            # тренд изменился
            max_v = curr_v = 0 # обнуляем пик спреда между линиями

    ax[2].fill_between([x for x in range(len(macdhist))], 0,macdhist,  facecolor='gray', interpolate=True)
    plt.gcf().texts.remove(fig.comment)
    fig.comment = plt.figtext(.6,.05, '%s %s%s' % (PAIR, time.ctime(), ' ТОРГУЕМ!!!! ' if activity_time else ''), style='italic', bbox={'facecolor':'red' if activity_time else 'green', 'alpha':0.5, 'pad':10})
    
ani = animation.FuncAnimation(fig, update_graph, interval=1000)
plt.show()

 

Возможно, просто еще что-то не учитывается? Или в выложенный скрипт закралась ошибка?

 

ПроголосоватьПроголосовать
1 0
24.01.2018 10:26:36

Его проверяли в работе?

ПроголосоватьПроголосовать
0 0
24.01.2018 10:28:08

Да, и простите за ошибки в тексте. Отвлекают.

ПроголосоватьПроголосовать
1 0
24.01.2018 10:28:31

Благодарю за ответ!

 

А скрипт или не работает или одно из двух...) Уже 4ый день " Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд BEAR; Рост False"

может есть какие нить мысли?

ПроголосоватьПроголосовать
0 0
25.01.2018 03:28:05

Согласен, не работает бот. "Условия рынка не подходят для торговли {'trand': 'BEAR', 'growing': False}", постоянно, на любой паре.

ПроголосоватьПроголосовать
0 0
25.01.2018 03:59:13

Хотя вот на этом участке тренд явно не медвежий. 

ПроголосоватьПроголосовать
0 0
25.01.2018 04:01:28

Похоже, что не работает... хотя должен был по идее... где-то в теле ошибка, которая не дает правильно оценить рынок... и всегда выходит условие - ложь...

Нужно искать, а тут только эксперт может подсказать или автор скрипта, я сам пока найти не смог...

ПроголосоватьПроголосовать
0 0
25.01.2018 04:32:16

Ой вей, одна опечатка при выгрузке кода, а какие последствия )

Обновил код в своем ответе выше, сейчас тестирую - покупка прошла, проверю заодно продажу.

И обратите внимание на этот параметр:

PERIOD = 5 - значит брать сделки в свече за 5 минут, а на графиках Эксмо - 30. 

С одной стороны, это добавляет боту гибкости, а с другой - MACD рассчитан на более длительные периоды,  так что может чаще ошибаться. Думаю, для пар, у которых одна сделка в две минуты (типа DOGE) стоит ставить интервал немного подольше

ПроголосоватьПроголосовать
0 0
25.01.2018 11:20:35
Всем привет, СПАСИБО за скрипт, если не сложно, растолкуйте эти значения 
BEAR_PERC = 70 
BULL_PERC = 99.9

Добрый день. В этой статье я расписывал эти параметры с картинками. Если вкратце, то первый отвечает за покупку на медвежьем рынке (в конце падения и начале подъема), а второй за продажу на бычьем (в процессе роста, до начала падения)

ПроголосоватьПроголосовать
0 0
25.01.2018 12:14:26

Андрей, спасибо большое. А не могли бы вы показать и объяснить, в чем была ошибка? Как тест продажи прошёл? 

ПроголосоватьПроголосовать
1 0
25.01.2018 14:31:16

"'

Ой вей, одна опечатка при выгрузке кода, а какие последствия )

Обновил код в своем ответе выше, сейчас тестирую - покупка прошла, проверю заодно продажу.

"

а в каком именно ответе?

Спасибо

ПроголосоватьПроголосовать
0 0
26.01.2018 04:33:51

а в каком именно ответе?

Спасибо

Во втором сверху, где код выложен)

Ошибка была в строке 

resource = requests.get('https://api.exmo.me/v1/trades/?pair=%s&limit=10000' % pair)

был лишний символ в адресе.

По итогам теста бот покупает и продает, сделок мало, но и рынок в целом падает

На всякий случай, напоминаю, что есть и другие боты для эксмо, попроще - один и два

 

ПроголосоватьПроголосовать
1 0
26.01.2018 08:24:35

stranno vrode takaya je stroka

 

ПроголосоватьПроголосовать
0 0
26.01.2018 13:53:38

Prishol s raBOTI, nashol raznicu!))) Spasibo eshe raz Андрей К.!!, Pishem otzivi o rabote!!!!

ПроголосоватьПроголосовать
0 0
27.01.2018 05:07:45

Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд BEAR; Рост True

2018-01-26 23:11:04.274217   BTC_USD  Обработка...

Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд BULL; Рост True

2018-01-26 23:12:11.776627   BCH_USD  Обработка...

 

Chtoto MEnya eti PREDUPREJDENIYA kak to zaputali.. . a kogdaj situaciya pothodyashaya!?

ПроголосоватьПроголосовать
0 0
27.01.2018 05:13:48

Спасибо за программу. Заметил один небольшой изъян. При закупе валюты новая программа берет сразу из ордеров на продажу, а не пытается выставить ордер по ценам ордеров на покупку. Можно как то скорректировать, чтобы закуп валюты был как в 1й версии программы, через выставление ордера на покупку по более выгодной цене?

ПроголосоватьПроголосовать
0 0
27.01.2018 09:36:28

Пошоркал программу своим напильником, для выставления ордера на покупку нужно было в 159 строке написать bid вместо ask, а для того чтобы он выставлял самую выгодную цену в стакане, вместо взятия средней цены из 3х первых беру самое выгодное предложение и перебиваю его прибавлением 0,0000001. Вот что вышло:

current_rate = sum([float(item[0]) for item in offers['bid'][:1]])+0.0000001 # покупка по самой выгодной цене в стакане

Кроме этого в алгоритме на отмену ордера покупки, при истечении времени была ошибка, стояла не заданная ничем переменная  STOCK_TIME_OFFSET. Строка 264, исправил на:

time_passed = time.time() - int(order['created'])

 

Тестировал алгоритм при отключенном MACD для быстрого результата, выставление и отмена ордера работает исправно.

 

ПроголосоватьПроголосовать
1 0
27.01.2018 19:20:34

Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд BEAR; Рост True

2018-01-26 23:11:04.274217   BTC_USD  Обработка...

Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд BULL; Рост True

2018-01-26 23:12:11.776627   BCH_USD  Обработка...

Chtoto MEnya eti PREDUPREJDENIYA kak to zaputali.. . a kogdaj situaciya pothodyashaya!?

Продавать нельзя, т.к. рынок еще в медвежьей фазе, но растет - так что выгоднее продать позже.

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

ПроголосоватьПроголосовать
0 0
29.01.2018 14:48:48

Здравствуйте Андрей.

Зашел на Ваш сайт и увлекся. К сожалению в программировании ноль, а чтобы чего-то добиться и написать код пусть даже простого робота для биржи с подключением по API и т.п., как я понял, потребуются месяцы, а может быть и годы. И, в конце концов, результат может оказаться плачевным. К тому же на это просто не хватает свободного времени и знаний.

Спасибо Вам за труд и затраченное время на просвещение непросвещенных.

Смотрел коды простых ботов для EXMO и WEX. В первом случае Вы при создании ордера на покупку берете среднюю цену по последним совершенным сделкам, а во втором - берете записи из стакана (как я понял "Ордера на покупку"). Если параметр OFFERS_AMOUNT = 1, то берется текущая лучшая цена (видимо первая строка в стакане, или я не прав?). Если это так, то я хочу попробовать изменить код простого бота EXMO как у бота WEX, то есть чтобы на EXMO бот тоже брал данные из верхней строки стакана "Ордера на покупку". Скорее всего не смогу, но хочу попробовать.

Если Вы планируете нечто подобное, то было бы хорошо.

Смотрел предложения Ильи Ч. по обновленному боту для EXMO. Попробовал модифицировать код по его предложениям, но ничего не вышло. В чем у него ошибки? Было бы интересно узнать Ваше мнение.

Спасибо. С уважением, Валерий.

P.S. Обращаюсь к Илье Ч. Илья если не трудно выложи исправленный тобой код, если можно с пояснениями. Интересно. Спасибо.

ПроголосоватьПроголосовать
0 0
29.01.2018 22:58:16

Вот мой код, потестировал на двух парах BTC_USD и ETH_USD, работает. Все пояснения в предпоследнем посте.

 

import time
import json
import requests
import urllib, http.client
import hmac, hashlib
 
# Если нет нужных пакетов - читаем тут: https://bablofil.ru/python-indicators/
import numpy 
import talib 
 
from datetime import datetime
 
# ключи API, которые предоставила exmo
API_KEY = 'K-'
# обратите внимание, что добавлена 'b' перед строкой
API_SECRET = b'S-'
 
# Список пар, на которые торгуем
MARKETS = [
    'BTC_USD', 'ETH_USD'
]
 
CAN_SPEND = 25 # Сколько USD готовы вложить в бай
MARKUP = 0.001 # 0.001 = 0.1% - Какой навар со сделки хотим получать
 
STOCK_FEE = 0.002 # Какую комиссию берет биржа
PERIOD = 5 # Период в минутах для построения свечей
ORDER_LIFE_TIME = 0.5 # Через сколько минут отменять неисполненный ордер на покупку 0.5 = 30 сек.
 
USE_MACD = True # True - оценивать тренд по MACD, False - покупать и продавать невзирая ни на что
 
BEAR_PERC = 70  # % что считаем поворотом при медведе (подробности - https://bablofil.ru/macd-python-stock-bot/
BULL_PERC = 99.9  # % что считаем поворотом при быке
 
#BEAR_PERC = 70  # % что считаем поворотом при медведе
#BULL_PERC = 100  # Так он будет продавать по минималке, как только курс пойдет вверх
 
API_URL = 'api.exmo.me'
API_VERSION = 'v1'
 
USE_LOG = False
DEBUG = False # True - выводить отладочную информацию, False - писать как можно меньше
 
numpy.seterr(all='ignore')
 
 
 
curr_pair = None
 
# Свой класс исключений
class ScriptError(Exception):
    pass
class ScriptQuitCondition(Exception):
    pass
     
# все обращения к API проходят через эту функцию
def call_api(api_method, http_method="POST", **kwargs):
     
    payload = {'nonce': int(round(time.time()*1000))}
 
    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(API_URL, timeout=90)
    conn.request(http_method, "/"+API_VERSION + "/" + api_method, 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 get_ticks(pair):
    resource = requests.get('https://api.exmo.me/v1/trades/?pair=%s&limit=10000' % pair)
    data = json.loads(resource.text)
     
    chart_data = {} # сформируем словарь с ценой закрытия по 5 минут
    for item in reversed(data[pair]):
        d = int(float(item['date'])/(PERIOD*60))*(PERIOD*60) # Округляем время сделки до PERIOD минут
        chart_data[d] = float(item['price']) 
    return chart_data
 
# С помощью MACD делаем вывод о целесообразности торговли в данный момент (https://bablofil.ru/macd-python-stock-bot/)
def get_macd_advice(chart_data):   
     
    macd, macdsignal, macdhist = talib.MACD(numpy.asarray([chart_data[item] for item in sorted(chart_data)]), fastperiod=12, slowperiod=26, signalperiod=9)
     
    idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
    inters = []
 
    for offset, elem in enumerate(macd):
        if offset in idx:
            inters.append(elem)
        else:
            inters.append(numpy.nan)
    trand = 'BULL' if macd[-1] > macdsignal[-1] else 'BEAR'
    hist_data = []
    max_v = 0
    growing = False
    for offset, elem in enumerate(macdhist):
        growing = False       
        curr_v = macd[offset] - macdsignal[offset]
        if abs(curr_v) > abs(max_v):
            max_v = curr_v
        perc = curr_v/max_v
         
        if       (   (macd[offset] > macdsignal[offset] and perc*100 > BULL_PERC) # восходящий тренд
                     or      (
                                 macd[offset] < macdsignal[offset] and perc*100 < (100-BEAR_PERC)
                            )
 
                ):
            v = 1
            growing = True
        else:
            v = 0
             
        if offset in idx and not numpy.isnan(elem):
            # тренд изменился
            max_v = curr_v = 0 # обнуляем пик спреда между линиями
        hist_data.append(v*1000)
  
    return ({'trand':trand, 'growing':growing})
 
# Выводит всякую информацию на экран, самое важное скидывает в Файл log.txt
def log(*args):
     
    if USE_LOG:
        l = open("./log.txt", 'a', encoding='utf-8')
        print(datetime.now(), *args, file=l)
        l.close()
    print(datetime.now(),' ', *args)
 
# Ф-ция для создания ордера на покупку 
def create_buy(pair):
    global USE_LOG
    USE_LOG = True
    log(pair, 'Создаем ордер на покупку')
    log(pair, 'Получаем текущие курсы')
     
    offers = call_api('order_book', pair=pair)[pair]
    try:
        #current_rate =  float(offers['bid'][0][0]) # покупка по лучшей цене
        current_rate = sum([float(item[0]) for item in offers['bid'][:1]])+0.0000001 # покупка по самой выгодной цене в стакане
        can_buy = CAN_SPEND/current_rate
        print('buy', can_buy, current_rate)                            
        log(pair, """
            Текущая цена - %0.8f
            На сумму %0.8f %s можно купить %0.8f %s
            Создаю ордер на покупку
            """ % (current_rate, CAN_SPEND, pair[0], can_buy, pair[1])
        )       
        new_order = call_api(
            'order_create',
            pair = pair,
            quantity = can_buy,
            price = current_rate,
            type = 'buy'
            )                
        log("Создан ордер на покупку %s" % new_order['order_id'] )
    except ZeroDivisionError:
        print('Не удается вычислить цену', prices)
    USE_LOG = False   
     
# Ф-ция для создания ордера на продажу
def create_sell(pair):
    global USE_LOG
    USE_LOG = True
    balances = call_api('user_info')['balances']
    #if float(balances[pair[:-4]]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать?
    wanna_get = CAN_SPEND + CAN_SPEND * (STOCK_FEE + MARKUP)
    order_amount = float(balances[pair[:-4]])
    new_rate = wanna_get/order_amount
    new_rate_fee = new_rate/(1-STOCK_FEE)   
    offers = call_api('order_book', pair=pair)[pair]       
    current_rate = float(offers['bid'][0][0]) # Берем верхнюю цену, по которой кто-то покупает
    choosen_rate = current_rate if current_rate > new_rate_fee else new_rate_fee        
    print('sell', balances[pair[:-4]], wanna_get, choosen_rate)
    log(pair, """
    Итого на этот ордер было потрачено %0.8f %s, получено %0.8f %s
    Что бы выйти в плюс, необходимо продать купленную валюту по курсу %0.8f
    Тогда, после вычета комиссии %0.4f останется сумма %0.8f %s
    Итоговая прибыль составит %0.8f %s
    Текущий курс продажи %0.8f
    Создаю ордер на продажу по курсу %0.8f
    """
    % (
        wanna_get, pair[0], order_amount, pair[1],
        new_rate_fee,
        STOCK_FEE, (new_rate_fee*order_amount - new_rate_fee * order_amount * STOCK_FEE), pair[0],
        (new_rate_fee*order_amount - new_rate_fee*order_amount * STOCK_FEE)-wanna_get, pair[0],
        current_rate,
        choosen_rate,
        )
    )
    new_order = call_api(
        'order_create',
        pair=pair,
        quantity = balances[pair[:-4]],
        price= choosen_rate,
        type='sell'
    )
    log(pair, "Создан ордер на продажу %s" % new_order['order_id'])
    print(new_order)
    if DEBUG:
        print('Создан ордер на продажу', pair[:-4], new_order['order_id']) 
    USE_LOG = False
 
# Бесконечный цикл процесса - основная логика
while True:
    try:
        for pair in MARKETS: # Проходим по каждой паре из списка в начале\            
            try:
            # Получаем список активных ордеров
                try:
                    opened_orders = call_api('user_open_orders')[pair]
                except KeyError:
                    if DEBUG:
                        print('Открытых ордеров нет')
                        log(pair, "Открытых ордеров нет")    
                    opened_orders = []
                sell_orders = []
                # Есть ли неисполненные ордера на продажу CURRENCY_1?
                log(pair, " Обработка...")
                for order in opened_orders:
                    if order['type'] == 'sell':
                        # Есть неисполненные ордера на продажу CURRENCY_1, выход
                        raise ScriptQuitCondition('Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)')
                        # пропуск продажи
                        # pass
                    else:
                        # Запоминаем ордера на покупку CURRENCY_1
                        sell_orders.append(order)
                # Проверяем, есть ли открытые ордера на покупку CURRENCY_1
                if sell_orders: # открытые ордера есть
                    for order in sell_orders:
                        # Проверяем, есть ли частично исполненные
                        if DEBUG:
                            print('Проверяем, что происходит с отложенным ордером', order['order_id'])
                        try:
                            order_history = call_api('order_trades', order_id=order['order_id'])
                            # по ордеру уже есть частичное выполнение, выход
                            raise ScriptQuitCondition('Выход, продолжаем надеяться докупить валюту по тому курсу, по которому уже купили часть')                               
                        except ScriptError as e:
                            if 'Error 50304' in str(e):
                                if DEBUG:
                                    print('Частично исполненных ордеров нет')
                             
                                time_passed = time.time() - int(order['created'])
 
                                if time_passed > ORDER_LIFE_TIME * 60:
                                    log('Пора отменять ордер %s' % order)
                                    # Ордер уже давно висит, никому не нужен, отменяем
                                    call_api('order_cancel', order_id=order['order_id'])  
                                    log('Ордер %s отменен' % order)
                                    raise ScriptQuitCondition('Отменяем ордер -за ' + str(ORDER_LIFE_TIME) + ' минут не удалось купить '+ str(CURRENCY_1))
                                else:
                                    raise ScriptQuitCondition('Выход, продолжаем надеяться купить валюту по указанному ранее курсу, со времени создания ордера прошло %s секунд' % str(time_passed))
                            else:
                                raise ScriptQuitCondition(str(e))
                else: # Открытых ордеров нет
                    balances = call_api('user_info')['balances']
                    reserved = call_api('user_info')['reserved']
                    min_quantityy = call_api('pair_settings',pair=pair)[pair]
                    CURRENCY_1_MIN_QUANTITY = float(min_quantityy['min_quantity'])                    
                    if float(balances[pair[:-4]]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать? 
                        print('Баланс: '+str(float(balances[pair[:-4]]))+' '+str(pair[:-4]))
                        if USE_MACD:
                                macd_advice = get_macd_advice(chart_data=get_ticks(pair)) # проверяем, можно ли создать sell
                                if macd_advice['trand'] == 'BEAR' or (macd_advice['trand'] == 'BULL' and macd_advice['growing']):
                                    print('Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд '+str(macd_advice['trand'])+'; Рост '+str(macd_advice['growing']))
                                    #log(pair, 'Для ордера %s не создаем ордер на продажу, т.к. ситуация на рынке неподходящая' % order['oreder_id'] )
                                else:
                                    print('Выставляем ордер на продажу, т.к ситуация подходящая: '+str(macd_advice['trand'])+' '+str(macd_advice['growing']))
                                    log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                                    create_sell(pair=pair)
                        else: # создаем sell если тенденция рынка позволяет
                            log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                            create_sell(pair=pair)
                    else:                    
                        if float(balances[pair[-3:]]) >= CAN_SPEND: 
                            #log(pair, "Неисполненных ордеров нет, пора ли создать новый?")
                            # Проверяем MACD, если рынок в нужном состоянии, выставляем ордер на покупку
                            if USE_MACD:
                                macd_advice = get_macd_advice(chart_data=get_ticks(pair))
                                if macd_advice['trand'] == 'BEAR' and macd_advice['growing']:
                                    log(pair, "Создаем ордер на покупку")
                                    create_buy(pair=pair)
                                else:
                                    log(pair, "Условия рынка не подходят для торговли", macd_advice)
                            else:
                                log(pair, "Создаем ордер на покупку")
                                create_buy(pair=pair)
                        else:
                            order = str(' В ордере :' + str(float(reserved[pair[:-4]])) + '. ' + str(pair[:-4])) if float(reserved[pair[:-4]]) > 0.0 else ''
                            raise ScriptQuitCondition('Не хватает денег для торговли: баланс ' + str(round(float(balances[pair[-3:]]))) + ' ' + str(pair[-3:]) + order)
            except ScriptError as e:
                    print(e)
            except ScriptQuitCondition as e:
                print(e)
            except Exception as e:
                print("!!!!",e)
        time.sleep(1)
    except Exception as e:
        print(e)

 

ПроголосоватьПроголосовать
2 0
30.01.2018 07:59:46

Последний, это какой?

ПроголосоватьПроголосовать
0 0
30.01.2018 18:31:31

Здравствуй, Илья.

Инфа из лога:

2018-02-01 01:31:06.995040 LTC_BTC 
    Итого на этот ордер было потрачено 0.05017500 L, получено 0.06839205 T
    Что бы выйти в плюс, необходимо продать купленную валюту по курсу 0.73510812
    Тогда, после вычета комиссии 0.0020 останется сумма 0.05017500 L
    Итоговая прибыль составит 0.00000000 L
    Текущий курс продажи 0.01593250
    Создаю ордер на продажу по курсу 0.73510812

Не знаю как здесь скрин exmo прикрепить но откуда такой курс?

 

ПроголосоватьПроголосовать
0 0
31.01.2018 22:54:06

Добрый день. Андрей, а можете прикрутить к боту такую функцию, чтобы он продавал с прибылью в 0 при простое лота более, ну, скажем получаса- часа или указанного времени?

Вот зачем это надо

Бот был запущен в ночь и получилась такая картина (USD_RUB):

1. он поставил в левый стакан по 1-й строке и продал всё что всё было из USD (стоимость 54,53)

2.. он подождал, видимо начала роста и, поставив в правый стакан, купил USD (стоимость 54,88)

3. бот купил по указанной стоимости USD и выставил продавать по 55,26

минимальный суточный курс при начале торгов 24,02, максимальный 55,2.

Это всё произошло достаточно быстро, но после последней ставки прошло уже более 8 часов. 

Странным мне кажется то, что он пытался продать по курсу выше суточного. Этого не должно быть.

Кроме того бот не должен иметь долгих простоев, т.к. даже если он продаёт с нулевой прибылью, но быстро есть призрачный шанс получения кэшбэка.

Огромное СПАСИБО Андрею и Илье за проделанную работу ребята - вы молодцы.  

ПроголосоватьПроголосовать
1 0
10.02.2018 07:26:43

здравствуйте. бот для битрикса перестал совершать операции. хотя на счету 18 криптодолларов. уже 2 дня висит просто так. пишет что условия рынка не подходят. или пишет по английски. что минимальные требования не соблюдены. как решить проблему?

ПроголосоватьПроголосовать
0 0
14.02.2018 06:30:35

Скажите, а нет ли случайно бота с такой логикой для полоникса? Может кто то уже реализовал?

 

ПроголосоватьПроголосовать
0 0
14.02.2018 19:18:56

Есть, вот тут подробнее https://bablofil.ru/macd-python-stock-bot/

Прямая ссылка https://bablofil.ru/static/macd_bots/poloniex_macd.py

ПроголосоватьПроголосовать
0 0
15.02.2018 08:18:47

Андрей, интересен бот с плавающей ценой при продаже на бирже полоникс.

Может есть?

ПроголосоватьПроголосовать
0 0
15.02.2018 14:23:51

Всего доброго всем!

Будьте внимательны!!!! Скрипт Илья Ч. 30.01.2018 07:59:46 содержит опасный код, который всю вашу криптовалюту спустит в одну секунду, когда он доберется до: "Для выполненного ордера на покупку выставляем ордер на продажу" . Сколько и по какой цене отработает:   create_sell(pair=pair). Код в сдудию!!!!!

if USE_MACD:
                                macd_advice = get_macd_advice(chart_data=get_ticks(pair)) # проверяем, можно ли создать sell
                                if macd_advice['trand'] == 'BEAR' or (macd_advice['trand'] == 'BULL' and macd_advice['growing']):
                                    print('Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд '+str(macd_advice['trand'])+'; Рост '+str(macd_advice['growing']))
                                    log('Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд '+str(macd_advice['trand'])+'; Рост '+str(macd_advice['growing']))
                                    #log(pair, 'Для ордера %s не создаем ордер на продажу, т.к. ситуация на рынке неподходящая' % order['oreder_id'] )
                                else:
                                    print('Выставляем ордер на продажу, т.к ситуация подходящая: '+str(macd_advice['trand'])+' '+str(macd_advice['growing']))
                                    log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                                    create_sell(pair=pair)
                        else: # создаем sell если тенденция рынка позволяет
                            log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                            create_sell(pair=pair)

ПроголосоватьПроголосовать
0 0
17.02.2018 17:07:15

Если торугете LTC_USD,  например, то LTC на старте не должно быть на балансе. Бот сам купит сколько надо LTC и потом продаст все монетки, в ноль. В данном случае, как я понимаю, у вас уже были какие-то монетки на балансе, и бот их продал, полагая что это он их и купил.

ПроголосоватьПроголосовать
0 0
18.02.2018 07:14:52

" Илья Ч. 30.01.2018 07:59:46 содержит опасный код " - c чего вы взяли?

ПроголосоватьПроголосовать
0 0
18.02.2018 14:57:09
import time
import json
import requests
import urllib, http.client
import hmac, hashlib

# Если нет нужных пакетов - читаем тут: https://bablofil.ru/python-indicators/
import numpy
import talib

from datetime import datetime

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

# Список пар, на которые торгуем
MARKETS = [
    'BTC_USD', 'ETH_USD', 'ETC_USD'
]

CAN_SPEND = 40  # Сколько USD готовы вложить в бай
MARKUP = 0.001  # 0.001 = 0.1% - Какой навар со сделки хотим получать

STOCK_FEE = 0.002  # Какую комиссию берет биржа
PERIOD = 5  # Период в минутах для построения свечей
ORDER_LIFE_TIME = 3  # Через сколько минут отменять неисполненный ордер на покупку 0.5 = 30 сек.

USE_MACD = True  # True - оценивать тренд по MACD, False - покупать и продавать невзирая ни на что

BEAR_PERC = 70  # % что считаем поворотом при медведе (подробности - https://bablofil.ru/macd-python-stock-bot/
BULL_PERC = 99.9  # % что считаем поворотом при быке

# BEAR_PERC = 70  # % что считаем поворотом при медведе
# BULL_PERC = 100  # Так он будет продавать по минималке, как только курс пойдет вверх

API_URL = 'api.exmo.me'
API_VERSION = 'v1'

USE_LOG = False
DEBUG = False  # True - выводить отладочную информацию, False - писать как можно меньше

numpy.seterr(all='ignore')

curr_pair = None


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


class ScriptQuitCondition(Exception):
    pass


# все обращения к API проходят через эту функцию
def call_api(api_method, http_method="POST", **kwargs):
    payload = {'nonce': int(round(time.time() * 1000))}

    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(API_URL, timeout=90)
    conn.request(http_method, "/" + API_VERSION + "/" + api_method, 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 get_ticks(pair):
    resource = requests.get('https://api.exmo.me/v1/trades/?pair=%s&limit=10000' % pair)
    data = json.loads(resource.text)

    chart_data = {}  # сформируем словарь с ценой закрытия по 5 минут
    for item in reversed(data[pair]):
        d = int(float(item['date']) / (PERIOD * 60)) * (PERIOD * 60)  # Округляем время сделки до PERIOD минут
        chart_data[d] = float(item['price'])
    return chart_data


# С помощью MACD делаем вывод о целесообразности торговли в данный момент (https://bablofil.ru/macd-python-stock-bot/)
def get_macd_advice(chart_data):
    macd, macdsignal, macdhist = talib.MACD(numpy.asarray([chart_data[item] for item in sorted(chart_data)]),
                                            fastperiod=12, slowperiod=26, signalperiod=9)

    idx = numpy.argwhere(numpy.diff(numpy.sign(macd - macdsignal)) != 0).reshape(-1) + 0
    inters = []

    for offset, elem in enumerate(macd):
        if offset in idx:
            inters.append(elem)
        else:
            inters.append(numpy.nan)
    trand = 'BULL' if macd[-1] > macdsignal[-1] else 'BEAR'
    hist_data = []
    max_v = 0
    growing = False
    for offset, elem in enumerate(macdhist):
        growing = False
        curr_v = macd[offset] - macdsignal[offset]
        if abs(curr_v) > abs(max_v):
            max_v = curr_v
        perc = curr_v / max_v

        if ((macd[offset] > macdsignal[offset] and perc * 100 > BULL_PERC)  # восходящий тренд
                or (
                        macd[offset] < macdsignal[offset] and perc * 100 < (100 - BEAR_PERC)
                )

        ):
            v = 1
            growing = True
        else:
            v = 0

        if offset in idx and not numpy.isnan(elem):
            # тренд изменился
            max_v = curr_v = 0  # обнуляем пик спреда между линиями
        hist_data.append(v * 1000)

    return ({'trand': trand, 'growing': growing})


# Выводит всякую информацию на экран, самое важное скидывает в Файл log.txt
def log(*args):
    if USE_LOG:
        l = open("./log.txt", 'a', encoding='utf-8')
        print(datetime.now(), *args, file=l)
        l.close()
    print(datetime.now(), ' ', *args)


# Ф-ция для создания ордера на покупку
def create_buy(pair):
    global USE_LOG
    USE_LOG = True
    log(pair, 'Создаем ордер на покупку')
    log(pair, 'Получаем текущие курсы')

    offers = call_api('order_book', pair=pair)[pair]
    try:
        # current_rate =  float(offers['bid'][0][0]) # покупка по лучшей цене
        current_rate = sum(
            [float(item[0]) for item in offers['bid'][:1]]) + 0.0000001  # покупка по самой выгодной цене в стакане
        can_buy = CAN_SPEND / current_rate
        print('buy', can_buy, current_rate)
        log(pair, """
            Текущая цена - %0.8f
            На сумму %0.8f %s можно купить %0.8f %s
            Создаю ордер на покупку
            """ % (current_rate, CAN_SPEND, pair[0], can_buy, pair[1])
            )
        new_order = call_api(
            'order_create',
            pair=pair,
            quantity=can_buy,
            price=current_rate,
            type='buy'
        )
        log("Создан ордер на покупку %s" % new_order['order_id'])
    except ZeroDivisionError:
        print('Не удается вычислить цену', prices)
    USE_LOG = False


# Ф-ция для создания ордера на продажу
def create_sell(pair):
    global USE_LOG
    USE_LOG = True
    balances = call_api('user_info')['balances']
    # if float(balances[pair[:-4]]) >= CURRENCY_1_MIN_QUANTITY: # Есть ли в наличии CURRENCY_1, которую можно продать?
    wanna_get = CAN_SPEND + CAN_SPEND * (STOCK_FEE + MARKUP)
    order_amount = float(balances[pair[:-4]])
    new_rate = wanna_get / order_amount
    new_rate_fee = new_rate / (1 - STOCK_FEE)
    offers = call_api('order_book', pair=pair)[pair]
    current_rate = float(offers['bid'][0][0])  # Берем верхнюю цену, по которой кто-то покупает
    choosen_rate = current_rate if current_rate > new_rate_fee else new_rate_fee
    print('sell', balances[pair[:-4]], wanna_get, choosen_rate)
    log(pair, """
    Итого на этот ордер было потрачено %0.8f %s, получено %0.8f %s
    Что бы выйти в плюс, необходимо продать купленную валюту по курсу %0.8f
    Тогда, после вычета комиссии %0.4f останется сумма %0.8f %s
    Итоговая прибыль составит %0.8f %s
    Текущий курс продажи %0.8f
    Создаю ордер на продажу по курсу %0.8f
    """
        % (
            wanna_get, pair[0], order_amount, pair[1],
            new_rate_fee,
            STOCK_FEE, (new_rate_fee * order_amount - new_rate_fee * order_amount * STOCK_FEE), pair[0],
            (new_rate_fee * order_amount - new_rate_fee * order_amount * STOCK_FEE) - wanna_get, pair[0],
            current_rate,
            choosen_rate,
        )
        )
    new_order = call_api(
        'order_create',
        pair=pair,
        quantity=balances[pair[:-4]],
        price=choosen_rate,
        type='sell'
    )
    log(pair, "Создан ордер на продажу %s" % new_order['order_id'])
    print(new_order)
    if DEBUG:
        print('Создан ордер на продажу', pair[:-4], new_order['order_id'])
    USE_LOG = False


# Бесконечный цикл процесса - основная логика
while True:
    try:
        for pair in MARKETS:  # Проходим по каждой паре из списка в начале\
            try:
                # Получаем список активных ордеров
                try:
                    opened_orders = call_api('user_open_orders')[pair]
                except KeyError:
                    if DEBUG:
                        print('Открытых ордеров нет')
                        log(pair, "Открытых ордеров нет")
                    opened_orders = []
                sell_orders = []
                # Есть ли неисполненные ордера на продажу CURRENCY_1?
                log(pair, " Обработка...")
                for order in opened_orders:
                    if order['type'] == 'sell':
                        # Есть неисполненные ордера на продажу CURRENCY_1, выход
                        raise ScriptQuitCondition(
                            'Выход, ждем пока не исполнятся/закроются все ордера на продажу (один ордер может быть разбит биржей на несколько и исполняться частями)')
                        # пропуск продажи
                        # pass
                    else:
                        # Запоминаем ордера на покупку CURRENCY_1
                        sell_orders.append(order)
                # Проверяем, есть ли открытые ордера на покупку CURRENCY_1
                if sell_orders:  # открытые ордера есть
                    for order in sell_orders:
                        # Проверяем, есть ли частично исполненные
                        if DEBUG:
                            print('Проверяем, что происходит с отложенным ордером', order['order_id'])
                        try:
                            order_history = call_api('order_trades', order_id=order['order_id'])
                            # по ордеру уже есть частичное выполнение, выход
                            raise ScriptQuitCondition(
                                'Выход, продолжаем надеяться докупить валюту по тому курсу, по которому уже купили часть')
                        except ScriptError as e:
                            if 'Error 50304' in str(e):
                                if DEBUG:
                                    print('Частично исполненных ордеров нет')

                                time_passed = time.time() - int(order['created'])

                                if time_passed > ORDER_LIFE_TIME * 60:
                                    log('Пора отменять ордер %s' % order)
                                    # Ордер уже давно висит, никому не нужен, отменяем
                                    call_api('order_cancel', order_id=order['order_id'])
                                    log('Ордер %s отменен' % order)
                                    raise ScriptQuitCondition('Отменяем ордер -за ' + str(
                                        ORDER_LIFE_TIME) + ' минут не удалось купить ' + str(CURRENCY_1))
                                else:
                                    raise ScriptQuitCondition(
                                        'Выход, продолжаем надеяться купить валюту по указанному ранее курсу, со времени создания ордера прошло %s секунд' % str(
                                            time_passed))
                            else:
                                raise ScriptQuitCondition(str(e))
                else:  # Открытых ордеров нет
                    balances = call_api('user_info')['balances']
                    reserved = call_api('user_info')['reserved']
                    min_quantityy = call_api('pair_settings', pair=pair)[pair]
                    CURRENCY_1_MIN_QUANTITY = float(min_quantityy['min_quantity'])
                    if float(balances[pair[
                                      :-4]]) >= CURRENCY_1_MIN_QUANTITY:  # Есть ли в наличии CURRENCY_1, которую можно продать?
                        print('Баланс: ' + str(float(balances[pair[:-4]])) + ' ' + str(pair[:-4]))
                        if USE_MACD:
                            macd_advice = get_macd_advice(
                                chart_data=get_ticks(pair))  # проверяем, можно ли создать sell
                            if macd_advice['trand'] == 'BEAR' or (
                                    macd_advice['trand'] == 'BULL' and macd_advice['growing']):
                                print('Продавать нельзя, т.к. ситуация на рынке неподходящая: Трэнд ' + str(
                                    macd_advice['trand']) + '; Рост ' + str(macd_advice['growing']))
                                # log(pair, 'Для ордера %s не создаем ордер на продажу, т.к. ситуация на рынке неподходящая' % order['oreder_id'] )
                            else:
                                print('Выставляем ордер на продажу, т.к ситуация подходящая: ' + str(
                                    macd_advice['trand']) + ' ' + str(macd_advice['growing']))
                                log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                                create_sell(pair=pair)
                        else:  # создаем sell если тенденция рынка позволяет
                            log(pair, "Для выполненного ордера на покупку выставляем ордер на продажу")
                            create_sell(pair=pair)
                    else:
                        if float(balances[pair[-3:]]) >= CAN_SPEND:
                            # log(pair, "Неисполненных ордеров нет, пора ли создать новый?")
                            # Проверяем MACD, если рынок в нужном состоянии, выставляем ордер на покупку
                            if USE_MACD:
                                macd_advice = get_macd_advice(chart_data=get_ticks(pair))
                                if macd_advice['trand'] == 'BEAR' and macd_advice['growing']:
                                    log(pair, "Создаем ордер на покупку")
                                    create_buy(pair=pair)
                                else:
                                    log(pair, "Условия рынка не подходят для торговли", macd_advice)
                            else:
                                log(pair, "Создаем ордер на покупку")
                                create_buy(pair=pair)
                        else:
                            order = str(
                                ' В ордере :' + str(float(reserved[pair[:-4]])) + '. ' + str(pair[:-4])) if float(
                                reserved[pair[:-4]]) > 0.0 else ''
                            raise ScriptQuitCondition('Не хватает денег для торговли: баланс ' + str(
                                round(float(balances[pair[-3:]]))) + ' ' + str(pair[-3:]) + order)
            except ScriptError as e:
                print(e)
            except ScriptQuitCondition as e:
                print(e)
            except Exception as e:
                print("!!!!", e)
        time.sleep(1)
    except Exception as e:
        print(e)

 

кто -нибудь нашел какие либо косяки обоснованные ? Запустил его, вроде работает, покупает и продает, только вот очень долго он это делает.

ПроголосоватьПроголосовать
0 0
18.02.2018 14:59:54

очень странная продажа

Итого на этот ордер было потрачено 40.12000000 B, получено 0.00445490 T
    Что бы выйти в плюс, необходимо продать купленную валюту по курсу 9023.86154607
    Тогда, после вычета комиссии 0.0020 останется сумма 40.12000000 B
    Итоговая прибыль составит 0.00000000 B
    Текущий курс продажи 10975.00000000
    Создаю ордер на продажу по курсу 10975.00000000

т.е.. в результате прибыль 0.00000000

ПроголосоватьПроголосовать
2 0
18.02.2018 15:15:43

доброго дня!

тестировал бота из второго поста.

сегодня купил ниже минимальной в паре BTC-USD= 0.00099879

ПроголосоватьПроголосовать
0 0
21.02.2018 03:31:29

Скажите пожалуйста, а "CAN_SPEND = 25 # Сколько USD готовы вложить в бай" это значит, что он будет вкладывать 25 USD  в каждую пару или во все пары вместе взятые?

И ещё, как можно сделать, чтобы он делал не 1 ставку на указанную сумму в указанной паре, а больше?

ПроголосоватьПроголосовать
0 0
21.02.2018 15:31:35
Пожалуйста, авторизуйтесь, что бы ответить на вопрос