@@ -3566,6 +3566,63 @@ def test_remote_trade_update_accepts_raw_okx_trade_aliases_and_fee():
35663566 broker .stop ()
35673567
35683568
3569+ def test_remote_trade_update_accepts_raw_okx_orders_envelope_rows ():
3570+ """Raw OKX private-channel envelopes must be flattened before fill booking."""
3571+ symbol = "BTC-USDT-SWAP"
3572+ client = FakeBtApiClient (
3573+ history = {symbol : [make_bar (0 , 100.0 , 101.0 , 99.0 , 100.5 )]},
3574+ )
3575+ store = make_store (api = client , provider = "okx" )
3576+ data = store .getdata (dataname = symbol )
3577+ broker = store .getbroker (account_refresh_interval = 60.0 , positions_refresh_interval = 60.0 )
3578+
3579+ data ._start ()
3580+ assert data .load () is True
3581+ broker .start ()
3582+ broker .setcommission (
3583+ commission = 10.0 ,
3584+ commtype = bt .CommInfoBase .COMM_FIXED ,
3585+ )
3586+ try :
3587+ order = broker .buy (
3588+ owner = None ,
3589+ data = data ,
3590+ size = 1 ,
3591+ price = 101.0 ,
3592+ exectype = bt .Order .Limit ,
3593+ )
3594+
3595+ client .push_broker_update (
3596+ {
3597+ "kind" : "trade" ,
3598+ "arg" : {"channel" : "orders" },
3599+ "id" : "okx-message-1" ,
3600+ "data" : [
3601+ {
3602+ "ordId" : "btapi-1" ,
3603+ "tradeId" : "okx-trade-1" ,
3604+ "instId" : symbol ,
3605+ "side" : "buy" ,
3606+ "fillSz" : "1" ,
3607+ "fillPx" : "101.5" ,
3608+ "fee" : "-0.25" ,
3609+ "feeCcy" : "USDT" ,
3610+ }
3611+ ],
3612+ }
3613+ )
3614+
3615+ broker .next ()
3616+
3617+ assert order .status == bt .Order .Completed
3618+ assert order .executed .size == pytest .approx (1.0 )
3619+ assert order .executed .price == pytest .approx (101.5 )
3620+ assert order .executed .comm == pytest .approx (0.25 )
3621+ assert broker .positions [symbol ].size == pytest .approx (1.0 )
3622+ finally :
3623+ broker .stop ()
3624+
3625+
35693626def test_remote_trade_update_accepts_raw_bybit_v5_execution_aliases_and_fee ():
35703627 """Raw Bybit V5 execution events must use exchange fill fields and exact fees."""
35713628 symbol = "BTCUSDT"
@@ -3667,6 +3724,69 @@ def test_remote_trade_update_accepts_raw_bybit_v5_execution_aliases_and_fee():
36673724 broker .stop ()
36683725
36693726
3727+ def test_remote_trade_update_accepts_raw_bybit_v5_execution_envelope_rows ():
3728+ """Raw Bybit V5 execution envelopes must be flattened and booked as fills."""
3729+ symbol = "BTCUSDT"
3730+ client = FakeBtApiClient (
3731+ history = {symbol : [make_bar (0 , 100.0 , 101.0 , 99.0 , 100.5 )]},
3732+ )
3733+ store = make_store (api = client , provider = "bybit" )
3734+ data = store .getdata (dataname = symbol )
3735+ broker = store .getbroker (account_refresh_interval = 60.0 , positions_refresh_interval = 60.0 )
3736+
3737+ data ._start ()
3738+ assert data .load () is True
3739+ broker .start ()
3740+ broker .setcommission (
3741+ commission = 10.0 ,
3742+ commtype = bt .CommInfoBase .COMM_FIXED ,
3743+ )
3744+ try :
3745+ order = broker .buy (
3746+ owner = None ,
3747+ data = data ,
3748+ size = 1 ,
3749+ price = 101.0 ,
3750+ exectype = bt .Order .Limit ,
3751+ )
3752+
3753+ client .push_broker_update (
3754+ {
3755+ "kind" : "trade" ,
3756+ "topic" : "execution" ,
3757+ "id" : "bybit-message-1" ,
3758+ "creationTime" : 1746270400355 ,
3759+ "data" : [
3760+ {
3761+ "category" : "linear" ,
3762+ "symbol" : symbol ,
3763+ "orderId" : "btapi-1" ,
3764+ "orderLinkId" : "btapi-1" ,
3765+ "side" : "Buy" ,
3766+ "execQty" : "1" ,
3767+ "execPrice" : "101.5" ,
3768+ "execFee" : "0.15" ,
3769+ "execId" : "bybit-exec-1" ,
3770+ "execTime" : "1746270400353" ,
3771+ "feeCurrency" : "USDT" ,
3772+ "isMaker" : False ,
3773+ }
3774+ ],
3775+ }
3776+ )
3777+
3778+ broker .next ()
3779+
3780+ assert order .status == bt .Order .Completed
3781+ assert order .executed .size == pytest .approx (1.0 )
3782+ assert order .executed .price == pytest .approx (101.5 )
3783+ assert order .executed .comm == pytest .approx (0.15 )
3784+ assert broker .positions [symbol ].size == pytest .approx (1.0 )
3785+ assert broker .positions [symbol ].price == pytest .approx (101.5 )
3786+ finally :
3787+ broker .stop ()
3788+
3789+
36703790def test_remote_trade_update_without_price_is_ignored_not_zero_filled ():
36713791 """Malformed fills must not execute locally at price zero."""
36723792 client = FakeBtApiClient (
0 commit comments