anotate ui phase 1
This commit is contained in:
400
TESTCASES/web/app.py
Normal file
400
TESTCASES/web/app.py
Normal file
@@ -0,0 +1,400 @@
|
||||
"""
|
||||
Manual Trade Annotation UI - Main Application
|
||||
|
||||
A web-based interface for manually marking profitable buy/sell signals on historical
|
||||
market data to generate training test cases for machine learning models.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path for imports
|
||||
parent_dir = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(parent_dir))
|
||||
|
||||
from flask import Flask, render_template, request, jsonify, send_file
|
||||
from dash import Dash, html
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
# Import core components from main system
|
||||
try:
|
||||
from core.data_provider import DataProvider
|
||||
from core.orchestrator import TradingOrchestrator
|
||||
from core.config import get_config
|
||||
except ImportError as e:
|
||||
print(f"Warning: Could not import main system components: {e}")
|
||||
print("Running in standalone mode with limited functionality")
|
||||
DataProvider = None
|
||||
TradingOrchestrator = None
|
||||
get_config = lambda: {}
|
||||
|
||||
# Import TESTCASES modules
|
||||
testcases_dir = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(testcases_dir))
|
||||
|
||||
try:
|
||||
from core.annotation_manager import AnnotationManager
|
||||
from core.training_simulator import TrainingSimulator
|
||||
except ImportError:
|
||||
# Try alternative import path
|
||||
import importlib.util
|
||||
|
||||
# Load annotation_manager
|
||||
ann_spec = importlib.util.spec_from_file_location(
|
||||
"annotation_manager",
|
||||
testcases_dir / "core" / "annotation_manager.py"
|
||||
)
|
||||
ann_module = importlib.util.module_from_spec(ann_spec)
|
||||
ann_spec.loader.exec_module(ann_module)
|
||||
AnnotationManager = ann_module.AnnotationManager
|
||||
|
||||
# Load training_simulator
|
||||
train_spec = importlib.util.spec_from_file_location(
|
||||
"training_simulator",
|
||||
testcases_dir / "core" / "training_simulator.py"
|
||||
)
|
||||
train_module = importlib.util.module_from_spec(train_spec)
|
||||
train_spec.loader.exec_module(train_module)
|
||||
TrainingSimulator = train_module.TrainingSimulator
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AnnotationDashboard:
|
||||
"""Main annotation dashboard application"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the dashboard"""
|
||||
# Load configuration
|
||||
self.config = get_config() if get_config else {}
|
||||
|
||||
# Initialize Flask app
|
||||
self.server = Flask(
|
||||
__name__,
|
||||
template_folder='templates',
|
||||
static_folder='static'
|
||||
)
|
||||
|
||||
# Initialize Dash app
|
||||
self.app = Dash(
|
||||
__name__,
|
||||
server=self.server,
|
||||
url_base_pathname='/dash/',
|
||||
external_stylesheets=[
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'
|
||||
]
|
||||
)
|
||||
|
||||
# Initialize core components
|
||||
self.data_provider = DataProvider() if DataProvider else None
|
||||
self.orchestrator = TradingOrchestrator(
|
||||
data_provider=self.data_provider
|
||||
) if TradingOrchestrator and self.data_provider else None
|
||||
|
||||
# Initialize TESTCASES components
|
||||
self.annotation_manager = AnnotationManager()
|
||||
self.training_simulator = TrainingSimulator(self.orchestrator) if self.orchestrator else None
|
||||
|
||||
# Setup routes
|
||||
self._setup_routes()
|
||||
|
||||
logger.info("Annotation Dashboard initialized")
|
||||
|
||||
def _setup_routes(self):
|
||||
"""Setup Flask routes"""
|
||||
|
||||
@self.server.route('/')
|
||||
def index():
|
||||
"""Main dashboard page"""
|
||||
# Get current annotations
|
||||
annotations = self.annotation_manager.get_annotations()
|
||||
|
||||
# Prepare template data
|
||||
template_data = {
|
||||
'current_symbol': 'ETH/USDT',
|
||||
'timeframes': ['1s', '1m', '1h', '1d'],
|
||||
'annotations': [ann.__dict__ if hasattr(ann, '__dict__') else ann
|
||||
for ann in annotations]
|
||||
}
|
||||
|
||||
return render_template('annotation_dashboard.html', **template_data)
|
||||
|
||||
@self.server.route('/api/chart-data', methods=['POST'])
|
||||
def get_chart_data():
|
||||
"""Get chart data for specified symbol and timeframes"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
symbol = data.get('symbol', 'ETH/USDT')
|
||||
timeframes = data.get('timeframes', ['1s', '1m', '1h', '1d'])
|
||||
start_time = data.get('start_time')
|
||||
end_time = data.get('end_time')
|
||||
|
||||
if not self.data_provider:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'DATA_PROVIDER_UNAVAILABLE',
|
||||
'message': 'Data provider not available'
|
||||
}
|
||||
})
|
||||
|
||||
# Fetch data for each timeframe
|
||||
chart_data = {}
|
||||
for timeframe in timeframes:
|
||||
df = self.data_provider.get_historical_data(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
limit=500
|
||||
)
|
||||
|
||||
if df is not None and not df.empty:
|
||||
# Convert to format suitable for Plotly
|
||||
chart_data[timeframe] = {
|
||||
'timestamps': df.index.strftime('%Y-%m-%d %H:%M:%S').tolist(),
|
||||
'open': df['open'].tolist(),
|
||||
'high': df['high'].tolist(),
|
||||
'low': df['low'].tolist(),
|
||||
'close': df['close'].tolist(),
|
||||
'volume': df['volume'].tolist()
|
||||
}
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'chart_data': chart_data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching chart data: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'CHART_DATA_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/save-annotation', methods=['POST'])
|
||||
def save_annotation():
|
||||
"""Save a new annotation"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
# Create annotation
|
||||
annotation = self.annotation_manager.create_annotation(
|
||||
entry_point=data['entry'],
|
||||
exit_point=data['exit'],
|
||||
symbol=data['symbol'],
|
||||
timeframe=data['timeframe']
|
||||
)
|
||||
|
||||
# Save annotation
|
||||
self.annotation_manager.save_annotation(annotation)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'annotation': annotation.__dict__ if hasattr(annotation, '__dict__') else annotation
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving annotation: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'SAVE_ANNOTATION_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/delete-annotation', methods=['POST'])
|
||||
def delete_annotation():
|
||||
"""Delete an annotation"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
annotation_id = data['annotation_id']
|
||||
|
||||
self.annotation_manager.delete_annotation(annotation_id)
|
||||
|
||||
return jsonify({'success': True})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting annotation: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'DELETE_ANNOTATION_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/generate-test-case', methods=['POST'])
|
||||
def generate_test_case():
|
||||
"""Generate test case from annotation"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
annotation_id = data['annotation_id']
|
||||
|
||||
# Get annotation
|
||||
annotations = self.annotation_manager.get_annotations()
|
||||
annotation = next((a for a in annotations
|
||||
if (a.annotation_id if hasattr(a, 'annotation_id')
|
||||
else a.get('annotation_id')) == annotation_id), None)
|
||||
|
||||
if not annotation:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'ANNOTATION_NOT_FOUND',
|
||||
'message': 'Annotation not found'
|
||||
}
|
||||
})
|
||||
|
||||
# Generate test case
|
||||
test_case = self.annotation_manager.generate_test_case(annotation)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'test_case': test_case
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating test case: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'GENERATE_TESTCASE_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/export-annotations', methods=['POST'])
|
||||
def export_annotations():
|
||||
"""Export annotations to file"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
symbol = data.get('symbol')
|
||||
format_type = data.get('format', 'json')
|
||||
|
||||
# Get annotations
|
||||
annotations = self.annotation_manager.get_annotations(symbol=symbol)
|
||||
|
||||
# Export to file
|
||||
output_path = self.annotation_manager.export_annotations(
|
||||
annotations=annotations,
|
||||
format_type=format_type
|
||||
)
|
||||
|
||||
return send_file(output_path, as_attachment=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error exporting annotations: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'EXPORT_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/train-model', methods=['POST'])
|
||||
def train_model():
|
||||
"""Start model training with annotations"""
|
||||
try:
|
||||
if not self.training_simulator:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
}
|
||||
})
|
||||
|
||||
data = request.get_json()
|
||||
model_name = data['model_name']
|
||||
annotation_ids = data['annotation_ids']
|
||||
|
||||
# Get annotations
|
||||
annotations = self.annotation_manager.get_annotations()
|
||||
selected_annotations = [a for a in annotations
|
||||
if (a.annotation_id if hasattr(a, 'annotation_id')
|
||||
else a.get('annotation_id')) in annotation_ids]
|
||||
|
||||
# Generate test cases
|
||||
test_cases = [self.annotation_manager.generate_test_case(ann)
|
||||
for ann in selected_annotations]
|
||||
|
||||
# Start training
|
||||
training_id = self.training_simulator.start_training(
|
||||
model_name=model_name,
|
||||
test_cases=test_cases
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'training_id': training_id
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error starting training: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
@self.server.route('/api/training-progress', methods=['POST'])
|
||||
def get_training_progress():
|
||||
"""Get training progress"""
|
||||
try:
|
||||
if not self.training_simulator:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
}
|
||||
})
|
||||
|
||||
data = request.get_json()
|
||||
training_id = data['training_id']
|
||||
|
||||
progress = self.training_simulator.get_training_progress(training_id)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'progress': progress
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting training progress: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'PROGRESS_ERROR',
|
||||
'message': str(e)
|
||||
}
|
||||
})
|
||||
|
||||
def run(self, host='127.0.0.1', port=8051, debug=False):
|
||||
"""Run the application"""
|
||||
logger.info(f"Starting Annotation Dashboard on http://{host}:{port}")
|
||||
self.server.run(host=host, port=port, debug=debug)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
dashboard = AnnotationDashboard()
|
||||
dashboard.run(debug=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user