ecf5606b7a
- fix: inactivity watchdog now waits 30s for HTML report then kills terminal64.exe unconditionally — ShutdownTerminal=1 does not cause MT5 to exit on Wine/macOS; relying on natural exit caused all runs to fall back to journal extraction - fix: tester agent log deduplication — each deal is written twice in the log; use HashSet to skip already-seen deal numbers - fix: position tracker for entry direction inference — infer "in"/"out" from per-symbol signed lot accumulation instead of guessing - fix: is_closed_trade() no longer gates on profit!=0.0 — journal deals have profit=0.0 legitimately; the old gate hid all 140 deals from list_deals - fix: log file selection priority — prefer Agent-127.0.0.1 over Agent-0.0.0.0 (startup-only log), tiebreak by file size - feat: launch_backtest default shutdown=true; inactivity_kill_secs default 120s exposed as tool parameter - feat: report DB stores deals in SQLite; analytics resolve by report_id / report_dir / latest Verified: 1-month DPS21/XAUUSD.cent/M5 backtest produces full HTML report with all metrics (win_rate=70%, profit_factor=0.77, sharpe=-3.61, max_dd=6.93%) and all 17 analytics tools returning real data. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
2.6 KiB
Rust
111 lines
2.6 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use std::path::PathBuf;
|
|
|
|
// Re-export chrono for BacktestJob
|
|
use chrono;
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Report {
|
|
pub report_dir: PathBuf,
|
|
pub expert: String,
|
|
pub symbol: String,
|
|
pub timeframe: String,
|
|
pub from_date: String,
|
|
pub to_date: String,
|
|
pub metrics_file: PathBuf,
|
|
pub analysis_file: Option<PathBuf>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PipelineMetadata {
|
|
pub expert: String,
|
|
pub symbol: String,
|
|
pub timeframe: String,
|
|
pub from_date: String,
|
|
pub to_date: String,
|
|
pub deposit: f64,
|
|
pub currency: String,
|
|
pub model: i32,
|
|
pub leverage: i32,
|
|
pub set_file: Option<String>,
|
|
pub report_dir: String,
|
|
pub duration_seconds: i64,
|
|
pub files: FilePaths,
|
|
#[serde(default)]
|
|
pub no_trades: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FilePaths {
|
|
pub metrics: String,
|
|
pub analysis: String,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BacktestStatus {
|
|
pub stage: PipelineStage,
|
|
pub elapsed_seconds: i64,
|
|
pub is_complete: bool,
|
|
pub message: String,
|
|
pub report_dir: Option<String>,
|
|
pub mt5_running: Option<bool>,
|
|
pub report_found: Option<bool>,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "UPPERCASE")]
|
|
pub enum PipelineStage {
|
|
Compile,
|
|
Clean,
|
|
Backtest,
|
|
Extract,
|
|
Analyze,
|
|
Done,
|
|
Failed,
|
|
}
|
|
|
|
/// Track a running backtest job for fire-and-poll pattern
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BacktestJob {
|
|
pub report_id: String,
|
|
pub report_dir: String,
|
|
pub expert: String,
|
|
pub symbol: String,
|
|
pub timeframe: String,
|
|
pub launched_at: String,
|
|
pub mt5_pid: Option<u32>,
|
|
pub expected_report_path: String,
|
|
pub timeout_seconds: u64,
|
|
/// Set by background monitor: "running"|"completed"|"completed_no_html"|"failed"|"timeout"
|
|
#[serde(default)]
|
|
pub status: Option<String>,
|
|
}
|
|
|
|
impl BacktestJob {
|
|
pub fn new(
|
|
report_id: String,
|
|
report_dir: String,
|
|
expert: String,
|
|
symbol: String,
|
|
timeframe: String,
|
|
expected_report_path: String,
|
|
timeout_seconds: u64,
|
|
) -> Self {
|
|
Self {
|
|
report_id,
|
|
report_dir,
|
|
expert,
|
|
symbol,
|
|
timeframe,
|
|
launched_at: chrono::Utc::now().to_rfc3339(),
|
|
mt5_pid: None,
|
|
expected_report_path,
|
|
timeout_seconds,
|
|
status: Some("running".to_string()),
|
|
}
|
|
}
|
|
}
|