diff --git a/src/Marathon.Domain/Betting/KellyCalculator.cs b/src/Marathon.Domain/Betting/KellyCalculator.cs
new file mode 100644
index 0000000..8805583
--- /dev/null
+++ b/src/Marathon.Domain/Betting/KellyCalculator.cs
@@ -0,0 +1,82 @@
+namespace Marathon.Domain.Betting;
+
+///
+/// Pure fractional-Kelly stake sizing for a single back bet at decimal odds.
+///
+///
+///
+/// The Kelly criterion maximises the long-run growth rate of a bankroll by staking
+/// a fraction of it proportional to the edge. For decimal odds o and an
+/// estimated win probability p, the full-Kelly fraction of bankroll is:
+///
+/// f* = (p·o − 1) / (o − 1)
+///
+/// When f* is zero or negative there is no positive expected value, and the
+/// suggested stake is 0 — the calculator never recommends betting into a
+/// negative-EV price. Most disciplined bettors stake a fraction of full
+/// Kelly (e.g. quarter-Kelly, fraction = 0.25) to cut variance and blunt the
+/// impact of probability-estimation error; full Kelly is famously over-aggressive
+/// once p is even slightly wrong.
+///
+///
+/// The win probability is an input the bettor supplies — it is intentionally NOT
+/// derived from an anomaly score here, so the calculator stays a pure, reusable
+/// money-management primitive independent of any signal source.
+///
+///
+public static class KellyCalculator
+{
+ /// Default Kelly fraction: quarter-Kelly — the conventional variance-safe choice.
+ public const decimal DefaultFraction = 0.25m;
+
+ ///
+ /// Full-Kelly fraction of bankroll (p·o − 1)/(o − 1). May be negative or
+ /// zero, signalling no positive edge. Exposed for callers that want the raw
+ /// figure (e.g. to display the edge) rather than a clamped stake.
+ ///
+ /// Estimated win probability in the closed interval [0, 1].
+ /// Decimal odds, strictly greater than 1.0.
+ public static decimal FullKellyFraction(decimal winProbability, decimal decimalOdds)
+ {
+ if (winProbability is < 0m or > 1m)
+ throw new ArgumentOutOfRangeException(nameof(winProbability), winProbability, "Must be in [0, 1].");
+ if (decimalOdds <= 1m)
+ throw new ArgumentOutOfRangeException(nameof(decimalOdds), decimalOdds, "Decimal odds must be greater than 1.0.");
+
+ return (winProbability * decimalOdds - 1m) / (decimalOdds - 1m);
+ }
+
+ ///
+ /// Suggested stake using fractional Kelly, rounded down to two decimals so the
+ /// suggestion is never larger than the theoretical figure. Returns 0 when
+ /// there is no positive edge.
+ ///
+ /// Estimated win probability in the open interval (0, 1).
+ /// Decimal odds, strictly greater than 1.0.
+ /// Total bankroll; must be non-negative.
+ /// Kelly fraction in (0, 1]; defaults to .
+ public static decimal SuggestStake(
+ decimal winProbability,
+ decimal decimalOdds,
+ decimal bankroll,
+ decimal fraction = DefaultFraction)
+ {
+ if (winProbability is <= 0m or >= 1m)
+ throw new ArgumentOutOfRangeException(nameof(winProbability), winProbability, "Must be in the open interval (0, 1).");
+ if (bankroll < 0m)
+ throw new ArgumentOutOfRangeException(nameof(bankroll), bankroll, "Must be non-negative.");
+ if (fraction is <= 0m or > 1m)
+ throw new ArgumentOutOfRangeException(nameof(fraction), fraction, "Kelly fraction must be in (0, 1].");
+
+ // FullKellyFraction validates decimalOdds.
+ var full = FullKellyFraction(winProbability, decimalOdds);
+ if (full <= 0m)
+ return 0m;
+
+ var stake = fraction * full * bankroll;
+
+ // Truncate (floor toward zero) to two decimals so a stake suggestion never
+ // exceeds the computed figure — a conservative bias for real-money sizing.
+ return Math.Truncate(stake * 100m) / 100m;
+ }
+}
diff --git a/src/Marathon.UI/Pages/MyBets/Journal.razor b/src/Marathon.UI/Pages/MyBets/Journal.razor
index fee5a80..af6eac6 100644
--- a/src/Marathon.UI/Pages/MyBets/Journal.razor
+++ b/src/Marathon.UI/Pages/MyBets/Journal.razor
@@ -9,6 +9,7 @@
@page "/my-bets"
@using Marathon.Application.Betting
+@using Marathon.Domain.Betting
@implements IDisposable
@inject IStringLocalizer L
@inject IBetJournalService Service
@@ -184,6 +185,50 @@
data-test="journal-add-stake" />
+