please fix the spacing import asyncio
import json
import time
import base64
import traceback
from pathlib import Path
from solana.rpc.async_api import AsyncClient
from solana.publickey import PublicKey
from solana.transaction import Transaction
from solana.keypair import Keypair
from solana.rpc.commitment import Commitment
from spl.token.instructions import (
    get_associated_token_address,
    create_associated_token_account,
)
from solana_rust_bot import TradingCore, TradeParams
from solana_logger import logger
from balance_manager import BalanceManager
from config_loader import config
import websockets
import torch
import os

SOLANA_VERSION = "solana-client/1.18.23 (src:aeb3a2e1; feat:4215500110, client:SolanaLabs)"
WEBSOCKET_CONFIG = {
    "ping_interval": 0.5,
    "ping_timeout": 5,
    "compression": None,
    "max_size": 2**23,
    "close_timeout": 1.0,
    "max_queue": 2**16,
    "read_limit": 2**16,
}

class QuantumTrader:
    def __init__(self):
        logger.system("🚀 Initializing Quantum Trader")
        
        # Load configurations first
        self.network_config = config.get_network_config()
        self.trade_config = config.get_trade_config()
        self.perf_config = config.get_performance_config()
        
        # Initialize components
        self._setup_cuda()
        self._init_core()
        
        # Setup state management
        self.execution_lock = asyncio.Lock()
        self.in_trade = False
        self.ata_cache = {}
        self.ws = None
        self.reconnect_delay = 1.0
        self.max_reconnect_delay = 30.0
        
        logger.system("✨ Quantum Trader Initialized!")

    def _setup_cuda(self):
        """Configure CUDA for optimal performance."""
        try:
            torch.cuda.init()
            torch.backends.cuda.matmul.allow_tf32 = self.perf_config["allow_tf32"]
            torch.backends.cudnn.benchmark = True
            torch.cuda.set_device(0)
            torch.cuda.empty_cache()

            memory_fraction = self.perf_config["gpu_memory_fraction"]
            torch.cuda.set_per_process_memory_fraction(memory_fraction)

            self.stream = torch.cuda.Stream(priority=self.perf_config["stream_priority"])
            logger.system(f"💻 CUDA Initialized | Memory: {memory_fraction * 100}%")
        except Exception as e:
            logger.error(f"CUDA initialization failed: {e}")
            raise

    def _init_core(self):
        """Initialize core trading components."""
        try:
            # Load keypair
            self.keypair_bytes = config.get_keypair_bytes()
            self.keypair = Keypair.from_secret_key(self.keypair_bytes)

            # Initialize trading core
            self.trading_core = TradingCore(
                rpc_url=self.network_config["rpc_url"],
                keypair_bytes=self.keypair_bytes
            )

            # Initialize balance manager and client
            self.balance_manager = BalanceManager()
            self.client = AsyncClient(
                self.network_config["rpc_url"], 
                commitment=Commitment("confirmed")
            )

            logger.info(f"Trading core initialized for wallet: {str(self.keypair.public_key)}")

        except Exception as e:
            logger.error(f"Core initialization error: {e}")
            raise

            async def _create_and_get_ata(self, token_mint: str) -> str:
                """Create ATA if needed and return address."""
                try:
                    # Check cache first
                    if token_mint in self.ata_cache:
                        return self.ata_cache[token_mint]

                    wallet_pubkey = self.keypair.public_key
                    token_pubkey = PublicKey(token_mint)
                    ata = get_associated_token_address(wallet_pubkey, token_pubkey)

                    # Check if account exists
                    account_info = await self.client.get_account_info(ata)
                    if account_info.value is not None:
                        self.ata_cache[token_mint] = str(ata)
                        return str(ata)

                    # Explicitly specify associated token program ID
                    create_ata_ix = create_associated_token_account(
                        payer=wallet_pubkey,
                        owner=wallet_pubkey,
                        mint=token_pubkey,
                        associated_token_program_id=PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
                    )

                    # Build transaction
                    recent_blockhash = (await self.client.get_latest_blockhash()).value.blockhash
                    tx = Transaction(
                        recent_blockhash=recent_blockhash,
                        fee_payer=wallet_pubkey,
                    )
                    tx.add(create_ata_ix)

                    # Sign and send
                    tx.sign(self.keypair)
                    tx_bytes = tx.serialize()
            
                    # Use skip preflight since we check simulation
                    sig = await self.client.send_raw_transaction(
                        tx_bytes,
                        opts={
                            "skip_preflight": False,
                            "preflight_commitment": "confirmed"
                        }
                    )

                    # Wait for confirmation
                    await self.client.confirm_transaction(sig.value, commitment="confirmed")

                    logger.info(f"Created ATA {str(ata)} for token {token_mint}")
                    self.ata_cache[token_mint] = str(ata)
                    return str(ata)

                except Exception as e:
                    logger.error(f"ATA creation failed: {str(e)}")
                    return None    
    def decode_raydium_data(self, raw_data: bytes) -> dict:
        """Decode Raydium instruction data."""
        try:
            if len(raw_data) < 9:
                return None

            # Extract instruction type and amount
            instruction_type = raw_data[0]
            amount = int.from_bytes(raw_data[1:9], "little") / 1e9

            # Validate size bounds
            if amount < self.trade_config["min_victim_size"] or amount > self.trade_config["max_victim_size"]:
                return None
            if instruction_type not in [0x03, 0x04]:
                return None

            # Extract token mint
            token_mint = None
            for offset in [9, 17]:
                if len(raw_data) >= offset + 32:
                    try:
                        from base58 import b58encode
                        token_bytes = raw_data[offset:offset + 32]
                        potential_mint = b58encode(token_bytes).decode("utf-8")
                        if len(potential_mint) in [32, 44]:
                            token_mint = potential_mint
                            break
                    except:
                        continue

            if not token_mint:
                return None

            # Calculate profit with CUDA
            with torch.cuda.stream(self.stream):
                position_size = self.trade_config["front_run_amount"]
                impact_multiplier = 1.2 if amount >= 20.0 else 1.0
                victim_impact = amount * 0.0015 * impact_multiplier
                our_impact = position_size * 0.002
                gas_cost = 0.002
                net_profit = (position_size * (victim_impact - our_impact)) - gas_cost

                if net_profit < self.trade_config["min_profit"]:
                    return None

            return {
                "victim_data": raw_data,
                "amount": amount,
                "token_mint": token_mint,
                "net_profit": float(net_profit),
                "instruction_type": instruction_type,
            }

        except Exception as e:
            logger.error(f"Decode error: {e}")
            return None

    async def execute_trade(self, trade_data: dict) -> bool:
        """Execute trade with proper ATA handling."""
        if self.in_trade or self.balance_manager.is_trading_paused:
            return False

        async with self.execution_lock:
            try:
                self.in_trade = True

                # Create ATA first
                user_token = await self._create_and_get_ata(trade_data["token_mint"])
                if not user_token:
                    return False

                # Account setup with proper ATA
                market_accounts = config.get_market_accounts()
                trade_accounts = {
                    "signer": config.get("wallet"),
                    "user": config.get("wallet"),
                    "user_token": str(user_token),
                    "user_wsol": config.get("wsol_token_account"),
                    "raydium_program": config.get("raydium"),
                    **market_accounts
                }

                # Verify accounts
                for key, value in trade_accounts.items():
                    if not value or value.lower() == "none":
                        logger.error(f"Missing required account: {key}")
                        return False

                position_size = self.trade_config["front_run_amount"]
                logger.trade(
                    f"""
🎯 Starting Trade:
Amount: {position_size} SOL
Token: {trade_data['token_mint']}
Expected Profit: {trade_data['net_profit']:.4f} SOL
"""
                )

                # Execute entry with high priority
                entry_params = TradeParams(
                    accounts=trade_accounts,
                    amount_in=position_size,
                    is_buy=True,
                    victim_data=None,  # Create fresh instruction
                    reuse_blockhash=False,
                    spl_token_mint=str(trade_data["token_mint"])
                )

                entry_sig = await self.trading_core.execute_trade_py(entry_params)
                if not entry_sig:
                    return False

                logger.trade(f"✅ Entry complete: {entry_sig}")

                # Quick exit attempts
                for i in range(3):
                    exit_params = TradeParams(
                        accounts=trade_accounts,
                        amount_in=position_size,
                        is_buy=False,
                        victim_data=None,
                        reuse_blockhash=i == 0,
                        spl_token_mint=str(trade_data["token_mint"])
                    )

                    exit_sig = await self.trading_core.execute_trade_py(exit_params)
                    if exit_sig:
                        logger.trade(
                            f"""
🎯 Trade Complete!
Entry: {entry_sig}
Exit: {exit_sig}
Profit: {trade_data['net_profit']:.4f} SOL
"""
                        )
                        return True

                    await asyncio.sleep(0.05)

                logger.error("Failed to complete exit")
                return False

            except Exception as e:
                logger.error(f"Trade execution error: {e}")
                return False
            finally:
                self.in_trade = False

    async def _process_message(self, msg: str):
        """Process incoming WebSocket messages."""
        try:
            data = json.loads(msg)
            if "params" not in data:
                return

            logs = (
                data["params"]
                .get("result", {})
                .get("value", {})
                .get("logs", [])
            )

            for log in logs:
                if "ray_log:" in log:
                    decoded = base64.b64decode(log.split("ray_log:")[1].strip())
                    trade_data = self.decode_raydium_data(decoded)
                    if trade_data:
                        await self.execute_trade(trade_data)

        except json.JSONDecodeError:
            logger.error("Failed to decode WebSocket message")
        except Exception as e:
            logger.error(f"Message processing error: {e}")

    async def start(self):
        """Start the quantum trader."""
        logger.system(
            f"""
🚀 QUANTUM TRADER STARTING
==================================================
⚡ Target Range: {self.trade_config["min_victim_size"]}-{self.trade_config["max_victim_size"]} SOL
💰 Position Size: {self.trade_config["front_run_amount"]} SOL
📊 Min Profit: {self.trade_config["min_profit"]} SOL
🔥 Priority Fee: {self.trade_config["priority_fee"] / 1_000_000} SOL
==================================================
"""
        )

        # Start balance manager
        balance_task = asyncio.create_task(self.balance_manager.start())

        try:
            while True:
                try:
                    async with websockets.connect(
                        self.network_config["ws_url"],
                        extra_headers={
                            "api-key": self.network_config["api_key"],
                            "User-Agent": SOLANA_VERSION
                        },
                        **WEBSOCKET_CONFIG
                    ) as self.ws:
                        logger.system("📡 Connected to Helius")
                        self.reconnect_delay = 1.0  # Reset delay on successful connection

                        # Subscribe to Raydium program
                        await self.ws.send(
                            json.dumps({
                                "jsonrpc": "2.0",
                                "id": 1,
                                "method": "logsSubscribe",
                                "params": [
                                    {"mentions": [config.get("raydium")]},
                                    {"commitment": "processed"},
                                ],
                            })
                        )

                        while True:
                            try:
                                msg = await asyncio.wait_for(
                                    self.ws.recv(),
                                    timeout=WEBSOCKET_CONFIG["ping_timeout"]
                                )
                                await self._process_message(msg)
                            except asyncio.TimeoutError:
                                # Send ping to check connection
                                pong = await self.ws.ping()
                                await asyncio.wait_for(
                                    pong, 
                                    timeout=WEBSOCKET_CONFIG["ping_timeout"]
                                )
                                continue

                except websockets.exceptions.ConnectionClosed:
                    logger.warning(f"WebSocket disconnected, reconnecting in {self.reconnect_delay}s...")
                    await asyncio.sleep(self.reconnect_delay)
                    self.reconnect_delay = min(self.reconnect_delay * 2, self.max_reconnect_delay)

                except Exception as e:
                    logger.error(f"WebSocket error: {e}")
                    await asyncio.sleep(self.reconnect_delay)
                    self.reconnect_delay = min(self.reconnect_delay * 2, self.max_reconnect_delay)

        except Exception as e:
            logger.error(f"Bot error: {e}")
            traceback.print_exc()
        finally:
            await self.stop()
            balance_task.cancel()
            try:
                await balance_task
            except asyncio.CancelledError:
                pass

    async def stop(self):
        """Gracefully stop the quantum trader."""
        logger.system("🛑 Stopping Quantum Trader")
        await self.balance_manager.cleanup()
        if self.ws:
            try:
                await asyncio.wait_for(self.ws.close(), timeout=1.0)
            except:
                pass

if __name__ == "__main__":
    try:
        trader = QuantumTrader()
        asyncio.run(trader.start())
    except KeyboardInterrupt:
        logger.system("Trader stopped by user")
    except Exception as e:
        logger.error(f"Trader crashed: {str(e)}")
        traceback.print_exc() and write debug logging ? print in terminal to make sure its good ?