import ccxt, { binance, Exchange, okx, type OHLCV } from "ccxt"; import { Command } from "commander"; import { createObjectCsvWriter } from "csv-writer"; import { parse } from "date-fns"; import ProgressBar from "progress"; async function fetchOHLCV( exchangeId: string, symbol: string, timeframe: string, since: number, until: number, outputPath: string, ) { const ExchangeClass = (ccxt as { [key: string]: any })[exchangeId]; if (!ExchangeClass) { throw new Error(`Exchange ${exchangeId} not supported`); } const defaultLimit = 500; const exchange: Exchange = new ExchangeClass(); const progressBar = new ProgressBar( "Fetching data [:bar] :percent :elapseds", { total: until - since, width: 40, }, ); let allOHLCV: OHLCV[] = []; let currentSince = since; while (true) { const ohlcv = await exchange.fetchOHLCV( symbol, timeframe, currentSince, defaultLimit, ); const lastTimestamp = ohlcv[ohlcv.length - 1][0]; if (lastTimestamp == undefined) { throw new Error(`last timestamp undefined`); } const increment = lastTimestamp - currentSince; progressBar.tick(increment); currentSince = lastTimestamp + 1; allOHLCV = allOHLCV.concat(ohlcv); if (lastTimestamp >= until) break; } const records = allOHLCV.map((candle) => { const [timestamp, open, high, low, close, volume] = candle; return { open, high, low, close, volume, timestamp, }; }); const csvWriter = createObjectCsvWriter({ path: outputPath, header: [ { id: "open", title: "open" }, { id: "high", title: "high" }, { id: "low", title: "low" }, { id: "close", title: "close" }, { id: "volume", title: "volume" }, { id: "timestamp", title: "timestamp" }, ], }); await csvWriter.writeRecords(records); console.log(`Total fetched: ${allOHLCV.length}, saved to ${outputPath}`); } // Use commander to handle command-line arguments const cli = new Command(); cli.description("CLI tool for fetching trading data from exchanges."); cli .requiredOption("-e, --exchange ", "Exchange, e.g., binance") .requiredOption( "-s, --symbol ", "Trading pair symbol, e.g., BTC/USDT", ) .requiredOption( "-t, --timeframe ", "Timeframe, e.g., 1m, 5m, 1h, 1d", ) .requiredOption("--since ", "Start time (format: 2024-07-01 00:00:00)") .requiredOption("--until ", "End time (format: 2024-07-18 23:59:59)") .requiredOption("-o, --output ", "Path to save the CSV file"); cli.parse(process.argv); const options = cli.opts(); const since = parse(options.since, "yyyy-MM-dd HH:mm:ss", new Date()).getTime(); const until = parse(options.until, "yyyy-MM-dd HH:mm:ss", new Date()).getTime(); try { await fetchOHLCV( options.exchange, options.symbol, options.timeframe, since, until, options.output, ); } catch (err) { console.error("Error:", err); }