Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add grid strike #190

Merged
merged 13 commits into from
Nov 15, 2024
15 changes: 15 additions & 0 deletions backend/services/backend_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,21 @@ def delete_controller_config(self, controller_name: str):
url = "delete-controller-config"
return self.post(url, params={"config_name": controller_name})

def delete_script_config(self, script_name: str):
"""Delete a script configuration."""
url = "delete-script-config"
return self.post(url, params={"script_name": script_name})

def delete_all_controller_configs(self):
"""Delete all controller configurations."""
endpoint = "delete-all-controller-configs"
return self.post(endpoint)

def delete_all_script_configs(self):
"""Delete all script configurations."""
endpoint = "delete-all-script-configs"
return self.post(endpoint)

def get_real_time_candles(self, connector: str, trading_pair: str, interval: str, max_records: int):
"""Get candles data."""
endpoint = "real-time-candles"
Expand Down
14 changes: 14 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
services:
dashboard:
container_name: dashboard
image: hummingbot/dashboard:latest
ports:
- "8501:8501"
environment:
- AUTH_SYSTEM_ENABLED=False
- BACKEND_API_HOST=backend-api
- BACKEND_API_PORT=8000
- BACKEND_API_USERNAME=admin
- BACKEND_API_PASSWORD=admin
volumes:
- .:/home/dashboard
12 changes: 7 additions & 5 deletions frontend/components/bot_performance_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def start_controllers(self, bot_name):
def __call__(self, bot_name: str):
try:
controller_configs = backend_api_client.get_all_configs_from_bot(bot_name)
controller_configs = controller_configs if controller_configs else []
bot_status = backend_api_client.get_bot_status(bot_name)
# Controllers Table
active_controllers_list = []
Expand All @@ -78,6 +79,9 @@ def __call__(self, bot_name: str):
total_open_order_volume = 0
total_imbalance = 0
total_unrealized_pnl_quote = 0
bot_data = bot_status.get("data")
error_logs = bot_data.get("error_logs", [])
general_logs = bot_data.get("general_logs", [])
if bot_status.get("status") == "error":
with mui.Card(key=self._key,
sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
Expand All @@ -91,11 +95,8 @@ def __call__(self, bot_name: str):
f"An error occurred while fetching bot status of the bot {bot_name}. Please check the bot client.",
severity="error")
else:
bot_data = bot_status.get("data")
is_running = bot_data.get("status") == "running"
performance = bot_data.get("performance")
error_logs = bot_data.get("error_logs")
general_logs = bot_data.get("general_logs")
if is_running:
for controller, inner_dict in performance.items():
controller_status = inner_dict.get("status")
Expand All @@ -115,14 +116,15 @@ def __call__(self, bot_name: str):
global_pnl_quote = controller_performance.get("global_pnl_quote", 0)
volume_traded = controller_performance.get("volume_traded", 0)
open_order_volume = controller_performance.get("open_order_volume", 0)
imbalance = controller_performance.get("imbalance", 0)
imbalance = controller_performance.get("inventory_imbalance", 0)
close_types = controller_performance.get("close_type_counts", {})
tp = close_types.get("CloseType.TAKE_PROFIT", 0)
sl = close_types.get("CloseType.STOP_LOSS", 0)
time_limit = close_types.get("CloseType.TIME_LIMIT", 0)
ts = close_types.get("CloseType.TRAILING_STOP", 0)
refreshed = close_types.get("CloseType.EARLY_STOP", 0)
close_types_str = f"TP: {tp} | SL: {sl} | TS: {ts} | TL: {time_limit} | RS: {refreshed}"
failed = close_types.get("CloseType.FAILED", 0)
close_types_str = f"TP: {tp} | SL: {sl} | TS: {ts} | TL: {time_limit} | ES: {refreshed} | F: {failed}"
controller_info = {
"id": controller,
"controller": controller_name,
Expand Down
54 changes: 49 additions & 5 deletions frontend/components/launch_strategy_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ def __init__(self, *args, **kwargs):
self._bot_name = None
self._image_name = "hummingbot/hummingbot:latest"
self._credentials = "master_account"
self._max_global_drawdown = None
self._max_controller_drawdown = None
self._rebalance_interval = None
self._asset_to_rebalance = "USDT"

def _set_bot_name(self, event):
self._bot_name = event.target.value
Expand All @@ -48,6 +52,18 @@ def _set_controller(self, event):
def _handle_row_selection(self, params, _):
self._controller_config_selected = [param + ".yml" for param in params]

def _set_max_global_drawdown(self, event):
self._max_global_drawdown = event.target.value

def _set_max_controller_drawdown(self, event):
self._max_controller_drawdown = event.target.value

def _set_rebalance_interval(self, event):
self._rebalance_interval = event.target.value

def _set_asset_to_rebalance(self, event):
self._asset_to_rebalance = event.target.value

def launch_new_bot(self):
if not self._bot_name:
st.warning("You need to define the bot name.")
Expand All @@ -72,7 +88,19 @@ def launch_new_bot(self):
"time_to_cash_out": None,
}
}

