stability and error cleaning. rule to read specs
This commit is contained in:
5
.cursor/rules/specs.mdc
Normal file
5
.cursor/rules/specs.mdc
Normal 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
|
||||
---
|
@@ -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