Бот для Wex.nz. Бот для биржи


Бот для Bittrex - продолжаем продолжать

Продолжаем развлекаться - соорудим бота для биржи Bittrex.com.

Регистрацию я расписывать не буду, она довольно тривиальна, а вот с подключением через API «есть нюанс» (с). Дело в том, что биржа параноидальна серьезно относится к безопасности пользователей и сохранности их средств, так что приготовьтесь к многочисленным подтверждениям того, что вы это вы. Так вот.

Подключаем API на Bittrex.com

В меню выбираем Settings – API Keys, и с грустью тоской смотрим на надпись:

To use API Keys, you must have Two-Factor Authentication enabled.

Для использования API необходимо подключить двуфакторную авторизацию. Круто, ну давайте включим. Там же, в Settings, переходим в раздел Two-Factor Authentication и с еще большей болью в сердце видим, что Bittrex использует только и исключительно Google Authenticator.

Пару слов о Google Authenticator: Первый раз я столкнулся с Google Authenticator, когда подключал двуфакторную авторизацию на btc-e.nz. Тогда я подумал, ух ты, как здорово, ставишь приложение на телефон и авторизуешься когда тебе надо, можно симки менять.. Прошло несколько лет, мой телефон давно сломался, я давно купил новый, и вот мне потребовалось зайти на эту биржу и авторизоваться под старым акканутом.. И разверзся ад! Сначала Google меня допрашивал, я ли это, требовал всевозможных подтверждений, гонял по кругу, не верил мне.. Потом вроде заработало. Я попытался авторизоваться на бирже, но теперь биржа начала ругаться на то, что код неправильный. Я сдался, удалил приложение, и пошел делать то, что хотел, на другую биржу. Через несколько месяцев владельца btc-e арестовали, а биржу на месяц прикрыли, потом вроде как перекупили, так что вся эта канитель по сути спасла меня от потери денег. Но Google Authenticator мне все равно с тех пор не нравится.

Так вот. Для двуфакторной авторизации нам нужно установить на телефон Google Authenticator. Это официальное приложение, его можно найти в Play Market и iTunes. После установки приложение попросит вас добавить аккаунт.

Отсканируйте в приложении QR-код с сайта, введите цифры с приложения на сайт, потом перейдите на почту и подтвердите, что вы действительно решили подключить авторизацию. После этого можно работать с ключами API.

Возвращаемся в Settings – API Keys, запускаем Google Authenticator, указываем на странице цифры из приложения, и указываем права доступа – я не стал включать вывод средств, ибо вроде бы незачем. Но вот описание:

  • Read Info – Можно получать информацию о балансе и других свойствах аккаунта
  • Withdraw – Вывод средств на указанный вами адрес. Эта опция может быть полезной для проведения арбитража или переводе средств в cold storage.
  • Trade Limit – Позволяет выставлять отложенные ордера по лимиту
  • Trade Market – Позволяет выставлять отложенные ордера по рынку. ПРЕДУПРЕЖДЕНИЕ (так на сайте написано): Не включайте эту опцию, если точно не уверены, что делаете. Покупки по рынку чрезвычайно опасны!

В общем я включил первые две опции. Насчет предупреждения не понял, т.к. в API нет метода, что бы торговать по рынку. Ну да ладно.

После выставления настроек вводим новые цифры из Google Authenticator, нажимаем Update Keys и ура! Получилось.

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

Реклама

Проверяем работу через API

Методы для работы API биттрекса расписаны тут. Изменим немного функцию из бота для эксмо, и попробуем что-то сделать.

Я закинул на баланс немного LTC и создал ордер на продажу по завышенной цене. Проверим, видно ли это через API запросы:

