mirror of
https://github.com/RomySaputraSihananda/ares.git
synced 2026-08-16 20:28:07 +00:00
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:
co-authored by
Claude Sonnet 4.6
parent
dbba172ed3
commit
272fca4f65
+18
-11
@@ -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 08–13 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 08–13 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:00–13: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 (08–13 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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
+26
-7
@@ -2,6 +2,7 @@
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import clsx from "clsx";
|
||||
import { GITHUB_URL } from "@/lib/github";
|
||||
|
||||
const links = [
|
||||
{ href: "/", label: "Dashboard" },
|
||||
@@ -9,10 +10,10 @@ const links = [
|
||||
{ href: "/backtest", label: "Backtest" },
|
||||
];
|
||||
|
||||
export default function Nav() {
|
||||
export default function Nav({ version }: { version?: string | null }) {
|
||||
const path = usePathname();
|
||||
return (
|
||||
<nav className="sticky top-0 z-50" style={{ backgroundColor: 'var(--c-canvas)', borderBottom: '1px solid var(--c-hl)' }}>
|
||||
<nav className="sticky top-0 z-50" style={{ backgroundColor: "var(--c-canvas)", borderBottom: "1px solid var(--c-hl)" }}>
|
||||
<div className="max-w-5xl mx-auto px-4 sm:px-6 h-14 flex items-center gap-6">
|
||||
{/* logo + wordmark */}
|
||||
<Link href="/" className="flex items-center gap-2 group">
|
||||
@@ -21,9 +22,14 @@ export default function Nav() {
|
||||
<path d="M8 19 L13 8 L18 19" stroke="white" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M10.2 15.5 H15.8" stroke="white" strokeWidth="1.8" strokeLinecap="round"/>
|
||||
</svg>
|
||||
<span className="font-mono text-sm font-semibold tracking-widest text-ink group-hover:text-accent transition-colors" style={{ letterSpacing: '0.18em' }}>
|
||||
<span className="font-mono text-sm font-semibold tracking-widest text-ink group-hover:text-accent transition-colors" style={{ letterSpacing: "0.18em" }}>
|
||||
ARES
|
||||
</span>
|
||||
{version && (
|
||||
<span className="hidden sm:inline text-[10px] font-mono text-ink-ter bg-s2 border border-hl px-1.5 py-0.5 rounded">
|
||||
{version}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
{/* nav links */}
|
||||
@@ -44,10 +50,23 @@ export default function Nav() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* live indicator */}
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-bull pulse-dot" />
|
||||
<span className="text-xs text-ink-sub font-medium tracking-eyebrow uppercase">Live</span>
|
||||
{/* right side: github + live */}
|
||||
<div className="ml-auto flex items-center gap-4">
|
||||
<a
|
||||
href={GITHUB_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink-ter hover:text-ink transition-colors"
|
||||
aria-label="GitHub"
|
||||
>
|
||||
<svg width="18" height="18" 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>
|
||||
</a>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-bull pulse-dot" />
|
||||
<span className="text-xs text-ink-sub font-medium tracking-eyebrow uppercase">Live</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
const REPO = "RomySaputraSihananda/ares";
|
||||
|
||||
export async function getLatestVersion(): Promise<string | null> {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://api.github.com/repos/${REPO}/releases/latest`,
|
||||
{
|
||||
headers: { Accept: "application/vnd.github+json" },
|
||||
next: { revalidate: 3600 },
|
||||
}
|
||||
);
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json();
|
||||
return (data.tag_name as string) ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const GITHUB_URL = `https://github.com/${REPO}`;
|
||||
export const GITHUB_REPO = REPO;
|
||||
+5
-2
@@ -107,8 +107,11 @@ export async function getDeals(dateFrom: string, dateTo: string, symbol?: string
|
||||
|
||||
// Fetch all deals from startDate to now by querying one day at a time,
|
||||
// working around the MT5 bridge per-request deal limit.
|
||||
export async function getAllDeals(startDate: string): Promise<Deal[]> {
|
||||
const start = new Date(startDate);
|
||||
// startDate defaults to 90 days ago if omitted.
|
||||
export async function getAllDeals(startDate?: string): Promise<Deal[]> {
|
||||
const start = startDate
|
||||
? new Date(startDate)
|
||||
: (() => { const d = new Date(); d.setUTCDate(d.getUTCDate() - 90); return d; })();
|
||||
const now = new Date();
|
||||
const days: Array<[string, string]> = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user