- Promoted prototype dashboard to main app - Added /api/market-data endpoint - Implemented market_simulator.py for realistic OHLCV generation - Updated MoomooClient to serve simulated scenarios - Wired frontend to backend API
100 lines
3.3 KiB
Python
100 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple dashboard server for Quantitative Trading Platform.
|
|
Provides mock metrics for PnL, drawdown, and win rate.
|
|
"""
|
|
|
|
from flask import Flask, render_template, jsonify, request
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
from data.connectors import create_moomoo_client, Interval
|
|
|
|
app = Flask(__name__)
|
|
|
|
# Initialize MoomooClient in mock mode (using our LLM scenario generator)
|
|
client = create_moomoo_client(mock_mode=True)
|
|
|
|
@app.route('/')
|
|
def dashboard():
|
|
"""Serve the dashboard HTML page."""
|
|
return render_template('index.html')
|
|
|
|
@app.route('/api/market-data/<symbol>')
|
|
def market_data(symbol):
|
|
"""
|
|
API endpoint returning market data for a symbol.
|
|
Fetches 30 days of daily data by default for the dashboard.
|
|
"""
|
|
try:
|
|
# Fetch OHLCV data from our connector (which uses the simulator)
|
|
end_date = datetime.now()
|
|
start_date = end_date - timedelta(days=30)
|
|
|
|
# Determine interval from query param, default to 1d
|
|
interval_str = request.args.get('interval', '1d')
|
|
interval = Interval(interval_str)
|
|
|
|
ohlcv_data = client.get_ohlcv(
|
|
symbol=symbol.upper(),
|
|
interval=interval,
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
limit=100
|
|
)
|
|
|
|
# Transform for frontend
|
|
daily_pnl = []
|
|
cumulative = 0
|
|
|
|
# Simple simulation of PnL based on close price changes
|
|
# In a real app, this would come from the Strategy/Portfolio engine
|
|
initial_balance = 10000.0
|
|
position_size = 10 # shares
|
|
|
|
prev_close = ohlcv_data[0].close if ohlcv_data else 0
|
|
|
|
for point in ohlcv_data:
|
|
change = point.close - prev_close
|
|
pnl = change * position_size
|
|
cumulative += pnl
|
|
|
|
daily_pnl.append({
|
|
'date': point.timestamp.strftime('%Y-%m-%d'),
|
|
'pnl': round(pnl, 2),
|
|
'cumulative': round(cumulative, 2),
|
|
'close': point.close
|
|
})
|
|
prev_close = point.close
|
|
|
|
# Calculate summary metrics
|
|
total_pnl = cumulative
|
|
peak = max([d['cumulative'] for d in daily_pnl]) if daily_pnl else 0
|
|
trough = min([d['cumulative'] for d in daily_pnl]) if daily_pnl else 0
|
|
max_drawdown = min(0, trough - peak)
|
|
|
|
profitable_days = sum(1 for d in daily_pnl if d['pnl'] > 0)
|
|
win_rate = (profitable_days / len(daily_pnl) * 100) if daily_pnl else 0
|
|
|
|
return jsonify({
|
|
'symbol': symbol.upper(),
|
|
'metrics': {
|
|
'total_pnl': round(total_pnl, 2),
|
|
'max_drawdown': round(max_drawdown, 2),
|
|
'win_rate': round(win_rate, 1),
|
|
'portfolio_value': round(initial_balance + total_pnl, 2),
|
|
'peak': round(peak, 2),
|
|
'trough': round(trough, 2)
|
|
},
|
|
'daily_pnl': daily_pnl
|
|
})
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@app.route('/health')
|
|
def health():
|
|
"""Health check endpoint."""
|
|
return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=5000, debug=True) |