import time import json import urllib, http.client import hmac, hashlib API_KEY = '...' # обратите внимание, что добавлена 'b' перед строкой API_SECRET = b'...' API_URL = 'bittrex.com' API_VERSION = 'v1.1' # Свой класс исключений class ScriptError(Exception): pass # все обращения к API проходят через эту функцию def call_api(**kwargs): http_method = kwargs.get('http_method') if kwargs.get('http_method', '') else 'POST' method = kwargs.get('method') nonce = str(int(round(time.time()))) payload = { 'nonce': nonce } if kwargs: payload.update(kwargs) uri = "https://" + API_URL + "/api/" + API_VERSION + method + '?apikey=' + API_KEY + '&nonce=' + nonce uri += urllib.parse.urlencode(payload) payload = urllib.parse.urlencode(payload) H = hmac.new(key=API_SECRET, digestmod=hashlib.sha512) H.update(uri.encode()) sign = H.hexdigest() apisign = hmac.new(API_SECRET, uri.encode(), hashlib.sha512).hexdigest() headers = {"Content-type": "application/x-www-form-urlencoded", "Key": API_KEY, "apisign": apisign} conn = http.client.HTTPSConnection(API_URL, timeout=60) conn.request(http_method, uri, 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) print(call_api(method='/account/getbalances')) print(call_api(method='/market/getopenorders', market='USDT-LTC'))

На выходе получаем:

Баланс: {'success': True, 'message': '', 'result': [{'Currency': 'LTC', 'Balance': 0.48107406, 'Available': 0.47107406, 'Pending': 0.0, 'CryptoAddress': 'LMGJrSr37DYpZGiWEpLzTEz7my6PUu48KP'}]} Открытые ордера: {'success': True, 'message': '', 'result': [{'Uuid': None, 'OrderUuid': '07dd349c-936f-426e-be6a-c3ad6d6d0bf4', 'Exchange': 'USDT-LTC', 'OrderType': 'LIMIT_SELL', 'Quantity': 0.01, 'QuantityRemaining': 0.01, 'Limit': 56.90798125, 'CommissionPaid': 0.0, 'Price': 0.0, 'PricePerUnit': None, 'Opened': '2017-11-05T08:47:53.18', 'Closed': None, 'CancelInitiated': False, 'ImmediateOrCancel': False, 'IsConditional': False, 'Condition': 'NONE', 'ConditionTarget': None}]}

Отлично, всё работает, можно начинать делать бота.

Реклама

Получаем данные для индикаторов

В официальном API нет метода, который позволил бы получить значения для свечей. Тем не менее, существует неофициальный API v2.0, который используется самим биттрексом, и о котором они никому ничего не рассказывают. Я обнаружил это, изучая запросы между моим браузером и биржей в панели разработчика (F12 - Network), позже, погуглив, обнаружил что люди уже повытаскивали все запросы и даже их расписали. Эту информацию вы можете найти в гугле, мне же нужна одна функция – получение данных свечей.

 Вот как выглядит запрос:

https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=USDT-LTC&tickInterval=fiveMin

Если требуется интервал по полчаса, то fiveMin меняем на thirtyMin, и т.п.

У этих данных, впрочем, есть недостаток – они возвращают данные за прошедшие периоды.. Т.е. если строить по минутам, то отставание составит 3 минуты, если по 5 минут, то как минимум 5 минут и т.п. Для оперативного принятия решений этого мало, поэтому недостающий набор данных я планирую брать с публичного метода getmarkethistory:

https://bittrex.com/api/v1.1/public/getmarkethistory?market=USDT-LTC.

Т.е. последние несколько минут торгов я буду брать с истории торгов, и дополнять ими то, что недополучил вначале – так я соберу наиболее полную картину. Более того, как раз последние несколько секунд и минут могут как раз оказаться решающими. Напишем код, проверим:

import time import json import requests from datetime import datetime MARKET = 'USDT-BTC' def get_ticks():     chart_data = {}     # Получаем готовые данные свечей     res = requests.get("https://bittrex.com/Api/v2.0/pub/market/GetTicks?marketName=" + MARKET + "&tickInterval=fiveMin")     for item in json.loads(res.text)['result']:         try:             dt_obj = datetime.strptime(item['T'], '%Y-%m-%dT%H:%M:%S.%f')         except ValueError:             dt_obj = datetime.strptime(item['T'], '%Y-%m-%dT%H:%M:%S')         ts = int(time.mktime(dt_obj.timetuple()))         if not ts in chart_data:             chart_data[ts] = {'open': float(item['O']), 'close': float(item['C']), 'high': float(item['H']), 'low': float(item['L'])}     # Добираем недостающее     res = requests.get("https://bittrex.com/api/v1.1/public/getmarkethistory?market=" + MARKET)     for trade in reversed(json.loads(res.text)['result']):         try:             dt_obj = datetime.strptime(trade['TimeStamp'], '%Y-%m-%dT%H:%M:%S.%f')         except ValueError:             dt_obj = datetime.strptime(item['T'], '%Y-%m-%dT%H:%M:%S')                      ts = int((time.mktime(dt_obj.timetuple())/300))*300 # округляем до 5 минут         if not ts in chart_data:             chart_data[ts] = {'open': 0, 'close': 0, 'high': 0,'low': 0}         chart_data[ts]['close'] = float(trade['Price'])         if not chart_data[ts]['open']:             chart_data[ts]['open'] = float(trade['Price'])         if not chart_data[ts]['high'] or chart_data[ts]['high'] < float(trade['Price']):             chart_data[ts]['high'] = float(trade['Price'])         if not chart_data[ts]['low'] or chart_data[ts]['low'] > float(trade['Price']):             chart_data[ts]['low'] = float(trade['Price'])     return chart_data # Выведем результат, посмотрим - работаем по времени биржи chart_data = get_ticks() for item in sorted(chart_data):     print(datetime.fromtimestamp(item),chart_data[item])

Результат:

2017-11-05 11:10:00 {'open': 54.75500015, 'close': 54.75500015, 'high': 54.75500015, 'low': 54.75500015} 2017-11-05 11:15:00 {'open': 54.87745003, 'close': 54.89744999, 'high': 54.89744999, 'low': 54.87745003} 2017-11-05 11:20:00 {'open': 54.87755003, 'close': 55.03567601, 'high': 55.03567601, 'low': 54.87755003} 2017-11-05 11:25:00 {'open': 55.03567601, 'close': 55.0, 'high': 55.03567601, 'low': 55.0} 2017-11-05 11:30:00 {'open': 55.00668, 'close': 55.0, 'high': 55.00668, 'low': 55.0} 2017-11-05 11:35:00 {'open': 55.0, 'close': 55.0, 'high': 55.0, 'low': 55.0} 2017-11-05 11:40:00 {'open': 55.0, 'close': 55.0, 'high': 55.0, 'low': 55.0} 2017-11-05 11:45:00 {'open': 55.0, 'close': 55.0, 'high': 55.0, 'low': 55.0} 2017-11-05 11:50:00 {'open': 54.8975, 'close': 54.79500025, 'high': 55.0, 'low': 54.79500025} 2017-11-05 11:55:00 {'open': 54.915338, 'close': 54.79500001, 'high': 54.915338, 'low': 54.79500001}

Для анализа этих данных мы будем использовать MACD, как и в предыдущей статье, потому что, во-первых, это сэкономит время и размер статьи, а во-вторых, потому что мы будем в этот раз использовать значения по другому.

Итак, принцип алгоритма расписан здесь, сделаем небольшую функцию, которая будет возвращать нам тренд и совет, пора или не пора торговать. Я подразумеваю, что тренд растет (growing),  если это либо бык в активной фазе, либо медведь в затухающей. Код выглядит вот так:

def get_macd_advice(chart_data): macd, macdsignal, macdhist = talib.MACD(numpy.asarray([chart_data[item]['close'] 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 trand = 'BULL' if macd[-1] > macdsignal[-1] else 'BEAR' max_v = 0 activity_time = False 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) ) ): activity_time = True growing = True if offset in idx and not numpy.isnan(elem): # тренд изменился max_v = curr_v = 0 # обнуляем пик спреда между линиями return ({'trand':trand, 'growing':growing})

