Take-Profit & Stop-Loss Orders¶
NexusTrader supports attaching take-profit (TP) and stop-loss (SL) legs to a parent order in a
single API call via create_tp_sl_order(). When the parent
order fills, the exchange automatically activates both conditional legs.
Overview¶
self.create_tp_sl_order(
symbol="BTCUSDT-PERP.BYBIT",
side=OrderSide.BUY,
type=OrderType.MARKET,
amount=Decimal("0.001"),
# Take-profit: trigger at 0.1 % above ask
tp_order_type=OrderType.MARKET,
tp_trigger_price=self.price_to_precision(symbol, bookl1.ask * 1.001),
# Stop-loss: trigger at 0.1 % below bid
sl_order_type=OrderType.MARKET,
sl_trigger_price=self.price_to_precision(symbol, bookl1.bid * 0.999),
)
Parameters¶
Parameter |
Type |
Description |
|---|---|---|
|
|
Instrument ID |
|
|
Parent order side ( |
|
|
Parent order type ( |
|
|
Parent order quantity |
|
|
Parent limit price (required when |
|
|
Take-profit order type ( |
|
|
Price that activates the take-profit leg |
|
|
Take-profit limit price (required when |
|
|
Stop-loss order type ( |
|
|
Price that activates the stop-loss leg |
|
|
Stop-loss limit price (required when |
|
|
Override the default account type |
Note
Support for TP/SL orders varies by exchange. Currently fully supported on Bybit and OKX. Check your exchange documentation for conditional order requirements.
Full Example¶
from decimal import Decimal
from nexustrader.constants import settings
from nexustrader.config import (
Config,
PublicConnectorConfig,
PrivateConnectorConfig,
BasicConfig,
)
from nexustrader.strategy import Strategy
from nexustrader.constants import ExchangeType, OrderSide, OrderType
from nexustrader.exchange import BybitAccountType
from nexustrader.schema import BookL1, Order
from nexustrader.engine import Engine
BYBIT_API_KEY = settings.BYBIT.TESTNET.API_KEY
BYBIT_SECRET = settings.BYBIT.TESTNET.SECRET
class TpSlDemo(Strategy):
def __init__(self):
super().__init__()
self.signal = True
def on_start(self):
self.subscribe_bookl1(symbols=["BTCUSDT-PERP.BYBIT"])
def on_failed_order(self, order: Order):
self.log.info(str(order))
def on_accepted_order(self, order: Order):
self.log.info(str(order))
def on_filled_order(self, order: Order):
self.log.info(str(order))
def on_bookl1(self, bookl1: BookL1):
if self.signal:
symbol = "BTCUSDT-PERP.BYBIT"
self.create_tp_sl_order(
symbol=symbol,
side=OrderSide.BUY,
type=OrderType.MARKET,
amount=Decimal("0.001"),
tp_order_type=OrderType.MARKET,
tp_trigger_price=self.price_to_precision(
symbol, bookl1.ask * 1.001
),
sl_order_type=OrderType.MARKET,
sl_trigger_price=self.price_to_precision(
symbol, bookl1.bid * 0.999
),
)
self.signal = False
config = Config(
strategy_id="bybit_tp_sl_order",
user_id="user_test",
strategy=TpSlDemo(),
basic_config={
ExchangeType.BYBIT: BasicConfig(
api_key=BYBIT_API_KEY,
secret=BYBIT_SECRET,
testnet=True,
)
},
public_conn_config={
ExchangeType.BYBIT: [
PublicConnectorConfig(account_type=BybitAccountType.LINEAR_TESTNET)
]
},
private_conn_config={
ExchangeType.BYBIT: [
PrivateConnectorConfig(account_type=BybitAccountType.UNIFIED_TESTNET)
]
},
)
engine = Engine(config)
if __name__ == "__main__":
try:
engine.start()
finally:
engine.dispose()
Price Precision Helper¶
Always use price_to_precision() to round prices to the
exchange’s tick size before passing them to order methods:
tp_price = self.price_to_precision("BTCUSDT-PERP.BYBIT", bookl1.ask * 1.005)
sl_price = self.price_to_precision("BTCUSDT-PERP.BYBIT", bookl1.bid * 0.995)
Similarly use amount_to_precision() for quantities.
See also
Orders for the full order status lifecycle.