From 532c5c888ec9e0d1a0a59f1254cd1366b2a5c90e Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Tue, 5 Aug 2025 23:41:29 +0200 Subject: [PATCH] feat: implement 24/7 server-side automation with AI learning integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Core Features: - True 24/7 automation runs without browser dependency - Server-side process in Docker container - Auto-executes paper trades with β‰₯60% confidence - Integrates with existing AI learning system - Safe paper trading mode only (zero real money risk) - working-24x7.js: Main automation process (currently running) - check-automation.js: Status monitoring and health checks - app/api/safe-paper-trading/create-trade/route.js: Paper trade API - app/api/automation-24x7/route.js: Automation control API - Fixed continuous learning state persistence issues - Added force enable function for debugging: window.forceEnableLearning() - Enhanced state restoration logic with immediate and delayed checks - Auto-execute toggle now properly unlocks when continuous learning active - System running successfully (PID: 3922502) - Already executed first automated paper trade (80% confidence SELL) - Scheduled to run every 60 minutes automatically - Logs all activity for monitoring and debugging ical Implementation: - Uses curl for HTTP requests (no fetch dependencies) - Background process with proper signal handling - Comprehensive error handling and logging - Integration with existing analysis pipeline - Maintains compatibility with browser-based safe paper trading This completes the 24/7 automation requirement - system now runs continuously in Docker container without requiring browser tabs to remain open. --- app/api/automation-24x7/route.js | 76 +++++ .../safe-paper-trading/create-trade/route.js | 111 +++++++ app/safe-paper-trading/page.js | 140 ++++----- automation-daemon.js | 271 ++++++++++++++++++ check-automation.js | 90 ++++++ enable-continuous-learning.js | 26 ++ nohup.out | 7 + simple-24x7-automation.js | 169 +++++++++++ start-24-7-automation.js | 263 +++++++++++++++++ working-24x7.js | 102 +++++++ 10 files changed, 1166 insertions(+), 89 deletions(-) create mode 100644 app/api/automation-24x7/route.js create mode 100644 app/api/safe-paper-trading/create-trade/route.js create mode 100644 automation-daemon.js create mode 100644 check-automation.js create mode 100644 enable-continuous-learning.js create mode 100644 nohup.out create mode 100644 simple-24x7-automation.js create mode 100644 start-24-7-automation.js create mode 100644 working-24x7.js diff --git a/app/api/automation-24x7/route.js b/app/api/automation-24x7/route.js new file mode 100644 index 0000000..460c207 --- /dev/null +++ b/app/api/automation-24x7/route.js @@ -0,0 +1,76 @@ +import { NextResponse } from 'next/server' + +// Import the 24/7 automation service +let automation24x7 +try { + const automationModule = require('../../../../start-24-7-automation.js') + automation24x7 = automationModule.automation24x7 +} catch (error) { + console.error('❌ Could not load 24/7 automation service:', error.message) +} + +export async function POST(request) { + try { + if (!automation24x7) { + return NextResponse.json({ + success: false, + message: '24/7 automation service not available' + }, { status: 500 }) + } + + const { action, config } = await request.json() + + if (action === 'start') { + // Update config if provided + if (config) { + Object.assign(automation24x7.config, config) + } + + const result = await automation24x7.start() + return NextResponse.json(result) + + } else if (action === 'stop') { + const result = await automation24x7.stop() + return NextResponse.json(result) + + } else { + return NextResponse.json({ + success: false, + message: 'Invalid action. Use "start" or "stop"' + }, { status: 400 }) + } + + } catch (error) { + console.error('❌ 24/7 automation control error:', error) + return NextResponse.json({ + success: false, + message: 'Failed to control automation', + error: error.message + }, { status: 500 }) + } +} + +export async function GET(request) { + try { + if (!automation24x7) { + return NextResponse.json({ + success: false, + message: '24/7 automation service not available' + }, { status: 500 }) + } + + const status = automation24x7.getStatus() + return NextResponse.json({ + success: true, + automation: status + }) + + } catch (error) { + console.error('❌ 24/7 automation status error:', error) + return NextResponse.json({ + success: false, + message: 'Failed to get automation status', + error: error.message + }, { status: 500 }) + } +} diff --git a/app/api/safe-paper-trading/create-trade/route.js b/app/api/safe-paper-trading/create-trade/route.js new file mode 100644 index 0000000..f03b819 --- /dev/null +++ b/app/api/safe-paper-trading/create-trade/route.js @@ -0,0 +1,111 @@ +import { NextResponse } from 'next/server' + +// Simple in-memory storage for paper trades (in production, use database) +let paperTrades = [] +let tradeIdCounter = 1 + +export async function POST(request) { + try { + const tradeData = await request.json() + + // Validate required fields + const required = ['symbol', 'side', 'amount', 'entry', 'confidence'] + for (const field of required) { + if (!tradeData[field]) { + return NextResponse.json({ + success: false, + message: `Missing required field: ${field}` + }, { status: 400 }) + } + } + + // Create paper trade + const trade = { + id: `PAPER_${Date.now()}_${tradeIdCounter++}`, + symbol: tradeData.symbol, + side: tradeData.side, + amount: tradeData.amount, + entry: tradeData.entry, + stopLoss: tradeData.stopLoss, + takeProfit: tradeData.takeProfit, + confidence: tradeData.confidence, + reasoning: tradeData.reasoning, + source: tradeData.source || 'manual', + status: 'OPEN', + createdAt: new Date().toISOString(), + pnl: 0, + fees: 0 + } + + // Store trade + paperTrades.push(trade) + + console.log(`πŸ“„ Paper trade created: ${trade.id} - ${trade.side} ${trade.symbol} at $${trade.entry} (${trade.confidence}% confidence)`) + + // Log to AI learning system if available + try { + const learningData = { + id: `decision_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + symbol: trade.symbol, + timeframe: '60', + side: trade.side, + confidence: trade.confidence, + entry: trade.entry, + stopLoss: trade.stopLoss, + takeProfit: trade.takeProfit, + reasoning: trade.reasoning, + source: 'paper_trade_automation', + createdAt: new Date().toISOString() + } + + // Store in learning system (try to call learning API) + fetch('http://localhost:9001/api/ai-learning/record-decision', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(learningData) + }).catch(error => { + console.log('⚠️ Could not log to learning system:', error.message) + }) + + } catch (error) { + console.log('⚠️ Learning system integration error:', error.message) + } + + return NextResponse.json({ + success: true, + message: 'Paper trade created successfully', + trade: trade + }) + + } catch (error) { + console.error('❌ Create paper trade error:', error) + return NextResponse.json({ + success: false, + message: 'Failed to create paper trade', + error: error.message + }, { status: 500 }) + } +} + +export async function GET(request) { + try { + // Return all paper trades + return NextResponse.json({ + success: true, + trades: paperTrades, + summary: { + total: paperTrades.length, + open: paperTrades.filter(t => t.status === 'OPEN').length, + closed: paperTrades.filter(t => t.status === 'CLOSED').length, + totalPnL: paperTrades.reduce((sum, t) => sum + (t.pnl || 0), 0) + } + }) + } catch (error) { + console.error('❌ Get paper trades error:', error) + return NextResponse.json({ + success: false, + message: 'Failed to get paper trades', + error: error.message + }, { status: 500 }) + } +} diff --git a/app/safe-paper-trading/page.js b/app/safe-paper-trading/page.js index 700600e..c983b6c 100644 --- a/app/safe-paper-trading/page.js +++ b/app/safe-paper-trading/page.js @@ -147,18 +147,42 @@ export default function SafePaperTradingPage() { if (savedContinuousLearning === 'true') { console.log('πŸ”„ Restoring continuous learning state...') setContinuousLearning(true) + // Force restart continuous learning immediately setTimeout(() => { console.log('πŸŽ“ Starting continuous learning from restored state') startContinuousLearning() - }, 2000) + }, 1000) // Reduced delay + } else { + console.log('πŸ’‘ No continuous learning state found - user needs to start manually') } } catch (error) { console.error('⚠️ Error checking continuous learning state:', error) } } - // Check state after a short delay to ensure everything is loaded - setTimeout(checkContinuousLearningState, 1000) + // Force enable learning for testing - DEBUG FUNCTION + const forceEnableLearning = () => { + console.log('πŸ”§ FORCE ENABLING CONTINUOUS LEARNING...') + localStorage.setItem('safePaperTrading_continuousLearning', 'true') + setContinuousLearning(true) + setTimeout(() => { + console.log('πŸŽ“ Force starting continuous learning') + startContinuousLearning() + }, 500) + console.log('βœ… Continuous learning forcefully enabled') + } + + // Check state immediately + checkContinuousLearningState() + + // Expose force enable function to browser console for debugging + if (typeof window !== 'undefined') { + window.forceEnableLearning = forceEnableLearning + console.log('πŸ”§ Debug function exposed: window.forceEnableLearning()') + } + + // Also check after a short delay to ensure everything is loaded + setTimeout(checkContinuousLearningState, 2000) }, []) // Persist analysis data whenever it changes @@ -810,51 +834,6 @@ export default function SafePaperTradingPage() { - {/* AI LEARNING SETUP NOTICE */} - {!continuousLearning || !autoExecuteTrades ? ( -
-

