Skip to content

Commit e75fa29

Browse files
authored
Merge pull request #190 from hummingbot/feat/add_grid_strike
Feat/add grid strike
2 parents f24c1bf + 59f1188 commit e75fa29

File tree

9 files changed

+562
-10
lines changed

9 files changed

+562
-10
lines changed

backend/services/backend_api_client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,21 @@ def delete_controller_config(self, controller_name: str):
175175
url = "delete-controller-config"
176176
return self.post(url, params={"config_name": controller_name})
177177

178+
def delete_script_config(self, script_name: str):
179+
"""Delete a script configuration."""
180+
url = "delete-script-config"
181+
return self.post(url, params={"script_name": script_name})
182+
183+
def delete_all_controller_configs(self):
184+
"""Delete all controller configurations."""
185+
endpoint = "delete-all-controller-configs"
186+
return self.post(endpoint)
187+
188+
def delete_all_script_configs(self):
189+
"""Delete all script configurations."""
190+
endpoint = "delete-all-script-configs"
191+
return self.post(endpoint)
192+
178193
def get_real_time_candles(self, connector: str, trading_pair: str, interval: str, max_records: int):
179194
"""Get candles data."""
180195
endpoint = "real-time-candles"

docker-compose.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
services:
2+
dashboard:
3+
container_name: dashboard
4+
image: hummingbot/dashboard:latest
5+
ports:
6+
- "8501:8501"
7+
environment:
8+
- AUTH_SYSTEM_ENABLED=False
9+
- BACKEND_API_HOST=backend-api
10+
- BACKEND_API_PORT=8000
11+
- BACKEND_API_USERNAME=admin
12+
- BACKEND_API_PASSWORD=admin
13+
volumes:
14+
- .:/home/dashboard

