Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions controllers/generic/xemm_copycat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import time
from decimal import Decimal
from typing import Dict, List, Set

import pandas as pd
from pydantic import Field

from hummingbot.client.config.config_data_types import ClientFieldData
from hummingbot.client.ui.interface_utils import format_df_for_printout
from hummingbot.core.data_type.common import PriceType, TradeType
from hummingbot.data_feed.candles_feed.data_types import CandlesConfig
from hummingbot.strategy_v2.controllers.controller_base import ControllerBase, ControllerConfigBase
from hummingbot.strategy_v2.executors.data_types import ConnectorPair
from hummingbot.strategy_v2.executors.xemm_executor.data_types import XEMMExecutorConfig
from hummingbot.strategy_v2.models.executor_actions import CreateExecutorAction, ExecutorAction


class XEMMCopycatConfig(ControllerConfigBase):
controller_name: str = "xemm_copycat"
candles_config: List[CandlesConfig] = []
maker_connector: str = Field(
default="cube",
client_data=ClientFieldData(
prompt=lambda e: "Enter the maker connector: ",
prompt_on_new=True
))
maker_trading_pair: str = Field(
default="$DOG-USDC",
client_data=ClientFieldData(
prompt=lambda e: "Enter the maker trading pair: ",
prompt_on_new=True
))
taker_connector: str = Field(
default="gate_io",
client_data=ClientFieldData(
prompt=lambda e: "Enter the taker connector: ",
prompt_on_new=True
))
taker_trading_pair: str = Field(
default="DOG-USDT",
client_data=ClientFieldData(
prompt=lambda e: "Enter the taker trading pair: ",
prompt_on_new=True
))
target_bid_liquidity: Decimal = Field(
default=100,
client_data=ClientFieldData(
prompt=lambda e: "Enter the target bid liquidity: ",
prompt_on_new=True
))
target_ask_liquidity: Decimal = Field(
default=100,
client_data=ClientFieldData(
prompt=lambda e: "Enter the target ask liquidity: ",
prompt_on_new=True
))
min_profitability: Decimal = Field(
default=0.2,
client_data=ClientFieldData(
prompt=lambda e: "Enter the minimum profitability (in %): ",
prompt_on_new=True
))
max_spread: Decimal = Field(
default=1,
client_data=ClientFieldData(
prompt=lambda e: "Enter the maximum spread (in %): ",
prompt_on_new=True
))
max_order_levels: int = Field(
default=5,
client_data=ClientFieldData(
prompt=lambda e: "Enter the maximum number of order levels per side: ",
prompt_on_new=True
))

def update_markets(self, markets: Dict[str, Set[str]]) -> Dict[str, Set[str]]:
if self.maker_connector not in markets:
markets[self.maker_connector] = set()
markets[self.maker_connector].add(self.maker_trading_pair)
if self.taker_connector not in markets:
markets[self.taker_connector] = set()
markets[self.taker_connector].add(self.taker_trading_pair)
return markets


class XEMMCopycat(ControllerBase):

def __init__(self, config: XEMMCopycatConfig, *args, **kwargs):
self.config = config
super().__init__(config, *args, **kwargs)

async def update_processed_data(self):
pass

def get_targets(self):
"""
Returns the target profitability and amount for each executor.

It is based on the hedge exchange order book.
We take the best bids and asks and add levels of liquidity, based on the amount of liquidity posted on
the hedge exchange. We stop adding levels when the target liquidity is reached. Target profitability is
based on the price levels we added.
"""