πŸ€– Enable AI Learning from Virtual Trading

-
- Current Issue: AI is analyzing but not learning from outcomes because virtual trading is not enabled. -
-
-
-

Step 1: Enable Continuous Learning

-

- {continuousLearning ? 'βœ… Enabled' : '❌ Click "πŸŽ“ Start Learning" button below'} -

-
-
-

Step 2: Enable Auto-Execute

-

- {autoExecuteTrades ? 'βœ… Enabled' : continuousLearning ? '❌ Enable "Auto-Execute Trades" below' : '⏸️ Waiting for Step 1'} -

-
-
-
- Result: AI will automatically execute virtual trades β†’ track outcomes β†’ learn patterns β†’ improve over time -
-
- ) : ( -
-

βœ… AI Learning System Active

-
-
- πŸŽ“ Continuous Learning: ON -
-
- πŸ€– Auto-Execute: ON -
-
- πŸ“ˆ Virtual Trading: Active -
-
-
- 🧠 AI will automatically execute virtual trades based on analysis and learn from outcomes to improve performance -
-
- )} - {/* Header with Balance */}
@@ -995,49 +974,32 @@ export default function SafePaperTradingPage() {
- {/* Auto-Execute Toggle - Show always, but disabled until continuous learning is active */} -
-
-
- Auto-Execute Trades - - {continuousLearning - ? "Automatically execute paper trades based on AI recommendations (β‰₯60% confidence)" - : "⚠️ Enable Continuous Learning first to activate auto-execute virtual trading" - } - + {/* Auto-Execute Toggle - Only show when continuous learning is active */} + {continuousLearning && ( +
+
+
+ Auto-Execute Trades + Automatically execute paper trades based on AI recommendations (β‰₯60% confidence) +
+
- + {autoExecuteTrades && ( +
+ ⚑ Paper trades will be executed automatically when AI recommends BUY/SELL with β‰₯60% confidence +
+ )}
- {autoExecuteTrades && continuousLearning && ( -
- ⚑ Paper trades will be executed automatically when AI recommends BUY/SELL with β‰₯60% confidence -
- )} - {!continuousLearning && ( -
- πŸ’‘ For AI Learning: Enable "Continuous Learning" + "Auto-Execute" so the AI can learn from virtual trade outcomes -
- )} -
+ )}
diff --git a/automation-daemon.js b/automation-daemon.js new file mode 100644 index 0000000..c8fdea3 --- /dev/null +++ b/automation-daemon.js @@ -0,0 +1,271 @@ +#!/usr/bin/env node + +/** + * Simple 24/7 Background Automation Daemon + * Runs as a background process in Docker container + */ + +const { spawn } = require('child_process'); +const fs = require('fs').promises; +const path = require('path'); + +class BackgroundAutomation { + constructor() { + this.logFile = '/tmp/automation-24x7.log'; + this.pidFile = '/tmp/automation-24x7.pid'; + this.config = { + symbol: 'SOLUSD', + timeframe: '60', + intervalMinutes: 60, + autoExecuteThreshold: 60 + }; + } + + async log(message) { + const timestamp = new Date().toISOString(); + const logEntry = `[${timestamp}] ${message}\n`; + console.log(logEntry.trim()); + + try { + await fs.appendFile(this.logFile, logEntry); + } catch (error) { + // Ignore file errors + } + } + + async isRunning() { + try { + const pidData = await fs.readFile(this.pidFile, 'utf8'); + const pid = parseInt(pidData.trim()); + + // Check if process exists + try { + process.kill(pid, 0); // Signal 0 checks if process exists + return { running: true, pid }; + } catch (error) { + // Process doesn't exist + await fs.unlink(this.pidFile).catch(() => {}); + return { running: false, pid: null }; + } + } catch (error) { + return { running: false, pid: null }; + } + } + + async start() { + const status = await this.isRunning(); + if (status.running) { + await this.log(`⚠️ Automation already running (PID: ${status.pid})`); + return { success: false, message: 'Already running', pid: status.pid }; + } + + await this.log('πŸš€ Starting 24/7 Background Automation...'); + + // Start the main automation loop + this.startAutomationLoop(); + + await this.log(`βœ… Automation started (PID: ${process.pid})`); + return { success: true, message: 'Started', pid: process.pid }; + } + + async startAutomationLoop() { + // Save PID + await fs.writeFile(this.pidFile, process.pid.toString()); + + // Handle cleanup on exit + process.on('SIGTERM', () => this.cleanup()); + process.on('SIGINT', () => this.cleanup()); + process.on('exit', () => this.cleanup()); + + let cycleCount = 0; + + // Main automation loop + const runCycle = async () => { + try { + cycleCount++; + await this.log(`πŸ”„ Starting analysis cycle #${cycleCount}`); + + // Get analysis + const analysis = await this.getAnalysis(); + if (analysis && analysis.confidence >= this.config.autoExecuteThreshold) { + await this.log(`🎯 Executing trade: ${analysis.recommendation} (${analysis.confidence}% confidence)`); + await this.createPaperTrade(analysis); + } else if (analysis) { + await this.log(`⏸️ No trade: ${analysis.recommendation} (${analysis.confidence}% confidence < ${this.config.autoExecuteThreshold}% threshold)`); + } + + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + } + }; + + // Run first cycle immediately + await runCycle(); + + // Schedule recurring cycles + setInterval(runCycle, this.config.intervalMinutes * 60 * 1000); + + await this.log(`⏰ Scheduled to run every ${this.config.intervalMinutes} minutes`); + } + + async getAnalysis() { + try { + // Use curl to avoid fetch dependencies + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + + const { stdout } = await execAsync(`curl -s "http://localhost:9001/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}"`); + + const data = JSON.parse(stdout); + + if (data.success && data.data && data.data.analysis) { + return data.data.analysis; + } + + return null; + } catch (error) { + await this.log(`❌ Analysis error: ${error.message}`); + return null; + } + } + + async createPaperTrade(analysis) { + try { + // Use curl to avoid fetch dependencies + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, + amount: 100, + entry: analysis.entry, + stopLoss: analysis.stopLoss, + takeProfit: analysis.takeProfit, + confidence: analysis.confidence, + reasoning: analysis.reasoning, + source: '24x7_daemon' + }; + + const curlCommand = `curl -s -X POST http://localhost:9001/api/safe-paper-trading/create-trade \ + -H "Content-Type: application/json" \ + -d '${JSON.stringify(tradeData)}'`; + + const { stdout } = await execAsync(curlCommand); + const result = JSON.parse(stdout); + + if (result.success) { + await this.log(`βœ… Paper trade created: ${result.trade.id}`); + } else { + await this.log(`❌ Trade creation failed: ${result.message}`); + } + + } catch (error) { + await this.log(`❌ Trade creation error: ${error.message}`); + } + } + + async stop() { + const status = await this.isRunning(); + if (!status.running) { + await this.log('⚠️ Automation not running'); + return { success: false, message: 'Not running' }; + } + + try { + process.kill(status.pid, 'SIGTERM'); + await fs.unlink(this.pidFile).catch(() => {}); + await this.log(`πŸ›‘ Automation stopped (PID: ${status.pid})`); + return { success: true, message: 'Stopped', pid: status.pid }; + } catch (error) { + await this.log(`❌ Stop error: ${error.message}`); + return { success: false, message: error.message }; + } + } + + async cleanup() { + try { + await fs.unlink(this.pidFile).catch(() => {}); + await this.log('🧹 Cleanup completed'); + } catch (error) { + // Ignore cleanup errors + } + } + + async getStatus() { + const status = await this.isRunning(); + + let logs = ''; + try { + const logData = await fs.readFile(this.logFile, 'utf8'); + logs = logData.split('\n').slice(-10).join('\n'); // Last 10 lines + } catch (error) { + logs = 'No logs available'; + } + + return { + running: status.running, + pid: status.pid, + config: this.config, + recentLogs: logs + }; + } +} + +// Create instance +const daemon = new BackgroundAutomation(); + +// CLI interface +if (require.main === module) { + const command = process.argv[2]; + + switch (command) { + case 'start': + daemon.start().then(result => { + console.log(JSON.stringify(result, null, 2)); + if (result.success) { + // Keep process alive + process.stdin.resume(); + } else { + process.exit(1); + } + }); + break; + + case 'stop': + daemon.stop().then(result => { + console.log(JSON.stringify(result, null, 2)); + process.exit(0); + }); + break; + + case 'status': + daemon.getStatus().then(status => { + console.log(JSON.stringify(status, null, 2)); + process.exit(0); + }); + break; + + default: + console.log(` +πŸ€– 24/7 Background Automation Daemon + +Usage: + node automation-daemon.js start # Start in background + node automation-daemon.js stop # Stop daemon + node automation-daemon.js status # Check status + +Features: + βœ… True background process + βœ… Survives browser close + βœ… Auto-executes trades β‰₯60% confidence + βœ… Logs all activity + βœ… Safe paper trading only + `); + } +} + +// Export for API use +module.exports = { daemon }; diff --git a/check-automation.js b/check-automation.js new file mode 100644 index 0000000..a1f3611 --- /dev/null +++ b/check-automation.js @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +// Quick automation status checker + +const { exec } = require('child_process'); +const { promisify } = require('util'); +const execAsync = promisify(exec); + +async function checkStatus() { + console.log('πŸ€– 24/7 AUTOMATION STATUS CHECK\n'); + + try { + // Check if process is running + const { stdout } = await execAsync('ps aux | grep "working-24x7" | grep -v grep'); + if (stdout.trim()) { + console.log('βœ… AUTOMATION STATUS: RUNNING'); + console.log(`πŸ“Š Process: ${stdout.trim().split(/\s+/).slice(0, 11).join(' ')}`); + } else { + console.log('❌ AUTOMATION STATUS: NOT RUNNING'); + return; + } + } catch (error) { + console.log('❌ AUTOMATION STATUS: NOT RUNNING'); + return; + } + + try { + // Check recent logs + const { stdout: logs } = await execAsync('tail -5 nohup.out'); + console.log('\nπŸ“ RECENT LOGS:'); + console.log(logs); + } catch (error) { + console.log('\n⚠️ No logs available'); + } + + try { + // Check current analysis + const { stdout } = await execAsync('curl -s "http://localhost:9001/api/ai-analysis/latest?symbol=SOLUSD&timeframe=60"'); + const data = JSON.parse(stdout); + + if (data.success && data.data && data.data.analysis) { + const analysis = data.data.analysis; + console.log('\nπŸ“Š CURRENT ANALYSIS:'); + console.log(` Signal: ${analysis.recommendation}`); + console.log(` Confidence: ${analysis.confidence}%`); + console.log(` Entry: $${analysis.entry}`); + console.log(` Status: ${analysis.confidence >= 60 ? '🎯 WILL AUTO-EXECUTE' : '⏸️ Below threshold'}`); + } else { + console.log('\n❌ No analysis data available'); + } + } catch (error) { + console.log('\n❌ Analysis check failed:', error.message); + } + + try { + // Check recent paper trades + const { stdout } = await execAsync('curl -s "http://localhost:9001/api/safe-paper-trading/create-trade"'); + const data = JSON.parse(stdout); + + if (data.success && data.trades) { + console.log('\nπŸ“„ PAPER TRADES:'); + console.log(` Total: ${data.summary.total}`); + console.log(` Open: ${data.summary.open}`); + console.log(` Closed: ${data.summary.closed}`); + console.log(` P&L: $${data.summary.totalPnL}`); + + // Show recent trades + const recentTrades = data.trades.slice(-3); + if (recentTrades.length > 0) { + console.log('\nπŸ“ˆ RECENT TRADES:'); + recentTrades.forEach(trade => { + console.log(` ${trade.id}: ${trade.side} ${trade.symbol} @ $${trade.entry} (${trade.confidence}%)`); + }); + } + } + } catch (error) { + console.log('\n⚠️ Could not check paper trades'); + } + + console.log('\n🎯 AUTOMATION CONFIGURATION:'); + console.log(' Symbol: SOLUSD'); + console.log(' Timeframe: 1 hour'); + console.log(' Interval: Every 60 minutes'); + console.log(' Auto-execute: β‰₯60% confidence'); + console.log(' Mode: Safe paper trading only'); + + console.log('\n✨ System is running 24/7 in your Docker container!'); +} + +checkStatus(); diff --git a/enable-continuous-learning.js b/enable-continuous-learning.js new file mode 100644 index 0000000..5ad4818 --- /dev/null +++ b/enable-continuous-learning.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node + +// Quick script to enable continuous learning via browser console +console.log(` +πŸ”§ ENABLE CONTINUOUS LEARNING MANUALLY + +1. Open http://localhost:9001/safe-paper-trading in your browser +2. Open Developer Tools (F12) +3. Go to Console tab +4. Run this command: + + window.forceEnableLearning() + +5. Refresh the page - you should see: + - Button says "πŸ›‘ Stop Learning" (instead of Start) + - Auto-execute toggle becomes available: "πŸ”’ Locked" β†’ "πŸ€– OFF" + +6. Click the auto-execute toggle to enable it: "πŸ€– OFF" β†’ "πŸ€– ON" + +7. Now when you get strong signals (β‰₯60% confidence), it will auto-create paper trades! + +Alternative: You can also run this in console: + localStorage.setItem('safePaperTrading_continuousLearning', 'true') + location.reload() + +`); diff --git a/nohup.out b/nohup.out new file mode 100644 index 0000000..c1254db --- /dev/null +++ b/nohup.out @@ -0,0 +1,7 @@ +[2025-08-05T21:34:07.846Z] πŸš€ 24/7 AUTOMATION STARTED +[2025-08-05T21:34:07.848Z] πŸ“Š SOLUSD every 60m, threshold β‰₯60% +[2025-08-05T21:34:07.848Z] πŸ”„ Analysis cycle #1 +[2025-08-05T21:34:52.488Z] πŸ“Š SELL (80% confidence) +[2025-08-05T21:34:52.489Z] 🎯 AUTO-EXECUTING: 80% β‰₯ 60% +[2025-08-05T21:34:52.961Z] βœ… TRADE CREATED: PAPER_1754429692956_1 +[2025-08-05T21:34:53.032Z] ⏰ Next cycle: 12:34:52 AM diff --git a/simple-24x7-automation.js b/simple-24x7-automation.js new file mode 100644 index 0000000..171bbdd --- /dev/null +++ b/simple-24x7-automation.js @@ -0,0 +1,169 @@ +#!/usr/bin/env node + +/** + * Simple 24/7 Automation Service + * Direct execution without complex daemon management + */ + +const { exec } = require('child_process'); +const { promisify } = require('util'); +const execAsync = promisify(exec); +const fs = require('fs').promises; + +class SimpleAutomation { + constructor() { + this.isRunning = false; + this.config = { + symbol: 'SOLUSD', + timeframe: '60', + intervalMinutes: 60, + autoExecuteThreshold: 60 + }; + this.stats = { + startTime: new Date(), + totalCycles: 0, + totalTrades: 0 + }; + } + + async log(message) { + const timestamp = new Date().toISOString(); + const logEntry = `[${timestamp}] ${message}`; + console.log(logEntry); + + // Also save to log file + try { + await fs.appendFile('/tmp/automation-simple.log', logEntry + '\n'); + } catch (error) { + // Ignore file errors + } + } + + async start() { + if (this.isRunning) { + await this.log('⚠️ Already running'); + return; + } + + this.isRunning = true; + await this.log('πŸš€ Starting Simple 24/7 Automation...'); + await this.log(`πŸ“Š Config: ${this.config.symbol} ${this.config.timeframe}m, every ${this.config.intervalMinutes} minutes`); + + // Run first cycle immediately + await this.runCycle(); + + // Schedule recurring cycles + setInterval(async () => { + try { + await this.runCycle(); + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + } + }, this.config.intervalMinutes * 60 * 1000); + + await this.log('βœ… 24/7 Automation running'); + } + + async runCycle() { + this.stats.totalCycles++; + await this.log(`πŸ”„ Analysis cycle #${this.stats.totalCycles}`); + + try { + // Get analysis using curl + const { stdout } = await execAsync(`curl -s "http://localhost:9001/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}"`); + + const data = JSON.parse(stdout); + + if (data.success && data.data && data.data.analysis) { + const analysis = data.data.analysis; + await this.log(`πŸ“Š Analysis: ${analysis.recommendation} (${analysis.confidence}% confidence)`); + + // Check if we should execute trade + if (analysis.confidence >= this.config.autoExecuteThreshold) { + await this.log(`🎯 Executing trade: confidence ${analysis.confidence}% β‰₯ ${this.config.autoExecuteThreshold}%`); + await this.executeTrade(analysis); + } else { + await this.log(`⏸️ No trade: confidence ${analysis.confidence}% < ${this.config.autoExecuteThreshold}%`); + } + } else { + await this.log('❌ No analysis data received'); + } + + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + } + } + + async executeTrade(analysis) { + try { + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, + amount: 100, + entry: analysis.entry, + stopLoss: analysis.stopLoss, + takeProfit: analysis.takeProfit, + confidence: analysis.confidence, + reasoning: analysis.reasoning, + source: 'simple_24x7' + }; + + // Use curl to create paper trade + const curlData = JSON.stringify(tradeData).replace(/'/g, "'\\''"); + const { stdout } = await execAsync(`curl -s -X POST http://localhost:9001/api/safe-paper-trading/create-trade -H "Content-Type: application/json" -d '${curlData}'`); + + const result = JSON.parse(stdout); + + if (result.success) { + this.stats.totalTrades++; + await this.log(`βœ… Paper trade created: ${result.trade.id}`); + } else { + await this.log(`❌ Trade failed: ${result.message}`); + } + + } catch (error) { + await this.log(`❌ Trade execution error: ${error.message}`); + } + } + + getStatus() { + const uptime = Math.floor((Date.now() - this.stats.startTime.getTime()) / 1000); + return { + isRunning: this.isRunning, + config: this.config, + stats: { + ...this.stats, + uptime: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`, + nextCycle: new Date(Date.now() + (this.config.intervalMinutes * 60 * 1000)) + } + }; + } +} + +// Start automation +const automation = new SimpleAutomation(); + +// Handle graceful shutdown +process.on('SIGTERM', async () => { + await automation.log('πŸ›‘ Received SIGTERM, shutting down...'); + process.exit(0); +}); + +process.on('SIGINT', async () => { + await automation.log('πŸ›‘ Received SIGINT, shutting down...'); + process.exit(0); +}); + +// Start the automation +automation.start().catch(async (error) => { + await automation.log(`❌ Startup error: ${error.message}`); + process.exit(1); +}); + +// Keep process alive +console.log('πŸ€– Simple 24/7 Automation Service'); +console.log('βœ… Running in background - press Ctrl+C to stop'); +console.log('πŸ“ Logs: /tmp/automation-simple.log'); + +// Prevent the process from exiting +process.stdin.resume(); diff --git a/start-24-7-automation.js b/start-24-7-automation.js new file mode 100644 index 0000000..ac4d4f9 --- /dev/null +++ b/start-24-7-automation.js @@ -0,0 +1,263 @@ +#!/usr/bin/env node + +/** + * 24/7 Server-Side Automation with AI Learning + * Runs continuously in Docker container without browser dependency + */ + +const fs = require('fs').promises; +const path = require('path'); + +class Server24x7Automation { + constructor() { + this.isRunning = false; + this.config = { + mode: 'SIMULATION', // Safe paper trading + symbol: 'SOLUSD', + timeframe: '60', // 1 hour + intervalMinutes: 60, // Run every 60 minutes + autoExecuteThreshold: 60, // Execute trades with β‰₯60% confidence + maxDailyTrades: 10, + tradingAmount: 100, + learningEnabled: true + }; + this.stats = { + startTime: null, + totalCycles: 0, + totalTrades: 0, + successfulTrades: 0, + lastAnalysis: null, + lastTrade: null + }; + this.intervalId = null; + } + + async log(message) { + const timestamp = new Date().toISOString(); + const logEntry = `[${timestamp}] ${message}`; + console.log(logEntry); + + // Also save to log file + try { + await fs.appendFile('/tmp/24x7-automation.log', logEntry + '\n'); + } catch (error) { + // Ignore file errors + } + } + + async start() { + if (this.isRunning) { + await this.log('⚠️ Automation already running'); + return { success: false, message: 'Already running' }; + } + + await this.log('πŸš€ Starting 24/7 Server Automation...'); + await this.log(`πŸ“Š Config: ${this.config.symbol} ${this.config.timeframe}m, every ${this.config.intervalMinutes} minutes`); + await this.log(`🎯 Auto-execute threshold: β‰₯${this.config.autoExecuteThreshold}% confidence`); + + this.isRunning = true; + this.stats.startTime = new Date(); + + // Run first cycle immediately + await this.runAnalysisCycle(); + + // Schedule recurring cycles + this.intervalId = setInterval(async () => { + try { + await this.runAnalysisCycle(); + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + } + }, this.config.intervalMinutes * 60 * 1000); + + await this.log('βœ… 24/7 Automation started successfully'); + return { success: true, message: '24/7 automation started' }; + } + + async stop() { + if (!this.isRunning) { + await this.log('⚠️ Automation not running'); + return { success: false, message: 'Not running' }; + } + + this.isRunning = false; + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = null; + } + + await this.log('πŸ›‘ 24/7 Automation stopped'); + return { success: true, message: 'Automation stopped' }; + } + + async runAnalysisCycle() { + this.stats.totalCycles++; + await this.log(`πŸ”„ Starting analysis cycle #${this.stats.totalCycles}`); + + try { + // 1. Get AI analysis + const analysis = await this.getAIAnalysis(); + if (!analysis) { + await this.log('❌ Failed to get AI analysis'); + return; + } + + this.stats.lastAnalysis = new Date(); + await this.log(`πŸ“Š Analysis: ${analysis.recommendation} (${analysis.confidence}% confidence)`); + + // 2. Check if we should auto-execute + if (analysis.confidence >= this.config.autoExecuteThreshold) { + await this.log(`🎯 Confidence ${analysis.confidence}% β‰₯ threshold ${this.config.autoExecuteThreshold}% - executing trade`); + + const tradeResult = await this.executeAutoTrade(analysis); + if (tradeResult.success) { + this.stats.totalTrades++; + this.stats.lastTrade = new Date(); + await this.log(`βœ… Auto-trade executed: ${tradeResult.message}`); + } else { + await this.log(`❌ Auto-trade failed: ${tradeResult.message}`); + } + } else { + await this.log(`⏸️ Confidence ${analysis.confidence}% < threshold ${this.config.autoExecuteThreshold}% - no trade`); + } + + } catch (error) { + await this.log(`❌ Analysis cycle error: ${error.message}`); + } + } + + async getAIAnalysis() { + try { + // Call the same API that the browser version uses + const response = await fetch(`http://localhost:9001/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`); + + if (!response.ok) { + throw new Error(`API responded with ${response.status}`); + } + + const data = await response.json(); + + if (data.success && data.data && data.data.analysis) { + return { + recommendation: data.data.analysis.recommendation, + confidence: data.data.analysis.confidence, + reasoning: data.data.analysis.reasoning, + entry: data.data.analysis.entry, + stopLoss: data.data.analysis.stopLoss, + takeProfit: data.data.analysis.takeProfit + }; + } + + return null; + } catch (error) { + await this.log(`❌ AI Analysis API error: ${error.message}`); + return null; + } + } + + async executeAutoTrade(analysis) { + try { + // Create paper trade using the same API + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, // BUY or SELL + amount: this.config.tradingAmount, + entry: analysis.entry, + stopLoss: analysis.stopLoss, + takeProfit: analysis.takeProfit, + confidence: analysis.confidence, + reasoning: analysis.reasoning, + source: '24x7_automation' + }; + + const response = await fetch('http://localhost:9001/api/safe-paper-trading/create-trade', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(tradeData) + }); + + if (!response.ok) { + throw new Error(`Trade API responded with ${response.status}`); + } + + const result = await response.json(); + + if (result.success) { + this.stats.successfulTrades++; + return { success: true, message: `Paper trade created: ${result.trade.id}` }; + } else { + return { success: false, message: result.message || 'Trade creation failed' }; + } + + } catch (error) { + return { success: false, message: error.message }; + } + } + + getStatus() { + const uptime = this.stats.startTime ? Math.floor((Date.now() - this.stats.startTime.getTime()) / 1000) : 0; + const successRate = this.stats.totalTrades > 0 ? Math.round((this.stats.successfulTrades / this.stats.totalTrades) * 100) : 0; + + return { + isRunning: this.isRunning, + config: this.config, + stats: { + ...this.stats, + uptime: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`, + successRate: `${successRate}%`, + nextCycle: this.intervalId ? new Date(Date.now() + (this.config.intervalMinutes * 60 * 1000)) : null + } + }; + } +} + +// Create singleton instance +const automation24x7 = new Server24x7Automation(); + +// Export for API use +if (typeof module !== 'undefined') { + module.exports = { automation24x7 }; +} + +// CLI interface +if (require.main === module) { + const command = process.argv[2]; + + switch (command) { + case 'start': + automation24x7.start().then(result => { + console.log(result); + if (!result.success) process.exit(1); + }); + break; + + case 'stop': + automation24x7.stop().then(result => { + console.log(result); + process.exit(0); + }); + break; + + case 'status': + console.log(JSON.stringify(automation24x7.getStatus(), null, 2)); + break; + + default: + console.log(` +πŸ€– 24/7 Server Automation + +Usage: + node start-24-7-automation.js start # Start automation + node start-24-7-automation.js stop # Stop automation + node start-24-7-automation.js status # Check status + +Features: + βœ… Runs without browser dependency + βœ… Safe paper trading mode + βœ… AI analysis every 60 minutes + βœ… Auto-execute trades β‰₯60% confidence + βœ… Integrates with existing learning system + βœ… Logs all activity + `); + } +} diff --git a/working-24x7.js b/working-24x7.js new file mode 100644 index 0000000..99d1179 --- /dev/null +++ b/working-24x7.js @@ -0,0 +1,102 @@ +#!/usr/bin/env node + +/** + * Working 24/7 Automation - No stdin issues + */ + +const { exec } = require('child_process'); +const { promisify } = require('util'); +const execAsync = promisify(exec); +const fs = require('fs').promises; + +class WorkingAutomation { + constructor() { + this.config = { + symbol: 'SOLUSD', + timeframe: '60', + intervalMinutes: 60, + autoExecuteThreshold: 60 + }; + this.stats = { + startTime: new Date(), + totalCycles: 0, + totalTrades: 0 + }; + } + + async log(message) { + const timestamp = new Date().toISOString(); + const logEntry = `[${timestamp}] ${message}`; + console.log(logEntry); + } + + async runCycle() { + this.stats.totalCycles++; + await this.log(`πŸ”„ Analysis cycle #${this.stats.totalCycles}`); + + try { + // Get current analysis + const { stdout } = await execAsync(`curl -s "http://localhost:9001/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}"`); + + const data = JSON.parse(stdout); + + if (data.success && data.data && data.data.analysis) { + const analysis = data.data.analysis; + await this.log(`πŸ“Š ${analysis.recommendation} (${analysis.confidence}% confidence)`); + + if (analysis.confidence >= this.config.autoExecuteThreshold) { + await this.log(`🎯 AUTO-EXECUTING: ${analysis.confidence}% β‰₯ ${this.config.autoExecuteThreshold}%`); + + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, + amount: 100, + entry: analysis.entry, + stopLoss: analysis.stopLoss, + takeProfit: analysis.takeProfit, + confidence: analysis.confidence, + reasoning: analysis.reasoning, + source: 'automation_24x7' + }; + + // Create paper trade + const curlData = JSON.stringify(tradeData).replace(/"/g, '\\"'); + const { stdout: tradeResult } = await execAsync(`curl -s -X POST http://localhost:9001/api/safe-paper-trading/create-trade -H "Content-Type: application/json" -d "${curlData}"`); + + const result = JSON.parse(tradeResult); + + if (result.success) { + this.stats.totalTrades++; + await this.log(`βœ… TRADE CREATED: ${result.trade.id}`); + } else { + await this.log(`❌ TRADE FAILED: ${result.message}`); + } + } else { + await this.log(`⏸️ NO TRADE: ${analysis.confidence}% < ${this.config.autoExecuteThreshold}%`); + } + } else { + await this.log('❌ No analysis data available'); + } + + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + } + } + + async start() { + await this.log('πŸš€ 24/7 AUTOMATION STARTED'); + await this.log(`πŸ“Š ${this.config.symbol} every ${this.config.intervalMinutes}m, threshold β‰₯${this.config.autoExecuteThreshold}%`); + + // Run first cycle + await this.runCycle(); + + // Schedule recurring cycles + setInterval(() => this.runCycle().catch(console.error), this.config.intervalMinutes * 60 * 1000); + + await this.log(`⏰ Next cycle: ${new Date(Date.now() + this.config.intervalMinutes * 60 * 1000).toLocaleTimeString()}`); + } +} + +// Create and start automation +const automation = new WorkingAutomation(); +automation.start();