Files
gogo2/TESTCASES/web/static/js/annotation_manager.js
2025-10-18 16:37:13 +03:00

194 lines
5.6 KiB
JavaScript

/**
* 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};
}
}