← Назад
const axios = require('axios'); const config = require('../config'); const logger = require('../utils/logger'); async function fetchOptions() { try { const resp = await axios.get(config.binance.tickerUrl); return resp.data; } catch (err) { logger.error(`Binance ticker error: ${err.message}`); return null; } } async function fetchGreeks() { try { const resp = await axios.get(config.binance.markUrl); return resp.data; } catch (err) { logger.error(`Binance greeks error: ${err.message}`); return null; } } async function fetchSpotPrices() { try { const resp = await axios.get('https://api.binance.com/api/v3/ticker/price', { params: { symbols: '["BTCUSDT","ETHUSDT","SOLUSDT","DOGEUSDT","XRPUSDT","BNBUSDT"]' }, }); const prices = {}; for (const item of resp.data) { if (item.symbol === 'BTCUSDT') prices.BTC = parseFloat(item.price); if (item.symbol === 'ETHUSDT') prices.ETH = parseFloat(item.price); if (item.symbol === 'SOLUSDT') prices.SOL = parseFloat(item.price); if (item.symbol === 'DOGEUSDT') prices.DOGE = parseFloat(item.price); if (item.symbol === 'XRPUSDT') prices.XRP = parseFloat(item.price); if (item.symbol === 'BNBUSDT') prices.BNB = parseFloat(item.price); } return prices; } catch (err) { logger.error(`Binance spot price error: ${err.message}`); return {}; } } /** * Fetch open interest from /eapi/v1/openInterest. * Requires both underlyingAsset AND expiration params. * @param {Array} options - ticker data to extract expiry dates from */ async function fetchOpenInterest(options) { const oiMap = {}; // symbol → sumOpenInterest if (!options || !options.length) return oiMap; // Extract unique underlying+expiry combos from ticker symbols // Symbol format: BTC-260412-69500-C const combos = new Map(); // "BTC|260412" → true for (const o of options) { const parts = o.symbol.split('-'); if (parts.length >= 3) { combos.set(`${parts[0]}|${parts[1]}`, true); } } // Fetch OI for each underlying+expiry combo (parallel per underlying) const tasks = []; for (const key of combos.keys()) { const [asset, expiration] = key.split('|'); tasks.push( axios.get('https://eapi.binance.com/eapi/v1/openInterest', { params: { underlyingAsset: asset, expiration }, timeout: 5000, }).then(resp => { if (Array.isArray(resp.data)) { for (const item of resp.data) { oiMap[item.symbol] = parseFloat(item.sumOpenInterest || 0); } } }).catch(err => { // -6010 = no data for this combo, skip silently const code = err.response?.data?.code; if (code !== -6010) { logger.error(`OI fetch error (${asset}/${expiration}): ${err.message}`); } }) ); } await Promise.all(tasks); logger.info(`OI fetched: ${Object.keys(oiMap).length} symbols from ${combos.size} expiry combos`); return oiMap; } /** * Fetch contract unit multipliers from exchangeInfo. * XRP=100, DOGE=1000, BTC/ETH/SOL/BNB=1. * Premium in API is per-contract: pricePerUnit = lastPrice / unit. * Returns { BTC: 1, ETH: 1, SOL: 1, BNB: 1, XRP: 100, DOGE: 1000, ... } */ async function fetchContractUnits() { try { const resp = await axios.get('https://eapi.binance.com/eapi/v1/exchangeInfo'); const units = {}; for (const sym of (resp.data.optionSymbols || [])) { const underlying = sym.underlying?.replace('USDT', ''); if (underlying && !units[underlying]) { units[underlying] = parseInt(sym.unit) || 1; } } logger.info(`[BINANCE] Contract units loaded: ${JSON.stringify(units)}`); return units; } catch (err) { logger.error(`[BINANCE] Failed to load contract units: ${err.message}`); return { BTC: 1, ETH: 1, SOL: 1, BNB: 1, XRP: 100, DOGE: 1000 }; // fallback } } module.exports = { fetchOptions, fetchGreeks, fetchSpotPrices, fetchOpenInterest, fetchContractUnits };