ГОТОВЫЙ бот WEX - СКАЧАТЬ ТУТ или ТУТ
Выкладываю реализацию бота для 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)