Skip to content

Commit

Permalink
Merge pull request #6990 from hummingbot/staging
Browse files Browse the repository at this point in the history
Sync / Staging to master, release 1.27.0
  • Loading branch information
rapcmia committed Apr 26, 2024
2 parents 3f73b17 + 722df0e commit a9d6d99
Show file tree
Hide file tree
Showing 137 changed files with 15,557 additions and 445 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ omit =
hummingbot/strategy/*/start.py
hummingbot/strategy/dev*
hummingbot/user/user_balances.py
hummingbot/connector/exchange/cube/cube_ws_protobufs/*
dynamic_context = test_function
branch = true

Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Help us **democratize high-frequency trading** and make powerful trading algorit
## Quick Links

* [Website and Docs](https://hummingbot.org): Official Hummingbot website and documentation
* [Installation](https://hummingbot.org/installation/): Install Hummingbot on various platforms
* [Installation](https://hummingbot.org/installation/docker/): Install Hummingbot on various platforms
* [FAQs](https://hummingbot.org/faq/): Answers to all your burning questions
* [Botcamp](https://hummingbot.org/botcamp/): Learn how build your own custom HFT strategy in Hummingbot with our hands-on bootcamp!
* [Newsletter](https://hummingbot.substack.com): Get our monthly newletter whenever we ship a new release
Expand All @@ -38,7 +38,8 @@ Help us **democratize high-frequency trading** and make powerful trading algorit

We recommend installing Hummingbot using Docker if you want the simplest, easiest installation method and don't need to modify the Hummingbot codebase.

**Prerequesites:**

**Prerequisites:**

* MacOS 10.12.6+ / Linux (Ubuntu 20.04+, Debian 10+) / Windows 10+
* Memory: 4 GB RAM per instance
Expand All @@ -56,7 +57,7 @@ docker attach hummingbot

We recommend installing Hummingbot from source if you want to customize or extend the Hummingbot codebase, build new components like connectors or strategies, and/or learn how Hummingbot works at a deeper, technical level.

**Prerequesites:**
**Prerequisites:**

* MacOS 10.12.6+ / Linux (Ubuntu 20.04+, Debian 10+)
* Memory: 4 GB RAM per instance
Expand All @@ -72,7 +73,7 @@ conda activate hummingbot
./start
```

See [Installation](https://hummingbot.org/installation/) for detailed guides for each OS.
See [Installation](https://hummingbot.org/installation/linux/) for detailed guides for each OS.

## Architecture

Expand Down
4 changes: 4 additions & 0 deletions conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
binance_perpetuals_api_key = os.getenv("BINANCE_PERPETUALS_API_KEY")
binance_perpetuals_api_secret = os.getenv("BINANCE_PERPETUALS_API_SECRET")
# Coinbase Advanced Trade Tests
coinbase_advanced_trade_api_key = os.getenv("COINBASE_ADVANCED_TRADE_API_KEY")
coinbase_advanced_trade_secret_key = os.getenv("COINBASE_ADVANCED_TRADE_SECRET_KEY")
# Coinbase Pro Tests
coinbase_pro_api_key = os.getenv("COINBASE_PRO_API_KEY")
coinbase_pro_secret_key = os.getenv("COINBASE_PRO_SECRET_KEY")
Expand Down
Empty file added controllers/generic/__init__.py
Empty file.
146 changes: 146 additions & 0 deletions controllers/generic/xemm_multiple_levels.py
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", )]
138 changes: 138 additions & 0 deletions controllers/market_making/dman_maker_v2.py
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,
)
8 changes: 4 additions & 4 deletions controllers/market_making/pmm_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class PMMSimpleConfig(MarketMakingControllerConfigBase):
controller_name = "pmm_simple"
# As this controller is a simple version of the PMM, we are not using the candles feed
candles_config: List[CandlesConfig] = Field(default=[], client_data=ClientFieldData(prompt_on_new=False))
top_order_refresh_time: Optional[float] = Field(
top_executor_refresh_time: Optional[float] = Field(
default=None,
client_data=ClientFieldData(
is_updatable=True,
Expand All @@ -31,9 +31,9 @@ def __init__(self, config: PMMSimpleConfig, *args, **kwargs):
self.config = config

def first_level_refresh_condition(self, executor):
if self.config.top_order_refresh_time is not None:
if self.get_level_from_level_id(executor.custom_info["level_id"]) == 1:
return time.time() - executor.timestamp > self.config.top_order_refresh_time
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):
Expand Down
9 changes: 5 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ version: "3.9"
services:
hummingbot:
container_name: hummingbot
build:
context: .
dockerfile: Dockerfile
image: hummingbot/hummingbot:latest
# build: Uncomment this and comment image if you want to build it locally
# context: .
# dockerfile: Dockerfile
volumes:
- ./conf:/home/hummingbot/conf
- ./conf/connectors:/home/hummingbot/conf/connectors
Expand Down Expand Up @@ -41,4 +42,4 @@ services:
# - "./gateway_files/db:/home/gateway/db"
# - "./certs:/home/gateway/certs"
# environment:
# - GATEWAY_PASSPHRASE=a
# - GATEWAY_PASSPHRASE=a
2 changes: 1 addition & 1 deletion hummingbot/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.26.0
1.27.0

0 comments on commit a9d6d99

Please sign in to comment.