예제 #1
0
 def get_market_status(self, symbol, price_example=None, with_fixer=True):
     try:
         if with_fixer:
             return exchanges.ExchangeMarketStatusFixer(
                 self.client.market(symbol), price_example).market_status
         return self.client.market(symbol)
     except ccxt.NotSupported:
         raise octobot_trading.errors.NotSupported
     except Exception as e:
         self.logger.error(f"Fail to get market status of {symbol}: {e}")
         return {}
예제 #2
0
 def get_market_status(self, symbol, price_example=None, with_fixer=True):
     try:
         market_status = self._fix_market_status(
             copy.deepcopy(self.connector.client.market(symbol)))
         if with_fixer:
             market_status = exchanges.ExchangeMarketStatusFixer(
                 market_status, price_example).market_status
         return market_status
     except ccxt.NotSupported:
         raise octobot_trading.errors.NotSupported
     except Exception as e:
         self.logger.error(f"Fail to get market status of {symbol}: {e}")
     return {}
def decimal_add_dusts_to_quantity_if_necessary(quantity, price, symbol_market,
                                               current_symbol_holding):
    """
    Adds remaining quantity to the order if the remaining quantity is too small
    :param quantity:
    :param price:
    :param symbol_market:
    :param current_symbol_holding:
    :return:
    """
    if price == constants.ZERO:
        return quantity

    remaining_portfolio_amount = decimal.Decimal("{1:.{0}f}".format(
        constants.CURRENCY_DEFAULT_MAX_PRICE_DIGITS,
        current_symbol_holding - quantity))
    remaining_max_total_order_price = remaining_portfolio_amount * price

    symbol_market_limits = symbol_market[Ecmsc.LIMITS.value]

    limit_amount = symbol_market_limits[Ecmsc.LIMITS_AMOUNT.value]
    limit_cost = symbol_market_limits[Ecmsc.LIMITS_COST.value]

    if not (personal_data.is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value)
            and personal_data.is_valid(limit_cost,
                                       Ecmsc.LIMITS_COST_MIN.value)):
        fixed_market_status = exchanges.ExchangeMarketStatusFixer(
            symbol_market, price).market_status
        limit_amount = fixed_market_status[Ecmsc.LIMITS.value][
            Ecmsc.LIMITS_AMOUNT.value]
        limit_cost = fixed_market_status[Ecmsc.LIMITS.value][
            Ecmsc.LIMITS_COST.value]

    min_quantity = limit_amount.get(Ecmsc.LIMITS_AMOUNT_MIN.value, math.nan)
    min_cost = limit_cost.get(Ecmsc.LIMITS_COST_MIN.value, math.nan)

    # check with 40% more than remaining total not to require huge market moves to sell this asset
    min_cost_to_consider = decimal.Decimal(str(min_cost)) * decimal.Decimal(
        str(1.4))
    min_quantity_to_consider = decimal.Decimal(
        str(min_quantity)) * decimal.Decimal(str(1.4))

    if remaining_max_total_order_price < min_cost_to_consider \
            or remaining_portfolio_amount < min_quantity_to_consider:
        return current_symbol_holding
    else:
        return quantity
def decimal_check_and_adapt_order_details_if_necessary(quantity,
                                                       price,
                                                       symbol_market,
                                                       fixed_symbol_data=False
                                                       ):
    """
    Checks if order attributes are valid and try to fix it if not
    :param quantity:
    :param price:
    :param symbol_market:
    :param fixed_symbol_data:
    :return:
    """
    if quantity.is_nan() or price.is_nan() or price == constants.ZERO:
        return []

    symbol_market_limits = symbol_market[Ecmsc.LIMITS.value]

    limit_amount = symbol_market_limits[Ecmsc.LIMITS_AMOUNT.value]
    limit_cost = symbol_market_limits[Ecmsc.LIMITS_COST.value]
    limit_price = symbol_market_limits[Ecmsc.LIMITS_PRICE.value]

    # case 1: try with data directly from exchange
    if personal_data.is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value):
        min_quantity = decimal.Decimal(
            str(limit_amount.get(Ecmsc.LIMITS_AMOUNT_MIN.value, math.nan)))
        max_quantity = None
        # not all symbol data have a max quantity
        if personal_data.is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MAX.value):
            max_quantity = decimal.Decimal(
                str(limit_amount.get(Ecmsc.LIMITS_AMOUNT_MAX.value, math.nan)))

        # adapt digits if necessary
        valid_quantity = decimal_adapt_quantity(symbol_market, quantity)
        valid_price = decimal_adapt_price(symbol_market, price)

        total_order_price = valid_quantity * valid_price

        if valid_quantity < min_quantity:
            # invalid order
            return []

        # case 1.1: use only quantity and cost
        if personal_data.is_valid(limit_cost, Ecmsc.LIMITS_COST_MIN.value):
            min_cost = decimal.Decimal(
                str(limit_cost.get(Ecmsc.LIMITS_COST_MIN.value, math.nan)))
            max_cost = None
            # not all symbol data have a max cost
            if personal_data.is_valid(limit_cost, Ecmsc.LIMITS_COST_MAX.value):
                max_cost = decimal.Decimal(
                    str(limit_cost.get(Ecmsc.LIMITS_COST_MAX.value, math.nan)))

            # check total_order_price not < min_cost
            if not personal_data.check_cost(float(total_order_price),
                                            min_cost):
                return []

            # check total_order_price not > max_cost and valid_quantity not > max_quantity
            elif (max_cost is not None and total_order_price > max_cost) or \
                    (max_quantity is not None and valid_quantity > max_quantity):
                # split quantity into smaller orders
                return decimal_split_orders(total_order_price, max_cost,
                                            valid_quantity, max_quantity,
                                            valid_price, quantity,
                                            symbol_market)

            else:
                # valid order that can be handled by the exchange
                return [(valid_quantity, valid_price)]

        # case 1.2: use only quantity and price
        elif personal_data.is_valid(limit_price, Ecmsc.LIMITS_PRICE_MIN.value):
            min_price = decimal.Decimal(
                str(limit_price.get(Ecmsc.LIMITS_PRICE_MIN.value, math.nan)))
            max_price = None
            # not all symbol data have a max price
            if personal_data.is_valid(limit_price,
                                      Ecmsc.LIMITS_PRICE_MAX.value):
                max_price = decimal.Decimal(
                    str(limit_price.get(Ecmsc.LIMITS_PRICE_MAX.value,
                                        math.nan)))

            if (max_price is not None and
                (max_price <= valid_price)) or valid_price <= min_price:
                # invalid order
                return []

            # check total_order_price not > max_cost and valid_quantity not > max_quantity
            elif max_quantity is not None and valid_quantity > max_quantity:
                # split quantity into smaller orders
                return decimal_adapt_order_quantity_because_quantity(
                    valid_quantity, max_quantity, quantity, valid_price,
                    symbol_market)
            else:
                # valid order that can be handled wy the exchange
                return [(valid_quantity, valid_price)]

    if not fixed_symbol_data:
        # case 2: try fixing data from exchanges
        fixed_data = exchanges.ExchangeMarketStatusFixer(
            symbol_market, float(price)).market_status
        return decimal_check_and_adapt_order_details_if_necessary(
            quantity, price, fixed_data, fixed_symbol_data=True)
    else:
        # impossible to check if order is valid: refuse it
        return []