feat(agent): add strategy quality introspection

Log full strategy JSON at debug level, show full anyhow cause chain on
submit failures, surface condition_audit_summary for 0-trade results in
both logs and the summary fed back to the AI each iteration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 12:58:49 +02:00
parent deb28f6714
commit fc9b7e094a
2 changed files with 55 additions and 5 deletions

View File

@@ -82,7 +82,7 @@ impl BacktestResult {
self.error_message.as_deref().unwrap_or("unknown error")
);
}
format!(
let mut s = format!(
"[{}] trades={} win_rate={:.1}% pf={:.2} net_pnl={:.2} sharpe={:.2} avg_bars={:.1}",
self.instrument,
self.total_positions.unwrap_or(0),
@@ -91,7 +91,17 @@ impl BacktestResult {
self.net_pnl.unwrap_or(0.0),
self.sharpe_ratio.unwrap_or(0.0),
self.avg_bars_in_trade.unwrap_or(0.0),
)
);
if self.total_positions.unwrap_or(0) == 0 {
if let Some(audit) = &self.condition_audit_summary {
let audit_str = format_audit_summary(audit);
if !audit_str.is_empty() {
s.push_str(" | audit: ");
s.push_str(&audit_str);
}
}
}
s
}
/// Is this result promising enough to warrant out-of-sample validation?
@@ -103,6 +113,35 @@ impl BacktestResult {
}
}
/// Render a condition_audit_summary Value into a compact one-line string.
/// Handles both object and array shapes we might receive from the API.
fn format_audit_summary(audit: &Value) -> String {
match audit {
Value::Object(map) => map
.iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(", "),
Value::Array(arr) => arr
.iter()
.filter_map(|item| {
let name = item.get("name").or_else(|| item.get("condition"))?.as_str()?;
// Try common field names for hit counts
if let (Some(true_count), Some(total)) = (
item.get("true_count").or_else(|| item.get("hit_count")).or_else(|| item.get("true_bars")).and_then(|v| v.as_u64()),
item.get("total").or_else(|| item.get("total_bars")).or_else(|| item.get("evaluated")).and_then(|v| v.as_u64()),
) {
Some(format!("{name}: {true_count}/{total}"))
} else {
Some(format!("{name}: {item}"))
}
})
.collect::<Vec<_>>()
.join(", "),
other => other.to_string(),
}
}
impl SwymClient {
pub fn new(base_url: &str) -> Result<Self> {
let client = Client::builder()