From 284e1c8b8c2d6695ccf9b6bcb9ffcec41abe77c2 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Thu, 7 Aug 2025 16:55:41 +0200 Subject: [PATCH] feat: fix Safe Paper Trading display formatting and API sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed field mapping between API and frontend (amountβ†’positionSize, entryβ†’entryPrice, createdAtβ†’timestamp) - Updated API sync function to properly convert API trade format to frontend format - Resolved display issues: 'Invalid Date', missing entry price, missing trade size - Added trade monitoring system and automation improvements - Enhanced automation with simple-automation.js for reliable 24/7 operation - Working automation now detecting 85% confidence BUY signals and executing trades --- .../safe-paper-trading/create-trade/route.js | 5 + app/api/safe-paper-trading/trades/route.js | 34 +++ app/safe-paper-trading/page.js | 62 +++++ automation.log | 5 + enhanced-global-automation.js | 25 +- host-automation.js | 233 ++++++++++++++++++ lib/paper-trades-storage.js | 43 ++++ robust-automation.js | 188 ++++++++++++++ simple-automation.js | 156 ++++++++++++ simple-automation.log | 11 + trade-monitor.js | 41 +++ trade-monitor.log | 68 +++++ trading_alerts.log | 41 +++ 13 files changed, 901 insertions(+), 11 deletions(-) create mode 100644 app/api/safe-paper-trading/trades/route.js create mode 100644 automation.log create mode 100644 host-automation.js create mode 100644 lib/paper-trades-storage.js create mode 100644 robust-automation.js create mode 100644 simple-automation.js create mode 100644 simple-automation.log create mode 100644 trade-monitor.js create mode 100644 trade-monitor.log create mode 100644 trading_alerts.log diff --git a/app/api/safe-paper-trading/create-trade/route.js b/app/api/safe-paper-trading/create-trade/route.js index f03b819..e7ec385 100644 --- a/app/api/safe-paper-trading/create-trade/route.js +++ b/app/api/safe-paper-trading/create-trade/route.js @@ -4,6 +4,11 @@ import { NextResponse } from 'next/server' let paperTrades = [] let tradeIdCounter = 1 +// Export function to get trades (for other API routes) +export function getAllPaperTrades() { + return paperTrades +} + export async function POST(request) { try { const tradeData = await request.json() diff --git a/app/api/safe-paper-trading/trades/route.js b/app/api/safe-paper-trading/trades/route.js new file mode 100644 index 0000000..72cc0ba --- /dev/null +++ b/app/api/safe-paper-trading/trades/route.js @@ -0,0 +1,34 @@ +import { NextResponse } from 'next/server' +import { getAllPaperTrades } from '../create-trade/route.js' + +export async function GET() { + try { + const trades = getAllPaperTrades() + + // Calculate stats + const totalTrades = trades.length + const totalValue = trades.reduce((sum, trade) => { + return sum + (trade.side === 'BUY' ? -trade.amount : trade.amount) + trade.pnl + }, 0) + + const buyTrades = trades.filter(t => t.side === 'BUY').length + const sellTrades = trades.filter(t => t.side === 'SELL').length + + return NextResponse.json({ + success: true, + trades: trades, + totalTrades, + totalValue, + buyTrades, + sellTrades, + timestamp: new Date().toISOString() + }) + } catch (error) { + console.error('Error fetching paper trades:', error) + return NextResponse.json({ + success: false, + message: 'Failed to fetch paper trades', + error: error.message + }, { status: 500 }) + } +} diff --git a/app/safe-paper-trading/page.js b/app/safe-paper-trading/page.js index 7926888..de8f0b2 100644 --- a/app/safe-paper-trading/page.js +++ b/app/safe-paper-trading/page.js @@ -440,6 +440,68 @@ export default function SafePaperTradingPage() { console.log('πŸ“‚ Restored paper balance from localStorage') } + // Sync with API trades (from automation) + const syncApiTrades = async () => { + try { + console.log('πŸ”„ Syncing trades from API...') + const response = await fetch('/api/safe-paper-trading/trades') + if (response.ok) { + const data = await response.json() + if (data.success && data.trades.length > 0) { + console.log(`πŸ“ˆ Found ${data.trades.length} API trades, syncing with localStorage...`) + + // Get existing localStorage trades + const existingTrades = JSON.parse(localStorage.getItem('safePaperTrading_paperTrades') || '[]') + + // Convert API trades to frontend format and filter out existing ones + const existingIds = new Set(existingTrades.map(t => t.id)) + const newTrades = data.trades + .filter(t => !existingIds.has(t.id)) + .map(apiTrade => ({ + id: apiTrade.id, + symbol: apiTrade.symbol, + side: apiTrade.side, + positionSize: apiTrade.amount, // Map amount to positionSize + entryPrice: apiTrade.entry, // Map entry to entryPrice + confidence: apiTrade.confidence, + reasoning: apiTrade.reasoning, + source: apiTrade.source, + status: apiTrade.status, + timestamp: apiTrade.createdAt, // Map createdAt to timestamp + pnl: apiTrade.pnl || 0, + fees: apiTrade.fees || 0 + })) + + if (newTrades.length > 0) { + const combinedTrades = [...existingTrades, ...newTrades] + setPaperTrades(combinedTrades) + localStorage.setItem('safePaperTrading_paperTrades', JSON.stringify(combinedTrades)) + + // Update balance based on new trades + const additionalValue = newTrades.reduce((sum, trade) => { + return sum + (trade.side === 'BUY' ? -trade.positionSize : trade.positionSize) + }, 0) + + const newBalance = paperBalance + additionalValue + setPaperBalance(newBalance) + localStorage.setItem('safePaperTrading_paperBalance', newBalance.toString()) + + console.log(`βœ… Synced ${newTrades.length} new trades from API`) + } else { + console.log('πŸ“Š All API trades already in localStorage') + } + } else { + console.log('πŸ“Š No API trades found') + } + } + } catch (error) { + console.log('❌ Failed to sync API trades:', error.message) + } + } + + // Sync API trades after loading localStorage + syncApiTrades() + // Fetch AI learning status fetchLearningStatus() diff --git a/automation.log b/automation.log new file mode 100644 index 0000000..53da5f3 --- /dev/null +++ b/automation.log @@ -0,0 +1,5 @@ +nohup: ignoring input +[Thu Aug 7 16:00:18 CEST 2025] Running automation cycle... +πŸš€ Executing BUY trade at 85% confidence (entry: 171.8) +βœ… Trade executed successfully: PAPER_1754575319443_2 +πŸ’€ Sleeping for 1 hour... diff --git a/enhanced-global-automation.js b/enhanced-global-automation.js index 59555a0..f436dda 100644 --- a/enhanced-global-automation.js +++ b/enhanced-global-automation.js @@ -5,10 +5,6 @@ * Integrates Fear & Greed Index and macro indicators for better trading decisions */ -const { exec } = require('child_process'); -const { promisify } = require('util'); -const execAsync = promisify(exec); - // Import M2 Money Supply indicator const M2MoneySupplyIndicator = require('./m2-money-supply-indicator'); @@ -19,6 +15,8 @@ class EnhancedGlobalAutomation { timeframe: '60', intervalMinutes: 60, autoExecuteThreshold: 60, + // API endpoint for container environment + apiHost: '192.168.0.1:9001', // Enhanced with sentiment-based adjustments sentimentThresholds: { extremeFear: 75, // Lower threshold during extreme fear (more aggressive) @@ -125,8 +123,8 @@ class EnhancedGlobalAutomation { async estimateFearGreedFromPriceAction() { try { // Get recent analysis to estimate sentiment - 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); + const response = await fetch(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`); + const data = await response.json(); if (data.success && data.data && data.data.analysis) { const analysis = data.data.analysis; @@ -258,8 +256,8 @@ class EnhancedGlobalAutomation { await this.updateMarketSentiment(); // Get current technical 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); + const response = await fetch(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`); + const data = await response.json(); if (data.success && data.data && data.data.analysis) { const analysis = data.data.analysis; @@ -322,10 +320,15 @@ class EnhancedGlobalAutomation { }; // Create enhanced 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 response = await fetch(`http://${this.config.apiHost}/api/safe-paper-trading/create-trade`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(tradeData) + }); - const result = JSON.parse(tradeResult); + const result = await response.json(); if (result.success) { this.stats.totalTrades++; diff --git a/host-automation.js b/host-automation.js new file mode 100644 index 0000000..4ec4544 --- /dev/null +++ b/host-automation.js @@ -0,0 +1,233 @@ +#!/usr/bin/env node + +/** + * Host-based 24/7 Trading Automation + * Runs on the host system, not inside the container + */ + +const https = require('https'); +const http = require('http'); + +class HostAutomation { + constructor() { + this.config = { + symbol: 'SOLUSD', + timeframe: '60', + intervalMinutes: 60, + autoExecuteThreshold: 60, + apiHost: 'localhost:9001', + requestTimeout: 30000 + }; + + this.stats = { + startTime: new Date(), + totalCycles: 0, + totalTrades: 0, + successfulCycles: 0, + failedCycles: 0, + lastSignal: null, + lastTrade: null + }; + } + + async log(message) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] ${message}`); + } + + async makeRequest(url, options = {}) { + return new Promise((resolve, reject) => { + const isHttps = url.startsWith('https'); + const client = isHttps ? https : http; + + const requestOptions = { + timeout: this.config.requestTimeout, + ...options + }; + + const req = client.request(url, requestOptions, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => { + try { + const parsed = JSON.parse(data); + resolve(parsed); + } catch (e) { + resolve({ success: false, error: 'Invalid JSON response' }); + } + }); + }); + + req.on('error', (error) => { + this.log(`❌ Request error: ${error.message}`); + resolve(null); + }); + + req.on('timeout', () => { + this.log(`⏱️ Request timeout: ${url}`); + req.destroy(); + resolve(null); + }); + + if (options.body) { + req.write(options.body); + } + + req.end(); + }); + } + + async makePostRequest(url, data) { + return new Promise((resolve, reject) => { + const postData = JSON.stringify(data); + + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(postData) + }, + timeout: this.config.requestTimeout + }; + + const req = http.request(url, options, (res) => { + let responseData = ''; + res.on('data', chunk => responseData += chunk); + res.on('end', () => { + try { + const parsed = JSON.parse(responseData); + resolve(parsed); + } catch (e) { + resolve({ success: false, error: 'Invalid JSON response' }); + } + }); + }); + + req.on('error', (error) => { + this.log(`❌ POST request error: ${error.message}`); + resolve(null); + }); + + req.on('timeout', () => { + this.log(`⏱️ POST request timeout: ${url}`); + req.destroy(); + resolve(null); + }); + + req.write(postData); + req.end(); + }); + } + + async runAnalysisCycle() { + this.stats.totalCycles++; + await this.log(`πŸ”„ Analysis cycle #${this.stats.totalCycles}`); + + try { + await this.log(`πŸ“‘ Fetching analysis from API...`); + const data = await this.makeRequest(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`); + + if (data && data.success && data.data && data.data.analysis) { + const analysis = data.data.analysis; + this.stats.lastSignal = { + time: new Date(), + recommendation: analysis.recommendation, + confidence: analysis.confidence + }; + + await this.log(`πŸ“Š Signal: ${analysis.recommendation} (${analysis.confidence}% confidence)`); + + if (analysis.confidence >= this.config.autoExecuteThreshold && + (analysis.recommendation === 'BUY' || analysis.recommendation === 'SELL')) { + + await this.log(`πŸš€ Executing ${analysis.recommendation} trade with ${analysis.confidence}% confidence`); + + const trade = await this.executeTrade(analysis); + if (trade && trade.success) { + this.stats.totalTrades++; + this.stats.lastTrade = { + time: new Date(), + type: analysis.recommendation, + confidence: analysis.confidence, + price: trade.trade?.entry || analysis.entry?.price || 'unknown' + }; + await this.log(`βœ… Trade executed: ${analysis.recommendation} at ${trade.trade?.entry || analysis.entry?.price}`); + } else { + await this.log(`❌ Trade execution failed`); + } + } else { + await this.log(`⏸️ Confidence ${analysis.confidence}% below threshold ${this.config.autoExecuteThreshold}% - holding`); + } + + this.stats.successfulCycles++; + } else { + await this.log(`❌ No valid analysis data received`); + this.stats.failedCycles++; + } + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + this.stats.failedCycles++; + } + + await this.printStats(); + + const nextCycle = new Date(Date.now() + this.config.intervalMinutes * 60 * 1000); + await this.log(`⏰ Next cycle: ${nextCycle.toLocaleTimeString()}`); + + setTimeout(() => this.runAnalysisCycle(), this.config.intervalMinutes * 60 * 1000); + } + + async executeTrade(analysis) { + try { + await this.log(`πŸ’± Preparing trade data...`); + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, + amount: 100, + entry: analysis.entry?.price || 150, + confidence: analysis.confidence, + reasoning: analysis.reasoning || 'Automated trade', + source: 'host_automation_24x7' + }; + + await this.log(`πŸ“€ Sending trade request...`); + const result = await this.makePostRequest(`http://${this.config.apiHost}/api/safe-paper-trading/create-trade`, tradeData); + + return result; + } catch (error) { + await this.log(`❌ Trade execution error: ${error.message}`); + return null; + } + } + + async printStats() { + const uptime = Math.floor((Date.now() - this.stats.startTime) / 1000 / 60); + const successRate = this.stats.totalCycles > 0 ? Math.round((this.stats.successfulCycles / this.stats.totalCycles) * 100) : 0; + + await this.log(`πŸ“ˆ Stats: ${this.stats.totalCycles} cycles (${successRate}% success), ${this.stats.totalTrades} trades, ${uptime}m uptime`); + + if (this.stats.lastSignal) { + const signalAge = Math.floor((Date.now() - this.stats.lastSignal.time) / 1000 / 60); + await this.log(`🎯 Last signal: ${this.stats.lastSignal.recommendation} (${this.stats.lastSignal.confidence}%) ${signalAge}m ago`); + } + + if (this.stats.lastTrade) { + const tradeAge = Math.floor((Date.now() - this.stats.lastTrade.time) / 1000 / 60); + await this.log(`πŸ’° Last trade: ${this.stats.lastTrade.type} at ${this.stats.lastTrade.price} ${tradeAge}m ago`); + } + } + + async start() { + await this.log('πŸš€ Host-based 24/7 Trading Automation Started'); + await this.log(`πŸ“Š Config: ${this.config.symbol} every ${this.config.intervalMinutes}m, threshold: ${this.config.autoExecuteThreshold}%`); + await this.log(`🌐 API Host: ${this.config.apiHost}`); + + await this.runAnalysisCycle(); + } +} + +const automation = new HostAutomation(); +automation.start().catch(error => { + console.error('πŸ’₯ Automation failed to start:', error); + process.exit(1); +}); diff --git a/lib/paper-trades-storage.js b/lib/paper-trades-storage.js new file mode 100644 index 0000000..07bfba4 --- /dev/null +++ b/lib/paper-trades-storage.js @@ -0,0 +1,43 @@ +// Shared storage for paper trades +// In production, this should be replaced with a proper database + +let paperTrades = [] +let tradeIdCounter = 1 + +export function addTrade(trade) { + const newTrade = { + ...trade, + id: `PAPER_${Date.now()}_${tradeIdCounter++}`, + status: 'OPEN', + createdAt: new Date().toISOString(), + pnl: 0, + fees: 0 + } + + paperTrades.push(newTrade) + console.log(`πŸ“„ Paper trade stored: ${newTrade.id} - ${newTrade.side} ${newTrade.symbol} at $${newTrade.entry}`) + + return newTrade +} + +export function getAllTrades() { + return paperTrades +} + +export function getTradeStats() { + const totalTrades = paperTrades.length + const totalValue = paperTrades.reduce((sum, trade) => { + return sum + (trade.side === 'BUY' ? -trade.amount : trade.amount) + trade.pnl + }, 0) + + const buyTrades = paperTrades.filter(t => t.side === 'BUY').length + const sellTrades = paperTrades.filter(t => t.side === 'SELL').length + + return { + totalTrades, + totalValue, + buyTrades, + sellTrades, + paperTrades + } +} diff --git a/robust-automation.js b/robust-automation.js new file mode 100644 index 0000000..2123ede --- /dev/null +++ b/robust-automation.js @@ -0,0 +1,188 @@ +#!/usr/bin/env node + +/** + * Robust 24/7 Trading Automation + * Handles API timeouts and network issues gracefully + */ + +class RobustAutomation { + constructor() { + this.config = { + symbol: 'SOLUSD', + timeframe: '60', + intervalMinutes: 60, + autoExecuteThreshold: 60, + apiHost: '192.168.0.1:9001', + requestTimeout: 30000 // 30 seconds + }; + this.cycleCount = 0; + + this.stats = { + startTime: new Date(), + totalCycles: 0, + totalTrades: 0, + successfulCycles: 0, + failedCycles: 0, + lastSignal: null, + lastTrade: null + }; + } + + async log(message) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] ${message}`); + } + + async makeRequest(url, options = {}) { + try { + // Add timeout to all requests + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), this.config.requestTimeout); + + const response = await fetch(url, { + ...options, + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + if (error.name === 'AbortError') { + await this.log(`⏱️ Request timeout: ${url}`); + } else { + await this.log(`❌ Request failed: ${error.message}`); + } + return null; + } + } + + async runAnalysisCycle() { + this.stats.totalCycles++; + await this.log(`πŸ”„ Analysis cycle #${this.stats.totalCycles}`); + + try { + // Get current technical analysis with timeout + await this.log(`πŸ“‘ Fetching analysis from API...`); + const data = await this.makeRequest(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`); + + if (data && data.success && data.data && data.data.analysis) { + const analysis = data.data.analysis; + this.stats.lastSignal = { + time: new Date(), + recommendation: analysis.recommendation, + confidence: analysis.confidence + }; + + await this.log(`πŸ“Š Signal: ${analysis.recommendation} (${analysis.confidence}% confidence)`); + + // Execute trade if confidence is high enough + if (analysis.confidence >= this.config.autoExecuteThreshold && + (analysis.recommendation === 'BUY' || analysis.recommendation === 'SELL')) { + + await this.log(`πŸš€ Executing ${analysis.recommendation} trade with ${analysis.confidence}% confidence`); + + const trade = await this.executeTrade(analysis); + if (trade && trade.success) { + this.stats.totalTrades++; + this.stats.lastTrade = { + time: new Date(), + type: analysis.recommendation, + confidence: analysis.confidence, + price: trade.trade?.entry || analysis.entry?.price || 'unknown' + }; + await this.log(`βœ… Trade executed successfully: ${analysis.recommendation} at ${trade.trade?.entry || analysis.entry?.price || 'unknown'}`); + } else { + await this.log(`❌ Trade execution failed`); + } + } else { + await this.log(`⏸️ Confidence ${analysis.confidence}% below threshold ${this.config.autoExecuteThreshold}% - holding`); + } + + this.stats.successfulCycles++; + } else { + await this.log(`❌ No valid analysis data received`); + this.stats.failedCycles++; + } + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + this.stats.failedCycles++; + } + + // Print stats every cycle + await this.printStats(); + + // Schedule next cycle + const nextCycle = new Date(Date.now() + this.config.intervalMinutes * 60 * 1000); + await this.log(`⏰ Next cycle: ${nextCycle.toLocaleTimeString()}`); + await this.log(`πŸ”„ Scheduling next cycle in ${this.config.intervalMinutes} minutes...`); + + setTimeout(() => this.runAnalysisCycle(), this.config.intervalMinutes * 60 * 1000); + } + + async executeTrade(analysis) { + try { + await this.log(`πŸ’± Preparing trade data...`); + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, + amount: 100, // $100 paper trade + entry: analysis.entry?.price || 150, // Use analysis entry price or fallback + confidence: analysis.confidence, + reasoning: analysis.reasoning || 'Automated trade from analysis', + source: 'robust_automation_24x7' + }; + + await this.log(`πŸ“€ Sending trade request...`); + const result = await this.makeRequest(`http://${this.config.apiHost}/api/safe-paper-trading/create-trade`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(tradeData) + }); + + return result; + } catch (error) { + await this.log(`❌ Trade execution error: ${error.message}`); + return null; + } + } + + async printStats() { + const uptime = Math.floor((Date.now() - this.stats.startTime) / 1000 / 60); // minutes + const successRate = this.stats.totalCycles > 0 ? Math.round((this.stats.successfulCycles / this.stats.totalCycles) * 100) : 0; + + await this.log(`πŸ“ˆ Stats: ${this.stats.totalCycles} cycles (${successRate}% success), ${this.stats.totalTrades} trades, ${uptime}m uptime`); + + if (this.stats.lastSignal) { + const signalAge = Math.floor((Date.now() - this.stats.lastSignal.time) / 1000 / 60); + await this.log(`🎯 Last signal: ${this.stats.lastSignal.recommendation} (${this.stats.lastSignal.confidence}%) ${signalAge}m ago`); + } + + if (this.stats.lastTrade) { + const tradeAge = Math.floor((Date.now() - this.stats.lastTrade.time) / 1000 / 60); + await this.log(`πŸ’° Last trade: ${this.stats.lastTrade.type} at ${this.stats.lastTrade.price} ${tradeAge}m ago`); + } + } + + async start() { + await this.log('πŸš€ Robust 24/7 Trading Automation Started'); + await this.log(`πŸ“Š Config: ${this.config.symbol} every ${this.config.intervalMinutes}m, threshold: ${this.config.autoExecuteThreshold}%`); + await this.log(`🌐 API Host: ${this.config.apiHost}, Timeout: ${this.config.requestTimeout}ms`); + + // Start first cycle + await this.runAnalysisCycle(); + } +} + +// Start the automation +const automation = new RobustAutomation(); +automation.start().catch(error => { + console.error('πŸ’₯ Automation failed to start:', error); + process.exit(1); +}); diff --git a/simple-automation.js b/simple-automation.js new file mode 100644 index 0000000..dcdbcad --- /dev/null +++ b/simple-automation.js @@ -0,0 +1,156 @@ +#!/usr/bin/env node + +/** + * Simple 24/7 Trading Automation + * Focuses on core trading without complex sentiment analysis + */ + +class SimpleAutomation { + constructor() { + this.config = { + symbol: 'SOLUSD', + timeframe: '60', + intervalMinutes: 60, + autoExecuteThreshold: 60, + apiHost: '192.168.0.1:9001' + }; + this.cycleCount = 0; + + this.stats = { + startTime: new Date(), + totalCycles: 0, + totalTrades: 0, + lastSignal: null, + lastTrade: null + }; + } + + async log(message) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] ${message}`); + } + + async makeRequest(url, options = {}) { + try { + const response = await fetch(url, options); + return await response.json(); + } catch (error) { + await this.log(`❌ Request failed: ${error.message}`); + return null; + } + } + + async runAnalysisCycle() { + this.stats.totalCycles++; + await this.log(`πŸ”„ Analysis cycle #${this.stats.totalCycles}`); + + try { + // Get current technical analysis + const data = await this.makeRequest(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`); + + if (data && data.success && data.data && data.data.analysis) { + const analysis = data.data.analysis; + this.stats.lastSignal = { + time: new Date(), + recommendation: analysis.recommendation, + confidence: analysis.confidence + }; + + await this.log(`πŸ“Š Signal: ${analysis.recommendation} (${analysis.confidence}% confidence)`); + + // Execute trade if confidence is high enough + if (analysis.confidence >= this.config.autoExecuteThreshold && + (analysis.recommendation === 'BUY' || analysis.recommendation === 'SELL')) { + + await this.log(`πŸš€ Executing ${analysis.recommendation} trade with ${analysis.confidence}% confidence`); + + const trade = await this.executeTrade(analysis); + if (trade && trade.success) { + this.stats.totalTrades++; + this.stats.lastTrade = { + time: new Date(), + type: analysis.recommendation, + confidence: analysis.confidence, + price: trade.trade?.price || 'unknown' + }; + await this.log(`βœ… Trade executed successfully: ${analysis.recommendation} at ${trade.trade?.price || 'unknown'}`); + } else { + await this.log(`❌ Trade execution failed`); + } + } else { + await this.log(`⏸️ Confidence ${analysis.confidence}% below threshold ${this.config.autoExecuteThreshold}% - holding`); + } + } else { + await this.log(`❌ No valid analysis data received`); + } + } catch (error) { + await this.log(`❌ Cycle error: ${error.message}`); + } + + // Schedule next cycle + const nextCycle = new Date(Date.now() + this.config.intervalMinutes * 60 * 1000); + await this.log(`⏰ Next cycle: ${nextCycle.toLocaleTimeString()}`); + + setTimeout(() => this.runAnalysisCycle(), this.config.intervalMinutes * 60 * 1000); + } + + async executeTrade(analysis) { + try { + const tradeData = { + symbol: this.config.symbol, + side: analysis.recommendation, + amount: 100, // $100 paper trade + entry: analysis.currentPrice || 150, // Fallback price + confidence: analysis.confidence, + reasoning: analysis.reasoning, + source: 'simple_automation_24x7' + }; + + const result = await this.makeRequest(`http://${this.config.apiHost}/api/safe-paper-trading/create-trade`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(tradeData) + }); + + return result; + } catch (error) { + await this.log(`❌ Trade execution error: ${error.message}`); + return null; + } + } + + async printStats() { + const uptime = Math.floor((Date.now() - this.stats.startTime) / 1000 / 60); // minutes + await this.log(`πŸ“ˆ Stats: ${this.stats.totalCycles} cycles, ${this.stats.totalTrades} trades, ${uptime}m uptime`); + + if (this.stats.lastSignal) { + const signalAge = Math.floor((Date.now() - this.stats.lastSignal.time) / 1000 / 60); + await this.log(`🎯 Last signal: ${this.stats.lastSignal.recommendation} (${this.stats.lastSignal.confidence}%) ${signalAge}m ago`); + } + + if (this.stats.lastTrade) { + const tradeAge = Math.floor((Date.now() - this.stats.lastTrade.time) / 1000 / 60); + await this.log(`πŸ’° Last trade: ${this.stats.lastTrade.type} at ${this.stats.lastTrade.price} ${tradeAge}m ago`); + } + } + + async start() { + await this.log('πŸš€ Simple 24/7 Trading Automation Started'); + await this.log(`πŸ“Š Config: ${this.config.symbol} every ${this.config.intervalMinutes}m, threshold: ${this.config.autoExecuteThreshold}%`); + + // Print stats every 30 minutes + setInterval(() => this.printStats(), 30 * 60 * 1000); + + // Start first cycle + await this.runAnalysisCycle(); + } +} + +// Start the automation +const automation = new SimpleAutomation(); +automation.start().catch(error => { + console.error('πŸ’₯ Automation failed to start:', error); + process.exit(1); +}); diff --git a/simple-automation.log b/simple-automation.log new file mode 100644 index 0000000..37c651d --- /dev/null +++ b/simple-automation.log @@ -0,0 +1,11 @@ +nohup: ignoring input +[2025-08-07T14:03:02.012Z] πŸš€ Simple 24/7 Trading Automation Started +[2025-08-07T14:03:02.016Z] πŸ“Š Config: SOLUSD every 60m, threshold: 60% +[2025-08-07T14:03:02.016Z] πŸ”„ Analysis cycle #1 +[2025-08-07T14:04:44.103Z] πŸ“Š Signal: BUY (85% confidence) +[2025-08-07T14:04:44.103Z] πŸš€ Executing BUY trade with 85% confidence +[2025-08-07T14:04:44.120Z] βœ… Trade executed successfully: BUY at unknown +[2025-08-07T14:04:44.166Z] ⏰ Next cycle: 5:04:44 PM +[2025-08-07T14:33:02.116Z] πŸ“ˆ Stats: 1 cycles, 1 trades, 30m uptime +[2025-08-07T14:33:02.116Z] 🎯 Last signal: BUY (85%) 28m ago +[2025-08-07T14:33:02.116Z] πŸ’° Last trade: BUY at unknown 28m ago diff --git a/trade-monitor.js b/trade-monitor.js new file mode 100644 index 0000000..745f3ed --- /dev/null +++ b/trade-monitor.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +// Trade Monitor - Track trade creation and persistence +const fs = require('fs'); + +async function checkTrades() { + try { + const response = await fetch('http://localhost:9001/api/safe-paper-trading/trades'); + const data = await response.json(); + + const timestamp = new Date().toISOString(); + const logEntry = { + timestamp, + totalTrades: data.totalTrades, + trades: data.trades.map(t => ({ + id: t.id, + side: t.side, + source: t.source, + createdAt: t.createdAt + })) + }; + + // Log to file + fs.appendFileSync('trade-monitor.log', JSON.stringify(logEntry) + '\n'); + + console.log(`[${timestamp}] Trades: ${data.totalTrades}`); + if (data.totalTrades > 0) { + data.trades.forEach(trade => { + console.log(` - ${trade.id}: ${trade.side} (${trade.source})`); + }); + } + + } catch (error) { + console.error(`[${new Date().toISOString()}] Error checking trades:`, error.message); + } +} + +// Check every 30 seconds +console.log('πŸ” Trade Monitor Started - Checking every 30 seconds'); +setInterval(checkTrades, 30000); +checkTrades(); // Initial check diff --git a/trade-monitor.log b/trade-monitor.log new file mode 100644 index 0000000..ad77612 --- /dev/null +++ b/trade-monitor.log @@ -0,0 +1,68 @@ +{"timestamp":"2025-08-07T14:13:56.843Z","totalTrades":1,"trades":[{"id":"PAPER_1754575919891_1","side":"BUY","source":"manual_verification","createdAt":"2025-08-07T14:11:59.891Z"}]} +{"timestamp":"2025-08-07T14:14:26.816Z","totalTrades":1,"trades":[{"id":"PAPER_1754575919891_1","side":"BUY","source":"manual_verification","createdAt":"2025-08-07T14:11:59.891Z"}]} +{"timestamp":"2025-08-07T14:14:56.839Z","totalTrades":1,"trades":[{"id":"PAPER_1754575919891_1","side":"BUY","source":"manual_verification","createdAt":"2025-08-07T14:11:59.891Z"}]} +{"timestamp":"2025-08-07T14:15:26.872Z","totalTrades":1,"trades":[{"id":"PAPER_1754575919891_1","side":"BUY","source":"manual_verification","createdAt":"2025-08-07T14:11:59.891Z"}]} +{"timestamp":"2025-08-07T14:15:56.890Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:16:26.924Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:16:56.940Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:17:26.971Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:17:57.003Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:18:27.030Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:18:57.054Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:19:27.081Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:19:57.108Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:20:27.135Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:20:57.165Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:21:27.185Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:21:57.209Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:22:27.238Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:22:57.250Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:23:27.273Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:23:57.300Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:24:27.321Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:24:57.350Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:25:27.376Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:25:57.391Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:26:27.427Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:26:57.452Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:27:27.478Z","totalTrades":1,"trades":[{"id":"PAPER_1754576825487_1","side":"BUY","source":"manual_test","createdAt":"2025-08-07T14:27:05.487Z"}]} +{"timestamp":"2025-08-07T14:27:57.505Z","totalTrades":1,"trades":[{"id":"PAPER_1754576825487_1","side":"BUY","source":"manual_test","createdAt":"2025-08-07T14:27:05.487Z"}]} +{"timestamp":"2025-08-07T14:28:27.993Z","totalTrades":1,"trades":[{"id":"PAPER_1754576825487_1","side":"BUY","source":"manual_test","createdAt":"2025-08-07T14:27:05.487Z"}]} +{"timestamp":"2025-08-07T14:28:57.560Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:29:27.580Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:29:57.581Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:30:27.597Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:30:57.626Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:31:27.653Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:31:57.674Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:32:27.704Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:32:57.904Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:33:27.749Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:33:57.802Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:34:27.835Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:34:57.834Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:35:27.857Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:35:57.894Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:36:27.939Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:36:57.944Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:37:27.969Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:37:57.998Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:38:28.041Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:38:58.049Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:39:28.056Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:39:58.084Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:40:28.109Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:40:58.137Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:41:28.160Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:41:58.182Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:42:28.209Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:42:58.233Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:43:28.247Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:43:58.260Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:44:28.286Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:44:58.303Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:45:28.336Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:45:58.368Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:46:28.386Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:46:58.411Z","totalTrades":0,"trades":[]} +{"timestamp":"2025-08-07T14:47:28.439Z","totalTrades":0,"trades":[]} diff --git a/trading_alerts.log b/trading_alerts.log new file mode 100644 index 0000000..384c5b7 --- /dev/null +++ b/trading_alerts.log @@ -0,0 +1,41 @@ +nohup: ignoring input +[Wed Aug 6 20:59:50 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Wed Aug 6 22:00:42 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Wed Aug 6 23:01:43 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 00:02:47 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 01:03:55 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 02:05:05 CEST 2025] Checking for trading signals... +No strong signal (HOLD at 75%) +[Thu Aug 7 03:06:19 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 04:07:28 CEST 2025] Checking for trading signals... +No strong signal (HOLD at 75%) +[Thu Aug 7 05:08:39 CEST 2025] Checking for trading signals... +No strong signal (HOLD at 75%) +[Thu Aug 7 06:09:53 CEST 2025] Checking for trading signals... +No strong signal (HOLD at 75%) +[Thu Aug 7 07:11:01 CEST 2025] Checking for trading signals... +No strong signal (HOLD at 75%) +[Thu Aug 7 08:12:16 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 09:13:36 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 10:14:59 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 11:16:26 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 12:17:52 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 13:19:20 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 14:20:52 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 15:22:25 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually +[Thu Aug 7 16:24:00 CEST 2025] Checking for trading signals... +SIGNAL: BUY at 85% confidence - Execute trade manually