diff --git a/app/api/automation/start/route.js b/app/api/automation/start/route.js
index 1c05f4c..6083b16 100644
--- a/app/api/automation/start/route.js
+++ b/app/api/automation/start/route.js
@@ -1,18 +1,26 @@
-import { emergencyAutomation } from '@/lib/emergency-automation'
+import { NextResponse } from 'next/server';
+import { simpleAutomation } from '@/lib/simple-automation';
export async function POST(request) {
try {
- const config = await request.json()
- console.log('šØ EMERGENCY: Automation start request received')
+ const config = await request.json();
- const result = await emergencyAutomation.start(config)
+ console.log('š AUTOMATION START: Received config:', JSON.stringify(config, null, 2));
+
+ const result = await simpleAutomation.start(config);
+
+ if (result.success) {
+ return NextResponse.json(result);
+ } else {
+ return NextResponse.json(result, { status: 400 });
+ }
- return Response.json(result)
} catch (error) {
- console.error('Emergency start failed:', error)
- return Response.json({
- success: false,
- message: 'Emergency start failed: ' + error.message
- }, { status: 500 })
+ console.error('Start automation error:', error);
+ return NextResponse.json({
+ success: false,
+ error: 'Internal server error',
+ message: error.message
+ }, { status: 500 });
}
}
diff --git a/app/api/automation/start/route.js.container b/app/api/automation/start/route.js.container
new file mode 100644
index 0000000..bed4c85
--- /dev/null
+++ b/app/api/automation/start/route.js.container
@@ -0,0 +1,71 @@
+import { safeParallelAutomation } from '@/lib/safe-parallel-automation'
+
+export async function POST(request) {
+ try {
+ const config = await request.json()
+
+ console.log('SAFE START: Received config:', JSON.stringify(config, null, 2))
+
+ // Validate timeframes
+ return Response.json({
+ success: false,
+ message: 'At least one timeframe is required'
+ }, { status: 400 })
+ }
+
+ // Detect trading strategy based on timeframes
+ const timeframes = config.selectedTimeframes
+ let strategyType = 'General'
+
+ const isScalping = timeframes.includes('5') || timeframes.includes('15') || timeframes.includes('30')
+ const isDayTrading = timeframes.includes('60') || timeframes.includes('120')
+ const isSwingTrading = timeframes.includes('240') || timeframes.includes('D')
+
+ if (isScalping) {
+ strategyType = 'Scalping'
+ } else if (isDayTrading) {
+ strategyType = 'Day Trading'
+ } else if (isSwingTrading) {
+ strategyType = 'Swing Trading'
+ }
+
+ console.log('STRATEGY: Detected', strategyType, 'strategy')
+ console.log('TIMEFRAMES:', timeframes)
+
+ // Create safe automation config
+ const automationConfig = {
+ symbol: config.symbol || 'SOLUSD',
+ timeframes: timeframes,
+ mode: config.mode || 'SIMULATION',
+ tradingAmount: config.tradingAmount || 1.0,
+ leverage: config.leverage || 1,
+ stopLoss: config.stopLoss || 2.0,
+ takeProfit: config.takeProfit || 3.0,
+ strategyType: strategyType,
+ userId: 'default-user'
+ }
+
+ const result = await safeParallelAutomation.startSafeAutomation(automationConfig)
+
+ if (result.success) {
+ return Response.json({
+ success: true,
+ message: 'Safe ' + strategyType + ' automation started successfully',
+ strategy: strategyType,
+ timeframes: timeframes
+ })
+ } else {
+ return Response.json({
+ success: false,
+ error: result.message || 'Failed to start automation'
+ }, { status: 400 })
+ }
+ } catch (error) {
+ console.error('Start automation error:', error)
+ return Response.json({
+ success: false,
+ error: 'Internal server error',
+ message: error.message
+ }, { status: 500 })
+ }
+}
diff --git a/app/api/automation/status/route.js b/app/api/automation/status/route.js
index 7662978..9ed9034 100644
--- a/app/api/automation/status/route.js
+++ b/app/api/automation/status/route.js
@@ -1,17 +1,15 @@
-import { emergencyAutomation } from '../../../../lib/emergency-automation'
+import { NextResponse } from 'next/server';
+import { simpleAutomation } from '@/lib/simple-automation';
export async function GET() {
try {
- const status = emergencyAutomation.getStatus()
-
- return Response.json({
- success: true,
- status
- })
+ const status = simpleAutomation.getStatus();
+ return NextResponse.json(status);
} catch (error) {
- return Response.json({
- success: false,
- error: error.message
- }, { status: 500 })
+ console.error('Status error:', error);
+ return NextResponse.json({
+ error: 'Failed to get status',
+ message: error.message
+ }, { status: 500 });
}
}
diff --git a/app/api/automation/stop/route.js b/app/api/automation/stop/route.js
index b36905e..5e900bd 100644
--- a/app/api/automation/stop/route.js
+++ b/app/api/automation/stop/route.js
@@ -1,25 +1,26 @@
-import { emergencyAutomation } from '@/lib/emergency-automation'
+import { simpleAutomation } from '@/lib/simple-automation';
export async function POST() {
try {
- console.log('šØ EMERGENCY: Stop request received')
+ console.log('š AUTOMATION STOP: Request received');
- const result = await emergencyAutomation.stop()
+ const result = await simpleAutomation.stop();
- // Also force kill any remaining processes
+ // Additional cleanup
try {
- const { execSync } = require('child_process')
- execSync('pkill -f "chrome|chromium" 2>/dev/null || true')
- console.log('ā
EMERGENCY: Cleanup completed')
+ const { execSync } = require('child_process');
+ execSync('pkill -f "chrome|chromium" 2>/dev/null || true');
+ console.log('ā
Additional cleanup completed');
} catch (cleanupError) {
- console.error('Cleanup error:', cleanupError.message)
+ console.error('Cleanup error:', cleanupError.message);
}
-
- return Response.json(result)
+
+ return Response.json(result);
} catch (error) {
+ console.error('Stop automation error:', error);
return Response.json({
success: false,
message: error.message
- }, { status: 500 })
+ }, { status: 500 });
}
}
diff --git a/app/api/batch-analysis/route.js b/app/api/batch-analysis/route.js
index 9cb3fe6..b79a39a 100644
--- a/app/api/batch-analysis/route.js
+++ b/app/api/batch-analysis/route.js
@@ -3,12 +3,48 @@ import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot'
import { aiAnalysisService } from '../../../lib/ai-analysis'
import { progressTracker } from '../../../lib/progress-tracker'
+import { PrismaClient } from '@prisma/client';
+
+const prisma = new PrismaClient();
+
+// Store analysis results for AI learning
+async function storeAnalysisForLearning(symbol, analysis) {
+ try {
+ console.log('š¾ Storing analysis for AI learning...')
+
+ // Extract market conditions for learning
+ const marketConditions = {
+ marketSentiment: analysis.marketSentiment || 'NEUTRAL',
+ keyLevels: analysis.keyLevels || {},
+ trends: analysis.trends || {},
+ timeframes: ['5m', '15m', '30m'], // Multi-timeframe analysis
+ timestamp: new Date().toISOString()
+ }
+
+ await prisma.aILearningData.create({
+ data: {
+ userId: 'default-user', // Use same default user as ai-learning-status
+ symbol: symbol,
+ timeframe: 'MULTI', // Indicates multi-timeframe batch analysis
+ analysisData: JSON.stringify(analysis),
+ marketConditions: JSON.stringify(marketConditions),
+ confidenceScore: Math.round(analysis.confidence || 50),
+ createdAt: new Date()
+ }
+ })
+
+ console.log(`ā
Analysis stored for learning: ${symbol} - ${analysis.recommendation || 'HOLD'} (${analysis.confidence || 50}% confidence)`)
+ } catch (error) {
+ console.error('ā Failed to store analysis for learning:', error)
+ }
+}
+
export async function POST(request) {
try {
const body = await request.json()
const { symbol, layouts, timeframes, selectedLayouts, analyze = true } = body
- console.log('š Batch analysis request:', { symbol, layouts, timeframes, selectedLayouts })
+ console.log('š Batch analysis request:', { symbol, layouts, timeframes, selectedLayouts, analyze })
// Validate inputs
if (!symbol || !timeframes || !Array.isArray(timeframes) || timeframes.length === 0) {
@@ -170,6 +206,9 @@ export async function POST(request) {
if (analysis) {
console.log('ā
Comprehensive AI analysis completed')
progressTracker.updateStep(sessionId, 'analysis', 'completed', 'AI analysis completed successfully!')
+
+ // Store analysis for learning
+ await storeAnalysisForLearning(symbol, analysis)
} else {
throw new Error('AI analysis returned null')
}
@@ -266,6 +305,8 @@ function getTimeframeLabel(timeframe) {
return timeframes.find(t => t.value === timeframe)?.label || timeframe
}
+
+
export async function GET() {
return NextResponse.json({
message: 'Batch Analysis API - use POST method for multi-timeframe analysis',
diff --git a/app/automation-v2/page.js b/app/automation-v2/page.js
index 6fc99d8..c29de0a 100644
--- a/app/automation-v2/page.js
+++ b/app/automation-v2/page.js
@@ -17,18 +17,15 @@ export default function AutomationPageV2() {
mode: 'SIMULATION',
dexProvider: 'DRIFT',
symbol: 'SOLUSD',
- timeframe: '1h', // Primary timeframe for backwards compatibility
selectedTimeframes: ['60'], // Multi-timeframe support
tradingAmount: 100,
balancePercentage: 50, // Default to 50% of available balance
- // stopLossPercent and takeProfitPercent removed - AI calculates these automatically
})
const [status, setStatus] = useState(null)
const [balance, setBalance] = useState(null)
const [positions, setPositions] = useState([])
const [loading, setLoading] = useState(false)
- const [nextAnalysisCountdown, setNextAnalysisCountdown] = useState(0)
useEffect(() => {
fetchStatus()
@@ -43,51 +40,6 @@ export default function AutomationPageV2() {
return () => clearInterval(interval)
}, [])
- // Timer effect for countdown
- useEffect(() => {
- let countdownInterval = null
-
- if (status?.isActive && status?.nextAnalysisIn > 0) {
- setNextAnalysisCountdown(status.nextAnalysisIn)
-
- countdownInterval = setInterval(() => {
- setNextAnalysisCountdown(prev => {
- if (prev <= 1) {
- // Refresh status when timer reaches 0
- fetchStatus()
- return 0
- }
- return prev - 1
- })
- }, 1000)
- } else {
- setNextAnalysisCountdown(0)
- }
-
- return () => {
- if (countdownInterval) {
- clearInterval(countdownInterval)
- }
- }
- }, [status?.nextAnalysisIn, status?.isActive])
-
- // Helper function to format countdown time
- const formatCountdown = (seconds) => {
- if (seconds <= 0) return 'Analyzing now...'
-
- const hours = Math.floor(seconds / 3600)
- const minutes = Math.floor((seconds % 3600) / 60)
- const secs = seconds % 60
-
- if (hours > 0) {
- return `${hours}h ${minutes}m ${secs}s`
- } else if (minutes > 0) {
- return `${minutes}m ${secs}s`
- } else {
- return `${secs}s`
- }
- }
-
const toggleTimeframe = (timeframe) => {
setConfig(prev => ({
...prev,
@@ -101,9 +53,12 @@ export default function AutomationPageV2() {
try {
const response = await fetch('/api/automation/status')
const data = await response.json()
- console.log('Status fetched:', data) // Debug log
- if (data.success) {
- setStatus(data.status)
+ console.log('Status response:', data) // Debug log
+
+ if (response.ok && !data.error) {
+ setStatus(data) // Status data is returned directly, not wrapped in 'success'
+ } else {
+ console.error('Status API error:', data.error || 'Unknown error')
}
} catch (error) {
console.error('Failed to fetch status:', error)
@@ -135,140 +90,65 @@ export default function AutomationPageV2() {
}
const handleStart = async () => {
- console.log('š Starting OPTIMIZED automation with batch processing!')
+ console.log('š Starting automation...')
setLoading(true)
try {
- // Ensure we have selectedTimeframes before starting
if (config.selectedTimeframes.length === 0) {
- alert('Please select at least one timeframe for analysis')
+ console.error('No timeframes selected')
setLoading(false)
return
}
- console.log('š„ Starting OPTIMIZED automation with config:', {
- ...config,
- selectedTimeframes: config.selectedTimeframes
- })
-
- // š„ USE THE NEW FANCY OPTIMIZED ENDPOINT! š„
- const optimizedConfig = {
- symbol: config.symbol, // FIX: Use config.symbol not config.asset
- timeframes: config.selectedTimeframes,
- layouts: ['ai', 'diy'],
- analyze: true,
- automationMode: true, // Flag to indicate this is automation, not just testing
- mode: config.mode, // Pass the user's trading mode choice
+ const automationConfig = {
+ symbol: config.symbol,
+ selectedTimeframes: config.selectedTimeframes,
+ mode: config.mode,
tradingAmount: config.tradingAmount,
- balancePercentage: config.balancePercentage,
- dexProvider: config.dexProvider
+ leverage: config.leverage,
+ stopLoss: config.stopLoss,
+ takeProfit: config.takeProfit
}
- const startTime = Date.now()
- const response = await fetch('/api/analysis-optimized', {
+ const response = await fetch('/api/automation/start', {
method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(optimizedConfig)
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(automationConfig)
})
- const duration = ((Date.now() - startTime) / 1000).toFixed(1)
const data = await response.json()
if (data.success) {
- console.log(`š OPTIMIZED automation completed in ${duration}s!`)
- console.log(`šø Screenshots: ${data.screenshots?.length || 0}`)
- console.log(`š¤ Analysis: ${data.analysis ? 'Yes' : 'No'}`)
-
- // Show clean success message without performance spam
- const message = data.mode === 'automation'
- ? `š Optimized Automation Started!\n\nā±ļø Duration: ${duration}s\nļæ½ Analysis: ${data.analysis ? `${data.analysis.overallRecommendation} (${data.analysis.confidence}% confidence)` : 'Completed'}\nš° Trade: ${data.trade?.executed ? `${data.trade.direction} executed` : 'No trade executed'}`
- : `ā
Analysis Complete!\n\nā±ļø Duration: ${duration}s\nš Recommendation: ${data.analysis ? `${data.analysis.overallRecommendation} (${data.analysis.confidence}% confidence)` : 'No analysis'}`
-
- alert(message)
-
- fetchStatus() // Refresh to show automation status
+ console.log('ā
Automation started successfully')
+ fetchStatus()
} else {
- alert('Failed to start optimized automation: ' + data.error)
+ console.error('Failed to start automation:', data.error)
}
} catch (error) {
console.error('Failed to start automation:', error)
- alert('Failed to start automation')
} finally {
setLoading(false)
}
}
const handleStop = async () => {
- console.log('Stop button clicked') // Debug log
+ console.log('š Stopping automation...')
setLoading(true)
try {
const response = await fetch('/api/automation/stop', {
- method: 'POST'
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' }
})
+
const data = await response.json()
- console.log('Stop response:', data) // Debug log
+
if (data.success) {
+ console.log('ā
Automation stopped successfully')
fetchStatus()
} else {
- alert('Failed to stop automation: ' + data.error)
+ console.error('Failed to stop automation:', data.error)
}
} catch (error) {
console.error('Failed to stop automation:', error)
- alert('Failed to stop automation')
- } finally {
- setLoading(false)
- }
- }
-
- const handleOptimizedTest = async () => {
- console.log('š Testing optimized analysis...')
- setLoading(true)
- try {
- // Ensure we have selectedTimeframes before testing
- if (config.selectedTimeframes.length === 0) {
- alert('Please select at least one timeframe for optimized analysis test')
- setLoading(false)
- return
- }
-
- const testConfig = {
- symbol: config.symbol, // FIX: Use config.symbol not config.asset
- timeframes: config.selectedTimeframes,
- layouts: ['ai', 'diy'],
- analyze: true
- }
-
- console.log('š¬ Testing with config:', testConfig)
-
- const startTime = Date.now()
- const response = await fetch('/api/analysis-optimized', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(testConfig)
- })
-
- const duration = ((Date.now() - startTime) / 1000).toFixed(1)
- const data = await response.json()
-
- if (data.success) {
- console.log('ā
Optimized analysis completed!')
- console.log(`ā±ļø Duration: ${duration}s`)
- console.log(`šø Screenshots: ${data.screenshots?.length || 0}`)
- console.log(`š¤ Analysis: ${data.analysis ? 'Yes' : 'No'}`)
- console.log(`š Efficiency: ${data.optimization?.efficiency || 'N/A'}`)
-
- // Clean success message without annoying speed metrics
- alert(`ā
Analysis Complete!\n\n${data.analysis ? `š Recommendation: ${data.analysis.overallRecommendation} (${data.analysis.confidence}% confidence)` : 'Analysis completed successfully'}`)
- } else {
- console.error('ā Optimized analysis failed:', data.error)
- alert(`ā Optimized analysis failed: ${data.error}`)
- }
- } catch (error) {
- console.error('Failed to run optimized analysis:', error)
- alert('Failed to run optimized analysis: ' + error.message)
} finally {
setLoading(false)
}
@@ -276,27 +156,11 @@ export default function AutomationPageV2() {
return (
-
- š NEW AUTOMATION V2 - MULTI-TIMEFRAME READY š
-
-
-
-
-
-
ā” NEW: Optimized Multi-Timeframe Analysis
-
70% faster processing ⢠Single AI call ⢠Parallel screenshot capture
-
-
-
-
-
+ {/* Header with Start/Stop */}
-
Automated Trading V2 ā” OPTIMIZED
-
Drift Protocol - Multi-Timeframe Batch Analysis (70% Faster)
+
Automated Trading
+
Multi-Timeframe Analysis
{status?.isActive ? (
@@ -311,10 +175,9 @@ export default function AutomationPageV2() {
)}
@@ -355,7 +218,6 @@ export default function AutomationPageV2() {
-
{/* Symbol and Position Size */}
@@ -490,340 +352,95 @@ export default function AutomationPageV2() {
-
- {/* AI Risk Management Notice */}
-
-
- š§
-
AI-Powered Risk Management
-
-
- Stop loss and take profit levels are automatically calculated by the AI based on:
-
-
- - ⢠Multi-timeframe technical analysis
- - ⢠Market volatility and support/resistance levels
- - ⢠Real-time risk assessment and position sizing
- - ⢠Learning from previous trade outcomes
-
-
-
- ā
Ultra-tight scalping enabled (0.5%+ stop losses proven effective)
-
-
-
- {/* Status Panels */}
+ {/* Status and Info Panel */}
- {/* Account Status */}
+ {/* Status */}
-
-
Account Status
-
-
- {balance ? (
-
-
- Available Balance:
- ${parseFloat(balance.availableBalance).toFixed(2)}
-
-
- Account Value:
- ${parseFloat(balance.accountValue || balance.availableBalance).toFixed(2)}
-
-
- Unrealized P&L:
- 0 ? 'text-green-400' : balance.unrealizedPnl < 0 ? 'text-red-400' : 'text-gray-400'}`}>
- ${parseFloat(balance.unrealizedPnl || 0).toFixed(2)}
-
-
-
- Open Positions:
- {positions.length}
-
+
Bot Status
+
+
+
+ Status:
+
+ {status?.isActive ? 'RUNNING' : 'STOPPED'}
+
- ) : (
-
-
Loading account data...
-
- )}
-
-
- {/* Bot Status */}
-
-
Bot Status
- {status ? (
-
-
- Status:
-
- {status.isActive ? 'ACTIVE' : 'STOPPED'}
-
-
-
- Mode:
-
- {status.mode}
-
-
-
- Protocol:
- DRIFT
-
-
- Symbol:
- {status.symbol}
-
-
- Timeframes:
-
- {status && status.selectedTimeframes ?
- status.selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label || tf).filter(Boolean).join(', ') :
- status && status.timeframe ?
- (timeframes.find(t => t.value === status.timeframe)?.label || status.timeframe) :
- 'N/A'
- }
-
-
-
- ) : (
-
Loading bot status...
- )}
-
-
- {/* Analysis Progress */}
- {status?.analysisProgress && (
-
-
-
Analysis Progress
-
- Session: {status.analysisProgress.sessionId.split('-').pop()}
-
-
-
- {/* Overall Progress */}
-
- Step {status.analysisProgress.currentStep} of {status.analysisProgress.totalSteps}
-
- {Math.round((status.analysisProgress.currentStep / status.analysisProgress.totalSteps) * 100)}%
-
-
-
-
- {/* Timeframe Progress */}
- {status.analysisProgress.timeframeProgress && (
-
-
-
- Analyzing {status.analysisProgress.timeframeProgress.currentTimeframe || 'timeframes'}
-
-
- {status.analysisProgress.timeframeProgress.current}/{status.analysisProgress.timeframeProgress.total}
-
-
+
+ {status?.isActive && (
+ <>
+
+ Symbol:
+ {status.symbol}
- )}
-
- {/* Detailed Steps */}
-
- {status.analysisProgress.steps.map((step, index) => (
-
+
Mode:
+
- {/* Status Icon */}
-
- {step.status === 'active' ? 'ā³' :
- step.status === 'completed' ? 'ā' :
- step.status === 'error' ? 'ā' :
- index + 1}
-
-
- {/* Step Info */}
-
-
- {step.title}
-
-
- {step.details || step.description}
-
-
-
- {/* Duration */}
- {step.duration && (
-
- {(step.duration / 1000).toFixed(1)}s
-
- )}
-
- ))}
-
-
+ {status.mode}
+
+
+
+
+ Timeframes:
+
+ {status.timeframes?.map(tf => timeframes.find(t => t.value === tf)?.label || tf).join(', ')}
+
+
+ >
+ )}
- )}
+
- {/* Analysis Timer */}
- {status?.isActive && !status?.analysisProgress && (
+ {/* Balance */}
+ {balance && (
-
-
Analysis Timer
-
- Cycle #{status.currentCycle || 0}
-
-
+
Account Balance
+
-
-
- {formatCountdown(nextAnalysisCountdown)}
-
-
- {nextAnalysisCountdown > 0 ? 'Next Analysis In' : 'Analysis Starting Soon'}
-
+
+ Available:
+ ${balance.availableBalance}
-
-
0 ?
- `${Math.max(0, 100 - (nextAnalysisCountdown / status.analysisInterval) * 100)}%` :
- '0%'
- }}
- >
+
+
+ Total:
+ ${balance.totalCollateral}
-
- Analysis Interval: {(() => {
- const intervalSec = status?.analysisInterval || 0
- const intervalMin = Math.floor(intervalSec / 60)
-
- // Determine strategy type for display
- if (status?.selectedTimeframes) {
- const timeframes = status.selectedTimeframes
- const isScalping = timeframes.includes('5') || timeframes.includes('3') ||
- (timeframes.length > 1 && timeframes.every(tf => ['1', '3', '5', '15', '30'].includes(tf)))
-
- if (isScalping) {
- return '2m (Scalping Mode)'
- }
-
- const isDayTrading = timeframes.includes('60') || timeframes.includes('120')
- if (isDayTrading) {
- return '5m (Day Trading Mode)'
- }
-
- const isSwingTrading = timeframes.includes('240') || timeframes.includes('D')
- if (isSwingTrading) {
- return '15m (Swing Trading Mode)'
- }
- }
-
- return `${intervalMin}m`
- })()}
+
+
+ Positions:
+ {balance.positions || 0}
)}
- {/* Individual Timeframe Results */}
- {status?.individualTimeframeResults && status.individualTimeframeResults.length > 0 && (
+ {/* Positions */}
+ {positions.length > 0 && (
-
Timeframe Analysis
+
Open Positions
+
- {status.individualTimeframeResults.map((result, index) => (
-
-
-
- {timeframes.find(tf => tf.value === result.timeframe)?.label || result.timeframe}
-
-
- {result.recommendation}
-
-
-
-
- {result.confidence}%
-
-
- confidence
-
-
+ {positions.map((position, index) => (
+
+ {position.symbol}
+
+ {position.side} ${position.size}
+
))}
-
-
- ā
Last Updated: {status.individualTimeframeResults[0]?.timestamp ?
- new Date(status.individualTimeframeResults[0].timestamp).toLocaleTimeString() :
- 'N/A'
- }
-
-
)}
-
- {/* Trading Metrics */}
-
-
Trading Metrics
-
-
-
- ${balance ? parseFloat(balance.accountValue || balance.availableBalance).toFixed(2) : '0.00'}
-
-
Portfolio
-
-
-
- {balance ? parseFloat(balance.leverage || 0).toFixed(1) : '0.0'}%
-
-
Leverage Used
-
-
-
- ${balance ? parseFloat(balance.unrealizedPnl || 0).toFixed(2) : '0.00'}
-
-
Unrealized P&L
-
-
-
- {positions.length}
-
-
Open Positions
-
-
-
diff --git a/lib/safe-parallel-automation-fixed.ts b/lib/safe-parallel-automation-fixed.ts
new file mode 100644
index 0000000..c52d74f
--- /dev/null
+++ b/lib/safe-parallel-automation-fixed.ts
@@ -0,0 +1,326 @@
+class SafeParallelAutomation {
+ private isRunning = false
+ private config: any = null
+ private intervalId: NodeJS.Timeout | null = null
+ private lastAnalysisTime = 0
+
+ // SAFE PARAMETERS - Respect timeframe strategy
+ private readonly MIN_ANALYSIS_INTERVAL = 10 * 60 * 1000 // 10 minutes minimum
+ private readonly MAX_TRADES_PER_HOUR = 2
+ private readonly ANALYSIS_COOLDOWN = 5 * 60 * 1000 // 5 minutes
+
+ private stats = {
+ totalTrades: 0,
+ successfulTrades: 0,
+ winRate: 0,
+ totalPnL: 0,
+ errorCount: 0,
+ lastAnalysis: null as string | null,
+ nextScheduled: null as string | null,
+ nextAnalysisIn: 0,
+ analysisInterval: 0,
+ currentCycle: 0,
+ lastError: null as string | null,
+ strategyType: 'General'
+ }
+
+ async startSafeAutomation(config: any): Promise<{ success: boolean, message?: string }> {
+ try {
+ if (this.isRunning) {
+ return { success: false, message: 'Safe automation already running' }
+ }
+
+ // SAFETY CHECK: Rate limiting
+ const now = Date.now()
+ if (now - this.lastAnalysisTime < this.ANALYSIS_COOLDOWN) {
+ const remaining = Math.ceil((this.ANALYSIS_COOLDOWN - (now - this.lastAnalysisTime)) / 1000)
+ return {
+ success: false,
+ message: 'Safety cooldown: Wait ' + remaining + ' seconds'
+ }
+ }
+
+ // SAFETY CHECK: Validate timeframes
+ if (!config.timeframes || config.timeframes.length === 0) {
+ return { success: false, message: 'At least one timeframe required' }
+ }
+
+ this.config = config
+ this.isRunning = true
+ this.lastAnalysisTime = now
+ this.stats.currentCycle = 0
+ this.stats.strategyType = config.strategyType || 'General'
+
+ console.log('SAFE PARALLEL: Starting for ' + config.symbol)
+ console.log('STRATEGY: ' + this.stats.strategyType)
+ console.log('TIMEFRAMES: [' + config.timeframes.join(', ') + ']')
+ console.log('SAFETY: Max ' + this.MAX_TRADES_PER_HOUR + ' trades/hour')
+
+ // Start with strategy-appropriate interval
+ this.startSafeAnalysisCycle()
+
+ return { success: true, message: 'Safe parallel automation started' }
+ } catch (error) {
+ console.error('Failed to start safe automation:', error)
+ return { success: false, message: 'Failed to start automation' }
+ }
+ }
+
+ private startSafeAnalysisCycle(): void {
+ // Get strategy-appropriate interval but enforce minimum safety
+ const strategyInterval = this.getStrategyInterval(this.config.timeframes, this.config.strategyType)
+ const safeInterval = Math.max(this.MIN_ANALYSIS_INTERVAL, strategyInterval)
+
+ console.log('STRATEGY INTERVAL: ' + (strategyInterval / 1000 / 60) + ' minutes')
+ console.log('SAFE INTERVAL: ' + (safeInterval / 1000 / 60) + ' minutes (enforced minimum)')
+
+ this.stats.analysisInterval = safeInterval
+ this.stats.nextScheduled = new Date(Date.now() + safeInterval).toISOString()
+ this.stats.nextAnalysisIn = safeInterval
+
+ // Set safe interval
+ this.intervalId = setInterval(async () => {
+ if (this.isRunning && this.config) {
+ await this.runSafeAnalysisCycle()
+ }
+ }, safeInterval)
+
+ // First cycle after delay
+ setTimeout(() => {
+ if (this.isRunning && this.config) {
+ this.runSafeAnalysisCycle()
+ }
+ }, 30000)
+ }
+
+ private async runSafeAnalysisCycle(): Promise
{
+ try {
+ console.log('\nSAFE CYCLE ' + (this.stats.currentCycle + 1) + ': ' + this.config.symbol)
+ console.log('STRATEGY: ' + this.stats.strategyType)
+
+ // SAFETY CHECK
+ const canTrade = await this.canExecuteTrade()
+ if (!canTrade.allowed) {
+ console.log('SAFETY BLOCK: ' + canTrade.reason)
+ this.updateNextAnalysisTime()
+ return
+ }
+
+ // PARALLEL ANALYSIS using enhanced screenshot API
+ console.log('PARALLEL: Analyzing ' + this.config.timeframes.length + ' timeframes...')
+
+ const analysisResult = await this.performParallelAnalysis()
+
+ if (!analysisResult || !analysisResult.analysis) {
+ console.log('Analysis failed, skipping')
+ return
+ }
+
+ this.stats.lastAnalysis = new Date().toISOString()
+ console.log('ANALYSIS: ' + analysisResult.analysis.recommendation)
+
+ // CONTROLLED TRADING with strategy-specific logic
+ if (this.shouldExecuteTrade(analysisResult.analysis)) {
+ console.log('EXECUTING: Trade criteria met for ' + this.stats.strategyType)
+ const tradeResult = await this.executeSafeTrade(analysisResult.analysis)
+ if (tradeResult?.success) {
+ this.stats.totalTrades++
+ this.stats.successfulTrades++
+ this.stats.winRate = (this.stats.successfulTrades / this.stats.totalTrades) * 100
+ console.log('TRADE: Executed successfully')
+ }
+ } else {
+ console.log('SIGNAL: Criteria not met for ' + this.stats.strategyType)
+ }
+
+ this.stats.currentCycle++
+
+ } catch (error) {
+ console.error('Error in safe cycle:', error)
+ this.stats.errorCount++
+ this.stats.lastError = error instanceof Error ? error.message : 'Unknown error'
+ } finally {
+ await this.guaranteedCleanup()
+ this.updateNextAnalysisTime()
+ }
+ }
+
+ private async performParallelAnalysis(): Promise {
+ try {
+ // Use the enhanced screenshot API endpoint with all selected timeframes
+ const response = await fetch('http://localhost:3000/api/enhanced-screenshot', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ symbol: this.config.symbol,
+ timeframes: this.config.timeframes,
+ layouts: ['ai', 'diy'],
+ analyze: true
+ })
+ })
+
+ throw new Error('API response: ' + response.status)
+ }
+
+ const result = await response.json()
+
+ if (result.analysis) {
+ console.log('PARALLEL: Confidence ' + result.analysis.confidence + '%')
+ return result
+ }
+
+ return null
+ } catch (error) {
+ console.error('Error in parallel analysis:', error)
+ throw error
+ }
+ }
+
+ private shouldExecuteTrade(analysis: any): boolean {
+ const recommendation = analysis.recommendation.toLowerCase()
+ const confidence = analysis.confidence || 0
+
+ // Strategy-specific confidence requirements
+ let minConfidence = 75 // Default
+
+ if (this.stats.strategyType === 'Scalping') {
+ minConfidence = 80 // Higher confidence for scalping
+ } else if (this.stats.strategyType === 'Day Trading') {
+ minConfidence = 75 // Standard confidence
+ } else if (this.stats.strategyType === 'Swing Trading') {
+ minConfidence = 70 // Slightly lower for swing (longer timeframes)
+ }
+
+ const isHighConfidence = confidence >= minConfidence
+ const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell')
+
+ console.log('SIGNAL: ' + recommendation + ' (' + confidence + '%) - Required: ' + minConfidence + '%')
+
+ return isHighConfidence && isClearDirection
+ }
+
+ private getStrategyInterval(timeframes: string[], strategyType: string): number {
+ // Strategy-based intervals (but minimum will be enforced)
+
+ if (strategyType === 'Scalping') {
+ console.log('SCALPING: Using 2-minute analysis cycle')
+ return 2 * 60 * 1000 // 2 minutes for scalping (will be enforced to 10 min minimum)
+ }
+
+ if (strategyType === 'Day Trading') {
+ console.log('DAY TRADING: Using 5-minute analysis cycle')
+ return 5 * 60 * 1000 // 5 minutes for day trading (will be enforced to 10 min minimum)
+ }
+
+ if (strategyType === 'Swing Trading') {
+ console.log('SWING TRADING: Using 15-minute analysis cycle')
+ return 15 * 60 * 1000 // 15 minutes for swing trading
+ }
+
+ // Fallback based on shortest timeframe
+ const shortest = Math.min(...timeframes.map(tf => {
+ if (tf === 'D') return 1440
+ return parseInt(tf) || 60
+ }))
+
+ if (shortest <= 5) return 2 * 60 * 1000 // 2 min for very short
+ if (shortest <= 15) return 5 * 60 * 1000 // 5 min for short
+ if (shortest <= 60) return 10 * 60 * 1000 // 10 min for medium
+ return 15 * 60 * 1000 // 15 min for long
+ }
+
+ private async executeSafeTrade(analysis: any): Promise {
+ try {
+ console.log('SAFE TRADE: Executing...')
+
+ // Use drift trading API endpoint
+ const response = await fetch('http://localhost:3000/api/trading', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ mode: this.config.mode,
+ symbol: this.config.symbol,
+ amount: this.config.tradingAmount,
+ leverage: this.config.leverage,
+ stopLoss: this.config.stopLoss,
+ takeProfit: this.config.takeProfit,
+ analysis: analysis,
+ strategy: this.stats.strategyType
+ })
+ })
+
+ const result = await response.json()
+ return result
+ } catch (error) {
+ console.error('Error executing trade:', error)
+ return { success: false, error: error.message }
+ }
+ }
+
+ private async canExecuteTrade(): Promise<{ allowed: boolean, reason?: string }> {
+ const now = Date.now()
+
+ if (now - this.lastAnalysisTime < this.ANALYSIS_COOLDOWN) {
+ const remaining = Math.ceil((this.ANALYSIS_COOLDOWN - (now - this.lastAnalysisTime)) / 1000 / 60)
+ return { allowed: false, reason: 'Cooldown: ' + remaining + 'min' }
+ }
+
+ if (this.stats.totalTrades >= this.MAX_TRADES_PER_HOUR) {
+ return { allowed: false, reason: 'Hour limit: ' + this.stats.totalTrades + '/' + this.MAX_TRADES_PER_HOUR }
+ }
+
+ return { allowed: true }
+ }
+
+ private async guaranteedCleanup(): Promise {
+ try {
+ const { execSync } = require('child_process')
+ execSync('pkill -f "chrome|chromium" 2>/dev/null || true')
+ console.log('CLEANUP: Completed')
+ } catch (error) {
+ console.error('CLEANUP: Failed', error)
+ }
+ }
+
+ private updateNextAnalysisTime(): void {
+ const nextTime = Date.now() + this.stats.analysisInterval
+ this.stats.nextScheduled = new Date(nextTime).toISOString()
+ this.stats.nextAnalysisIn = this.stats.analysisInterval
+ }
+
+ async stopSafeAutomation(): Promise<{ success: boolean, message?: string }> {
+ try {
+ this.isRunning = false
+
+ if (this.intervalId) {
+ clearInterval(this.intervalId)
+ this.intervalId = null
+ }
+
+ await this.guaranteedCleanup()
+
+ this.config = null
+ this.stats.nextAnalysisIn = 0
+ this.stats.nextScheduled = null
+
+ console.log('SAFE: Automation stopped')
+ return { success: true, message: 'Safe automation stopped' }
+ } catch (error) {
+ return { success: false, message: 'Stop failed' }
+ }
+ }
+
+ getStatus() {
+ return {
+ isActive: this.isRunning,
+ mode: this.config?.mode || 'SIMULATION',
+ symbol: this.config?.symbol || 'SOLUSD',
+ timeframes: this.config?.timeframes || ['1h'],
+ automationType: 'SAFE_PARALLEL',
+ strategy: this.stats.strategyType,
+ ...this.stats
+ }
+ }
+}
+
+export const safeParallelAutomation = new SafeParallelAutomation()
diff --git a/lib/safe-parallel-automation.ts.broken b/lib/safe-parallel-automation.ts.broken
new file mode 100644
index 0000000..c52d74f
--- /dev/null
+++ b/lib/safe-parallel-automation.ts.broken
@@ -0,0 +1,326 @@
+class SafeParallelAutomation {
+ private isRunning = false
+ private config: any = null
+ private intervalId: NodeJS.Timeout | null = null
+ private lastAnalysisTime = 0
+
+ // SAFE PARAMETERS - Respect timeframe strategy
+ private readonly MIN_ANALYSIS_INTERVAL = 10 * 60 * 1000 // 10 minutes minimum
+ private readonly MAX_TRADES_PER_HOUR = 2
+ private readonly ANALYSIS_COOLDOWN = 5 * 60 * 1000 // 5 minutes
+
+ private stats = {
+ totalTrades: 0,
+ successfulTrades: 0,
+ winRate: 0,
+ totalPnL: 0,
+ errorCount: 0,
+ lastAnalysis: null as string | null,
+ nextScheduled: null as string | null,
+ nextAnalysisIn: 0,
+ analysisInterval: 0,
+ currentCycle: 0,
+ lastError: null as string | null,
+ strategyType: 'General'
+ }
+
+ async startSafeAutomation(config: any): Promise<{ success: boolean, message?: string }> {
+ try {
+ if (this.isRunning) {
+ return { success: false, message: 'Safe automation already running' }
+ }
+
+ // SAFETY CHECK: Rate limiting
+ const now = Date.now()
+ if (now - this.lastAnalysisTime < this.ANALYSIS_COOLDOWN) {
+ const remaining = Math.ceil((this.ANALYSIS_COOLDOWN - (now - this.lastAnalysisTime)) / 1000)
+ return {
+ success: false,
+ message: 'Safety cooldown: Wait ' + remaining + ' seconds'
+ }
+ }
+
+ // SAFETY CHECK: Validate timeframes
+ if (!config.timeframes || config.timeframes.length === 0) {
+ return { success: false, message: 'At least one timeframe required' }
+ }
+
+ this.config = config
+ this.isRunning = true
+ this.lastAnalysisTime = now
+ this.stats.currentCycle = 0
+ this.stats.strategyType = config.strategyType || 'General'
+
+ console.log('SAFE PARALLEL: Starting for ' + config.symbol)
+ console.log('STRATEGY: ' + this.stats.strategyType)
+ console.log('TIMEFRAMES: [' + config.timeframes.join(', ') + ']')
+ console.log('SAFETY: Max ' + this.MAX_TRADES_PER_HOUR + ' trades/hour')
+
+ // Start with strategy-appropriate interval
+ this.startSafeAnalysisCycle()
+
+ return { success: true, message: 'Safe parallel automation started' }
+ } catch (error) {
+ console.error('Failed to start safe automation:', error)
+ return { success: false, message: 'Failed to start automation' }
+ }
+ }
+
+ private startSafeAnalysisCycle(): void {
+ // Get strategy-appropriate interval but enforce minimum safety
+ const strategyInterval = this.getStrategyInterval(this.config.timeframes, this.config.strategyType)
+ const safeInterval = Math.max(this.MIN_ANALYSIS_INTERVAL, strategyInterval)
+
+ console.log('STRATEGY INTERVAL: ' + (strategyInterval / 1000 / 60) + ' minutes')
+ console.log('SAFE INTERVAL: ' + (safeInterval / 1000 / 60) + ' minutes (enforced minimum)')
+
+ this.stats.analysisInterval = safeInterval
+ this.stats.nextScheduled = new Date(Date.now() + safeInterval).toISOString()
+ this.stats.nextAnalysisIn = safeInterval
+
+ // Set safe interval
+ this.intervalId = setInterval(async () => {
+ if (this.isRunning && this.config) {
+ await this.runSafeAnalysisCycle()
+ }
+ }, safeInterval)
+
+ // First cycle after delay
+ setTimeout(() => {
+ if (this.isRunning && this.config) {
+ this.runSafeAnalysisCycle()
+ }
+ }, 30000)
+ }
+
+ private async runSafeAnalysisCycle(): Promise {
+ try {
+ console.log('\nSAFE CYCLE ' + (this.stats.currentCycle + 1) + ': ' + this.config.symbol)
+ console.log('STRATEGY: ' + this.stats.strategyType)
+
+ // SAFETY CHECK
+ const canTrade = await this.canExecuteTrade()
+ if (!canTrade.allowed) {
+ console.log('SAFETY BLOCK: ' + canTrade.reason)
+ this.updateNextAnalysisTime()
+ return
+ }
+
+ // PARALLEL ANALYSIS using enhanced screenshot API
+ console.log('PARALLEL: Analyzing ' + this.config.timeframes.length + ' timeframes...')
+
+ const analysisResult = await this.performParallelAnalysis()
+
+ if (!analysisResult || !analysisResult.analysis) {
+ console.log('Analysis failed, skipping')
+ return
+ }
+
+ this.stats.lastAnalysis = new Date().toISOString()
+ console.log('ANALYSIS: ' + analysisResult.analysis.recommendation)
+
+ // CONTROLLED TRADING with strategy-specific logic
+ if (this.shouldExecuteTrade(analysisResult.analysis)) {
+ console.log('EXECUTING: Trade criteria met for ' + this.stats.strategyType)
+ const tradeResult = await this.executeSafeTrade(analysisResult.analysis)
+ if (tradeResult?.success) {
+ this.stats.totalTrades++
+ this.stats.successfulTrades++
+ this.stats.winRate = (this.stats.successfulTrades / this.stats.totalTrades) * 100
+ console.log('TRADE: Executed successfully')
+ }
+ } else {
+ console.log('SIGNAL: Criteria not met for ' + this.stats.strategyType)
+ }
+
+ this.stats.currentCycle++
+
+ } catch (error) {
+ console.error('Error in safe cycle:', error)
+ this.stats.errorCount++
+ this.stats.lastError = error instanceof Error ? error.message : 'Unknown error'
+ } finally {
+ await this.guaranteedCleanup()
+ this.updateNextAnalysisTime()
+ }
+ }
+
+ private async performParallelAnalysis(): Promise {
+ try {
+ // Use the enhanced screenshot API endpoint with all selected timeframes
+ const response = await fetch('http://localhost:3000/api/enhanced-screenshot', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ symbol: this.config.symbol,
+ timeframes: this.config.timeframes,
+ layouts: ['ai', 'diy'],
+ analyze: true
+ })
+ })
+
+ throw new Error('API response: ' + response.status)
+ }
+
+ const result = await response.json()
+
+ if (result.analysis) {
+ console.log('PARALLEL: Confidence ' + result.analysis.confidence + '%')
+ return result
+ }
+
+ return null
+ } catch (error) {
+ console.error('Error in parallel analysis:', error)
+ throw error
+ }
+ }
+
+ private shouldExecuteTrade(analysis: any): boolean {
+ const recommendation = analysis.recommendation.toLowerCase()
+ const confidence = analysis.confidence || 0
+
+ // Strategy-specific confidence requirements
+ let minConfidence = 75 // Default
+
+ if (this.stats.strategyType === 'Scalping') {
+ minConfidence = 80 // Higher confidence for scalping
+ } else if (this.stats.strategyType === 'Day Trading') {
+ minConfidence = 75 // Standard confidence
+ } else if (this.stats.strategyType === 'Swing Trading') {
+ minConfidence = 70 // Slightly lower for swing (longer timeframes)
+ }
+
+ const isHighConfidence = confidence >= minConfidence
+ const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell')
+
+ console.log('SIGNAL: ' + recommendation + ' (' + confidence + '%) - Required: ' + minConfidence + '%')
+
+ return isHighConfidence && isClearDirection
+ }
+
+ private getStrategyInterval(timeframes: string[], strategyType: string): number {
+ // Strategy-based intervals (but minimum will be enforced)
+
+ if (strategyType === 'Scalping') {
+ console.log('SCALPING: Using 2-minute analysis cycle')
+ return 2 * 60 * 1000 // 2 minutes for scalping (will be enforced to 10 min minimum)
+ }
+
+ if (strategyType === 'Day Trading') {
+ console.log('DAY TRADING: Using 5-minute analysis cycle')
+ return 5 * 60 * 1000 // 5 minutes for day trading (will be enforced to 10 min minimum)
+ }
+
+ if (strategyType === 'Swing Trading') {
+ console.log('SWING TRADING: Using 15-minute analysis cycle')
+ return 15 * 60 * 1000 // 15 minutes for swing trading
+ }
+
+ // Fallback based on shortest timeframe
+ const shortest = Math.min(...timeframes.map(tf => {
+ if (tf === 'D') return 1440
+ return parseInt(tf) || 60
+ }))
+
+ if (shortest <= 5) return 2 * 60 * 1000 // 2 min for very short
+ if (shortest <= 15) return 5 * 60 * 1000 // 5 min for short
+ if (shortest <= 60) return 10 * 60 * 1000 // 10 min for medium
+ return 15 * 60 * 1000 // 15 min for long
+ }
+
+ private async executeSafeTrade(analysis: any): Promise {
+ try {
+ console.log('SAFE TRADE: Executing...')
+
+ // Use drift trading API endpoint
+ const response = await fetch('http://localhost:3000/api/trading', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ mode: this.config.mode,
+ symbol: this.config.symbol,
+ amount: this.config.tradingAmount,
+ leverage: this.config.leverage,
+ stopLoss: this.config.stopLoss,
+ takeProfit: this.config.takeProfit,
+ analysis: analysis,
+ strategy: this.stats.strategyType
+ })
+ })
+
+ const result = await response.json()
+ return result
+ } catch (error) {
+ console.error('Error executing trade:', error)
+ return { success: false, error: error.message }
+ }
+ }
+
+ private async canExecuteTrade(): Promise<{ allowed: boolean, reason?: string }> {
+ const now = Date.now()
+
+ if (now - this.lastAnalysisTime < this.ANALYSIS_COOLDOWN) {
+ const remaining = Math.ceil((this.ANALYSIS_COOLDOWN - (now - this.lastAnalysisTime)) / 1000 / 60)
+ return { allowed: false, reason: 'Cooldown: ' + remaining + 'min' }
+ }
+
+ if (this.stats.totalTrades >= this.MAX_TRADES_PER_HOUR) {
+ return { allowed: false, reason: 'Hour limit: ' + this.stats.totalTrades + '/' + this.MAX_TRADES_PER_HOUR }
+ }
+
+ return { allowed: true }
+ }
+
+ private async guaranteedCleanup(): Promise {
+ try {
+ const { execSync } = require('child_process')
+ execSync('pkill -f "chrome|chromium" 2>/dev/null || true')
+ console.log('CLEANUP: Completed')
+ } catch (error) {
+ console.error('CLEANUP: Failed', error)
+ }
+ }
+
+ private updateNextAnalysisTime(): void {
+ const nextTime = Date.now() + this.stats.analysisInterval
+ this.stats.nextScheduled = new Date(nextTime).toISOString()
+ this.stats.nextAnalysisIn = this.stats.analysisInterval
+ }
+
+ async stopSafeAutomation(): Promise<{ success: boolean, message?: string }> {
+ try {
+ this.isRunning = false
+
+ if (this.intervalId) {
+ clearInterval(this.intervalId)
+ this.intervalId = null
+ }
+
+ await this.guaranteedCleanup()
+
+ this.config = null
+ this.stats.nextAnalysisIn = 0
+ this.stats.nextScheduled = null
+
+ console.log('SAFE: Automation stopped')
+ return { success: true, message: 'Safe automation stopped' }
+ } catch (error) {
+ return { success: false, message: 'Stop failed' }
+ }
+ }
+
+ getStatus() {
+ return {
+ isActive: this.isRunning,
+ mode: this.config?.mode || 'SIMULATION',
+ symbol: this.config?.symbol || 'SOLUSD',
+ timeframes: this.config?.timeframes || ['1h'],
+ automationType: 'SAFE_PARALLEL',
+ strategy: this.stats.strategyType,
+ ...this.stats
+ }
+ }
+}
+
+export const safeParallelAutomation = new SafeParallelAutomation()
diff --git a/lib/simple-automation.js b/lib/simple-automation.js
new file mode 100644
index 0000000..448d843
--- /dev/null
+++ b/lib/simple-automation.js
@@ -0,0 +1,350 @@
+// Simple automation service for basic start/stop functionality
+class SimpleAutomation {
+ constructor() {
+ this.isRunning = false;
+ this.config = null;
+ this.intervalId = null;
+ this.stats = {
+ totalCycles: 0,
+ totalTrades: 0,
+ startTime: null,
+ lastActivity: null,
+ status: 'Stopped'
+ };
+ }
+
+ async start(config) {
+ try {
+ if (this.isRunning) {
+ return { success: false, message: 'Automation already running' };
+ }
+
+ // Validate basic config
+ if (!config.selectedTimeframes || config.selectedTimeframes.length === 0) {
+ return { success: false, message: 'At least one timeframe required' };
+ }
+
+ this.config = config;
+ this.isRunning = true;
+ this.stats.startTime = new Date().toISOString();
+ this.stats.status = 'Running';
+ this.stats.totalCycles = 0;
+
+ // Detect strategy
+ const timeframes = config.selectedTimeframes;
+ let strategy = 'General';
+
+ const isScalping = timeframes.includes('5') || timeframes.includes('15') || timeframes.includes('30');
+ const isDayTrading = timeframes.includes('60') || timeframes.includes('120');
+ const isSwingTrading = timeframes.includes('240') || timeframes.includes('D');
+
+ if (isScalping) strategy = 'Scalping';
+ else if (isDayTrading) strategy = 'Day Trading';
+ else if (isSwingTrading) strategy = 'Swing Trading';
+
+ console.log('SIMPLE AUTOMATION: Starting ' + strategy + ' strategy');
+ console.log('TIMEFRAMES: [' + timeframes.join(', ') + ']');
+ console.log('MODE: ' + (config.mode || 'SIMULATION'));
+
+ // Start simple monitoring cycle (10 minutes for safety)
+ this.intervalId = setInterval(() => {
+ this.runCycle();
+ }, 10 * 60 * 1000); // 10 minutes
+
+ // First cycle after 30 seconds
+ setTimeout(() => {
+ this.runCycle();
+ }, 30000);
+
+ return {
+ success: true,
+ message: strategy + ' automation started successfully',
+ strategy: strategy,
+ timeframes: timeframes
+ };
+
+ } catch (error) {
+ console.error('Failed to start automation:', error);
+ return { success: false, message: 'Failed to start automation: ' + error.message };
+ }
+ }
+
+ async stop() {
+ try {
+ this.isRunning = false;
+ this.stats.status = 'Stopped';
+
+ if (this.intervalId) {
+ clearInterval(this.intervalId);
+ this.intervalId = null;
+ }
+
+ console.log('SIMPLE AUTOMATION: Stopped');
+
+ return { success: true, message: 'Automation stopped successfully' };
+ } catch (error) {
+ console.error('Failed to stop automation:', error);
+ return { success: false, message: 'Failed to stop automation: ' + error.message };
+ }
+ }
+
+ async runCycle() {
+ try {
+ this.stats.totalCycles++;
+ this.stats.lastActivity = new Date().toISOString();
+
+ console.log('š AUTOMATION CYCLE ' + this.stats.totalCycles + ': ' + (this.config?.symbol || 'SOLUSD'));
+ console.log('ā° TIME: ' + new Date().toLocaleTimeString());
+ console.log('š STRATEGY: ' + (this.config.selectedTimeframes?.join(', ') || 'N/A') + ' timeframes');
+
+ if (this.config) {
+ // Perform actual analysis using enhanced screenshot API
+ await this.performAnalysis();
+ }
+
+ } catch (error) {
+ console.error('Error in automation cycle:', error);
+ }
+ }
+
+ async performAnalysis() {
+ console.log(`š TRUE PARALLEL ANALYSIS: Starting simultaneous analysis for ${this.config.selectedTimeframes.length} timeframes...`);
+ console.log(`š This will capture ${this.config.selectedTimeframes.length * 2} screenshots in parallel (${this.config.selectedTimeframes.length} timeframes Ć 2 layouts)`);
+
+ try {
+ // Use batch analysis API for true parallelism - captures all timeframes simultaneously
+ const response = await fetch('http://localhost:3000/api/batch-analysis', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ symbol: this.config.symbol,
+ timeframes: this.config.selectedTimeframes, // All timeframes at once!
+ layouts: ['ai', 'diy'],
+ analyze: true
+ })
+ });
+
+ if (!response.ok) {
+ console.warn(`ā ļø BATCH API ERROR: ${response.status}, falling back to sequential...`);
+ return this.performSequentialAnalysis();
+ }
+
+ const result = await response.json();
+
+ if (result.success && result.analysis) {
+ console.log(`ā
PARALLEL ANALYSIS COMPLETE: ${result.totalScreenshots} screenshots captured`);
+ console.log(`š COMBINED Recommendation: ${result.analysis.recommendation}`);
+ console.log(`šÆ COMBINED Confidence: ${result.analysis.confidence}%`);
+ console.log(`ā° Timeframes analyzed: ${result.timeframes.join(', ')}`);
+
+ // Check if we should execute a trade based on combined analysis
+ if (this.shouldExecuteTrade(result.analysis)) {
+ console.log('š° TRADE SIGNAL: Executing trade...');
+ await this.executeTrade(result.analysis);
+ } else {
+ console.log('š SIGNAL: Combined analysis complete, no trade executed');
+ }
+
+ return;
+ } else {
+ console.warn('ā ļø BATCH ANALYSIS: No valid data, falling back to sequential...');
+ return this.performSequentialAnalysis();
+ }
+ } catch (error) {
+ console.error(`ā PARALLEL ANALYSIS FAILED:`, error);
+ console.log(`š FALLBACK: Using sequential analysis...`);
+ return this.performSequentialAnalysis();
+ }
+ }
+
+ // Fallback sequential analysis method
+ async performSequentialAnalysis() {
+ try {
+ console.log('šø SEQUENTIAL ANALYSIS: Starting...');
+
+ const allResults = [];
+
+ // Analyze each timeframe separately
+ for (const timeframe of this.config.selectedTimeframes) {
+ console.log(`š ANALYZING: ${timeframe} timeframe...`);
+
+ // Use the enhanced screenshot API for each timeframe
+ const response = await fetch('http://localhost:3000/api/enhanced-screenshot', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ symbol: this.config.symbol,
+ timeframe: timeframe, // Send one timeframe at a time
+ layouts: ['ai', 'diy'],
+ analyze: true
+ })
+ });
+
+ if (!response.ok) {
+ console.log(`ā ļø TIMEFRAME ${timeframe}: API error ${response.status}`);
+ continue;
+ }
+
+ const result = await response.json();
+
+ if (result.analysis) {
+ console.log(`ā
TIMEFRAME ${timeframe}: ${result.analysis.recommendation} (${result.analysis.confidence}%)`);
+ allResults.push({
+ timeframe: timeframe,
+ analysis: result.analysis,
+ screenshots: result.screenshots
+ });
+ } else {
+ console.log(`ā ļø TIMEFRAME ${timeframe}: No analysis data`);
+ }
+ }
+
+ if (allResults.length > 0) {
+ console.log('ā
MULTI-TIMEFRAME ANALYSIS COMPLETE:');
+
+ // Combine results for overall decision
+ const combinedAnalysis = this.combineTimeframeAnalysis(allResults);
+
+ console.log('š COMBINED Recommendation: ' + combinedAnalysis.recommendation);
+ console.log('šÆ COMBINED Confidence: ' + combinedAnalysis.confidence + '%');
+ console.log('ā° Timeframes analyzed: ' + allResults.map(r => r.timeframe).join(', '));
+
+ // Check if we should execute a trade based on combined analysis
+ if (this.shouldExecuteTrade(combinedAnalysis)) {
+ console.log('š° TRADE SIGNAL: Executing trade...');
+ await this.executeTrade(combinedAnalysis);
+ } else {
+ console.log('š SIGNAL: Combined analysis complete, no trade executed');
+ }
+ } else {
+ console.log('ā ļø ANALYSIS: No valid analysis data from any timeframe');
+ }
+
+ } catch (error) {
+ console.error('ā ANALYSIS ERROR:', error.message);
+ }
+ }
+
+ combineTimeframeAnalysis(results) {
+ if (results.length === 1) {
+ return results[0].analysis;
+ }
+
+ // Simple combination logic - can be enhanced
+ const recommendations = results.map(r => r.analysis.recommendation?.toLowerCase() || 'hold');
+ const confidences = results.map(r => r.analysis.confidence || 0);
+
+ // Count votes for each recommendation
+ const votes = {};
+ recommendations.forEach(rec => {
+ votes[rec] = (votes[rec] || 0) + 1;
+ });
+
+ // Get majority recommendation
+ const majorityRec = Object.keys(votes).reduce((a, b) => votes[a] > votes[b] ? a : b);
+
+ // Average confidence, but boost if multiple timeframes agree
+ const avgConfidence = confidences.reduce((sum, conf) => sum + conf, 0) / confidences.length;
+ const agreementBonus = votes[majorityRec] > 1 ? 10 : 0; // +10% if multiple agree
+ const finalConfidence = Math.min(95, avgConfidence + agreementBonus);
+
+ console.log(`š CONSENSUS: ${majorityRec.toUpperCase()} from ${votes[majorityRec]}/${results.length} timeframes`);
+
+ return {
+ recommendation: majorityRec,
+ confidence: Math.round(finalConfidence),
+ reasoning: `Multi-timeframe consensus from ${results.length} timeframes (${results.map(r => r.timeframe).join(', ')})`,
+ timeframeResults: results
+ };
+ }
+
+ shouldExecuteTrade(analysis) {
+ if (this.config.mode !== 'LIVE') {
+ console.log('š SIMULATION MODE: Would execute trade');
+ return false;
+ }
+
+ const recommendation = analysis.recommendation?.toLowerCase() || '';
+ const confidence = analysis.confidence || 0;
+
+ // Strategy-specific confidence requirements
+ let minConfidence = 75;
+ if (this.config.selectedTimeframes?.includes('5') || this.config.selectedTimeframes?.includes('15')) {
+ minConfidence = 80; // Higher confidence for scalping
+ }
+
+ const isHighConfidence = confidence >= minConfidence;
+ const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell');
+
+ console.log('šÆ TRADE DECISION: ' + recommendation + ' (' + confidence + '%) - Min: ' + minConfidence + '%');
+
+ return isHighConfidence && isClearDirection;
+ }
+
+ async executeTrade(analysis) {
+ try {
+ console.log('š° EXECUTING TRADE...');
+
+ // Map analysis recommendation to trading side
+ const recommendation = analysis.recommendation?.toLowerCase() || '';
+ let side = '';
+
+ if (recommendation.includes('buy')) {
+ side = 'BUY';
+ } else if (recommendation.includes('sell')) {
+ side = 'SELL';
+ } else {
+ console.log('ā TRADE SKIP: Invalid recommendation - ' + recommendation);
+ return { success: false, error: 'Invalid recommendation: ' + recommendation };
+ }
+
+ // Use the trading API with proper fields
+ const tradePayload = {
+ symbol: this.config.symbol,
+ side: side,
+ amount: this.config.tradingAmount || 10, // Default to $10 if not set
+ type: 'market',
+ tradingMode: 'SPOT',
+ analysis: analysis // Include analysis for reference
+ };
+
+ console.log('š TRADE PAYLOAD:', tradePayload);
+
+ const response = await fetch('http://localhost:3000/api/trading', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(tradePayload)
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ console.log('ā
TRADE EXECUTED: ' + result.message);
+ this.stats.totalTrades = (this.stats.totalTrades || 0) + 1;
+ this.stats.successfulTrades = (this.stats.successfulTrades || 0) + 1;
+ } else {
+ console.log('ā TRADE FAILED: ' + result.error);
+ }
+
+ return result;
+ } catch (error) {
+ console.error('ā TRADE ERROR:', error.message);
+ return { success: false, error: error.message };
+ }
+ }
+
+ getStatus() {
+ return {
+ isActive: this.isRunning,
+ mode: this.config?.mode || 'SIMULATION',
+ symbol: this.config?.symbol || 'SOLUSD',
+ timeframes: this.config?.selectedTimeframes || [],
+ automationType: 'SIMPLE',
+ ...this.stats
+ };
+ }
+}
+
+// Export singleton instance
+const simpleAutomation = new SimpleAutomation();
+export { simpleAutomation };
diff --git a/prisma/prisma/dev.db b/prisma/prisma/dev.db
index 8f180af..3ef86e1 100644
Binary files a/prisma/prisma/dev.db and b/prisma/prisma/dev.db differ