Files

126 lines
4.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 os
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)
STRATEGY_CONFIG_PATH = os.path.join(os.path.dirname(__file__), 'data', 'strategy_configs.json')
@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('/api/strategy-config', methods=['POST'])
def save_strategy_config():
"""Save strategy configuration to a JSON file (simple local persistence)."""
try:
data = request.get_json(force=True) or {}
os.makedirs(os.path.join(os.path.dirname(__file__), 'data'), exist_ok=True)
configs = []
if os.path.exists(STRATEGY_CONFIG_PATH):
import json as _json
with open(STRATEGY_CONFIG_PATH, 'r') as f:
try:
configs = _json.load(f)
except Exception:
configs = []
# Append with timestamp
data['savedAt'] = datetime.now().isoformat()
configs.append(data)
import json
with open(STRATEGY_CONFIG_PATH, 'w') as f:
json.dump(configs, f, indent=2)
return jsonify({'status': 'ok', 'count': len(configs)})
except Exception as e:
return jsonify({'status': 'error', '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)