← Back
β˜†
'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 };

πŸ“œ Git History

163bb5dfeat: migrate to options-screener-v2 folder to isolate deployment4 months ago
Show last diff
Loading...