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 {}
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 []