diff --git a/core/trading_executor.py b/core/trading_executor.py index 0000a59..8993ff5 100644 --- a/core/trading_executor.py +++ b/core/trading_executor.py @@ -276,7 +276,7 @@ class TradingExecutor: quote_asset = 'USDC' # Use USDC instead logger.info(f"BALANCE CHECK: Using USDC fallback balance for {symbol}") else: - available_balance = self.exchange.get_balance(quote_asset) + available_balance = self.exchange.get_balance(quote_asset) logger.info(f"BALANCE CHECK: Symbol: {symbol}, Action: {action}, Required: ${required_capital:.2f} {quote_asset}, Available: ${available_balance:.2f} {quote_asset}") @@ -303,23 +303,23 @@ class TradingExecutor: logger.info(f"LOCK ACQUIRED: Executing {action} for {symbol}") start_time = time.time() - if action == 'BUY': + if action == 'BUY': result = self._execute_buy(symbol, confidence, current_price) - elif action == 'SELL': + elif action == 'SELL': result = self._execute_sell(symbol, confidence, current_price) - elif action == 'SHORT': # Explicitly handle SHORT if it's a direct signal + elif action == 'SHORT': # Explicitly handle SHORT if it's a direct signal result = self._execute_short(symbol, confidence, current_price) - else: - logger.warning(f"Unknown action: {action}") + else: + logger.warning(f"Unknown action: {action}") result = False execution_time = time.time() - start_time logger.info(f"EXECUTION COMPLETE: {action} for {symbol} took {execution_time:.2f}s, result: {result}") return result - except Exception as e: - logger.error(f"Error executing {action} signal for {symbol}: {e}") - return False + except Exception as e: + logger.error(f"Error executing {action} signal for {symbol}: {e}") + return False finally: self.lock.release() logger.debug(f"LOCK RELEASED: {action} for {symbol}") @@ -386,7 +386,7 @@ class TradingExecutor: logger.info(f"Test mode: bypassing trade interval for {symbol}") else: logger.info(f"Trade interval not met for {symbol} ({time_since_last:.1f}s < {min_interval}s)") - return False + return False # Check concurrent positions max_positions = self.exchange_config.get('max_concurrent_positions', @@ -442,7 +442,7 @@ class TradingExecutor: # Only create position if order was actually filled if result.get('filled', True): # Assume filled for backward compatibility self.positions[symbol] = Position( - symbol=symbol, + symbol=symbol, side='LONG', quantity=float(filled_quantity), entry_price=float(fill_price), @@ -452,7 +452,7 @@ class TradingExecutor: logger.info(f"BUY position created: {filled_quantity:.6f} {symbol} at ${fill_price:.4f}") self.last_trade_time[symbol] = datetime.now() return True - else: + else: logger.error(f"BUY order placed but not filled: {result}") return False else: @@ -488,12 +488,12 @@ class TradingExecutor: if self.simulation_mode: # Create simulated short position - self.positions[symbol] = Position( - symbol=symbol, + self.positions[symbol] = Position( + symbol=symbol, side='SHORT', - quantity=quantity, - entry_price=current_price, - entry_time=datetime.now(), + quantity=quantity, + entry_price=current_price, + entry_time=datetime.now(), order_id=f"sim_{int(datetime.now().timestamp())}" ) logger.info(f"Simulated SHORT order: {quantity:.6f} {symbol} at ${current_price:.2f}") @@ -517,9 +517,9 @@ class TradingExecutor: order_id=result['orderId'] ) logger.info(f"SHORT position created: {filled_quantity:.6f} {symbol} at ${fill_price:.4f}") - self.last_trade_time[symbol] = datetime.now() - return True - else: + self.last_trade_time[symbol] = datetime.now() + return True + else: logger.error(f"SHORT order placed but not filled: {result}") return False else: @@ -628,7 +628,7 @@ class TradingExecutor: try: self.exchange.cancel_order(symbol, order_id) logger.info(f"Cancelled unfilled order {order_id}") - except Exception as e: + except Exception as e: logger.error(f"Failed to cancel unfilled order {order_id}: {e}") # If this was the last attempt, return failure @@ -1190,10 +1190,10 @@ class TradingExecutor: raw_balances = self.exchange.get_all_balances() if raw_balances: # Convert to the expected format with 'type' field - combined_balances = {} + combined_balances = {} for asset, balance_data in raw_balances.items(): if isinstance(balance_data, dict): - combined_balances[asset] = { + combined_balances[asset] = { 'free': balance_data.get('free', 0.0), 'locked': balance_data.get('locked', 0.0), 'total': balance_data.get('total', 0.0), @@ -1201,7 +1201,7 @@ class TradingExecutor: } logger.info(f"Retrieved balances for {len(combined_balances)} assets from {self.primary_name}") - return combined_balances + return combined_balances else: logger.warning(f"No balances returned from {self.primary_name} exchange") return {} diff --git a/test_bybit_eth_futures_fixed.py b/test_bybit_eth_futures_fixed.py new file mode 100644 index 0000000..a92341c --- /dev/null +++ b/test_bybit_eth_futures_fixed.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +""" +Fixed Bybit ETH futures trading test with proper minimum order size handling +""" + +import os +import sys +import time +import logging +import json + +# Add the project root to the path +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +# Load environment variables +try: + from dotenv import load_dotenv + load_dotenv() +except ImportError: + if os.path.exists('.env'): + with open('.env', 'r') as f: + for line in f: + if line.strip() and not line.startswith('#'): + key, value = line.strip().split('=', 1) + os.environ[key] = value + +from NN.exchanges.bybit_interface import BybitInterface + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def get_instrument_info(bybit: BybitInterface, symbol: str) -> dict: + """Get instrument information including minimum order size""" + try: + instruments = bybit.get_instruments("linear") + for instrument in instruments: + if instrument.get('symbol') == symbol: + return instrument + return {} + except Exception as e: + logger.error(f"Error getting instrument info: {e}") + return {} + +def test_eth_futures_trading(): + """Test ETH futures trading with proper minimum order size""" + print("šŸš€ Starting Fixed Bybit ETH Futures Live Trading Test...") + print("=" * 60) + print("BYBIT ETH FUTURES LIVE TRADING TEST (FIXED)") + print("=" * 60) + print("āš ļø This uses LIVE environment with real money!") + print("āš ļø Will check minimum order size first") + print("=" * 60) + + # Check if API credentials are set + api_key = os.getenv('BYBIT_API_KEY') + api_secret = os.getenv('BYBIT_API_SECRET') + + if not api_key or not api_secret: + print("āŒ API credentials not found in environment") + return False + + # Create Bybit interface with live environment + bybit = BybitInterface( + api_key=api_key, + api_secret=api_secret, + test_mode=False # Use live environment + ) + + symbol = 'ETHUSDT' + + # Test 1: Connection + print(f"\nšŸ“” Testing connection to Bybit live environment...") + try: + if not bybit.connect(): + print("āŒ Failed to connect to Bybit") + return False + print("āœ… Successfully connected to Bybit live environment") + except Exception as e: + print(f"āŒ Connection error: {e}") + return False + + # Test 2: Get instrument information to check minimum order size + print(f"\nšŸ“‹ Getting instrument information for {symbol}...") + try: + instrument_info = get_instrument_info(bybit, symbol) + if not instrument_info: + print(f"āŒ Failed to get instrument info for {symbol}") + return False + + print("āœ… Instrument information retrieved:") + print(f" Symbol: {instrument_info.get('symbol')}") + print(f" Status: {instrument_info.get('status')}") + print(f" Base Coin: {instrument_info.get('baseCoin')}") + print(f" Quote Coin: {instrument_info.get('quoteCoin')}") + + # Extract minimum order size + lot_size_filter = instrument_info.get('lotSizeFilter', {}) + min_order_qty = float(lot_size_filter.get('minOrderQty', 0.01)) + max_order_qty = float(lot_size_filter.get('maxOrderQty', 10000)) + qty_step = float(lot_size_filter.get('qtyStep', 0.01)) + + print(f" Minimum Order Qty: {min_order_qty}") + print(f" Maximum Order Qty: {max_order_qty}") + print(f" Quantity Step: {qty_step}") + + # Use minimum order size for testing + test_quantity = min_order_qty + print(f" Using test quantity: {test_quantity} ETH") + + except Exception as e: + print(f"āŒ Instrument info error: {e}") + return False + + # Test 3: Get account balance + print(f"\nšŸ’° Checking account balance...") + try: + usdt_balance = bybit.get_balance('USDT') + print(f"USDT Balance: ${usdt_balance:.2f}") + + # Calculate required balance (with some buffer) + current_price_data = bybit.get_ticker(symbol) + if not current_price_data: + print("āŒ Failed to get current ETH price") + return False + + current_price = current_price_data['last_price'] + required_balance = current_price * test_quantity * 1.1 # 10% buffer + + print(f"Current ETH price: ${current_price:.2f}") + print(f"Required balance: ${required_balance:.2f}") + + if usdt_balance < required_balance: + print(f"āŒ Insufficient USDT balance for testing (need at least ${required_balance:.2f})") + return False + + print("āœ… Sufficient balance for testing") + + except Exception as e: + print(f"āŒ Balance check error: {e}") + return False + + # Test 4: Check existing positions + print(f"\nšŸ“Š Checking existing positions...") + try: + positions = bybit.get_positions(symbol) + if positions: + print(f"Found {len(positions)} existing positions:") + for pos in positions: + print(f" {pos['symbol']}: {pos['side']} {pos['size']} @ ${pos['entry_price']:.2f}") + print(f" PnL: ${pos['unrealized_pnl']:.2f}") + else: + print("No existing positions found") + except Exception as e: + print(f"āŒ Position check error: {e}") + return False + + # Test 5: Ask user confirmation before trading + print(f"\nāš ļø TRADING CONFIRMATION") + print(f" Symbol: {symbol}") + print(f" Quantity: {test_quantity} ETH") + print(f" Estimated cost: ${current_price * test_quantity:.2f}") + print(f" Environment: LIVE (real money)") + print(f" Minimum order size confirmed: {min_order_qty}") + + response = input("\nDo you want to proceed with the live trading test? (y/N): ").lower() + if response != 'y' and response != 'yes': + print("āŒ Trading test cancelled by user") + return False + + # Test 6: Open a small long position + print(f"\nšŸš€ Opening small long position...") + try: + order = bybit.place_order( + symbol=symbol, + side='buy', + order_type='market', + quantity=test_quantity + ) + + if 'error' in order: + print(f"āŒ Order failed: {order['error']}") + return False + + print("āœ… Long position opened successfully:") + print(f" Order ID: {order['order_id']}") + print(f" Symbol: {order['symbol']}") + print(f" Side: {order['side']}") + print(f" Quantity: {order['quantity']}") + print(f" Status: {order['status']}") + + order_id = order['order_id'] + + except Exception as e: + print(f"āŒ Order placement error: {e}") + return False + + # Test 7: Wait a moment and check position + print(f"\nā³ Waiting 5 seconds for position to be reflected...") + time.sleep(5) + + try: + positions = bybit.get_positions(symbol) + if positions: + position = positions[0] + print("āœ… Position confirmed:") + print(f" Symbol: {position['symbol']}") + print(f" Side: {position['side']}") + print(f" Size: {position['size']}") + print(f" Entry Price: ${position['entry_price']:.2f}") + print(f" Current PnL: ${position['unrealized_pnl']:.2f}") + print(f" Leverage: {position['leverage']}x") + else: + print("āš ļø No position found (may already be closed)") + + except Exception as e: + print(f"āŒ Position check error: {e}") + + # Test 8: Close the position + print(f"\nšŸ”„ Closing the position...") + try: + close_order = bybit.close_position(symbol) + + if 'error' in close_order: + print(f"āŒ Close order failed: {close_order['error']}") + # Don't return False here, as the position might still exist + print("āš ļø You may need to manually close the position") + else: + print("āœ… Position closed successfully:") + print(f" Order ID: {close_order['order_id']}") + print(f" Symbol: {close_order['symbol']}") + print(f" Side: {close_order['side']}") + print(f" Quantity: {close_order['quantity']}") + print(f" Status: {close_order['status']}") + + except Exception as e: + print(f"āŒ Close position error: {e}") + print("āš ļø You may need to manually close the position") + + # Test 9: Final position check + print(f"\nšŸ“Š Final position check...") + time.sleep(3) + + try: + positions = bybit.get_positions(symbol) + if positions: + position = positions[0] + print("āš ļø Position still exists:") + print(f" Size: {position['size']}") + print(f" PnL: ${position['unrealized_pnl']:.2f}") + print("šŸ’” You may want to manually close this position") + else: + print("āœ… No open positions - trading test completed successfully") + + except Exception as e: + print(f"āŒ Final position check error: {e}") + + # Test 10: Final balance check + print(f"\nšŸ’° Final balance check...") + try: + final_balance = bybit.get_balance('USDT') + print(f"Final USDT Balance: ${final_balance:.2f}") + + balance_change = final_balance - usdt_balance + if balance_change > 0: + print(f"šŸ’° Profit: +${balance_change:.2f}") + elif balance_change < 0: + print(f"šŸ“‰ Loss: ${balance_change:.2f}") + else: + print(f"šŸ”„ No change: ${balance_change:.2f}") + + except Exception as e: + print(f"āŒ Final balance check error: {e}") + + return True + +def main(): + """Main function""" + print("šŸš€ Starting Fixed Bybit ETH Futures Live Trading Test...") + + success = test_eth_futures_trading() + + if success: + print("\n" + "=" * 60) + print("āœ… BYBIT ETH FUTURES TRADING TEST COMPLETED") + print("=" * 60) + print("šŸŽÆ Your Bybit integration is fully functional!") + print("šŸ”„ Position opening and closing works correctly") + print("šŸ’° Account balance integration works") + print("šŸ“Š All trading functions are operational") + print("šŸ“ Minimum order size handling works") + print("=" * 60) + else: + print("\nšŸ’„ Trading test failed!") + print("šŸ” Check the error messages above for details") + + return success + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py index 329df06..de654e8 100644 --- a/web/clean_dashboard.py +++ b/web/clean_dashboard.py @@ -3495,7 +3495,7 @@ class CleanTradingDashboard: # Train CNN model with multiple passes for enhanced learning if hasattr(self.orchestrator.cnn_model, 'train_on_batch'): for _ in range(int(training_weight)): - loss = self.orchestrator.cnn_model.train_on_batch(feature_tensor, target_tensor) + loss = self.orchestrator.cnn_model.train_on_batch(feature_tensor, target_tensor) logger.info(f"CNN enhanced training on executed signal - loss: {loss:.4f}, pnl: {pnl:.2f}") except Exception as e: