第三章:市场数据获取
市场数据是交易决策的基础,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数据获取
- 多时间周期分析
✅ 成交记录分析
- 历史成交数据处理
- 买卖力度分析
🔗 下一步
掌握了市场数据获取后,接下来学习:
下一章:账户与订单管理