コード例 #1
0
 def get_fees(self, symbol):
     try:
         market_status = self.client.market(symbol)
         return {
             ExchangeConstantsMarketPropertyColumns.TAKER.value:
             get_value_or_default(
                 market_status,
                 ExchangeConstantsMarketPropertyColumns.TAKER.value,
                 CONFIG_DEFAULT_FEES),
             ExchangeConstantsMarketPropertyColumns.MAKER.value:
             get_value_or_default(
                 market_status,
                 ExchangeConstantsMarketPropertyColumns.MAKER.value,
                 CONFIG_DEFAULT_FEES),
             ExchangeConstantsMarketPropertyColumns.FEE.value:
             get_value_or_default(
                 market_status,
                 ExchangeConstantsMarketPropertyColumns.FEE.value,
                 CONFIG_DEFAULT_FEES)
         }
     except Exception as e:
         self.logger.error(f"Fees data for {symbol} was not found ({e})")
         return {
             ExchangeConstantsMarketPropertyColumns.TAKER.value:
             CONFIG_DEFAULT_FEES,
             ExchangeConstantsMarketPropertyColumns.MAKER.value:
             CONFIG_DEFAULT_FEES,
             ExchangeConstantsMarketPropertyColumns.FEE.value:
             CONFIG_DEFAULT_FEES
         }
