← Назадconst { Router } = require('express');
const { checkApiKey } = require('../middleware/auth');
const { cache } = require('../services/cache');
const { fetchSpotPrices } = require('../services/binance');
const { findUnusualVolume } = require('../analysis/unusualVolume');
const { findTopMovers } = require('../analysis/topMovers');
const { calculateAllMaxPain } = require('../analysis/maxPain');
const { getAtmIv, calculateIvRankAndPercentile } = require('../analysis/ivAnalysis');
const { analyzeAllPCRatios } = require('../analysis/putCallRatio');
const { analyzeSkew } = require('../analysis/ivSkew');
const { findGammaPlays } = require('../analysis/gammaPlay');
const { analyzeOiConcentration } = require('../analysis/oiConcentration');
const { getAtmIvHistory } = require('../services/ivHistory');
const { mergeGreeksIntoOptions } = require('../utils/mergeGreeks');
const router = Router();
function mergeGreeks(optionsData) {
return mergeGreeksIntoOptions(optionsData, cache.greeks);
}
router.get('/api/signals/unusual-volume', checkApiKey, async (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const spotPrices = await fetchSpotPrices();
const data = mergeGreeks(cache.options);
const result = findUnusualVolume(data, spotPrices, {
underlying: req.query.underlying || null,
limit: parseInt(req.query.limit) || 30,
});
res.json({ success: true, lastUpdate: cache.lastUpdate, ...result });
});
router.get('/api/signals/iv-analysis', checkApiKey, async (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const days = parseInt(req.query.days) || 30;
const spotPrices = await fetchSpotPrices();
const data = mergeGreeks(cache.options);
const results = {};
for (const [asset, spot] of Object.entries(spotPrices)) {
const atm = getAtmIv(data, asset, spot);
if (!atm) continue;
const history = await getAtmIvHistory(asset, days);
const ranked = calculateIvRankAndPercentile(atm.iv, history);
results[asset] = {
...atm,
spotPrice: spot,
...ranked,
historyDays: days,
historyPoints: history.length,
rankSource: history.length >= 10 ? 'db' : 'insufficient_history',
};
}
res.json({ success: true, lastUpdate: cache.lastUpdate, data: results });
});
router.get('/api/signals/max-pain', checkApiKey, async (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const spotPrices = await fetchSpotPrices();
const data = mergeGreeks(cache.options);
const result = calculateAllMaxPain(data, spotPrices);
res.json({ success: true, lastUpdate: cache.lastUpdate, count: result.length, data: result });
});
router.get('/api/signals/oi-concentration', checkApiKey, async (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const data = mergeGreeks(cache.options);
const underlying = req.query.underlying ? req.query.underlying.toUpperCase() : null;
const result = analyzeOiConcentration(data, {
underlying,
limit: parseInt(req.query.limit) || 20,
});
res.json({
success: true,
lastUpdate: cache.lastUpdate,
underlying: underlying || 'ALL',
count: result.length,
data: result,
});
});
router.get('/api/signals/put-call-ratio', checkApiKey, (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const data = mergeGreeks(cache.options);
const underlying = req.query.underlying ? req.query.underlying.toUpperCase() : null;
const result = analyzeAllPCRatios(data, underlying);
res.json({ success: true, lastUpdate: cache.lastUpdate, ...result });
});
router.get('/api/signals/iv-skew', checkApiKey, async (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const spotPrices = await fetchSpotPrices();
const data = mergeGreeks(cache.options);
const results = {};
for (const [asset, spot] of Object.entries(spotPrices)) {
results[asset] = analyzeSkew(data, asset, spot);
}
res.json({ success: true, lastUpdate: cache.lastUpdate, data: results });
});
router.get('/api/signals/gamma-play', checkApiKey, async (req, res) => {
if (!cache.options) {
return res.status(503).json({ error: 'Data not ready' });
}
const spotPrices = await fetchSpotPrices();
const data = mergeGreeks(cache.options);
const result = findGammaPlays(data, spotPrices, {
limit: parseInt(req.query.limit) || 20,
});
res.json({ success: true, lastUpdate: cache.lastUpdate, count: result.length, data: result });
});
module.exports = router;