def determine_executor_actions(self) -> List[ExecutorAction]:
executor_actions = []
mid_price = self.market_data_provider.get_price_by_type(self.config.maker_connector, self.config.maker_trading_pair, PriceType.MidPrice)
active_buy_executors = self.filter_executors(
executors=self.executors_info,
filter_func=lambda e: not e.is_done and e.config.maker_side == TradeType.BUY
)
active_sell_executors = self.filter_executors(
executors=self.executors_info,
filter_func=lambda e: not e.is_done and e.config.maker_side == TradeType.SELL
)
stopped_buy_executors = self.filter_executors(
executors=self.executors_info,
filter_func=lambda e: e.is_done and e.config.maker_side == TradeType.BUY and e.filled_amount_quote != 0
)
stopped_sell_executors = self.filter_executors(
executors=self.executors_info,
filter_func=lambda e: e.is_done and e.config.maker_side == TradeType.SELL and e.filled_amount_quote != 0
)
imbalance = len(stopped_buy_executors) - len(stopped_sell_executors)
for target_profitability, amount in self.buy_levels_targets_amount:
active_buy_executors_target = [e.config.target_profitability == target_profitability for e in active_buy_executors]

if len(active_buy_executors_target) == 0 and imbalance < self.config.max_executors_imbalance:
config = XEMMExecutorConfig(
controller_id=self.config.id,
timestamp=self.market_data_provider.time(),
buying_market=ConnectorPair(connector_name=self.config.maker_connector,
trading_pair=self.config.maker_trading_pair),
selling_market=ConnectorPair(connector_name=self.config.taker_connector,
trading_pair=self.config.taker_trading_pair),
maker_side=TradeType.BUY,
order_amount=amount / mid_price,
min_profitability=self.config.min_profitability,
target_profitability=target_profitability,
max_profitability=self.config.max_profitability
)
executor_actions.append(CreateExecutorAction(executor_config=config, controller_id=self.config.id))
for target_profitability, amount in self.sell_levels_targets_amount:
active_sell_executors_target = [e.config.target_profitability == target_profitability for e in active_sell_executors]
if len(active_sell_executors_target) == 0 and imbalance > -self.config.max_executors_imbalance:
config = XEMMExecutorConfig(
controller_id=self.config.id,
timestamp=time.time(),
buying_market=ConnectorPair(connector_name=self.config.taker_connector,
trading_pair=self.config.taker_trading_pair),
selling_market=ConnectorPair(connector_name=self.config.maker_connector,
trading_pair=self.config.maker_trading_pair),
maker_side=TradeType.SELL,
order_amount=amount / mid_price,
min_profitability=self.config.min_profitability,
target_profitability=target_profitability,
max_profitability=self.config.max_profitability
)
executor_actions.append(CreateExecutorAction(executor_config=config, controller_id=self.config.id))
return executor_actions

def to_format_status(self) -> List[str]:
all_executors_custom_info = pd.DataFrame(e.custom_info for e in self.executors_info)
return [format_df_for_printout(all_executors_custom_info, table_format="psql", )]
6 changes: 3 additions & 3 deletions hummingbot/client/command/gateway_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
native_tokens,
search_configs,
)
from hummingbot.core.utils.ssl_cert import create_self_sign_certs
from hummingbot.core.utils.ssl_cert import generate_certs

if TYPE_CHECKING:
from hummingbot.client.hummingbot_application import HummingbotApplication # noqa: F401
Expand Down Expand Up @@ -127,7 +127,6 @@ async def _generate_certs(
self, # type: HummingbotApplication
from_client_password: bool = False,
):

certs_path: str = get_gateway_paths(
self.client_config_map).local_certs_path.as_posix()

