Skip to content

Commit 37af1de

Browse files
committed
Add OKX WebSocket reconnect on stale order book
okx_order_book: track last_update_time on snapshot and update messages. liquidation_bid_bot: check staleness at top of each loop iteration and reconnect if no update received in 60s.
1 parent 6a4415a commit 37af1de

2 files changed

Lines changed: 54 additions & 10 deletions

File tree

keeper_bots/liquidation_bid_bot.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,27 @@ async def run_liquidation_bid_bot():
599599

600600
log.info("Running liquidation bid bot in %s environment", trade_env.name)
601601

602+
async def reconnect_order_book() -> bool:
603+
"""Reconnect the OKX order book WebSocket. Returns True on success."""
604+
log.warning("OKX order book stale — reconnecting WebSocket")
605+
okx_order_book.initialized = False
606+
okx_order_book.book = {}
607+
try:
608+
await okx_order_book.connect()
609+
await okx_order_book.subscribe()
610+
except Exception as err:
611+
log.error("OKX WebSocket reconnect failed: %s", err)
612+
return False
613+
waited = 0.0
614+
while not okx_order_book.initialized and waited < 30:
615+
await asyncio.sleep(0.5)
616+
waited += 0.5
617+
if okx_order_book.initialized:
618+
log.info("OKX order book reconnected mid=%.4f", okx_order_book.mid_price())
619+
return True
620+
log.error("OKX order book did not receive snapshot after reconnect")
621+
return False
622+
602623
stablecoin_symbol = "BYC" # Circuit stablecoin (BYC)
603624

604625
# BYC proxy asset to be used for hedging
@@ -653,27 +674,38 @@ async def run_liquidation_bid_bot():
653674

654675
try:
655676
while True:
677+
if time.monotonic() - okx_order_book.last_update_time > 60:
678+
if not await reconnect_order_book():
679+
await asyncio.sleep(CONTINUE_DELAY)
680+
continue
681+
656682
await rpc_client.set_fee_per_cost()
657683

658684
if time.monotonic() - last_okx_heartbeat >= HEARTBEAT_INTERVAL:
659685
try:
660686
await accountAPI.get_account_balance()
661687
log.info("OKX API heartbeat: balance query successful")
662688
except Exception as err:
663-
log.warning("OKX API heartbeat failed. %s: %s", type(err).__name__, err)
689+
log.warning(
690+
"OKX API heartbeat failed. %s: %s", type(err).__name__, err
691+
)
664692
last_okx_heartbeat = time.monotonic()
665693

666694
try:
667695
state = await rpc_client.upkeep_state(vaults=True)
668696
except Exception as err:
669-
log.error("Failed to get state of vaults. %s: %s", type(err).__name__, err)
697+
log.error(
698+
"Failed to get state of vaults. %s: %s", type(err).__name__, err
699+
)
670700
await asyncio.sleep(CONTINUE_DELAY)
671701
continue
672702

673703
vaults_in_liquidation = state.get("vaults_in_liquidation", [])
674704

675705
if not vaults_in_liquidation:
676-
log.info("No vaults in liquidation. Sleeping for %s seconds", RUN_INTERVAL)
706+
log.info(
707+
"No vaults in liquidation. Sleeping for %s seconds", RUN_INTERVAL
708+
)
677709
await asyncio.sleep(RUN_INTERVAL)
678710
continue
679711

@@ -683,7 +715,9 @@ async def run_liquidation_bid_bot():
683715
try:
684716
balances = await rpc_client.wallet_balances()
685717
except Exception as err:
686-
log.error("Failed to get wallet balances. %s: %s", type(err).__name__, err)
718+
log.error(
719+
"Failed to get wallet balances. %s: %s", type(err).__name__, err
720+
)
687721

688722
available_xch_amount = balances.get("xch", None)
689723
available_byc_amount = balances.get("byc", None)
@@ -758,7 +792,9 @@ async def run_liquidation_bid_bot():
758792
statutes["implemented_statutes"]["VAULT_MINIMUM_DEBT"]
759793
)
760794
liquidation_ratio_pct = int(
761-
statutes["implemented_statutes"]["VAULT_LIQUIDATION_RATIO_PCT"]
795+
statutes["implemented_statutes"][
796+
"VAULT_LIQUIDATION_RATIO_PCT"
797+
]
762798
)
763799
except Exception:
764800
log.error(
@@ -808,7 +844,9 @@ async def run_liquidation_bid_bot():
808844
response = await rpc_client.vault_deposit(deposit_amount)
809845
except Exception as err:
810846
log.error(
811-
"Failed to deposit to vault. %s: %s", type(err).__name__, err
847+
"Failed to deposit to vault. %s: %s",
848+
type(err).__name__,
849+
err,
812850
)
813851
else:
814852
if response.get("status") != "success":
@@ -818,12 +856,16 @@ async def run_liquidation_bid_bot():
818856
response,
819857
)
820858
else:
821-
log.info("Deposited %.12f XCH", deposit_amount / MOJOS_PER_XCH)
859+
log.info(
860+
"Deposited %.12f XCH", deposit_amount / MOJOS_PER_XCH
861+
)
822862
try:
823863
response = await rpc_client.vault_borrow(borrow_amount)
824864
except Exception as err:
825865
log.error(
826-
"Failed to borrow BYC. %s: %s", type(err).__name__, err
866+
"Failed to borrow BYC. %s: %s",
867+
type(err).__name__,
868+
err,
827869
)
828870
if response.get("status") != "success":
829871
log.error(

keeper_bots/okx_order_book.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import time
34
from datetime import datetime
45
from pprint import pprint
56
import asyncio
@@ -62,6 +63,7 @@ def __init__(self, sym, uquote, url, verbose=False, logger=None):
6263
self.book = {}
6364
self.url = url
6465
self.initialized = False
66+
self.last_update_time: float = 0.0
6567
self._lock = asyncio.Lock()
6668
self.logger = logger or logging.getLogger(__name__)
6769

@@ -162,6 +164,7 @@ def __call__(self, message):
162164
self.book["bids"][price_float] = depth[1]
163165

164166
self.initialized = True
167+
self.last_update_time = time.monotonic()
165168

166169
elif message["action"] == "update":
167170
if self.verbose:
@@ -185,7 +188,6 @@ def __call__(self, message):
185188
else:
186189
# Remove price level if volume is 0
187190
self.book[side].pop(price_float, None)
188-
189191
## Validate order book depth
190192
# asks_depth = len(self.book.get("asks", {}))
191193
# bids_depth = len(self.book.get("bids", {}))
@@ -198,7 +200,7 @@ def __call__(self, message):
198200
# self.logger.warning(
199201
# f"OKX order book bid depth is {bids_depth}, expected 400"
200202
# )
201-
203+
self.last_update_time = time.monotonic()
202204
else:
203205
raise Exception(
204206
f"Unknown action {message['action']} returned in callback"

0 commit comments

Comments
 (0)