feat(web): v1.4.0 UI — backtest results, trade stats, dot grid background

backtest page:
- equity progression SVG chart ($5k → $27,439)
- filter impact cards (ATR gate, entry zone 75%, session)
- v1.4.0 results: PF 2.08, +448.7%, WR 62.7%

trades page:
- avg win/loss, friction, streak tiles
- win/loss distribution bar (visual green/red)
- best trade + worst trade highlight cards

home page:
- 4 strategy cards (added ATR Quality Gate)
- backtest CTA updated to v1.4.0 numbers with v1.3.0 comparison

globals/layout:
- dot grid background pattern
- top accent glow (purple radial)
- edge vignette fade

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
romysaputrasihananda
2026-06-12 02:41:44 +07:00
co-authored by Claude Sonnet 4.6
parent 2c02589521
commit 31b95808b1
5 changed files with 382 additions and 70 deletions
+200 -33
View File
@@ -3,35 +3,62 @@ import Link from "next/link";
export const metadata: Metadata = {
title: "Backtest Results",
description: "ARES backtest results: M5 Momentum FVG scalper on XAUUSDm and BTCUSDm. London session filter, EMA-20 trend filter.",
description: "ARES v1.4.0 backtest results: M5 Momentum FVG scalper on XAUUSDm. ATR quality filter, EMA-20 trend filter, entry zone 75%, London session.",
};
const results: Array<{
symbol: string; period: string; tf: string; risk: string;
symbol: string; period: string; tf: string; risk: string; version: 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 },
{ symbol: "XAUUSDm", period: "50k bars (~8 mo)", tf: "M5", risk: "5%", version: "v1.4.0", trades: 83, wr: 62.7, pf: 2.08, ret: 448.7, dd: null, highlight: true, note: "Session 0813 UTC" },
{ symbol: "XAUUSDm", period: "2025 Full Year", tf: "M5", risk: "5%", version: "v1.4.0", trades: 26, wr: 65.4, pf: 2.08, ret: 78.8, dd: null, highlight: false, note: "Session 0813 UTC" },
{ symbol: "XAUUSDm", period: "50k bars (~8 mo)", tf: "M5", risk: "1%", version: "v1.4.0", trades: 59, wr: 61.0, pf: 1.93, ret: 25.4, dd: -4.8, highlight: false, note: "Session 0813 UTC" },
{ symbol: "XAUUSDm", period: "50k bars (~8 mo)", tf: "M5", risk: "5%", version: "v1.3.0", trades: 224, wr: 50.0, pf: 1.17, ret: 174.7, dd: null, highlight: false, note: "No ATR filter" },
];
const params = [
["Timeframe", "M5"],
["Symbols", "XAUUSDm · BTCUSDm"],
["Session", "08:0013:00 UTC"],
["EMA Period", "20"],
["Min FVG Pips", "1"],
["Min SL Pips", "5"],
["Min RR", "1.5×"],
["FVG Expiry", "10 candles"],
["Body PCT Min", "60%"],
["Close PCT Min", "80%"],
["Timeframe", "M5"],
["Symbol", "XAUUSDm"],
["Session", "08:0013:00 UTC"],
["EMA Period", "20"],
["ATR FVG Filter", "≥ 0.3× ATR(14)"],
["ATR Impulse Filter","≥ 1.0× ATR(14)"],
["Entry Zone", "75% into FVG"],
["Min RR", "1.5×"],
["FVG Expiry", "10 candles"],
["Body PCT Min", "50%"],
["Close PCT Min", "80%"],
];
// Simplified balance milestones for equity progression (50k bars, 5% risk, v1.4.0)
// $5,000 start → $27,439 end
const equityPoints = [
{ label: "Start", value: 5000 },
{ label: "Oct '25", value: 4953 },
{ label: "Nov '25", value: 5173 },
{ label: "Dec '25", value: 5901 },
{ label: "Jan '26", value: 6902 },
{ label: "Feb '26", value: 8240 },
{ label: "Mar '26", value: 12440 },
{ label: "Apr '26", value: 15600 },
{ label: "May '26", value: 16038 },
{ label: "Jun '26", value: 27439 },
];
export default function BacktestPage() {
const maxEquity = Math.max(...equityPoints.map(p => p.value));
const minEquity = Math.min(...equityPoints.map(p => p.value));
// Normalise to SVG viewBox height 80px
const h = 80;
const w = 340;
const pts = equityPoints.map((p, i) => {
const x = (i / (equityPoints.length - 1)) * w;
const y = h - ((p.value - minEquity) / (maxEquity - minEquity)) * h;
return `${x},${y}`;
}).join(" ");
return (
<>
<section className="pt-6 pb-12">
@@ -40,27 +67,27 @@ export default function BacktestPage() {
Backtest Results
</h1>
<p className="text-[15px] text-ink-sub max-w-xl">
Historical simulation on real MT5 tick data. Includes spread costs, commission, and slippage.
Historical simulation on real MT5 tick data. Includes spread, commission ($7/lot), and slippage.
London session filter (0813 UTC) applied.
</p>
</section>
{/* Highlight */}
<div className="card-featured p-10 mb-10">
<div className="card-featured p-10 mb-6">
<div className="flex flex-col sm:flex-row sm:items-start gap-4 mb-8">
<div>
<span className="status-pill status-bull mb-3 inline-block">Recommended</span>
<h2 className="text-2xl font-semibold tracking-tight-sm text-ink">M5 · 1 Month · 1% Risk</h2>
<p className="text-sm text-ink-sub mt-1">XAUUSDm · Exness Demo</p>
<span className="status-pill status-bull mb-3 inline-block">v1.4.0 · Best Result</span>
<h2 className="text-2xl font-semibold tracking-tight-sm text-ink">M5 · 50k Bars · 5% Risk</h2>
<p className="text-sm text-ink-sub mt-1">XAUUSDm · Sep 2025 Jun 2026</p>
</div>
<Link href="/trades" className="btn-primary sm:ml-auto shrink-0">View Live Trades </Link>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-6 pt-8 border-t border-hl">
{[
{ label: "Profit Factor", value: "1.42", note: "> 1.3 = good" },
{ label: "Net Return", value: "+43.2%", note: "on $600 balance" },
{ label: "Max Drawdown", value: "11.5%", note: "manageable" },
{ label: "Win Rate", value: "55.3%", note: "159 trades" },
{ label: "Profit Factor", value: "2.08", note: "> 2.0 = strong" },
{ label: "Net Return", value: "+448.7%", note: "$5,000 → $27,439" },
{ label: "Win Rate", value: "62.7%", note: "83 trades" },
{ label: "vs v1.3.0", value: "+274pp", note: "v1.3.0 was +174.7%" },
].map(({ label, value, note }) => (
<div key={label}>
<p className="text-xs text-ink-sub mb-1.5">{label}</p>
@@ -71,6 +98,145 @@ export default function BacktestPage() {
</div>
</div>
{/* Equity curve */}
<div className="card mb-10 p-6">
<div className="flex items-center justify-between mb-5">
<div>
<p className="eyebrow mb-1">Equity Progression</p>
<p className="text-xs text-ink-ter">50k M5 bars · 5% risk · XAUUSDm</p>
</div>
<div className="text-right">
<p className="font-mono text-lg font-semibold text-bull">$27,439</p>
<p className="text-xs text-ink-ter">from $5,000</p>
</div>
</div>
<div className="relative w-full overflow-x-auto">
<svg viewBox={`0 0 ${w} ${h + 20}`} className="w-full" style={{ minWidth: 280 }}>
{/* Grid lines */}
{[0, 0.25, 0.5, 0.75, 1].map((t) => {
const y = h - t * h;
const val = Math.round(minEquity + t * (maxEquity - minEquity));
return (
<g key={t}>
<line x1="0" y1={y} x2={w} y2={y} stroke="#23252a" strokeWidth="0.5" />
<text x={w + 2} y={y + 4} fontSize="7" fill="#62666d" textAnchor="start">
${(val / 1000).toFixed(0)}k
</text>
</g>
);
})}
{/* Area fill */}
<defs>
<linearGradient id="areaGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#27a644" stopOpacity="0.18" />
<stop offset="100%" stopColor="#27a644" stopOpacity="0.01" />
</linearGradient>
</defs>
<polygon
points={`0,${h} ${pts} ${w},${h}`}
fill="url(#areaGrad)"
/>
{/* Line */}
<polyline
points={pts}
fill="none"
stroke="#27a644"
strokeWidth="1.5"
strokeLinejoin="round"
strokeLinecap="round"
/>
{/* Dots + labels */}
{equityPoints.map((p, i) => {
const x = (i / (equityPoints.length - 1)) * w;
const y = h - ((p.value - minEquity) / (maxEquity - minEquity)) * h;
return (
<g key={i}>
<circle cx={x} cy={y} r="2.5" fill="#27a644" />
<text
x={x}
y={h + 14}
fontSize="6.5"
fill="#62666d"
textAnchor="middle"
>
{p.label}
</text>
</g>
);
})}
</svg>
</div>
</div>
{/* Filter impact */}
<div className="mb-10">
<p className="eyebrow mb-5">What Changed in v1.4.0</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div className="card p-5">
<div className="flex items-center gap-2 mb-3">
<span className="text-accent text-base"></span>
<p className="text-sm font-medium text-ink">ATR Quality Gate</p>
</div>
<p className="text-sm text-ink-sub leading-relaxed mb-4">
FVG zone 0.3× ATR(14) and impulse body 1.0× ATR(14). Weak, low-energy setups are discarded before entry.
</p>
<div className="flex gap-3 pt-3 border-t border-hl">
<div>
<p className="text-[10px] text-ink-ter mb-0.5">Trades cut</p>
<p className="font-mono text-sm font-semibold text-ink">224 83</p>
</div>
<div className="w-px bg-hl" />
<div>
<p className="text-[10px] text-ink-ter mb-0.5">PF impact</p>
<p className="font-mono text-sm font-semibold text-bull">1.17 2.08</p>
</div>
</div>
</div>
<div className="card p-5">
<div className="flex items-center gap-2 mb-3">
<span className="text-accent text-base"></span>
<p className="text-sm font-medium text-ink">Entry Zone 75%</p>
</div>
<p className="text-sm text-ink-sub leading-relaxed mb-4">
Limit order placed 75% deep into the FVG instead of the midpoint. Forces price to retest the level, confirming it as real support/resistance.
</p>
<div className="flex gap-3 pt-3 border-t border-hl">
<div>
<p className="text-[10px] text-ink-ter mb-0.5">DD reduction</p>
<p className="font-mono text-sm font-semibold text-bull">63%</p>
</div>
<div className="w-px bg-hl" />
<div>
<p className="text-[10px] text-ink-ter mb-0.5">Win rate</p>
<p className="font-mono text-sm font-semibold text-bull">50% 62.7%</p>
</div>
</div>
</div>
<div className="card p-5">
<div className="flex items-center gap-2 mb-3">
<span className="text-accent text-base"></span>
<p className="text-sm font-medium text-ink">Session 0813 UTC</p>
</div>
<p className="text-sm text-ink-sub leading-relaxed mb-4">
Entries restricted to London open + early NY overlap. XAU liquidity and directional moves concentrate in this window other hours produce noise.
</p>
<div className="flex gap-3 pt-3 border-t border-hl">
<div>
<p className="text-[10px] text-ink-ter mb-0.5">vs all sessions</p>
<p className="font-mono text-sm font-semibold text-bull">PF best</p>
</div>
<div className="w-px bg-hl" />
<div>
<p className="text-[10px] text-ink-ter mb-0.5">DD vs no filter</p>
<p className="font-mono text-sm font-semibold text-bull">lowest</p>
</div>
</div>
</div>
</div>
</div>
{/* Results table */}
<div className="rounded-lg border border-hl overflow-hidden mb-10">
<div className="px-6 py-4 border-b border-hl">
@@ -80,7 +246,7 @@ export default function BacktestPage() {
<table className="data-table">
<thead>
<tr>
{["Symbol", "Period", "TF", "Risk", "Trades", "Win Rate", "Profit Factor", "Return", "Max DD", ""].map(h => (
{["Symbol", "Period", "TF", "Risk", "Version", "Trades", "Win Rate", "Profit Factor", "Return", "Max DD", ""].map(h => (
<th key={h}>{h}</th>
))}
</tr>
@@ -92,6 +258,7 @@ export default function BacktestPage() {
<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-xs text-ink-ter">{r.version}</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">
@@ -133,13 +300,13 @@ export default function BacktestPage() {
<div className="card">
<p className="eyebrow mb-6">How It Works</p>
<ol className="space-y-5">
<ol className="space-y-4">
{[
["Detect Impulse", "3-candle momentum: body ≥ 60% of range, close in top/bottom 20%."],
["Find FVG", "Measure gap between candle 1 high and candle 3 low (bull) or reverse."],
["EMA Filter", "Long only above EMA-20, short only below. Strict trend confirmation."],
["Place Limit", "Set limit at FVG midpoint. Auto-cancel after 10 candles if unfilled."],
["Manage Risk", "SL at FVG boundary, TP at ≥ 1.5× RR, size at exactly 1% risk."],
["Detect Impulse", "3-candle momentum: body ≥ 50% of range, close in top/bottom 20%."],
["ATR Quality Gate", "FVG zone ≥ 0.3× ATR(14). Impulse body ≥ 1.0× ATR(14). Weak setups discarded."],
["EMA-20 Filter", "Long only above EMA-20, short only below. Confirms short-term trend."],
["Entry Zone 75%", "Limit order at 75% depth inside FVG — waits for price to retrace before committing."],
["Manage Risk", "SL at FVG boundary, TP at ≥ 1.5× RR, position sized at exactly N% risk per trade."],
].map(([title, desc], i) => (
<li key={i} className="flex gap-4">
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-s2 border border-hl text-xs font-mono text-ink-sub flex items-center justify-center">