stability and error cleaning. rule to read specs

This commit is contained in:
Dobromir Popov
2025-08-10 13:05:41 +03:00
parent e05163deb7
commit 6861d0f20b
4 changed files with 51 additions and 8 deletions

5
.cursor/rules/specs.mdc Normal file
View File

@@ -0,0 +1,5 @@
---
description: use .kiro\specs cnotent as project guideline and specifications. they may change as project develops, but will give you a good starting point and broad understanding of the project we are working on. Also, when you find problems proceed to fixing them without asking. We are discovering problems so we fix them :)
globs:
alwaysApply: false
---

View File

@@ -98,6 +98,14 @@ class BybitInterface(ExchangeInterface):
testnet=self.test_mode 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 # Test pybit connection first
try: try:
account_info = self.session.get_wallet_balance(accountType="UNIFIED") account_info = self.session.get_wallet_balance(accountType="UNIFIED")

View File

@@ -40,6 +40,9 @@ class BybitRestClient:
# Rate limiting # Rate limiting
self.last_request_time = 0 self.last_request_time = 0
self.min_request_interval = 0.1 # 100ms between requests 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 # Request session for connection pooling
self.session = requests.Session() self.session = requests.Session()
@@ -61,7 +64,7 @@ class BybitRestClient:
HMAC-SHA256 signature HMAC-SHA256 signature
""" """
# Bybit signature format: timestamp + api_key + recv_window + params # 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}" param_str = f"{timestamp}{self.api_key}{recv_window}{params}"
signature = hmac.new( signature = hmac.new(
@@ -86,7 +89,7 @@ class BybitRestClient:
'X-BAPI-API-KEY': self.api_key, 'X-BAPI-API-KEY': self.api_key,
'X-BAPI-SIGN': signature, 'X-BAPI-SIGN': signature,
'X-BAPI-TIMESTAMP': timestamp, 'X-BAPI-TIMESTAMP': timestamp,
'X-BAPI-RECV-WINDOW': '5000', 'X-BAPI-RECV-WINDOW': str(self.recv_window_ms),
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
@@ -116,7 +119,9 @@ class BybitRestClient:
self._rate_limit() self._rate_limit()
url = f"{self.base_url}{endpoint}" 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: if params is None:
params = {} params = {}
@@ -165,6 +170,20 @@ class BybitRestClient:
error_msg = result.get('retMsg', 'Unknown error') error_msg = result.get('retMsg', 'Unknown error')
error_code = result.get('retCode', 'Unknown') error_code = result.get('retCode', 'Unknown')
logger.error(f"Bybit Error {error_code}: {error_msg}") 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}") raise Exception(f"Bybit Error {error_code}: {error_msg}")
return result return result
@@ -173,6 +192,18 @@ class BybitRestClient:
"""Get server time (public endpoint).""" """Get server time (public endpoint)."""
return self._make_request('GET', '/v5/market/time') 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]: def get_account_info(self) -> Dict[str, Any]:
"""Get account information (private endpoint).""" """Get account information (private endpoint)."""
return self._make_request('GET', '/v5/account/wallet-balance', return self._make_request('GET', '/v5/account/wallet-balance',

View File

@@ -2513,7 +2513,7 @@ class TradingExecutor:
'error': str(e) '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) """Execute a trade directly (compatibility method for dashboard)
Args: Args:
@@ -2525,9 +2525,8 @@ class TradingExecutor:
bool: True if trade executed successfully bool: True if trade executed successfully
""" """
try: try:
# Get current price # Get current price (allow explicit price or fallback to real fetch)
current_price = None if current_price is None:
# Always get real current price - never use simulated data
current_price = self._get_real_current_price(symbol) current_price = self._get_real_current_price(symbol)
if current_price is None: if current_price is None:
logger.error(f"Failed to get real current price for {symbol}") logger.error(f"Failed to get real current price for {symbol}")