diff --git a/src/bin/sim.rs b/src/bin/sim.rs
index 6fcffc5..8e41180 100644
--- a/src/bin/sim.rs
+++ b/src/bin/sim.rs
@@ -16,26 +16,30 @@ async fn main() -> Result<()> {
let tg = TelegramConfig { token, chat_id, thread_id };
let http = Client::new();
- // 1. Pending
- tracing::info!("step 1: PENDING");
- let msg_id = tg.send(&http,
- "๐ก PENDING\nXAUUSDm Long\nEntry: 3325.50\nSL: 3320.00 TP: 3333.75\nVol: 0.03 lot RR: 1.5",
+ // โโ Flow: pending โ filled โ TP hit โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ let msg = tg.send(&http,
+ "๐ก PENDING\nXAUUSDm ยท Long\n\nEntry 3325.50\nTP 3333.75\nSL 3320.00\nVol 0.03 lot ยท RR 1:1.5"
).await?;
- tracing::info!(msg_id, "sent");
+ tracing::info!(msg_id = msg, "PENDING sent");
sleep(Duration::from_secs(3)).await;
- // 2. Filled
- tracing::info!("step 2: FILLED");
- tg.edit(&http, msg_id,
- "โก FILLED\nXAUUSDm Long\nEntry: 3325.50\nSL: 3320.00 TP: 3333.75\nVol: 0.03 lot",
+ tg.edit(&http, msg,
+ "โก FILLED\nXAUUSDm ยท Long\n\nEntry 3325.50\nTP 3333.75\nSL 3320.00\nVol 0.03 lot"
).await?;
+ tracing::info!(msg_id = msg, "FILLED edited");
sleep(Duration::from_secs(3)).await;
- // 3. Result
- tracing::info!("step 3: TP HIT");
- tg.edit(&http, msg_id,
- "โ
TP HIT +12.68\nXAUUSDm Long\n3325.50 โ 3333.75\nVol: 0.03 lot\nBal: $5012.68",
+ tg.edit(&http, msg,
+ "โ
TAKE PROFIT +12.68\nXAUUSDm ยท Long\n\n3325.50 โ 3333.75\nVol 0.03 lot ยท Bal $5012.68"
).await?;
+ tracing::info!(msg_id = msg, "TP HIT edited");
+ sleep(Duration::from_secs(3)).await;
+
+ // โโ PnL summary โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ tg.send(&http,
+ "๐ PnL Summary\n\nToday ยท XAUUSDm ยท 2026-06-11\n2 trades ยท 1W 1L ยท WR 50%\nNet +$5.18\n\nAll-time\n17 trades ยท 10W 7L ยท WR 59%\nNet +$482.41"
+ ).await?;
+ tracing::info!("PnL summary sent");
println!("\nDone โ
check Telegram");
Ok(())
diff --git a/src/live.rs b/src/live.rs
index 0245057..d7b765e 100644
--- a/src/live.rs
+++ b/src/live.rs
@@ -246,9 +246,9 @@ async fn tick(
}
if let (Some(tg), Some(msg_id)) = (&cfg.telegram, state.tg_message_id) {
let text = format!(
- "โฑ EXPIRED\n{} {}\nEntry: {:.5}\nNo fill after {} candles",
- symbol, state.side, state.entry,
- cfg.fvg_expiry_candles,
+ "โฑ EXPIRED\n{} ยท {}\n\nEntry {}\nUnfilled after {} candles",
+ symbol, state.side,
+ fp(state.entry), cfg.fvg_expiry_candles,
);
let _ = tg.edit(http, msg_id, &text).await;
}
@@ -353,9 +353,9 @@ async fn tick(
// send Telegram "Pending" message
let tg_msg_id = if let Some(tg) = &cfg.telegram {
let text = format!(
- "๐ก PENDING\n{} {}\nEntry: {}\nSL: {} TP: {}\nVol: {} lot RR: {:.1}",
+ "๐ก PENDING\n{} ยท {}\n\nEntry {}\nTP {}\nSL {}\nVol {} lot ยท RR 1:{:.1}",
symbol, side_str,
- fmt_price(fvg.entry, prec), fmt_price(sl, prec), fmt_price(tp, prec),
+ fmt_price(fvg.entry, prec), fmt_price(tp, prec), fmt_price(sl, prec),
volume, rr,
);
match tg.send(http, &text).await {
@@ -493,8 +493,9 @@ async fn on_position_opened(
if let (Some(tg), Some(msg_id)) = (tg, tg_msg_id) {
let text = format!(
- "โก FILLED\n{} {}\nEntry: {:.5}\nSL: {:.5} TP: {:.5}\nVol: {:.2} lot",
- symbol, ps.side, entry, sl, tp, volume,
+ "โก FILLED\n{} ยท {}\n\nEntry {}\nTP {}\nSL {}\nVol {:.2} lot",
+ symbol, ps.side,
+ fp(entry), fp(tp), fp(sl), volume,
);
let _ = tg.edit(http, msg_id, &text).await;
}
@@ -516,7 +517,7 @@ async fn on_position_closed(
let Some(tg) = tg else { return };
let Some(msg_id) = ps.as_ref().and_then(|s| s.tg_message_id) else { return };
- let (icon, label) = if profit >= 0.0 { ("โ
", "TP HIT") } else { ("โ", "SL HIT") };
+ let (icon, label) = if profit >= 0.0 { ("โ
", "TAKE PROFIT") } else { ("โ", "STOP LOSS") };
let exit = payload.price_current.unwrap_or(0.0);
let entry = ps.as_ref().map(|s| s.entry).unwrap_or(payload.price_open.unwrap_or(0.0));
let volume = payload.volume.unwrap_or(ps.as_ref().map(|s| s.volume).unwrap_or(0.0));
@@ -525,11 +526,11 @@ async fn on_position_closed(
let acct_bal = mt5.account().await.ok().map(|a| a.balance).unwrap_or(Decimal::ZERO);
let text = format!(
- "{} {} {:+.2}\n{} {}\n{:.5} โ {:.5}\nVol: {:.2} lot\nBal: ${}",
+ "{} {} {:+.2}\n{} ยท {}\n\n{} โ {}\nVol {:.2} lot ยท Bal ${:.2}",
icon, label, profit,
symbol, side_str,
- entry, exit,
- volume, acct_bal,
+ fp(entry), fp(exit),
+ volume, d2f(acct_bal),
);
let _ = tg.edit(http, msg_id, &text).await;
}
@@ -550,8 +551,9 @@ async fn on_position_modified(
if let (Some(tg), Some(msg_id)) = (tg, ps.tg_message_id) {
let text = format!(
- "๐ MODIFIED\n{} {}\nEntry: {:.5}\nSL: {:.5} TP: {:.5}\nVol: {:.2} lot",
- symbol, ps.side, ps.entry, ps.sl, ps.tp, ps.volume,
+ "๐ MODIFIED\n{} ยท {}\n\nEntry {}\nTP {}\nSL {}\nVol {:.2} lot",
+ symbol, ps.side,
+ fp(ps.entry), fp(ps.tp), fp(ps.sl), ps.volume,
);
let _ = tg.edit(http, msg_id, &text).await;
}
@@ -573,8 +575,8 @@ async fn on_order_cancelled(
let side_str = state.as_ref().map(|s| s.side.as_str()).unwrap_or("?");
let entry = state.as_ref().map(|s| s.entry).unwrap_or(0.0);
let text = format!(
- "๐ซ CANCELLED\n{} {}\nEntry: {:.5}",
- symbol, side_str, entry,
+ "๐ซ CANCELLED\n{} ยท {}\n\nEntry {}",
+ symbol, side_str, fp(entry),
);
let _ = tg.edit(http, msg_id, &text).await;
}
@@ -598,8 +600,9 @@ async fn on_order_modified(
if let (Some(tg), Some(msg_id)) = (tg, state.tg_message_id) {
let text = format!(
- "๐ ORDER MODIFIED\n{} {}\nEntry: {:.5}\nSL: {:.5} TP: {:.5}",
- symbol, state.side, state.entry, state.sl, state.tp,
+ "๐ ORDER MODIFIED\n{} ยท {}\n\nEntry {}\nTP {}\nSL {}",
+ symbol, state.side,
+ fp(state.entry), fp(state.tp), fp(state.sl),
);
let _ = tg.edit(http, msg_id, &text).await;
}
@@ -624,27 +627,27 @@ async fn send_pnl_summary(
mt5.history_deals(&epoch_str, &now_str, Some(symbol)),
)?;
- let summary_text = |label: &str, deals: &[domain::Deal]| {
+ let summary_text = |deals: &[domain::Deal]| {
let closing: Vec<_> = deals.iter()
.filter(|d| d.entry == 1 && d.magic == MAGIC)
.collect();
let total = closing.len();
let wins = closing.iter().filter(|d| d.profit > Decimal::ZERO).count();
let losses = total - wins;
- let profit: Decimal = closing.iter().map(|d| d.profit + d.commission + d.swap).sum();
+ let net: Decimal = closing.iter().map(|d| d.profit + d.commission + d.swap).sum();
let wr = if total > 0 { wins as f64 / total as f64 * 100.0 } else { 0.0 };
format!(
- "{}\nTrades: {} ({}W / {}L) WR: {:.0}%\nProfit: ${:+.2}",
- label, total, wins, losses, wr, profit,
+ "{} trades ยท {}W {}L ยท WR {:.0}%\nNet {:+.2}",
+ total, wins, losses, wr, net,
)
};
let today_date = now.format("%Y-%m-%d").to_string();
let text = format!(
- "๐ Today โ {} {}\n{}\n\n๐ All-time\n{}",
+ "๐ PnL Summary\n\nToday ยท {} ยท {}\n{}\n\nAll-time\n{}",
symbol, today_date,
- summary_text("", &today_deals),
- summary_text("", &all_deals),
+ summary_text(&today_deals),
+ summary_text(&all_deals),
);
tg.send(http, &text).await?;
@@ -653,6 +656,12 @@ async fn send_pnl_summary(
// โโ helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+fn fp(v: f64) -> String {
+ let s = format!("{v:.5}");
+ let s = s.trim_end_matches('0');
+ s.trim_end_matches('.').to_string()
+}
+
fn timeframe_minutes(tf: Timeframe) -> i64 {
match tf {
Timeframe::M1 => 1,