Skip to content

第七章:高级功能与应用

本章将介绍 CCXT 的高级应用场景,包括多交易所套利、量化交易策略、投资组合管理和风险控制系统的实现。

🔄 多交易所套利

套利机会检测器

javascript
class ArbitrageDetector {
    constructor(exchanges, config = {}) {
        this.exchanges = exchanges;
        this.minProfitThreshold = config.minProfitThreshold || 0.5; // 0.5%
        this.maxSpreadThreshold = config.maxSpreadThreshold || 5.0;  // 5%
        this.symbols = config.symbols || ['BTC/USDT', 'ETH/USDT'];
        this.opportunities = [];
        this.isRunning = false;
    }
    
    // 开始检测套利机会
    async startDetection(interval = 30000) {
        console.log('🔍 开始多交易所套利检测...\n');
        
        this.isRunning = true;
        
        while (this.isRunning) {
            try {
                for (const symbol of this.symbols) {
                    await this.detectArbitrageOpportunities(symbol);
                    await this.sleep(1000); // 避免频率限制
                }
                
                // 显示最佳机会
                this.displayTopOpportunities();
                
                await this.sleep(interval);
                
            } catch (error) {
                console.error('❌ 套利检测错误:', error.message);
                await this.sleep(5000);
            }
        }
    }
    
    // 检测特定交易对的套利机会
    async detectArbitrageOpportunities(symbol) {
        const tickers = await this.fetchAllTickers(symbol);
        
        if (tickers.length < 2) {
            console.warn(`⚠️ ${symbol} 可用交易所数量不足`);
            return;
        }
        
        // 检查所有交易所对之间的价差
        for (let i = 0; i < tickers.length; i++) {
            for (let j = i + 1; j < tickers.length; j++) {
                const opportunity = this.calculateArbitrage(tickers[i], tickers[j], symbol);
                
                if (opportunity && opportunity.profitPercent >= this.minProfitThreshold) {
                    this.recordOpportunity(opportunity);
                }
            }
        }
    }
    
    // 获取所有交易所的行情
    async fetchAllTickers(symbol) {
        const tickers = [];
        
        const promises = this.exchanges.map(async exchange => {
            try {
                if (exchange.symbols.includes(symbol)) {
                    const ticker = await exchange.fetchTicker(symbol);
                    return {
                        exchange: exchange.id,
                        ticker,
                        timestamp: Date.now(),
                    };
                }
            } catch (error) {
                console.warn(`⚠️ 获取 ${exchange.id} ${symbol} 行情失败: ${error.message}`);
                return null;
            }
        });
        
        const results = await Promise.all(promises);
        return results.filter(result => result !== null);
    }
    
    // 计算套利机会
    calculateArbitrage(ticker1, ticker2, symbol) {
        const buy = ticker1.ticker.ask < ticker2.ticker.ask ? ticker1 : ticker2;
        const sell = ticker1.ticker.bid > ticker2.ticker.bid ? ticker1 : ticker2;
        
        if (buy.exchange === sell.exchange) return null;
        
        const buyPrice = buy.ticker.ask;
        const sellPrice = sell.ticker.bid;
        
        if (sellPrice <= buyPrice) return null;
        
        const profit = sellPrice - buyPrice;
        const profitPercent = (profit / buyPrice) * 100;
        
        // 检查价差合理性
        const spread = (sell.ticker.ask - sell.ticker.bid) / sell.ticker.bid * 100;
        if (spread > this.maxSpreadThreshold) return null;
        
        // 估算最大交易量(取两边较小的)
        const maxVolume = Math.min(
            buy.ticker.askVolume || 0,
            sell.ticker.bidVolume || 0
        );
        
        return {
            symbol,
            buyExchange: buy.exchange,
            sellExchange: sell.exchange,
            buyPrice,
            sellPrice,
            profit,
            profitPercent,
            maxVolume,
            timestamp: Date.now(),
            spread: spread,
        };
    }
    
    // 记录套利机会
    recordOpportunity(opportunity) {
        // 检查是否已存在相似机会
        const existing = this.opportunities.find(opp => 
            opp.symbol === opportunity.symbol &&
            opp.buyExchange === opportunity.buyExchange &&
            opp.sellExchange === opportunity.sellExchange &&
            Date.now() - opp.timestamp < 60000 // 1分钟内
        );
        
        if (existing) {
            // 更新现有机会
            Object.assign(existing, opportunity);
        } else {
            // 添加新机会
            this.opportunities.push(opportunity);
            
            // 显示新发现的机会
            console.log(`🚀 发现套利机会!`);
            this.displayOpportunity(opportunity);
        }
        
        // 限制历史记录数量
        if (this.opportunities.length > 100) {
            this.opportunities = this.opportunities.slice(-100);
        }
    }
    
    // 显示单个套利机会
    displayOpportunity(opp) {
        console.log(`💰 ${opp.symbol}:`);
        console.log(`   买入: ${opp.buyExchange} @ $${opp.buyPrice.toFixed(2)}`);
        console.log(`   卖出: ${opp.sellExchange} @ $${opp.sellPrice.toFixed(2)}`);
        console.log(`   利润: $${opp.profit.toFixed(2)} (${opp.profitPercent.toFixed(2)}%)`);
        console.log(`   最大量: ${opp.maxVolume.toFixed(4)}`);
        console.log(`   价差: ${opp.spread.toFixed(4)}%\n`);
    }
    
