UNPKG

binance-historical

Version:

Download historical klines from binance api

286 lines (285 loc) 9.17 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runCommand = runCommand; const prompts_1 = __importDefault(require("prompts")); const commander_1 = require("commander"); const klines_1 = require("./klines"); const utils_1 = require("./utils"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const VALID_INTERVALS = [ '1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', ]; const questions = [ { type: 'text', name: 'pair', message: 'Pair that you want to track:', initial: 'ETHUSDT', }, { type: 'select', name: 'interval', message: 'The interval:', choices: [ { title: '1 minute', value: '1m' }, { title: '3 minutes', value: '3m' }, { title: '5 minutes', value: '5m' }, { title: '15 minutes', value: '15m' }, { title: '30 minutes', value: '30m' }, { title: '1 hour', value: '1h' }, { title: '2 hours', value: '2h' }, { title: '4 hours', value: '4h' }, { title: '6 hours', value: '6h' }, { title: '8 hours', value: '8h' }, { title: '12 hours', value: '12h' }, { title: '1 day', value: '1d' }, { title: '3 days', value: '3d' }, { title: '1 week', value: '1w' }, ], initial: 7, }, { type: 'date', name: 'startDate', message: 'The starting date of the interval:', initial: new Date(), }, { type: 'date', name: 'endDate', message: 'The ending date of the interval:', initial: new Date(), }, { type: 'text', name: 'fileName', message: 'The path of the file that will be saved:', initial: `${process.cwd()}/`, }, { type: 'select', name: 'format', message: 'Output format:', choices: [ { title: 'JSON', value: 'json' }, { title: 'CSV', value: 'csv' }, ], initial: 0, }, ]; function isValidInterval(interval) { return VALID_INTERVALS.includes(interval); } function isValidFormat(format) { return format === 'json' || format === 'csv'; } function parseDate(dateStr) { const date = new Date(dateStr); if (Number.isNaN(date.getTime())) { return null; } return date; } function validateOptions(options) { const errors = []; if (options.interval && !isValidInterval(options.interval)) { errors.push(`Invalid interval "${options.interval}". Valid intervals: ${VALID_INTERVALS.join(', ')}`); } if (options.start && !parseDate(options.start)) { errors.push(`Invalid start date "${options.start}". Use format: YYYY-MM-DD or ISO 8601`); } if (options.end && !parseDate(options.end)) { errors.push(`Invalid end date "${options.end}". Use format: YYYY-MM-DD or ISO 8601`); } if (options.start && options.end) { const startDate = parseDate(options.start); const endDate = parseDate(options.end); if (startDate && endDate && startDate >= endDate) { errors.push('Start date must be before end date'); } } if (options.format && !isValidFormat(options.format)) { errors.push(`Invalid format "${options.format}". Valid formats: json, csv`); } return { valid: errors.length === 0, errors }; } function hasAllRequiredOptions(options) { return !!(options.pair && options.interval && options.start && options.end && options.output && options.format); } async function promptUser() { const { pair, interval, startDate, endDate, fileName, format } = await (0, prompts_1.default)(questions); return { pair, interval, startDate, endDate, fileName, format }; } async function promptMissingOptions(options) { const providedValues = {}; const missingQuestions = []; if (options.pair) { providedValues.pair = options.pair; } else { missingQuestions.push(questions[0]); } if (options.interval && isValidInterval(options.interval)) { providedValues.interval = options.interval; } else { missingQuestions.push(questions[1]); } if (options.start) { const date = parseDate(options.start); if (date) { providedValues.startDate = date; } else { missingQuestions.push(questions[2]); } } else { missingQuestions.push(questions[2]); } if (options.end) { const date = parseDate(options.end); if (date) { providedValues.endDate = date; } else { missingQuestions.push(questions[3]); } } else { missingQuestions.push(questions[3]); } if (options.output) { providedValues.fileName = options.output; } else { missingQuestions.push(questions[4]); } if (options.format && isValidFormat(options.format)) { providedValues.format = options.format; } else { missingQuestions.push(questions[5]); } if (missingQuestions.length > 0) { const answers = await (0, prompts_1.default)(missingQuestions); return { ...providedValues, ...answers }; } return providedValues; } async function downloadKlines(config) { const { pair, interval, startDate, endDate, fileName, format } = config; const kLines = await (0, klines_1.getKline)(pair, interval, startDate, endDate).catch((error) => { console.error('Error fetching klines:', error.message || error); return null; }); if (kLines) { const extension = format === 'csv' ? 'csv' : 'json'; const outputPath = fileName + `${pair}_${interval}_${(0, utils_1.formatDate)(startDate)}_${(0, utils_1.formatDate)(endDate)}.${extension}`; (0, utils_1.saveKline)(outputPath, kLines, format); console.log(`Downloaded ${kLines.length} klines to ${outputPath}`); } } async function processWithOptions(options) { const validation = validateOptions(options); if (!validation.valid) { for (const err of validation.errors) { console.error(`Error: ${err}`); } process.exit(1); } if (hasAllRequiredOptions(options)) { const pair = options.pair; const interval = options.interval; const startDate = parseDate(options.start); const endDate = parseDate(options.end); const fileName = options.output; const format = options.format; await downloadKlines({ pair, interval, startDate, endDate, fileName, format }); } else { const result = await promptMissingOptions(options); if (!result.pair || !result.interval || !result.startDate || !result.endDate || !result.fileName || !result.format) { console.error('Missing required information'); process.exit(1); } await downloadKlines(result); } } async function processInteractive() { const result = await promptUser(); if (!result.pair || !result.interval || !result.startDate || !result.endDate || !result.fileName || !result.format) { console.error('Missing required information'); process.exit(1); } await downloadKlines(result); } async function runCommand() { const program = new commander_1.Command(); let version = '1.0.0'; try { const packageJsonPath = (0, node_path_1.join)(__dirname, '../package.json'); const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf-8')); version = packageJson.version; } catch { // Fallback to default version if package.json cannot be read } program .name('binance-historical') .description('Download historical klines from Binance') .version(version) .option('-p, --pair <symbol>', 'Trading pair (e.g., BTCUSDT, ETHUSDT)') .option('-i, --interval <interval>', `Kline interval (${VALID_INTERVALS.join(', ')})`) .option('-s, --start <date>', 'Start date (YYYY-MM-DD or ISO 8601)') .option('-e, --end <date>', 'End date (YYYY-MM-DD or ISO 8601)') .option('-o, --output <path>', 'Output directory path (filename is auto-generated)') .option('-f, --format <format>', 'Output format (json, csv)', 'json') .action(async (options) => { const hasAnyOption = options.pair || options.interval || options.start || options.end || options.output; if (hasAnyOption) { await processWithOptions(options); } else { await processInteractive(); } }); program.parse(); }