diff --git a/.env.example b/.env.example index b44266f..e69fe04 100644 --- a/.env.example +++ b/.env.example @@ -28,6 +28,9 @@ ACLED_PASSWORD= PORT=3117 # Auto-refresh interval (minutes) REFRESH_INTERVAL_MINUTES=15 +# Run LLM analysis every N sweeps instead of every sweep (saves API costs) +# Example: 12 = every 3 hours with 15-min sweeps. Default: 1 (every sweep) +LLM_EVERY_N_SWEEPS=1 # === LLM Layer (optional) === # Enables AI-enhanced trade ideas and breaking news Telegram alerts. diff --git a/crucix.config.mjs b/crucix.config.mjs index c9e7235..1a66429 100644 --- a/crucix.config.mjs +++ b/crucix.config.mjs @@ -5,6 +5,7 @@ import './apis/utils/env.mjs'; // Load .env first export default { port: parseInt(process.env.PORT) || 3117, refreshIntervalMinutes: parseInt(process.env.REFRESH_INTERVAL_MINUTES) || 15, + llmEveryNSweeps: parseInt(process.env.LLM_EVERY_N_SWEEPS) || 1, llm: { provider: process.env.LLM_PROVIDER || null, // anthropic | openai | gemini | codex | openrouter | minimax | mistral diff --git a/server.mjs b/server.mjs index 157fbc8..d761854 100644 --- a/server.mjs +++ b/server.mjs @@ -33,6 +33,7 @@ let lastSweepTime = null; // Timestamp of last sweep let sweepStartedAt = null; // Timestamp when current/last sweep started let sweepInProgress = false; const startTime = Date.now(); +let sweepCount = 0; const sseClients = new Set(); // === Delta/Memory === @@ -272,6 +273,7 @@ app.get('/api/health', (req, res) => { llmProvider: config.llm.provider, telegramEnabled: !!(config.telegram.botToken && config.telegram.chatId), refreshIntervalMinutes: config.refreshIntervalMinutes, + llmEveryNSweeps: config.llmEveryNSweeps, language: currentLanguage, }); }); @@ -335,7 +337,14 @@ async function runSweepCycle() { synthesized.delta = delta; // 5. LLM-powered trade ideas (LLM-only feature) — isolated so failures don't kill sweep - if (llmProvider?.isConfigured) { + sweepCount++; + const runLLM = (sweepCount % config.llmEveryNSweeps) === 0; + if (!runLLM && llmProvider?.isConfigured) { + console.log(`[Crucix] Skipping LLM this sweep (${sweepCount}/${config.llmEveryNSweeps})`); + const prev = memory.getLastRun(); + synthesized.ideas = prev?.ideas || []; + synthesized.ideasSource = prev?.ideasSource || 'carried'; + } else if (runLLM && llmProvider?.isConfigured) { try { console.log('[Crucix] Generating LLM trade ideas...'); const previousIdeas = memory.getLastRun()?.ideas || []; @@ -353,13 +362,13 @@ async function runSweepCycle() { synthesized.ideas = []; synthesized.ideasSource = 'llm-failed'; } - } else { + } else if (!llmProvider?.isConfigured) { synthesized.ideas = []; synthesized.ideasSource = 'disabled'; } // 6. Alert evaluation — Telegram + Discord (LLM with rule-based fallback, multi-tier, semantic dedup) - if (delta?.summary?.totalChanges > 0) { + if (delta?.summary?.totalChanges > 0 && runLLM) { if (telegramAlerter.isConfigured) { telegramAlerter.evaluateAndAlert(llmProvider, delta, memory).catch(err => { console.error('[Crucix] Telegram alert error:', err.message);