Expand All @@ -143,7 +142,8 @@ async def _generate_certs(
self.notify("Error: Invalid pass phrase")
else:
pass_phase = Security.secrets_manager.password.get_secret_value()
create_self_sign_certs(pass_phase, certs_path)

generate_certs(pass_phase, certs_path)
self.notify(
f"Gateway SSL certification files are created in {certs_path}.")
self._get_gateway_instance().reload_certs(self.client_config_map)
Expand Down
75 changes: 51 additions & 24 deletions hummingbot/client/config/config_crypt.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import binascii
import json
import os
from abc import ABC, abstractmethod
from os.path import exists

from eth_account import Account
from eth_keyfile.keyfile import (
Expand All @@ -19,10 +21,35 @@
)
from pydantic import SecretStr

from hummingbot import root_path
from hummingbot.client.settings import CONF_DIR_PATH

PASSWORD_VERIFICATION_WORD = "HummingBot"
PASSWORD_VERIFICATION_PATH = CONF_DIR_PATH / ".password_verification"

# Global static values
def load_security_config_values():
config_file_path = str(root_path() / "conf" / "settings_conf_client.yml")
if exists(config_file_path):
with open(config_file_path, "r") as file:
config_data = json.load(file)
else:
config_data = {}

return {
"PASSWORD_VERIFICATION_WORD": config_data.get(
"PASSWORD_VERIFICATION_WORD", os.environ.get("PASSWORD_VERIFICATION_WORD", "HummingBot")
),
"PASSWORD_VERIFICATION_PATH": config_data.get(
"PASSWORD_VERIFICATION_PATH",
os.environ.get("PASSWORD_VERIFICATION_PATH", str(CONF_DIR_PATH / ".password_verification")),
),
}


security_config_values = load_security_config_values()

# Security Configs
PASSWORD_VERIFICATION_WORD = security_config_values["PASSWORD_VERIFICATION_WORD"]
PASSWORD_VERIFICATION_PATH = security_config_values["PASSWORD_VERIFICATION_PATH"]


class BaseSecretsManager(ABC):
Expand Down Expand Up @@ -90,21 +117,21 @@ def _create_v3_keyfile_json(message_to_encrypt, password, kdf="pbkdf2", work_fac
if work_factor is None:
work_factor = get_default_work_factor_for_kdf(kdf)

if kdf == 'pbkdf2':
if kdf == "pbkdf2":
derived_key = _pbkdf2_hash(
password,
hash_name='sha256',
hash_name="sha256",
salt=salt,
iterations=work_factor,
dklen=DKLEN,
)
kdfparams = {
'c': work_factor,
'dklen': DKLEN,
'prf': 'hmac-sha256',
'salt': encode_hex_no_prefix(salt),
"c": work_factor,
"dklen": DKLEN,
"prf": "hmac-sha256",
"salt": encode_hex_no_prefix(salt),
}
elif kdf == 'scrypt':
elif kdf == "scrypt":
derived_key = _scrypt_hash(
password,
salt=salt,
Expand All @@ -114,11 +141,11 @@ def _create_v3_keyfile_json(message_to_encrypt, password, kdf="pbkdf2", work_fac
n=work_factor,
)
kdfparams = {
'dklen': DKLEN,
'n': work_factor,
'r': SCRYPT_R,
'p': SCRYPT_P,
'salt': encode_hex_no_prefix(salt),
"dklen": DKLEN,
"n": work_factor,
"r": SCRYPT_R,
"p": SCRYPT_P,
"salt": encode_hex_no_prefix(salt),
}
else:
raise NotImplementedError("KDF not implemented: {0}".format(kdf))
Expand All @@ -129,16 +156,16 @@ def _create_v3_keyfile_json(message_to_encrypt, password, kdf="pbkdf2", work_fac
mac = keccak(derived_key[16:32] + ciphertext)

return {
'crypto': {
'cipher': 'aes-128-ctr',
'cipherparams': {
'iv': encode_hex_no_prefix(int_to_big_endian(iv)),
"crypto": {
"cipher": "aes-128-ctr",
"cipherparams": {
"iv": encode_hex_no_prefix(int_to_big_endian(iv)),
},
'ciphertext': encode_hex_no_prefix(ciphertext),
'kdf': kdf,
'kdfparams': kdfparams,
'mac': encode_hex_no_prefix(mac),
"ciphertext": encode_hex_no_prefix(ciphertext),
"kdf": kdf,
"kdfparams": kdfparams,
"mac": encode_hex_no_prefix(mac),
},
'version': 3,
'alias': '', # Add this line to include the 'alias' field with an empty string value
"version": 3,
"alias": "", # Add this line to include the 'alias' field with an empty string value
}
Loading