Grid Trading Within Price Bounds Using Configurable Execution Controls
Summary
This controller configures a grid strategy for one exchange connector and trading pair. It specifies a price range, a direction, a quote budget, order spacing and minimum size, limits on concurrent and batched orders, and an order update frequency. A grid executor is created only when no executor is active and the market mid-price is inside the configured bounds.
The configuration also supports leverage, hedge position mode, activation bounds, a limit price, and an option to keep the resulting position. A triple-barrier configuration supplies maker order types and a take-profit setting. The status display reports grid-level states, order outcomes, realized volumes, profit and loss, fees, position size, and open liquidity. This is implementation guidance rather than a tested trading study: it contains no market history, performance results, or evidence that the chosen defaults are suitable. The grid update method is empty, so the actual order placement and management depend on the executor framework.
Key ideas
- A grid executor is launched only when the mid-price lies within configured start and end prices and there is no active executor.
- The configuration exposes controls for order spacing, minimum order value, open-order limits, batching, and update frequency.
- Leverage, position mode, trading direction, budget, and whether to keep positions can also be configured.
- A triple-barrier setup supplies maker order types and a take-profit parameter.
- The controller displays execution and position metrics but provides no backtest evidence for its defaults.
Tags
Full text
# GridStrike
# GridStrike
Configuration required to run the GridStrike strategy for one connector and trading pair.
## Source (Apache-2.0)
```python
from decimal import Decimal
from typing import List, Optional
from pydantic import Field
from hummingbot.core.data_type.common import MarketDict, OrderType, PositionMode, PriceType, TradeType
from hummingbot.strategy_v2.controllers import ControllerBase, ControllerConfigBase
from hummingbot.strategy_v2.executors.data_types import ConnectorPair
from hummingbot.strategy_v2.executors.grid_executor.data_types import GridExecutorConfig
from hummingbot.strategy_v2.executors.position_executor.data_types import TripleBarrierConfig
from hummingbot.strategy_v2.models.executor_actions import CreateExecutorAction, ExecutorAction
from hummingbot.strategy_v2.models.executors_info import ExecutorInfo
class GridStrikeConfig(ControllerConfigBase):
"""
Configuration required to run the GridStrike strategy for one connector and trading pair.
"""
controller_type: str = "generic"
controller_name: str = "grid_strike"
# Account configuration
leverage: int = 20
position_mode: PositionMode = PositionMode.HEDGE
# Boundaries
connector_name: str = "okx"
trading_pair: str = "WLD-USDT"
side: TradeType = TradeType.BUY
start_price: Decimal = Field(default=Decimal("0.38"), json_schema_extra={"is_updatable": True})
end_price: Decimal = Field(default=Decimal("0.75"), json_schema_extra={"is_updatable": True})
limit_price: Decimal = Field(default=Decimal("0.35"), json_schema_extra={"is_updatable": True})
# Profiling
total_amount_quote: Decimal = Field(default=Decimal("1000"), json_schema_extra={"is_updatable": True})
min_spread_between_orders: Optional[Decimal] = Field(default=Decimal("0.001"), json_schema_extra={"is_updatable": True})
min_order_amount_quote: Optional[Decimal] = Field(default=Decimal("5"), json_schema_extra={"is_updatable": True})
# Execution
max_open_orders: int = Field(default=2, json_schema_extra={"is_updatable": True})
max_orders_per_batch: Optional[int] = Field(default=1, json_schema_extra={"is_updatable": True})
order_frequency: int = Field(default=3, json_schema_extra={"is_updatable": True})
activation_bounds: Optional[Decimal] = Field(default=None, json_schema_extra={"is_updatable": True})
keep_position: bool = Field(default=False, json_schema_extra={"is_updatable": True})
# Risk Management
triple_barrier_config: TripleBarrierConfig = TripleBarrierConfig(
take_profit=Decimal("0.001"),
open_order_type=OrderType.LIMIT_MAKER,
take_profit_order_type=OrderType.LIMIT_MAKER,
)
def update_markets(self, markets: MarketDict) -> MarketDict:
return markets.add_or_update(self.connector_name, self.trading_pair)
class GridStrike(ControllerBase):
def __init__(self, config: GridStrikeConfig, *args, **kwargs):
super().__init__(config, *args, **kwargs)
self.config = config
self._last_grid_levels_update = 0
self.trading_rules = None
self.grid_levels = []
self.initialize_rate_sources()
def initialize_rate_sources(self):
self.market_data_provider.initialize_rate_sources([ConnectorPair(connector_name=self.config.connector_name,
trading_pair=self.config.trading_pair)])
def active_executors(self) -> List[ExecutorInfo]:
return [
executor for executor in self.executors_info
if executor.is_active
]
def is_inside_bounds(self, price: Decimal) -> bool:
return self.config.start_price <= price <= self.config.end_price
def determine_executor_actions(self) -> List[ExecutorAction]:
mid_price = self.market_data_provider.get_price_by_type(
self.config.connector_name, self.config.trading_pair, PriceType.MidPrice)
if len(self.active_executors()) == 0 and self.is_inside_bounds(mid_price):
return [CreateExecutorAction(
controller_id=self.config.id,
executor_config=GridExecutorConfig(
timestamp=self.market_data_provider.time(),
connector_name=self.config.connector_name,
trading_pair=self.config.trading_pair,
start_price=self.config.start_price,
end_price=self.config.end_price,
leverage=self.config.leverage,
limit_price=self.config.limit_price,
side=self.config.side,
total_amount_quote=self.config.total_amount_quote,
min_spread_between_orders=self.config.min_spread_between_orders,
min_order_amount_quote=self.config.min_order_amount_quote,
max_open_orders=self.config.max_open_orders,
max_orders_per_batch=self.config.max_orders_per_batch,
order_frequency=self.config.order_frequency,
activation_bounds=self.config.activation_bounds,
triple_barrier_config=self.config.triple_barrier_config,
level_id=None,
keep_position=self.config.keep_position,
))]
return []
async def update_processed_data(self):
pass
def to_format_status(self) -> List[str]:
status = []
mid_price = self.market_data_provider.get_price_by_type(
self.config.connector_name, self.config.trading_pair, PriceType.MidPrice)
# Define standard box width for consistency
box_width = 114
# Top Grid Configuration box with simple borders
status.append("┌" + "─" * box_width + "┐")
# First line: Grid Configuration and Mid Price
left_section = "Grid Configuration:"
padding = box_width - len(left_section) - 4 # -4 for the border characters and spacing
config_line1 = f"│ {left_section}{' ' * padding}"
padding2 = box_width - len(config_line1) + 1 # +1 for correct right border alignment
config_line1 += " " * padding2 + "│"
status.append(config_line1)
# Second line: Configuration parameters
config_line2 = f"│ Start: {self.config.start_price:.4f} │ End: {self.config.end_price:.4f} │ Side: {self.config.side} │ Limit: {self.config.limit_price:.4f} │ Mid Price: {mid_price:.4f} │"
padding = box_width - len(config_line2) + 1 # +1 for correct right border alignment
config_line2 += " " * padding + "│"
status.append(config_line2)
# Third line: Max orders and Inside bounds
config_line3 = f"│ Max Orders: {self.config.max_open_orders} │ Inside bounds: {1 if self.is_inside_bounds(mid_price) else 0}"
padding = box_width - len(config_line3) + 1 # +1 for correct right border alignment
config_line3 += " " * padding + "│"
status.append(config_line3)
status.append("└" + "─" * box_width + "┘")
for level in self.active_executors():
# Define column widths for perfect alignment
col_width = box_width // 3 # Dividing the total width by 3 for equal columns
total_width = box_width
# Grid Status header - use long line and running status
status_header = f"Grid Status: {level.id} (RunnableStatus.RUNNING)"
status_line = f"┌ {status_header}" + "─" * (total_width - len(status_header) - 2) + "┐"
status.append(status_line)
# Calculate exact column widths for perfect alignment
col1_end = col_width
# Column headers
header_line = "│ Level Distribution" + " " * (col1_end - 20) + "│"
header_line += " Order Statistics" + " " * (col_width - 18) + "│"
header_line += " Performance Metrics" + " " * (col_width - 21) + "│"
status.append(header_line)
# Data for the three columns
level_dist_data = [
f"NOT_ACTIVE: {level.custom_info['levels_by_state'].get('NOT_ACTIVE', 0)}",
f"OPEN_ORDER_PLACED: {level.custom_info['levels_by_state'].get('OPEN_ORDER_PLACED', 0)}",
f"OPEN_ORDER_FILLED: {level.custom_info['levels_by_state'].get('OPEN_ORDER_FILLED', 0)}",
f"CLOSE_ORDER_PLACED: {level.custom_info['levels_by_state'].get('CLOSE_ORDER_PLACED', 0)}",
f"COMPLETE: {level.custom_info['levels_by_state'].get('COMPLETE', 0)}"
]
order_stats_data = [
f"Total: {sum(len(level.custom_info[k]) for k in ['filled_orders', 'failed_orders', 'canceled_orders'])}",
f"Filled: {len(level.custom_info['filled_orders'])}",
f"Failed: {len(level.custom_info['failed_orders'])}",
f"Canceled: {len(level.custom_info['canceled_orders'])}"
]
perf_metrics_data = [
f"Buy Vol: {level.custom_info['realized_buy_size_quote']:.4f}",
f"Sell Vol: {level.custom_info['realized_sell_size_quote']:.4f}",
f"R. PnL: {level.custom_info['realized_pnl_quote']:.4f}",
f"R. Fees: {level.custom_info['realized_fees_quote']:.4f}",
f"P. PnL: {level.custom_info['position_pnl_quote']:.4f}",
f"Position: {level.custom_info['position_size_quote']:.4f}"
]
# Build rows with perfect alignment
max_rows = max(len(level_dist_data), len(order_stats_data), len(perf_metrics_data))
for i in range(max_rows):
col1 = level_dist_data[i] if i < len(level_dist_data) else ""
col2 = order_stats_data[i] if i < len(order_stats_data) else ""
col3 = perf_metrics_data[i] if i < len(perf_metrics_data) else ""
row = "│ " + col1
row += " " * (col1_end - len(col1) - 2) # -2 for the "│ " at the start
row += "│ " + col2
row += " " * (col_width - len(col2) - 2) # -2 for the "│ " before col2
row += "│ " + col3
row += " " * (col_width - len(col3) - 2) # -2 for the "│ " before col3
row += "│"
status.append(row)
# Liquidity line with perfect alignment
status.append("├" + "─" * total_width + "┤")
liquidity_line = f"│ Open Liquidity: {level.custom_info['open_liquidity_placed']:.4f} │ Close Liquidity: {level.custom_info['close_liquidity_placed']:.4f} │"
liquidity_line += " " * (total_width - len(liquidity_line) + 1) # +1 for correct right border alignment
liquidity_line += "│"
status.append(liquidity_line)
status.append("└" + "─" * total_width + "┘")
return status
```Shown in full with attribution under the source's licence. Licence: Apache-2.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.