    // 显示最佳套利机会
    displayTopOpportunities(limit = 5) {
        const recent = this.opportunities
            .filter(opp => Date.now() - opp.timestamp < 300000) // 5分钟内
            .sort((a, b) => b.profitPercent - a.profitPercent)
            .slice(0, limit);
        
        if (recent.length === 0) {
            console.log('📭 暂无套利机会');
            return;
        }
        
        console.log(`🏆 最佳套利机会 (Top ${recent.length}):`);
        console.log('='.repeat(60));
        
        recent.forEach((opp, index) => {
            console.log(`${index + 1}. ${opp.symbol} | ${opp.profitPercent.toFixed(2)}% | ${opp.buyExchange}→${opp.sellExchange}`);
        });
        
        console.log('='.repeat(60) + '\n');
    }
    
    // 获取套利统计
    getArbitrageStats() {
        const now = Date.now();
        const recent = this.opportunities.filter(opp => now - opp.timestamp < 3600000); // 1小时内
        
        if (recent.length === 0) return null;
        
        const stats = {
            totalOpportunities: recent.length,
            avgProfitPercent: recent.reduce((sum, opp) => sum + opp.profitPercent, 0) / recent.length,
            maxProfitPercent: Math.max(...recent.map(opp => opp.profitPercent)),
            symbolStats: {},
            exchangeStats: {},
        };
        
        // 按交易对统计
        recent.forEach(opp => {
            if (!stats.symbolStats[opp.symbol]) {
                stats.symbolStats[opp.symbol] = { count: 0, avgProfit: 0 };
            }
            stats.symbolStats[opp.symbol].count++;
            stats.symbolStats[opp.symbol].avgProfit += opp.profitPercent;
        });
        
        // 计算平均值
        Object.keys(stats.symbolStats).forEach(symbol => {
            stats.symbolStats[symbol].avgProfit /= stats.symbolStats[symbol].count;
        });
        
        return stats;
    }
    
    // 停止检测
    stop() {
        this.isRunning = false;
        console.log('⏹️ 停止套利检测');
    }
    
    async sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

// 使用示例
async function arbitrageExample() {
    // 初始化多个交易所
    const exchanges = [
        new ccxt.binance({ enableRateLimit: true }),
        new ccxt.coinbasepro({ enableRateLimit: true }),
        new ccxt.kraken({ enableRateLimit: true }),
    ];
    
    // 加载市场数据
    await Promise.all(exchanges.map(exchange => exchange.loadMarkets()));
    
    const detector = new ArbitrageDetector(exchanges, {
        minProfitThreshold: 0.3, // 0.3%
        symbols: ['BTC/USDT', 'ETH/USDT', 'BNB/USDT'],
    });
    
    // 开始检测(运行30秒后停止)
    setTimeout(() => detector.stop(), 30000);
    await detector.startDetection(10000); // 每10秒检测一次
}

// arbitrageExample();

📊 量化交易策略

移动平均线交叉策略

javascript
class MovingAverageCrossStrategy {
    constructor(exchange, config = {}) {
        this.exchange = exchange;
        this.symbol = config.symbol || 'BTC/USDT';
        this.fastPeriod = config.fastPeriod || 5;
        this.slowPeriod = config.slowPeriod || 20;
        this.timeframe = config.timeframe || '1h';
        this.positionSize = config.positionSize || 0.01; // BTC
        
        this.position = 0; // 当前持仓:1=多头,-1=空头,0=无持仓
        this.priceHistory = [];
        this.signals = [];
        this.trades = [];
        this.isRunning = false;
    }
    
    // 开始策略执行
    async start() {
        console.log(`📈 启动移动平均线交叉策略: ${this.symbol}`);
        console.log(`配置: 快线=${this.fastPeriod}, 慢线=${this.slowPeriod}, 周期=${this.timeframe}\n`);
        
        this.isRunning = true;
        
        // 初始加载历史数据
        await this.loadHistoricalData();
        
        // 开始实时监控
        while (this.isRunning) {
            try {
                await this.updateData();
                await this.checkSignals();
                await this.sleep(60000); // 每分钟检查一次
                
            } catch (error) {
                console.error('❌ 策略执行错误:', error.message);
                await this.sleep(5000);
            }
        }
    }
    
    // 加载历史数据
    async loadHistoricalData() {
        try {
            const limit = Math.max(this.slowPeriod * 2, 100);
            const ohlcv = await this.exchange.fetchOHLCV(this.symbol, this.timeframe, undefined, limit);
            
            this.priceHistory = ohlcv.map(candle => ({
                timestamp: candle[0],
                open: candle[1],
                high: candle[2],
                low: candle[3],
                close: candle[4],
                volume: candle[5],
            }));
            
            console.log(`✅ 加载历史数据: ${this.priceHistory.length} 根K线`);
            
        } catch (error) {
            console.error('❌ 加载历史数据失败:', error.message);
            throw error;
        }
    }
    
    // 更新最新数据
    async updateData() {
        const ticker = await this.exchange.fetchTicker(this.symbol);
        const now = Date.now();
        
        // 更新最新价格(简化处理,实际应该基于K线)
        const latestPrice = {
            timestamp: now,
            close: ticker.last,
            volume: ticker.baseVolume,
        };
        
        // 如果是新的时间周期,添加新数据点
        if (this.priceHistory.length === 0 || 
            now - this.priceHistory[this.priceHistory.length - 1].timestamp > this.getTimeframeMs()) {
            this.priceHistory.push(latestPrice);
            
            // 限制历史数据数量
            if (this.priceHistory.length > 200) {
                this.priceHistory = this.priceHistory.slice(-200);
            }
        } else {
            // 更新最后一个数据点
            this.priceHistory[this.priceHistory.length - 1] = latestPrice;
        }
    }
    
    // 检查交易信号
    async checkSignals() {
        if (this.priceHistory.length < this.slowPeriod) {
            return; // 数据不足
        }
        
        // 计算移动平均线
        const fastMA = this.calculateSMA(this.fastPeriod);
        const slowMA = this.calculateSMA(this.slowPeriod);
        
        if (fastMA.length < 2 || slowMA.length < 2) {
            return; // 数据不足
        }
        
        const currentFast = fastMA[fastMA.length - 1];
        const currentSlow = slowMA[slowMA.length - 1];
        const prevFast = fastMA[fastMA.length - 2];
        const prevSlow = slowMA[slowMA.length - 2];
        
        const currentPrice = this.priceHistory[this.priceHistory.length - 1].close;
        
        // 检测金叉和死叉
        let signal = null;
        
        if (prevFast <= prevSlow && currentFast > currentSlow) {
            signal = 'buy'; // 金叉
        } else if (prevFast >= prevSlow && currentFast < currentSlow) {
            signal = 'sell'; // 死叉
        }
        
        if (signal) {
            await this.processSignal(signal, currentPrice, {
                fastMA: currentFast,
                slowMA: currentSlow,
            });
        }
        
        // 显示当前状态
        this.displayCurrentStatus(currentPrice, currentFast, currentSlow);
    }
    
    // 处理交易信号
    async processSignal(signal, price, indicators) {
        const timestamp = Date.now();
        
        console.log(`🎯 交易信号: ${signal.toUpperCase()}`);
        console.log(`价格: $${price.toFixed(2)}`);
        console.log(`快线: $${indicators.fastMA.toFixed(2)}`);
        console.log(`慢线: $${indicators.slowMA.toFixed(2)}`);
        
        // 记录信号
        this.signals.push({
            timestamp,
            signal,
            price,
            indicators,
        });
        
        // 执行交易
        if (signal === 'buy' && this.position <= 0) {
            await this.executeBuy(price);
        } else if (signal === 'sell' && this.position >= 0) {
            await this.executeSell(price);
        }
    }
    
    // 执行买入
    async executeBuy(price) {
        try {
            console.log(`🟢 执行买入: ${this.positionSize} ${this.symbol.split('/')[0]} @ $${price.toFixed(2)}`);
            
            // 这里应该实际执行买入订单
            // const order = await this.exchange.createMarketBuyOrder(this.symbol, this.positionSize);
            
            // 模拟执行
            const trade = {
                timestamp: Date.now(),
                side: 'buy',
                amount: this.positionSize,
                price: price,
                cost: this.positionSize * price,
                id: `sim_${Date.now()}`,
            };
            
            this.trades.push(trade);
            this.position = 1; // 设置为多头
            
            console.log(`✅ 买入成功,持仓: ${this.position > 0 ? '多头' : '空头'}\n`);
            
        } catch (error) {
            console.error('❌ 买入失败:', error.message);
        }
    }
    
    // 执行卖出
    async executeSell(price) {
        try {
            console.log(`🔴 执行卖出: ${this.positionSize} ${this.symbol.split('/')[0]} @ $${price.toFixed(2)}`);
            
            // 这里应该实际执行卖出订单
            // const order = await this.exchange.createMarketSellOrder(this.symbol, this.positionSize);
            
            // 模拟执行
            const trade = {
                timestamp: Date.now(),
                side: 'sell',
                amount: this.positionSize,
                price: price,
                cost: this.positionSize * price,
                id: `sim_${Date.now()}`,
            };
            
            this.trades.push(trade);
            this.position = -1; // 设置为空头
            
            console.log(`✅ 卖出成功,持仓: ${this.position > 0 ? '多头' : '空头'}\n`);
            
        } catch (error) {
            console.error('❌ 卖出失败:', error.message);
        }
    }
    
    // 计算简单移动平均线
    calculateSMA(period) {
        if (this.priceHistory.length < period) return [];
        
        const sma = [];
        for (let i = period - 1; i < this.priceHistory.length; i++) {
            const sum = this.priceHistory
                .slice(i - period + 1, i + 1)
                .reduce((acc, item) => acc + item.close, 0);
            sma.push(sum / period);
        }
        
        return sma;
    }
    
    // 显示当前状态
    displayCurrentStatus(price, fastMA, slowMA) {
        const positionText = this.position > 0 ? '多头 🟢' : 
                           this.position < 0 ? '空头 🔴' : '空仓 ⚪';
        
        console.log(`⏰ ${new Date().toLocaleTimeString()} | ${this.symbol}`);
        console.log(`价格: $${price.toFixed(2)} | 快线: $${fastMA.toFixed(2)} | 慢线: $${slowMA.toFixed(2)} | 持仓: ${positionText}`);
    }
    
    // 计算策略表现
    calculatePerformance() {
        if (this.trades.length === 0) {
            return { totalReturn: 0, winRate: 0, tradesCount: 0 };
        }
        
        let totalReturn = 0;
        let wins = 0;
        let entryPrice = null;
        
        this.trades.forEach(trade => {
            if (trade.side === 'buy') {
                entryPrice = trade.price;
            } else if (trade.side === 'sell' && entryPrice) {
                const tradeReturn = (trade.price - entryPrice) / entryPrice;
                totalReturn += tradeReturn;
                if (tradeReturn > 0) wins++;
                entryPrice = null;
            }
        });
        
        const completedTrades = Math.floor(this.trades.length / 2);
        const winRate = completedTrades > 0 ? (wins / completedTrades) * 100 : 0;
        
        return {
            totalReturn: totalReturn * 100,
            winRate,
            tradesCount: this.trades.length,
            completedTrades,
        };
    }
    
    // 获取时间周期毫秒数
    getTimeframeMs() {
        const timeframes = {
            '1m': 60000,
            '5m': 300000,
            '15m': 900000,
            '1h': 3600000,
            '4h': 14400000,
            '1d': 86400000,
        };
        return timeframes[this.timeframe] || 3600000;
    }
    
    // 显示策略报告
    displayReport() {
        const performance = this.calculatePerformance();
        
        console.log('\n📊 策略表现报告:');
        console.log('='.repeat(40));
        console.log(`交易对: ${this.symbol}`);
        console.log(`策略: MA(${this.fastPeriod}) × MA(${this.slowPeriod})`);
        console.log(`总交易数: ${performance.tradesCount}`);
        console.log(`完成交易: ${performance.completedTrades}`);
        console.log(`胜率: ${performance.winRate.toFixed(1)}%`);
        console.log(`总收益率: ${performance.totalReturn.toFixed(2)}%`);
        console.log(`信号数: ${this.signals.length}`);
        console.log('='.repeat(40));
    }
    
    // 停止策略
    stop() {
        this.isRunning = false;
        this.displayReport();
        console.log('⏹️ 策略已停止');
    }
    
    async sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

// 使用示例
async function quantStrategyExample() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    await exchange.loadMarkets();
    
    const strategy = new MovingAverageCrossStrategy(exchange, {
        symbol: 'BTC/USDT',
        fastPeriod: 5,
        slowPeriod: 20,
        timeframe: '1h',
        positionSize: 0.001,
    });
    
    // 运行5分钟后停止
    setTimeout(() => strategy.stop(), 300000);
    
    await strategy.start();
}

// quantStrategyExample();

📊 投资组合管理

智能投资组合管理器

javascript
class PortfolioManager {
    constructor(exchanges, config = {}) {
        this.exchanges = Array.isArray(exchanges) ? exchanges : [exchanges];
        this.baseCurrency = config.baseCurrency || 'USDT';
        this.rebalanceThreshold = config.rebalanceThreshold || 5; // 5%
        this.maxPositionSize = config.maxPositionSize || 30; // 30%
        this.minPositionSize = config.minPositionSize || 1;  // 1%
        
        this.portfolio = {
            totalValue: 0,
            positions: new Map(),
            cash: 0,
            lastUpdate: null,
        };
        
        this.targetAllocation = new Map();
        this.rebalanceHistory = [];
    }
    
    // 设置目标配置
    setTargetAllocation(allocations) {
        this.targetAllocation.clear();
        
        let totalPercent = 0;
        for (const [asset, percent] of Object.entries(allocations)) {
            this.targetAllocation.set(asset, percent);
            totalPercent += percent;
        }
        
        if (Math.abs(totalPercent - 100) > 0.1) {
            throw new Error(`目标配置总和必须为100%,当前为${totalPercent}%`);
        }
        
        console.log('🎯 目标配置已设置:');
        for (const [asset, percent] of this.targetAllocation) {
            console.log(`  ${asset}: ${percent}%`);
        }
        console.log('');
    }
    
    // 更新投资组合数据
    async updatePortfolio() {
        console.log('🔄 更新投资组合数据...');
        
        const allBalances = new Map();
        const allPrices = new Map();
        
        // 获取所有交易所的余额
        for (const exchange of this.exchanges) {
            try {
                const balance = await exchange.fetchBalance();
                
                for (const [currency, amounts] of Object.entries(balance)) {
                    if (['info', 'free', 'used', 'total'].includes(currency)) continue;
                    if (amounts.total <= 0) continue;
                    
                    const current = allBalances.get(currency) || 0;
                    allBalances.set(currency, current + amounts.total);
                }
                
            } catch (error) {
                console.warn(`⚠️ 获取 ${exchange.id} 余额失败: ${error.message}`);
            }
        }
        
        // 获取价格数据
        await this.updatePrices(allBalances, allPrices);
        
        // 计算投资组合价值
        this.calculatePortfolioValue(allBalances, allPrices);
        
        this.portfolio.lastUpdate = Date.now();
        
        console.log('✅ 投资组合数据更新完成\n');
    }
    
    // 更新价格数据
    async updatePrices(balances, prices) {
        const exchange = this.exchanges[0]; // 使用第一个交易所获取价格
        
        for (const currency of balances.keys()) {
            if (currency === this.baseCurrency) {
                prices.set(currency, 1);
                continue;
            }
            
            const symbol = `${currency}/${this.baseCurrency}`;
            
            try {
                if (exchange.symbols.includes(symbol)) {
                    const ticker = await exchange.fetchTicker(symbol);
                    prices.set(currency, ticker.last);
                } else {
                    console.warn(`⚠️ 无法获取 ${symbol} 价格`);
                    prices.set(currency, 0);
                }
                
                await this.sleep(200); // 避免频率限制
                
            } catch (error) {
                console.warn(`⚠️ 获取 ${symbol} 价格失败: ${error.message}`);
                prices.set(currency, 0);
            }
        }
    }
    
    // 计算投资组合价值
    calculatePortfolioValue(balances, prices) {
        this.portfolio.positions.clear();
        this.portfolio.totalValue = 0;
        this.portfolio.cash = balances.get(this.baseCurrency) || 0;
        
        for (const [currency, amount] of balances) {
            const price = prices.get(currency) || 0;
            const value = amount * price;
            
            if (value > 0) {
                this.portfolio.positions.set(currency, {
                    amount,
                    price,
                    value,
                    percentage: 0, // 稍后计算
                });
                
                this.portfolio.totalValue += value;
            }
        }
        
        // 计算百分比
        for (const [currency, position] of this.portfolio.positions) {
            position.percentage = (position.value / this.portfolio.totalValue) * 100;
        }
    }
    
    // 分析投资组合
    analyzePortfolio() {
        if (this.portfolio.positions.size === 0) {
            console.log('📭 投资组合为空');
            return null;
        }
        
        console.log('📊 投资组合分析:');
        console.log('='.repeat(60));
        console.log(`总价值: ${this.portfolio.totalValue.toFixed(2)} ${this.baseCurrency}`);
        console.log(`现金: ${this.portfolio.cash.toFixed(2)} ${this.baseCurrency} (${(this.portfolio.cash/this.portfolio.totalValue*100).toFixed(1)}%)`);
        console.log('');
        
        // 按价值排序显示持仓
        const sortedPositions = Array.from(this.portfolio.positions.entries())
            .sort(([,a], [,b]) => b.value - a.value);
        
        console.log('资产'.padEnd(8) + '数量'.padEnd(15) + '价格'.padEnd(12) + '价值'.padEnd(15) + '占比');
        console.log('-'.repeat(60));
        
        sortedPositions.forEach(([currency, position]) => {
            console.log(
                `${currency.padEnd(8)}` +
                `${position.amount.toFixed(4).padEnd(15)}` +
                `${position.price.toFixed(2).padEnd(12)}` +
                `${position.value.toFixed(2).padEnd(15)}` +
                `${position.percentage.toFixed(1)}%`
            );
        });
        
        console.log('='.repeat(60));
        
        // 与目标配置对比
        if (this.targetAllocation.size > 0) {
            this.compareWithTarget();
        }
        
        return this.portfolio;
    }
    
    // 与目标配置对比
    compareWithTarget() {
        console.log('\n🎯 目标配置对比:');
        console.log('-'.repeat(50));
        
        const deviations = [];
        
        for (const [asset, targetPercent] of this.targetAllocation) {
            const currentPosition = this.portfolio.positions.get(asset);
            const currentPercent = currentPosition ? currentPosition.percentage : 0;
            const deviation = currentPercent - targetPercent;
            
            deviations.push({
                asset,
                target: targetPercent,
                current: currentPercent,
                deviation,
                needsRebalance: Math.abs(deviation) > this.rebalanceThreshold,
            });
            
            const statusEmoji = Math.abs(deviation) > this.rebalanceThreshold ? '⚠️' : '✅';
            console.log(
                `${statusEmoji} ${asset.padEnd(8)}: ` +
                `目标 ${targetPercent.toFixed(1)}% | ` +
                `当前 ${currentPercent.toFixed(1)}% | ` +
                `偏差 ${deviation > 0 ? '+' : ''}${deviation.toFixed(1)}%`
            );
        }
        
        // 检查是否需要再平衡
        const needsRebalance = deviations.some(d => d.needsRebalance);
        
        if (needsRebalance) {
            console.log('\n🔄 建议进行再平衡操作');
            this.generateRebalanceActions(deviations);
        } else {
            console.log('\n✅ 投资组合配置符合目标');
        }
    }
    
    // 生成再平衡操作建议
    generateRebalanceActions(deviations) {
        console.log('\n📋 再平衡操作建议:');
        
        const actions = [];
        
        deviations.forEach(deviation => {
            if (!deviation.needsRebalance) return;
            
            const targetValue = (this.portfolio.totalValue * deviation.target) / 100;
            const currentValue = (this.portfolio.totalValue * deviation.current) / 100;
            const valueChange = targetValue - currentValue;
            
            if (Math.abs(valueChange) < 10) return; // 忽略小额调整
            
            const action = {
                asset: deviation.asset,
                action: valueChange > 0 ? 'buy' : 'sell',
                valueChange: Math.abs(valueChange),
                targetPercent: deviation.target,
                currentPercent: deviation.current,
            };
            
            actions.push(action);
            
            const actionEmoji = action.action === 'buy' ? '🟢' : '🔴';
            console.log(
                `${actionEmoji} ${action.action.toUpperCase()} ${action.asset}: ` +
                `${action.valueChange.toFixed(2)} ${this.baseCurrency} ` +
                `(${action.currentPercent.toFixed(1)}% → ${action.targetPercent.toFixed(1)}%)`
            );
        });
        
        return actions;
    }
    
    // 执行再平衡
    async executeRebalance(dryRun = true) {
        if (this.targetAllocation.size === 0) {
            throw new Error('未设置目标配置');
        }
        
        await this.updatePortfolio();
        
        const deviations = [];
        for (const [asset, targetPercent] of this.targetAllocation) {
            const currentPosition = this.portfolio.positions.get(asset);
            const currentPercent = currentPosition ? currentPosition.percentage : 0;
            const deviation = currentPercent - targetPercent;
            
            if (Math.abs(deviation) > this.rebalanceThreshold) {
                deviations.push({
                    asset,
                    target: targetPercent,
                    current: currentPercent,
                    deviation,
                });
            }
        }
        
        if (deviations.length === 0) {
            console.log('✅ 无需再平衡');
            return;
        }
        
        console.log(`🔄 ${dryRun ? '模拟' : '执行'}再平衡操作...`);
        
        const actions = this.generateRebalanceActions(deviations);
        
        if (!dryRun) {
            // 这里应该实际执行交易
            console.log('⚠️ 实际交易执行需要实现具体的下单逻辑');
        }
        
        // 记录再平衡历史
        this.rebalanceHistory.push({
            timestamp: Date.now(),
            actions,
            portfolioValue: this.portfolio.totalValue,
            dryRun,
        });
        
        return actions;
    }
    
    // 风险分析
    calculateRiskMetrics() {
        if (this.portfolio.positions.size === 0) return null;
        
        const positions = Array.from(this.portfolio.positions.values());
        
        // 计算集中度风险
        const concentrationRisk = Math.max(...positions.map(p => p.percentage));
        
        // 计算多样化程度
        const diversificationScore = 100 - concentrationRisk;
        
        // 计算风险等级
        let riskLevel = 'low';
        if (concentrationRisk > 50) riskLevel = 'high';
        else if (concentrationRisk > 30) riskLevel = 'medium';
        
        const riskMetrics = {
            concentrationRisk: concentrationRisk.toFixed(1),
            diversificationScore: diversificationScore.toFixed(1),
            riskLevel,
            positionCount: this.portfolio.positions.size,
            largestPosition: Math.max(...positions.map(p => p.percentage)),
        };
        
        console.log('\n⚠️ 风险分析:');
        console.log(`集中度风险: ${riskMetrics.concentrationRisk}%`);
        console.log(`多样化评分: ${riskMetrics.diversificationScore}%`);
        console.log(`风险等级: ${riskLevel.toUpperCase()}`);
        console.log(`持仓数量: ${riskMetrics.positionCount}`);
        
        return riskMetrics;
    }
    
    // 生成投资组合报告
    async generateReport() {
        await this.updatePortfolio();
        
        console.log('\n📈 投资组合完整报告');
        console.log('='.repeat(80));
        console.log(`生成时间: ${new Date().toLocaleString()}`);
        console.log(`更新时间: ${new Date(this.portfolio.lastUpdate).toLocaleString()}`);
        
        // 基本信息
        this.analyzePortfolio();
        
        // 风险分析
        this.calculateRiskMetrics();
        
        // 再平衡历史
        if (this.rebalanceHistory.length > 0) {
            console.log('\n📋 再平衡历史:');
            this.rebalanceHistory.slice(-5).forEach((rebalance, index) => {
                const time = new Date(rebalance.timestamp).toLocaleString();
                console.log(`${index + 1}. ${time} - ${rebalance.actions.length} 个操作 (${rebalance.dryRun ? '模拟' : '实际'})`);
            });
        }
        
        console.log('='.repeat(80));
    }
    
    async sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

// 使用示例
async function portfolioExample() {
    const exchange = new ccxt.binance({
        apiKey: process.env.BINANCE_API_KEY,
        secret: process.env.BINANCE_SECRET,
        enableRateLimit: true,
        sandbox: true,
    });
    
    await exchange.loadMarkets();
    
    const portfolio = new PortfolioManager([exchange], {
        baseCurrency: 'USDT',
        rebalanceThreshold: 5,
    });
    
    // 设置目标配置
    portfolio.setTargetAllocation({
        'BTC': 40,
        'ETH': 30,
        'BNB': 20,
        'USDT': 10,
    });
    
    // 生成报告
    await portfolio.generateReport();
    
    // 检查是否需要再平衡
    await portfolio.executeRebalance(true); // 模拟运行
}

// portfolioExample();

🛡️ 风险管理系统

风险控制管理器

javascript
class RiskManager {
    constructor(config = {}) {
        this.maxDailyLoss = config.maxDailyLoss || 5;     // 5%
        this.maxPositionSize = config.maxPositionSize || 10; // 10%
        this.maxLeverage = config.maxLeverage || 3;       // 3倍
        this.stopLossPercent = config.stopLossPercent || 2; // 2%
        this.maxCorrelation = config.maxCorrelation || 0.8; // 80%
        
        this.dailyPnL = 0;
        this.startOfDayValue = 0;
        this.positions = new Map();
        this.riskEvents = [];
        this.isEmergencyMode = false;
    }
    
    // 设置交易日开始值
    setStartOfDayValue(value) {
        this.startOfDayValue = value;
        this.dailyPnL = 0;
        console.log(`📅 交易日开始,初始价值: ${value.toFixed(2)}`);
    }
    
    // 更新当前组合价值
    updatePortfolioValue(currentValue) {
        if (this.startOfDayValue === 0) {
            this.setStartOfDayValue(currentValue);
            return;
        }
        
        this.dailyPnL = ((currentValue - this.startOfDayValue) / this.startOfDayValue) * 100;
        
        // 检查每日损失限制
        this.checkDailyLossLimit();
    }
    
    // 检查每日损失限制
    checkDailyLossLimit() {
        if (this.dailyPnL <= -this.maxDailyLoss) {
            this.triggerEmergencyMode('daily_loss_limit', {
                currentLoss: this.dailyPnL,
                limit: this.maxDailyLoss,
            });
        }
    }
    
    // 检查订单风险
    checkOrderRisk(order, portfolioValue) {
        const risks = [];
        
        // 检查仓位大小
        const positionValue = order.amount * order.price;
        const positionPercent = (positionValue / portfolioValue) * 100;
        
        if (positionPercent > this.maxPositionSize) {
            risks.push({
                type: 'position_size',
                severity: 'high',
                message: `仓位过大: ${positionPercent.toFixed(1)}% > ${this.maxPositionSize}%`,
                value: positionPercent,
                limit: this.maxPositionSize,
            });
        }
        
        // 检查杠杆
        if (order.leverage && order.leverage > this.maxLeverage) {
            risks.push({
                type: 'leverage',
                severity: 'high',
                message: `杠杆过高: ${order.leverage}x > ${this.maxLeverage}x`,
                value: order.leverage,
                limit: this.maxLeverage,
            });
        }
        
        // 检查止损设置
        if (order.side === 'buy' && !order.stopLoss) {
            risks.push({
                type: 'no_stop_loss',
                severity: 'medium',
                message: '建议设置止损价格',
            });
        }
        
        return risks;
    }
    
    // 验证订单
    validateOrder(order, portfolioValue) {
        if (this.isEmergencyMode) {
            throw new Error('🚨 应急模式激活,暂停所有交易');
        }
        
        const risks = this.checkOrderRisk(order, portfolioValue);
        const highRisks = risks.filter(risk => risk.severity === 'high');
        
        if (highRisks.length > 0) {
            console.error('🚫 订单风险过高:');
            highRisks.forEach(risk => {
                console.error(`  - ${risk.message}`);
            });
            return false;
        }
        
        if (risks.length > 0) {
            console.warn('⚠️ 订单风险警告:');
            risks.forEach(risk => {
                console.warn(`  - ${risk.message}`);
            });
        }
        
        return true;
    }
    
    // 计算建议止损价格
    calculateStopLoss(entryPrice, side) {
        const stopLossMultiplier = this.stopLossPercent / 100;
        
        if (side === 'buy') {
            return entryPrice * (1 - stopLossMultiplier);
        } else {
            return entryPrice * (1 + stopLossMultiplier);
        }
    }
    
    // 添加持仓
    addPosition(positionId, position) {
        this.positions.set(positionId, {
            ...position,
            entryTime: Date.now(),
            stopLoss: position.stopLoss || this.calculateStopLoss(position.entryPrice, position.side),
            maxDrawdown: 0,
            unrealizedPnL: 0,
        });
        
        console.log(`📍 新建持仓: ${positionId}`);
        this.displayPosition(positionId);
    }
    
    // 更新持仓
    updatePosition(positionId, currentPrice) {
        const position = this.positions.get(positionId);
        if (!position) return;
        
        // 计算未实现盈亏
        const pnl = position.side === 'buy' ? 
            (currentPrice - position.entryPrice) / position.entryPrice :
            (position.entryPrice - currentPrice) / position.entryPrice;
        
        position.unrealizedPnL = pnl * 100;
        position.currentPrice = currentPrice;
        
        // 更新最大回撤
        if (position.unrealizedPnL < position.maxDrawdown) {
            position.maxDrawdown = position.unrealizedPnL;
        }
        
        // 检查止损
        this.checkStopLoss(positionId);
        
        // 检查持仓风险
        this.checkPositionRisk(positionId);
    }
    
    // 检查止损
    checkStopLoss(positionId) {
        const position = this.positions.get(positionId);
        if (!position || !position.stopLoss) return;
        
        const shouldTriggerStopLoss = position.side === 'buy' ?
            position.currentPrice <= position.stopLoss :
            position.currentPrice >= position.stopLoss;
        
        if (shouldTriggerStopLoss) {
            this.triggerStopLoss(positionId);
        }
    }
    
    // 检查持仓风险
    checkPositionRisk(positionId) {
        const position = this.positions.get(positionId);
        if (!position) return;
        
        // 检查是否达到风险阈值
        if (position.unrealizedPnL <= -this.stopLossPercent) {
            this.recordRiskEvent('position_loss_threshold', {
                positionId,
                unrealizedPnL: position.unrealizedPnL,
                threshold: -this.stopLossPercent,
            });
        }
        
        // 检查持仓时间
        const holdingTime = Date.now() - position.entryTime;
        const maxHoldingTime = 7 * 24 * 60 * 60 * 1000; // 7天
        
        if (holdingTime > maxHoldingTime) {
            this.recordRiskEvent('long_holding_time', {
                positionId,
                holdingDays: Math.floor(holdingTime / (24 * 60 * 60 * 1000)),
            });
        }
    }
    
    // 触发止损
    triggerStopLoss(positionId) {
        const position = this.positions.get(positionId);
        if (!position) return;
        
        console.log(`🛑 触发止损: ${positionId}`);
        console.log(`  止损价: ${position.stopLoss.toFixed(2)}`);
        console.log(`  当前价: ${position.currentPrice.toFixed(2)}`);
        console.log(`  损失: ${position.unrealizedPnL.toFixed(2)}%`);
        
        // 这里应该执行实际的止损订单
        // await this.executeStopLoss(positionId);
        
        this.recordRiskEvent('stop_loss_triggered', {
            positionId,
            stopLossPrice: position.stopLoss,
            currentPrice: position.currentPrice,
            loss: position.unrealizedPnL,
        });
    }
    
    // 触发应急模式
    triggerEmergencyMode(reason, data) {
        if (this.isEmergencyMode) return;
        
        this.isEmergencyMode = true;
        
        console.error('🚨 触发应急模式!');
        console.error(`原因: ${reason}`);
        console.error('数据:', data);
        
        this.recordRiskEvent('emergency_mode', {
            reason,
            data,
            timestamp: Date.now(),
        });
        
        // 这里应该实施应急措施
        // 如: 取消所有订单、平仓所有持仓等
        this.executeEmergencyProcedures();
    }
    
    // 执行应急程序
    executeEmergencyProcedures() {
        console.log('🚨 执行应急程序:');
        console.log('  1. 取消所有挂单');
        console.log('  2. 平仓所有持仓');
        console.log('  3. 停止自动交易');
        console.log('  4. 发送告警通知');
        
        // 实际实现中应该执行这些操作
    }
    
    // 记录风险事件
    recordRiskEvent(type, data) {
        const event = {
            type,
            data,
            timestamp: Date.now(),
            id: `${type}_${Date.now()}`,
        };
        
        this.riskEvents.push(event);
        
        // 限制事件历史数量
        if (this.riskEvents.length > 1000) {
            this.riskEvents = this.riskEvents.slice(-1000);
        }
        
        console.warn(`⚠️ 风险事件: ${type}`, data);
    }
    
