/** * AnnotationManager - Manages trade marking interactions */ class AnnotationManager { constructor(chartManager) { this.chartManager = chartManager; this.pendingAnnotation = null; this.enabled = true; console.log('AnnotationManager initialized'); } /** * Handle chart click for marking entry/exit */ handleChartClick(clickData) { if (!this.enabled) { console.log('Annotation mode disabled'); return; } if (!this.pendingAnnotation) { // Mark entry point this.markEntry(clickData); } else { // Mark exit point this.markExit(clickData); } } /** * Mark entry point */ markEntry(clickData) { this.pendingAnnotation = { symbol: window.appState.currentSymbol, timeframe: clickData.timeframe, entry: { timestamp: clickData.timestamp, price: clickData.price, index: clickData.index } }; console.log('Entry marked:', this.pendingAnnotation); // Show pending annotation status document.getElementById('pending-annotation-status').style.display = 'block'; // Visual feedback on chart this.showPendingMarker(clickData); } /** * Mark exit point */ markExit(clickData) { if (!this.pendingAnnotation) return; // Validate exit is after entry const entryTime = new Date(this.pendingAnnotation.entry.timestamp); const exitTime = new Date(clickData.timestamp); if (exitTime <= entryTime) { window.showError('Exit time must be after entry time'); return; } // Complete annotation this.pendingAnnotation.exit = { timestamp: clickData.timestamp, price: clickData.price, index: clickData.index }; // Calculate P&L const entryPrice = this.pendingAnnotation.entry.price; const exitPrice = this.pendingAnnotation.exit.price; const direction = exitPrice > entryPrice ? 'LONG' : 'SHORT'; const profitLossPct = ((exitPrice - entryPrice) / entryPrice) * 100; this.pendingAnnotation.direction = direction; this.pendingAnnotation.profit_loss_pct = profitLossPct; console.log('Exit marked:', this.pendingAnnotation); // Save annotation this.saveAnnotation(this.pendingAnnotation); } /** * Save annotation to server */ saveAnnotation(annotation) { fetch('/api/save-annotation', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(annotation) }) .then(response => response.json()) .then(data => { if (data.success) { // Add to app state window.appState.annotations.push(data.annotation); // Update UI window.renderAnnotationsList(window.appState.annotations); // Add to chart this.chartManager.addAnnotation(data.annotation); // Clear pending annotation this.pendingAnnotation = null; document.getElementById('pending-annotation-status').style.display = 'none'; window.showSuccess('Annotation saved successfully'); } else { window.showError('Failed to save annotation: ' + data.error.message); } }) .catch(error => { window.showError('Network error: ' + error.message); }); } /** * Show pending marker on chart */ showPendingMarker(clickData) { // TODO: Add visual marker for pending entry console.log('Showing pending marker at:', clickData); } /** * Mark current position (for keyboard shortcut) */ markCurrentPosition() { // TODO: Implement marking at current crosshair position console.log('Mark current position'); } /** * Enable annotation mode */ enable() { this.enabled = true; console.log('Annotation mode enabled'); } /** * Disable annotation mode */ disable() { this.enabled = false; this.pendingAnnotation = null; document.getElementById('pending-annotation-status').style.display = 'none'; console.log('Annotation mode disabled'); } /** * Calculate profit/loss percentage */ calculateProfitLoss(entryPrice, exitPrice, direction) { if (direction === 'LONG') { return ((exitPrice - entryPrice) / entryPrice) * 100; } else { return ((entryPrice - exitPrice) / entryPrice) * 100; } } /** * Validate annotation */ validateAnnotation(annotation) { if (!annotation.entry || !annotation.exit) { return {valid: false, error: 'Missing entry or exit point'}; } const entryTime = new Date(annotation.entry.timestamp); const exitTime = new Date(annotation.exit.timestamp); if (exitTime <= entryTime) { return {valid: false, error: 'Exit time must be after entry time'}; } if (!annotation.entry.price || !annotation.exit.price) { return {valid: false, error: 'Missing price data'}; } return {valid: true}; } }