binance spot trading

please check if this codes logics is okay to place order,take profit and stop loss in spot and futures trading because i tried testing it on a development enviroment is showing internal error so i will drop the code below. please help me and review it if its correct and where a change is needed in the code.

import { addDoc, collection, doc, getDoc, getDocs, limit, onSnapshot, orderBy, query, setDoc, updateDoc, where } from ‘firebase/firestore’;
import { auth, db } from ‘@/lib/firebase’;

import Hex from ‘crypto-js/enc-hex’;
import WebSocket from ‘ws’;
import { getBinanceKeys } from ‘@/lib/db’;
import hmacSHA256 from ‘crypto-js/hmac-sha256’;

class BinanceService {
constructor(userId) {
this.apiKey = process.env.BINANCE_API_KEY;
this.apiSecret = process.env.BINANCE_API_SECRET;
this.userId = auth.currentUser; // Store userId from authenticated context
this.spotBaseUrl = ‘https://api.binance.com/api/v3’;
this.futuresBaseUrl = process.env.BINANCE_TESTNET === ‘true’
? ‘https://testnet.binancefuture.com/fapi/v1
: ‘’;
this.wsUrl = process.env.BINANCE_TESTNET === ‘true’
? ‘wss://stream.binancefuture.com:9443/ws’
: ‘’;
this.maxRetries = 3;
this.retryDelay = 1000;
this.rateLimitWindow = 60000;
this.rateLimitCount = 0;
this.rateLimitStart = Date.now();
this.requestWeight = 0;
this.maxRequestWeight = 1200;
this.listenKey = null;
this.wsConnection = null;
this.balanceUpdateCallbacks = new Set();
this.notificationCallbacks = new Set();
this.connectionState = {
isConnected: false,
isConnecting: false,
lastError: null,
retryCount: 0,
lastReconnectAttempt: null,
connectionTimeout: null,
pingInterval: null
};

    if (!this.apiKey || !this.apiSecret) {
        console.warn('Binance API credentials not configured.');
    }

    this.initializeConnectionState();
}

initializeConnectionState() {
    this.connectionState = {
        isConnected: false,
        isConnecting: false,
        lastError: null,
        retryCount: 0,
        lastReconnectAttempt: null,
        connectionTimeout: null,
        pingInterval: null
    };
}

async makeRequest(url, options = {}, retryCount = 0) {
    try {
        await this.checkRateLimits();

        const headers = {
            'X-MBX-APIKEY': this.apiKey,
            'Content-Type': 'application/json',
            ...options.headers
        };

        let finalUrl = url;
        if (options.method === 'GET' || options.method === 'POST') {
            const timestamp = Date.now();
            const params = {
                ...options.params,
                timestamp,
                recvWindow: 5000
            };

            const queryString = Object.keys(params)
                .sort()
                .map(key => `${key}=${encodeURIComponent(params[key])}`)
                .join('&');
            const signature = this.generateSignature(params);
            finalUrl = `${url}?${queryString}&signature=${signature}`;
        }

        const response = await fetch(finalUrl, {
            ...options,
            headers,
            timeout: 10000
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(`Binance API Error ${errorData.code}: ${errorData.msg}`);
        }

        this.updateRateLimitTracking();
        return await response.json();
    } catch (error) {
        console.error('Network request error:', {
            url,
            error: error.message,
            code: error.code,
            retryCount
        });

        if (this.shouldRetry(error, retryCount)) {
            await this.handleRetry(retryCount);
            return this.makeRequest(url, options, retryCount + 1);
        }

        if (error.message.includes('timeout')) {
            throw new Error('Request timeout. Please try again.');
        }

        throw error;
    }
}

shouldRetry(error, retryCount) {
    const isNetworkError = error.message.includes('network') || 
                          error.message.includes('timeout') || 
                          error.response?.status === 429;
    return isNetworkError && retryCount < this.maxRetries;
}

async handleRetry(retryCount) {
    const delay = this.retryDelay * Math.pow(2, retryCount);
    console.warn(`Retrying request (${retryCount + 1}/${this.maxRetries}) after ${delay}ms...`);
    await new Promise(resolve => setTimeout(resolve, delay));
}

async checkRateLimits() {
    const now = Date.now();
    if (now - this.rateLimitStart > this.rateLimitWindow) {
        this.rateLimitCount = 0;
        this.rateLimitStart = now;
        this.requestWeight = 0;
    }
    if (this.requestWeight >= this.maxRequestWeight) {
        const waitTime = this.rateLimitWindow - (now - this.rateLimitStart);
        console.warn(`Rate limit reached. Waiting ${waitTime}ms...`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
        this.rateLimitCount = 0;
        this.rateLimitStart = Date.now();
        this.requestWeight = 0;
    }
}

updateRateLimitTracking() {
    this.rateLimitCount++;
    this.requestWeight += 1;
}

async setLeverage(data) {
    try {
        const { symbol, leverage, userId } = data;
        if (!symbol || !leverage) {
            throw new Error('Symbol and leverage are required');
        }
        const response = await this.makeRequest(`${this.futuresBaseUrl}/leverage`, {
            method: 'POST',
            params: { symbol, leverage }
        });
        await this.updateUserSettings(userId, { leverage });
        return response;
    } catch (error) {
        console.error('Error setting leverage:', error);
        throw new Error(error.message || 'Failed to set leverage');
    }
}

async setMargin(data) {
    try {
        const { symbol, margin, userId } = data;
        if (!symbol || !margin) {
            throw new Error('Symbol and margin are required');
        }
        const response = await this.makeRequest(`${this.futuresBaseUrl}/marginType`, {
            method: 'POST',
            params: { symbol, marginType: margin.toUpperCase() }
        });
        await this.updateUserSettings(userId, { margin });
        return response;
    } catch (error) {
        console.error('Error setting margin:', error);
        throw new Error(error.message || 'Failed to set margin');
    }
}

async openPosition(data) {
    try {
        const { symbol, entryPrice, qPrecision, userId, leverage = 1, positionSide = 'BOTH' } = data;
        if (!symbol || !entryPrice || !qPrecision) {
            throw new Error('Missing required position parameters');
        }
        const amount = parseFloat(process.env.BINANCE_AMOUNT || '100') * leverage;
        const quantity = amount / entryPrice;
        const formattedQuantity = quantity.toFixed(qPrecision);
        const response = await this.makeRequest(`${this.futuresBaseUrl}/order`, {
            method: 'POST',
            params: {
                symbol,
                side: 'BUY',
                type: 'MARKET',
                quantity: formattedQuantity,
                positionSide
            }
        });
        await this.storeTradeDetails(userId, {
            ...data,
            orderId: response.orderId,
            status: 'OPEN',
            entryTime: Date.now()
        });
        return response;
    } catch (error) {
        console.error('Error opening position:', error);
        throw new Error(error.message || 'Failed to open position');
    }
}

async setTakeProfit(data, quantity) {
    try {
        const { symbol, takeProfit, qPrecision, positionSide = 'BOTH', entryPrice } = data;
        const formattedQuantity = quantity.toFixed(qPrecision);
        const response = await this.makeRequest(`${this.futuresBaseUrl}/order`, {
            method: 'POST',
            params: {
                symbol,
                side: 'SELL',
                quantity: formattedQuantity,
                type: 'TAKE_PROFIT_MARKET',
                stopPrice: takeProfit,
                positionSide
            }
        });

        const baseAsset = symbol.split('USDT')[0];
        const profitAmount = (takeProfit - entryPrice) * quantity;
        const clearTime = new Date(Date.now() + 24 * 60 * 60 * 1000);
        await this.updateBalanceInFirestore(baseAsset, profitAmount, clearTime);

        return response;
    } catch (error) {
        console.error('Error setting take profit:', error);
        throw new Error(error.message || 'Failed to set take profit');
    }
}

async getCoinBalance(coin) {
    try {
        if (!this.apiKey || !this.apiSecret) {
            console.warn('Binance API credentials not configured');
            return 0;
        }
        if (!coin) {
            console.warn('No coin specified for balance check');
            return 0;
        }
        const response = await this.makeRequest(`${this.spotBaseUrl}/account`, {
            method: 'GET'
        });
        const balance = response.balances.find(b => b.asset === coin);
        return balance ? parseFloat(balance.free || '0') + parseFloat(balance.locked || '0') : 0;
    } catch (error) {
        console.error('Error fetching coin balance:', error);
        return 0;
    }
}

}

// Export the BinanceService class as default
export default BinanceService;

here the spot processing code:
import { authOptions } from ‘@/lib/auth’;
import binanceService from ‘./binanceService’;
import { createTradeNotification } from ‘@/lib/notificationService’;
import { getAuth } from ‘firebase-admin/auth’;
import { getServerSession } from ‘next-auth’;

export default async function handler(req, res) {
const session = await getServerSession(req, res, authOptions);

if (!session) {
    return res.status(401).json({ error: 'Unauthorized' });
}

try {
    if (req.method !== 'POST') {
        return res.status(405).json({ error: 'Method not allowed' });
    }

    const { symbol, entryPrice, takeProfit, stopLoss, userId } = req.body;

    // Fetch and validate precision
    const { quantityPrecision, pricePrecision } = await binanceService.getSymbolInfo(symbol);
    if (parseInt(quantityPrecision) !== quantityPrecision || parseInt(pricePrecision) !== pricePrecision) {
        return res.status(400).json({ message: 'Invalid quantity or price precision' });
    }

    const order = await binanceService.placeSpotOrder(req.body);
    if (!order) {
        return res.status(400).json({ message: 'Failed to place spot order' });
    }

    // After successful trade execution, create notification
    await createTradeNotification(userId, {
        type: 'trade',
        title: 'New Trade Opened',
        message: `Opened ${symbol} trade at ${entryPrice}`,
        tradeId: 'trade-id-here', // Replace with actual trade ID
        profit: null,
        profitPercentage: null
    });

    // If take profit or stop loss is hit, create another notification
    if (takeProfit || stopLoss) {
        const price = takeProfit || stopLoss;
        const type = takeProfit ? 'take_profit' : 'stop_loss';
        const profit = type === 'take_profit' ? price - entryPrice : entryPrice - price;
        const profitPercentage = (profit / entryPrice) * 100;

        await createTradeNotification(userId, {
            type: 'trade_closed',
            title: `Trade ${type === 'take_profit' ? 'Take Profit' : 'Stop Loss'} Hit`,
            message: `${symbol} trade closed at ${price}`,
            tradeId: 'trade-id-here', // Replace with actual trade ID
            profit,
            profitPercentage
        });
    }

    return res.status(200).json({
        message: 'Spot trade processed successfully',
        order
    });
} catch (error) {
    console.error('Error processing spot trade:', error);
    return res.status(500).json({ error: 'Internal server error' });
}

}

here is the futures processing codes:

import binanceService from ‘./binanceService’;
import { getAuth } from ‘firebase-admin/auth’;
import telegramService from ‘./telegramService’;

export default async function handler(req, res) {
if (req.method !== ‘POST’) {
return res.status(405).json({ message: ‘Method not allowed’ });
}

try {
    // Verify Firebase Authentication
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({ message: 'Unauthorized' });
    }
    const token = authHeader.split('Bearer ')[1];
    const decodedToken = await getAuth().verifyIdToken(token);
    const userId = decodedToken.uid;

    const data = req.body;
    data.userId = userId;

    // Fetch and validate precision
    const { quantityPrecision, pricePrecision } = await binanceService.getSymbolInfo(data.symbol);
    if (parseInt(data.qPrecision) !== quantityPrecision || parseInt(data.pPrecision) !== pricePrecision) {
        return res.status(400).json({ message: 'Invalid quantity or price precision' });
    }

    const leverageResult = await binanceService.setLeverage(data);
    if (!leverageResult) {
        return res.status(400).json({ message: 'Failed to set leverage' });
    }

    await binanceService.setMargin(data);
    const order = await binanceService.openPosition(data);
    if (!order) {
        return res.status(400).json({ message: 'Failed to open position' });
    }

    const quantity = order.origQty;
    await binanceService.setTakeProfit(data, quantity);

     // Send to Telegram
     await telegramService.sendMessage(data);

    return res.status(200).json({
        message: 'Future trade processed successfully',
        order
    });
} catch (error) {
    console.error('Error processing future trade:', error);
    return res.status(500).json({
        message: 'Internal server error',
        error: error.message
    });
}

}