Перейти к публикации
Форум ботоводов

bros3670

Members
  • Публикации

    20
  • Зарегистрирован

  • Посещение

Репутация

0 Neutral

Посетители профиля

Блок посетителей профиля отключен и не будет отображаться другим пользователям

  1. bros3670

    Bot для Binance с MACD

    Добрый день. Может кто пробовал к боту с индикаторами прикрутить без numpy и talib "помогайку" как делали здесь https://bablofil.ru/macd-python-stock-bot/ У нас и данные вроде все есть, а точку пересечения графиков не пойму как посчитать.
  2. bros3670

    Bot для Binance с MACD

    Доброе утро. Заходите на биржу, открывайте график TradingView, добавляйте на графике индикаторы RSI и STOCH и там по своему усмотрению подбирайте при каких условиях боту покупать и продавать. Как показала практика, чем больше индикаторов и условий, тем меньше покупок. Период можно выбрать по числам Фибоначчи 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 У меня вот такие настройки. Но одна сделка в сутки((( Это фиаско((( Ручками больше торгую. RSI_PERIODS = 7 RSI_OVER_SOLD_THRESH = 34 RSI_OVER_BOUGHT_THRESH = 77 STOCH_PERIODS = 34 STOCH_OVER_SOLD_THRESH = 25 STOCH_OVER_BOUGHT_THRESH = 81 elif macd_advice['trand'] == 'BEAR' and macd_advice['growing'] and macd_advice['rsi'] == 'BUY' and \ (macd_advice['trend_motion_macd'] == 'DOWN_UP' or macd_advice['trend_motion_macd'] == 'UP'):
  3. bros3670

    Bot для Binance с MACD

    Сам бот 17_v3.14-6 pair.py
  4. bros3670

    Bot для Binance с MACD

    Работает. Но сделок мало. Пока только тестирую. Изначально был только rsi. Как по мне, действительно, не так прыгает.
  5. bros3670

    Bot для Binance с MACD

    Режим покупки: Проверка 6-ти часового MACD. Если тренд BEAR, пропускаем проверку 5-ти минутного MACD и RSI. Пропускаем покупку. Если 6-ти часовой тренд BULL, проверяем условия для покупки. По условиям: 5-ти минутный MACD = BEAR, Рост = True, RSI меньше 30, Движение тренда = DOWN_UP (тренд должен подать и развернуться в сторону роста). Производим покупку. Режим продажи: Проверка 5-ти минутного MACD. Если MACD = BEAR, пропускаем проверку RSI и STOCH. Если MACD = BULL, проверяем условие RSI больше 76. Если RSI больше 76, проверяем STOCH, если RSI меньше 76, пропускаем проверку STOCH. Если STOCH больше 80, ждём начало разворота тренда вниз UP _DOWN. Продаем. Стоп-лосс: Задумывалось так))) Если цена упала на 2 % от цены покупки, отменяем ордер и продаем по рынку. Пара уходит в заморозку на 1 час. После часа идет проверка 6-ти часового MACD. Если 6-ти часовой тренд BULL, реактивируем пару. Бот много пишет в лог. Размер log файла и к-во его бэкапов можно ограничить.
  6. bros3670

    stop loss и бинанс

    Спасибо. Разобрался. В настройках пары параметр указан. Ошибка возникает когда одна из пар выключена вручную. 'active': False,
  7. bros3670

    stop loss и бинанс

    Добрый день. Подскажите пожалуйста почему возникает ошибка? 2019-05-10 10:26:40,145 [ERROR] 'reactivate_pair_after_sec' Traceback (most recent call last): File "H:\БОТ в Разр\16 binance_bot\16binance_bot.py", line 321, in <module> if (int(time.time()) - p['deactivation_time']) >= p['reactivate_pair_after_sec']: KeyError: 'reactivate_pair_after_sec'
  8. bros3670

    Bot для Binance с MACD

    Спасибо большое. Помогло!
  9. bros3670

    Bot для Binance с MACD

    Вот полностью код. Если не сложно, тыкните меня носом) import sqlite3 import numpy import talib import requests import json import logging import time import os from datetime import datetime from binance_api import Binance bot = Binance( API_KEY='', API_SECRET='' ) """ Пропишите пары, на которые будет идти торговля. base - это базовая пара (BTC, ETH, BNB, USDT) - то, что на бинансе пишется в табличке сверху quote - это квотируемая валюта. Например, для торгов по паре NEO/USDT базовая валюта USDT, NEO - квотируемая """ pairs = [ { 'base': 'BTC', 'quote': 'ETH', 'offers_amount': 20, # Сколько предложений из стакана берем для расчета средней цены # Максимум 1000. Допускаются следующие значения:[5, 10, 20, 50, 100, 500, 1000] 'spend_sum': 0.0011, # Сколько тратить base каждый раз при покупке quote 'profit_markup': 0.002, # Какой навар нужен с каждой сделки? (0.001 = 0.1%) 'active': False, # True - по паре будет торговля. False 'use_stop_loss': True, # Нужно ли продавать с убытком при падении цены 'stop_loss': 2, # 1% - На сколько должна упасть цена, что бы продавать с убытком 'stop_after_sl': True, # True - если сработал stop-loss, то прекратить торговлю по паре }, { 'base': 'BTC', 'quote': 'XRP', 'offers_amount': 20, # Сколько предложений из стакана берем для расчета средней цены # Максимум 1000. Допускаются следующие значения:[5, 10, 20, 50, 100, 500, 1000] 'spend_sum': 0.0011, # Сколько тратить base каждый раз при покупке quote 'profit_markup': 0.002, # Какой навар нужен с каждой сделки? (0.001 = 0.1%) 'active': False, # True - по паре будет торговля 'use_stop_loss': True, # Нужно ли продавать с убытком при падении цены 'stop_loss': 2, # 2% - На сколько должна упасть цена, что бы продавать с убытком 'stop_after_sl': True, # True - если сработал stop-loss, то прекратить торговлю по паре }, { 'base': 'BTC', 'quote': 'LTC', 'offers_amount': 20, # Сколько предложений из стакана берем для расчета средней цены # Максимум 1000. Допускаются следующие значения:[5, 10, 20, 50, 100, 500, 1000] 'spend_sum': 0.0011, # Сколько тратить base каждый раз при покупке quote 'profit_markup': 0.002, # Какой навар нужен с каждой сделки? (0.001 = 0.1%) 'active': False, # True - по паре будет торговля 'use_stop_loss': True, # Нужно ли продавать с убытком при падении цены 'stop_loss': 2, # 1% - На сколько должна упасть цена, что бы продавать с убытком 'stop_after_sl': True, # True - если сработал stop-loss, то прекратить торговлю по паре }, { 'base': 'BTC', 'quote': 'EOS', 'offers_amount': 20, # Сколько предложений из стакана берем для расчета средней цены # Максимум 1000. Допускаются следующие значения:[5, 10, 20, 50, 100, 500, 1000] 'spend_sum': 0.0011, # Сколько тратить base каждый раз при покупке quote 'profit_markup': 0.002, # Какой навар нужен с каждой сделки? (0.001 = 0.1%) 'active': False , # True - по паре будет торговля 'use_stop_loss': True, # Нужно ли продавать с убытком при падении цены 'stop_loss': 2, # 2% - На сколько должна упасть цена, что бы продавать с убытком 'stop_after_sl': True, # True - если сработал stop-loss, то прекратить торговлю по паре }, { 'base': 'BTC', 'quote': 'BNB', 'offers_amount': 20, # Сколько предложений из стакана берем для расчета средней цены # Максимум 1000. Допускаются следующие значения:[5, 10, 20, 50, 100, 500, 1000] 'spend_sum': 0.0011, # Сколько тратить base каждый раз при покупке quote 'profit_markup': 0.002, # Какой навар нужен с каждой сделки? (0.001 = 0.1%) 'active': True, # True - по паре будет торговля 'use_stop_loss': True, # Нужно ли продавать с убытком при падении цены 'stop_loss': 2, # 1% - На сколько должна упасть цена, что бы продавать с убытком 'stop_after_sl': True, # True - если сработал stop-loss, то прекратить торговлю по паре }, { 'base': 'USDT', 'quote': 'BTC', 'offers_amount': 20, # Сколько предложений из стакана берем для расчета средней цены # Максимум 1000. Допускаются следующие значения:[5, 10, 20, 50, 100, 500, 1000] 'spend_sum': 0.15, # Сколько тратить base каждый раз при покупке quote 'profit_markup': 0.002, # Какой навар нужен с каждой сделки? (0.001 = 0.1%) 'active': False, # True - по паре будет торговля 'use_stop_loss': True, # Нужно ли продавать с убытком при падении цены 'stop_loss': 2, # 2% - На сколько должна упасть цена, что бы продавать с убытком 'stop_after_sl': True, # True - если сработал stop-loss, то прекратить торговлю по паре } ] BUY_LIFE_TIME_SEC = 30 # Сколько (в секундах) держать ордер на покупку открытым STOCK_FEE = 0.00075 # Комиссия, которую берет биржа (0.001 = 0.1%) # Если вы решите не платить комиссию в BNB, то установите в False. Обычно делать этого не надо USE_BNB_FEES = True # Relative_Strength_Index RSI_PERIODS = 14 # RSI Расчетный цикл RSI_OVER_SOLD_THRESH = 27 # Порог перепроданности RSI_OVER_BOUGHT_THRESH = 76 # Порог перекупленности # Stochastic_Oscillator STOCH_OVER_SOLD_THRESH = 20 # Порог перепроданности STOCH_OVER_BOUGHT_THRESH = 81 # Порог перекупленности # Moving_Average_Convergence_Divergence MACD_FAST = 12 MACD_SLOW = 26 MACD_SIGNAL = 9 BEAR_PERC = 70 # % Что считаем поворотом при медведе (подробности - https://bablofil.ru/macd-python-stock-bot/ BULL_PERC = 99 # % Что считаем поворотом при быке USE_MACD = True # True - оценивать тренд по MACD, False - покупать и продавать невзирая ни на что interval = '1h' # 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M # С помощью индикаторов технического анализа делаем вывод о целесообразности торговли в данный момент (https://bablofil.ru/macd-python-stock-bot/) def check_indicators(pair): numpy.seterr(all='ignore') resource = requests.get("https://api.binance.com/api/v1/klines?symbol=%s&interval=%s" % (pair, interval)) data = json.loads(resource.text) quotes = {} quotes['open']=numpy.asarray([item[1] for item in data], dtype='f8') quotes['high']=numpy.asarray([item[2] for item in data], dtype='f8') quotes['low']=numpy.asarray([item[3] for item in data], dtype='f8') quotes['close']=numpy.asarray([item[4] for item in data], dtype='f8') # Расчет Moving_Average_Convergence_Divergence macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=MACD_FAST, slowperiod=MACD_SLOW, signalperiod=MACD_SIGNAL) 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) # Расчет Stochastic_Oscillator slowk, slowd = talib.STOCH(quotes['high'], quotes['low'], quotes['close'], fastk_period=21, slowk_period=1, slowk_matype=0, slowd_period=3, slowd_matype=0) for offset, elem in enumerate(slowk): a=STOCH_OVER_SOLD_THRESH p_a=float(slowk[offset-1]) p_b=float(slowd[offset-1]) p_c=float(slowd[offset]) p_d=float(slowk[offset]) try: if (p_a<a and p_b<a and p_c<a and p_d<a) and (p_a==p_b and p_a==p_d): av_point=a/2 elif (p_a>100-a and p_b>100-a and p_c>100-a and p_d>100-a) and (p_a==p_b and p_a==p_d): av_point=100-a/2 else: av_point=((p_c-p_b)*(p_a-p_b)/((p_c-p_b)+(p_a-p_d)))+p_b except: print('деление на 0?') v = 'WAIT' if (slowk[offset-1] < slowd[offset-1] and slowk[offset] >= slowd[offset] and av_point<a): v = 'BUY' if (slowk[offset-1] > slowd[offset-1] and slowk[offset] <= slowd[offset] and av_point>STOCH_OVER_BOUGHT_THRESH): v = 'SELL' # hist_data2.append(v) # Расчет Relative_Strength_Index rsi = talib.RSI(quotes['close'], timeperiod=RSI_PERIODS) for offset, elem in enumerate(rsi): q=rsi[offset] q1=float(rsi[-2]) q2=float(rsi[-1]) if q2 < RSI_OVER_SOLD_THRESH: v1 = 'BUY' elif q2 > RSI_OVER_BOUGHT_THRESH: v1 = 'SELL' else: v1 = 'WAIT' return ({'trand':trand, 'growing':growing, 'stoch':v, 'rsi': v1}) # Получаем ограничения торгов по всем парам с биржи local_time = int(time.time()) limits = bot.exchangeInfo() server_time = int(limits['serverTime'])//1000 # Ф-ция, которая приводит любое число к числу, кратному шагу, указанному биржей # Если передать параметр increase=True то округление произойдет к следующему шагу def adjust_to_step(value, step, increase=False): return ((int(value * 100000000) - int(value * 100000000) % int( float(step) * 100000000)) / 100000000)+(float(step) if increase else 0) # Подключаем логирование logging.basicConfig( format="%(asctime)s [%(levelname)-5.5s] %(message)s", level=logging.INFO, #DEBUG handlers=[ logging.FileHandler("{path}/logs/{fname}.log".format(path=os.path.dirname(os.path.abspath(__file__)), fname="binance")), logging.StreamHandler() ]) log = logging.getLogger('') # Бесконечный цикл программы shift_seconds = server_time-local_time bot.set_shift_seconds(shift_seconds) log.info(""" Текущее время: {local_time_d} {local_time_u} Время сервера: {server_time_d} {server_time_u} Разница: {diff:0.8f} {warn} Бот будет работать, как будто сейчас: {fake_time_d} {fake_time_u} """.format( local_time_d = datetime.fromtimestamp(local_time), local_time_u=local_time, server_time_d=datetime.fromtimestamp(server_time), server_time_u=server_time, diff=abs(local_time-server_time), warn="ТЕКУЩЕЕ ВРЕМЯ ВЫШЕ" if local_time > server_time else '', fake_time_d=datetime.fromtimestamp(local_time+shift_seconds), fake_time_u=local_time+shift_seconds)) while True: try: # Синхронизация времени time.sleep(0.045) bot.set_shift_seconds(int(bot.time()['serverTime'])//1000 - int(time.time())) # Устанавливаем соединение с локальной базой данных conn = sqlite3.connect('binance.db') cursor = conn.cursor() # Если не существует таблиц, их нужно создать (первый запуск) orders_q = """ create table if not exists orders ( order_type TEXT, order_pair TEXT, buy_order_id NUMERIC, buy_amount REAL, buy_price REAL, buy_created DATETIME, buy_finished DATETIME NULL, buy_cancelled DATETIME NULL, sell_order_id NUMERIC NULL, sell_amount REAL NULL, sell_price REAL NULL, sell_created DATETIME NULL, sell_finished DATETIME NULL, force_sell INT DEFAULT 0 ); """ cursor.execute(orders_q) log.debug("Получаем все неисполненные ордера по БД") orders_q = """ SELECT CASE WHEN order_type='buy' THEN buy_order_id ELSE sell_order_id END order_id , order_type , order_pair , sell_amount , sell_price , strftime('%s',buy_created) , buy_amount , buy_price FROM orders WHERE buy_cancelled IS NULL AND CASE WHEN order_type='buy' THEN buy_finished IS NULL ELSE sell_finished IS NULL END """ orders_info = {} for row in cursor.execute(orders_q): orders_info[str(row[0])] = {'order_type': row[1], 'order_pair': row[2], 'sell_amount': row[3], 'sell_price': row[4], 'buy_created': row[5], 'buy_amount': row[6], 'buy_price': row[7]} # формируем словарь из указанных пар, для удобного доступа all_pairs = {pair['quote'].upper() + pair['base'].upper():pair for pair in pairs} if not [p for p in pairs if p['active']]: log.info('Больше нет активных пар') break if orders_info: log.info("Получены неисполненные ордера из БД: {orders}".format(orders=[(order, orders_info[order]['order_pair']) for order in orders_info])) # Проверяем каждый неисполненный по базе ордер for order in orders_info: # Получаем по ордеру последнюю информацию по бирже stock_order_data = bot.orderInfo(symbol=orders_info[order]['order_pair'], orderId=order) order_status = stock_order_data['status'] log.debug("Состояние ордера {order} - {status}".format(order=order, status=order_status)) if order_status == 'NEW': log.info('Ордер {order} всё еще не выполнен'.format(order=order)) # Если ордер на покупку if orders_info[order]['order_type'] == 'buy': # Если ордер уже исполнен if order_status == 'FILLED': log.info("""Ордер {order} выполнен, получено {exec_qty:0.8f}. Проверяем индикаторы, если рынок в нужном состоянии, выставляем ордер на продажу""".format(order=order, exec_qty=float(stock_order_data['executedQty']))) # смотрим, какие ограничения есть для создания ордера на продажу for elem in limits['symbols']: if elem['symbol'] == orders_info[order]['order_pair']: CURR_LIMITS = elem break else: raise Exception("Не удалось найти настройки выбранной пары " + pair_name) # Проверяем индикаторы, если рынок в нужном состоянии, выставляем ордер на продажу if USE_MACD: macd_advice = check_indicators(pair_name) if macd_advice['trand'] == 'BEAR' or (macd_advice['trand'] == 'BULL' and macd_advice['growing'] == False): log.info('Выставлять ордер на продажу {pair}'.format(pair=pair_name)+\ ' нельзя, т.к. ситуация на рынке неподходящая: Трэнд '+str(macd_advice['trand'])+'; Рост '+str(macd_advice['growing'])) log.info('Пропускаем проверку RSI и STOCH, т.к. MACD не позволяет выставлять ордер на продажу') elif macd_advice['rsi'] == 'WAIT' or macd_advice['rsi'] == 'BUY': log.info('MACD допускает выставление ордера на продажу {pair}'.format(pair=pair_name)+': Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])) log.info('Продавать нельзя, т.к. ситуация на рынке неподходящая: RSI '+str(macd_advice['rsi'])) log.info('Пропускаем проверку STOCH, т.к. RSI не позволяет выставлять ордер на продажу') elif macd_advice['stoch'] == 'WAIT' or macd_advice['stoch'] == 'BUY': log.info('MACD допускает выставление ордера на продажу {pair}'.format(pair=pair_name)+': Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])) log.info('RSI допускает уход с рынка: RSI '+str(macd_advice['rsi'])) log.info('Продавать нельзя, т.к. ситуация на рынке неподходящая: STOCH '+str(macd_advice['stoch'])) else: log.info('Выставляем ордер на продажу {pair}'.format(pair=pair_name)+', т.к ситуация подходящая: Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])+'; RSI '+str(macd_advice['rsi'])+'; STOCH '+str(macd_advice['stoch'])) # Рассчитываем данные для ордера на продажу # Имеющееся кол-во на продажу has_amount = orders_info[order]['buy_amount']*((1-STOCK_FEE) if not USE_BNB_FEES else 1) # Приводим количество на продажу к числу, кратному по ограничению sell_amount = adjust_to_step(has_amount, CURR_LIMITS['filters'][2]['stepSize']) # Рассчитываем минимальную сумму, которую нужно получить, что бы остаться в плюсе need_to_earn = orders_info[order]['buy_amount']*orders_info[order]['buy_price']*(1+all_pairs[stock_order_data['symbol']]['profit_markup']) # Рассчитываем минимальную цену для продажи min_price = (need_to_earn/sell_amount)/((1-STOCK_FEE) if not USE_BNB_FEES else 1) # Приводим к нужному виду, если цена после срезки лишних символов меньше нужной, увеличиваем на шаг cut_price = max( adjust_to_step(min_price, CURR_LIMITS['filters'][0]['tickSize'], increase=True), adjust_to_step(min_price, CURR_LIMITS['filters'][0]['tickSize'])) # Получаем текущие курсы с биржи curr_rate = float(bot.tickerPrice(symbol=orders_info[order]['order_pair'])['price']) # Если текущая цена выше нужной, продаем по текущей need_price = max(cut_price, curr_rate) log.info(""" Изначально было куплено {buy_initial:0.8f}, за вычетом комиссии {has_amount:0.8f}, Получится продать только {sell_amount:0.8f} Нужно получить как минимум {need_to_earn:0.8f} {curr} Мин. цена (с комиссией) составит {min_price}, после приведения {cut_price:0.8f} Текущая цена рынка {curr_rate:0.8f} Итоговая цена продажи: {need_price:0.8f} """.format( buy_initial=orders_info[order]['buy_amount'], has_amount=has_amount,sell_amount=sell_amount, need_to_earn=need_to_earn, curr=all_pairs[orders_info[order]['order_pair']]['base'], min_price=min_price, cut_price=cut_price, need_price=need_price, curr_rate=curr_rate )) # Если итоговая сумма продажи меньше минимума, ругаемся и не продаем if (need_price*has_amount) <float(CURR_LIMITS['filters'][3]['minNotional']): raise Exception(""" Итоговый размер сделки {trade_am:0.8f} меньше допустимого по паре {min_am:0.8f}. """.format( trade_am=(need_price*has_amount), min_am=float(CURR_LIMITS['filters'][3]['minNotional']))) log.info('Рассчитан ордер на продажу: кол-во {amount:0.8f}, курс: {rate:0.8f}'.format(amount=sell_amount, rate=need_price)) # Отправляем команду на создание ордера с рассчитанными параметрами new_order = bot.createOrder( symbol=orders_info[order]['order_pair'], recvWindow=5000, side='SELL', type='LIMIT', timeInForce='GTC', # Good Till Cancel quantity="{quantity:0.{precision}f}".format( quantity=sell_amount, precision=CURR_LIMITS['baseAssetPrecision'] ), price="{price:0.{precision}f}".format( price=need_price, precision=CURR_LIMITS['baseAssetPrecision'] ), newOrderRespType='FULL' ) # Если ордер создался без ошибок, записываем данные в базу данных if 'orderId' in new_order: log.info("Создан ордер на продажу {new_order}".format(new_order=new_order)) cursor.execute( """ UPDATE orders SET order_type = 'sell', buy_finished = datetime(), sell_order_id = :sell_order_id, sell_created = datetime(), sell_amount = :sell_amount, sell_price = :sell_initial_price WHERE buy_order_id = :buy_order_id """, { 'buy_order_id': order, 'sell_order_id': new_order['orderId'], 'sell_amount': sell_amount, 'sell_initial_price': need_price } ) conn.commit() # Если были ошибки при создании, выводим сообщение else: log.warning("Не удалось создать ордер на продажу {new_order}".format(new_order=new_order)) else: log.info("Для выполненного ордера на покупку выставляем ордер на продажу {pair}".format(pair=pair_name)) # Ордер еще не исполнен, частичного исполнения нет, проверяем возможность отмены elif order_status == 'NEW': order_created = int(orders_info[order]['buy_created']) time_passed = int(time.time()) - order_created log.debug("Прошло времени после создания {passed:0.2f}".format(passed=time_passed)) # Прошло больше времени, чем разрешено держать ордер if time_passed > BUY_LIFE_TIME_SEC: log.info("""Ордер {order} пора отменять, прошло {passed:0.1f} сек.""".format(order=order, passed=time_passed)) # Отменяем ордер на бирже cancel = bot.cancelOrder( symbol=orders_info[order]['order_pair'], orderId=order) # Если удалось отменить ордер, скидываем информацию в БД if 'orderId' in cancel: log.info("Ордер {order} был успешно отменен".format(order=order)) cursor.execute( """ UPDATE orders SET buy_cancelled = datetime() WHERE buy_order_id = :buy_order_id """, { 'buy_order_id': order } ) conn.commit() else: log.warning("Не удалось отменить ордер: {cancel}".format(cancel=cancel)) elif order_status == 'PARTIALLY_FILLED': log.info("Ордер {order} частично исполнен, ждем завершения".format(order=order)) # Если это ордер на продажу, и он исполнен if order_status == 'FILLED' and orders_info[order]['order_type'] == 'sell': log.info("Ордер {order} на продажу исполнен".format(order=order)) # Обновляем информацию в БД cursor.execute( """ UPDATE orders SET sell_finished = datetime() WHERE sell_order_id = :sell_order_id """, { 'sell_order_id': order } ) conn.commit() if all_pairs[orders_info[order]['order_pair']]['use_stop_loss']: if order_status == 'NEW' and orders_info[order]['order_type'] == 'sell': curr_rate = float(bot.tickerPrice(symbol=orders_info[order]['order_pair'])['price']) if (1 - curr_rate/orders_info[order]['buy_price'])*100 >= all_pairs[orders_info[order]['order_pair']]['stop_loss']: log.info("{pair} Цена упала до стоплосс (покупали по {b:0.8f}, сейчас {s:0.8f}), пора продавать".format( pair=orders_info[order]['order_pair'],b=orders_info[order]['buy_price'],s=curr_rate)) # Отменяем ордер на бирже cancel = bot.cancelOrder( symbol=orders_info[order]['order_pair'], orderId=order) # Если удалось отменить ордер, скидываем информацию в БД if 'orderId' in cancel: log.info("Ордер {order} был успешно отменен, продаем по рынку".format(order=order)) new_order = bot.createOrder( symbol=orders_info[order]['order_pair'], recvWindow=15000, side='SELL', type='MARKET', quantity=orders_info[order]['sell_amount'], ) if not new_order.get('code'): log.info("Создан ордер на продажу по рынку " + str(new_order)) cursor.execute( """ DELETE FROM orders WHERE sell_order_id = :sell_order_id """, { 'sell_order_id': order } ) conn.commit() if all_pairs[orders_info[order]['order_pair']]['stop_after_sl']: all_pairs[orders_info[order]['order_pair']]['active'] = False log.info("Сработал Stop-Loss по паре {pair}, она отключена".format(pair=orders_info[order]['order_pair'])) else: log.warning("Не удалось отменить ордер: {cancel}".format(cancel=cancel)) else: log.info("{pair} (покупали по {b:0.8f}, сейчас {s:0.8f}), расхождение {sl:0.4f}%, panic_sell = {ps:0.4f}% ({ps_rate:0.8f}), продажа с профитом: {tp:0.8f}".format( pair=orders_info[order]['order_pair'], b=orders_info[order]['buy_price'], s=curr_rate, sl=(1 - curr_rate/orders_info[order]['buy_price'])*100, ps=all_pairs[orders_info[order]['order_pair']]['stop_loss'], ps_rate=orders_info[order]['buy_price']/100 * (100-all_pairs[orders_info[order]['order_pair']]['stop_loss']), tp=orders_info[order]['sell_price'])) elif order_status == 'CANCELED' and orders_info[order]['order_type'] == 'sell': # На случай, если после отмены произошел разрыв связи new_order = bot.createOrder( symbol=orders_info[order]['order_pair'], recvWindow=15000, side='SELL', type='MARKET', quantity=orders_info[order]['sell_amount'], ) if not new_order.get('code'): log.info("Создан ордер на продажу по рынку " + str(new_order)) cursor.execute( """ DELETE FROM orders WHERE sell_order_id = :sell_order_id """, { 'sell_order_id': order } ) conn.commit() else: log.debug("Неисполненных ордеров в БД нет") log.debug('Получаем из настроек все пары, по которым нет неисполненных ордеров') orders_q = """ SELECT distinct(order_pair) pair FROM orders WHERE buy_cancelled IS NULL AND CASE WHEN order_type='buy' THEN buy_finished IS NULL ELSE sell_finished IS NULL END """ # Получаем из базы все ордера, по которым есть торги, и исключаем их из списка, по которому будем создавать новые ордера for row in cursor.execute(orders_q): del all_pairs[row[0]] all_pairs = {k:v for k,v in all_pairs.items() if v['active']} # Если остались пары, по которым нет текущих торгов if all_pairs: log.info('Найдены пары, по которым нет неисполненных ордеров: {pairs}'.format(pairs=list(all_pairs.keys()))) for pair_name, pair_obj in all_pairs.items(): try: log.info("*****Работаем с парой {pair}*****".format(pair=pair_name)) if not pair_obj['active']: log.debug("Пара {pair} не активна, пропускаем".format(pair=pair_name)) continue # Получаем лимиты пары с биржи for elem in limits['symbols']: if elem['symbol'] == pair_name: CURR_LIMITS = elem break else: raise Exception("Не удалось найти настройки выбранной пары " + pair_name) # Получаем балансы с биржи по указанным валютам balances = { balance['asset']: float(balance['free']) for balance in bot.account()['balances'] if balance['asset'] in [pair_obj['base'], pair_obj['quote']] } log.info("Баланс {balance}".format(balance=["{k}:{bal:0.8f}".format(k=k, bal=balances[k]) for k in balances])) # Если баланс позволяет торговать - выше лимитов биржи и выше указанной суммы в настройках if balances[pair_obj['base']] >= pair_obj['spend_sum']: # Получаем информацию по предложениям из стакана, в кол-ве указанном в настройках offers = bot.depth( symbol=pair_name, limit=pair_obj['offers_amount']) # Берем цены покупок (для цен продаж замените bids на asks) prices = [float(bid[0]) for bid in offers['bids']] try: # Рассчитываем среднюю цену из полученных цен avg_price = sum(prices) / len(prices) # Среднюю цену приводим к требованиям биржи о кратности my_need_price = adjust_to_step(avg_price, CURR_LIMITS['filters'][0]['tickSize']) # Рассчитываем кол-во, которое можно купить, и тоже приводим его к кратному значению my_amount = adjust_to_step(pair_obj['spend_sum']/ my_need_price, CURR_LIMITS['filters'][2]['stepSize']) # Если в итоге получается объем торгов меньше минимально разрешенного, то ругаемся и не создаем ордер if my_amount < float(CURR_LIMITS['filters'][2]['stepSize']) or my_amount < float(CURR_LIMITS['filters'][2]['minQty']): log.warning(""" Минимальная сумма лота: {min_lot:0.8f} Минимальный шаг лота: {min_lot_step:0.8f} На свои деньги мы могли бы купить {wanted_amount:0.8f} После приведения к минимальному шагу мы можем купить {my_amount:0.8f} Покупка невозможна, выход. Увеличьте размер ставки """.format( wanted_amount=pair_obj['spend_sum']/ my_need_price, my_amount=my_amount, min_lot=float(CURR_LIMITS['filters'][2]['minQty']), min_lot_step=float(CURR_LIMITS['filters'][2]['stepSize']) )) continue # Итоговый размер лота trade_am = my_need_price*my_amount log.debug(""" Средняя цена {av_price:0.8f}, после приведения {need_price:0.8f}, объем после приведения {my_amount:0.8f}, итоговый размер сделки {trade_am:0.8f} """.format( av_price=avg_price, need_price=my_need_price, my_amount=my_amount, trade_am=trade_am)) # Если итоговый размер лота меньше минимального разрешенного, то ругаемся и не создаем ордер if trade_am < float(CURR_LIMITS['filters'][3]['minNotional']): raise Exception(""" Итоговый размер сделки {trade_am:0.8f} меньше допустимого по паре {min_am:0.8f}. Увеличьте сумму торгов (в {incr} раз(а))""".format( trade_am=trade_am, min_am=float(CURR_LIMITS['filters'][3]['minNotional']), incr=float(CURR_LIMITS['filters'][3]['minNotional'])/trade_am)) log.info('Рассчитан ордер на покупку: кол-во {amount:0.8f}, курс: {rate:0.8f}'.format(amount=my_amount, rate=my_need_price)) # Проверяем индикаторы, если рынок в нужном состоянии, выставляем ордер на покупку if USE_MACD: macd_advice = check_indicators(pair_name) if macd_advice['trand'] == 'BEAR' and macd_advice['growing'] == False and macd_advice['rsi'] == 'BUY' and macd_advice['stoch'] == 'BUY': log.info("Благоприятные условия для покупки {pair}".format(pair=pair_name)) log.info('Выставляем ордер на покупку {pair}'.format(pair=pair_name)+', т.к ситуация подходящая: Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])+'; RSI '+str(macd_advice['rsi'])+'; STOCH '+str(macd_advice['stoch'])) # Отправляем команду на бирже о создании ордера на покупку с рассчитанными параметрами new_order = bot.createOrder( symbol=pair_name, recvWindow=5000, side='BUY', type='LIMIT', timeInForce='GTC', # Good Till Cancel quantity="{quantity:0.{precision}f}".format( quantity=my_amount, precision=CURR_LIMITS['baseAssetPrecision'] ), price="{price:0.{precision}f}".format( price=my_need_price, precision=CURR_LIMITS['baseAssetPrecision'] ), newOrderRespType='FULL') # Если удалось создать ордер на покупку, записываем информацию в БД if 'orderId' in new_order: log.info("Создан ордер на покупку {new_order}".format(new_order=new_order)) cursor.execute( """ INSERT INTO orders( order_type, order_pair, buy_order_id, buy_amount, buy_price, buy_created ) Values ( 'buy', :order_pair, :order_id, :buy_order_amount, :buy_initial_price, datetime() ) """, { 'order_pair': pair_name, 'order_id': new_order['orderId'], 'buy_order_amount': my_amount, 'buy_initial_price': my_need_price } ) conn.commit() else: log.warning("Не удалось создать ордер на покупку! {new_order}".format(new_order=str(new_order))) else: log.info('Условия рынка не подходят для покупки {pair}'.format(pair=pair_name)+': Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])+'; RSI '+str(macd_advice['rsi'])+'; STOCH '+str(macd_advice['stoch'])) else: log.info("Создан ордер на покупку {new_order}".format(new_order=new_order)) except ZeroDivisionError: log.info('Не удается вычислить среднюю цену: {prices}'.format(prices=str(prices))) else: log.warning('Для создания ордера на покупку нужно минимум {min_qty:0.8f} {curr}, выход'.format( min_qty=pair_obj['spend_sum'], curr=pair_obj['base'])) except Exception as e: log.exception(e) else: log.debug('По всем парам есть неисполненные ордера') except Exception as e: log.exception(e) finally: conn.close()
  10. bros3670

    Bot для Binance с MACD

    # Проверяем индикаторы, если рынок в нужном состоянии, выставляем ордер на покупку if USE_MACD: macd_advice = check_indicators(pair_name) if macd_advice['trand'] == 'BEAR' and macd_advice['growing'] == False and macd_advice['rsi'] == 'BUY' and macd_advice['stoch'] == 'BUY': log.info("Благоприятные условия для покупки {pair}".format(pair=pair_name)) log.info('Выставляем ордер на покупку {pair}'.format(pair=pair_name)+', т.к ситуация подходящая: Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])+'; RSI '+str(macd_advice['rsi'])+'; STOCH '+str(macd_advice['stoch']))
  11. bros3670

    Bot для Binance с MACD

    # Проверяем индикаторы, если рынок в нужном состоянии, выставляем ордер на продажу if USE_MACD: macd_advice = check_indicators(pair_name) if macd_advice['trand'] == 'BEAR' or (macd_advice['trand'] == 'BULL' and macd_advice['growing'] == False): log.info('Выставлять ордер на продажу {pair}'.format(pair=pair_name)+\ ' нельзя, т.к. ситуация на рынке неподходящая: Трэнд '+str(macd_advice['trand'])+'; Рост '+str(macd_advice['growing'])) log.info('Пропускаем проверку RSI и STOCH, т.к. MACD не позволяет выставлять ордер на продажу') elif macd_advice['rsi'] == 'WAIT' or macd_advice['rsi'] == 'BUY': log.info('MACD допускает выставление ордера на продажу {pair}'.format(pair=pair_name)+': Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])) log.info('Продавать нельзя, т.к. ситуация на рынке неподходящая: RSI '+str(macd_advice['rsi'])) log.info('Пропускаем проверку STOCH, т.к. RSI не позволяет выставлять ордер на продажу') elif macd_advice['stoch'] == 'WAIT' or macd_advice['stoch'] == 'BUY': log.info('MACD допускает выставление ордера на продажу {pair}'.format(pair=pair_name)+': Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])) log.info('RSI допускает уход с рынка: RSI '+str(macd_advice['rsi'])) log.info('Продавать нельзя, т.к. ситуация на рынке неподходящая: STOCH '+str(macd_advice['stoch'])) else: log.info('Выставляем ордер на продажу {pair}'.format(pair=pair_name)+', т.к ситуация подходящая: Трэнд '+str(macd_advice['trand'])+\ '; Рост '+str(macd_advice['growing'])+'; RSI '+str(macd_advice['rsi'])+'; STOCH '+str(macd_advice['stoch']))
  12. bros3670

    Bot для Binance с MACD

    # Relative_Strength_Index RSI_PERIODS = 14 # RSI Расчетный цикл RSI_OVER_SOLD_THRESH = 27 # Порог перепроданности RSI_OVER_BOUGHT_THRESH = 76 # Порог перекупленности # Stochastic_Oscillator STOCH_OVER_SOLD_THRESH = 20 # Порог перепроданности STOCH_OVER_BOUGHT_THRESH = 81 # Порог перекупленности # Moving_Average_Convergence_Divergence MACD_FAST = 12 MACD_SLOW = 26 MACD_SIGNAL = 9 BEAR_PERC = 70 # % Что считаем поворотом при медведе (подробности - https://bablofil.ru/macd-python-stock-bot/ BULL_PERC = 99 # % Что считаем поворотом при быке USE_MACD = True # True - оценивать тренд по MACD, False - покупать и продавать невзирая ни на что interval = '1h' # 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M # С помощью индикаторов технического анализа делаем вывод о целесообразности торговли в данный момент (https://bablofil.ru/macd-python-stock-bot/) def check_indicators(pair): numpy.seterr(all='ignore') resource = requests.get("https://api.binance.com/api/v1/klines?symbol=%s&interval=%s" % (pair, interval)) data = json.loads(resource.text) quotes = {} quotes['open']=numpy.asarray([item[1] for item in data], dtype='f8') quotes['high']=numpy.asarray([item[2] for item in data], dtype='f8') quotes['low']=numpy.asarray([item[3] for item in data], dtype='f8') quotes['close']=numpy.asarray([item[4] for item in data], dtype='f8') # Расчет Moving_Average_Convergence_Divergence macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=MACD_FAST, slowperiod=MACD_SLOW, signalperiod=MACD_SIGNAL) 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) # Расчет Stochastic_Oscillator slowk, slowd = talib.STOCH(quotes['high'], quotes['low'], quotes['close'], fastk_period=21, slowk_period=1, slowk_matype=0, slowd_period=3, slowd_matype=0) for offset, elem in enumerate(slowk): a=STOCH_OVER_SOLD_THRESH p_a=float(slowk[offset-1]) p_b=float(slowd[offset-1]) p_c=float(slowd[offset]) p_d=float(slowk[offset]) try: if (p_a<a and p_b<a and p_c<a and p_d<a) and (p_a==p_b and p_a==p_d): av_point=a/2 elif (p_a>100-a and p_b>100-a and p_c>100-a and p_d>100-a) and (p_a==p_b and p_a==p_d): av_point=100-a/2 else: av_point=((p_c-p_b)*(p_a-p_b)/((p_c-p_b)+(p_a-p_d)))+p_b except: print('деление на 0?') v = 'WAIT' if (slowk[offset-1] < slowd[offset-1] and slowk[offset] >= slowd[offset] and av_point<a): v = 'BUY' if (slowk[offset-1] > slowd[offset-1] and slowk[offset] <= slowd[offset] and av_point>STOCH_OVER_BOUGHT_THRESH): v = 'SELL' # hist_data2.append(v) # Расчет Relative_Strength_Index rsi = talib.RSI(quotes['close'], timeperiod=RSI_PERIODS) for offset, elem in enumerate(rsi): q=rsi[offset] q1=float(rsi[-2]) q2=float(rsi[-1]) if q2 < RSI_OVER_SOLD_THRESH: v1 = 'BUY' elif q2 > RSI_OVER_BOUGHT_THRESH: v1 = 'SELL' else: v1 = 'WAIT' return ({'trand':trand, 'growing':growing, 'stoch':v, 'rsi': v1})
  13. bros3670

    Bot для Binance с MACD

    А как вставить код?
  14. bros3670

    Bot для Binance с MACD

    Добрый вечер Андрей! Я новичок. Помогите пожалуйста найти ошибку в Вашем боте для Бинанса с моими вставками. Даже не моими, код ребят с чата и форума. По моим соображениям дело в функции def check_indicators(pair): и ее аргументе. При покупке бот нормально отрабатывает. Покупает. 2019-05-06 09:18:38,470 [INFO ] Баланс ['BTC:0.01141078', 'BNB:0.48739166'] 2019-05-06 09:18:39,000 [INFO ] Рассчитан ордер на покупку: кол-во 0.28000000, курс: 0.00384390 2019-05-06 09:18:39,562 [INFO ] Благоприятные условия для покупки BNBBTC 2019-05-06 09:18:39,562 [INFO ] Выставляем ордер на покупку BNBBTC, т.к ситуация подходящая: Трэнд BEAR; Рост False; RSI BUY; STOCH BUY 2019-05-06 09:18:41,231 [INFO ] Создан ордер на покупку {'symbol': 'BNBBTC', 'orderId': 157603959, 'clientOrderId': 'N2Aga7jC5KNYKifkBECZcT', 2019-05-07 19:16:46,521 [INFO ] Найдены пары, по которым нет неисполненных ордеров: ['BNBBTC'] 2019-05-07 19:16:46,536 [INFO ] *****Работаем с парой BNBBTC***** 2019-05-07 19:16:47,083 [INFO ] Баланс ['BTC:0.01033463', 'BNB:0.76718166'] 2019-05-07 19:16:47,568 [INFO ] Рассчитан ордер на покупку: кол-во 0.29000000, курс: 0.00369010 2019-05-07 19:16:48,833 [INFO ] Условия рынка не подходят для покупки BNBBTC: Трэнд BEAR; Рост True; RSI WAIT; STOCH WAIT Но при продаже выскакивает ошибка 2019-05-07 19:41:34,676 [INFO ] Ордер 157606767 выполнен, получено 0.28000000. Проверяем индикаторы, если рынок в нужном состоянии, выставляем ордер на продажу 2019-05-07 19:41:34,720 [ERROR] name 'pair_name' is not defined Traceback (most recent call last): File "H:\БОТ в Разр\12 binance_bot\13binance_bot.py", line 334, in <module> macd_advice = check_indicators(pair_name) NameError: name 'pair_name' is not defined И еще вопрос. При такой доработку бота, корректно ли отработает расчет цены ордера на продажу? # Если текущая цена выше нужной, продаем по текущей need_price = max(cut_price, curr_rate) Спасибо. Извините за много букв
  15. bros3670

    Bot для Binance с MACD

    binance MACD.py
×
×
  • Создать...