# Directory Structure
```
├── .dockerignore
├── .gitignore
├── .npmrc
├── Dockerfile
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── README_CN.md
├── README.md
├── smithery.yaml
├── src
│ ├── index.ts
│ ├── models
│ │ └── db.ts
│ ├── services
│ │ ├── binance.ts
│ │ ├── binanceFutures.ts
│ │ ├── keystore.ts
│ │ └── tools.ts
│ └── types
│ ├── binance-connector.d.ts
│ ├── binance.ts
│ ├── errors.ts
│ └── futures.ts
├── start-bn-service.sh
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
node_modules/
build/
*.log
.env*
data/*
.vscode/*
.env
```
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
```
node_modules
npm-debug.log
build
.env
.git
.gitignore
README.md
.DS_Store
```
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
```
enable-pre-post-scripts=true
unsafe-perm=true
node-linker=hoisted
ignore-scripts=false
public-hoist-pattern[]=*
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# mcp-server-cex-bn
[](https://smithery.ai/server/mcp-server-cex-bn)
This MCP Server provides comprehensive integration with Binance's spot and futures trading operations.
[中文说明](README_CN.md)
## Features
### Spot Trading Operations
- Execute spot trading operations (LIMIT/MARKET orders)
- Monitor account balances
- Track and manage open orders
- Cancel existing orders
### Futures Trading Operations
- Create various types of futures orders (LIMIT, MARKET, STOP, TAKE_PROFIT, etc.)
- Manage leverage settings (1-125x)
- Monitor futures positions and account information
- Track funding rates
- Support for both one-way and hedge mode positions
- Advanced order types including trailing stops and reduce-only orders
### Tools
#### API Configuration
##### `configure_api_keys`
Securely store your Binance API credentials:
```typescript
await configureBinanceApiKeys({
apiKey: 'your-api-key',
apiSecret: 'your-api-secret'
});
```
#### Spot Trading Tools
##### `create_spot_order`
Create LIMIT or MARKET orders:
```typescript
// LIMIT order
await createSpotOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT',
quantity: '0.001',
price: '40000'
});
// MARKET order
await createSpotOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'MARKET',
quantity: '0.001'
});
```
##### `cancel_order`
Cancel an existing order:
```typescript
await cancelOrder({
symbol: 'BTCUSDT',
orderId: '12345678'
});
```
##### `get_balances`
Check your account balances:
```typescript
const balances = await getBalances();
// Returns: { BTC: '0.1', USDT: '1000', ... }
```
##### `get_open_orders`
List all open orders:
```typescript
const orders = await getOpenOrders({
symbol: 'BTCUSDT' // Optional: specify symbol
});
```
#### Futures Trading Tools
##### `create_futures_order`
Create various types of futures orders:
```typescript
// LIMIT order
await createFuturesOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT',
quantity: '0.001',
price: '40000',
timeInForce: 'GTC'
});
// STOP MARKET order
await createFuturesOrder({
symbol: 'BTCUSDT',
side: 'SELL',
type: 'STOP_MARKET',
quantity: '0.001',
stopPrice: '38000'
});
// TRAILING STOP order
await createFuturesOrder({
symbol: 'BTCUSDT',
side: 'SELL',
type: 'TRAILING_STOP_MARKET',
quantity: '0.001',
callbackRate: '1.0' // 1% callback rate
});
```
##### `set_futures_leverage`
Adjust leverage for a trading pair:
```typescript
await setFuturesLeverage({
symbol: 'BTCUSDT',
leverage: 10 // 1-125x
});
```
##### `get_futures_positions`
Get all open futures positions:
```typescript
const positions = await getFuturesPositions();
```
##### `get_futures_account`
Get detailed futures account information:
```typescript
const account = await getFuturesAccount();
```
##### `get_funding_rate`
Get funding rate for a futures symbol:
```typescript
const fundingRate = await getFundingRate({
symbol: 'BTCUSDT'
});
```
##### `cancel_futures_order`
Cancel an existing futures order:
```typescript
await cancelFuturesOrder({
symbol: 'BTCUSDT',
orderId: '12345678'
});
```
## Futures Trading Details
### Position Modes
- One-way Mode: Single position per symbol
* Default mode, simpler position management
* Total position size is the sum of all orders
- Hedge Mode: Separate long and short positions
* Allows holding both long and short positions simultaneously
* Each position has independent margin requirements
### Margin Types
- Isolated Margin: Fixed margin per position
* Risk is limited to the allocated margin
* Each position has its own leverage setting
- Cross Margin: Shared margin across positions
* Higher capital efficiency
* Shared risk across all positions
### Funding Rate
Perpetual futures contracts use funding rates to keep futures prices aligned with spot prices:
- Positive rate: Longs pay shorts
- Negative rate: Shorts pay longs
- Payments occur every 8 hours
## Security Considerations
### Spot Trading Security
- Never commit API keys to version control
- Use environment variables or secure key storage
- Restrict API key permissions to only required operations
- Regularly rotate your API keys
### Futures Trading Security
- Set appropriate leverage limits based on risk tolerance
- Always use stop-loss orders to limit potential losses
- Monitor liquidation prices carefully
- Regularly check position risks and margin ratios
- Consider using reduce-only orders for risk management
- Be cautious with cross-margin due to shared risk
## Rate Limits
- Respect Binance API rate limits
- Default rate limits:
- 1200 requests per minute for order operations
- 100 requests per second for market data
- Implement proper error handling for rate limit errors
## Error Handling
### Common Error Scenarios
- Invalid API credentials
- Insufficient balance or margin
- Invalid order parameters
- Rate limit exceeded
- Network connectivity issues
### Futures-Specific Errors
- InsufficientMarginError: Not enough margin for operation
- InvalidPositionModeError: Wrong position mode setting
- OrderValidationError: Invalid futures order parameters
Example error handling:
```typescript
try {
await createFuturesOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT',
quantity: '0.001',
price: '40000',
timeInForce: 'GTC'
});
} catch (error) {
if (error instanceof InsufficientMarginError) {
console.error('Insufficient margin available');
} else if (error instanceof InvalidPositionModeError) {
console.error('Invalid position mode');
} else if (error instanceof OrderValidationError) {
console.error('Invalid order parameters');
}
}
```
## Project Structure
```
.
├── src/
│ ├── index.ts # Server entry point
│ ├── services/
│ │ ├── binance.ts # Binance API integration
│ │ ├── keystore.ts # API key management
│ │ └── tools.ts # Trading tools implementation
│ └── types/
│ ├── binance.ts # Binance types
│ └── binance-connector.d.ts # API client types
├── README.md
├── README_CN.md
├── package.json
├── pnpm-lock.yaml
└── tsconfig.json
```
## Development
1. Set up environment variables:
create `.env` file in the root directory, and set your Binance API credentials:
```txt
BINANCE_API_KEY=your_api_key_here
BINANCE_API_SECRET=your_secret_key_here
```
2. Install dependencies:
```bash
pnpm install
```
Build the server:
```bash
pnpm build
```
For development with auto-rebuild:
```bash
pnpm watch
```
## Installation
### Installing via Smithery
To install Binance Trading Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-server-cex-bn):
```bash
npx -y @smithery/cli install mcp-server-cex-bn --client claude
```
### Installing manually
1. Clone the repository
2. Install dependencies:
```bash
pnpm install
```
3. Configure your Binance API credentials in `.env`
4. Build and start the server:
```bash
pnpm build
pnpm start
```
### Debugging
Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:
```bash
pnpm inspector
```
The Inspector will provide a URL to access debugging tools in your browser.
# mcp-server-bn
```
--------------------------------------------------------------------------------
/start-bn-service.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
cd "$(dirname "$0")"
export BINANCE_API_KEY="rLV9ffU8oyqanh5znqit08j8CnILLvDCkRgcxdx2ePdVBxmxSYqgvSNeybyx2mtL"
export BINANCE_API_SECRET="ukawP66HgMDvLnSJOYAk7qkJEbdIvlcDwQuc2WxoD7E0FZJ5TupKhP4WsCbAeECd"
node build/index.js
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/src/models/db.ts:
--------------------------------------------------------------------------------
```typescript
import sqlite3 from "sqlite3";
export function getDb(): sqlite3.Database {
const dbName = process.env.BINANCE_DB_PATH || "";
if (!dbName) {
throw new Error("BINANCE_DB_PATH is not set");
}
const db = new sqlite3.Database(dbName, (err) => {
if (err) {
console.error("binance db connect failed: ", dbName, err.message);
return;
}
});
return db;
}
```
--------------------------------------------------------------------------------
/src/types/errors.ts:
--------------------------------------------------------------------------------
```typescript
export class BinanceClientError extends Error {
constructor(message: string) {
super(message);
this.name = 'BinanceClientError';
}
}
export class ApiKeyError extends Error {
constructor(message: string) {
super(message);
this.name = 'ApiKeyError';
}
}
export class OrderValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'OrderValidationError';
}
}
export class InsufficientMarginError extends Error {
constructor(message: string) {
super(message);
this.name = 'InsufficientMarginError';
}
}
export class InvalidPositionModeError extends Error {
constructor(message: string) {
super(message);
this.name = 'InvalidPositionModeError';
}
}
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
required:
- binanceApiKey
- binanceApiSecret
properties:
binanceApiKey:
type: string
description: The API key for accessing the Binance API.
binanceApiSecret:
type: string
description: The secret key for accessing the Binance API.
commandFunction:
# A function that produces the CLI command to start the MCP on stdio.
|-
(config) => ({ command: 'pnpm', args: ['start'], env: { BINANCE_API_KEY: config.binanceApiKey, BINANCE_API_SECRET: config.binanceApiSecret } })
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Build stage
FROM node:20-alpine AS builder
# Set the working directory
WORKDIR /app
# Install build dependencies
RUN apk add --no-cache python3 make g++ git
# Install pnpm
RUN npm install -g pnpm@latest typescript
# Copy package files
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install --frozen-lockfile --ignore-scripts
# Copy source files
COPY . .
# Build TypeScript
RUN pnpm exec tsc
# Production stage
FROM node:20-alpine
WORKDIR /app
# Install runtime dependencies
RUN apk add --no-cache python3 make g++
# Copy package files and install production dependencies
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm@latest && \
pnpm install --prod --frozen-lockfile --ignore-scripts
# Copy built files
COPY --from=builder /app/build ./build
# Set environment variables
ENV NODE_ENV=production
# Start the application
CMD ["node", "build/index.js"]
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "@kydfeng/mcp-server-cex-bn",
"version": "0.1.2",
"description": "MCP Server for Binance Spot Trading",
"type": "module",
"bin": {
"mcp-server-cex-bn": "./build/index.js"
},
"files": [
"build"
],
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"prepare": "npm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
},
"dependencies": {
"@binance/connector": "^3.6.1",
"@modelcontextprotocol/inspector": "^0.4.1",
"@modelcontextprotocol/sdk": "1.6.0",
"@types/keytar": "^4.4.2",
"dotenv": "^16.4.6",
"keytar": "^7.9.0",
"node-binance-api": "^0.13.1",
"sqlite3": "^5.1.7"
},
"devDependencies": {
"@types/node": "^20.17.9",
"typescript": "^5.3.3"
},
"keywords": [
"mcp",
"binance",
"trading",
"crypto",
"model-context-protocol"
],
"author": "kydfeng",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/kydfeng/mcp-server-bn.git"
}
}
```
--------------------------------------------------------------------------------
/src/types/binance-connector.d.ts:
--------------------------------------------------------------------------------
```typescript
declare module '@binance/connector' {
export class Spot {
constructor(apiKey: string, apiSecret: string);
usdmFutures: {
ping(): Promise<any>;
account(): Promise<any>;
positionRisk(): Promise<any>;
openOrders(params?: { symbol?: string }): Promise<any>;
newOrder(params: any): Promise<any>;
cancelOrder(params: any): Promise<any>;
leverage(params: any): Promise<any>;
fundingRate(params: any): Promise<any>;
};
newOrder(params: {
symbol: string;
side: 'BUY' | 'SELL';
type: 'LIMIT' | 'MARKET';
quantity?: string;
price?: string;
timeInForce?: 'GTC' | 'IOC' | 'FOK';
}): Promise<{ data: any }>;
cancelOrder(symbol: string, params: { orderId: number }): Promise<void>;
account(): Promise<{
data: {
balances: Array<{
asset: string;
free: string;
locked: string;
}>;
};
}>;
openOrders(params?: { symbol?: string }): Promise<{
data: Array<{
symbol: string;
orderId: number;
orderListId: number;
clientOrderId: string;
price: string;
origQty: string;
executedQty: string;
status: string;
timeInForce: string;
type: string;
side: string;
}>;
}>;
}
}
```
--------------------------------------------------------------------------------
/src/services/keystore.ts:
--------------------------------------------------------------------------------
```typescript
import dotenv from 'dotenv';
import * as fs from 'fs';
import * as path from 'path';
// Load environment variables
dotenv.config();
const logFile = path.join(process.cwd(), 'logs', 'keystore.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`);
}
export async function storeApiKeys(apiKey: string, apiSecret: string): Promise<void> {
process.env.BINANCE_API_KEY = apiKey;
process.env.BINANCE_API_SECRET = apiSecret;
log('API keys stored in environment variables');
}
export async function getApiKeys(): Promise<{ apiKey: string; apiSecret: string } | null> {
const envApiKey = process.env.BINANCE_API_KEY;
const envApiSecret = process.env.BINANCE_API_SECRET;
if (envApiKey && envApiSecret) {
log('Found API keys in environment variables');
return { apiKey: envApiKey, apiSecret: envApiSecret };
}
log('No API keys found in environment variables');
return null;
}
export async function deleteApiKeys(): Promise<void> {
delete process.env.BINANCE_API_KEY;
delete process.env.BINANCE_API_SECRET;
log('Cleared API keys from environment variables');
}
```
--------------------------------------------------------------------------------
/src/types/binance.ts:
--------------------------------------------------------------------------------
```typescript
export interface BinanceCredentials {
apiKey: string;
apiSecret: string;
}
export interface SpotOrder {
symbol: string;
side: 'BUY' | 'SELL';
type: 'LIMIT' | 'MARKET';
quantity?: string;
quoteOrderQty?: string;
price?: string;
timeInForce?: 'GTC' | 'IOC' | 'FOK';
}
export interface OrderResponse {
symbol: string;
orderId: number;
orderListId: number;
clientOrderId: string;
transactTime: number;
price: string;
origQty: string;
executedQty: string;
status: string;
timeInForce: string;
type: string;
side: string;
}
export interface AccountBalance {
asset: string;
free: string;
locked: string;
}
export interface SymbolFilter {
filterType: string;
minPrice?: string;
maxPrice?: string;
tickSize?: string;
minQty?: string;
maxQty?: string;
stepSize?: string;
minNotional?: string;
applyToMarket?: boolean;
limit?: number;
maxNumOrders?: number;
maxNumAlgoOrders?: number;
}
export interface SymbolInfo {
symbol: string;
status: string;
baseAsset: string;
baseAssetPrecision: number;
quoteAsset: string;
quotePrecision: number;
quoteAssetPrecision: number;
orderTypes: string[];
icebergAllowed: boolean;
ocoAllowed: boolean;
isSpotTradingAllowed: boolean;
isMarginTradingAllowed: boolean;
filters: SymbolFilter[];
permissions: string[];
}
export interface ExchangeInfo {
timezone: string;
serverTime: number;
rateLimits: any[];
exchangeFilters: any[];
symbols: SymbolInfo[];
}
export interface KlineData {
openTime: number;
open: string;
high: string;
low: string;
close: string;
volume: string;
closeTime: number;
quoteAssetVolume: string;
trades: number;
takerBuyBaseAssetVolume: string;
takerBuyQuoteAssetVolume: string;
}
```
--------------------------------------------------------------------------------
/src/types/futures.ts:
--------------------------------------------------------------------------------
```typescript
export enum PositionSide {
BOTH = 'BOTH',
LONG = 'LONG',
SHORT = 'SHORT'
}
export enum MarginType {
ISOLATED = 'ISOLATED',
CROSSED = 'CROSSED'
}
export enum WorkingType {
MARK_PRICE = 'MARK_PRICE',
CONTRACT_PRICE = 'CONTRACT_PRICE'
}
export enum TimeInForce {
GTC = 'GTC', // Good Till Cancel
IOC = 'IOC', // Immediate or Cancel
FOK = 'FOK', // Fill or Kill
GTX = 'GTX' // Good Till Crossing
}
export interface FuturesOrder {
symbol: string;
side: 'BUY' | 'SELL';
positionSide?: PositionSide;
type: 'LIMIT' | 'MARKET' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET' | 'TRAILING_STOP_MARKET';
quantity: string;
price?: string;
stopPrice?: string;
reduceOnly?: boolean;
workingType?: WorkingType;
timeInForce?: TimeInForce;
activationPrice?: string;
callbackRate?: string;
closePosition?: boolean;
priceProtect?: boolean;
}
export interface FuturesPosition {
symbol: string;
positionAmt: string;
entryPrice: string;
markPrice: string;
unRealizedProfit: string;
liquidationPrice: string;
leverage: number;
marginType: MarginType;
isolatedMargin: string;
positionSide: PositionSide;
updateTime: number;
}
export interface FundingRate {
symbol: string;
fundingRate: string;
fundingTime: number;
nextFundingTime: number;
}
export interface LeverageSettings {
symbol: string;
leverage: number;
maxNotionalValue?: string;
}
export interface FuturesAccountBalance {
asset: string;
walletBalance: string;
unrealizedProfit: string;
marginBalance: string;
maintMargin: string;
initialMargin: string;
positionInitialMargin: string;
openOrderInitialMargin: string;
maxWithdrawAmount: string;
crossWalletBalance: string;
crossUnPnl: string;
availableBalance: string;
}
export interface FuturesAccountInformation {
feeTier: number;
canTrade: boolean;
canDeposit: boolean;
canWithdraw: boolean;
updateTime: number;
totalInitialMargin: string;
totalMaintMargin: string;
totalWalletBalance: string;
totalUnrealizedProfit: string;
totalMarginBalance: string;
totalPositionInitialMargin: string;
totalOpenOrderInitialMargin: string;
totalCrossWalletBalance: string;
totalCrossUnPnl: string;
availableBalance: string;
maxWithdrawAmount: string;
assets: FuturesAccountBalance[];
positions: FuturesPosition[];
}
```
--------------------------------------------------------------------------------
/src/services/binance.ts:
--------------------------------------------------------------------------------
```typescript
import { Spot } from '@binance/connector';
import { BinanceCredentials, SpotOrder, OrderResponse, AccountBalance, ExchangeInfo } from '../types/binance.js';
import { BinanceClientError, OrderValidationError } from '../types/errors.js';
import { getApiKeys } from './keystore.js';
import * as fs from 'fs';
import * as path from 'path';
const logFile = path.join(process.cwd(), 'logs', 'binance.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`);
}
let client: Spot | null = null;
export async function initializeBinanceClient(): Promise<boolean> {
log('Initializing Binance spot client...');
const credentials = await getApiKeys();
if (!credentials) {
log('No credentials available for Binance spot client');
return false;
}
try {
log('Creating Binance spot client...');
client = new Spot(credentials.apiKey, credentials.apiSecret);
// Test the connection
log('Testing Binance spot client connection...');
await client.account();
log('Successfully connected to Binance spot API');
return true;
} catch (error) {
logError('Failed to initialize Binance spot client:', error);
client = null;
return false;
}
}
export async function createSpotOrder(order: SpotOrder): Promise<OrderResponse> {
if (!client) {
throw new BinanceClientError('Binance client not initialized');
}
try {
log(`Creating spot order: ${JSON.stringify(order)}`);
const params: any = {
symbol: order.symbol,
side: order.side,
type: order.type,
};
// 处理标准订单参数
if (order.quantity) params.quantity = order.quantity;
if (order.price) params.price = order.price;
if (order.timeInForce) params.timeInForce = order.timeInForce;
// 处理报价资产数量订单参数
if (order.quoteOrderQty) {
params.quoteOrderQty = order.quoteOrderQty;
log(`Using quoteOrderQty: ${order.quoteOrderQty}`);
}
if (order.type === 'LIMIT' && !order.price) {
throw new OrderValidationError('Price is required for LIMIT orders');
}
// 对于市价单,必须提供quantity或quoteOrderQty其中之一
if (order.type === 'MARKET' && !order.quantity && !order.quoteOrderQty) {
throw new OrderValidationError('For MARKET orders, either quantity or quoteOrderQty must be provided');
}
log(`Sending order with params: ${JSON.stringify(params)}`);
const response = await client.newOrder(params);
log(`Order created successfully: ${JSON.stringify(response.data)}`);
return response.data;
} catch (error) {
if (error instanceof OrderValidationError) {
throw error;
}
if (error instanceof Error) {
logError(`Failed to create spot order:`, error);
throw new BinanceClientError(`Failed to create spot order: ${error.message}`);
}
logError('Failed to create spot order: Unknown error', error);
throw new BinanceClientError('Failed to create spot order: Unknown error');
}
}
export async function cancelOrder(symbol: string, orderId: number): Promise<void> {
if (!client) {
throw new BinanceClientError('Binance client not initialized');
}
try {
await client.cancelOrder(symbol, { orderId });
} catch (error) {
if (error instanceof Error) {
throw new BinanceClientError(`Failed to cancel order: ${error.message}`);
}
throw new BinanceClientError('Failed to cancel order: Unknown error');
}
}
export async function getAccountBalances(): Promise<AccountBalance[]> {
if (!client) {
throw new BinanceClientError('Binance client not initialized');
}
try {
const response = await client.account();
return response.data.balances;
} catch (error) {
if (error instanceof Error) {
throw new BinanceClientError(`Failed to get account balances: ${error.message}`);
}
throw new BinanceClientError('Failed to get account balances: Unknown error');
}
}
export async function getOpenOrders(symbol?: string): Promise<OrderResponse[]> {
if (!client) {
throw new BinanceClientError('Binance client not initialized');
}
try {
const params = symbol ? { symbol } : {};
const response = await client.openOrders(params);
return response.data.map(order => ({
...order,
transactTime: Date.now()
}));
} catch (error) {
if (error instanceof Error) {
throw new BinanceClientError(`Failed to get open orders: ${error.message}`);
}
throw new BinanceClientError('Failed to get open orders: Unknown error');
}
}
// 仅保留可以确认正常工作的API
// 其他功能暂时移除
```
--------------------------------------------------------------------------------
/src/services/tools.ts:
--------------------------------------------------------------------------------
```typescript
export const configureApiKeysTool = {
name: "configure_api_keys",
description: "Configure Binance API keys for trading",
inputSchema: {
type: "object",
properties: {
apiKey: {
type: "string",
description: "Binance API key"
},
apiSecret: {
type: "string",
description: "Binance API secret"
}
},
required: ["apiKey", "apiSecret"],
},
};
export const createOrderTool = {
name: "create_spot_order",
description: "Create a new spot order on Binance",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (e.g., BTCUSDT)"
},
side: {
type: "string",
enum: ["BUY", "SELL"],
description: "Order side"
},
type: {
type: "string",
enum: ["LIMIT", "MARKET"],
description: "Order type"
},
quantity: {
type: "string",
description: "Order quantity (amount of base asset)"
},
quoteOrderQty: {
type: "string",
description: "Quote order quantity (amount of quote asset to spend or receive, e.g. USDT)"
},
price: {
type: "string",
description: "Order price (required for LIMIT orders)"
},
timeInForce: {
type: "string",
enum: ["GTC", "IOC", "FOK"],
description: "Time in force"
}
},
required: ["symbol", "side", "type"],
},
};
export const cancelOrderTool = {
name: "cancel_order",
description: "Cancel an existing order",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (e.g., BTCUSDT)"
},
orderId: {
type: "number",
description: "Order ID to cancel"
}
},
required: ["symbol", "orderId"],
},
};
export const getBalancesTool = {
name: "get_balances",
description: "Get account balances",
inputSchema: {
type: "object",
properties: {
random_string: {
type: "string",
description: "Dummy parameter for no-parameter tools"
}
},
required: ["random_string"],
},
};
export const getOpenOrdersTool = {
name: "get_open_orders",
description: "Get open orders",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (optional)"
}
},
required: [],
},
};
export const createFuturesOrderTool = {
name: "create_futures_order",
description: "Create a new futures order on Binance",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (e.g., BTCUSDT)"
},
side: {
type: "string",
enum: ["BUY", "SELL"],
description: "Order side"
},
positionSide: {
type: "string",
enum: ["BOTH", "LONG", "SHORT"],
description: "Position side"
},
type: {
type: "string",
enum: ["LIMIT", "MARKET", "STOP", "STOP_MARKET", "TAKE_PROFIT", "TAKE_PROFIT_MARKET", "TRAILING_STOP_MARKET"],
description: "Order type"
},
quantity: {
type: "string",
description: "Order quantity"
},
price: {
type: "string",
description: "Order price (required for LIMIT orders)"
},
stopPrice: {
type: "string",
description: "Stop price (required for STOP/TAKE_PROFIT orders)"
},
timeInForce: {
type: "string",
enum: ["GTC", "IOC", "FOK", "GTX"],
description: "Time in force"
},
reduceOnly: {
type: "boolean",
description: "Reduce only flag"
},
closePosition: {
type: "boolean",
description: "Close position flag"
}
},
required: ["symbol", "side", "type", "quantity"],
},
};
export const cancelFuturesOrderTool = {
name: "cancel_futures_order",
description: "Cancel an existing futures order",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (e.g., BTCUSDT)"
},
orderId: {
type: "number",
description: "Order ID to cancel"
}
},
required: ["symbol", "orderId"],
},
};
export const getFuturesPositionsTool = {
name: "get_futures_positions",
description: "Get all futures positions",
inputSchema: {
type: "object",
properties: {},
required: [],
},
};
export const setFuturesLeverageTool = {
name: "set_futures_leverage",
description: "Set leverage for a futures symbol",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (e.g., BTCUSDT)"
},
leverage: {
type: "number",
description: "Leverage value (1-125)"
}
},
required: ["symbol", "leverage"],
},
};
export const getFuturesAccountTool = {
name: "get_futures_account",
description: "Get futures account information",
inputSchema: {
type: "object",
properties: {},
required: [],
},
};
export const getFuturesOpenOrdersTool = {
name: "get_futures_open_orders",
description: "Get open futures orders",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (optional)"
}
},
required: [],
},
};
export const getFundingRateTool = {
name: "get_funding_rate",
description: "Get funding rate for a futures symbol",
inputSchema: {
type: "object",
properties: {
symbol: {
type: "string",
description: "Trading pair symbol (e.g., BTCUSDT)"
}
},
required: ["symbol"],
},
};
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { SpotOrder } from "./types/binance.js";
import {
storeApiKeys,
getApiKeys,
deleteApiKeys,
} from "./services/keystore.js";
import {
createSpotOrder,
cancelOrder,
getAccountBalances,
getOpenOrders,
} from "./services/binance.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import dotenv from "dotenv";
import { initializeBinanceClient } from "./services/binance.js";
import {
configureApiKeysTool,
createOrderTool,
cancelOrderTool,
getBalancesTool,
getOpenOrdersTool,
createFuturesOrderTool,
cancelFuturesOrderTool,
getFuturesPositionsTool,
setFuturesLeverageTool,
getFuturesAccountTool,
getFuturesOpenOrdersTool,
getFundingRateTool,
} from "./services/tools.js";
import {
createFuturesOrder,
cancelFuturesOrder,
getFuturesPositions,
setFuturesLeverage,
getFuturesAccountInformation as getFuturesAccount,
getFuturesOpenOrders,
getFundingRate,
initializeFuturesClient,
changePositionMode,
changeMarginType,
getFuturesKlines,
} from "./services/binanceFutures.js";
import { FuturesOrder, LeverageSettings, TimeInForce, PositionSide, WorkingType, MarginType } from "./types/futures.js";
import * as fs from 'fs';
import * as path from 'path';
import { z } from "zod";
// Load environment variables first
dotenv.config();
const logFile = path.join(process.cwd(), 'logs', 'server.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`);
}
// 创建高级McpServer实例,而不是低级Server实例
const server = new McpServer({
name: "mcp-server-binance",
version: "0.1.0",
});
// 注册工具
server.tool(
"configure_api_keys",
{
apiKey: z.string().describe("Binance API key"),
apiSecret: z.string().describe("Binance API secret")
},
async ({ apiKey, apiSecret }) => {
await storeApiKeys(apiKey, apiSecret);
const spotInitialized = await initializeBinanceClient();
const futuresInitialized = await initializeFuturesClient();
return {
content: [
{
type: "text",
text: spotInitialized && futuresInitialized
? "API keys configured successfully"
: "Failed to initialize Binance clients",
},
],
};
}
);
server.tool(
"create_spot_order",
{
symbol: z.string().describe("Trading pair symbol (e.g., BTCUSDT)"),
side: z.enum(["BUY", "SELL"]).describe("Order side"),
type: z.enum(["LIMIT", "MARKET"]).describe("Order type"),
quantity: z.string().optional().describe("Order quantity (amount of base asset)"),
quoteOrderQty: z.string().optional().describe("Quote order quantity (amount of quote asset to spend or receive, e.g. USDT)"),
price: z.string().optional().describe("Order price (required for LIMIT orders)"),
timeInForce: z.enum(["GTC", "IOC", "FOK"]).optional().describe("Time in force")
},
async (args) => {
const order: SpotOrder = {
symbol: args.symbol,
side: args.side,
type: args.type,
quantity: args.quantity,
quoteOrderQty: args.quoteOrderQty,
price: args.price,
timeInForce: args.timeInForce,
};
const response = await createSpotOrder(order);
return {
content: [
{
type: "text",
text: JSON.stringify(response),
},
],
};
}
);
server.tool(
"cancel_order",
{
symbol: z.string().describe("Trading pair symbol (e.g., BTCUSDT)"),
orderId: z.number().describe("Order ID to cancel")
},
async ({ symbol, orderId }) => {
await cancelOrder(symbol, orderId);
return {
content: [
{
type: "text",
text: "Order cancelled successfully",
},
],
};
}
);
server.tool(
"get_balances",
{},
async () => {
const balances = await getAccountBalances();
return {
content: [
{
type: "text",
text: JSON.stringify(balances),
},
],
};
}
);
server.tool(
"get_open_orders",
{
symbol: z.string().optional().describe("Trading pair symbol (optional)")
},
async ({ symbol }) => {
const orders = await getOpenOrders(symbol);
return {
content: [
{
type: "text",
text: JSON.stringify(orders),
},
],
};
}
);
server.tool(
"create_futures_order",
{
symbol: z.string().describe("Trading pair symbol (e.g., BTCUSDT)"),
side: z.enum(["BUY", "SELL"]).describe("Order side"),
type: z.enum([
"LIMIT", "MARKET", "STOP", "STOP_MARKET",
"TAKE_PROFIT", "TAKE_PROFIT_MARKET", "TRAILING_STOP_MARKET"
]).describe("Order type"),
quantity: z.string().describe("Order quantity"),
price: z.string().optional().describe("Order price (required for LIMIT orders)"),
stopPrice: z.string().optional().describe("Stop price (required for STOP orders)"),
timeInForce: z.enum(["GTC", "IOC", "FOK", "GTX"]).optional().describe("Time in force"),
reduceOnly: z.boolean().optional().describe("Reduce only flag"),
closePosition: z.boolean().optional().describe("Close position flag"),
positionSide: z.enum(["BOTH", "LONG", "SHORT"]).optional().describe("Position side"),
workingType: z.enum(["MARK_PRICE", "CONTRACT_PRICE"]).optional().describe("Working type"),
priceProtect: z.boolean().optional().describe("Price protect flag"),
activationPrice: z.string().optional().describe("Activation price for TRAILING_STOP_MARKET orders"),
callbackRate: z.string().optional().describe("Callback rate for TRAILING_STOP_MARKET orders")
},
async (args) => {
const order: FuturesOrder = {
symbol: args.symbol,
side: args.side,
type: args.type,
quantity: args.quantity,
price: args.price,
stopPrice: args.stopPrice,
timeInForce: args.timeInForce as TimeInForce,
reduceOnly: args.reduceOnly,
closePosition: args.closePosition,
positionSide: args.positionSide as PositionSide,
workingType: args.workingType as WorkingType,
priceProtect: args.priceProtect,
activationPrice: args.activationPrice,
callbackRate: args.callbackRate,
};
const response = await createFuturesOrder(order);
return {
content: [
{
type: "text",
text: JSON.stringify(response),
},
],
};
}
);
server.tool(
"cancel_futures_order",
{
symbol: z.string().describe("Trading pair symbol (e.g., BTCUSDT)"),
orderId: z.number().describe("Order ID to cancel")
},
async ({ symbol, orderId }) => {
await cancelFuturesOrder(symbol, orderId);
return {
content: [
{
type: "text",
text: "Futures order cancelled successfully",
},
],
};
}
);
server.tool(
"get_futures_positions",
{},
async () => {
const positions = await getFuturesPositions();
return {
content: [
{
type: "text",
text: JSON.stringify(positions),
},
],
};
}
);
server.tool(
"set_futures_leverage",
{
symbol: z.string().describe("Trading pair symbol (e.g., BTCUSDT)"),
leverage: z.number().describe("Leverage value (1-125)")
},
async ({ symbol, leverage }) => {
const settings: LeverageSettings = {
symbol: symbol,
leverage: leverage,
};
await setFuturesLeverage(settings);
return {
content: [
{
type: "text",
text: "Leverage set successfully",
},
],
};
}
);
server.tool(
"get_futures_account",
{},
async () => {
const account = await getFuturesAccount();
return {
content: [
{
type: "text",
text: JSON.stringify(account),
},
],
};
}
);
server.tool(
"get_futures_open_orders",
{
symbol: z.string().optional().describe("Trading pair symbol (optional)")
},
async ({ symbol }) => {
const orders = await getFuturesOpenOrders(symbol);
return {
content: [
{
type: "text",
text: JSON.stringify(orders),
},
],
};
}
);
server.tool(
"get_funding_rate",
{
symbol: z.string().describe("Trading pair symbol (e.g., BTCUSDT)")
},
async ({ symbol }) => {
const rate = await getFundingRate(symbol);
return {
content: [
{
type: "text",
text: JSON.stringify(rate),
},
],
};
}
);
async function main() {
try {
// Initialize Binance clients
const spotInitialized = await initializeBinanceClient();
const futuresInitialized = await initializeFuturesClient();
if (!spotInitialized || !futuresInitialized) {
logError('Binance clients not initialized');
} else {
log('Binance clients initialized successfully');
}
// 使用stdio传输层
const transport = new StdioServerTransport();
await server.connect(transport);
log('Server started successfully with stdio transport');
} catch (error) {
logError('Failed to start server:', error);
process.exit(1);
}
}
main().catch((error) => {
logError('Unhandled error:', error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/src/services/binanceFutures.ts:
--------------------------------------------------------------------------------
```typescript
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}`);
}
}
```