feat: implement 24/7 server-side automation with AI learning integration
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.
This commit is contained in:
169
simple-24x7-automation.js
Normal file
169
simple-24x7-automation.js
Normal file
@@ -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();
|
||||
Reference in New Issue
Block a user