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

symbol

str

Instrument ID

side

OrderSide

Parent order side (BUY or SELL)

type

OrderType

Parent order type (MARKET or LIMIT)

amount

Decimal

Parent order quantity

price

Decimal (optional)

Parent limit price (required when type=LIMIT)

tp_order_type

OrderType

Take-profit order type (MARKET or LIMIT)

tp_trigger_price

Decimal

Price that activates the take-profit leg

tp_price

Decimal (optional)

Take-profit limit price (required when tp_order_type=LIMIT)

sl_order_type

OrderType

Stop-loss order type (MARKET or LIMIT)

sl_trigger_price

Decimal

Price that activates the stop-loss leg

sl_price

Decimal (optional)

Stop-loss limit price (required when sl_order_type=LIMIT)

account_type

AccountType (optional)

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.