if self._max_global_drawdown:
script_config["content"]["max_global_drawdown"] = self._max_global_drawdown
if self._max_controller_drawdown:
script_config["content"]["max_controller_drawdown"] = self._max_controller_drawdown
if self._rebalance_interval:
script_config["content"]["rebalance_interval"] = self._rebalance_interval
if self._asset_to_rebalance and "USD" in self._asset_to_rebalance:
script_config["content"]["asset_to_rebalance"] = self._asset_to_rebalance
else:
st.error("You need to define the asset to rebalance in USD like token.")
return

self._backend_api_client.delete_all_script_configs()
self._backend_api_client.add_script_config(script_config)
deploy_config = {
"instance_name": bot_name,
Expand Down Expand Up @@ -102,25 +130,40 @@ def __call__(self):
mui.Typography("🎛️ Bot Configuration", variant="h5")

with mui.Grid(container=True, spacing=2, sx={"padding": "10px 15px 10px 15px"}):
with mui.Grid(item=True, xs=4):
with mui.Grid(item=True, xs=3):
mui.TextField(label="Instance Name", variant="outlined", onChange=lazy(self._set_bot_name),
sx={"width": "100%"})
with mui.Grid(item=True, xs=4):
with mui.Grid(item=True, xs=3):
available_images = self._backend_api_client.get_available_images("hummingbot")
with mui.FormControl(variant="standard", sx={"width": "100%"}):
mui.FormHelperText("Available Images")
with mui.Select(label="Hummingbot Image", defaultValue="hummingbot/hummingbot:latest",
variant="standard", onChange=lazy(self._set_image_name)):
for image in available_images:
mui.MenuItem(image, value=image)
with mui.Grid(item=True, xs=4):
available_credentials = self._backend_api_client.get_accounts()
with mui.FormControl(variant="standard", sx={"width": "100%"}):
mui.FormHelperText("Credentials")
with mui.Select(label="Credentials", defaultValue="master_account",
variant="standard", onChange=lazy(self._set_credentials)):
for master_config in available_credentials:
mui.MenuItem(master_config, value=master_config)
with mui.Grid(item=True, xs=3):
with mui.FormControl(variant="standard", sx={"width": "100%"}):
mui.FormHelperText("Risk Management")
mui.TextField(label="Max Global Drawdown (%)", variant="outlined", type="number",
onChange=lazy(self._set_max_global_drawdown), sx={"width": "100%"})
mui.TextField(label="Max Controller Drawdown (%)", variant="outlined", type="number",
onChange=lazy(self._set_max_controller_drawdown), sx={"width": "100%"})

with mui.Grid(item=True, xs=3):
with mui.FormControl(variant="standard", sx={"width": "100%"}):
mui.FormHelperText("Rebalance Configuration")
mui.TextField(label="Rebalance Interval (minutes)", variant="outlined", type="number",
onChange=lazy(self._set_rebalance_interval), sx={"width": "100%"})
mui.TextField(label="Asset to Rebalance", variant="outlined",
onChange=lazy(self._set_asset_to_rebalance),
sx={"width": "100%"}, default="USDT")
all_controllers_config = self._backend_api_client.get_all_controllers_config()
data = []
for config in all_controllers_config:
Expand All @@ -141,7 +184,8 @@ def __call__(self):
ts_text = str(trailing_stop["activation_price"]) + " / " + str(trailing_stop["trailing_delta"])
data.append({
"id": config["id"], "config_base": config_base, "version": version,
"controller_name": config["controller_name"], "controller_type": config["controller_type"],
"controller_name": config["controller_name"],
"controller_type": config.get("controller_type", "generic"),
"connector_name": connector_name, "trading_pair": trading_pair,
"total_amount_quote": total_amount_quote, "max_loss_quote": total_amount_quote * stop_loss / 2,
"stop_loss": stop_loss, "take_profit": take_profit,
Expand Down
94 changes: 94 additions & 0 deletions frontend/pages/config/grid_strike/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Grid Strike Configuration Tool

Welcome to the Grid Strike Configuration Tool! This tool allows you to create, modify, visualize, and save configurations for the Grid Strike trading strategy. Here's how you can make the most out of it.

## Features

- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration.
- **Dynamic Price Range Defaults**: Automatically sets grid ranges based on current market conditions.
- **Visual Grid Configuration**: See your grid ranges directly on the price chart.
- **Multiple Grid Ranges**: Configure up to 5 different grid ranges with different sides (BUY/SELL).
- **Save and Deploy**: Once satisfied, save the configuration to deploy it later.

## How to Use

### 1. Basic Configuration

Start by configuring the basic parameters:
- **Connector Name**: Select the trading platform or exchange (e.g., "binance").
- **Trading Pair**: Choose the cryptocurrency trading pair (e.g., "BTC-USDT").
- **Leverage**: Set the leverage ratio (use 1 for spot trading).

### 2. Chart Configuration

Configure how you want to visualize the market data:
- **Candles Connector**: Select the data source for candlestick data.
- **Interval**: Choose the timeframe for the candlesticks (1m to 1d).
- **Days to Display**: Select how many days of historical data to show.

### 3. Grid Ranges

Configure up to 5 grid ranges with different parameters:
- **Number of Grid Ranges**: Select how many ranges you want to configure (1-5).
- **Side**: Choose BUY or SELL for each range.
- **Start Price**: The price where the range begins.
- **End Price**: The price where the range ends.
- **Amount %**: Percentage of total amount allocated to this range.

### 4. Advanced Configuration

Fine-tune your strategy with advanced parameters:
- **Position Mode**: Choose between HEDGE or ONE-WAY.
- **Time Limit**: Maximum duration for orders (in hours).
- **Activation Bounds**: Price deviation to trigger updates.
- **Min Spread Between Orders**: Minimum price difference between orders.
- **Min Order Amount**: Minimum size for individual orders.
- **Max Open Orders**: Maximum number of active orders per range.
- **Grid Range Update Interval**: How often to update grid ranges (in seconds).

## Understanding Grid Strike Strategy

The Grid Strike strategy creates a grid of orders within specified price ranges. Here's how it works:

### Grid Range Mechanics
- Each grid range defines a price zone where the strategy will place orders
- BUY ranges place buy orders from higher to lower prices
- SELL ranges place sell orders from lower to higher prices
- Multiple ranges can work simultaneously with different configurations

### Order Placement
- Orders are placed within each range based on the min spread between orders
- The amount per order is calculated based on the range's allocation percentage
- Orders are automatically adjusted when price moves beyond activation bounds

### Visual Indicators
- Green lines represent BUY ranges
- Red lines represent SELL ranges
- Different dash patterns distinguish multiple ranges of the same side
- Horizontal lines show the start and end prices of each range

## Best Practices

1. **Range Placement**
- Place BUY ranges below current price
- Place SELL ranges above current price
- Avoid overlapping ranges of the same side

2. **Amount Allocation**
- Distribute amounts across ranges based on your risk preference
- Ensure total allocation across all ranges doesn't exceed 100%
- Consider larger allocations for ranges closer to current price

3. **Spread Configuration**
- Set min spread based on the asset's volatility
- Larger spreads mean fewer, more profitable orders
- Smaller spreads mean more frequent, less profitable orders

4. **Risk Management**
- Use appropriate leverage (1 for spot)
- Set reasonable time limits for orders
- Monitor and adjust activation bounds based on market conditions

## Example Configuration

Here's a sample configuration for a BTC-USDT grid:
Empty file.
Loading