feat: Add 'Mark as Traded' functionality for manual trade tracking
- Add markAsTraded() function to create virtual positions for follow-up analysis - Enable follow-up assistant for manually executed trades on external platforms - Add blue 'Mark Traded' buttons next to existing green 'Trade' buttons - Prompt for trade amount and confirm entry price before creating position - Automatically store analysis data (entry, SL, TP) with virtual position - Optional direct access to Trade Follow-up Assistant after marking - Solves issue where manual Drift trades couldn't use follow-up analysis - Maintains existing automated trade execution functionality
This commit is contained in:
@@ -504,6 +504,72 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
setTradeModalOpen(true)
|
setTradeModalOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual trade marking for follow-up (when trading manually on external platforms)
|
||||||
|
const markAsTraded = async (analysisData: any) => {
|
||||||
|
try {
|
||||||
|
console.log('📋 Marking analysis as manually traded for follow-up:', analysisData)
|
||||||
|
|
||||||
|
// Extract trade data from analysis
|
||||||
|
const analysis = analysisData.result?.analysis || analysisData.analysis
|
||||||
|
if (!analysis) {
|
||||||
|
alert('❌ No analysis data found to mark as traded')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create virtual position for follow-up tracking
|
||||||
|
const entryPrice = analysis.entry?.price || 0
|
||||||
|
const stopLoss = analysis.stopLoss?.price || null
|
||||||
|
const takeProfit = analysis.takeProfits?.tp1?.price || null
|
||||||
|
const side = analysis.recommendation === 'BUY' ? 'LONG' : analysis.recommendation === 'SELL' ? 'SHORT' : 'LONG'
|
||||||
|
|
||||||
|
if (!entryPrice) {
|
||||||
|
alert('❌ No entry price found in analysis')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for trade details
|
||||||
|
const amount = prompt('Enter trade amount (size):')
|
||||||
|
if (!amount || parseFloat(amount) <= 0) return
|
||||||
|
|
||||||
|
const actualEntryPrice = prompt(`Confirm entry price (from analysis: $${entryPrice}):`, entryPrice.toString())
|
||||||
|
if (!actualEntryPrice) return
|
||||||
|
|
||||||
|
const response = await fetch('/api/trading/positions', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
action: 'add',
|
||||||
|
symbol: symbol,
|
||||||
|
side: side,
|
||||||
|
amount: parseFloat(amount),
|
||||||
|
entryPrice: parseFloat(actualEntryPrice),
|
||||||
|
leverage: 1,
|
||||||
|
stopLoss: stopLoss,
|
||||||
|
takeProfit: takeProfit,
|
||||||
|
txId: `manual_${Date.now()}`,
|
||||||
|
notes: `Manual trade marked from AI analysis - ${analysis.summary?.substring(0, 100) || 'AI trading setup'}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
alert(`✅ Position marked as traded!\n\n• ${side} ${amount} ${symbol}\n• Entry: $${actualEntryPrice}\n• Follow-up assistant now available`)
|
||||||
|
|
||||||
|
// Optional: Show follow-up panel
|
||||||
|
if (confirm('Open Trade Follow-up Assistant now?')) {
|
||||||
|
setFollowUpPanelOpen(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(result.error || 'Failed to mark trade')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error marking trade:', error)
|
||||||
|
alert(`❌ Failed to mark trade: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trade execution API call
|
// Trade execution API call
|
||||||
const executeTrade = async (tradeData: any) => {
|
const executeTrade = async (tradeData: any) => {
|
||||||
try {
|
try {
|
||||||
@@ -1022,12 +1088,21 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
</h5>
|
</h5>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
{timeframeResult.success && timeframeResult.result.analysis && (
|
{timeframeResult.success && timeframeResult.result.analysis && (
|
||||||
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleTradeClick(timeframeResult)}
|
onClick={() => handleTradeClick(timeframeResult)}
|
||||||
className="px-3 py-1 bg-gradient-to-r from-green-500 to-green-600 text-white text-xs font-medium rounded hover:from-green-600 hover:to-green-700 transition-all transform hover:scale-105"
|
className="px-3 py-1 bg-gradient-to-r from-green-500 to-green-600 text-white text-xs font-medium rounded hover:from-green-600 hover:to-green-700 transition-all transform hover:scale-105"
|
||||||
>
|
>
|
||||||
💰 Trade
|
💰 Trade
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => markAsTraded(timeframeResult)}
|
||||||
|
className="px-3 py-1 bg-gradient-to-r from-blue-500 to-blue-600 text-white text-xs font-medium rounded hover:from-blue-600 hover:to-blue-700 transition-all transform hover:scale-105"
|
||||||
|
title="Mark as manually traded for follow-up analysis"
|
||||||
|
>
|
||||||
|
📋 Mark Traded
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<span className="text-xs text-gray-400">
|
<span className="text-xs text-gray-400">
|
||||||
{timeframeResult.success ? 'Analysis Complete' : 'Failed'}
|
{timeframeResult.success ? 'Analysis Complete' : 'Failed'}
|
||||||
@@ -1125,12 +1200,21 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
{result.analysis && (
|
{result.analysis && (
|
||||||
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleTradeClick({ result, timeframeLabel: selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ') })}
|
onClick={() => handleTradeClick({ result, timeframeLabel: selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ') })}
|
||||||
className="px-4 py-2 bg-gradient-to-r from-green-500 to-green-600 text-white text-sm font-medium rounded-lg hover:from-green-600 hover:to-green-700 transition-all transform hover:scale-105"
|
className="px-4 py-2 bg-gradient-to-r from-green-500 to-green-600 text-white text-sm font-medium rounded-lg hover:from-green-600 hover:to-green-700 transition-all transform hover:scale-105"
|
||||||
>
|
>
|
||||||
💰 Execute Trade
|
💰 Execute Trade
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => markAsTraded({ result, timeframeLabel: selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ') })}
|
||||||
|
className="px-4 py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white text-sm font-medium rounded-lg hover:from-blue-600 hover:to-blue-700 transition-all transform hover:scale-105"
|
||||||
|
title="Mark as manually traded for follow-up analysis"
|
||||||
|
>
|
||||||
|
📋 Mark as Traded
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{result.screenshots && (
|
{result.screenshots && (
|
||||||
<div className="text-xs text-gray-400">
|
<div className="text-xs text-gray-400">
|
||||||
|
|||||||
Reference in New Issue
Block a user