194 lines
5.6 KiB
JavaScript
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};
|
|
}
|
|
}
|