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

Ф-ция, которая приводит любое число к числу - редкая ошибка !!


Рекомендованные сообщения

# Ф-ция, которая приводит любое число к числу, кратному шагу, указанному биржей
# Если передать параметр 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)

если пара BNBUSDT,  ее CURR_LIMITS['filters'][2]['stepSize'] получит с биржи 0.01000000

если settings['amount'] дать цифру 0.58 (settings['amount']=0.58)

quantity = adjust_to_step(settings['amount'], CURR_LIMITS['filters'][2]['stepSize'])

quantity будет равна 0.57 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

пол дня голову ломал, решение нашел только такое:

перед вызовом функции adjust_to_step сделать

settings['amount'] = float(settings['amount'] + 0.00000001)

а далее уже quantity = adjust_to_step(settings['amount'], CURR_LIMITS['filters'][2]['stepSize'])

Проблемные цифры нашел только 0.57, 0.58, 1.13 )))

Та же проблема если округлять так:

ticks = {}

for filt in CURR_LIMITS['filters']:

      if filt['filterType'] == 'LOT_SIZE':

         ticks[pair] = filt['stepSize'].find('1') - 1

         break

quantity = settings['amount']

order_quantity = math.floor(quantity * 10 ** ticks[pair]) / float(10 ** ticks[pair])

***************************

settings['amount'] = float(settings['amount'] + 0.00000001) - спасает

Изменено пользователем Andrei22
Ссылка на сообщение
Поделиться на других сайтах

Добрый день !

у меня бот отличается

подскажите  где можно поправить

вот сам бот

"""
    Подробная информация о боте на сайте bablofil.ru/bot-dlya-binance
"""
import sqlite3

import time
import threading

import bablofil_ta as ta

from config import (bot, pairs, log, TIMEFRAME, KLINES_LIMITS, POINTS_TO_ENTER, USE_OPEN_CANDLES)
from misc import adjust_to_step, sync_time, calc_buy_avg_rate, calc_sell_avg_rate, get_order_trades
from db_queries import (make_initial_tables, get_db_open_orders, get_db_running_pairs,
                        add_db_new_order, update_buy_rate, store_sell_order, update_sell_rate)

limits = bot.exchangeInfo()

