← Blog/blog/deep-momentum-networks

Training a strategy to maximise Sharpe, read closely

01

Momentum, and the two rules it usually needs

Time-series momentum is one of the most durable facts in markets: an asset that has gone up over the past year tends to keep going up over the next month, and one that has fallen tends to keep falling. It is time-series momentum because each market is judged against its own past, not against its peers. Moskowitz, Ooi & Pedersen (2012) documented it across dozens of futures — equities, bonds, currencies, commodities — and a whole industry of trend-following funds runs on it.

To turn that fact into a strategy you make two decisions by hand. First a trend estimator: is this market trending up or down? The classic answer is the sign of the past year's return — up means long, down means short. Second a position-sizing rule: how large a bet? Here the standard trick is volatility scaling — divide the position by the market's recent volatility so a calm market and a wild one contribute the same risk. A quiet bond future gets levered up; a thrashing energy contract gets sized down. Everything is measured in Sharpe ratio: the average return divided by its standard deviation (annualised), i.e. reward per unit of risk. A Sharpe of 1 is a good strategy; a Sharpe of 2 is a great one.

02

The paper's idea: learn the position, optimise Sharpe directly

Lim, Zohren & Roberts ask a simple question: why hand-pick either rule? Keep the volatility-scaling layer — it is just good risk hygiene — but replace the trend estimator and the sizing rule with a network that maps recent returns straight to a position in (−1, 1). They call the result a Deep Momentum Network. The clever part is the training objective. Instead of teaching the network to forecast returns (a regression) and hoping good forecasts make a good strategy, they train it on the thing they actually care about: the Sharpe ratio of its own P&L. The Sharpe ratio is a smooth function of the positions, so you can differentiate it and backpropagate.

# The captured return of ONE trade. The network outputs a position in (-1, 1); the
# volatility-scaling layer then levers it so every asset carries the same risk.
def captured_return(position, sigma, r_next, sigma_tgt=0.15):
    return position * (sigma_tgt / sigma) * r_next     # sigma = annualised vol

# The loss the network MINIMISES: the negative annualised Sharpe ratio of its own
# returns. Sharpe is differentiable, so this whole thing backpropagates.
def sharpe_loss(R, ann=252):
    mu  = mean(R)
    var = mean([(x - mu) ** 2 for x in R])             # population variance
    return -(mu / sqrt(var)) * sqrt(ann)               # maximise Sharpe = minimise -Sharpe

That sharpe_loss is the entire innovation. Each trade's captured return is the position, levered to a common 15% volatility target, times the next day's move; the loss is the negative annualised Sharpe of those returns pooled across every asset and day. Gradient descent on it nudges the weights so the whole book's risk-adjusted return goes up. Our site runs the TypeScript above; the Python and C++ are line-for-line translations. To keep the demo honest and fully from-scratch, our “network” is a linear map through a tanh — a single-neuron net — rather than the paper's LSTM, but the loss, the volatility scaling and the gradient are exactly theirs.

03

It learns — and it beats the hand-made rule

We simulate a panel of 12 trending markets (each a persistent latent drift plus noise, seeded), hand the network normalised trailing returns over 5, 21, 63, 252-day windows, and ascend on the Sharpe loss. From a random start the strategy is worse than a coin flip; within a few dozen steps it has found the trend and the gross Sharpe climbs past 2.80.

gross Sharpe (what the loss sees)net Sharpe (what you keep)
Fig 1. Training the Sharpe loss by gradient ascent. Gross Sharpe (indigo) climbs from a negative random start to 2.80. The net Sharpe (orange), which pays a cost on every trade, tracks below it — and the gap between the two lines is the entire subject of this post.

Against the textbook baseline — position = sign of the trailing one-year return, then vol-scaled — the learned book is far ahead on gross Sharpe. That reproduces the paper's central “it works” claim, which reported the Sharpe-optimised LSTM roughly doubling the classic strategies before costs.

Fig 2. Gross Sharpe: the hand-crafted sign-of-trend rule versus the network trained to maximise Sharpe, on the same seeded panel. Learning the position is worth 2.03 points of Sharpe here — before costs.
strategySharpe (before costs)
Long only0.738
Sgn(returns) — classic TSMOM1.192
MACD trend signal0.976
Linear · Sharpe loss1.094
MLP · Sharpe loss1.383
LSTM · Sharpe loss2.804
The paper's reported Sharpe ratios (Table 2, raw signals, no transaction costs) on a portfolio of 88 continuous futures contracts, 1990–2015. The Sharpe-optimised LSTM more than doubles the classic Sgn(returns) rule. These are the paper's numbers; our seeded reproduction shows the same ordering at its own scale.
04

The thing a skim misses: the loss never pays for turnover

