Example: rest-spot-exchange-info.ts
Static snapshot for Binance/Rest/Spot/rest-spot-exchange-info.ts.
Example Path
Binance/Rest/Spot/rest-spot-exchange-info.ts
Source Link
Repository source: https://github.com/sieblyio/crypto-api-examples/blob/master/examples/Binance/Rest/Spot/rest-spot-exchange-info.ts
Code Snapshot
import {
ExchangeInfo,
MainClient,
numberInString,
roundToStepSize,
roundToTickSize,
} from 'binance';
const client = new MainClient({
// Optional (default: false) - when true, response strings are parsed to floats (only for known keys).
// beautifyResponses: true,
});
interface SymbolInfo {
tickSize?: numberInString;
qtyStepSize?: numberInString;
minOrderQty?: numberInString;
maxOrderQty?: numberInString;
maxMarketQty?: numberInString;
maxNumOfOrders?: number;
minNotional?: numberInString;
maxNotional?: numberInString;
maxBasePrecisionDecimals: number;
maxQuotePrecisionDecimals: number;
}
// Get full exchange info so we can cache it and use it for other functions without making request every time
async function fetchExchangeInfo() {
try {
const exchangeInfo = await client.getExchangeInfo();
return exchangeInfo;
} catch (error) {
throw new Error(
`Failed to get exchange info: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
const symbol = 'SOLUSDT';
async function getSymbolInfo(
exchangeInfo: ExchangeInfo,
symbol: string,
): Promise<SymbolInfo> {
try {
// Find the symbol information once
const symbolInfo = exchangeInfo.symbols.find((s) => s.symbol === symbol);
// console.log(symbolInfo);
if (!symbolInfo) {
throw new Error(`Symbol ${symbol} not found in exchange info`);
}
// Extract filters from the symbol info
const priceFilter = symbolInfo.filters.find(
(f) => f.filterType === 'PRICE_FILTER',
);
const lotSizeFilter = symbolInfo.filters.find(
(f) => f.filterType === 'LOT_SIZE',
);
const marketLotSizeFilter = symbolInfo.filters.find(
(f) => f.filterType === 'MARKET_LOT_SIZE',
);
const maxNumOrdersFilter = symbolInfo.filters.find(
(f) => f.filterType === 'MAX_NUM_ORDERS',
);
const notionalFilter = symbolInfo.filters.find(
(f) => f.filterType === 'NOTIONAL',
);
const symbolFilters = {
tickSize: priceFilter?.tickSize,
qtyStepSize: lotSizeFilter?.stepSize,
minOrderQty: lotSizeFilter?.minQty,
maxOrderQty: lotSizeFilter?.maxQty,
maxMarketQty: marketLotSizeFilter?.maxQty,
maxNumOfOrders: maxNumOrdersFilter?.maxNumOrders,
minNotional: notionalFilter?.minNotional,
maxNotional: notionalFilter?.maxNotional,
maxBasePrecisionDecimals: symbolInfo.baseAssetPrecision,
maxQuotePrecisionDecimals: symbolInfo.quoteAssetPrecision,
};
console.log(symbolFilters);
return symbolFilters;
} catch (error) {
throw new Error(
`Failed to get symbol info: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
/**
* Validates and formats an order based on symbol constraints
*/
function formatOrderParams(
symbol: string,
price: number,
quantity: number,
symbolInfo: any,
): { symbol: string; price: number; quantity: number } {
try {
// Check if price is within allowed range
const minPrice = parseFloat(symbolInfo.tickSize || '0');
if (price < minPrice) {
throw new Error(`Price ${price} is below minimum ${minPrice}`);
}
// Check if quantity is within allowed range
const minQty = parseFloat(symbolInfo.minOrderQty || '0');
const maxQty = parseFloat(symbolInfo.maxOrderQty || Infinity);
if (quantity < minQty) {
throw new Error(`Quantity ${quantity} is below minimum ${minQty}`);
}
if (quantity > maxQty) {
throw new Error(`Quantity ${quantity} exceeds maximum ${maxQty}`);
}
// Check notional value (price * quantity)
const notional = price * quantity;
const minNotional = parseFloat(symbolInfo.minNotional || '0');
if (notional < minNotional) {
throw new Error(
`Order value ${notional} is below minimum ${minNotional}`,
);
}
// Format price and quantity according to exchange requirements
if (!symbolInfo.tickSize || !symbolInfo.qtyStepSize) {
throw new Error('Missing required symbol info: tickSize or qtyStepSize');
}
const formattedPrice = roundToTickSize(price, symbolInfo.tickSize);
const formattedQty = roundToStepSize(quantity, symbolInfo.qtyStepSize);
return {
symbol,
price: formattedPrice,
quantity: formattedQty,
};
} catch (error) {
throw new Error(
`Failed to format order: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
// Example usage
async function testSymbolUtils() {
const exchangeInfo = await fetchExchangeInfo();
if (!exchangeInfo) return;
const symbolFilters = await getSymbolInfo(exchangeInfo, symbol);
if (!symbolFilters) return;
// Test price formatting
if (!symbolFilters.tickSize) {
console.log('tickSize not available for this symbol');
return;
}
const testPrice = 23.45678;
console.log(
`Original price: ${testPrice}`,
`Formatted price: ${roundToTickSize(testPrice, symbolFilters.tickSize.toString())}`,
);
// Test quantity formatting
if (!symbolFilters.qtyStepSize) {
console.log('qtyStepSize not available for this symbol');
return;
}
const testQty = 1.23456;
console.log(
`Original quantity: ${testQty}`,
`Formatted quantity: ${roundToStepSize(
testQty,
symbolFilters.qtyStepSize.toString(),
)}`,
);
// Test full order formatting
const orderParams = formatOrderParams(
symbol,
testPrice,
testQty,
symbolFilters,
);
console.log('Formatted order parameters:', orderParams);
// example how to use the order params
const order = await client.submitNewOrder({
symbol: orderParams.symbol,
side: 'BUY',
type: 'LIMIT',
quantity: Number(orderParams.quantity),
price: Number(orderParams.price),
timeInForce: 'GTC',
});
console.log(order);
}
testSymbolUtils();
This is a static, crawlable snapshot. The interactive app loads after JavaScript starts and can refresh live data.