def main_flow():
    while True:
        try:
            # Устанавливаем соединение с локальной базой данных
            conn = sqlite3.connect('binance.db')
            conn.row_factory = sqlite3.Row
            cursor = conn.cursor()

            # Если не существует таблиц, их нужно создать (первый запуск)
            make_initial_tables(cursor)

            log.debug("Получаем все неисполненные ордера по БД")
            orders_info = get_db_open_orders(cursor)

            # формируем словарь из указанных пар, для удобного доступа
            all_pairs = {pair['quote'].upper() + pair['base'].upper():pair for pair in pairs if pair['active']}

            if orders_info:
                log.debug("Получены неисполненные ордера из БД: {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 orders_info[order]['order_type'] == 'buy':
                        if not orders_info[order]['buy_verified']:
                            # По ордеру не были получены сделки
                            order_trades = get_order_trades(
                                order_id=orders_info[order]['order_id'],
                                pair=orders_info[order]['order_pair'], bot=bot
                            )

                            avg_rate = calc_buy_avg_rate(order_trades, log)
                            if avg_rate > 0:
                                update_buy_rate(cursor, conn, orders_info[order]['order_id'], avg_rate)
                            else:
                                log.debug("Не удается вычислить цену покупки, пропуск")
                                continue

                        # Если ордер уже исполнен
                        if order_status == 'FILLED':
                            got_qty = float(stock_order_data['executedQty'])
                            log.info("""
                                Ордер {order} выполнен, получено {exec_qty:0.8f}.
                                Проверяем, не стоит ли создать ордер на продажу
                            """.format(
                                order=order, exec_qty=got_qty
                            ))

                            # смотрим, какие ограничения есть для создания ордера на продажу
                            for elem in limits['symbols']:
                                if elem['symbol'] == orders_info[order]['order_pair']:
                                    CURR_LIMITS = elem
                                    break
                            else:
                                raise Exception("Не удалось найти настройки выбранной пары " + pair_name)

                            got_qty = adjust_to_step(got_qty, CURR_LIMITS['filters'][2]['stepSize'])

                            prices = bot.tickerBookTicker(
                                symbol=orders_info[order]['order_pair']
                            )

                            # Берем цены покупок (нужно будет продавать по рынку)
                            curr_rate = float(prices['bidPrice'])

                            price_change = (curr_rate/orders_info[order]['buy_price']-1)*100
                            log.debug("Цена изменилась на {r:0.8f}%, процент для продажи {sp:0.8f}".format(r=price_change, sp=all_pairs[stock_order_data['symbol']]['profit_markup']))
                            if price_change >= all_pairs[stock_order_data['symbol']]['profit_markup']:

                                # Отправляем команду на создание ордера с рассчитанными параметрами
                                new_order = bot.createOrder(
                                    symbol=orders_info[order]['order_pair'],
                                    recvWindow=5000,
                                    side='SELL',
                                    type='MARKET',
                                    quantity="{quantity:0.{precision}f}".format(
                                        quantity=got_qty, precision=CURR_LIMITS['baseAssetPrecision']
                                    ),
                                    newOrderRespType='FULL'
                                )
                                # Если ордер создался без ошибок, записываем данные в базу данных
                                if 'orderId' in new_order:
                                    log.info("Создан ордер на продажу по рынку {new_order}".format(new_order=new_order))
                                    store_sell_order(cursor, conn, order, new_order['orderId'], got_qty, 0)

                                    order_trades = get_order_trades(
                                        order_id=new_order['orderId'],
                                        pair=pair_name, bot=bot
                                    )

                                    avg_rate = calc_sell_avg_rate(order_trades, log)
                                    if avg_rate > 0:
                                        update_sell_rate(cursor, conn, new_order['orderId'], avg_rate)
                                    else:
                                        log.debug("Не удается вычислить цену покупки, пропуск")
                                        continue

                                # Если были ошибки при создании, выводим сообщение
                                else:
                                    log.warning("Не удалось создать ордер на продажу {new_order}".format(new_order=new_order))
                            else:
                                log.debug("Цена не изменилась до нужного %")

                    # Если это ордер на продажу, и он исполнен
                    if orders_info[order]['order_type'] == 'sell' and not orders_info[order]['sell_verified']:
                        order_trades = get_order_trades(
                            order_id=orders_info[order]['order_id'],
                            pair=orders_info[order]['order_pair'], bot=bot
                        )

                        avg_rate = calc_sell_avg_rate(order_trades, log)
                        if avg_rate > 0:
                            update_sell_rate(cursor, conn, orders_info[order]['order_id'], avg_rate)
                        else:
                            log.debug("Не удается вычислить цену покупки, пропуск")
                            continue

                    if all_pairs[orders_info[order]['order_pair']]['use_stop_loss']:

                       if order_status == 'FILLED' and orders_info[order]['order_type'] == 'buy':
                         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.debug("{pair} Цена упала до стоплосс (покупали по {b:0.8f}, сейчас {s:0.8f}), пора продавать".format(
                               pair=orders_info[order]['order_pair'],
                               b=orders_info[order]['buy_price'],
                               s=curr_rate
                            ))

                            # Получаем лимиты пары с биржи
                            for elem in limits['symbols']:
                                if elem['symbol'] == orders_info[order]['order_pair']:
                                    CURR_LIMITS = elem
                                    break
                            else:
                                raise Exception("Не удалось найти настройки выбранной пары " + orders_info[order]['order_pair'])

                            new_order = bot.createOrder(
                                      symbol=orders_info[order]['order_pair'],
                                      recvWindow=15000,
                                      side='SELL',
                                      type='MARKET',
                                      quantity="{quantity:0.{precision}f}".format(
                                            quantity=orders_info[order]['buy_amount'], precision=CURR_LIMITS['baseAssetPrecision']
                                      ),
                                )

                            if 'orderId' in new_order:
                                log.info("Создан ордер на продажу по рынку {new_order}".format(new_order=new_order))
                                store_sell_order(cursor, conn, order, new_order['orderId'], got_qty, 0)

                                order_trades = get_order_trades(
                                    order_id=new_order['orderId'],
                                    pair=orders_info[order]['order_pair'], bot=bot
                                )

                                avg_rate = calc_sell_avg_rate(order_trades, log)
                                if avg_rate > 0:
                                    update_sell_rate(cursor, conn, new_order['orderId'], avg_rate)
                                else:
                                    log.debug("Не удается вычислить цену покупки, пропуск")
                                    continue
            else:
                log.debug("Неисполненных ордеров в БД нет")

            log.debug('Получаем из настроек все пары, по которым нет неисполненных ордеров')

            # Получаем из базы все ордера, по которым есть торги, и исключаем их из списка, по которому будем создавать новые ордера
            for row in get_db_running_pairs(cursor):
                del all_pairs[row]

            # Если остались пары, по которым нет текущих торгов
            if all_pairs:
                log.debug('Найдены пары, по которым нет неисполненных ордеров: {pairs}'.format(pairs=list(all_pairs.keys())))
                for pair_name, pair_obj in all_pairs.items():
                    try:
                        log.debug("Работаем с парой {pair}".format(pair=pair_name))

                        # Получаем лимиты пары с биржи
                        for elem in limits['symbols']:
                            if elem['symbol'] == pair_name:
                                CURR_LIMITS = elem
                                break
                        else:
                            raise Exception("Не удалось найти настройки выбранной пары " + pair_name)

                        log.debug("Проверяем индикаторы")
                        # Получаем свечи и берем цены закрытия, high, low
                        klines = bot.klines(
                            symbol=pair_name.upper(),
                            interval=TIMEFRAME,
                            limit=KLINES_LIMITS
                        )
                        klines = klines[:len(klines)-int(USE_OPEN_CANDLES)]

                        closes = [float(x[4]) for x in klines]
                        high = [float(x[2]) for x in klines]
                        low = [float(x[3]) for x in klines]

                        # Скользящая средняя
                        sma_5 = ta.SMA(closes, 5)
                        sma_100 = ta.SMA(closes, 100)

                        ema_5 = ta.EMA(closes, 5)
                        ema_100 = ta.EMA(closes, 100)

                        enter_points = 0

                        if ema_5[-1] > ema_100[-1] and sma_5[-1] > sma_100[-1]:
                            # Быстрая EMA выше медленной и быстрая SMA выше медленной, считаем, что можно входить
                            enter_points += 1

                        macd, macdsignal, macdhist = ta.MACD(closes, 12, 26, 9)
                        if macd[-1] > macdsignal[-1] and macdhist[-1] > 0:
                            # Линия макд выше сигнальной и на гистограмме они выше нуля
                            enter_points += 1.3

                        rsi_9 = ta.RSI(closes, 9)
                        rsi_14 = ta.RSI(closes, 14)
                        rsi_21 = ta.RSI(closes, 21)

                        if rsi_9[-1] < 70 and rsi_14[-1] < 70 and rsi_21[-1] < 70:
                            # RSI не показывает перекупленности
                            enter_points += 2

                        fast, slow = ta.STOCH(high, low, closes, 5, 3, 3)
                        if fast[-1] > slow[-1]:
                            # Быстрая линия стохастика выше медленной, вход
                            enter_points += 1.5

                        fast, slow = ta.STOCHRSI(closes, 14, 3, 3)
                        if fast[-1] > slow[-1]:
                            # Быстрая линия STOCHRSI выше медленной, вход
                            enter_points += 1.8

                        upper, middle, lower = ta.BBANDS(closes, ma_period=21)
                        if high[-1] > upper[-1]:
                            # Свеча пробила верхнюю полосу Боллинджера
                            enter_points += 3


                        log.debug("Свеча набрала {b} баллов".format(b=enter_points))
                        if enter_points <  POINTS_TO_ENTER:
                            log.debug("Минимальный проходной балл {b}. Пропуск пары".format(b=POINTS_TO_ENTER))
                            continue

                        # Получаем балансы с биржи по указанным валютам
                        balances = {
                            balance['asset']: float(balance['free']) for balance in bot.account()['balances']
                            if balance['asset'] in [pair_obj['base'], pair_obj['quote']]
                        }
                        log.debug("Баланс {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']:

                            prices = bot.tickerBookTicker(
                                symbol=pair_name
                            )

                            # Берем цены продаж (продажа будет по рынку)
                            top_price = float(prices['askPrice'])

                            # Рассчитываем кол-во, которое можно купить на заданную сумму, и приводим его к кратному значению
                            my_amount = adjust_to_step(pair_obj['spend_sum']/top_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']/ top_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 = top_price*my_amount
                            # Если итоговый размер лота меньше минимального разрешенного, то ругаемся и не создаем ордер
                            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.debug(
                                'Рассчитан ордер на покупку по рынку: кол-во {amount:0.8f}, примерный курс: {rate:0.8f}'.format(amount=my_amount, rate=top_price)
                            )
                            # Отправляем команду на бирже о создании ордера на покупку с рассчитанными параметрами
                            new_order = bot.createOrder(
                                symbol=pair_name,
                                recvWindow=5000,
                                side='BUY',
                                type='MARKET',
                                quantity="{quantity:0.{precision}f}".format(
                                    quantity=my_amount, precision=CURR_LIMITS['baseAssetPrecision']
                                ),
                                newOrderRespType='FULL'
                            )
                            # Если удалось создать ордер на покупку, записываем информацию в БД
                            if 'orderId' in new_order:
                                log.info("Создан ордер на покупку {new_order}".format(new_order=new_order))

                                add_db_new_order(cursor,conn,pair_name,new_order['orderId'], my_amount, top_price)
                                # Получить итоговую цену ордера

                                log.debug('Получаем сделки и вычисляем комиссию')

                                order_trades = get_order_trades(
                                    order_id=new_order['orderId'],
                                    pair=pair_name,
                                    bot=bot
                                )

                                avg_rate = calc_buy_avg_rate(order_trades, log)
                                if avg_rate > 0:
                                    update_buy_rate(cursor, conn, new_order['orderId'], avg_rate)

                            else:
                                log.warning("Не удалось создать ордер на покупку! {new_order}".format(new_order=str(new_order)))

                        else:
                            log.warning('Для создания ордера на покупку нужно минимум {min_qty:0.8f} {curr}, выход'.format(
                                min_qty=pair_obj['spend_sum'], curr=pair_obj['base']
                            ))
                    except:
                        log.exception("Пропускаем пару " + pair_name)
            else:
                log.debug('По всем парам есть неисполненные ордера')

        except Exception as e:
            log.exception(e)
        finally:
            conn.close()

if __name__ == "__main__":

    sync_time(bot, log, False,)

    t1 = threading.Thread(target=main_flow)
    t2 = threading.Thread(target=sync_time, args=(bot, log, True,))

    threads = [t1, t2]

    for t in threads:
        t.start()

    for t in threads:
        t.join()

 

 

Ссылка на сообщение
Поделиться на других сайтах

Учите программирование, если хотите развиваться и что-то менять. Иначе так и будете простыни посылать, а толку никакого.

 

Эта функция , как видно из файла котоырй  вы привели ,  поиском находится в строке:

from misc import adjust_to_step, sync_time, calc_buy_avg_rate, calc_sell_avg_rate, get_order_trades

Эта строка говорит что функция adjust_to_step   находится и берется из файла misc.py    - там ее и смотрим и правим

 

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

 

Вот эта функция  - находится в файле misc.py 

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)

 

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

def adjust_to_step(value, step, increase=False):
   value += 0.000000001 
   return ((int(value * 100000000) - int(value * 100000000) % int(
        float(step) * 100000000)) / 100000000)+(float(step) if increase else 0)
Ссылка на сообщение
Поделиться на других сайтах
  • 3 weeks later...

Кстати говоря, я упростил эту формулу, и сделал более точной, но пока руки не дошли в боте поменять (планирую это сделать).

Если кому нужно, вот как я сейчас делаю в голанге

coins = math.Floor(coins*(1/symbols_params[pair].StepSize)) / (1 / symbols_params[pair].StepSize)
sell_price = math.Floor(sell_price*(1/symbols_params[pair].TickSize)) / (1 / symbols_params[pair].TickSize)

Если переложить её на питон, можно написать вот так

from math import floor
def adjust_to_step(value, step, increase=False):
   return floor(value*(1/step)) / (1 / step) +(float(step) if increase else 0)
   
"""
print(adjust_to_step(0.12345678, 0.001, False))
print(adjust_to_step(0.12345678, 0.001, True))

print(adjust_to_step(0.12345678, 0.1, False))
print(adjust_to_step(0.12345678, 0.1, True))

print(adjust_to_step(0.12345678, 0.00000001, False))
print(adjust_to_step(0.12345678, 0.00000001, True))
"""

Можно еще проще написать, но это уже на грани

from math import floor
def adjust_to_step(value, step, increase=False):
   return floor(value*(1/step)) / (1 / step) +(float(step) *increase)

 

Ссылка на сообщение
Поделиться на других сайтах
  • 1 month later...

Проблема в изначально ошибочной формуле. "+(float(step) if increase else 0)" - совершенно верно, математически, какие могут быть вопросы? Вот только это программирование и вещественные числа хранятся в особом виде. 0.1+0.1+0.1 может дать 0.299999999999. Мелкая неточность, но кратность теряется. Поэтому нужно прибавлять шаг в другом месте.  

def adjust_to_step(value, step, increase=False):
	if increase:
		value += step
	return ((int(value * 100000000) - int(value * 100000000) % int(
			float(step) * 100000000)) / 100000000)

 

Изменено пользователем bleach24472447@gmail.com
Ссылка на сообщение
Поделиться на других сайтах
  • 1 month later...
01.03.2020 в 16:40, admin сказал:

Кстати говоря, я упростил эту формулу, и сделал более точной, но пока руки не дошли в боте поменять (планирую это сделать).

))) Все равно Андрей у тебя не правильно, обрати внимание именно на каких цифрах не правильно считает в первом посту

Попробуй

print(adjust_to_step(0.58,0.01000000))

и вернет 0.57

Ссылка на сообщение
Поделиться на других сайтах
04.05.2020 в 08:47, Andrei22 сказал:

))) Все равно Андрей у тебя не правильно, обрати внимание именно на каких цифрах не правильно считает в первом посту

Попробуй

print(adjust_to_step(0.58,0.01000000))

и вернет 0.57

Да, забавно, если взять калькулятор, подставить в формулу значения и посчитать самому, то всё сходится

0.58*(1/0.01)/1(1/0.01) = 0.58

А если считать в питоне, то споткнемся на 

>>> 0.58*(1/0.01)
57.99999999999999

Нужно будет использовать модуль Decimal https://docs.python.org/3/library/decimal.html

Ссылка на сообщение
Поделиться на других сайтах
  • 9 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Гость
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Вставить в виде обычного текста

  Разрешено не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.

×
×
  • Создать...