fix: resolve Illegal category error by adding safety checks (v1.5.1)

This commit is contained in:
Gemini CLI
2026-03-05 23:21:15 +01:00
parent 6e6e9db5cc
commit f3ce68de22

View File

@ -68,7 +68,7 @@ class DatabaseManager:
class PingPongBot:
def __init__(self, config_path="config/ping_pong_config.yaml"):
self.version = "1.5.0"
self.version = "1.5.1"
with open(config_path, 'r') as f:
self.config = yaml.safe_load(f)
@ -170,8 +170,11 @@ class PingPongBot:
df_1d['close'] = df_1d['close'].astype(float)
self.ma_44_val = df_1d['close'].rolling(window=44).mean().iloc[-1]
# Get current price from exchange
ticker = await asyncio.to_thread(self.session.get_tickers, category="linear", symbol=f"{self.base_coin}USDC")
# Use BTCUSDT for price check if not initialized, otherwise use current symbol
ticker_symbol = self.symbol if self.symbol else f"{self.base_coin}USDT"
ticker_cat = self.category if self.category else "linear"
ticker = await asyncio.to_thread(self.session.get_tickers, category=ticker_cat, symbol=ticker_symbol)
current_price = float(ticker['result']['list'][0]['lastPrice'])
self.market_price = current_price
@ -210,6 +213,7 @@ class PingPongBot:
async def close_all_positions(self):
"""Closes any active position in the current category/symbol"""
try:
if not self.category or not self.symbol: return
pos = await asyncio.to_thread(self.session.get_positions, category=self.category, symbol=self.symbol)
if pos['retCode'] == 0:
for p in pos['result']['list']:
@ -254,12 +258,15 @@ class PingPongBot:
async def update_exchange_data(self):
"""Fetch Price, Balance, Position every 15s"""
if not self.category or not self.symbol: return
try:
ticker = await asyncio.to_thread(self.session.get_tickers, category=self.category, symbol=self.symbol)
if ticker['retCode'] == 0:
self.market_price = float(ticker['result']['list'][0]['lastPrice'])
pos = await asyncio.to_thread(self.session.get_positions, category=self.category, symbol=self.symbol, settleCoin="USDT" if self.category == "linear" else None)
# settleCoin is only for linear perpetuals
settle_coin = "USDC" if self.category == "linear" else None
pos = await asyncio.to_thread(self.session.get_positions, category=self.category, symbol=self.symbol, settleCoin=settle_coin)
if pos['retCode'] == 0:
active = [p for p in pos['result']['list'] if float(p.get('size', 0)) > 0]
self.position = active[0] if active else None
@ -270,13 +277,12 @@ class PingPongBot:
if wallet['retCode'] == 0:
res_list = wallet['result']['list']
if res_list:
# In inverse, we value in BTC, but dashboard usually shows USD.
# We'll stick to totalWalletBalance which is usually USD-equiv in UTA
self.wallet_balance = float(res_list[0].get('totalWalletBalance', 0))
except Exception as e:
logger.error(f"Exchange Sync Error: {e}")
def check_signals(self, df):
if len(df) < 2: return None
last, prev = df.iloc[-1], df.iloc[-2]
rsi_cfg, hurst_cfg = self.config['rsi'], self.config['hurst']
@ -297,7 +303,7 @@ class PingPongBot:
return "open" if s_open else ("close" if s_close else None)
async def execute_trade(self, signal):
if not signal: return
if not signal or not self.market_price: return
last_price = self.market_price
if signal == "close" and self.position:
@ -308,15 +314,14 @@ class PingPongBot:
elif signal == "open":
cur_qty = float(self.position['size']) if self.position else 0
# Notional calculation differs between Linear and Inverse
if self.category == "linear":
cur_notional = cur_qty * last_price
ping_notional = self.pos_size_margin * self.leverage
qty_to_open = ping_notional / last_price
else: # Inverse
cur_notional = cur_qty # Inverse size is in USD usually, but Bybit V5 size is in contracts (USD)
cur_notional = cur_qty
ping_notional = self.pos_size_margin * self.leverage
qty_to_open = ping_notional # For Inverse BTCUSD, Qty is USD amount
qty_to_open = ping_notional
if (cur_notional + ping_notional) / max(self.wallet_balance, 1) <= self.max_eff_lev:
await self.place_order(qty_to_open, is_close=False)
@ -324,10 +329,10 @@ class PingPongBot:
self.status_msg = "Max Leverage Reached"
async def place_order(self, qty, is_close=False):
if not self.category or not self.symbol: return
side = "Sell" if (self.direction == "long" and is_close) or (self.direction == "short" and not is_close) else "Buy"
pos_idx = 1 if self.direction == "long" else 2
try:
# Rounding: Linear BTC needs 3 decimals, Inverse BTCUSD needs integers (USD)
qty_str = str(int(qty)) if self.category == "inverse" else str(round(qty, 3))
res = await asyncio.to_thread(self.session.place_order,
@ -371,14 +376,14 @@ class PingPongBot:
async def run(self):
await self.db.connect()
await self.update_direction() # Initial point I
await self.update_direction()
last_exchange_update = 0
while True:
try:
now = time.time()
# 1. Periodically check direction (every 2m - Point III.2)
# 1. Periodically check direction (every 2m)
if now - self.last_ma_check_time >= 120:
await self.update_direction()
self.last_ma_check_time = now