File size: 5,523 Bytes
59da845 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | 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
}
}
|