feat: session filter, breakeven SL, daily loss limit, startup alert, multi-pair web

Bot improvements:
- Session filter (SESSION_FROM_UTC/TO_UTC) — backtest confirms 08-13 UTC optimal for XAU
- Breakeven SL management (BREAKEVEN_AT_RR) — disabled by default, hurts XAU momentum
- Daily loss limit circuit breaker (DAILY_LOSS_LIMIT_PCT)
- Telegram startup alert with symbol, session, risk, and balance
- MT5 modify_position (TRADE_ACTION_SLTP) support in mt5-client

Web updates:
- Version badge auto-fetched from GitHub Releases API
- GitHub icon link in Nav and footer
- Multi-pair general (not XAUUSDm-specific)
- BTCUSDm backtest results added, session params in params table
- Trades page uses rolling 90-day window instead of hardcoded date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
romysaputrasihananda
2026-06-12 00:48:45 +07:00
co-authored by Claude Sonnet 4.6
parent dbba172ed3
commit 272fca4f65
13 changed files with 431 additions and 103 deletions
+17
View File
@@ -97,6 +97,23 @@ impl Mt5Client {
Ok(w.data)
}
pub async fn modify_position(
&self,
ticket: u64,
symbol: &str,
sl: f64,
tp: f64,
) -> Result<TradeResult, Mt5Error> {
let req = TradeRequest::modify_sltp(symbol, ticket, sl, tp);
#[derive(serde::Serialize)]
struct Body<'a> { request: &'a TradeRequest }
let url = format!("{}/order/send", self.base_url);
let text = self.fetch_text(self.http.post(&url).json(&Body { request: &req })).await?;
tracing::debug!(endpoint = %url, ticket, "modify position ok");
let w: DataOne<TradeResult> = serde_json::from_str(&text)?;
Ok(w.data)
}
pub async fn cancel_order(&self, ticket: u64, symbol: &str) -> Result<TradeResult, Mt5Error> {
let req = TradeRequest::cancel(symbol, ticket);
#[derive(serde::Serialize)]
+23
View File
@@ -31,6 +31,9 @@ pub struct TradeRequest {
/// Ticket number — required for TRADE_ACTION_REMOVE (cancel pending order)
#[serde(skip_serializing_if = "Option::is_none")]
pub order: Option<u64>,
/// Position ticket — required for TRADE_ACTION_SLTP (modify SL/TP)
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<u64>,
/// Allowed price deviation in points
#[serde(skip_serializing_if = "Option::is_none")]
pub deviation: Option<u32>,
@@ -63,6 +66,25 @@ impl TradeRequest {
magic: Some(magic),
comment: Some(comment.into()),
order: None,
position: None,
deviation: None,
}
}
pub fn modify_sltp(symbol: impl Into<String>, position: u64, sl: f64, tp: f64) -> Self {
// TRADE_ACTION_SLTP = 6
Self {
action: 6,
symbol: symbol.into(),
volume: None,
order_type: None,
price: None,
sl: Some(sl),
tp: Some(tp),
magic: None,
comment: None,
order: None,
position: Some(position),
deviation: None,
}
}
@@ -81,6 +103,7 @@ impl TradeRequest {
magic: None,
comment: None,
order: Some(ticket),
position: None,
deviation: None,
}
}