Read the objective again. The Sharpe loss is computed on position × scale × return— the gross return of each trade. Nowhere in it is the cost of putting the trade on. So when the optimiser discovers that flipping the position a little more often nudges the Sharpe up a hair, it takes the trade every time; it is never billed. In backtest-world that is free money. In the real world every change in the position crosses a spread and pays a commission.

# What the strategy ACTUALLY earns. u is the vol-scaled position; every day-to-day
# change in it is a trade, and every trade pays a cost c. The Sharpe loss above never
# sees this term — so the optimiser is free to trade as much as it likes.
def net_return(u, u_prev, r_next, c):
    return u * r_next - c * abs(u - u_prev)

# The fix the paper adds: penalise squared position changes during training, so the
# network learns to sit still unless the signal is worth the trade.
def objective(R, u_series, lam):
    return sharpe(R) - lam * mean_sq_change(u_series)   # gradient-ASCEND on this

Now re-score the very same learned strategy, but subtract a cost c on every day-to-day change in the vol-scaled position — the honest P&L a desk would actually book. The result is Fig 3: the gross Sharpe you were admiring is the value at c = 0, and it slides relentlessly as costs rise. At a cost of 0.01 in our units (≈9 basis points on the traded notional, in the paper's convention) the learned book's net Sharpe is down to 1.18; a little past that it crosses the far-cheaper baseline and then goes negative outright.

Sharpe-optimised (learned)sign(trend) baseline
Fig 3. Net Sharpe as the per-trade cost rises, for the Sharpe-optimised network (indigo) and the classic sign-of-trend baseline (grey). The learned strategy starts far higher — but it also trades more aggressively per unit of edge, so it bleeds out faster and by the right-hand side is the worse book. The number the loss optimised was never the number you keep.
05

The fix hiding in one extra term

The paper's remedy is a single addition to the objective: a turnover regularisation term that penalises how much the position moves from one day to the next. Ascend on Sharpe − λ · (turnover) and the network learns to sit still unless the signal genuinely justifies a trade. The striking thing is how cheap this is. Sweeping the penalty λ from zero upward, turnover collapses while the gross Sharpe barely flinches:

Fig 4. Annualised turnover of the learned book as the turnover penalty λ grows (0 is the unconstrained Sharpe-optimiser, in orange). Trading falls from 6.8 to 1.2 — roughly 5.7× less churn — while gross Sharpe stays put (see the table). Most of that turnover was never buying any return; it was the loss gaming a metric that ignored it.
penalty λgross Sharpenet Sharpe (c = 0.006)turnover
02.801.186.8
102.801.154.8
302.801.163.3
1002.791.222.5
3002.791.171.7
8002.791.191.2
The turnover penalty is nearly free: raising λ from 0 to 100 cuts trading from 6.8 to 2.5 while gross Sharpe moves only from 2.80 to 2.79 — and the net Sharpe you actually keep is unchanged or better. In the paper's costed backtest the same term is the difference between a Sharpe of −5.31 and +0.91.
06

What I'd probe next

  • Cost-aware training, not cost-aware scoring. The cleanest version puts the real cost inside the loss, so the network optimises net Sharpe end-to-end. That couples consecutive days through the |Δu| term — a small but real change to the gradient, and the honest way to let the model decide when an edge clears the spread.
  • Does the edge survive an LSTM's memory — or just overfit it? Our single neuron can only take a fixed, linear view of the trailing windows. The paper's LSTM can condition on the path, which is where its extra gross Sharpe comes from — and also, plausibly, where its catastrophic turnover comes from. More capacity, more churn, more to regularise.
  • The Sharpe loss is fragile in small samples. Optimising a ratio of two noisy estimates (mean over standard deviation) rewards any lucky low-variance streak. A walk-forward split — train on the past, score on the untouched future — is the only test that separates a learned edge from a fitted one.

The mechanism at the heart of this is just gradient descent on a differentiable objective — the same loop behind every model on this site. Go turn the knobs on the neural-network page and picture the loss not as squared error but as a Sharpe ratio. Deep Momentum Networks are a genuinely elegant idea: let the strategy optimise the thing you care about. Just make sure the thing you care about is the number you actually get to keep.

References

  1. Lim, B., Zohren, S., & Roberts, S. (2019). Enhancing Time Series Momentum Strategies Using Deep Neural Networks. The Journal of Financial Data Science 1(4), 19–38 · arXiv:1904.04912
  2. Moskowitz, T. J., Ooi, Y. H., & Pedersen, L. H. (2012). Time Series Momentum. Journal of Financial Economics 104(2), 228–250
  3. Baz, J., Granger, N., Harvey, C. R., Le Roux, N., & Rattray, S. (2015). Dissecting Investment Strategies in the Cross Section and Time Series. SSRN 2695101