forked from hummingbot/hummingbot
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'hb-development' into kjr-development
- Loading branch information
Showing
137 changed files
with
15,591 additions
and
467 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import time | ||
from decimal import Decimal | ||
from typing import Dict, List, Set | ||
|
||
import pandas as pd | ||
from pydantic import Field, validator | ||
|
||
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.candles_factory import CandlesConfig | ||
from hummingbot.smart_components.controllers.controller_base import ControllerBase, ControllerConfigBase | ||
from hummingbot.smart_components.executors.data_types import ConnectorPair | ||
from hummingbot.smart_components.executors.xemm_executor.data_types import XEMMExecutorConfig | ||
from hummingbot.smart_components.models.executor_actions import CreateExecutorAction, ExecutorAction | ||
|
||
|
||
class XEMMMultipleLevelsConfig(ControllerConfigBase): | ||
controller_name: str = "xemm_multiple_levels" | ||
candles_config: List[CandlesConfig] = [] | ||
maker_connector: str = Field( | ||
default="kucoin", | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the maker connector: ", | ||
prompt_on_new=True | ||
)) | ||
maker_trading_pair: str = Field( | ||
default="LBR-USDT", | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the maker trading pair: ", | ||
prompt_on_new=True | ||
)) | ||
taker_connector: str = Field( | ||
default="okx", | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the taker connector: ", | ||
prompt_on_new=True | ||
)) | ||
taker_trading_pair: str = Field( | ||
default="LBR-USDT", | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the taker trading pair: ", | ||
prompt_on_new=True | ||
)) | ||
buy_levels_targets_amount: List[List[Decimal]] = Field( | ||
default="0.003,10-0.006,20-0.009,30", | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the buy levels targets with the following structure: (target_profitability1,amount1-target_profitability2,amount2): ", | ||
prompt_on_new=True | ||
)) | ||
sell_levels_targets_amount: List[List[Decimal]] = Field( | ||
default="0.003,10-0.006,20-0.009,30", | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the sell levels targets with the following structure: (target_profitability1,amount1-target_profitability2,amount2): ", | ||
prompt_on_new=True | ||
)) | ||
min_profitability: Decimal = Field( | ||
default=0.002, | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the minimum profitability: ", | ||
prompt_on_new=True | ||
)) | ||
max_profitability: Decimal = Field( | ||
default=0.01, | ||
client_data=ClientFieldData( | ||
prompt=lambda e: "Enter the maximum profitability: ", | ||
prompt_on_new=True | ||
)) | ||
|
||
@validator("buy_levels_targets_amount", "sell_levels_targets_amount", pre=True, always=True) | ||
def validate_levels_targets_amount(cls, v, values): | ||
if isinstance(v, str): | ||
v = [list(map(Decimal, x.split(","))) for x in v.split("-")] | ||
return v | ||
|
||
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 XEMMMultipleLevels(ControllerBase): | ||
|
||
def __init__(self, config: XEMMMultipleLevelsConfig, *args, **kwargs): | ||
self.config = config | ||
self.buy_levels_targets_amount = config.buy_levels_targets_amount | ||
self.sell_levels_targets_amount = config.sell_levels_targets_amount | ||
super().__init__(config, *args, **kwargs) | ||
|
||
async def update_processed_data(self): | ||
pass | ||
|
||
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 | ||
) | ||
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: | ||
config = XEMMExecutorConfig( | ||
controller_id=self.config.id, | ||
timestamp=time.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: | ||
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", )] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import time | ||
from decimal import Decimal | ||
from typing import List, Optional | ||
|
||
import pandas_ta as ta # noqa: F401 | ||
from pydantic import Field, validator | ||
|
||
from hummingbot.client.config.config_data_types import ClientFieldData | ||
from hummingbot.core.data_type.common import TradeType | ||
from hummingbot.data_feed.candles_feed.candles_factory import CandlesConfig | ||
from hummingbot.smart_components.controllers.market_making_controller_base import ( | ||
MarketMakingControllerBase, | ||
MarketMakingControllerConfigBase, | ||
) | ||
from hummingbot.smart_components.executors.dca_executor.data_types import DCAExecutorConfig, DCAMode | ||
from hummingbot.smart_components.models.executor_actions import ExecutorAction, StopExecutorAction | ||
|
||
|
||
class DManMakerV2Config(MarketMakingControllerConfigBase): | ||
""" | ||
Configuration required to run the D-Man Maker V2 strategy. | ||
""" | ||
controller_name: str = "dman_maker_v2" | ||
candles_config: List[CandlesConfig] = [] | ||
|
||
# DCA configuration | ||
dca_spreads: List[Decimal] = Field( | ||
default="0.01,0.02,0.04,0.08", | ||
client_data=ClientFieldData( | ||
prompt_on_new=True, | ||
prompt=lambda mi: "Enter a comma-separated list of spreads for each DCA level: ")) | ||
dca_amounts: List[Decimal] = Field( | ||
default="0.1,0.2,0.4,0.8", | ||
client_data=ClientFieldData( | ||
prompt_on_new=True, | ||
prompt=lambda mi: "Enter a comma-separated list of amounts for each DCA level: ")) | ||
time_limit: int = Field( | ||
default=60 * 60 * 24 * 7, gt=0, | ||
client_data=ClientFieldData( | ||
prompt=lambda mi: "Enter the time limit for each DCA level: ", | ||
prompt_on_new=False)) | ||
stop_loss: Decimal = Field( | ||
default=Decimal("0.03"), gt=0, | ||
client_data=ClientFieldData( | ||
prompt=lambda mi: "Enter the stop loss (as a decimal, e.g., 0.03 for 3%): ", | ||
prompt_on_new=True)) | ||
top_executor_refresh_time: Optional[float] = Field( | ||
default=None, | ||
client_data=ClientFieldData( | ||
is_updatable=True, | ||
prompt_on_new=False)) | ||
executor_activation_bounds: Optional[List[Decimal]] = Field( | ||
default=None, | ||
client_data=ClientFieldData( | ||
is_updatable=True, | ||
prompt=lambda mi: "Enter the activation bounds for the orders " | ||
"(e.g., 0.01 activates the next order when the price is closer than 1%): ", | ||
prompt_on_new=False)) | ||
|
||
@validator("executor_activation_bounds", pre=True, always=True) | ||
def parse_activation_bounds(cls, v): | ||
if isinstance(v, list): | ||
return [Decimal(val) for val in v] | ||
elif isinstance(v, str): | ||
if v == "": | ||
return None | ||
return [Decimal(val) for val in v.split(",")] | ||
return v | ||
|
||
@validator('dca_spreads', pre=True, always=True) | ||
def parse_spreads(cls, v): | ||
if v is None: | ||
return [] | ||
if isinstance(v, str): | ||
if v == "": | ||
return [] | ||
return [float(x.strip()) for x in v.split(',')] | ||
return v | ||
|
||
@validator('dca_amounts', pre=True, always=True) | ||
def parse_and_validate_amounts(cls, v, values, field): | ||
if v is None or v == "": | ||
return [1 for _ in values[values['dca_spreads']]] | ||
if isinstance(v, str): | ||
return [float(x.strip()) for x in v.split(',')] | ||
elif isinstance(v, list) and len(v) != len(values['dca_spreads']): | ||
raise ValueError( | ||
f"The number of {field.name} must match the number of {values['dca_spreads']}.") | ||
return v | ||
|
||
|
||
class DManMakerV2(MarketMakingControllerBase): | ||
def __init__(self, config: DManMakerV2Config, *args, **kwargs): | ||
super().__init__(config, *args, **kwargs) | ||
self.config = config | ||
self.dca_amounts_pct = [Decimal(amount) / sum(self.config.dca_amounts) for amount in self.config.dca_amounts] | ||
self.spreads = self.config.dca_spreads | ||
|
||
def first_level_refresh_condition(self, executor): | ||
if self.config.top_executor_refresh_time is not None: | ||
if self.get_level_from_level_id(executor.custom_info["level_id"]) == 0: | ||
return time.time() - executor.timestamp > self.config.top_executor_refresh_time | ||
return False | ||
|
||
def order_level_refresh_condition(self, executor): | ||
return time.time() - executor.timestamp > self.config.executor_refresh_time | ||
|
||
def executors_to_refresh(self) -> List[ExecutorAction]: | ||
executors_to_refresh = self.filter_executors( | ||
executors=self.executors_info, | ||
filter_func=lambda x: not x.is_trading and x.is_active and (self.order_level_refresh_condition(x) or self.first_level_refresh_condition(x))) | ||
return [StopExecutorAction( | ||
controller_id=self.config.id, | ||
executor_id=executor.id) for executor in executors_to_refresh] | ||
|
||
def get_executor_config(self, level_id: str, price: Decimal, amount: Decimal): | ||
trade_type = self.get_trade_type_from_level_id(level_id) | ||
if trade_type == TradeType.BUY: | ||
prices = [price * (1 - spread) for spread in self.spreads] | ||
else: | ||
prices = [price * (1 + spread) for spread in self.spreads] | ||
amounts = [amount * pct for pct in self.dca_amounts_pct] | ||
amounts_quote = [amount * price for amount, price in zip(amounts, prices)] | ||
return DCAExecutorConfig( | ||
timestamp=time.time(), | ||
connector_name=self.config.connector_name, | ||
trading_pair=self.config.trading_pair, | ||
mode=DCAMode.MAKER, | ||
side=trade_type, | ||
prices=prices, | ||
amounts_quote=amounts_quote, | ||
level_id=level_id, | ||
time_limit=self.config.time_limit, | ||
stop_loss=self.config.stop_loss, | ||
trailing_stop=self.config.trailing_stop, | ||
activation_bounds=self.config.executor_activation_bounds, | ||
leverage=self.config.leverage, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.