Fix: Resolve SL Learner database errors and enhance automation
- Fixed Prisma schema: Added @default(cuid()) to ai_learning_data.id field - Fixed all updatedAt fields: Added @updatedAt decorators across all models - Enhanced position-aware automation with intelligent DCA/doubling down logic - Added safe automation starter script with position awareness - Resolved 'Argument id is missing' database creation errors - All AI learning data can now be created without Prisma errors Database schema now properly auto-generates IDs and timestamps for: - ai_learning_data records - All model updatedAt fields - Prevents Enhanced Risk Manager database failures
This commit is contained in:
@@ -235,15 +235,249 @@ class PositionAwareAutomation {
|
|||||||
console.log(`💰 Current PnL: $${positionData.position.unrealizedPnl}`);
|
console.log(`💰 Current PnL: $${positionData.position.unrealizedPnl}`);
|
||||||
console.log(`🎯 Distance to SL: ${positionData.stopLossProximity.distancePercent}%`);
|
console.log(`🎯 Distance to SL: ${positionData.stopLossProximity.distancePercent}%`);
|
||||||
|
|
||||||
// Here you would run your analysis to decide:
|
try {
|
||||||
// 1. Close position early
|
// Run AI analysis to determine market reversal potential
|
||||||
// 2. Move stop loss
|
const analysis = await this.runAIAnalysisForDCA(positionData);
|
||||||
// 3. Double down
|
|
||||||
// 4. Do nothing
|
|
||||||
|
|
||||||
console.log('🤖 ANALYSIS DECISION: [Would run AI analysis here]');
|
if (analysis && analysis.recommendation === 'DCA_DOUBLE_DOWN') {
|
||||||
|
console.log('🎯 AI DECISION: Market showing reversal signs - DOUBLING DOWN');
|
||||||
|
console.log(`📈 Confidence: ${analysis.confidence}%`);
|
||||||
|
console.log(`💰 DCA Amount: ${analysis.dcaAmount} SOL`);
|
||||||
|
console.log(`🎯 New Average: $${analysis.newAveragePrice}`);
|
||||||
|
|
||||||
|
// Execute DCA trade
|
||||||
|
await this.executeDCATradeAction(analysis, positionData);
|
||||||
|
|
||||||
|
} else if (analysis && analysis.recommendation === 'CLOSE_EARLY') {
|
||||||
|
console.log('🛑 AI DECISION: No reversal signs - CLOSING EARLY to minimize loss');
|
||||||
|
console.log(`📉 Confidence: ${analysis.confidence}%`);
|
||||||
|
|
||||||
|
// Execute early close
|
||||||
|
await this.executeEarlyCloseAction(positionData);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('⏸️ AI DECISION: HOLD position - unclear signals');
|
||||||
|
console.log('⏰ Will re-analyze in 30 seconds');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Emergency analysis failed:', error);
|
||||||
console.log('⏰ Next check in 30 seconds due to high risk');
|
console.log('⏰ Next check in 30 seconds due to high risk');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async runAIAnalysisForDCA(positionData) {
|
||||||
|
console.log('🧠 Running AI DCA Analysis...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get fresh market analysis
|
||||||
|
const response = await fetch('http://localhost:3000/api/analysis-optimized', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
symbol: positionData.position.symbol.replace('-PERP', 'USD'),
|
||||||
|
timeframes: ['5', '15', '1h'],
|
||||||
|
layouts: ['ai'],
|
||||||
|
analyze: true,
|
||||||
|
dcaMode: true, // Special DCA analysis mode
|
||||||
|
currentPosition: positionData.position
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Analysis failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const analysisData = await response.json();
|
||||||
|
const analysis = analysisData.analysis;
|
||||||
|
|
||||||
|
if (!analysis) {
|
||||||
|
console.log('❌ No analysis returned');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine DCA strategy based on AI analysis
|
||||||
|
const dcaDecision = this.evaluateDCAOpportunity(analysis, positionData);
|
||||||
|
|
||||||
|
return dcaDecision;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ AI DCA analysis failed:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluateDCAOpportunity(analysis, positionData) {
|
||||||
|
const currentPrice = positionData.position.currentPrice;
|
||||||
|
const entryPrice = positionData.position.entryPrice;
|
||||||
|
const unrealizedPnl = positionData.position.unrealizedPnl;
|
||||||
|
const confidence = analysis.confidence || 0;
|
||||||
|
|
||||||
|
// Calculate price movement from entry
|
||||||
|
const priceMovement = ((currentPrice - entryPrice) / entryPrice) * 100;
|
||||||
|
const isLongPosition = positionData.position.side === 'long';
|
||||||
|
|
||||||
|
console.log(`📊 DCA Evaluation:`);
|
||||||
|
console.log(` Price Movement: ${priceMovement.toFixed(2)}%`);
|
||||||
|
console.log(` AI Confidence: ${confidence}%`);
|
||||||
|
console.log(` Analysis: ${analysis.recommendation}`);
|
||||||
|
|
||||||
|
// DCA Logic for LONG positions
|
||||||
|
if (isLongPosition) {
|
||||||
|
// Price has dropped significantly (good DCA opportunity)
|
||||||
|
const hasDropped = priceMovement < -2; // 2%+ drop
|
||||||
|
const aiSaysBuy = analysis.recommendation?.toLowerCase().includes('buy');
|
||||||
|
const highConfidence = confidence > 75;
|
||||||
|
const oversoldSignals = analysis.technicalIndicators?.rsi < 35; // Oversold
|
||||||
|
|
||||||
|
if (hasDropped && aiSaysBuy && highConfidence) {
|
||||||
|
const dcaAmount = this.calculateDCAAmount(positionData);
|
||||||
|
const newAveragePrice = this.calculateNewAveragePrice(
|
||||||
|
positionData.position.size,
|
||||||
|
entryPrice,
|
||||||
|
dcaAmount,
|
||||||
|
currentPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
recommendation: 'DCA_DOUBLE_DOWN',
|
||||||
|
confidence: confidence,
|
||||||
|
dcaAmount: dcaAmount,
|
||||||
|
newAveragePrice: newAveragePrice,
|
||||||
|
reasoning: `AI detects reversal: ${analysis.reasoning || 'Strong buy signal'}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DCA Logic for SHORT positions
|
||||||
|
if (!isLongPosition) {
|
||||||
|
// Price has risen significantly (good DCA opportunity for shorts)
|
||||||
|
const hasRisen = priceMovement > 2; // 2%+ rise
|
||||||
|
const aiSaysSell = analysis.recommendation?.toLowerCase().includes('sell');
|
||||||
|
const highConfidence = confidence > 75;
|
||||||
|
const overboughtSignals = analysis.technicalIndicators?.rsi > 65; // Overbought
|
||||||
|
|
||||||
|
if (hasRisen && aiSaysSell && highConfidence) {
|
||||||
|
const dcaAmount = this.calculateDCAAmount(positionData);
|
||||||
|
const newAveragePrice = this.calculateNewAveragePrice(
|
||||||
|
positionData.position.size,
|
||||||
|
entryPrice,
|
||||||
|
dcaAmount,
|
||||||
|
currentPrice
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
recommendation: 'DCA_DOUBLE_DOWN',
|
||||||
|
confidence: confidence,
|
||||||
|
dcaAmount: dcaAmount,
|
||||||
|
newAveragePrice: newAveragePrice,
|
||||||
|
reasoning: `AI detects short opportunity: ${analysis.reasoning || 'Strong sell signal'}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no DCA opportunity but low confidence, suggest early close
|
||||||
|
if (confidence < 40 && Math.abs(unrealizedPnl) > 20) {
|
||||||
|
return {
|
||||||
|
recommendation: 'CLOSE_EARLY',
|
||||||
|
confidence: 100 - confidence, // Inverse confidence for closing
|
||||||
|
reasoning: 'Low AI confidence + significant loss suggests early exit'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
recommendation: 'HOLD',
|
||||||
|
confidence: confidence,
|
||||||
|
reasoning: 'No clear DCA or exit signals'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateDCAAmount(positionData) {
|
||||||
|
// Calculate safe DCA amount (max 50% of current position)
|
||||||
|
const currentSize = positionData.position.size;
|
||||||
|
const maxDCASize = currentSize * 0.5; // Max 50% of current position
|
||||||
|
|
||||||
|
// Could also factor in available balance, but for now use conservative 50%
|
||||||
|
return Math.min(maxDCASize, 2.0); // Cap at 2 SOL for safety
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateNewAveragePrice(currentSize, currentEntryPrice, dcaAmount, dcaPrice) {
|
||||||
|
const totalValue = (currentSize * currentEntryPrice) + (dcaAmount * dcaPrice);
|
||||||
|
const totalSize = currentSize + dcaAmount;
|
||||||
|
return totalValue / totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeDCATradeAction(analysis, positionData) {
|
||||||
|
console.log('🎯 EXECUTING DCA TRADE...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tradeParams = {
|
||||||
|
symbol: positionData.position.symbol,
|
||||||
|
side: positionData.position.side === 'long' ? 'BUY' : 'SELL',
|
||||||
|
amount: analysis.dcaAmount,
|
||||||
|
orderType: 'MARKET',
|
||||||
|
leverage: 10, // Use moderate leverage for DCA
|
||||||
|
stopLoss: 1.0,
|
||||||
|
takeProfit: 3.0,
|
||||||
|
isExecution: true,
|
||||||
|
dcaMode: true,
|
||||||
|
analysis: analysis
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch('http://localhost:3000/api/trading/execute', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(tradeParams)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('✅ DCA TRADE EXECUTED SUCCESSFULLY!');
|
||||||
|
console.log(`💰 Added ${analysis.dcaAmount} SOL to position`);
|
||||||
|
console.log(`📊 New Average Price: $${analysis.newAveragePrice.toFixed(4)}`);
|
||||||
|
} else {
|
||||||
|
console.error('❌ DCA trade failed:', result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error executing DCA trade:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeEarlyCloseAction(positionData) {
|
||||||
|
console.log('🛑 EXECUTING EARLY CLOSE...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const closeParams = {
|
||||||
|
symbol: positionData.position.symbol,
|
||||||
|
side: positionData.position.side === 'long' ? 'SELL' : 'BUY',
|
||||||
|
amount: positionData.position.size,
|
||||||
|
orderType: 'MARKET',
|
||||||
|
isExecution: true,
|
||||||
|
earlyClose: true,
|
||||||
|
reason: 'Emergency early close due to low AI confidence'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch('http://localhost:3000/api/trading/execute', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(closeParams)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('✅ POSITION CLOSED EARLY');
|
||||||
|
console.log(`💰 Final P&L: ${positionData.position.unrealizedPnl >= 0 ? '+' : ''}$${positionData.position.unrealizedPnl.toFixed(2)}`);
|
||||||
|
} else {
|
||||||
|
console.error('❌ Early close failed:', result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error executing early close:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async stop() {
|
async stop() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ datasource db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model ai_learning_data {
|
model ai_learning_data {
|
||||||
id String @id
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
sessionId String?
|
sessionId String?
|
||||||
tradeId String?
|
tradeId String?
|
||||||
@@ -24,7 +24,7 @@ model ai_learning_data {
|
|||||||
screenshot String?
|
screenshot String?
|
||||||
feedbackData Json?
|
feedbackData Json?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ model api_keys {
|
|||||||
encryptedKey String
|
encryptedKey String
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([userId, provider, keyName])
|
@@unique([userId, provider, keyName])
|
||||||
@@ -66,7 +66,7 @@ model automation_sessions {
|
|||||||
errorCount Int @default(0)
|
errorCount Int @default(0)
|
||||||
lastError String?
|
lastError String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
lastAnalysisData Json?
|
lastAnalysisData Json?
|
||||||
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ model trades {
|
|||||||
executionTime DateTime?
|
executionTime DateTime?
|
||||||
learningData Json?
|
learningData Json?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
executedAt DateTime?
|
executedAt DateTime?
|
||||||
closedAt DateTime?
|
closedAt DateTime?
|
||||||
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
@@ -150,7 +150,7 @@ model trading_journals {
|
|||||||
marketCondition String?
|
marketCondition String?
|
||||||
sessionId String?
|
sessionId String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ model user_settings {
|
|||||||
stopLossPercent Float @default(2.0)
|
stopLossPercent Float @default(2.0)
|
||||||
takeProfitPercent Float @default(6.0)
|
takeProfitPercent Float @default(6.0)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
users users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ model users {
|
|||||||
email String @unique
|
email String @unique
|
||||||
name String?
|
name String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime
|
updatedAt DateTime @updatedAt
|
||||||
ai_learning_data ai_learning_data[]
|
ai_learning_data ai_learning_data[]
|
||||||
api_keys api_keys[]
|
api_keys api_keys[]
|
||||||
automation_sessions automation_sessions[]
|
automation_sessions automation_sessions[]
|
||||||
|
|||||||
87
start-automation-safe.js
Executable file
87
start-automation-safe.js
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safe automation starter - checks position status before starting
|
||||||
|
*/
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const BASE_URL = 'http://localhost:9001';
|
||||||
|
|
||||||
|
async function startAutomationSafely() {
|
||||||
|
console.log('🚀 Starting Position-Aware Automation Safely...\n');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Check current positions first
|
||||||
|
console.log('1. Checking current positions...');
|
||||||
|
const positionsResponse = await axios.get(`${BASE_URL}/api/drift/positions`);
|
||||||
|
const positions = positionsResponse.data.positions || [];
|
||||||
|
|
||||||
|
console.log(`📊 Found ${positions.length} open position(s)`);
|
||||||
|
if (positions.length > 0) {
|
||||||
|
positions.forEach((pos, idx) => {
|
||||||
|
console.log(` ${idx + 1}. ${pos.symbol} ${pos.side.toUpperCase()} ${pos.size} SOL`);
|
||||||
|
console.log(` Entry: $${pos.entryPrice.toFixed(4)}, Current: $${pos.markPrice.toFixed(4)}`);
|
||||||
|
console.log(` P&L: ${pos.unrealizedPnl >= 0 ? '+' : ''}$${pos.unrealizedPnl.toFixed(2)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
// 2. Start automation with position-aware configuration
|
||||||
|
console.log('2. Starting position-aware automation...');
|
||||||
|
const automationConfig = {
|
||||||
|
symbol: 'SOLUSD',
|
||||||
|
mode: 'LIVE', // Set to LIVE for real trading
|
||||||
|
selectedTimeframes: ['5', '15'], // Scalping timeframes
|
||||||
|
enableTrading: true, // Enable real trades
|
||||||
|
tradingAmount: 100, // Position size in USD
|
||||||
|
maxLeverage: 10, // Maximum leverage
|
||||||
|
riskPercentage: 2, // 2% risk per trade
|
||||||
|
maxDailyTrades: 5, // Maximum 5 trades per day
|
||||||
|
stopLoss: 1.0, // 1% stop loss
|
||||||
|
takeProfit: 2.0, // 2% take profit
|
||||||
|
dexProvider: 'DRIFT'
|
||||||
|
};
|
||||||
|
|
||||||
|
const automationResponse = await axios.post(`${BASE_URL}/api/automation/start`, automationConfig);
|
||||||
|
|
||||||
|
if (automationResponse.data.success) {
|
||||||
|
console.log('✅ Automation started successfully!');
|
||||||
|
console.log(`📊 Mode: ${automationConfig.mode}`);
|
||||||
|
console.log(`💰 Trading: ${automationConfig.enableTrading ? 'ENABLED' : 'SIMULATION ONLY'}`);
|
||||||
|
console.log(`🎯 Symbol: ${automationConfig.symbol}`);
|
||||||
|
console.log(`⏱️ Timeframes: ${automationConfig.selectedTimeframes.join(', ')}`);
|
||||||
|
|
||||||
|
if (positions.length > 0) {
|
||||||
|
console.log('\n🎯 INTELLIGENT BEHAVIOR:');
|
||||||
|
console.log(' ✅ Will monitor existing position for stop loss proximity');
|
||||||
|
console.log(' ✅ Will switch to DCA/doubling down if price approaches SL');
|
||||||
|
console.log(' ✅ Will scan for new opportunities only after position closes');
|
||||||
|
} else {
|
||||||
|
console.log('\n🎯 SCANNING MODE:');
|
||||||
|
console.log(' ✅ Will scan for new entry opportunities');
|
||||||
|
console.log(' ✅ Will execute trades based on AI analysis');
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.error('❌ Failed to start automation:', automationResponse.data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check final status
|
||||||
|
console.log('\n3. Checking automation status...');
|
||||||
|
const statusResponse = await axios.get(`${BASE_URL}/api/automation/status`);
|
||||||
|
console.log(`📊 Status: ${JSON.stringify(statusResponse.data, null, 2)}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error starting automation:', error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the automation starter
|
||||||
|
startAutomationSafely()
|
||||||
|
.then(() => {
|
||||||
|
console.log('\n✅ Automation startup completed');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('❌ Startup failed:', error);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user