folder rename
This commit is contained in:
193
ANNOTATE/web/static/js/annotation_manager.js
Normal file
193
ANNOTATE/web/static/js/annotation_manager.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user