Skip to content

第三章:市场数据获取

市场数据是交易决策的基础,CCXT 提供了丰富的市场数据获取方法。本章将详细介绍如何获取和处理各种类型的市场数据。

📊 市场列表管理

加载市场数据

javascript
// 基础市场加载
const ccxt = require('ccxt');

async function loadMarkets() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    
    try {
        console.log('🔄 加载市场数据...');
        
        // 加载所有市场
        const markets = await exchange.loadMarkets();
        
        console.log(`✅ 成功加载 ${Object.keys(markets).length} 个市场`);
        console.log(`📈 可用交易对: ${exchange.symbols.length} 个\n`);
        
        // 显示市场统计
        const stats = analyzeMarkets(markets);
        console.log('📊 市场统计:');
        console.log(`现货市场: ${stats.spot} 个`);
        console.log(`期货市场: ${stats.future} 个`);
        console.log(`期权市场: ${stats.option} 个`);
        console.log(`活跃市场: ${stats.active} 个`);
        console.log(`暂停市场: ${stats.inactive} 个\n`);
        
        // 显示主要基础货币
        console.log('💰 主要基础货币:');
        Object.entries(stats.baseCurrencies)
            .sort(([,a], [,b]) => b - a)
            .slice(0, 10)
            .forEach(([currency, count]) => {
                console.log(`${currency}: ${count} 个交易对`);
            });
        
        return markets;
        
    } catch (error) {
        console.error('❌ 加载市场失败:', error.message);
        throw error;
    }
}

function analyzeMarkets(markets) {
    const stats = {
        spot: 0,
        future: 0,
        option: 0,
        active: 0,
        inactive: 0,
        baseCurrencies: {},
        quoteCurrencies: {},
    };
    
    Object.values(markets).forEach(market => {
        // 市场类型统计
        if (market.spot) stats.spot++;
        if (market.future) stats.future++;
        if (market.option) stats.option++;
        
        // 活跃状态统计
        if (market.active) stats.active++;
        else stats.inactive++;
        
        // 货币统计
        stats.baseCurrencies[market.base] = (stats.baseCurrencies[market.base] || 0) + 1;
        stats.quoteCurrencies[market.quote] = (stats.quoteCurrencies[market.quote] || 0) + 1;
    });
    
    return stats;
}

loadMarkets();

市场筛选和搜索

javascript
class MarketFilter {
    constructor(exchange) {
        this.exchange = exchange;
        this.markets = null;
    }
    
    async initialize() {
        this.markets = await this.exchange.loadMarkets();
        return this;
    }
    
    // 按基础货币筛选
    filterByBase(baseCurrency) {
        return this.exchange.symbols.filter(symbol => {
            const market = this.markets[symbol];
            return market.base === baseCurrency;
        });
    }
    
    // 按计价货币筛选
    filterByQuote(quoteCurrency) {
        return this.exchange.symbols.filter(symbol => {
            const market = this.markets[symbol];
            return market.quote === quoteCurrency;
        });
    }
    
    // 按市场类型筛选
    filterByType(type) {
        return this.exchange.symbols.filter(symbol => {
            const market = this.markets[symbol];
            return market.type === type;
        });
    }
    
    // 只获取活跃市场
    getActiveMarkets() {
        return this.exchange.symbols.filter(symbol => {
            const market = this.markets[symbol];
            return market.active;
        });
    }
    
    // 按成交量排序
    async sortByVolume(limit = 20) {
        const activeSymbols = this.getActiveMarkets().slice(0, 100); // 限制数量避免频率限制
        const volumeData = [];
        
        console.log('📊 获取成交量数据...');
        
        for (const symbol of activeSymbols) {
            try {
                const ticker = await this.exchange.fetchTicker(symbol);
                if (ticker.quoteVolume) {
                    volumeData.push({
                        symbol: symbol,
                        volume: ticker.quoteVolume,
                        price: ticker.last,
                        change: ticker.percentage,
                    });
                }
                
                // 避免频率限制
                if (volumeData.length % 10 === 0) {
                    await this.exchange.sleep(1000);
                }
                
            } catch (error) {
                console.warn(`获取 ${symbol} 数据失败:`, error.message);
                continue;
            }
            
            if (volumeData.length >= limit) break;
        }
        
        return volumeData.sort((a, b) => b.volume - a.volume);
    }
    
    // 搜索市场
    searchMarkets(keyword) {
        const lowerKeyword = keyword.toLowerCase();
        return this.exchange.symbols.filter(symbol => {
            const market = this.markets[symbol];
            return symbol.toLowerCase().includes(lowerKeyword) ||
                   market.base.toLowerCase().includes(lowerKeyword) ||
                   market.quote.toLowerCase().includes(lowerKeyword);
        });
    }
}

// 使用示例
async function useMarketFilter() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    const filter = await new MarketFilter(exchange).initialize();
    
    // 获取所有 USDT 交易对
    const usdtPairs = filter.filterByQuote('USDT');
    console.log(`💰 USDT 交易对: ${usdtPairs.length} 个`);
    console.log('前10个:', usdtPairs.slice(0, 10));
    
    // 获取所有 BTC 交易对
    const btcPairs = filter.filterByBase('BTC');
    console.log(`₿ BTC 交易对: ${btcPairs.length} 个`);
    
    // 获取现货市场
    const spotMarkets = filter.filterByType('spot');
    console.log(`📈 现货市场: ${spotMarkets.length} 个`);
    
    // 按成交量排序
    const topVolume = await filter.sortByVolume(10);
    console.log('\n🔥 成交量前10名:');
    topVolume.forEach((item, index) => {
        console.log(`${index + 1}. ${item.symbol}: $${item.volume.toLocaleString()} (${item.change?.toFixed(2)}%)`);
    });
}

// useMarketFilter();

💹 实时行情数据

单个行情获取

javascript
async function fetchSingleTicker() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    
    try {
        const symbol = 'BTC/USDT';
        console.log(`📈 获取 ${symbol} 行情...\n`);
        
        const ticker = await exchange.fetchTicker(symbol);
        
        // 详细显示行情信息
        console.log('='.repeat(50));
        console.log(`🎯 ${symbol} 实时行情`);
        console.log('='.repeat(50));
        console.log(`📊 最新价格: $${ticker.last?.toFixed(2)}`);
        console.log(`📈 24h最高: $${ticker.high?.toFixed(2)}`);
        console.log(`📉 24h最低: $${ticker.low?.toFixed(2)}`);
        console.log(`🔄 24h涨跌: ${ticker.change?.toFixed(2)} (${ticker.percentage?.toFixed(2)}%)`);
        console.log(`💰 24h成交量: ${ticker.baseVolume?.toFixed(2)} BTC`);
        console.log(`💵 24h成交额: $${ticker.quoteVolume?.toLocaleString()}`);
        
        // 买卖盘信息
        if (ticker.bid && ticker.ask) {
            const spread = ticker.ask - ticker.bid;
            const spreadPercent = (spread / ticker.bid * 100).toFixed(4);
            console.log(`\n💱 盘口信息:`);
            console.log(`买一价: $${ticker.bid?.toFixed(2)} (量: ${ticker.bidVolume?.toFixed(4)})`);
            console.log(`卖一价: $${ticker.ask?.toFixed(2)} (量: ${ticker.askVolume?.toFixed(4)})`);
            console.log(`价差: $${spread.toFixed(2)} (${spreadPercent}%)`);
        }
        
        // 技术指标
        if (ticker.open && ticker.close) {
            const volatility = ((ticker.high - ticker.low) / ticker.open * 100).toFixed(2);
            console.log(`\n📊 技术指标:`);
            console.log(`波动率: ${volatility}%`);
            console.log(`开盘价: $${ticker.open?.toFixed(2)}`);
            console.log(`收盘价: $${ticker.close?.toFixed(2)}`);
            if (ticker.vwap) {
                console.log(`成交量加权平均价: $${ticker.vwap?.toFixed(2)}`);
            }
        }
        
        console.log(`\n⏰ 更新时间: ${new Date(ticker.timestamp).toLocaleString()}`);
        console.log('='.repeat(50));
        
        return ticker;
        
    } catch (error) {
        console.error('❌ 获取行情失败:', error.message);
    }
}

fetchSingleTicker();

批量行情获取

javascript
async function fetchMultipleTickers() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    
    try {
        // 方法1:逐个获取(适用于所有交易所)
        const symbols = ['BTC/USDT', 'ETH/USDT', 'BNB/USDT', 'ADA/USDT', 'DOT/USDT'];
        console.log('📊 方法1: 逐个获取行情...\n');
        
        const tickers = {};
        for (const symbol of symbols) {
            try {
                const ticker = await exchange.fetchTicker(symbol);
                tickers[symbol] = ticker;
                console.log(`✅ ${symbol}: $${ticker.last} (${ticker.percentage?.toFixed(2)}%)`);
                
                // 避免频率限制
                await exchange.sleep(exchange.rateLimit);
                
            } catch (error) {
                console.error(`❌ ${symbol}: ${error.message}`);
            }
        }
        
        // 方法2:批量获取(如果交易所支持)
        if (exchange.has['fetchTickers']) {
            console.log('\n📈 方法2: 批量获取行情...');
            
            const allTickers = await exchange.fetchTickers(symbols);
            
            console.log(`✅ 成功获取 ${Object.keys(allTickers).length} 个行情\n`);
            
            // 排序显示
            const sortedTickers = Object.entries(allTickers)
                .sort(([,a], [,b]) => (b.percentage || 0) - (a.percentage || 0));
            
            console.log('🚀 按涨跌幅排序:');
            sortedTickers.forEach(([symbol, ticker]) => {
                const emoji = (ticker.percentage || 0) >= 0 ? '🟢' : '🔴';
                console.log(`${emoji} ${symbol}: $${ticker.last} (${ticker.percentage?.toFixed(2)}%)`);
            });
        }
        
        return tickers;
        
    } catch (error) {
        console.error('❌ 批量获取行情失败:', error.message);
    }
}

// fetchMultipleTickers();

行情监控器

javascript
class TickerMonitor {
    constructor(exchange, symbols) {
        this.exchange = exchange;
        this.symbols = symbols;
        this.previousData = {};
        this.alerts = [];
    }
    
    // 添加价格警报
    addPriceAlert(symbol, price, condition = 'above') {
        this.alerts.push({
            symbol,
            price,
            condition, // 'above', 'below'
            triggered: false,
        });
    }
    
    // 添加涨跌幅警报
    addChangeAlert(symbol, percentage, condition = 'above') {
        this.alerts.push({
            symbol,
            type: 'change',
            percentage,
            condition,
            triggered: false,
        });
    }
    
    async checkAlerts(currentData) {
        for (const alert of this.alerts) {
            if (alert.triggered) continue;
            
            const ticker = currentData[alert.symbol];
            if (!ticker) continue;
            
            let shouldTrigger = false;
            
            if (alert.type === 'change') {
                const change = ticker.percentage || 0;
                shouldTrigger = alert.condition === 'above' ? 
                    change >= alert.percentage : 
                    change <= alert.percentage;
                    
                if (shouldTrigger) {
                    console.log(`🚨 涨跌幅警报: ${alert.symbol} ${change.toFixed(2)}% (阈值: ${alert.percentage}%)`);
                }
            } else {
                const price = ticker.last;
                shouldTrigger = alert.condition === 'above' ? 
                    price >= alert.price : 
                    price <= alert.price;
                    
                if (shouldTrigger) {
                    console.log(`🚨 价格警报: ${alert.symbol} $${price} (阈值: $${alert.price})`);
                }
            }
            
            if (shouldTrigger) {
                alert.triggered = true;
            }
        }
    }
    
    async monitor(interval = 30000) {
        console.log('🔍 开始监控价格变化...\n');
        
        // 设置一些警报示例
        this.addPriceAlert('BTC/USDT', 50000, 'above');
        this.addChangeAlert('ETH/USDT', 5, 'above');
        this.addChangeAlert('BTC/USDT', -5, 'below');
        
        while (true) {
            try {
                const currentData = {};
                
                console.log(`⏰ ${new Date().toLocaleString()} - 获取行情数据...`);
                
                // 获取当前行情
                for (const symbol of this.symbols) {
                    try {
                        const ticker = await this.exchange.fetchTicker(symbol);
                        currentData[symbol] = ticker;
                        
                        // 计算变化
                        const previous = this.previousData[symbol];
                        let changeIndicator = '⚪';
                        
                        if (previous && previous.last) {
                            const priceChange = ticker.last - previous.last;
                            const percentChange = (priceChange / previous.last) * 100;
                            
                            if (percentChange > 0.1) changeIndicator = '🟢';
                            else if (percentChange < -0.1) changeIndicator = '🔴';
                        }
                        
                        console.log(`${changeIndicator} ${symbol}: $${ticker.last} (${ticker.percentage?.toFixed(2)}%)`);
                        
                        await this.exchange.sleep(200); // 避免频率限制
                        
                    } catch (error) {
                        console.error(`❌ ${symbol}: ${error.message}`);
                    }
                }
                
                // 检查警报
                await this.checkAlerts(currentData);
                
                // 保存当前数据作为下次比较的基准
                this.previousData = { ...currentData };
                
                console.log(`\n💤 等待 ${interval/1000} 秒...\n`);
                await new Promise(resolve => setTimeout(resolve, interval));
                
            } catch (error) {
                console.error('❌ 监控过程中出错:', error.message);
                await new Promise(resolve => setTimeout(resolve, 5000));
            }
        }
    }
}

// 使用示例
async function startPriceMonitor() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    const symbols = ['BTC/USDT', 'ETH/USDT', 'BNB/USDT'];
    
    const monitor = new TickerMonitor(exchange, symbols);
    await monitor.monitor(30000); // 每30秒更新一次
}

// startPriceMonitor();

📖 订单簿数据处理

深度订单簿分析

javascript
class OrderBookAnalyzer {
    constructor(exchange) {
        this.exchange = exchange;
    }
    
    async fetchAndAnalyze(symbol, limit = 20) {
        try {
            console.log(`📖 分析 ${symbol} 订单簿深度...\n`);
            
            const orderbook = await this.exchange.fetchOrderBook(symbol, limit);
            
            // 基础信息
            console.log(`⏰ 更新时间: ${new Date(orderbook.timestamp).toLocaleString()}`);
            console.log(`📊 深度级别: ${Math.min(orderbook.bids.length, orderbook.asks.length)}\n`);
            
            // 计算深度统计
            const stats = this.calculateDepthStats(orderbook);
            
            // 显示统计信息
            this.displayStats(stats);
            
            // 显示订单簿
            this.displayOrderBook(orderbook, 10);
            
            // 分析支撑阻力位
            this.analyzeSupportResistance(orderbook);
            
            return { orderbook, stats };
            
        } catch (error) {
            console.error(`❌ 分析订单簿失败: ${error.message}`);
        }
    }
    
    calculateDepthStats(orderbook) {
        const bids = orderbook.bids;
        const asks = orderbook.asks;
        
        if (!bids.length || !asks.length) {
            return null;
        }
        
        // 最优价格
        const bestBid = bids[0][0];
        const bestAsk = asks[0][0];
        const spread = bestAsk - bestBid;
        const spreadPercent = (spread / bestBid) * 100;
        
        // 深度统计
        const bidVolume = bids.reduce((sum, [, amount]) => sum + amount, 0);
        const askVolume = asks.reduce((sum, [, amount]) => sum + amount, 0);
        const totalVolume = bidVolume + askVolume;
        
        // 成交量加权平均价
        const bidVWAP = bids.reduce((sum, [price, amount]) => sum + (price * amount), 0) / bidVolume;
        const askVWAP = asks.reduce((sum, [price, amount]) => sum + (price * amount), 0) / askVolume;
        
        // 价格分布
        const bidPriceRange = bids[0][0] - bids[bids.length - 1][0];
        const askPriceRange = asks[asks.length - 1][0] - asks[0][0];
        
        return {
            bestBid,
            bestAsk,
            spread,
            spreadPercent,
            bidVolume,
            askVolume,
            totalVolume,
            volumeRatio: bidVolume / askVolume,
            bidVWAP,
            askVWAP,
            bidPriceRange,
            askPriceRange,
        };
    }
    
    displayStats(stats) {
        if (!stats) return;
        
        console.log('📊 订单簿统计:');
        console.log(`💰 最优买价: $${stats.bestBid.toFixed(2)}`);
        console.log(`💰 最优卖价: $${stats.bestAsk.toFixed(2)}`);
        console.log(`📏 价差: $${stats.spread.toFixed(2)} (${stats.spreadPercent.toFixed(4)}%)`);
        console.log(`📈 买盘总量: ${stats.bidVolume.toFixed(4)}`);
        console.log(`📉 卖盘总量: ${stats.askVolume.toFixed(4)}`);
        console.log(`⚖️ 买卖比: ${stats.volumeRatio.toFixed(2)}`);
        console.log(`📊 买盘VWAP: $${stats.bidVWAP.toFixed(2)}`);
        console.log(`📊 卖盘VWAP: $${stats.askVWAP.toFixed(2)}\n`);
    }
    
    displayOrderBook(orderbook, displayLimit = 10) {
        const bids = orderbook.bids.slice(0, displayLimit);
        const asks = orderbook.asks.slice(0, displayLimit).reverse();
        
        console.log('📈 订单簿深度:');
        console.log('-'.repeat(60));
        console.log('       卖盘 (ASK)       |       买盘 (BID)       ');
        console.log('   价格    |    数量     |   价格    |    数量   ');
        console.log('-'.repeat(60));
        
        const maxLength = Math.max(asks.length, bids.length);
        
        for (let i = 0; i < maxLength; i++) {
            const ask = asks[i] || ['', ''];
            const bid = bids[i] || ['', ''];
            
            const askPrice = ask[0] ? `$${ask[0].toFixed(2)}` : '';
            const askAmount = ask[1] ? ask[1].toFixed(4) : '';
            const bidPrice = bid[0] ? `$${bid[0].toFixed(2)}` : '';
            const bidAmount = bid[1] ? bid[1].toFixed(4) : '';
            
            console.log(
                `${askPrice.padStart(9)} | ${askAmount.padStart(9)} | ` +
                `${bidPrice.padStart(9)} | ${bidAmount.padStart(9)}`
            );
        }
        
        console.log('-'.repeat(60) + '\n');
    }
    
    analyzeSupportResistance(orderbook) {
        const bids = orderbook.bids;
        const asks = orderbook.asks;
        
        if (!bids.length || !asks.length) return;
        
        // 找到大订单(可能的支撑/阻力位)
        const avgBidSize = bids.reduce((sum, [, amount]) => sum + amount, 0) / bids.length;
        const avgAskSize = asks.reduce((sum, [, amount]) => sum + amount, 0) / asks.length;
        
        const largeBids = bids.filter(([, amount]) => amount > avgBidSize * 2);
        const largeAsks = asks.filter(([, amount]) => amount > avgAskSize * 2);
        
        console.log('🎯 关键价位分析:');
        
        if (largeBids.length > 0) {
            console.log('📈 主要支撑位:');
            largeBids.slice(0, 3).forEach(([price, amount], index) => {
                console.log(`  ${index + 1}. $${price.toFixed(2)} (量: ${amount.toFixed(4)})`);
            });
        }
        
        if (largeAsks.length > 0) {
            console.log('📉 主要阻力位:');
            largeAsks.slice(0, 3).forEach(([price, amount], index) => {
                console.log(`  ${index + 1}. $${price.toFixed(2)} (量: ${amount.toFixed(4)})`);
            });
        }
        
        console.log('');
    }
}

// 使用示例
async function analyzeOrderBook() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    const analyzer = new OrderBookAnalyzer(exchange);
    
    await analyzer.fetchAndAnalyze('BTC/USDT', 20);
}

// analyzeOrderBook();

订单簿变化监控

javascript
class OrderBookMonitor {
    constructor(exchange, symbol) {
        this.exchange = exchange;
        this.symbol = symbol;
        this.previousOrderBook = null;
        this.changes = [];
    }
    
    async start(interval = 5000, limit = 10) {
        console.log(`🔍 开始监控 ${this.symbol} 订单簿变化...\n`);
        
        while (true) {
            try {
                const orderbook = await this.exchange.fetchOrderBook(this.symbol, limit);
                
                if (this.previousOrderBook) {
                    const changes = this.detectChanges(this.previousOrderBook, orderbook);
                    
                    if (changes.length > 0) {
                        console.log(`⏰ ${new Date().toLocaleString()} - 检测到 ${changes.length} 个变化:`);
                        
                        changes.forEach(change => {
                            const emoji = change.side === 'bid' ? '🟢' : '🔴';
                            const action = change.type === 'add' ? '新增' : 
                                          change.type === 'remove' ? '移除' : '修改';
                            
                            console.log(`  ${emoji} ${action} ${change.side}: $${change.price} × ${change.amount}`);
                        });
                        
                        console.log('');
                    }
                }
                
                this.previousOrderBook = this.cloneOrderBook(orderbook);
                
                await new Promise(resolve => setTimeout(resolve, interval));
                
            } catch (error) {
                console.error(`❌ 监控错误: ${error.message}`);
                await new Promise(resolve => setTimeout(resolve, 5000));
            }
        }
    }
    
    detectChanges(previous, current) {
        const changes = [];
        
        // 检查买盘变化
        changes.push(...this.detectSideChanges(previous.bids, current.bids, 'bid'));
        
        // 检查卖盘变化
        changes.push(...this.detectSideChanges(previous.asks, current.asks, 'ask'));
        
        return changes;
    }
    
    detectSideChanges(previousSide, currentSide, side) {
        const changes = [];
        const previousMap = new Map(previousSide.map(([price, amount]) => [price, amount]));
        const currentMap = new Map(currentSide.map(([price, amount]) => [price, amount]));
        
        // 检查新增和修改
        for (const [price, amount] of currentMap) {
            const previousAmount = previousMap.get(price);
            
            if (previousAmount === undefined) {
                // 新增订单
                changes.push({
                    type: 'add',
                    side: side,
                    price: price,
                    amount: amount,
                });
            } else if (Math.abs(previousAmount - amount) > 0.001) {
                // 订单量变化
                changes.push({
                    type: 'modify',
                    side: side,
                    price: price,
                    amount: amount,
                    previousAmount: previousAmount,
                    change: amount - previousAmount,
                });
            }
        }
        
        // 检查移除
        for (const [price, amount] of previousMap) {
            if (!currentMap.has(price)) {
                changes.push({
                    type: 'remove',
                    side: side,
                    price: price,
                    amount: amount,
                });
            }
        }
        
        return changes;
    }
    
    cloneOrderBook(orderbook) {
        return {
            symbol: orderbook.symbol,
            timestamp: orderbook.timestamp,
            bids: [...orderbook.bids.map(([price, amount]) => [price, amount])],
            asks: [...orderbook.asks.map(([price, amount]) => [price, amount])],
        };
    }
}

// 使用示例
async function monitorOrderBookChanges() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    const monitor = new OrderBookMonitor(exchange, 'BTC/USDT');
    
    await monitor.start(3000, 5); // 每3秒检查一次,监控前5档
}

// monitorOrderBookChanges();

📈 K线数据获取

基础K线数据获取

javascript
async function fetchOHLCVData() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    
    if (!exchange.has['fetchOHLCV']) {
        console.log('❌ 该交易所不支持K线数据获取');
        return;
    }
    
    try {
        const symbol = 'BTC/USDT';
        const timeframe = '1d';
        const limit = 30;
        
        console.log(`📈 获取 ${symbol} ${timeframe} K线数据 (最近${limit}根)...\n`);
        
        const ohlcv = await exchange.fetchOHLCV(symbol, timeframe, undefined, limit);
        
        console.log('📊 K线数据格式: [时间戳, 开盘, 最高, 最低, 收盘, 成交量]');
        console.log('-'.repeat(80));
        
        ohlcv.forEach((candle, index) => {
            const [timestamp, open, high, low, close, volume] = candle;
            const date = new Date(timestamp).toISOString().split('T')[0];
            const change = ((close - open) / open * 100).toFixed(2);
            const emoji = parseFloat(change) >= 0 ? '🟢' : '🔴';
            
            console.log(
                `${emoji} ${date}: ` +
                `开:$${open.toFixed(2)} ` +
                `高:$${high.toFixed(2)} ` +
                `低:$${low.toFixed(2)} ` +
                `收:$${close.toFixed(2)} ` +
                `量:${volume.toFixed(2)} ` +
                `涨跌:${change}%`
            );
        });
        
        // 计算基础统计
        const stats = calculateOHLCVStats(ohlcv);
        console.log('\n📊 统计信息:');
        console.log(`最高价: $${stats.maxHigh.toFixed(2)}`);
        console.log(`最低价: $${stats.minLow.toFixed(2)}`);
        console.log(`平均收盘价: $${stats.avgClose.toFixed(2)}`);
        console.log(`总成交量: ${stats.totalVolume.toFixed(2)}`);
        console.log(`上涨天数: ${stats.upDays}/${ohlcv.length} (${(stats.upDays/ohlcv.length*100).toFixed(1)}%)`);
        
        return ohlcv;
        
    } catch (error) {
        console.error('❌ 获取K线数据失败:', error.message);
    }
}

function calculateOHLCVStats(ohlcv) {
    let maxHigh = 0;
    let minLow = Infinity;
    let totalClose = 0;
    let totalVolume = 0;
    let upDays = 0;
    
    ohlcv.forEach(([, open, high, low, close, volume]) => {
        maxHigh = Math.max(maxHigh, high);
        minLow = Math.min(minLow, low);
        totalClose += close;
        totalVolume += volume;
        if (close > open) upDays++;
    });
    
    return {
        maxHigh,
        minLow,
        avgClose: totalClose / ohlcv.length,
        totalVolume,
        upDays,
    };
}

fetchOHLCVData();

多时间周期分析

javascript
class MultiTimeframeAnalyzer {
    constructor(exchange) {
        this.exchange = exchange;
        this.timeframes = ['1m', '5m', '15m', '1h', '4h', '1d'];
    }
    
    async analyzeSymbol(symbol) {
        console.log(`📈 多时间周期分析: ${symbol}\n`);
        
        const results = {};
        
        for (const timeframe of this.timeframes) {
            if (timeframe in this.exchange.timeframes) {
                try {
                    const limit = this.getLimit(timeframe);
                    const ohlcv = await this.exchange.fetchOHLCV(symbol, timeframe, undefined, limit);
                    
                    if (ohlcv.length > 0) {
                        const analysis = this.analyzeTimeframe(ohlcv, timeframe);
                        results[timeframe] = analysis;
                        
                        console.log(`${timeframe.padEnd(4)} | ` +
                                  `价格: $${analysis.lastClose.toFixed(2)} | ` +
                                  `趋势: ${analysis.trend} | ` +
                                  `涨跌: ${analysis.change.toFixed(2)}% | ` +
                                  `波动: ${analysis.volatility.toFixed(2)}%`);
                    }
                    
                    await this.exchange.sleep(200); // 避免频率限制
                    
                } catch (error) {
                    console.log(`${timeframe.padEnd(4)} | 获取失败: ${error.message}`);
                }
            }
        }
        
        // 综合分析
        this.provideSummary(results);
        
        return results;
    }
    
    getLimit(timeframe) {
        const limits = {
            '1m': 100,
            '5m': 100,
            '15m': 96,  // 1天
            '1h': 168,  // 1周
            '4h': 168,  // 4周
            '1d': 30,   // 1月
        };
        return limits[timeframe] || 50;
    }
    
    analyzeTimeframe(ohlcv, timeframe) {
        if (ohlcv.length < 2) return null;
        
        const latest = ohlcv[ohlcv.length - 1];
        const previous = ohlcv[ohlcv.length - 2];
        
        const [, , , , lastClose] = latest;
        const [, firstOpen] = ohlcv[0];
        
        // 计算变化
        const change = ((lastClose - firstOpen) / firstOpen) * 100;
        
        // 计算波动率
        const volatility = this.calculateVolatility(ohlcv);
        
        // 判断趋势
        const trend = this.determineTrend(ohlcv);
        
        // 计算支撑阻力
        const { support, resistance } = this.calculateSupportResistance(ohlcv);
        
        return {
            timeframe,
            lastClose,
            change,
            volatility,
            trend,
            support,
            resistance,
            dataPoints: ohlcv.length,
        };
    }
    
    calculateVolatility(ohlcv) {
        const changes = [];
        
        for (let i = 1; i < ohlcv.length; i++) {
            const prevClose = ohlcv[i-1][4];
            const currClose = ohlcv[i][4];
            const change = (currClose - prevClose) / prevClose;
            changes.push(change);
        }
        
        if (changes.length === 0) return 0;
        
        const mean = changes.reduce((sum, change) => sum + change, 0) / changes.length;
        const variance = changes.reduce((sum, change) => sum + Math.pow(change - mean, 2), 0) / changes.length;
        
        return Math.sqrt(variance) * 100; // 转换为百分比
    }
    
    determineTrend(ohlcv) {
        if (ohlcv.length < 5) return '数据不足';
        
        const recent = ohlcv.slice(-5);
        const closes = recent.map(candle => candle[4]);
        
        let upCount = 0;
        let downCount = 0;
        
        for (let i = 1; i < closes.length; i++) {
            if (closes[i] > closes[i-1]) upCount++;
            else if (closes[i] < closes[i-1]) downCount++;
        }
        
        if (upCount > downCount + 1) return '上升 📈';
        else if (downCount > upCount + 1) return '下降 📉';
        else return '震荡 ↔️';
    }
    
    calculateSupportResistance(ohlcv) {
        const lows = ohlcv.map(candle => candle[3]);
        const highs = ohlcv.map(candle => candle[2]);
        
        lows.sort((a, b) => a - b);
        highs.sort((a, b) => b - a);
        
        const support = lows[Math.floor(lows.length * 0.1)]; // 第10百分位
        const resistance = highs[Math.floor(highs.length * 0.1)]; // 第90百分位
        
        return { support, resistance };
    }
    
    provideSummary(results) {
        console.log('\n📊 综合分析总结:');
        
        const timeframes = Object.keys(results);
        if (timeframes.length === 0) {
            console.log('❌ 无有效数据');
            return;
        }
        
        // 趋势一致性分析
        const trends = timeframes.map(tf => results[tf].trend);
        const upTrends = trends.filter(t => t.includes('上升')).length;
        const downTrends = trends.filter(t => t.includes('下降')).length;
        
        console.log('🔍 趋势一致性:');
        if (upTrends > downTrends + 1) {
            console.log('  多时间周期显示上升趋势 🚀');
        } else if (downTrends > upTrends + 1) {
            console.log('  多时间周期显示下降趋势 📉');
        } else {
            console.log('  不同时间周期趋势不一致 ⚠️');
        }
        
        // 波动率分析
        const volatilities = timeframes.map(tf => results[tf].volatility);
        const avgVolatility = volatilities.reduce((sum, vol) => sum + vol, 0) / volatilities.length;
        
        console.log(`💥 平均波动率: ${avgVolatility.toFixed(2)}%`);
        if (avgVolatility > 5) {
            console.log('  高波动环境,注意风险控制 ⚠️');
        } else if (avgVolatility < 2) {
            console.log('  低波动环境,可能酝酿大行情 🤔');
        } else {
            console.log('  波动率正常 ✅');
        }
    }
}

// 使用示例
async function runMultiTimeframeAnalysis() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    const analyzer = new MultiTimeframeAnalyzer(exchange);
    
    await analyzer.analyzeSymbol('BTC/USDT');
}

// runMultiTimeframeAnalysis();

📋 成交记录分析

历史成交数据

javascript
async function analyzeTradeHistory() {
    const exchange = new ccxt.binance({ enableRateLimit: true });
    
    try {
        const symbol = 'BTC/USDT';
        const limit = 100;
        
        console.log(`📊 分析 ${symbol} 最近 ${limit} 笔成交...\n`);
        
        const trades = await exchange.fetchTrades(symbol, undefined, limit);
        
        if (trades.length === 0) {
            console.log('❌ 未获取到成交数据');
            return;
        }
        
        // 分析成交数据
        const analysis = analyzeTradeData(trades);
        
        // 显示成交统计
        console.log('📈 成交分析:');
        console.log(`总成交笔数: ${analysis.totalTrades}`);
        console.log(`成交时间范围: ${analysis.timeRange}`);
        console.log(`平均成交价: $${analysis.avgPrice.toFixed(2)}`);
        console.log(`成交量加权平均价: $${analysis.vwap.toFixed(2)}`);
        console.log(`最高成交价: $${analysis.maxPrice.toFixed(2)}`);
        console.log(`最低成交价: $${analysis.minPrice.toFixed(2)}`);
        console.log(`总成交量: ${analysis.totalVolume.toFixed(4)} BTC`);
        console.log(`总成交额: $${analysis.totalValue.toLocaleString()}\n`);
        
        // 买卖分析
        console.log('💹 买卖分析:');
        console.log(`买入订单: ${analysis.buyTrades} 笔 (${analysis.buyRatio.toFixed(1)}%)`);
        console.log(`卖出订单: ${analysis.sellTrades} 笔 (${analysis.sellRatio.toFixed(1)}%)`);
        console.log(`买入成交量: ${analysis.buyVolume.toFixed(4)} BTC`);
        console.log(`卖出成交量: ${analysis.sellVolume.toFixed(4)} BTC`);
        console.log(`买卖力度: ${analysis.buyVolume > analysis.sellVolume ? '买方优势' : '卖方优势'}\n`);
        
        // 大单分析
        const largeTradesThreshold = analysis.avgAmount * 3;
        const largeTrades = trades.filter(trade => trade.amount > largeTradesThreshold);
        
        if (largeTrades.length > 0) {
            console.log(`🐋 大单分析 (>${largeTradesThreshold.toFixed(4)} BTC):`);
            console.log(`大单数量: ${largeTrades.length} 笔`);
            
            largeTrades.slice(0, 5).forEach((trade, index) => {
                const time = new Date(trade.timestamp).toLocaleTimeString();
                const emoji = trade.side === 'buy' ? '🟢' : '🔴';
                console.log(`  ${index + 1}. ${emoji} ${time}: ${trade.amount.toFixed(4)} @ $${trade.price.toFixed(2)}`);
            });
        }
        
        return analysis;
        
    } catch (error) {
        console.error('❌ 分析成交历史失败:', error.message);
    }
}

function analyzeTradeData(trades) {
    let totalVolume = 0;
    let totalValue = 0;
    let buyTrades = 0;
    let sellTrades = 0;
    let buyVolume = 0;
    let sellVolume = 0;
    let maxPrice = 0;
    let minPrice = Infinity;
    let totalPriceVolume = 0;
    
    trades.forEach(trade => {
        totalVolume += trade.amount;
        totalValue += trade.amount * trade.price;
        totalPriceVolume += trade.price * trade.amount;
        
        maxPrice = Math.max(maxPrice, trade.price);
        minPrice = Math.min(minPrice, trade.price);
        
        if (trade.side === 'buy') {
            buyTrades++;
            buyVolume += trade.amount;
        } else {
            sellTrades++;
            sellVolume += trade.amount;
        }
    });
    
    const timeRange = `${new Date(trades[0].timestamp).toLocaleTimeString()} - ${new Date(trades[trades.length-1].timestamp).toLocaleTimeString()}`;
    
    return {
        totalTrades: trades.length,
        timeRange,
        avgPrice: totalValue / totalVolume,
        vwap: totalPriceVolume / totalVolume,
        maxPrice,
        minPrice,
        totalVolume,
        totalValue,
        avgAmount: totalVolume / trades.length,
        buyTrades,
        sellTrades,
        buyRatio: (buyTrades / trades.length) * 100,
        sellRatio: (sellTrades / trades.length) * 100,
        buyVolume,
        sellVolume,
    };
}

analyzeTradeHistory();

🎯 章节总结

本章我们学习了:

市场列表管理

  • 加载和筛选市场数据
  • 市场信息分析和统计

实时行情获取

  • 单个和批量行情获取
  • 行情监控和警报机制

订单簿数据处理

  • 深度分析和统计
  • 实时变化监控

K线数据分析

  • 基础OHLCV数据获取
  • 多时间周期分析

成交记录分析

  • 历史成交数据处理
  • 买卖力度分析

🔗 下一步

掌握了市场数据获取后,接下来学习:

  1. 账户与订单管理 - 进行实际的交易操作
  2. 实时数据流 - 使用WebSocket获取实时数据
  3. 错误处理 - 处理各种异常情况

下一章:账户与订单管理

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