    // 显示持仓信息
    displayPosition(positionId) {
        const position = this.positions.get(positionId);
        if (!position) return;
        
        console.log(`📊 持仓 ${positionId}:`);
        console.log(`  方向: ${position.side}`);
        console.log(`  入场价: ${position.entryPrice.toFixed(2)}`);
        console.log(`  数量: ${position.amount}`);
        console.log(`  止损价: ${position.stopLoss?.toFixed(2) || 'N/A'}`);
        console.log(`  未实现盈亏: ${position.unrealizedPnL?.toFixed(2) || 0}%`);
        console.log('');
    }
    
    // 风险报告
    generateRiskReport() {
        console.log('\n🛡️ 风险管理报告');
        console.log('='.repeat(50));
        
        // 基本风险指标
        console.log(`每日盈亏: ${this.dailyPnL.toFixed(2)}%`);
        console.log(`每日损失限制: ${this.maxDailyLoss}%`);
        console.log(`应急模式: ${this.isEmergencyMode ? '🔴 激活' : '🟢 正常'}`);
        console.log(`活跃持仓: ${this.positions.size} 个`);
        
        // 持仓概览
        if (this.positions.size > 0) {
            console.log('\n📊 持仓概览:');
            for (const [id, position] of this.positions) {
                const status = position.unrealizedPnL >= 0 ? '🟢' : '🔴';
                console.log(`  ${status} ${id}: ${position.unrealizedPnL?.toFixed(2) || 0}%`);
            }
        }
        
        // 风险事件
        const recentEvents = this.riskEvents.slice(-10);
        if (recentEvents.length > 0) {
            console.log('\n⚠️ 最近风险事件:');
            recentEvents.forEach(event => {
                const time = new Date(event.timestamp).toLocaleTimeString();
                console.log(`  ${time}: ${event.type}`);
            });
        }
        
        console.log('='.repeat(50));
    }
    
    // 重置应急模式
    resetEmergencyMode() {
        if (!this.isEmergencyMode) return;
        
        console.log('🔄 重置应急模式');
        this.isEmergencyMode = false;
        
        this.recordRiskEvent('emergency_mode_reset', {
            timestamp: Date.now(),
        });
    }
    
    // 获取风险指标
    getRiskMetrics() {
        const activePositions = Array.from(this.positions.values());
        
        return {
            dailyPnL: this.dailyPnL,
            maxDailyLoss: this.maxDailyLoss,
            isEmergencyMode: this.isEmergencyMode,
            activePositions: this.positions.size,
            averageUnrealizedPnL: activePositions.length > 0 ?
                activePositions.reduce((sum, pos) => sum + (pos.unrealizedPnL || 0), 0) / activePositions.length : 0,
            worstPosition: Math.min(...activePositions.map(pos => pos.unrealizedPnL || 0)),
            riskEventsToday: this.riskEvents.filter(event => 
                Date.now() - event.timestamp < 24 * 60 * 60 * 1000
            ).length,
        };
    }
}

// 使用示例
function riskManagementExample() {
    const riskManager = new RiskManager({
        maxDailyLoss: 3,      // 3%
        maxPositionSize: 15,  // 15%
        stopLossPercent: 2,   // 2%
    });
    
    // 设置交易日开始价值
    riskManager.setStartOfDayValue(10000);
    
    // 模拟订单风险检查
    const order = {
        symbol: 'BTC/USDT',
        side: 'buy',
        amount: 0.1,
        price: 45000,
        leverage: 2,
    };
    
    const portfolioValue = 10000;
    const isValid = riskManager.validateOrder(order, portfolioValue);
    console.log(`订单验证结果: ${isValid ? '通过' : '拒绝'}`);
    
    // 添加持仓
    if (isValid) {
        riskManager.addPosition('BTC_001', {
            symbol: 'BTC/USDT',
            side: 'buy',
            amount: 0.1,
            entryPrice: 45000,
        });
        
        // 模拟价格变化
        riskManager.updatePosition('BTC_001', 44000); // 价格下跌
        riskManager.updatePosition('BTC_001', 43000); // 继续下跌
    }
    
    // 模拟每日损失
    riskManager.updatePortfolioValue(9500); // 组合价值下跌5%
    
    // 生成风险报告
    riskManager.generateRiskReport();
}

// riskManagementExample();

🎯 章节总结

本章我们学习了 CCXT 的高级应用场景:

多交易所套利

  • 套利机会检测和分析
  • 跨交易所价差监控
  • 套利统计和评估

量化交易策略

  • 移动平均线交叉策略
  • 技术指标计算和信号生成
  • 策略回测和性能评估

投资组合管理

  • 多资产投资组合分析
  • 目标配置和再平衡
  • 风险分散和多样化评估

风险管理系统

  • 多层次风险控制
  • 实时风险监控和预警
  • 应急处理和止损机制

💡 实践建议

  1. 从简单开始 - 先掌握基础功能再构建复杂策略
  2. 充分测试 - 在沙盒环境中验证所有策略
  3. 风险第一 - 建立完善的风险控制体系
  4. 持续优化 - 根据市场变化调整策略参数
  5. 记录分析 - 详细记录交易过程和结果

🔗 下一步

完成高级功能学习后,可以:

  1. 查阅常见问题 - 解决开发中的常见问题
  2. 实际项目开发 - 将所学知识应用到真实项目
  3. 社区交流 - 参与 CCXT 社区讨论和分享

下一章:常见问题与解决方案

基于 VitePress 构建的现代化知识库