Market Order Entry and Delayed Position Closing in Hummingbot
Summary
This Hummingbot controller example opens a market position in a configured direction, sizing the order from a quote-currency amount and the current mid-price. It records when the opening action is submitted, then waits for a configurable delay before placing a closing-side market order for the held position.
The close can target the full position or half of it. A configuration option also changes the position action from closing to opening in the opposite direction, allowing the second order to add an opposing position in hedge mode. The example demonstrates order-action construction and position lookup; it does not provide a signal for when to trade or evidence of profitability. Its simplicity also leaves operational behavior, such as handling failed or incomplete opening orders and repeated controller cycles, outside the example.
Key ideas
- The controller sizes the entry by dividing a quote-currency allocation by the current mid-price.
- It submits a market entry once and tracks the time of that action.
- After a configured delay, it submits a market order in the opposite direction.
- The close may cover the entire position or half of it.
- An option can make the opposite-side order open a position instead of closing one.
Tags
Full text
# BasicOrderOpenClose
# BasicOrderOpenClose
## Source (Apache-2.0)
```python
from decimal import Decimal
from hummingbot.core.data_type.common import MarketDict, PositionAction, PositionMode, PriceType, TradeType
from hummingbot.strategy_v2.controllers import ControllerBase, ControllerConfigBase
from hummingbot.strategy_v2.executors.order_executor.data_types import ExecutionStrategy, OrderExecutorConfig
from hummingbot.strategy_v2.models.executor_actions import CreateExecutorAction, ExecutorAction
class BasicOrderOpenCloseExampleConfig(ControllerConfigBase):
controller_name: str = "examples.basic_order_open_close_example"
controller_type: str = "generic"
connector_name: str = "binance_perpetual"
trading_pair: str = "WLD-USDT"
side: TradeType = TradeType.BUY
position_mode: PositionMode = PositionMode.HEDGE
leverage: int = 50
close_order_delay: int = 10
open_short_to_close_long: bool = False
close_partial_position: bool = False
amount_quote: Decimal = Decimal("20")
def update_markets(self, markets: MarketDict) -> MarketDict:
return markets.add_or_update(self.connector_name, self.trading_pair)
class BasicOrderOpenClose(ControllerBase):
def __init__(self, config: BasicOrderOpenCloseExampleConfig, *args, **kwargs):
super().__init__(config, *args, **kwargs)
self.config = config
self.open_order_placed = False
self.closed_order_placed = False
self.last_timestamp = 0
self.open_side = self.config.side
self.close_side = TradeType.SELL if self.config.side == TradeType.BUY else TradeType.BUY
def get_position(self, connector_name, trading_pair):
for position in self.positions_held:
if position.connector_name == connector_name and position.trading_pair == trading_pair:
return position
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 not self.open_order_placed:
config = OrderExecutorConfig(
timestamp=self.market_data_provider.time(),
connector_name=self.config.connector_name,
trading_pair=self.config.trading_pair,
side=self.config.side,
amount=self.config.amount_quote / mid_price,
execution_strategy=ExecutionStrategy.MARKET,
position_action=PositionAction.OPEN,
price=mid_price,
)
self.open_order_placed = True
self.last_timestamp = self.market_data_provider.time()
return [CreateExecutorAction(
controller_id=self.config.id,
executor_config=config)]
else:
if self.market_data_provider.time() - self.last_timestamp > self.config.close_order_delay and not self.closed_order_placed:
current_position = self.get_position(self.config.connector_name, self.config.trading_pair)
if current_position is None:
self.logger().info("The original position is not found, can close the position")
else:
amount = current_position.amount / 2 if self.config.close_partial_position else current_position.amount
config = OrderExecutorConfig(
timestamp=self.market_data_provider.time(),
connector_name=self.config.connector_name,
trading_pair=self.config.trading_pair,
side=self.close_side,
amount=amount,
execution_strategy=ExecutionStrategy.MARKET,
position_action=PositionAction.OPEN if self.config.open_short_to_close_long else PositionAction.CLOSE,
price=mid_price,
)
self.closed_order_placed = True
return [CreateExecutorAction(
controller_id=self.config.id,
executor_config=config)]
return []
async def update_processed_data(self):
pass
```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.