stability and error cleaning. rule to read specs
This commit is contained in:
@@ -98,6 +98,14 @@ class BybitInterface(ExchangeInterface):
|
||||
testnet=self.test_mode
|
||||
)
|
||||
|
||||
# Sync server time for REST client and prefer larger recv_window
|
||||
try:
|
||||
self.rest_client.sync_server_time()
|
||||
# Increase REST recv window defensively
|
||||
self.rest_client.recv_window_ms = max(self.rest_client.recv_window_ms, 20000)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Test pybit connection first
|
||||
try:
|
||||
account_info = self.session.get_wallet_balance(accountType="UNIFIED")
|
||||
|
||||
@@ -40,6 +40,9 @@ class BybitRestClient:
|
||||
# Rate limiting
|
||||
self.last_request_time = 0
|
||||
self.min_request_interval = 0.1 # 100ms between requests
|
||||
# Recv window and server time sync
|
||||
self.recv_window_ms = 20000 # 20 seconds to tolerate clock drift
|
||||
self.time_delta_ms = 0 # server_time_ms - local_time_ms
|
||||
|
||||
# Request session for connection pooling
|
||||
self.session = requests.Session()
|
||||
@@ -61,7 +64,7 @@ class BybitRestClient:
|
||||
HMAC-SHA256 signature
|
||||
"""
|
||||
# Bybit signature format: timestamp + api_key + recv_window + params
|
||||
recv_window = "5000" # 5 seconds
|
||||
recv_window = str(self.recv_window_ms)
|
||||
param_str = f"{timestamp}{self.api_key}{recv_window}{params}"
|
||||
|
||||
signature = hmac.new(
|
||||
@@ -86,7 +89,7 @@ class BybitRestClient:
|
||||
'X-BAPI-API-KEY': self.api_key,
|
||||
'X-BAPI-SIGN': signature,
|
||||
'X-BAPI-TIMESTAMP': timestamp,
|
||||
'X-BAPI-RECV-WINDOW': '5000',
|
||||
'X-BAPI-RECV-WINDOW': str(self.recv_window_ms),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
@@ -116,7 +119,9 @@ class BybitRestClient:
|
||||
self._rate_limit()
|
||||
|
||||
url = f"{self.base_url}{endpoint}"
|
||||
timestamp = str(int(time.time() * 1000))
|
||||
# Apply server time delta
|
||||
local_ms = int(time.time() * 1000)
|
||||
timestamp = str(local_ms + self.time_delta_ms)
|
||||
|
||||
if params is None:
|
||||
params = {}
|
||||
@@ -165,6 +170,20 @@ class BybitRestClient:
|
||||
error_msg = result.get('retMsg', 'Unknown error')
|
||||
error_code = result.get('retCode', 'Unknown')
|
||||
logger.error(f"Bybit Error {error_code}: {error_msg}")
|
||||
# Handle time drift (10002) by syncing server time and retrying once
|
||||
if signed and str(error_code) == '10002':
|
||||
try:
|
||||
self.sync_server_time()
|
||||
# increase recv window defensively
|
||||
self.recv_window_ms = max(self.recv_window_ms, 30000)
|
||||
# Retry once
|
||||
return self._make_request(method, endpoint, params, signed)
|
||||
except Exception:
|
||||
pass
|
||||
# Handle rate limit (10016) by brief backoff and retry
|
||||
if str(error_code) == '10016':
|
||||
time.sleep(0.25)
|
||||
return self._make_request(method, endpoint, params, signed)
|
||||
raise Exception(f"Bybit Error {error_code}: {error_msg}")
|
||||
|
||||
return result
|
||||
@@ -173,6 +192,18 @@ class BybitRestClient:
|
||||
"""Get server time (public endpoint)."""
|
||||
return self._make_request('GET', '/v5/market/time')
|
||||
|
||||
def sync_server_time(self) -> None:
|
||||
"""Sync local-server time delta to reduce 10002 invalid timestamp errors."""
|
||||
try:
|
||||
res = self.get_server_time()
|
||||
server_ts = int(res.get('result', {}).get('timeSecond', 0)) * 1000
|
||||
if server_ts > 0:
|
||||
local_ms = int(time.time() * 1000)
|
||||
self.time_delta_ms = server_ts - local_ms
|
||||
logger.info(f"Bybit server time synced. Delta: {self.time_delta_ms} ms")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to sync Bybit server time: {e}")
|
||||
|
||||
def get_account_info(self) -> Dict[str, Any]:
|
||||
"""Get account information (private endpoint)."""
|
||||
return self._make_request('GET', '/v5/account/wallet-balance',
|
||||
|
||||
@@ -2513,7 +2513,7 @@ class TradingExecutor:
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
def execute_trade(self, symbol: str, action: str, quantity: float) -> bool:
|
||||
def execute_trade(self, symbol: str, action: str, quantity: float, current_price: Optional[float] = None) -> bool:
|
||||
"""Execute a trade directly (compatibility method for dashboard)
|
||||
|
||||
Args:
|
||||
@@ -2525,10 +2525,9 @@ class TradingExecutor:
|
||||
bool: True if trade executed successfully
|
||||
"""
|
||||
try:
|
||||
# Get current price
|
||||
current_price = None
|
||||
# Always get real current price - never use simulated data
|
||||
current_price = self._get_real_current_price(symbol)
|
||||
# Get current price (allow explicit price or fallback to real fetch)
|
||||
if current_price is None:
|
||||
current_price = self._get_real_current_price(symbol)
|
||||
if current_price is None:
|
||||
logger.error(f"Failed to get real current price for {symbol}")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user