trading-platform/apps/mcp-binance-connector/src/services/binance-client.ts
rckrdmrd c1b5081208 feat(ml): Complete FASE 11 - BTCUSD update and comprehensive documentation alignment
ML Engine Updates:
- Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records
- Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence)
- Backtest results: +176.71R profit with aggressive_filter strategy

Documentation Consolidation:
- Created docs/99-analisis/_MAP.md index with 13 new analysis documents
- Consolidated inventories: removed duplicates from orchestration/inventarios/
- Updated ML_INVENTORY.yml with BTCUSD metrics and training results
- Added execution reports: FASE11-BTCUSD, correction issues, alignment validation

Architecture & Integration:
- Updated all module documentation with NEXUS v3.4 frontmatter
- Fixed _MAP.md indexes across all folders
- Updated orchestration plans and traces

Files: 229 changed, 5064 insertions(+), 1872 deletions(-)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:31:29 -06:00

472 lines
12 KiB
TypeScript

/**
* Binance Client Service
*
* CCXT wrapper for Binance operations.
* Provides a unified interface for both Spot and Futures trading.
*
* @version 1.0.0
* @author Trading Platform Trading Platform
*/
import ccxt, { Ticker, OrderBook, OHLCV, Balance, Order, Trade } from 'ccxt';
import { createBinanceSpotClient, createBinanceFuturesClient, binanceConfig } from '../config';
import { logger } from '../utils/logger';
// ==========================================
// Types
// ==========================================
export interface BinanceTicker {
symbol: string;
price: number;
bid: number;
ask: number;
high24h: number;
low24h: number;
volume24h: number;
change24h: number;
timestamp: number;
}
export interface BinanceOrderBook {
symbol: string;
bids: [number, number][];
asks: [number, number][];
spread: number;
spreadPercentage: number;
timestamp: number;
}
export interface BinanceKline {
timestamp: number;
open: number;
high: number;
low: number;
close: number;
volume: number;
}
export interface BinanceAccountBalance {
asset: string;
free: number;
locked: number;
total: number;
}
export interface BinanceAccount {
accountType: string;
balances: BinanceAccountBalance[];
canTrade: boolean;
canWithdraw: boolean;
updateTime: number;
}
export interface BinanceOrder {
id: string;
symbol: string;
side: string;
type: string;
price: number | null;
amount: number;
filled: number;
remaining: number;
status: string;
createdAt: number;
}
export interface CreateOrderParams {
symbol: string;
side: 'buy' | 'sell';
type: 'market' | 'limit' | 'stop_loss' | 'take_profit';
amount: number;
price?: number;
stopPrice?: number;
}
export interface OrderResult {
success: boolean;
order?: BinanceOrder;
error?: string;
}
// ==========================================
// Binance Client Class
// ==========================================
export class BinanceClient {
private spotClient: ccxt.binance;
private futuresClient: ccxt.binance;
private marketsLoaded: boolean = false;
constructor() {
this.spotClient = createBinanceSpotClient();
this.futuresClient = createBinanceFuturesClient();
}
/**
* Check if client is properly configured
*/
isConfigured(): boolean {
return binanceConfig.apiKey !== '' && binanceConfig.apiSecret !== '';
}
/**
* Test connectivity to Binance
*/
async isConnected(): Promise<boolean> {
try {
await this.spotClient.fetchTime();
return true;
} catch {
return false;
}
}
/**
* Load markets if not already loaded
*/
private async ensureMarketsLoaded(): Promise<void> {
if (!this.marketsLoaded) {
await this.spotClient.loadMarkets();
this.marketsLoaded = true;
}
}
// ==========================================
// Market Data Methods
// ==========================================
/**
* Get ticker for a symbol
*/
async getTicker(symbol: string): Promise<BinanceTicker> {
try {
await this.ensureMarketsLoaded();
const ticker: Ticker = await this.spotClient.fetchTicker(symbol);
return {
symbol: ticker.symbol,
price: ticker.last ?? 0,
bid: ticker.bid ?? 0,
ask: ticker.ask ?? 0,
high24h: ticker.high ?? 0,
low24h: ticker.low ?? 0,
volume24h: ticker.baseVolume ?? 0,
change24h: ticker.percentage ?? 0,
timestamp: ticker.timestamp ?? Date.now(),
};
} catch (error) {
logger.error(`Failed to get ticker for ${symbol}`, { error });
throw error;
}
}
/**
* Get order book for a symbol
*/
async getOrderBook(symbol: string, limit: number = 20): Promise<BinanceOrderBook> {
try {
await this.ensureMarketsLoaded();
const orderbook: OrderBook = await this.spotClient.fetchOrderBook(symbol, limit);
const topBid = orderbook.bids[0]?.[0] ?? 0;
const topAsk = orderbook.asks[0]?.[0] ?? 0;
const spread = topAsk - topBid;
const spreadPercentage = topBid > 0 ? (spread / topBid) * 100 : 0;
return {
symbol,
bids: orderbook.bids.slice(0, limit) as [number, number][],
asks: orderbook.asks.slice(0, limit) as [number, number][],
spread,
spreadPercentage,
timestamp: orderbook.timestamp ?? Date.now(),
};
} catch (error) {
logger.error(`Failed to get order book for ${symbol}`, { error });
throw error;
}
}
/**
* Get OHLCV (klines/candles) for a symbol
*/
async getKlines(
symbol: string,
interval: string = '5m',
limit: number = 100
): Promise<BinanceKline[]> {
try {
await this.ensureMarketsLoaded();
const ohlcv: OHLCV[] = await this.spotClient.fetchOHLCV(symbol, interval, undefined, limit);
return ohlcv.map((candle) => ({
timestamp: candle[0] as number,
open: candle[1] as number,
high: candle[2] as number,
low: candle[3] as number,
close: candle[4] as number,
volume: candle[5] as number,
}));
} catch (error) {
logger.error(`Failed to get klines for ${symbol}`, { error });
throw error;
}
}
// ==========================================
// Account Methods
// ==========================================
/**
* Get account balance
*/
async getAccount(): Promise<BinanceAccount> {
try {
if (!this.isConfigured()) {
throw new Error('Binance API keys not configured');
}
const balance: Balance = await this.spotClient.fetchBalance();
// Filter non-zero balances
const balances: BinanceAccountBalance[] = Object.entries(balance.total)
.filter(([_, amount]) => (amount as number) > 0)
.map(([asset, total]) => ({
asset,
free: (balance.free[asset] as number) ?? 0,
locked: (balance.used[asset] as number) ?? 0,
total: total as number,
}));
return {
accountType: 'SPOT',
balances,
canTrade: true,
canWithdraw: true,
updateTime: Date.now(),
};
} catch (error) {
logger.error('Failed to get account info', { error });
throw error;
}
}
/**
* Get open orders
*/
async getOpenOrders(symbol?: string): Promise<BinanceOrder[]> {
try {
if (!this.isConfigured()) {
throw new Error('Binance API keys not configured');
}
const orders: Order[] = await this.spotClient.fetchOpenOrders(symbol);
return orders.map((order) => ({
id: order.id,
symbol: order.symbol,
side: order.side,
type: order.type,
price: order.price,
amount: order.amount,
filled: order.filled,
remaining: order.remaining,
status: order.status,
createdAt: order.timestamp ?? Date.now(),
}));
} catch (error) {
logger.error('Failed to get open orders', { error });
throw error;
}
}
/**
* Get trade history
*/
async getTradeHistory(symbol: string, limit: number = 50): Promise<Trade[]> {
try {
if (!this.isConfigured()) {
throw new Error('Binance API keys not configured');
}
return await this.spotClient.fetchMyTrades(symbol, undefined, limit);
} catch (error) {
logger.error(`Failed to get trade history for ${symbol}`, { error });
throw error;
}
}
// ==========================================
// Order Methods
// ==========================================
/**
* Create a new order
*/
async createOrder(params: CreateOrderParams): Promise<OrderResult> {
try {
if (!this.isConfigured()) {
throw new Error('Binance API keys not configured');
}
await this.ensureMarketsLoaded();
let order: Order;
switch (params.type) {
case 'market':
order = await this.spotClient.createMarketOrder(
params.symbol,
params.side,
params.amount
);
break;
case 'limit':
if (!params.price) {
return { success: false, error: 'Price is required for limit orders' };
}
order = await this.spotClient.createLimitOrder(
params.symbol,
params.side,
params.amount,
params.price
);
break;
case 'stop_loss':
if (!params.stopPrice) {
return { success: false, error: 'Stop price is required for stop loss orders' };
}
order = await this.spotClient.createOrder(
params.symbol,
'stop_loss',
params.side,
params.amount,
undefined,
{ stopPrice: params.stopPrice }
);
break;
case 'take_profit':
if (!params.stopPrice) {
return { success: false, error: 'Stop price is required for take profit orders' };
}
order = await this.spotClient.createOrder(
params.symbol,
'take_profit',
params.side,
params.amount,
undefined,
{ stopPrice: params.stopPrice }
);
break;
default:
return { success: false, error: `Unsupported order type: ${params.type}` };
}
return {
success: true,
order: {
id: order.id,
symbol: order.symbol,
side: order.side,
type: order.type,
price: order.price ?? order.average ?? null,
amount: order.amount,
filled: order.filled,
remaining: order.remaining,
status: order.status,
createdAt: order.timestamp ?? Date.now(),
},
};
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error('Failed to create order', { error, params });
return { success: false, error: message };
}
}
/**
* Cancel an order
*/
async cancelOrder(orderId: string, symbol: string): Promise<OrderResult> {
try {
if (!this.isConfigured()) {
throw new Error('Binance API keys not configured');
}
const result = await this.spotClient.cancelOrder(orderId, symbol);
return {
success: true,
order: {
id: result.id,
symbol: result.symbol,
side: result.side,
type: result.type,
price: result.price,
amount: result.amount,
filled: result.filled,
remaining: result.remaining,
status: 'CANCELLED',
createdAt: result.timestamp ?? Date.now(),
},
};
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error('Failed to cancel order', { error, orderId, symbol });
return { success: false, error: message };
}
}
/**
* Cancel all orders for a symbol
*/
async cancelAllOrders(symbol: string): Promise<{ success: boolean; cancelledCount: number; error?: string }> {
try {
if (!this.isConfigured()) {
throw new Error('Binance API keys not configured');
}
const result = await this.spotClient.cancelAllOrders(symbol);
return {
success: true,
cancelledCount: Array.isArray(result) ? result.length : 0,
};
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error('Failed to cancel all orders', { error, symbol });
return { success: false, cancelledCount: 0, error: message };
}
}
/**
* Get current price for a symbol (helper method)
*/
async getCurrentPrice(symbol: string): Promise<number> {
const ticker = await this.getTicker(symbol);
return ticker.price;
}
}
// ==========================================
// Singleton Instance
// ==========================================
let clientInstance: BinanceClient | null = null;
export function getBinanceClient(): BinanceClient {
if (!clientInstance) {
clientInstance = new BinanceClient();
}
return clientInstance;
}
export function resetBinanceClient(): void {
clientInstance = null;
}