Skip to main content
Glama

MCP Server for Binance Spot Trading

by kydlikebtc
import { Spot } from '@binance/connector'; import { BinanceClientError, ApiKeyError, OrderValidationError, InsufficientMarginError, InvalidPositionModeError } from '../types/errors.js'; import { getApiKeys } from './keystore.js'; import * as fs from 'fs'; import * as path from 'path'; import { FuturesOrder, FuturesPosition, FuturesAccountInformation, LeverageSettings, FundingRate, PositionSide, MarginType, WorkingType } from '../types/futures.js'; const logFile = path.join(process.cwd(), 'logs', 'futures.log'); // 确保日志目录存在 if (!fs.existsSync(path.dirname(logFile))) { fs.mkdirSync(path.dirname(logFile), { recursive: true }); } function log(message: string) { const timestamp = new Date().toISOString(); fs.appendFileSync(logFile, `${timestamp} - ${message}\n`); } function logError(message: string, error?: unknown) { const timestamp = new Date().toISOString(); const errorMessage = error instanceof Error ? error.message : String(error); fs.appendFileSync(logFile, `${timestamp} - ERROR: ${message} ${error ? `- ${errorMessage}` : ''}\n`); } interface SpotExtended extends Spot { changeLeverage: (params: { symbol: string; leverage: number }) => Promise<any>; premiumIndex: (params: { symbol: string }) => Promise<any>; changePositionMode: (params: { dualSidePosition: boolean }) => Promise<any>; changeMarginType: (params: { symbol: string; marginType: MarginType }) => Promise<any>; klines: (symbol: string, interval: string, options?: any) => Promise<any>; } let futuresClient: SpotExtended | null = null; export async function initializeFuturesClient(): Promise<boolean> { log('Initializing Binance futures client...'); try { const keys = await getApiKeys(); if (!keys) { logError('No credentials available for Binance futures client'); throw new ApiKeyError('Futures API keys not found'); } const { apiKey, apiSecret } = keys; log('Creating Binance futures client...'); // Initialize client for USDⓈ-M Futures futuresClient = new Spot(apiKey, apiSecret) as SpotExtended; // Test the connection log('Testing Binance futures client connection...'); await futuresClient.account(); log('Successfully connected to Binance futures API'); return true; } catch (error) { logError('Failed to initialize futures client:', error); futuresClient = null; if (error instanceof ApiKeyError) { throw error; } throw new BinanceClientError(`Failed to initialize futures client: ${error instanceof Error ? error.message : 'Unknown error'}`); } } export async function createFuturesOrder(order: FuturesOrder): Promise<any> { if (!futuresClient) { logError('Attempted to create futures order without initialized client'); throw new BinanceClientError('Futures client not initialized'); } try { log(`Creating futures order: ${JSON.stringify(order, null, 2)}`); const params = { symbol: order.symbol, side: order.side, type: order.type, quantity: order.quantity, timeInForce: order.timeInForce || 'GTC' } as any; if (order.positionSide) params.positionSide = order.positionSide; if (order.price) params.price = order.price; if (order.stopPrice) params.stopPrice = order.stopPrice; if (order.reduceOnly !== undefined) params.reduceOnly = order.reduceOnly; if (order.workingType) params.workingType = order.workingType; if (order.activationPrice) params.activationPrice = order.activationPrice; if (order.callbackRate) params.callbackRate = order.callbackRate; if (order.closePosition !== undefined) params.closePosition = order.closePosition; if (order.priceProtect !== undefined) params.priceProtect = order.priceProtect; // Validate required parameters based on order type if (['LIMIT', 'STOP', 'TAKE_PROFIT'].includes(order.type) && !order.price) { throw new OrderValidationError(`Price is required for ${order.type} orders`); } if (['STOP', 'STOP_MARKET', 'TAKE_PROFIT', 'TAKE_PROFIT_MARKET'].includes(order.type) && !order.stopPrice) { throw new OrderValidationError(`Stop price is required for ${order.type} orders`); } if (order.type === 'TRAILING_STOP_MARKET' && !order.callbackRate) { throw new OrderValidationError('Callback rate is required for TRAILING_STOP_MARKET orders'); } log(`Sending futures order request with params: ${JSON.stringify(params, null, 2)}`); const response = await futuresClient.newOrder(params); log(`Futures order created successfully: ${JSON.stringify(response, null, 2)}`); return response.data; } catch (error) { logError('Error creating futures order:', error); if (error instanceof OrderValidationError) { throw error; } const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (errorMessage.includes('insufficient margin')) { logError('Insufficient margin for futures order'); throw new InsufficientMarginError(`Insufficient margin to create futures order: ${errorMessage}`); } if (errorMessage.includes('invalid position mode')) { logError('Invalid position mode for futures order'); throw new InvalidPositionModeError(`Invalid position mode for futures order: ${errorMessage}`); } throw new BinanceClientError(`Failed to create futures order: ${errorMessage}`); } } export async function getFuturesAccountInformation(): Promise<FuturesAccountInformation> { if (!futuresClient) { logError('Attempted to get futures account information without initialized client'); throw new BinanceClientError('Futures client not initialized'); } try { log('Fetching futures account information...'); const response = await futuresClient.account(); log('Successfully retrieved futures account information'); const accountInfo = { feeTier: 0, canTrade: true, canDeposit: true, canWithdraw: true, updateTime: Date.now(), totalInitialMargin: '0', totalMaintMargin: '0', totalWalletBalance: '0', totalUnrealizedProfit: '0', totalMarginBalance: '0', totalPositionInitialMargin: '0', totalOpenOrderInitialMargin: '0', totalCrossWalletBalance: '0', totalCrossUnPnl: '0', availableBalance: '0', maxWithdrawAmount: '0', assets: response.data.balances.map((balance: any) => ({ asset: balance.asset, walletBalance: balance.free, unrealizedProfit: '0', marginBalance: balance.locked, maintMargin: '0', initialMargin: '0', positionInitialMargin: '0', openOrderInitialMargin: '0', maxWithdrawAmount: balance.free, crossWalletBalance: '0', crossUnPnl: '0', availableBalance: balance.free })), positions: [] }; return accountInfo; } catch (error) { logError('Error getting futures account information:', error); throw new BinanceClientError(`Failed to get futures account information: ${error instanceof Error ? error.message : 'Unknown error'}`); } } export async function getFuturesPositions(): Promise<FuturesPosition[]> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { const response = await futuresClient.account(); const accountData = response.data as any; const positions = accountData.positions || []; return positions.map((pos: any) => ({ symbol: pos.symbol || '', positionAmt: pos.positionAmt || '0', entryPrice: pos.entryPrice || '0', markPrice: pos.markPrice || '0', unRealizedProfit: pos.unrealizedProfit || '0', liquidationPrice: pos.liquidationPrice || '0', leverage: parseInt(pos.leverage || '1'), marginType: pos.marginType || MarginType.CROSSED, isolatedMargin: pos.isolatedMargin || '0', positionSide: pos.positionSide || PositionSide.BOTH, updateTime: pos.updateTime || Date.now() })); } catch (error) { throw new BinanceClientError(`Failed to get futures positions: ${error instanceof Error ? error.message : 'Unknown error'}`); } } export async function setFuturesLeverage(settings: LeverageSettings): Promise<boolean> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { await futuresClient.changeLeverage({ symbol: settings.symbol, leverage: settings.leverage }); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (errorMessage.includes('insufficient margin')) { throw new InsufficientMarginError(`Insufficient margin to change leverage: ${errorMessage}`); } if (errorMessage.includes('invalid position mode')) { throw new InvalidPositionModeError(`Invalid position mode when changing leverage: ${errorMessage}`); } throw new BinanceClientError(`Failed to set futures leverage: ${errorMessage}`); } } export async function getFundingRate(symbol: string): Promise<FundingRate> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { const response = await futuresClient.premiumIndex({ symbol }); const data = response.data[0] || {}; return { symbol: data.symbol || symbol, fundingRate: data.lastFundingRate || '0', fundingTime: data.nextFundingTime || Date.now(), nextFundingTime: data.nextFundingTime || Date.now() }; } catch (error) { throw new BinanceClientError(`Failed to get funding rate: ${error instanceof Error ? error.message : 'Unknown error'}`); } } export async function cancelFuturesOrder(symbol: string, orderId: number): Promise<void> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { await futuresClient.cancelOrder(symbol, { orderId }); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; if (errorMessage.includes('invalid position mode')) { throw new InvalidPositionModeError(`Invalid position mode when canceling futures order: ${errorMessage}`); } throw new BinanceClientError(`Failed to cancel futures order: ${errorMessage}`); } } export async function getFuturesOpenOrders(symbol?: string): Promise<FuturesOrder[]> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { const params = symbol ? { symbol } : {}; const response = await futuresClient.openOrders(params); return response.data.map((order: any) => ({ symbol: order.symbol, side: order.side, type: order.type, quantity: order.origQty, price: order.price, timeInForce: order.timeInForce, positionSide: order.positionSide, stopPrice: order.stopPrice, workingType: order.workingType, closePosition: order.closePosition, activationPrice: order.activationPrice, callbackRate: order.callbackRate, priceProtect: order.priceProtect, reduceOnly: order.reduceOnly })); } catch (error) { throw new BinanceClientError(`Failed to get open futures orders: ${error instanceof Error ? error.message : 'Unknown error'}`); } } export async function changePositionMode(dualSidePosition: boolean): Promise<boolean> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { log(`Changing position mode to ${dualSidePosition ? 'dual-side' : 'one-way'}`); await futuresClient.changePositionMode({ dualSidePosition: dualSidePosition }); log('Position mode changed successfully'); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logError(`Failed to change position mode:`, error); throw new BinanceClientError(`Failed to change position mode: ${errorMessage}`); } } export async function changeMarginType(symbol: string, marginType: MarginType): Promise<boolean> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { log(`Changing margin type for ${symbol} to ${marginType}`); await futuresClient.changeMarginType({ symbol: symbol, marginType: marginType }); log('Margin type changed successfully'); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logError(`Failed to change margin type:`, error); if (errorMessage.includes('isolated balance insufficient')) { throw new InsufficientMarginError(`Insufficient margin to change margin type: ${errorMessage}`); } throw new BinanceClientError(`Failed to change margin type: ${errorMessage}`); } } export async function getFuturesKlines(symbol: string, interval: string, limit?: number): Promise<any[]> { if (!futuresClient) { throw new BinanceClientError('Futures client not initialized'); } try { const params: any = {}; if (limit) params.limit = limit; log(`Querying futures klines for ${symbol}, interval ${interval}`); const response = await futuresClient.klines(symbol, interval, params); log('Futures klines retrieved successfully'); // 转换返回数据为更易于使用的格式 return response.data.map((kline: any[]) => ({ openTime: kline[0], open: kline[1], high: kline[2], low: kline[3], close: kline[4], volume: kline[5], closeTime: kline[6], quoteAssetVolume: kline[7], trades: kline[8], takerBuyBaseAssetVolume: kline[9], takerBuyQuoteAssetVolume: kline[10] })); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; logError(`Failed to get futures klines:`, error); throw new BinanceClientError(`Failed to get futures klines: ${errorMessage}`); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kydlikebtc/mcp-server-bn'

If you have feedback or need assistance with the MCP directory API, please join our Discord server