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
+18 -11
View File
@@ -3,22 +3,27 @@ import Link from "next/link";
export const metadata: Metadata = {
title: "Backtest Results",
description: "ARES backtest results: M5 Momentum FVG scalper on XAUUSDm. Best run: PF 1.42, +43.2% net return, 11.5% max drawdown.",
description: "ARES backtest results: M5 Momentum FVG scalper on XAUUSDm and BTCUSDm. London session filter, EMA-20 trend filter.",
};
const results = [
{ period: "1 Month", tf: "M5", risk: "1%", trades: 159, wr: 55.3, pf: 1.42, ret: 43.2, dd: -11.5, highlight: true },
{ period: "1 Month", tf: "M5", risk: "5%", trades: 159, wr: 55.3, pf: 1.26, ret: 390, dd: -156, highlight: false },
{ period: "1 Week", tf: "M5", risk: "1%", trades: 34, wr: 47.1, pf: 0.94, ret: -6.2, dd: -8.1, highlight: false },
{ period: "Yesterday", tf: "M1", risk: "1%", trades: 36, wr: 47.2, pf: 1.05, ret: 6.6, dd: -51, highlight: false },
{ period: "Yesterday", tf: "M5", risk: "1%", trades: 3, wr: 66.7, pf: null, ret: null, dd: null, highlight: false, note: "Too few trades" },
const results: Array<{
symbol: string; period: string; tf: string; risk: string;
trades: number | null; wr: number; pf: number | null; ret: number | null; dd: number | null;
highlight: boolean; note?: string;
}> = [
{ symbol: "XAUUSDm", period: "1 Month", tf: "M5", risk: "1%", trades: 159, wr: 55.3, pf: 1.42, ret: 43.2, dd: -11.5, highlight: true },
{ symbol: "XAUUSDm", period: "50k bars", tf: "M5", risk: "5%", trades: 1421, wr: 50.3, pf: 1.17, ret: null, dd: null, highlight: false, note: "Session 0813 UTC" },
{ symbol: "BTCUSDm", period: "50k bars", tf: "M5", risk: "5%", trades: null, wr: 56.9, pf: 1.11, ret: null, dd: null, highlight: false, note: "Session 0813 UTC" },
{ symbol: "XAUUSDm", period: "1 Month", tf: "M5", risk: "5%", trades: 159, wr: 55.3, pf: 1.26, ret: 390, dd: -156, highlight: false },
{ symbol: "XAUUSDm", period: "1 Week", tf: "M5", risk: "1%", trades: 34, wr: 47.1, pf: 0.94, ret: -6.2, dd: -8.1, highlight: false },
];
const params = [
["Timeframe", "M5"],
["Symbol", "XAUUSDm"],
["Symbols", "XAUUSDm · BTCUSDm"],
["Session", "08:0013:00 UTC"],
["EMA Period", "20"],
["Min FVG Pips", "3"],
["Min FVG Pips", "1"],
["Min SL Pips", "5"],
["Min RR", "1.5×"],
["FVG Expiry", "10 candles"],
@@ -36,6 +41,7 @@ export default function BacktestPage() {
</h1>
<p className="text-[15px] text-ink-sub max-w-xl">
Historical simulation on real MT5 tick data. Includes spread costs, commission, and slippage.
London session filter (0813 UTC) applied.
</p>
</section>
@@ -74,7 +80,7 @@ export default function BacktestPage() {
<table className="data-table">
<thead>
<tr>
{["Period", "TF", "Risk", "Trades", "Win Rate", "Profit Factor", "Return", "Max DD", ""].map(h => (
{["Symbol", "Period", "TF", "Risk", "Trades", "Win Rate", "Profit Factor", "Return", "Max DD", ""].map(h => (
<th key={h}>{h}</th>
))}
</tr>
@@ -82,10 +88,11 @@ export default function BacktestPage() {
<tbody>
{results.map((r, i) => (
<tr key={i} className={r.highlight ? "bg-s2" : ""}>
<td className="font-mono font-medium text-ink">{r.symbol}</td>
<td className="font-medium text-ink">{r.period}</td>
<td className="font-mono text-ink-sub">{r.tf}</td>
<td className="font-mono text-ink-sub">{r.risk}</td>
<td className="font-mono text-ink-md">{r.trades}</td>
<td className="font-mono text-ink-md">{r.trades ?? "—"}</td>
<td className="font-mono text-ink-md">{r.wr.toFixed(1)}%</td>
<td className="font-mono font-medium">
{r.pf != null
+36 -12
View File
@@ -1,21 +1,22 @@
import type { Metadata } from "next";
import "./globals.css";
import Nav from "@/components/Nav";
import { getLatestVersion, GITHUB_URL } from "@/lib/github";
const SITE_URL = "https://ares.romys.my.id";
export const metadata: Metadata = {
metadataBase: new URL(SITE_URL),
title: {
default: "ARES — Automated Gold Trading Bot",
default: "ARES — Algorithmic Trading Bot",
template: "%s · ARES",
},
description:
"Live forward-test results for ARES, an M5 Momentum FVG scalper built in Rust. Trades XAUUSDm with EMA-20 trend filter and automated risk management.",
"Open-source algorithmic trading bot built in Rust. Momentum FVG scalper with EMA trend filter, automated risk management, and live forward-test results.",
keywords: [
"algorithmic trading", "gold trading bot", "XAUUSD EA",
"algorithmic trading", "trading bot", "open source", "XAUUSD",
"MT5 expert advisor", "Rust trading bot", "FVG scalper",
"forex robot", "automated trading", "ICT strategy",
"forex robot", "automated trading", "momentum strategy",
],
authors: [{ name: "Romy Saputra Sihananda" }],
creator: "Romy Saputra Sihananda",
@@ -28,14 +29,14 @@ export const metadata: Metadata = {
type: "website",
url: SITE_URL,
siteName: "ARES Trading Bot",
title: "ARES — Automated Gold Trading Bot",
description: "Live forward-test · M5 Momentum FVG scalper · XAUUSDm · Built in Rust",
title: "ARES — Algorithmic Trading Bot",
description: "Open-source Momentum FVG scalper built in Rust · Live forward-test · MT5",
locale: "en_US",
},
twitter: {
card: "summary_large_image",
title: "ARES — Automated Gold Trading Bot",
description: "Live forward-test · M5 Momentum FVG scalper · XAUUSDm · Built in Rust",
title: "ARES — Algorithmic Trading Bot",
description: "Open-source Momentum FVG scalper built in Rust · Live forward-test · MT5",
},
icons: {
icon: "/favicon.svg",
@@ -46,11 +47,13 @@ export const metadata: Metadata = {
},
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const version = await getLatestVersion();
return (
<html lang="en">
<body className="min-h-screen antialiased">
<Nav />
<Nav version={version} />
<main className="max-w-5xl mx-auto px-4 sm:px-6 py-12">
{children}
</main>
@@ -64,10 +67,26 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<path d="M10.2 15.5 H15.8" stroke="white" strokeWidth="1.8" strokeLinecap="round"/>
</svg>
<span className="font-mono text-sm font-semibold" style={{ letterSpacing: "0.18em", color: "var(--c-ink)" }}>ARES</span>
{version && (
<span className="text-[10px] font-mono text-ink-ter bg-s2 border border-hl px-1.5 py-0.5 rounded">
{version}
</span>
)}
</div>
<p className="text-sm leading-relaxed" style={{ color: "var(--c-ink-sub)" }}>
M5 Momentum FVG Scalper<br />Built in Rust · XAUUSDm
<p className="text-sm leading-relaxed mb-3" style={{ color: "var(--c-ink-sub)" }}>
Open-source algorithmic trading bot<br />built in Rust · M5 Momentum FVG
</p>
<a
href={GITHUB_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 text-xs text-ink-sub hover:text-ink transition-colors"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/>
</svg>
View on GitHub
</a>
</div>
<div>
<p className="eyebrow mb-4">Navigation</p>
@@ -75,6 +94,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<li><a href="/" className="hover:text-ink transition-colors">Dashboard</a></li>
<li><a href="/trades" className="hover:text-ink transition-colors">Trades</a></li>
<li><a href="/backtest" className="hover:text-ink transition-colors">Backtest</a></li>
<li>
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="hover:text-ink transition-colors">
GitHub
</a>
</li>
</ul>
</div>
<div>
+3 -2
View File
@@ -11,8 +11,9 @@ export default function DashboardPage() {
ARES Trading Bot
</h1>
<p className="text-[18px] text-ink-md leading-relaxed max-w-2xl mb-8">
M5 Momentum FVG scalper built in Rust. Targets Fair Value Gaps on XAUUSDm
with EMA-20 trend filter, automatic position sizing, and Telegram alerts.
Open-source algorithmic trading bot built in Rust. Momentum FVG scalper
with EMA trend filter, configurable session window, automatic position
sizing, and Telegram alerts runs on any MT5 symbol.
</p>
<div className="flex gap-3 flex-wrap">
<Link href="/trades" className="btn-primary">View Trades</Link>
+2 -2
View File
@@ -6,7 +6,7 @@ import TradesRefresher from "@/components/TradesRefresher";
export const metadata: Metadata = {
title: "Trade History",
description: "Live closed trades and equity curve for ARES — M5 Momentum FVG scalper on XAUUSDm.",
description: "Live closed trades and equity curve for ARES — M5 Momentum FVG scalper on XAUUSDm and BTCUSDm.",
};
export const dynamic = "force-dynamic";
@@ -20,7 +20,7 @@ export default async function TradesPage() {
try {
[account, deals] = await Promise.all([
getAccount(),
getAllDeals("2026-06-10T00:00:00"),
getAllDeals(),
]);
} catch { error = true; }