Соответственно, при работе мы будем вызывать эту функцию, передавать ей полученные ранее данные и узнавать, пора ли покупать/продавать и т.п.

Реклама

Стратегия

В этот раз предлагаю поступить так – бот будет закупать на падении графика, когда MACD все уже падает, но тренд уже начал разворот, и будет продавать купленное на росте тренда, пока он все еще растет, но уже начинает спад.

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

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

Но, тем не менее, предусмотрена опция отключения такого поведения - USE_MACD = True заставит бота думать, USE_MACD = False включит ковбойский режим. Если режим MACD включен, управлять настройками задумчивости можно через переменные BEAR_PERC и BULL_PERC (подробнее рассмотрено тут) – выставляйте каждую от 0 и до 101 и смотрите, как лучше.

В общем-то вот код, скачивайте, пользуйтесь, может еще какие-то идеи придут. Удачи вам)

P.S. Если вы раньше не запускали ботов с моего сайта, то знайте, вам нужно установить Python с модулями (описано тут) и ta-lib (описано тут). О всяких других аспектах, таких как работа с API или комиссиями, читайте в статьях цикла, ссылки под этой статьёй. 

Реклама

Статистика по результатам тестирования

Через 6 суток непрерывной работы бота я снял статистику - выкачал историю сделок с биттрекса, и посчитал чистый доход за вычетом комиссий.

Играл на 8 пар, каждой выделял по 2 доллара. Из-за политики биржи, еще 0.25% прибавлялись к платежам, так что каждый ордер на покупку обходился примерно в 2,00499762 доллара.

Профитность ставил 0.1% с каждой сделки.

В итоге за 6 дней было совершено 144 сделки (72 цикла покупки/продажи), прибыль с каждой составила большей частью 0.1%, в некоторых случаях больше (иногда на пару порядков больше).

Итоговый доход с 16 долларов, заряженных в игру, составил 1,23 - или 7.6%.

Зависших пар (купленных на пике) - не обнаружено.

Продолжаем наблюдение..

bablofil.ru

Бот для 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.ru

Бот для Wex.nz

Выкладываю реализацию бота для wex.nz.

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

Важно: на балансе не должно быть первой валюты! Т.е. если играете на пару ltc_usd, то избавьтесь от всех ltc - купите на них доллары или там мороженное, не знаю, главное, что бы на балансе был ноль! Тогда все будет хорошо, и никаких неприятных сюрпризов не случится.

Для определения цены покупки берет записи из стакана (параметр OFFERS_AMOUNT), и берет из них среднюю. Если указать OFFERS_AMOUNT = 1 то будет брать по текущей лучшей цене.

О запуске - для тех, кто недавно присоединился:

1. Нужно скачать питон версии 3.6+ с официального сайта 

2. После установки запустить командную строку (cmd) вбить туда pip install requests + Enter

3. Код, выложенный на сайте, скопировать в блокнот и сохранить (например wex_bot.py)

3. Получить на Wex ключи API и прописать в файл бота (поставьте галочки trade & info на бирже, при создании ключа).

4. В настройках указать на какую пару хотите играть и на какую сумму

5. Запустить бота (в командной строке введите python путь_к_файлу_wex_bot.py)

Реклама

Настройка

В коде бота нужно прописать API ключи, вот тут, буква b'' во второй строчке нужна, просто вставьте апи-ключи в кавычки

# Вписываем свои ключи API_KEY = 'TW0......NB' API_SECRET = b'191....02'

Пропишите пару, на которую собираетесь играть, в данном случае у меня пара eur_usd, поэтому на балансе изначально должно быть 0 евро

# Тонкая настройка CURRENCY_1 = 'eur' CURRENCY_2 = 'usd'

Остальное вроде бы прописано, 

CAN_SPEND - это сколько CURRENCY_2 тратить (долларов в данном случае)

