β ΠΠ°Π·Π°Π΄'use strict';
const { sendMessage } = require('./telegram');
const config = require('../config');
// In-memory dedup: signalId β timestamp of last sent alert
const sentAlerts = new Map();
const DEDUP_TTL_MS = 30 * 60 * 1000; // 30 minutes
function shouldSend(signalId) {
const lastSent = sentAlerts.get(signalId);
return !lastSent || Date.now() - lastSent > DEDUP_TTL_MS;
}
function formatNumber(key, value) {
if (typeof value !== 'number') return String(value);
// Show more decimals for small values like IV, gamma, delta
if (Math.abs(value) < 0.001) return value.toFixed(6);
if (Math.abs(value) < 1) return value.toFixed(4);
if (Math.abs(value) >= 10000) return value.toLocaleString('en');
return value.toFixed(2);
}
function formatSignalMessage(signal) {
const dirEmoji = {
BULLISH: 'π',
BEARISH: 'π',
NEUTRAL: 'βοΈ',
NEUTRAL_VOLATILE: 'π',
NEUTRAL_RANGE: 'π',
NEUTRAL_BULLISH: 'π',
BULLISH_HEDGED: 'π‘οΈ',
};
const sevEmoji = { EXTREME: 'π΄', HIGH: 'π ', MEDIUM: 'π‘', LOW: 'π’' };
const emoji = dirEmoji[signal.direction] || 'π';
const sev = sevEmoji[signal.severity] || 'βͺ';
const paramsText = Object.entries(signal.parameters || {})
.slice(0, 7)
.map(([k, v]) => ` β’ <b>${k}</b>: ${formatNumber(k, v)}`)
.join('\n');
return [
`${sev} <b>${signal.strategy.toUpperCase()}</b>`,
``,
`${emoji} ${signal.signal} β <b>${signal.underlying}</b>`,
`Confidence: <b>${signal.confidence}%</b> Severity: ${signal.severity}`,
``,
`<i>${signal.description}</i>`,
``,
signal.rationale,
``,
`<b>ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ:</b>`,
paramsText,
``,
`<code>${new Date().toISOString()}</code>`,
].join('\n');
}
// Process a list of signals and send Telegram notifications for new HIGH+ ones
async function processSignals(signals) {
if (!config.telegram.botToken || !config.telegram.chatId) return;
const minConfidence = config.alerts.minConfidence;
const toSend = signals.filter(s => s.confidence >= minConfidence && shouldSend(s.id));
for (const signal of toSend) {
await sendMessage(formatSignalMessage(signal));
sentAlerts.set(signal.id, Date.now());
// Small delay to respect Telegram rate limits (30 msg/sec global, 1 msg/sec per chat)
await new Promise(r => setTimeout(r, 400));
}
}
// Periodically clean up old dedup entries
setInterval(() => {
const cutoff = Date.now() - DEDUP_TTL_MS * 2;
for (const [id, ts] of sentAlerts.entries()) {
if (ts < cutoff) sentAlerts.delete(id);
}
}, 15 * 60 * 1000);
module.exports = { processSignals };