diff --git a/src/clis/binance/asks.yaml b/src/clis/binance/asks.yaml new file mode 100644 index 000000000..cc876cde9 --- /dev/null +++ b/src/clis/binance/asks.yaml @@ -0,0 +1,32 @@ +site: binance +name: asks +description: Order book ask prices for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + limit: + type: int + default: 10 + description: Number of price levels (5, 10, 20, 50, 100) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/depth?symbol=${{ args.symbol }}&limit=${{ args.limit }} + + - select: asks + + - map: + rank: ${{ index + 1 }} + ask_price: ${{ item.0 }} + ask_qty: ${{ item.1 }} + + - limit: ${{ args.limit }} + +columns: [rank, ask_price, ask_qty] diff --git a/src/clis/binance/commands.test.ts b/src/clis/binance/commands.test.ts new file mode 100644 index 000000000..a16b146b9 --- /dev/null +++ b/src/clis/binance/commands.test.ts @@ -0,0 +1,63 @@ +import fs from 'node:fs'; +import yaml from 'js-yaml'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { executePipeline } from '../../pipeline.js'; + +function loadPipeline(name: string): any[] { + const file = new URL(`./${name}.yaml`, import.meta.url); + const def = yaml.load(fs.readFileSync(file, 'utf-8')) as { pipeline: any[] }; + return def.pipeline; +} + +function mockJsonOnce(payload: unknown) { + vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ + json: vi.fn().mockResolvedValue(payload), + })); +} + +afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); +}); + +describe('binance YAML adapters', () => { + it('sorts top pairs by numeric quote volume', async () => { + mockJsonOnce([ + { symbol: 'SMALL', lastPrice: '1', priceChangePercent: '1.2', highPrice: '1', lowPrice: '1', quoteVolume: '9.9' }, + { symbol: 'LARGE', lastPrice: '2', priceChangePercent: '2.3', highPrice: '2', lowPrice: '2', quoteVolume: '100.0' }, + { symbol: 'MID', lastPrice: '3', priceChangePercent: '3.4', highPrice: '3', lowPrice: '3', quoteVolume: '11.0' }, + ]); + + const result = await executePipeline(null, loadPipeline('top'), { args: { limit: 3 } }); + + expect(result.map((item: any) => item.symbol)).toEqual(['LARGE', 'MID', 'SMALL']); + expect(result.map((item: any) => item.rank)).toEqual([1, 2, 3]); + }); + + it('sorts gainers by numeric percent change', async () => { + mockJsonOnce([ + { symbol: 'TEN', lastPrice: '1', priceChangePercent: '10.0', quoteVolume: '100' }, + { symbol: 'NINE', lastPrice: '1', priceChangePercent: '9.5', quoteVolume: '100' }, + { symbol: 'HUNDRED', lastPrice: '1', priceChangePercent: '100.0', quoteVolume: '100' }, + ]); + + const result = await executePipeline(null, loadPipeline('gainers'), { args: { limit: 3 } }); + + expect(result.map((item: any) => item.symbol)).toEqual(['HUNDRED', 'TEN', 'NINE']); + }); + + it('keeps only TRADING pairs', async () => { + mockJsonOnce({ + symbols: [ + { symbol: 'BTCUSDT', baseAsset: 'BTC', quoteAsset: 'USDT', status: 'TRADING' }, + { symbol: 'OLDPAIR', baseAsset: 'OLD', quoteAsset: 'USDT', status: 'BREAK' }, + ], + }); + + const result = await executePipeline(null, loadPipeline('pairs'), { args: { limit: 10 } }); + + expect(result).toEqual([ + { symbol: 'BTCUSDT', base: 'BTC', quote: 'USDT', status: 'TRADING' }, + ]); + }); +}); diff --git a/src/clis/binance/depth.yaml b/src/clis/binance/depth.yaml new file mode 100644 index 000000000..58ddc6841 --- /dev/null +++ b/src/clis/binance/depth.yaml @@ -0,0 +1,32 @@ +site: binance +name: depth +description: Order book bid prices for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + limit: + type: int + default: 10 + description: Number of price levels (5, 10, 20, 50, 100) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/depth?symbol=${{ args.symbol }}&limit=${{ args.limit }} + + - select: bids + + - map: + rank: ${{ index + 1 }} + bid_price: ${{ item.0 }} + bid_qty: ${{ item.1 }} + + - limit: ${{ args.limit }} + +columns: [rank, bid_price, bid_qty] diff --git a/src/clis/binance/gainers.yaml b/src/clis/binance/gainers.yaml new file mode 100644 index 000000000..36e5732d8 --- /dev/null +++ b/src/clis/binance/gainers.yaml @@ -0,0 +1,40 @@ +site: binance +name: gainers +description: Top gaining trading pairs by 24h price change +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 10 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - filter: item.priceChangePercent + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + volume: ${{ item.quoteVolume }} + sort_change: ${{ Number(item.priceChangePercent) }} + + - sort: + by: sort_change + order: desc + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + volume: ${{ item.quoteVolume }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price, change_24h, volume] diff --git a/src/clis/binance/klines.yaml b/src/clis/binance/klines.yaml new file mode 100644 index 000000000..f2cf3cb79 --- /dev/null +++ b/src/clis/binance/klines.yaml @@ -0,0 +1,36 @@ +site: binance +name: klines +description: Candlestick/kline data for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + interval: + type: str + default: 1d + description: "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + limit: + type: int + default: 10 + description: Number of klines (max 1000) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/klines?symbol=${{ args.symbol }}&interval=${{ args.interval }}&limit=${{ args.limit }} + + - map: + open: ${{ item.1 }} + high: ${{ item.2 }} + low: ${{ item.3 }} + close: ${{ item.4 }} + volume: ${{ item.5 }} + + - limit: ${{ args.limit }} + +columns: [open, high, low, close, volume] diff --git a/src/clis/binance/losers.yaml b/src/clis/binance/losers.yaml new file mode 100644 index 000000000..63c36f1da --- /dev/null +++ b/src/clis/binance/losers.yaml @@ -0,0 +1,39 @@ +site: binance +name: losers +description: Top losing trading pairs by 24h price change +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 10 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - filter: item.priceChangePercent + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + volume: ${{ item.quoteVolume }} + sort_change: ${{ Number(item.priceChangePercent) }} + + - sort: + by: sort_change + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + volume: ${{ item.quoteVolume }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price, change_24h, volume] diff --git a/src/clis/binance/pairs.yaml b/src/clis/binance/pairs.yaml new file mode 100644 index 000000000..dec32c840 --- /dev/null +++ b/src/clis/binance/pairs.yaml @@ -0,0 +1,30 @@ +site: binance +name: pairs +description: List active trading pairs on Binance +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/exchangeInfo + + - select: symbols + + - filter: item.status === 'TRADING' + + - map: + symbol: ${{ item.symbol }} + base: ${{ item.baseAsset }} + quote: ${{ item.quoteAsset }} + status: ${{ item.status }} + + - limit: ${{ args.limit }} + +columns: [symbol, base, quote, status] diff --git a/src/clis/binance/price.yaml b/src/clis/binance/price.yaml new file mode 100644 index 000000000..dd597baba --- /dev/null +++ b/src/clis/binance/price.yaml @@ -0,0 +1,30 @@ +site: binance +name: price +description: Quick price check for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr?symbol=${{ args.symbol }} + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change: ${{ item.priceChange }} + change_pct: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.volume }} + quote_volume: ${{ item.quoteVolume }} + trades: ${{ item.count }} + +columns: [symbol, price, change, change_pct, high, low, volume, quote_volume, trades] diff --git a/src/clis/binance/prices.yaml b/src/clis/binance/prices.yaml new file mode 100644 index 000000000..e4553dcb1 --- /dev/null +++ b/src/clis/binance/prices.yaml @@ -0,0 +1,25 @@ +site: binance +name: prices +description: Latest prices for all trading pairs +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of prices + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/price + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.price }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price] diff --git a/src/clis/binance/ticker.yaml b/src/clis/binance/ticker.yaml new file mode 100644 index 000000000..9f2a46456 --- /dev/null +++ b/src/clis/binance/ticker.yaml @@ -0,0 +1,45 @@ +site: binance +name: ticker +description: 24h ticker statistics for top trading pairs by volume +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of tickers + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_pct: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.volume }} + quote_vol: ${{ item.quoteVolume }} + trades: ${{ item.count }} + sort_volume: ${{ Number(item.quoteVolume) }} + + - sort: + by: sort_volume + order: desc + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_pct: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.volume }} + quote_vol: ${{ item.quoteVolume }} + trades: ${{ item.count }} + + - limit: ${{ args.limit }} + +columns: [symbol, price, change_pct, high, low, volume, quote_vol, trades] diff --git a/src/clis/binance/top.yaml b/src/clis/binance/top.yaml new file mode 100644 index 000000000..b4a871301 --- /dev/null +++ b/src/clis/binance/top.yaml @@ -0,0 +1,42 @@ +site: binance +name: top +description: Top trading pairs by 24h volume on Binance +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.quoteVolume }} + sort_volume: ${{ Number(item.quoteVolume) }} + + - sort: + by: sort_volume + order: desc + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.quoteVolume }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price, change_24h, high, low, volume] diff --git a/src/clis/binance/trades.yaml b/src/clis/binance/trades.yaml new file mode 100644 index 000000000..958e23143 --- /dev/null +++ b/src/clis/binance/trades.yaml @@ -0,0 +1,32 @@ +site: binance +name: trades +description: Recent trades for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + limit: + type: int + default: 20 + description: Number of trades (max 1000) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/trades?symbol=${{ args.symbol }}&limit=${{ args.limit }} + + - map: + id: ${{ item.id }} + price: ${{ item.price }} + qty: ${{ item.qty }} + quote_qty: ${{ item.quoteQty }} + buyer_maker: ${{ item.isBuyerMaker }} + + - limit: ${{ args.limit }} + +columns: [id, price, qty, quote_qty, buyer_maker]