コード例 #2
0
def 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:
    """
    remaining_portfolio_amount = float("{1:.{0}f}".format(
        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 (is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value)
            and is_valid(limit_cost, Ecmsc.LIMITS_COST_MIN.value)):
        fixed_market_status = ExchangeMarketStatusFixer(
            symbol_market, price).get_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 = get_value_or_default(limit_amount,
                                        Ecmsc.LIMITS_AMOUNT_MIN.value,
                                        math.nan)
    min_cost = get_value_or_default(limit_cost, 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 = min_cost * 1.4
    min_quantity_to_consider = min_quantity * 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
コード例 #3
0
def get_min_max_amounts(symbol_market, default_value=None):
    """
    Returns the min and max quantity, cost and price according to the specified market
    :param symbol_market:
    :param default_value:
    :return:
    """
    min_quantity = max_quantity = min_cost = max_cost = min_price = max_price = default_value
    if Ecmsc.LIMITS.value in symbol_market:
        symbol_market_limits = symbol_market[Ecmsc.LIMITS.value]

        if Ecmsc.LIMITS_AMOUNT.value in symbol_market_limits:
            limit_amount = symbol_market_limits[Ecmsc.LIMITS_AMOUNT.value]
            if is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value) \
                    or is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MAX.value):
                min_quantity = get_value_or_default(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value, default_value)
                max_quantity = get_value_or_default(limit_amount, Ecmsc.LIMITS_AMOUNT_MAX.value, default_value)

        # case 2: use cost and price
        if Ecmsc.LIMITS_COST.value in symbol_market_limits:
            limit_cost = symbol_market_limits[Ecmsc.LIMITS_COST.value]
            if is_valid(limit_cost, Ecmsc.LIMITS_COST_MIN.value) \
                    or is_valid(limit_cost, Ecmsc.LIMITS_COST_MAX.value):
                min_cost = get_value_or_default(limit_cost, Ecmsc.LIMITS_COST_MIN.value, default_value)
                max_cost = get_value_or_default(limit_cost, Ecmsc.LIMITS_COST_MAX.value, default_value)

        # case 2: use quantity and price
        if Ecmsc.LIMITS_PRICE.value in symbol_market_limits:
            limit_price = symbol_market_limits[Ecmsc.LIMITS_PRICE.value]
            if is_valid(limit_price, Ecmsc.LIMITS_PRICE_MIN.value) \
                    or is_valid(limit_price, Ecmsc.LIMITS_PRICE_MAX.value):
                min_price = get_value_or_default(limit_price, Ecmsc.LIMITS_PRICE_MIN.value, default_value)
                max_price = get_value_or_default(limit_price, Ecmsc.LIMITS_PRICE_MAX.value, default_value)

    return min_quantity, max_quantity, min_cost, max_cost, min_price, max_price
コード例 #4
0
    def update_from_raw(self, raw_order):
        if self.side is None or self.order_type is None:
            try:
                self.__update_type_from_raw(raw_order)
                if self.taker_or_maker is None:
                    self.__update_taker_maker_from_raw()
            except KeyError:
                get_logger(self.__class__.__name__).warning(
                    "Failed to parse order side and type")

        return self.update(
            symbol=str(
                get_value_or_default(
                    raw_order, ExchangeConstantsOrderColumns.SYMBOL.value,
                    None)),
            current_price=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.PRICE.value, 0.0),
            quantity=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.AMOUNT.value, 0.0),
            price=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.PRICE.value, 0.0),
            status=parse_order_status(raw_order),
            order_id=str(
                get_value_or_default(raw_order,
                                     ExchangeConstantsOrderColumns.ID.value,
                                     None)),
            quantity_filled=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.FILLED.value, 0.0),
            filled_price=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.PRICE.value, 0.0),
            total_cost=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.COST.value, 0.0),
            fee=get_value_or_default(raw_order,
                                     ExchangeConstantsOrderColumns.FEE.value,
                                     None),
            timestamp=get_value_or_default(
                raw_order, ExchangeConstantsOrderColumns.TIMESTAMP.value,
                None))
コード例 #5
0
def test_get_value_or_default():
    assert get_value_or_default({"test": 1}, "test") == 1
    assert get_value_or_default({"test": 1}, "test2", "a") == "a"
コード例 #6
0
def get_reference_market(config) -> str:
    # The reference market is the currency unit of the calculated quantity value
    return get_value_or_default(config[CONFIG_TRADING],
                                CONFIG_TRADER_REFERENCE_MARKET,
                                DEFAULT_REFERENCE_MARKET)
コード例 #7
0
def adapt_quantity(symbol_market, quantity):
    maximal_volume_digits = get_value_or_default(
        symbol_market[Ecmsc.PRECISION.value], Ecmsc.PRECISION_AMOUNT.value, 0)
    return trunc_with_n_decimal_digits(quantity, maximal_volume_digits)
コード例 #8
0
def adapt_price(symbol_market, price):
    maximal_price_digits = get_value_or_default(
        symbol_market[Ecmsc.PRECISION.value], Ecmsc.PRECISION_PRICE.value,
        CURRENCY_DEFAULT_MAX_PRICE_DIGITS)
    return trunc_with_n_decimal_digits(price, maximal_price_digits)
コード例 #9
0
def 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:
    """
    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 is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value):
        min_quantity = get_value_or_default(limit_amount,
                                            Ecmsc.LIMITS_AMOUNT_MIN.value,
                                            math.nan)
        max_quantity = None
        # not all symbol data have a max quantity
        if is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MAX.value):
            max_quantity = get_value_or_default(limit_amount,
                                                Ecmsc.LIMITS_AMOUNT_MAX.value,
                                                math.nan)

        # adapt digits if necessary
        valid_quantity = adapt_quantity(symbol_market, quantity)
        valid_price = 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 is_valid(limit_cost, Ecmsc.LIMITS_COST_MIN.value):
            min_cost = get_value_or_default(limit_cost,
                                            Ecmsc.LIMITS_COST_MIN.value,
                                            math.nan)
            max_cost = None
            # not all symbol data have a max cost
            if is_valid(limit_cost, Ecmsc.LIMITS_COST_MAX.value):
                max_cost = get_value_or_default(limit_cost,
                                                Ecmsc.LIMITS_COST_MAX.value,
                                                math.nan)

            # check total_order_price not < min_cost
            if not check_cost(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 split_orders(total_order_price, max_cost,
                                    valid_quantity, max_quantity, price,
                                    quantity, symbol_market)

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

        # case 1.2: use only quantity and price
        elif is_valid(limit_price, Ecmsc.LIMITS_PRICE_MIN.value):
            min_price = get_value_or_default(limit_price,
                                             Ecmsc.LIMITS_PRICE_MIN.value,
                                             math.nan)
            max_price = None
            # not all symbol data have a max price
            if is_valid(limit_price, Ecmsc.LIMITS_PRICE_MAX.value):
                max_price = get_value_or_default(limit_price,
                                                 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 adapt_order_quantity_because_quantity(
                    valid_quantity, max_quantity, quantity, 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 = ExchangeMarketStatusFixer(symbol_market,
                                               price).market_status
        return 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 []