feat(ui): strategy config form and backend endpoint

This commit is contained in:
2026-02-07 08:06:27 +08:00
parent f30bcdc680
commit 06081aa939
3 changed files with 102 additions and 15 deletions

28
app.py
View File

@@ -5,7 +5,7 @@ Provides mock metrics for PnL, drawdown, and win rate.
"""
from flask import Flask, render_template, jsonify, request
import random
import os
from datetime import datetime, timedelta
from data.connectors import create_moomoo_client, Interval
@@ -14,6 +14,8 @@ 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."""
@@ -91,6 +93,30 @@ def market_data(symbol):
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."""

View File

@@ -121,13 +121,50 @@ function setupEventListeners() {
}
});
// Form submissions
// Strategy config form submission (real backend call)
const strategyForm = document.getElementById('strategy-config-form');
if (strategyForm) {
strategyForm.addEventListener('submit', function(e) {
e.preventDefault();
const payload = {
name: document.getElementById('strategy-name').value,
type: document.getElementById('strategy-type').value,
initialCapital: Number(document.getElementById('initial-capital').value),
entryLookback: Number(document.getElementById('entry-lookback').value),
exitLookback: Number(document.getElementById('exit-lookback').value),
riskPerTrade: Number(document.getElementById('risk-per-trade').value),
maxPositions: Number(document.getElementById('max-positions').value),
notes: document.getElementById('strategy-notes').value,
};
fetch('/api/strategy-config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
.then(res => res.json())
.then(data => {
if (data.status === 'ok') {
showToast('Strategy configuration saved', 'success');
} else {
showToast('Failed to save configuration', 'danger');
}
})
.catch(err => {
console.error('Error saving strategy config', err);
showToast('Error saving configuration', 'danger');
});
});
}
// Generic form submissions (fallback)
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
e.preventDefault();
showToast('Settings saved successfully', 'success');
});
if (form.id !== 'strategy-config-form') {
form.addEventListener('submit', function(e) {
e.preventDefault();
showToast('Settings saved successfully', 'success');
});
}
});
// Toggle buttons

View File

@@ -560,29 +560,53 @@
<h6 class="mb-0">Create New Strategy</h6>
</div>
<div class="card-body">
<form>
<form id="strategy-config-form">
<div class="mb-3">
<label class="form-label">Strategy Name</label>
<input type="text" class="form-control bg-dark border-secondary-dark text-light" placeholder="e.g., Momentum Breakout">
<input id="strategy-name" type="text" class="form-control bg-dark border-secondary-dark text-light" placeholder="e.g., Momentum Breakout" required>
</div>
<div class="mb-3">
<label class="form-label">Strategy Type</label>
<select class="form-select bg-dark border-secondary-dark text-light">
<option>Trend Following</option>
<option>Mean Reversion</option>
<option>Arbitrage</option>
<option>Machine Learning</option>
<select id="strategy-type" class="form-select bg-dark border-secondary-dark text-light">
<option value="trend">Trend Following</option>
<option value="mean-reversion">Mean Reversion</option>
<option value="arbitrage">Arbitrage</option>
<option value="ml">Machine Learning</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Initial Capital</label>
<div class="input-group">
<span class="input-group-text bg-dark border-secondary-dark text-light">$</span>
<input type="number" class="form-control bg-dark border-secondary-dark text-light" value="100000">
<input id="initial-capital" type="number" class="form-control bg-dark border-secondary-dark text-light" value="100000" min="1000" step="1000" required>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<label class="form-label">Entry Lookback (bars)</label>
<input id="entry-lookback" type="number" class="form-control bg-dark border-secondary-dark text-light" value="20" min="1">
</div>
<div class="col-md-6">
<label class="form-label">Exit Lookback (bars)</label>
<input id="exit-lookback" type="number" class="form-control bg-dark border-secondary-dark text-light" value="50" min="1">
</div>
</div>
<div class="row mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<label class="form-label">Risk per Trade (%)</label>
<input id="risk-per-trade" type="number" class="form-control bg-dark border-secondary-dark text-light" value="1" min="0.1" max="5" step="0.1">
</div>
<div class="col-md-6">
<label class="form-label">Max Positions</label>
<input id="max-positions" type="number" class="form-control bg-dark border-secondary-dark text-light" value="5" min="1">
</div>
</div>
<div class="mb-3">
<label class="form-label">Notes</label>
<textarea id="strategy-notes" class="form-control bg-dark border-secondary-dark text-light" rows="2" placeholder="Optional notes about this configuration"></textarea>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Create & Backtest</button>
<button type="submit" class="btn btn-primary">Save Configuration</button>
</div>
</form>
</div>