def validate_max_spread(value: str) -> Optional[str]: validate_decimal(value, 0, 100, inclusive=False) if avellaneda_market_making_config_map["min_spread"].value is not None: min_spread = Decimal(avellaneda_market_making_config_map["min_spread"].value) max_spread = Decimal(value) if min_spread >= max_spread: return f"Max spread cannot be lesser or equal to min spread {max_spread}%<={min_spread}%"
def balance(self, option: str = None, args: List[str] = None): if threading.current_thread() != threading.main_thread(): self.ev_loop.call_soon_threadsafe(self.balance, option, args) return self.app.clear_input() if option is None: safe_ensure_future(self.show_balances()) elif option in OPTIONS: config_map = global_config_map file_path = GLOBAL_CONFIG_PATH if option == "limit": config_var = config_map["balance_asset_limit"] if args is None or len(args) == 0: safe_ensure_future(self.show_asset_limits()) return if len(args) != 3 or validate_exchange( args[0]) is not None or validate_decimal( args[2]) is not None: self._notify("Error: Invalid command arguments") self.notify_balance_limit_set() return exchange = args[0] asset = args[1].upper() amount = float(args[2]) if exchange not in config_var.value or config_var.value[ exchange] is None: config_var.value[exchange] = {} if amount < 0 and asset in config_var.value[exchange].keys(): config_var.value[exchange].pop(asset) self._notify( f"Limit for {asset} on {exchange} exchange removed.") elif amount >= 0: config_var.value[exchange][asset] = amount self._notify( f"Limit for {asset} on {exchange} exchange set to {amount}" ) save_to_yml(file_path, config_map) elif option == "paper": config_var = config_map["paper_trade_account_balance"] if args is None or len(args) == 0: safe_ensure_future(self.show_paper_account_balance()) return if len(args) != 2 or validate_decimal(args[1]) is not None: self._notify("Error: Invalid command arguments") self.notify_balance_paper_set() return asset = args[0].upper() amount = float(args[1]) paper_balances = dict( config_var.value) if config_var.value else {} paper_balances[asset] = amount config_var.value = paper_balances self._notify( f"Paper balance for {asset} token set to {amount}") save_to_yml(file_path, config_map)
def balance( self, # type: HummingbotApplication option: str = None, args: List[str] = None): if threading.current_thread() != threading.main_thread(): self.ev_loop.call_soon_threadsafe(self.balance, option, args) return self.app.clear_input() if option is None: safe_ensure_future(self.show_balances()) elif option in OPTIONS: if option == "limit": balance_asset_limit = self.client_config_map.balance_asset_limit if args is None or len(args) == 0: safe_ensure_future(self.show_asset_limits()) return if len(args) != 3 or validate_exchange( args[0]) is not None or validate_decimal( args[2]) is not None: self.notify("Error: Invalid command arguments") self.notify_balance_limit_set() return exchange = args[0] asset = args[1].upper() amount = float(args[2]) if balance_asset_limit.get(exchange) is None: balance_asset_limit[exchange] = {} if amount < 0 and asset in balance_asset_limit[exchange].keys( ): balance_asset_limit[exchange].pop(asset) self.notify( f"Limit for {asset} on {exchange} exchange removed.") elif amount >= 0: balance_asset_limit[exchange][asset] = amount self.notify( f"Limit for {asset} on {exchange} exchange set to {amount}" ) self.save_client_config() elif option == "paper": paper_balances = self.client_config_map.paper_trade.paper_trade_account_balance if args is None or len(args) == 0: safe_ensure_future(self.show_paper_account_balance()) return if len(args) != 2 or validate_decimal(args[1]) is not None: self.notify("Error: Invalid command arguments") self.notify_balance_paper_set() return asset = args[0].upper() amount = float(args[1]) paper_balances[asset] = amount self.notify(f"Paper balance for {asset} token set to {amount}") self.save_client_config()
def new_fee_config_var(key): return ConfigVar( key=key, prompt=None, required_if=lambda x: x is not None, type_str="decimal", validator=lambda v: validate_decimal(v, Decimal(-0.1), Decimal(0.1)))
def validate_pct_exclusive(cls, v: str): ret = validate_decimal(v, min_value=Decimal("0"), max_value=Decimal("100"), inclusive=False) if ret is not None: raise ValueError(ret) return v
def validate_decimal_list(value: str) -> Optional[str]: decimal_list = list(value.split(",")) for number in decimal_list: try: validate_result = validate_decimal(Decimal(number), 0, 100, inclusive=False) except decimal.InvalidOperation: return "Please enter valid decimal numbers" if validate_result is not None: return validate_result
def validate_pct_inclusive(cls, v: str): """Used for client-friendly error output.""" ret = validate_decimal(v, min_value=Decimal("0"), max_value=Decimal("100"), inclusive=True) if ret is not None: raise ValueError(ret) return v
def validate_order_refresh_tolerance_pct(cls, v: str): """Used for client-friendly error output.""" ret = validate_decimal(v, min_value=Decimal("-10"), max_value=Decimal("10"), inclusive=True) if ret is not None: raise ValueError(ret) return v
def validate_amount_max(value: str): valid_dec = validate_decimal(value, 0, inclusive=False) decimal_value = Decimal(value) if valid_dec is None else s_decimal_zero amount_min = random_loop_trade_config_map.get("order_amount_min").value if not valid_empty(amount_min): if valid_empty(value): return "order_amount_max can't be 0 if order_amount_min is set" elif valid_dec is None and not amount_min < decimal_value: return "order_amount_min must be lower than order_amount_max" else: return valid_dec
def validate_price(value: str): valid_dec = validate_decimal(value, 0, inclusive=False) # decimal_value = Decimal(value) if valid_dec is None else s_decimal_zero enabled_rand = random_loop_trade_config_map.get( "order_pricetype_random").value enabled_spread = random_loop_trade_config_map.get( "order_pricetype_spread").value if not enabled_rand and not enabled_spread: if valid_empty(value): return "order_price can't be 0 if not using random/spread" else: return valid_dec
def validate_order_step_size(value: str = None): """ Invalidates non-decimal input and checks if order_step_size is less than the target_asset_amount value :param value: User input for order_step_size parameter :return: Error message printed in output pane """ result = validate_decimal(value, min_value=Decimal("0"), inclusive=False) if result is not None: return result target_asset_amount = twap_config_map.get("target_asset_amount").value if Decimal(value) > target_asset_amount: return "Order step size cannot be greater than the total trade amount."
def validate_decimal(v: str, field: Field): """Used for client-friendly error output.""" field_info = field.field_info inclusive = field_info.ge is not None or field_info.le is not None min_value = field_info.gt if field_info.gt is not None else field_info.ge min_value = Decimal(min_value) if min_value is not None else min_value max_value = field_info.lt if field_info.lt is not None else field_info.le max_value = Decimal(max_value) if max_value is not None else max_value ret = validate_decimal(v, min_value, max_value, inclusive) if ret is not None: raise ValueError(ret) return v
async def inventory_price_prompt( self, # type: HummingbotApplication config_map, input_value=None, ): key = "inventory_price" if input_value: config_map[key].value = Decimal(input_value) else: exchange = config_map["exchange"].value market = config_map["market"].value base_asset, quote_asset = market.split("-") if exchange.endswith("paper_trade"): balances = global_config_map[ "paper_trade_account_balance"].value else: balances = await UserBalances.instance().balances( exchange, base_asset, quote_asset) if balances.get(base_asset) is None: return cvar = ConfigVar( key="temp_config", prompt= f"On {exchange}, you have {balances[base_asset]:.4f} {base_asset}. " f"What was the price for this amount in {quote_asset}? >>> ", required_if=lambda: True, type_str="decimal", validator=lambda v: validate_decimal( v, min_value=Decimal("0"), inclusive=True), ) await self.prompt_a_config(cvar) config_map[key].value = cvar.value try: quote_volume = balances[base_asset] * cvar.value except TypeError: # TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'NoneType' - bad input / no input self._notify("Inventory price not updated due to bad input") return with self.trade_fill_db.get_new_session() as session: with session.begin(): InventoryCost.add_volume( session, base_asset=base_asset, quote_asset=quote_asset, base_volume=balances[base_asset], quote_volume=quote_volume, overwrite=True, )
def validate_price_max(value: str): valid_dec = validate_decimal(value, 0, inclusive=False) decimal_value = Decimal(value) if valid_dec is None else s_decimal_zero price_min = random_loop_trade_config_map.get("order_price_min").value enabled_rand = random_loop_trade_config_map.get( "order_pricetype_random").value if enabled_rand: if valid_empty(value): return "order_price_max can't be 0 if random is enabled" elif valid_dec is None and not valid_empty( price_min) and not price_min < decimal_value: return "order_price_min must be lower than order_price_max" else: return valid_dec
def validate_spread_min(value: str): valid_dec = validate_decimal(value, 0, 100, inclusive=False) decimal_value = Decimal(value) if valid_dec is None else s_decimal_zero spread_max = random_loop_trade_config_map.get("order_spread_max").value enabled_rand = random_loop_trade_config_map.get( "order_pricetype_random").value enabled_spread = random_loop_trade_config_map.get( "order_pricetype_spread").value if enabled_rand and enabled_spread: if valid_empty(value): return "order_spread_min can't be 0 if random spread is enabled" elif valid_dec is None and not valid_empty( spread_max) and not spread_max > decimal_value: return "order_spread_min must be lower than order_spread_max" else: return valid_dec
ConfigVar(key="connector_2", prompt="Enter your second spot connector (Exchange/AMM) >>> ", prompt_on_new=True, validator=validate_connector, on_validated=exchange_on_validated), "market_2": ConfigVar(key="market_2", prompt=market_2_prompt, prompt_on_new=True, validator=market_2_validator, on_validated=market_2_on_validated), "order_amount": ConfigVar(key="order_amount", prompt=order_amount_prompt, type_str="decimal", validator=lambda v: validate_decimal(v, Decimal("0")), prompt_on_new=True), "min_profitability": ConfigVar( key="min_profitability", prompt= "What is the minimum profitability for you to make a trade? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, default=Decimal("1"), validator=lambda v: validate_decimal(v), type_str="decimal"), "market_1_slippage_buffer": ConfigVar( key="market_1_slippage_buffer", prompt= "How much buffer do you want to add to the price to account for slippage for orders on the first market "
prompt_on_new=True), "position_mode": ConfigVar( key="position_mode", prompt="Which position mode do you want to use? (One-way/Hedge) >>> ", validator=validate_derivative_position_mode, type_str="str", default="One-way", prompt_on_new=True), "bid_spread": ConfigVar( key="bid_spread", prompt="How far away from the mid price do you want to place the " "first bid order? (Enter 1 to indicate 1%) >>> ", type_str="decimal", validator=lambda v: validate_decimal(v, 0, 100, inclusive=False), prompt_on_new=True), "ask_spread": ConfigVar( key="ask_spread", prompt="How far away from the mid price do you want to place the " "first ask order? (Enter 1 to indicate 1%) >>> ", type_str="decimal", validator=lambda v: validate_decimal(v, 0, 100, inclusive=False), prompt_on_new=True), "minimum_spread": ConfigVar( key="minimum_spread", prompt= "At what minimum spread should the bot automatically cancel orders? (Enter 1 for 1%) >>> ", required_if=lambda: False,
type_str="bool", default=False), "kill_switch_enabled": ConfigVar(key="kill_switch_enabled", prompt="Would you like to enable the kill switch? (Yes/No) >>> ", required_if=paper_trade_disabled, type_str="bool", default=False, validator=validate_bool), "kill_switch_rate": ConfigVar(key="kill_switch_rate", prompt="At what profit/loss rate would you like the bot to stop? " "(e.g. -5 equals 5 percent loss) >>> ", type_str="decimal", default=-100, validator=lambda v: validate_decimal(v, Decimal(-100), Decimal(100)), required_if=lambda: global_config_map["kill_switch_enabled"].value), "telegram_enabled": ConfigVar(key="telegram_enabled", prompt="Would you like to enable telegram? >>> ", type_str="bool", default=False, required_if=lambda: False), "telegram_token": ConfigVar(key="telegram_token", prompt="What is your telegram token? >>> ", required_if=lambda: False), "telegram_chat_id": ConfigVar(key="telegram_chat_id", prompt="What is your telegram chat id? >>> ", required_if=lambda: False),
prompt="Enter your maker exchange name >>> ", validator=validate_exchange, on_validated=exchange_on_validated, prompt_on_new=True), "market": ConfigVar(key="market", prompt=maker_trading_pair_prompt, validator=validate_exchange_trading_pair, prompt_on_new=True), "bid_spread": ConfigVar( key="bid_spread", prompt="How far away from the mid price do you want to place the " "first bid order? (Enter 1 to indicate 1%) >>> ", type_str="decimal", validator=lambda v: validate_decimal(v, 0, 100, inclusive=False), prompt_on_new=True), "ask_spread": ConfigVar( key="ask_spread", prompt="How far away from the mid price do you want to place the " "first ask order? (Enter 1 to indicate 1%) >>> ", type_str="decimal", validator=lambda v: validate_decimal(v, 0, 100, inclusive=False), prompt_on_new=True), "order_refresh_time": ConfigVar( key="order_refresh_time", prompt="How often do you want to cancel and replace bids and asks " "(in seconds)? >>> ", required_if=lambda: not (using_exchange("radar_relay")() or
prompt= "Do you want to automate Avellaneda-Stoikov parameters based on min/max spread? >>> ", type_str="bool", validator=validate_bool, on_validated=on_validated_parameters_based_on_spread, default=True, prompt_on_new=True), "min_spread": ConfigVar( key="min_spread", prompt="Enter the minimum spread allowed from mid-price in percentage " "(Enter 1 to indicate 1%) >>> ", type_str="decimal", required_if=lambda: avellaneda_market_making_config_map.get( "parameters_based_on_spread").value, validator=lambda v: validate_decimal(v, 0, 100, inclusive=False), prompt_on_new=True, on_validated=onvalidated_min_spread), "max_spread": ConfigVar( key="max_spread", prompt="Enter the maximum spread allowed from mid-price in percentage " "(Enter 1 to indicate 1%) >>> ", type_str="decimal", required_if=lambda: avellaneda_market_making_config_map.get( "parameters_based_on_spread").value, validator=lambda v: validate_max_spread(v), prompt_on_new=True), "vol_to_spread_multiplier": ConfigVar( key="vol_to_spread_multiplier",
key="secondary_exchange", prompt="Enter your secondary spot connector >>> ", prompt_on_new=True, validator=validate_exchange, on_validated=exchange_on_validated), "secondary_market": ConfigVar( key="secondary_market", prompt=market_trading_pair_prompt, prompt_on_new=True, validator=lambda x: validate_market_trading_pair(celo_arb_config_map["secondary_exchange"].value, x)), "order_amount": ConfigVar( key="order_amount", prompt=order_amount_prompt, type_str="decimal", prompt_on_new=True), "min_profitability": ConfigVar( key="min_profitability", prompt="What is the minimum profitability for you to make a trade? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, default=Decimal("0.3"), validator=lambda v: validate_decimal(v), type_str="decimal"), "celo_slippage_buffer": ConfigVar( key="celo_slippage_buffer", prompt="How much buffer do you want to add to the Celo price to account for slippage (Enter 1 for 1%)? >>> ", prompt_on_new=True, default=Decimal("0.01"), validator=lambda v: validate_decimal(v), type_str="decimal"), }
type_str="decimal", prompt_on_new=True), "perpetual_leverage": ConfigVar( key="perpetual_leverage", prompt="How much leverage would you like to use on the perpetual exchange? (Enter 1 to indicate 1X) >>> ", type_str="int", default=1, validator= lambda v: validate_int(v), prompt_on_new=True), "min_opening_arbitrage_pct": ConfigVar( key="min_opening_arbitrage_pct", prompt="What is the minimum arbitrage percentage between the spot and perpetual market price before opening " "an arbitrage position? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, default=Decimal("1"), validator=lambda v: validate_decimal(v, Decimal(-100), 100, inclusive=False), type_str="decimal"), "min_closing_arbitrage_pct": ConfigVar( key="min_closing_arbitrage_pct", prompt="What is the minimum arbitrage percentage between the spot and perpetual market price before closing " "an existing arbitrage position? (Enter 1 to indicate 1%) (This can be negative value to close out the " "position with lesser profit at higher chance of closing) >>> ", prompt_on_new=True, default=Decimal("-0.1"), validator=lambda v: validate_decimal(v, Decimal(-100), 100, inclusive=False), type_str="decimal"), "spot_market_slippage_buffer": ConfigVar( key="spot_market_slippage_buffer", prompt="How much buffer do you want to add to the price to account for slippage for orders on the spot market " "(Enter 1 for 1%)? >>> ", prompt_on_new=True,
"secondary_market_trading_pair": ConfigVar( key="secondary_market_trading_pair", prompt=secondary_trading_pair_prompt, prompt_on_new=True, validator=validate_secondary_market_trading_pair, on_validated=update_oracle_settings, ), "min_profitability": ConfigVar( key="min_profitability", prompt= "What is the minimum profitability for you to make a trade? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, default=Decimal("0.3"), validator=lambda v: validate_decimal( v, Decimal(-100), Decimal("100"), inclusive=True), type_str="decimal", ), "use_oracle_conversion_rate": ConfigVar( key="use_oracle_conversion_rate", type_str="bool", prompt= "Do you want to use rate oracle on unmatched trading pairs? (Yes/No) >>> ", prompt_on_new=True, validator=lambda v: validate_bool(v), on_validated=update_oracle_settings, ), "secondary_to_primary_base_conversion_rate": ConfigVar( key="secondary_to_primary_base_conversion_rate",
prompt= "What is your preferred quantity (denominated in the base asset, default is 1)? " ">>> ", default=1.0, type_str="float", prompt_on_new=True), "total_order_per_session": ConfigVar( key="total_order_per_session", prompt= "What is the desired quantity per trading session (denominated in the base asset, default is 1)? " ">>> ", default=1.0, validator=lambda v: validate_decimal(v, 0, vwap_trade_config_map.get( "total_order_amount").value, inclusive=True), type_str="float", prompt_on_new=True), "is_buy": ConfigVar( key="is_buy", prompt= "Enter True for Buy order and False for Sell order (default is Buy Order) >>> ", type_str="bool", default=True, validator=validate_bool, prompt_on_new=True), "is_vwap": ConfigVar(
prompt_on_new=True, validator=validate_maker_market_trading_pair, on_validated=update_oracle_settings), "taker_market_trading_pair": ConfigVar(key="taker_market_trading_pair", prompt=taker_trading_pair_prompt, prompt_on_new=True, validator=validate_taker_market_trading_pair, on_validated=update_oracle_settings), "min_profitability": ConfigVar( key="min_profitability", prompt= "What is the minimum profitability for you to make a trade? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, validator=lambda v: validate_decimal( v, Decimal(-100), Decimal("100"), inclusive=True), type_str="decimal", ), "order_amount": ConfigVar( key="order_amount", prompt=order_amount_prompt, prompt_on_new=True, type_str="decimal", validator=lambda v: validate_decimal( v, min_value=Decimal("0"), inclusive=False), ), "adjust_order_enabled": ConfigVar( key="adjust_order_enabled", prompt="Do you want to enable adjust order? (Yes/No) >>> ",
prompt= "On which fee tier do you want to provide liquidity on? (LOWEST/LOW/MEDIUM/HIGH) ", validator=lambda s: None if s in { "LOWEST", "LOW", "MEDIUM", "HIGH", } else "Invalid fee tier.", prompt_on_new=True), "price_spread": ConfigVar( key="price_spread", prompt= "How wide around current pool price and/or last created positions do you want new positions to span? (Enter 1 to indicate 1%) >>> ", type_str="decimal", validator=lambda v: validate_decimal(v, Decimal("0"), inclusive=False), default=Decimal("1"), prompt_on_new=True), "amount": ConfigVar( key="amount", prompt= "Enter the maximum value(in terms of base asset) to use for providing liquidity. >>>", prompt_on_new=True, validator=lambda v: validate_decimal(v, Decimal("0"), inclusive=False), type_str="decimal"), "min_profitability": ConfigVar( key="min_profitability", prompt= "What is the minimum unclaimed fees an out of range position must have before it is closed? (in terms of base asset) >>>",
def validate_decimal_zero_or_above(cls, v: str): ret = validate_decimal(v, min_value=Decimal("0"), inclusive=True) if ret is not None: raise ValueError(ret) return v
def validate_decimal_above_zero(cls, v: str): """Used for client-friendly error output.""" ret = validate_decimal(v, min_value=Decimal("0"), inclusive=False) if ret is not None: raise ValueError(ret) return v
ConfigVar( key="derivative_leverage", prompt= "How much leverage would you like to use on the derivative exchange? (Enter 1 to indicate 1X) ", type_str="int", default=1, validator=lambda v: validate_int(v), prompt_on_new=True), "min_divergence": ConfigVar( key="min_divergence", prompt= "What is the minimum spread between the spot and derivative market price before starting an arbitrage? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, default=Decimal("1"), validator=lambda v: validate_decimal(v, 0, 100, inclusive=False), type_str="decimal"), "min_convergence": ConfigVar( key="min_convergence", prompt= "What is the minimum spread between the spot and derivative market price before closing an existing arbitrage? (Enter 1 to indicate 1%) >>> ", prompt_on_new=True, default=Decimal("0.1"), validator=lambda v: validate_decimal( v, 0, spot_perpetual_arbitrage_config_map["min_divergence"].value), type_str="decimal"), "maximize_funding_rate": ConfigVar( key="maximize_funding_rate", prompt=
key="order_type", prompt="Enter type of order (limit/market) default is limit >>> ", type_str="str", validator=lambda v: None if v in {"limit", "market", ""} else "Invalid order type.", default="limit", prompt_on_new=True), "order_amount": ConfigVar( key="order_amount", prompt= "What is your preferred quantity per order (denominated in the base asset, default is 1)? " ">>> ", default=Decimal("1.0"), type_str="decimal", validator=lambda v: validate_decimal(v, 0, inclusive=False), prompt_on_new=True), "order_amount_min": ConfigVar( key="order_amount_min", prompt= "What is your preferred min quantity per order (denominated in the base asset, default is 0)? " ">>> ", required_if=lambda: (random_loop_trade_config_map.get("order_type").value == "limit"), validator=validate_amount_min, default=s_decimal_zero, type_str="decimal"), "order_amount_max": ConfigVar( key="order_amount_max",