Esempio n. 1
0
    def add_dusts_to_quantity_if_necessary(quantity, price, symbol_market,
                                           current_symbol_holding):
        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 (AbstractTradingModeCreator._is_valid(
                limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value)
                and AbstractTradingModeCreator._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)

        if remaining_max_total_order_price < min_cost or remaining_portfolio_amount < min_quantity:
            return current_symbol_holding
        else:
            return quantity
Esempio n. 2
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
         }
Esempio n. 3
0
    def __init__(self, trading_mode):
        super().__init__(trading_mode)
        self.MAX_SUM_RESULT = 2

        self.STOP_LOSS_ORDER_MAX_PERCENT = 0.99
        self.STOP_LOSS_ORDER_MIN_PERCENT = 0.95
        self.STOP_LOSS_ORDER_ATTENUATION = (self.STOP_LOSS_ORDER_MAX_PERCENT -
                                            self.STOP_LOSS_ORDER_MIN_PERCENT)

        self.QUANTITY_MIN_PERCENT = 0.1
        self.QUANTITY_MAX_PERCENT = 0.9
        self.QUANTITY_ATTENUATION = (
            self.QUANTITY_MAX_PERCENT -
            self.QUANTITY_MIN_PERCENT) / self.MAX_SUM_RESULT

        self.QUANTITY_MARKET_MIN_PERCENT = 0.3
        self.QUANTITY_MARKET_MAX_PERCENT = 1
        self.QUANTITY_BUY_MARKET_ATTENUATION = 0.2
        self.QUANTITY_MARKET_ATTENUATION = (self.QUANTITY_MARKET_MAX_PERCENT - self.QUANTITY_MARKET_MIN_PERCENT) \
                                           / self.MAX_SUM_RESULT

        self.BUY_LIMIT_ORDER_MAX_PERCENT = 0.995
        self.BUY_LIMIT_ORDER_MIN_PERCENT = 0.98
        self.SELL_LIMIT_ORDER_MIN_PERCENT = 1 + (
            1 - self.BUY_LIMIT_ORDER_MAX_PERCENT)
        self.SELL_LIMIT_ORDER_MAX_PERCENT = 1 + (
            1 - self.BUY_LIMIT_ORDER_MIN_PERCENT)
        self.LIMIT_ORDER_ATTENUATION = (self.BUY_LIMIT_ORDER_MAX_PERCENT - self.BUY_LIMIT_ORDER_MIN_PERCENT) \
                                       / self.MAX_SUM_RESULT

        self.QUANTITY_RISK_WEIGHT = 0.2
        self.MAX_QUANTITY_RATIO = 1
        self.MIN_QUANTITY_RATIO = 0.2
        self.DELTA_RATIO = self.MAX_QUANTITY_RATIO - self.MIN_QUANTITY_RATIO
        # If USE_HOLDINGS_FOR_RATIO is True: orders quantity is computed using current holdings ratio, otherwise it
        # is computed using the count of total traded assets
        self.USE_HOLDINGS_FOR_RATIO = True

        self.SELL_MULTIPLIER = 5
        self.FULL_SELL_MIN_RATIO = 0.05

        trading_config = self.trading_mode.trading_config if self.trading_mode else {}

        self.USE_CLOSE_TO_CURRENT_PRICE = \
            get_value_or_default(trading_config, "use_prices_close_to_current_price", False, strict=True)
        self.CLOSE_TO_CURRENT_PRICE_DEFAULT_RATIO = \
            get_value_or_default(trading_config, "close_to_current_price_difference", 0.02, strict=True)
        self.USE_MAXIMUM_SIZE_ORDERS =  \
            get_value_or_default(trading_config, "use_maximum_size_orders", False, strict=True)
        self.USE_STOP_ORDERS =  \
            get_value_or_default(trading_config, "use_stop_orders", True, strict=True)
    def __init__(self, trading_mode, symbol_evaluator, exchange, trader, creators):
        AbstractTradingModeDeciderWithBot.__init__(self, trading_mode, symbol_evaluator, exchange, trader, creators)
        Initializable.__init__(self)
        exchange_fees = max(exchange.get_fees(self.symbol).values())
        self.LONG_THRESHOLD = -2 * exchange_fees
        self.SHORT_THRESHOLD = 2 * exchange_fees
        self.filled_creators = []
        self.pending_creators = []
        self.blocked_creators = []

        self.currency, self.market = split_symbol(self.symbol)
        market_status = exchange.get_market_status(self.symbol)
        self.currency_max_digits = get_value_or_default(market_status[Ecmsc.PRECISION.value],
                                                        Ecmsc.PRECISION_PRICE.value,
                                                        CURRENCY_DEFAULT_MAX_PRICE_DIGITS)
        limit_cost = market_status[Ecmsc.LIMITS.value][Ecmsc.LIMITS_COST.value]
        self.currency_min_cost = get_value_or_default(limit_cost, Ecmsc.LIMITS_COST_MIN.value)
Esempio n. 5
0
 def get_market_pair(config, currency) -> (str, bool):
     if CONFIG_TRADING in config:
         reference_market = get_value_or_default(
             config[CONFIG_TRADING], CONFIG_TRADER_REFERENCE_MARKET,
             DEFAULT_REFERENCE_MARKET)
         for symbol in ConfigManager.get_symbols(config):
             symbol_currency, symbol_market = split_symbol(symbol)
             if currency == symbol_currency and reference_market == symbol_market:
                 return symbol, False
             elif reference_market == symbol_currency and currency == symbol_market:
                 return symbol, True
     return "", False
    def get_min_max_amounts(symbol_market, default_value=None):
        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 AbstractTradingModeCreator._is_valid(limit_amount, Ecmsc.LIMITS_AMOUNT_MIN.value) \
                        or AbstractTradingModeCreator._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 AbstractTradingModeCreator._is_valid(limit_cost, Ecmsc.LIMITS_COST_MIN.value) \
                        or AbstractTradingModeCreator._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 AbstractTradingModeCreator._is_valid(limit_price, Ecmsc.LIMITS_PRICE_MIN.value) \
                        or AbstractTradingModeCreator._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
Esempio n. 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 AbstractTradingModeCreator._trunc_with_n_decimal_digits(
         quantity, maximal_volume_digits)
Esempio n. 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 AbstractTradingModeCreator._trunc_with_n_decimal_digits(
         price, maximal_price_digits)
Esempio n. 9
0
    def check_and_adapt_order_details_if_necessary(quantity,
                                                   price,
                                                   symbol_market,
                                                   fixed_symbol_data=False):
        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 AbstractTradingModeCreator._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 AbstractTradingModeCreator._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 = AbstractTradingModeCreator._adapt_quantity(
                symbol_market, quantity)
            valid_price = AbstractTradingModeCreator.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 AbstractTradingModeCreator._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 AbstractTradingModeCreator._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 AbstractTradingModeCreator._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 AbstractTradingModeCreator._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 AbstractTradingModeCreator._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 AbstractTradingModeCreator._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 AbstractTradingModeCreator \
                        ._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).get_market_status()
            return AbstractTradingModeCreator.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 []
Esempio n. 10
0
def test_get_value_or_default():
    test_dict = {"a": 1, "b": 2, "c": 3}
    assert get_value_or_default(test_dict, "b", default="") == 2
    assert get_value_or_default(test_dict, "d", nan) is nan
    assert get_value_or_default(test_dict, "d", default="") == ""
    assert get_value_or_default(test_dict, "z") is None
Esempio n. 11
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)