mql5-compiler / mql-rust /src /ui /backtest_panel.rs
algorembrant's picture
Upload 25 files
59da845 verified
use eframe::egui;
use rfd::FileDialog;
use std::path::PathBuf;
use crate::engine::backtester::BacktestConfig;
pub struct BacktestPanel {
pub initial_deposit_str: String,
pub leverage_str: String,
pub spread_modifier_str: String,
pub selected_symbols: Vec<String>,
pub new_symbol_input: String,
pub loaded_mql5_path: Option<PathBuf>,
pub compiler_log: String,
pub is_running: bool,
}
impl BacktestPanel {
pub fn new() -> Self {
Self {
initial_deposit_str: "10000.0".to_string(),
leverage_str: "100.0".to_string(),
spread_modifier_str: "0".to_string(),
selected_symbols: vec!["EURUSD".to_string()],
new_symbol_input: "".to_string(),
loaded_mql5_path: None,
compiler_log: "Ready.".to_string(),
is_running: false,
}
}
pub fn draw(&mut self, ui: &mut egui::Ui) -> Option<BacktestConfig> {
ui.heading("⚙️ Backtest Configuration");
ui.separator();
// Account Settings
ui.group(|ui| {
ui.label("Account Settings");
egui::Grid::new("config_grid").num_columns(2).spacing([10.0, 5.0]).show(ui, |ui| {
ui.label("Initial Deposit ($):");
ui.text_edit_singleline(&mut self.initial_deposit_str);
ui.end_row();
ui.label("Leverage (1:X):");
ui.text_edit_singleline(&mut self.leverage_str);
ui.end_row();
ui.label("Spread Modifier (points):");
ui.text_edit_singleline(&mut self.spread_modifier_str);
ui.end_row();
});
});
ui.add_space(10.0);
// Symbol Selection
ui.group(|ui| {
ui.label("Symbols for Backtest");
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut self.new_symbol_input);
if ui.button("Add").clicked() {
if !self.new_symbol_input.is_empty() && !self.selected_symbols.contains(&self.new_symbol_input) {
self.selected_symbols.push(self.new_symbol_input.clone());
self.new_symbol_input.clear();
}
}
});
ui.horizontal_wrapped(|ui| {
let mut to_remove = None;
for (i, sym) in self.selected_symbols.iter().enumerate() {
ui.group(|ui| {
ui.horizontal(|ui| {
ui.label(sym);
if ui.small_button("x").clicked() {
to_remove = Some(i);
}
});
});
}
if let Some(i) = to_remove {
self.selected_symbols.remove(i);
}
});
});
ui.add_space(10.0);
// MQL5 File Loading
ui.group(|ui| {
ui.label("MQL5 Strategy Code");
ui.horizontal(|ui| {
if ui.button("📂 Load .mq5 File").clicked() {
if let Some(path) = FileDialog::new()
.add_filter("MQL5 Source", &["mq5"])
.pick_file()
{
self.loaded_mql5_path = Some(path);
self.compiler_log = "File loaded. Ready to compile.".to_string();
}
}
if let Some(p) = &self.loaded_mql5_path {
ui.label(p.display().to_string());
} else {
ui.label("No file selected.");
}
});
});
ui.add_space(10.0);
// Start button
ui.horizontal(|ui| {
if self.is_running {
ui.add_enabled(false, egui::Button::new("Running Backtest..."));
} else {
if ui.button("🚀 Compile & Run Backtest").clicked() {
if self.loaded_mql5_path.is_none() {
self.compiler_log = "Error: Please load an .mq5 file first.".to_string();
return None;
}
if self.selected_symbols.is_empty() {
self.compiler_log = "Error: Please add at least one symbol.".to_string();
return None;
}
if let (Ok(dep), Ok(lev), Ok(spr)) = (
self.initial_deposit_str.parse::<f64>(),
self.leverage_str.parse::<f64>(),
self.spread_modifier_str.parse::<u32>(),
) {
self.compiler_log = "Starting compilation...".to_string();
self.is_running = true;
return Some(BacktestConfig {
initial_deposit: dep,
leverage: lev,
spread_modifier: spr,
});
} else {
self.compiler_log = "Error: Invalid numeric configuration fields.".to_string();
}
}
}
});
ui.add_space(5.0);
// Logs
ui.group(|ui| {
ui.set_min_height(60.0);
ui.label(&self.compiler_log);
});
None
}
}