[WITH CODE] Risk Engine: Computing optimal targets
When RANSAC and Conformal Prediction converge to estimate how much and how far to lose money
Table of contents:
Introduction.
Local extrema detection.
Initialization.
Defining the trend.
Incremental processing.
Calculations between extrema.
Robust model estimation.
Uncertainty quantification via split Conformal Prediction.
Introduction
Risk management isn’t about predicting the future—it’s about preparing for it. Markets are riddled with traps: sudden reversals, hidden volatility, and false signals that lure even seasoned traders into costly mistakes. This technique wasn’t built to chase profits; it was crafted to survive.
Imagine you’re navigating a mountain pass in the dark. One misstep—a missed peak, an unseen valley—and disaster strikes. Financial markets are no gentler. The algorithm we’ll unpack here acts as your headlamp, scanning every twist and turn in the data to flag inflection points where risk spikes or recedes. It answers the critical question: When does a trend turn from ally to enemy?
Traditional risk models often lag, reacting only after the damage is done. Ours is different. By hunting for local extrema—those fleeting moments where prices pivot—it spots fractures in trends before they cascade into crises. Think of it as a seismograph for financial tremors: subtle shifts that hint at coming quakes.
By quantifying the magnitude of market swings and wrapping predictions in statistically sound uncertainty bands, it transforms raw data into a playbook for resilience. Whether guarding a portfolio against crashes or timing exits before a bubble bursts, these tools turn volatility from a threat into a mapped minefield.
Local extrema detection
Before delving into our risk engine, we need to set the stage with some fundamental concepts. We denote our time series by a function
where each index i corresponds to a sample s(i). Think of s(i) as the price or rate at time i. The overall goal is to detect local extrema—local minima or “bottoms” and local maxima or “peaks”—by monitoring changes in the direction of s(i) as we move sequentially through the data. The functions that follow implement this by tracking the trend and then, upon detecting a reversal, marking the previously recorded candidate as an extremum.
You can expect something like:
Let's see how it works.
Initialization
So the first step is to extract meaningful signals from our time series by identifying turning points—peaks and bottoms—that often correspond to important market events. These extrema help us understand when the market might be poised for a reversal.
Our algorithm initializes by setting the candidate index c=0 and candidate value s(c)=s(0). Additionally, we define a trend variable τ∈{−1,0,1}, where:
τ=1 indicates that the series is currently increasing,
τ=−1 indicates that the series is decreasing,
τ=0 means the trend has not yet been established.
Simultaneously, we construct an output signal array extrema(i) such that:
This signal array acts as our signal detector, flagging potential turning points in the market.
Defining the trend
Once the algorithm begins iterating from i=1, it tries to establish a trend. If the trend is undefined (τ=0), it computes:
Here, the sign function sgn(x) tells us whether our time series is initially rising or falling. This is starting to get interesting.
Incremental processing
Once a trend is defined, the algorithm processes each new data point in one of two ways:
Case A - Increasing trend (τ=1):
If
\(s(i) \ge s(c),\)the series continues to rise. Hence, the candidate is updated:
\(c \leftarrow i \quad \text{and} \quad s(c) \leftarrow s(i).\)If, however, s(i)<s(c), then a reversal occurs. The candidate c is deemed a local maximum or peak and is flagged as:
\(\text{extrema}(c) = -1.\)The trend is then switched to decreasing (τ←−1) and the candidate is reset:
\(c \leftarrow i, \quad s(c) \leftarrow s(i).\)Case B - Decreasing trend (τ=−1):
If
\(s(i) \le s(c),\)the series continues to fall, so we update:
\(c \leftarrow i \quad \text{and} \quad s(c) \leftarrow s(i).\)When s(i)>s(c), the candidate is marked as a local minimum (bottom):
\(\text{extrema}(c) = 1.\)Then, the trend is switched to increasing (τ←1) and the candidate is reset:
\(c \leftarrow i, \quad s(c) \leftarrow s(i).\)
This incremental procedure effectively implements the following idea:
A local maximum is detected at index c if there exists a neighborhood around ccc such that
\(s(c) \ge s(j) \quad \text{for } j \text{ in some neighborhood of } c,\)with the series rising before ccc and falling after ccc.
Similarly, a local minimum is detected at ccc if
\(s(c) \le s(j) \quad \text{for } j \text{ in a neighborhood of } c,\)with the series falling before and rising after c.
A key aspect of this method is its inherent delay of one sample. The algorithm needs an extra data point to confirm that the trend has reversed. So don't even think about using it as a method to enter the market. Worst case, the signal will be late and the trade won't be opened.