frontend/components/bot_performance_card.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def start_controllers(self, bot_name):
6868
def __call__(self, bot_name: str):
6969
try:
7070
controller_configs = backend_api_client.get_all_configs_from_bot(bot_name)
71+
controller_configs = controller_configs if controller_configs else []
7172
bot_status = backend_api_client.get_bot_status(bot_name)
7273
# Controllers Table
7374
active_controllers_list = []
@@ -78,6 +79,9 @@ def __call__(self, bot_name: str):
7879
total_open_order_volume = 0
7980
total_imbalance = 0
8081
total_unrealized_pnl_quote = 0
82+
bot_data = bot_status.get("data")
83+
error_logs = bot_data.get("error_logs", [])
84+
general_logs = bot_data.get("general_logs", [])
8185
if bot_status.get("status") == "error":
8286
with mui.Card(key=self._key,
8387
sx={"display": "flex", "flexDirection": "column", "borderRadius": 2, "overflow": "auto"},
@@ -91,11 +95,8 @@ def __call__(self, bot_name: str):
9195
f"An error occurred while fetching bot status of the bot {bot_name}. Please check the bot client.",
9296
severity="error")
9397
else:
94-
bot_data = bot_status.get("data")
9598
is_running = bot_data.get("status") == "running"
9699
performance = bot_data.get("performance")
97-
error_logs = bot_data.get("error_logs")
98-
general_logs = bot_data.get("general_logs")
99100
if is_running:
100101
for controller, inner_dict in performance.items():
101102
controller_status = inner_dict.get("status")
@@ -115,14 +116,15 @@ def __call__(self, bot_name: str):
115116
global_pnl_quote = controller_performance.get("global_pnl_quote", 0)
116117
volume_traded = controller_performance.get("volume_traded", 0)
117118
open_order_volume = controller_performance.get("open_order_volume", 0)
118-
imbalance = controller_performance.get("imbalance", 0)
119+
imbalance = controller_performance.get("inventory_imbalance", 0)
119120
close_types = controller_performance.get("close_type_counts", {})
120121
tp = close_types.get("CloseType.TAKE_PROFIT", 0)
121122
sl = close_types.get("CloseType.STOP_LOSS", 0)
122123
time_limit = close_types.get("CloseType.TIME_LIMIT", 0)
123124
ts = close_types.get("CloseType.TRAILING_STOP", 0)
124125
refreshed = close_types.get("CloseType.EARLY_STOP", 0)
125-
close_types_str = f"TP: {tp} | SL: {sl} | TS: {ts} | TL: {time_limit} | RS: {refreshed}"
126+
failed = close_types.get("CloseType.FAILED", 0)
127+
close_types_str = f"TP: {tp} | SL: {sl} | TS: {ts} | TL: {time_limit} | ES: {refreshed} | F: {failed}"
126128
controller_info = {
127129
"id": controller,
128130
"controller": controller_name,

frontend/components/launch_strategy_v2.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ def __init__(self, *args, **kwargs):
3232
self._bot_name = None
3333
self._image_name = "hummingbot/hummingbot:latest"
3434
self._credentials = "master_account"
35+
self._max_global_drawdown = None
36+
self._max_controller_drawdown = None
37+
self._rebalance_interval = None
38+
self._asset_to_rebalance = "USDT"
3539

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

55+
def _set_max_global_drawdown(self, event):
56+
self._max_global_drawdown = event.target.value
57+
58+
def _set_max_controller_drawdown(self, event):
59+
self._max_controller_drawdown = event.target.value
60+
61+
def _set_rebalance_interval(self, event):
62+
self._rebalance_interval = event.target.value
63+
64+
def _set_asset_to_rebalance(self, event):
65+
self._asset_to_rebalance = event.target.value
66+
5167
def launch_new_bot(self):
5268
if not self._bot_name:
5369
st.warning("You need to define the bot name.")
@@ -72,7 +88,19 @@ def launch_new_bot(self):
7288
"time_to_cash_out": None,
7389
}
7490
}
75-
91+
if self._max_global_drawdown:
92+
script_config["content"]["max_global_drawdown"] = self._max_global_drawdown
93+
if self._max_controller_drawdown:
94+
script_config["content"]["max_controller_drawdown"] = self._max_controller_drawdown
95+
if self._rebalance_interval:
96+
script_config["content"]["rebalance_interval"] = self._rebalance_interval
97+
if self._asset_to_rebalance and "USD" in self._asset_to_rebalance:
98+
script_config["content"]["asset_to_rebalance"] = self._asset_to_rebalance
99+
else:
100+
st.error("You need to define the asset to rebalance in USD like token.")
101+
return
102+
103+
self._backend_api_client.delete_all_script_configs()
76104
self._backend_api_client.add_script_config(script_config)
77105
deploy_config = {
78106
"instance_name": bot_name,
@@ -102,25 +130,40 @@ def __call__(self):
102130
mui.Typography("🎛️ Bot Configuration", variant="h5")
103131

104132
with mui.Grid(container=True, spacing=2, sx={"padding": "10px 15px 10px 15px"}):
105-
with mui.Grid(item=True, xs=4):
133+
with mui.Grid(item=True, xs=3):
106134
mui.TextField(label="Instance Name", variant="outlined", onChange=lazy(self._set_bot_name),
107135
sx={"width": "100%"})
108-
with mui.Grid(item=True, xs=4):
136+
with mui.Grid(item=True, xs=3):
109137
available_images = self._backend_api_client.get_available_images("hummingbot")
110138
with mui.FormControl(variant="standard", sx={"width": "100%"}):
111139
mui.FormHelperText("Available Images")
112140
with mui.Select(label="Hummingbot Image", defaultValue="hummingbot/hummingbot:latest",
113141
variant="standard", onChange=lazy(self._set_image_name)):
114142
for image in available_images:
115143
mui.MenuItem(image, value=image)
116-
with mui.Grid(item=True, xs=4):
117144
available_credentials = self._backend_api_client.get_accounts()
118145
with mui.FormControl(variant="standard", sx={"width": "100%"}):
119146
mui.FormHelperText("Credentials")
120147
with mui.Select(label="Credentials", defaultValue="master_account",
121148
variant="standard", onChange=lazy(self._set_credentials)):
122149
for master_config in available_credentials:
123150
mui.MenuItem(master_config, value=master_config)
151+
with mui.Grid(item=True, xs=3):
152+
with mui.FormControl(variant="standard", sx={"width": "100%"}):
153+
mui.FormHelperText("Risk Management")
154+
mui.TextField(label="Max Global Drawdown (%)", variant="outlined", type="number",
155+
onChange=lazy(self._set_max_global_drawdown), sx={"width": "100%"})
156+
mui.TextField(label="Max Controller Drawdown (%)", variant="outlined", type="number",
157+
onChange=lazy(self._set_max_controller_drawdown), sx={"width": "100%"})
158+
159+
with mui.Grid(item=True, xs=3):
160+
with mui.FormControl(variant="standard", sx={"width": "100%"}):
161+
mui.FormHelperText("Rebalance Configuration")
162+
mui.TextField(label="Rebalance Interval (minutes)", variant="outlined", type="number",
163+
onChange=lazy(self._set_rebalance_interval), sx={"width": "100%"})
164+
mui.TextField(label="Asset to Rebalance", variant="outlined",
165+
onChange=lazy(self._set_asset_to_rebalance),
166+
sx={"width": "100%"}, default="USDT")
124167
all_controllers_config = self._backend_api_client.get_all_controllers_config()
125168
data = []
126169
for config in all_controllers_config:
@@ -141,7 +184,8 @@ def __call__(self):
141184
ts_text = str(trailing_stop["activation_price"]) + " / " + str(trailing_stop["trailing_delta"])
142185
data.append({
143186
"id": config["id"], "config_base": config_base, "version": version,
144-
"controller_name": config["controller_name"], "controller_type": config["controller_type"],
187+
"controller_name": config["controller_name"],
188+
"controller_type": config.get("controller_type", "generic"),
145189
"connector_name": connector_name, "trading_pair": trading_pair,
146190
"total_amount_quote": total_amount_quote, "max_loss_quote": total_amount_quote * stop_loss / 2,
147191
"stop_loss": stop_loss, "take_profit": take_profit,
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Grid Strike Configuration Tool
2+
3+
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.
4+
5+
## Features
6+
7+
- **Start from Default Configurations**: Begin with a default configuration or use the values from an existing configuration.
8+
- **Dynamic Price Range Defaults**: Automatically sets grid ranges based on current market conditions.
9+
- **Visual Grid Configuration**: See your grid ranges directly on the price chart.
10+
- **Multiple Grid Ranges**: Configure up to 5 different grid ranges with different sides (BUY/SELL).
11+
- **Save and Deploy**: Once satisfied, save the configuration to deploy it later.
12+
13+
## How to Use
14+
15+
### 1. Basic Configuration
16+
17+
Start by configuring the basic parameters:
18+
- **Connector Name**: Select the trading platform or exchange (e.g., "binance").
19+
- **Trading Pair**: Choose the cryptocurrency trading pair (e.g., "BTC-USDT").
20+
- **Leverage**: Set the leverage ratio (use 1 for spot trading).
21+
22+
### 2. Chart Configuration
23+
24+
Configure how you want to visualize the market data:
25+
- **Candles Connector**: Select the data source for candlestick data.
26+
- **Interval**: Choose the timeframe for the candlesticks (1m to 1d).
27+
- **Days to Display**: Select how many days of historical data to show.
28+
29+
### 3. Grid Ranges
30+
31+
Configure up to 5 grid ranges with different parameters:
32+
- **Number of Grid Ranges**: Select how many ranges you want to configure (1-5).
33+
- **Side**: Choose BUY or SELL for each range.
34+
- **Start Price**: The price where the range begins.
35+
- **End Price**: The price where the range ends.
36+
- **Amount %**: Percentage of total amount allocated to this range.
37+
38+
### 4. Advanced Configuration
39+
40+
Fine-tune your strategy with advanced parameters:
41+
- **Position Mode**: Choose between HEDGE or ONE-WAY.
42+
- **Time Limit**: Maximum duration for orders (in hours).
43+
- **Activation Bounds**: Price deviation to trigger updates.
44+
- **Min Spread Between Orders**: Minimum price difference between orders.
45+
- **Min Order Amount**: Minimum size for individual orders.
46+
- **Max Open Orders**: Maximum number of active orders per range.
47+
- **Grid Range Update Interval**: How often to update grid ranges (in seconds).
48+
49+
## Understanding Grid Strike Strategy
50+
51+
The Grid Strike strategy creates a grid of orders within specified price ranges. Here's how it works:
52+
53+
### Grid Range Mechanics
54+
- Each grid range defines a price zone where the strategy will place orders
55+
- BUY ranges place buy orders from higher to lower prices
56+
- SELL ranges place sell orders from lower to higher prices
57+
- Multiple ranges can work simultaneously with different configurations
58+
59+
### Order Placement
60+
- Orders are placed within each range based on the min spread between orders
61+
- The amount per order is calculated based on the range's allocation percentage
62+
- Orders are automatically adjusted when price moves beyond activation bounds
63+
64+
### Visual Indicators
65+
- Green lines represent BUY ranges
66+
- Red lines represent SELL ranges
67+
- Different dash patterns distinguish multiple ranges of the same side
68+
- Horizontal lines show the start and end prices of each range
69+
70+
## Best Practices
71+
72+
1. **Range Placement**
73+
- Place BUY ranges below current price
74+
- Place SELL ranges above current price
75+
- Avoid overlapping ranges of the same side
76+
77+
2. **Amount Allocation**
78+
- Distribute amounts across ranges based on your risk preference
79+
- Ensure total allocation across all ranges doesn't exceed 100%
80+
- Consider larger allocations for ranges closer to current price
81+
82+
3. **Spread Configuration**
83+
- Set min spread based on the asset's volatility
84+
- Larger spreads mean fewer, more profitable orders
85+
- Smaller spreads mean more frequent, less profitable orders
86+
87+
4. **Risk Management**
88+
- Use appropriate leverage (1 for spot)
89+
- Set reasonable time limits for orders
90+
- Monitor and adjust activation bounds based on market conditions
91+
92+
## Example Configuration
93+
94+
Here's a sample configuration for a BTC-USDT grid:

frontend/pages/config/grid_strike/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)