Skip to content

Commit 280dac6

Browse files
authored
Add execution.fast support for Bybit (#2165)
1 parent 6f39a83 commit 280dac6

File tree

5 files changed

+79
-17
lines changed

5 files changed

+79
-17
lines changed

nautilus_trader/adapters/bybit/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class BybitExecClientConfig(LiveExecClientConfig, frozen=True):
9696
use_gtd : bool, default False
9797
If False, then GTD time in force will be remapped to GTC
9898
(this is useful if managing GTD orders locally).
99+
use_ws_execution_fast : bool, default False
100+
If use fast execution stream.
99101
use_ws_trade_api : bool, default False
100102
If the client is using websocket to send order requests.
101103
use_http_batch_api : bool, default False
@@ -126,6 +128,7 @@ class BybitExecClientConfig(LiveExecClientConfig, frozen=True):
126128
demo: bool = False
127129
testnet: bool = False
128130
use_gtd: bool = False # Not supported on Bybit
131+
use_ws_execution_fast: bool = False
129132
use_ws_trade_api: bool = False
130133
use_http_batch_api: bool = False
131134
max_retries: PositiveInt | None = None

nautilus_trader/adapters/bybit/execution.py

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
from __future__ import annotations
1717

18-
from decimal import Decimal
1918
from typing import TYPE_CHECKING
2019

2120
import msgspec
@@ -39,6 +38,8 @@
3938
from nautilus_trader.adapters.bybit.http.errors import should_retry
4039
from nautilus_trader.adapters.bybit.schemas.common import BYBIT_PONG
4140
from nautilus_trader.adapters.bybit.schemas.ws import BybitWsAccountExecution
41+
from nautilus_trader.adapters.bybit.schemas.ws import BybitWsAccountExecutionFast
42+
from nautilus_trader.adapters.bybit.schemas.ws import BybitWsAccountExecutionFastMsg
4243
from nautilus_trader.adapters.bybit.schemas.ws import BybitWsAccountExecutionMsg
4344
from nautilus_trader.adapters.bybit.schemas.ws import BybitWsAccountOrderMsg
4445
from nautilus_trader.adapters.bybit.schemas.ws import BybitWsAccountWalletMsg
@@ -170,11 +171,13 @@ def __init__(
170171
self._product_types = product_types
171172
self._use_gtd = config.use_gtd
172173
self._use_ws_trade_api = config.use_ws_trade_api
174+
self._use_ws_execution_fast = config.use_ws_execution_fast
173175
self._use_http_batch_api = config.use_http_batch_api
174176

175177
self._log.info(f"Account type: {account_type_to_str(account_type)}", LogColor.BLUE)
176178
self._log.info(f"Product types: {[p.value for p in product_types]}", LogColor.BLUE)
177179
self._log.info(f"{config.use_gtd=}", LogColor.BLUE)
180+
self._log.info(f"{config.use_ws_execution_fast=}", LogColor.BLUE)
178181
self._log.info(f"{config.use_ws_trade_api=}", LogColor.BLUE)
179182
self._log.info(f"{config.use_http_batch_api=}", LogColor.BLUE)
180183
self._log.info(f"{config.ws_trade_timeout_secs=}", LogColor.BLUE)
@@ -248,6 +251,9 @@ def __init__(
248251

249252
self._decoder_ws_account_order_update = msgspec.json.Decoder(BybitWsAccountOrderMsg)
250253
self._decoder_ws_account_execution_update = msgspec.json.Decoder(BybitWsAccountExecutionMsg)
254+
self._decoder_ws_account_execution_fast_update = msgspec.json.Decoder(
255+
BybitWsAccountExecutionFastMsg,
256+
)
251257
# self._decoder_ws_account_position_update = msgspec.json.Decoder(BybitWsAccountPositionMsg)
252258
self._decoder_ws_account_wallet_update = msgspec.json.Decoder(BybitWsAccountWalletMsg)
253259

@@ -269,10 +275,15 @@ async def _connect(self) -> None:
269275
await self._update_account_state()
270276

271277
await self._ws_client.connect()
272-
await self._ws_client.subscribe_executions_update()
278+
273279
await self._ws_client.subscribe_orders_update()
274280
await self._ws_client.subscribe_wallet_update()
275281

282+
if self._use_ws_execution_fast:
283+
await self._ws_client.subscribe_executions_fast_update()
284+
else:
285+
await self._ws_client.subscribe_executions_update()
286+
276287
if self._use_ws_trade_api:
277288
await self._ws_order_client.connect()
278289

@@ -991,6 +1002,8 @@ def _handle_ws_message_private(self, raw: bytes) -> None:
9911002
self._handle_account_order_update(raw)
9921003
elif "execution" in ws_message.topic:
9931004
self._handle_account_execution_update(raw)
1005+
elif "execution.fast" in ws_message.topic:
1006+
self._handle_account_execution_fast_update(raw)
9941007
elif "wallet" == ws_message.topic:
9951008
self._handle_account_wallet_update(raw)
9961009
else:
@@ -1006,7 +1019,18 @@ def _handle_account_execution_update(self, raw: bytes) -> None:
10061019
except Exception as e:
10071020
self._log.exception(f"Failed to handle account execution update: {e}", e)
10081021

1009-
def _process_execution(self, execution: BybitWsAccountExecution) -> None:
1022+
def _handle_account_execution_fast_update(self, raw: bytes) -> None:
1023+
try:
1024+
msg = self._decoder_ws_account_execution_fast_update.decode(raw)
1025+
for trade in msg.data:
1026+
self._process_execution(trade)
1027+
except Exception as e:
1028+
self._log.exception(f"Failed to handle account execution update: {e}", e)
1029+
1030+
def _process_execution(
1031+
self,
1032+
execution: BybitWsAccountExecution | BybitWsAccountExecutionFast,
1033+
) -> None:
10101034
instrument_id = self._get_cached_instrument_id(execution.symbol, execution.category)
10111035
client_order_id = ClientOrderId(execution.orderLinkId) if execution.orderLinkId else None
10121036
venue_order_id = VenueOrderId(execution.orderId)
@@ -1047,6 +1071,13 @@ def _process_execution(self, execution: BybitWsAccountExecution) -> None:
10471071
if instrument is None:
10481072
raise ValueError(f"Cannot handle trade event: instrument {instrument_id} not found")
10491073

1074+
quote_currency = instrument.quote_currency
1075+
fee = instrument.maker_fee if execution.isMaker else instrument.taker_fee
1076+
1077+
last_qty = Quantity(float(execution.execQty), instrument.size_precision)
1078+
last_px = Price(float(execution.execPrice), instrument.price_precision)
1079+
commission_amount = last_qty * last_px * fee
1080+
10501081
self.generate_order_filled(
10511082
strategy_id=strategy_id,
10521083
instrument_id=instrument_id,
@@ -1056,10 +1087,10 @@ def _process_execution(self, execution: BybitWsAccountExecution) -> None:
10561087
trade_id=TradeId(execution.execId),
10571088
order_side=order_side,
10581089
order_type=order_type,
1059-
last_qty=Quantity(float(execution.execQty), instrument.size_precision),
1060-
last_px=Price(float(execution.execPrice), instrument.price_precision),
1061-
quote_currency=instrument.quote_currency,
1062-
commission=Money(Decimal(execution.execFee), instrument.quote_currency),
1090+
last_qty=last_qty,
1091+
last_px=last_px,
1092+
quote_currency=quote_currency,
1093+
commission=Money(commission_amount, quote_currency),
10631094
liquidity_side=LiquiditySide.MAKER if execution.isMaker else LiquiditySide.TAKER,
10641095
ts_event=millis_to_nanos(float(execution.execTime)),
10651096
)

nautilus_trader/adapters/bybit/http/account.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,26 +64,26 @@
6464
from nautilus_trader.adapters.bybit.endpoints.trade.trade_history import BybitTradeHistoryEndpoint
6565
from nautilus_trader.adapters.bybit.endpoints.trade.trade_history import BybitTradeHistoryGetParams
6666
from nautilus_trader.adapters.bybit.http.client import BybitHttpClient
67-
from nautilus_trader.adapters.bybit.schemas.account.balance import BybitWalletBalance
68-
from nautilus_trader.adapters.bybit.schemas.account.fee_rate import BybitFeeRate
69-
from nautilus_trader.adapters.bybit.schemas.order import BybitAmendOrder
70-
from nautilus_trader.adapters.bybit.schemas.order import BybitCancelOrder
71-
from nautilus_trader.adapters.bybit.schemas.order import BybitOrder
72-
from nautilus_trader.adapters.bybit.schemas.order import BybitPlaceOrderResponse
73-
from nautilus_trader.adapters.bybit.schemas.order import BybitSetTradingStopResponse
74-
from nautilus_trader.adapters.bybit.schemas.position import BybitPositionStruct
75-
from nautilus_trader.adapters.bybit.schemas.trade import BybitExecution
7667
from nautilus_trader.common.component import LiveClock
7768
from nautilus_trader.core.correctness import PyCondition
7869

7970

8071
if TYPE_CHECKING:
8172
from nautilus_trader.adapters.bybit.common.enums import BybitMarginMode
8273
from nautilus_trader.adapters.bybit.http.client import BybitHttpClient
74+
from nautilus_trader.adapters.bybit.schemas.account.balance import BybitWalletBalance
75+
from nautilus_trader.adapters.bybit.schemas.account.fee_rate import BybitFeeRate
8376
from nautilus_trader.adapters.bybit.schemas.account.info import BybitAccountInfo
8477
from nautilus_trader.adapters.bybit.schemas.account.set_leverage import BybitSetLeverageResponse
8578
from nautilus_trader.adapters.bybit.schemas.account.set_margin_mode import BybitSetMarginModeResponse
8679
from nautilus_trader.adapters.bybit.schemas.account.switch_mode import BybitSwitchModeResponse
80+
from nautilus_trader.adapters.bybit.schemas.order import BybitAmendOrder
81+
from nautilus_trader.adapters.bybit.schemas.order import BybitCancelOrder
82+
from nautilus_trader.adapters.bybit.schemas.order import BybitOrder
83+
from nautilus_trader.adapters.bybit.schemas.order import BybitPlaceOrderResponse
84+
from nautilus_trader.adapters.bybit.schemas.order import BybitSetTradingStopResponse
85+
from nautilus_trader.adapters.bybit.schemas.position import BybitPositionStruct
86+
from nautilus_trader.adapters.bybit.schemas.trade import BybitExecution
8787
from nautilus_trader.common.component import LiveClock
8888

8989
# fmt: on

nautilus_trader/adapters/bybit/schemas/ws.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,34 @@ class BybitWsAccountExecutionMsg(msgspec.Struct):
720720
data: list[BybitWsAccountExecution]
721721

722722

723+
################################################################################
724+
# Private - Account Execution Fast
725+
################################################################################
726+
727+
728+
class BybitWsAccountExecutionFast(msgspec.Struct):
729+
category: BybitProductType
730+
symbol: str
731+
orderId: str
732+
isMaker: bool
733+
orderLinkId: str
734+
side: BybitOrderSide
735+
execId: str
736+
execPrice: str
737+
execQty: str
738+
execTime: str
739+
seq: int
740+
741+
orderType: BybitOrderType = BybitOrderType.UNKNOWN
742+
stopOrderType: BybitStopOrderType = BybitStopOrderType.NONE
743+
744+
745+
class BybitWsAccountExecutionFastMsg(msgspec.Struct):
746+
topic: str
747+
creationTime: int
748+
data: list[BybitWsAccountExecutionFast]
749+
750+
723751
################################################################################
724752
# Private - Account Wallet
725753
################################################################################

nautilus_trader/adapters/bybit/websocket/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ async def subscribe_executions_update(self) -> None:
375375
subscription = "execution"
376376
await self._subscribe(subscription)
377377

378-
async def subscribe_executions_update_fast(self) -> None:
378+
async def subscribe_executions_fast_update(self) -> None:
379379
subscription = "execution.fast"
380380
await self._subscribe(subscription)
381381

0 commit comments

Comments
 (0)