← Назад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 };