ORDER_LIFE_TIME  - через сколько минут отменять buy, если он не сработал

PROFIT_MARKUP - какой навар желаем с каждого раунда купли/продажи

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

Реклама

Непосредственно код:

import os import json import requests import urllib, http.client import hmac, hashlib import time # Вписываем свои ключи API_KEY = 'TW.....NB' API_SECRET = b'191....e02' # Тонкая настройка CURRENCY_1 = 'eur' CURRENCY_2 = 'usd' ORDER_LIFE_TIME = 3 # через сколько минут отменять неисполненный ордер на покупку CURRENCY_1 STOCK_FEE = 0.002 # Комиссия, которую берет биржа (0.002 = 0.2%) OFFERS_AMOUNT = 1 # Сколько предложений из стакана берем для расчета средней цены CAN_SPEND = 5 # Сколько тратить CURRENCY_2 каждый раз при покупке CURRENCY_1 (5$ в моем случае) 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("wex.nz", 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']: print(response.decode('utf-8')) raise ScriptError(obj['error']) return obj except ValueError: raise ScriptError('Ошибка анализа возвращаемых данных, получена строка', response) # Узнаем лимиты по парам pair_settings = json.loads(requests.get("https://wex.nz/api/3/info").text)['pairs'] # Реализация алгоритма def main_flow(): try: # Получаем список активных ордеров opened_orders = [] try: wex_orders = call_api(method="ActiveOrders", pair=CURR_PAIR)['return'] for order in wex_orders: o = wex_orders[order] o['order_id']=order opened_orders.append(o) except ScriptError: pass 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_status = call_api(method="OrderInfo", order_id=order['order_id'])['return'][str(order['order_id'])]['status'] 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: # Открытых ордеров нет balances = call_api(method="getInfo")['return']['funds'] if float(balances.get(CURRENCY_1, 0)) > 0: # Есть ли в наличии CURRENCY_1, которую можно продать? """ Высчитываем курс для продажи. Нам надо продать всю валюту, которую купили, на сумму, за которую купили + немного навара и минус комиссия биржи При этом важный момент, что валюты у нас меньше, чем купили - бирже ушла комиссия 0.00134345 1.5045 Поэтому курс продажи может получиться довольно высоким """ wanna_get = (CAN_SPEND + CAN_SPEND * PROFIT_MARKUP)/(1-STOCK_FEE) # сколько хотим получить за наше кол-во 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=round(wanna_get/float(balances[CURRENCY_1]),pair_settings[CURR_PAIR]['decimal_places']), amount=round(balances[CURRENCY_1],8) )['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://wex.nz/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 """ # Купить как есть, потом продать с учетом комиссии my_need_price = avg_price 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=round(my_need_price,8), amount=round(my_amount,8))['return'] print(new_order) if DEBUG: print('Создан ордер на покупку', new_order['order_id']) except ZeroDivisionError: print('Не удается вычислить среднюю цену', prices) else: raise ScriptQuitCondition('Выход, не хватает денег') except ScriptError as e: print('ScriptError', e) except ScriptQuitCondition as e: print('ScriptQuitCondition', e) except Exception as e: print("!!!!",e) while(True): main_flow() time.sleep(1)

Реклама

Заключение

Бот проверен, покупку/продажу/отмену создает, в минус не уходит (хотя может купить на пике и соответственно притормозиться и ждать роста). Если будут замечены какие-то косяки в работе, пишите, поправим.

Вот как в процессе тестирования я заработал один цент с 10 долларов (выставлял 0.1% прибыльности):

Кому интересно, как устроено API у Wex и какие там есть методы, можете пройтись по этим ссылкам - официальная документация (https://wex.nz/api/3/docs#info, https://wex.nz/tapi/docs#Trade), а так же посмотреть примеры в статье про Yobit - у них одинаковое API. 

Так что всем удачи и хороших заработков!

bablofil.ru


Смотрите также

.