Trading the Breaking

Trading the Breaking

Alpha Lab

[WITH CODE] Risk Engine: Position sizing

Experience next‑level performance with smarter positioning

Quant Beckman's avatar
Quant Beckman
Apr 22, 2025
∙ Paid

Table of contents:

  1. Introduction.

  2. Limitations and hidden dangers of static position sizing.

  3. Architectural design and operational framework.

  4. Mathematical foundations and implementation.

    1. Drawdown-based size reduction.

    2. Profit-based size increase.

    3. Recovery adjustment.

    4. Algorithmic flow of the stochastic sizer.

    5. Introducing abstraction via regret minimization.

    6. Pruning of candidates.

    7. Abstracted position sizer.


Before you begin, remember that you have an index with the newsletter content organized by clicking on “Read full story” in this image.


Introduction

You've got your algorithmic trading strategy, honed and backtested. It spots an opportunity, the signal fires, and you're ready to execute. But before the order goes live, there's that crucial, sometimes gut-wrenching question: How many contracts? How many shares?

This isn't just a minor detail; the position size is a direct multiplier of your strategy's outcome. It amplifies wins, yes, but it also amplifies losses. For a long time, the approach was often static–a fixed number, or a simple fixed percentage of capital. It felt straightforward, predictable even. But in the financial markets, relying on a single, predetermined bet size feels increasingly... inadequate. It's the central dilemma: using a rigid tool in a fluid environment.

The problem with a static position size quickly becomes apparent when the market stops behaving nicely—which, let's be honest, is often. If your strategy hits a rough patch – maybe the market regime shifted, or your model is temporarily out of sync – a fixed, large position size means every loss hits hard, accelerating drawdowns precisely when you need to preserve capital. It's like steering into a storm with the sail fully open because that's the size you started with.

Conversely, imagine your strategy finds its rhythm, navigating the market beautifully and racking up profits. If you're stuck with a small size decided upon when things were less certain, you're fundamentally limiting your upside. You're leaving potential alpha on the table simply because your risk allocation isn't scaling with your success. A fixed size ignores the vital feedback loop of performance, creating a dangerous disconnect between the risk being run and the system's actual ability to handle it in the current market context.

So then, why don’t you make position size adaptive?

Limitations and hidden dangers of static position sizing

Most trading systems use disappointingly simplistic approaches:

  • Fixed lot sizing: Always trade the same number of contracts.

  • Fixed fractional: Risk a consistent percentage of capital.

  • Kelly criterion: Optimize based on win rate and payoff ratio.

These approaches ignore a fundamental truth about markets: they're living, breathing organisms that cycle through regimes of volatility, trending behavior, and choppy consolidation. A position sizing strategy that works beautifully in a bull market might prove disastrous during a flash crash.

Consider these risks of conventional approaches:

  1. Volatility blindness: Fixed lot sizes ignore changing market conditions.

  2. Recovery challenges: After drawdowns, fixed fractional models struggle to rebuild capital.

  3. Parameter sensitivity: Methods like Kelly are highly sensitive to estimation errors.

  4. Black swan vulnerability: Most models perform poorly during extreme market events.

Architectural design and operational framework

The pivotal insight that drives our exploration is this: position sizing should be as dynamic and adaptive as the markets themselves. What if we could develop a system that:

  • Expands positions during favorable periods.

  • Contracts during drawdowns to preserve capital.

  • Smoothly recovers after periods of losses.

  • Automatically adapts to changing system’s performance.

This brings us to what I have dubbed the Stochastic Position Sizer. A framework that treats position sizing not as a static formula but as a dynamic process. Indeed, translating the concept of adaptive, performance-contingent sizing into a functional algorithm requires defining specific rules and mechanisms.

The StochasticPositionSizer is designed to dynamically adjust position size based on the system's performance history, particularly its Profit and Loss and resulting equity curve. It is initialized with boundaries min_contracts, max_contracts and a starting size initial_contracts. Its adaptive behavior is governed by several key parameters:

  • drawdown_sensitivity: Controls how aggressively position size is reduced as the system enters or deepens a drawdown. Higher sensitivity leads to steeper reductions.

  • profit_smoothing: Determines the influence of recent positive PnL on scaling up position size. A higher value allows recent profits to more readily increase size.

  • recovery_rate: Specifies how quickly position size is increased towards the initial_contracts level when the system is showing signs of recovering from a drawdown—i.e., drawdown is shrinking.

  • lookback_period: Defines the window size for calculating recent performance metrics, such as average PnL.

These parameters calibrate the sizer's response profile, allowing it to be more risk-averse during losses and more aggressive during profitable periods, according to the system designer's preference.

But before implementing it, let’s take a look of the mathematical background.

Mathematical foundations of adaptive sizing

The core of the StochasticPositionSizer's logic resides in how it mathematically adjusts the position size based on calculated performance metrics within its update method.

Drawdown-based size reduction

Drawdown (DD) is a critical metric representing the percentage decline from the peak equity reached so far. It is calculated as:

\(DD_t = \max\left( 0, \frac{ \max_{i \leq t}(Equity_i) - Equity_t }{ \left| \max_{i \leq t}(Equity_i) \right| } \right) \)

The absolute value in the denominator provides robustness, though for established positive equity curves, it simplifies to just the peak.

The size adjustment based on drawdown uses an exponential decay function:

\(Size_{adjusted\_for\_DD} = Size_{base} \times e^{-sensitivity \times DD} \)

Here, e is the base of the natural logarithm. This formula ensures a non-linear response: small drawdowns result in minor size reductions—factor close to 1—while larger drawdowns lead to increasingly aggressive reductions—factor decreasing rapidly towards 0. A higher drawdown_sensitivity makes this reduction curve steeper. This mechanism is fundamental for dynamic capital preservation during unfavorable periods.

Profit-based size increase

Positive recent performance encourages scaling up. The sizer looks at the average PnL over the lookback_period.

avg_pnl = sum(recent_pnl) / len(recent_pnl)

This average PnL, relative to the equity scale (represented in the code by the sum of the equity curve for normalization), contributes to a growth_factor.

\(GrowthFactor \approx 1 + \left( \frac{\text{avg_pnl}}{|\text{Equity Scale}|} \right) \times \text{profit_smoothing} \)

This factor influences how much of the remaining distance to the max_contracts is added to the current size:

distance_to_max = self.max_contracts - current_size
if distance_to_max > 0:
    contracts += distance_to_max * (1 - math.exp(-growth_factor)) * 0.1

The term 1−e−GrowthFactor increases towards 1 as GrowthFactor increases. The additional 0.1 multiplier in the code dampens this effect, ensuring that scaling up towards the maximum is gradual and smoothed, preventing over-leveraging based on short-term volatility spikes.

Recovery adjustment

When the system's drawdown percentage begins to decrease—prev_dd > self.current_drawdown—indicating a recovery phase, a specific adjustment is applied.

def _apply_recovery(self, contracts: float, improving: bool) -> float:
    if not improving:
        return contracts
    distance_to_initial = self.initial_contracts - contracts
    if distance_to_initial > 0: # Only if current size is below initial
        contracts += distance_to_initial * self.recovery_rate
    return contracts

If the system is improving and the current size is below the initial_contracts, a fraction or recovery_rate of the distance back towards the initial size is added. This provides a focused mechanism to help the position size rebound towards a baseline operating level once the system shows signs of emerging from a slump.

Algorithmic flow of the stochastic sizer

This post is for paid subscribers

Already a paid subscriber? Sign in
© 2026 Quant Beckman · Publisher Privacy ∙ Publisher Terms
Substack · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture