TWAP Orders¶
TWAP (Time-Weighted Average Price) is an algorithmic order type that splits a large order into smaller child orders executed evenly over a specified time window. This reduces market impact and achieves an average execution price close to the time-weighted average.
Overview¶
Use create_twap() to submit a TWAP order and
cancel_twap() to cancel one that is still running.
oid = self.create_twap(
symbol="BTCUSDT-PERP.BYBIT",
side=OrderSide.BUY,
amount=Decimal("0.3"), # total quantity to execute
duration=300, # spread over 300 seconds (5 minutes)
wait=3, # place a child order every 3 seconds
)
Parameters¶
Parameter |
Type |
Description |
|---|---|---|
|
|
Instrument ID (e.g. |
|
|
|
|
|
Total quantity to execute |
|
|
Total execution window in seconds |
|
|
Interval between child orders in seconds |
|
|
Override the default account type |
TWAP Status¶
The TWAP order lifecycle has five states:
RUNNING: actively placing child ordersCANCELING: cancel has been requested but child orders may still be in-flightCANCELLED: all remaining child orders have been cancelledFINISHED: all child orders have been filledFAILED: a child order failed; the entire TWAP is aborted
Full Example¶
The following example subscribes to BTCUSDT-PERP.BYBIT level-1 order book data, then places
a single TWAP BUY order of 0.3 BTC spread over 5 minutes with a 3-second interval.
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
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 TwapDemo(Strategy):
def __init__(self):
super().__init__()
self.signal = True
def on_start(self):
self.subscribe_bookl1(symbols=["BTCUSDT-PERP.BYBIT"])
def on_canceled_order(self, order: Order):
self.log.info(f"canceled: {order.oid}")
def on_accepted_order(self, order: Order):
self.log.info(f"accepted: {order.oid}")
def on_filled_order(self, order: Order):
self.log.info(f"filled: {order.oid}")
def on_bookl1(self, bookl1: BookL1):
if self.signal:
self.create_twap(
symbol="BTCUSDT-PERP.BYBIT",
side=OrderSide.BUY,
amount=Decimal("0.3"),
duration=60 * 5, # 5 minutes
wait=3, # every 3 seconds
)
self.signal = False
position = self.cache.get_position("BTCUSDT-PERP.BYBIT")
self.log.info(f"position: {position}")
config = Config(
strategy_id="bybit_twap",
user_id="user_test",
strategy=TwapDemo(),
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),
PublicConnectorConfig(account_type=BybitAccountType.SPOT_TESTNET),
]
},
private_conn_config={
ExchangeType.BYBIT: [
PrivateConnectorConfig(account_type=BybitAccountType.UNIFIED_TESTNET)
]
},
)
engine = Engine(config)
if __name__ == "__main__":
try:
engine.start()
finally:
engine.dispose()
Cancelling a TWAP¶
Call cancel_twap() with the OID returned by
create_twap:
class TwapCancelDemo(Strategy):
def __init__(self):
super().__init__()
self.twap_oid = None
def on_bookl1(self, bookl1: BookL1):
if self.twap_oid is None:
self.twap_oid = self.create_twap(
symbol="BTCUSDT-PERP.BYBIT",
side=OrderSide.BUY,
amount=Decimal("0.3"),
duration=300,
wait=5,
)
# Cancel after some condition is met
elif <some_exit_condition>:
self.cancel_twap(
symbol="BTCUSDT-PERP.BYBIT",
oid=self.twap_oid,
)
self.twap_oid = None
See also
Orders for a full description of the TWAP order lifecycle diagram.