Выкладываю реализацию бота для 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="{r:0.8f}".format(r=round(wanna_get/float(balances[CURRENCY_1]),pair_settings[CURR_PAIR]['decimal_places'])), amount="{a:0.8f}".format(a=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="{r:0.8f}".format(r=round(my_need_price,8)), amount="{a:0.8f}".format(a=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.
Так что всем удачи и хороших заработков!