Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions crucix.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 12 additions & 3 deletions server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ===
Expand Down Expand Up @@ -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,
});
});
Expand Down Expand Up @@ -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 || [];
Expand All @@ -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);
Expand Down