Stochastic Oscillator: The Exact Rule Our Engine Runs
The stochastic oscillator answers one narrow question: where did this bar close inside the range of the last fourteen bars? At the top of that range it reads 100, at the bottom 0, and everywhere else it interpolates. Vike computes it as batch_stochf, one of the momentum detectors in our indicator engine — and one of three functions in our crate that answer to this name.
What the stochastic oscillator measures
Position, not speed. That distinction gets lost constantly and it is the source of most misuse.
Take the highest high and the lowest low across a fourteen-bar window. Those two prices define a channel. Now measure how far up that channel the latest close sits, scaled to a hundred. A reading of 90 says the close landed in the top tenth of everything the last fortnight traded through — it says nothing at all about how quickly price got there, or whether the channel itself was wide or narrow.
Contrast that with the RSI, which averages the size of up moves against down moves. Two indicators, both bounded, measuring genuinely different things.
The exact stochastic formula our engine runs
batch_stochf runs. The zero-range branch is the clause that separates two otherwise identical implementations.Four lines of arithmetic and one clause that most write-ups never mention.
How Vike computes it.batch_stochfwalks a fourteen-bar window, takeshhas the maximum high andllas the minimum low, and writes100 × (close − ll) ÷ (hh − ll)into%K. If that range is exactly zero it writes0.0instead — not 50, not a missing value.%Dis a three-bar simple average of%K, produced bysmooth_definedand then explicitly blanked before bar 15 by a loop written for that purpose.%Kitself begins at bar 13. The bands are20and80, declared in the registry beside the function, so they are the engine's numbers rather than a convention I picked.
I ported that rule from the crate source and ran it against the Python engine the crate mirrors, across 344,012 daily readings from 243 Binance pairs. %K matched at 0.000e+00. %D came back at 5.684e-13, and the cause is traceable rather than mysterious: our math::sma refolds each window from scratch while the Python helper carries a running sum, so the two accumulate floating-point error in different orders. That residual is a hundred-billionth of a percentage point.
Reading %K against %D
Crossings are the event; levels are the context. Both matter, though only one of them is ever a signal, and mixing the two up is how a chart full of lines becomes a chart full of noise.
When %K rises through %D, the close has moved up its channel faster than its own three-bar average — the earliest arithmetic evidence that pressure is shifting. Taken anywhere in the middle of the scale, that happens constantly and means very little. Taken inside a band, it is at least a statement about an extreme. Across the tape scanned here, %K crossed upward while under 20 on 14,886 bars and downward while over 80 on 5,868, and the asymmetry between those counts is itself worth noticing on assets that spent the period trending.
Two of the examples here worked and two did not, which is roughly what a rule this simple deserves. I skip crossings that happen between the bands entirely.
Fast, slow and stochastic RSI: three functions, one name
Grep our crate for stoch and three kernels come back. They are not variants of a setting; they are separate code.
batch_stochf is the fast one described above. batch_stochastic takes three periods rather than two — a window, a %K smoothing and a %D smoothing — so its %K is already averaged before you see it. batch_stochrsi runs the identical window logic over an RSI series instead of over prices, which pushes its first value out to bar 27 and its signal to bar 29.
Here is the part worth measuring rather than asserting: the fast function's %D and the slow function's %K are the same line. Compared position by position across the whole tape, maximum absolute difference 0.000e+00, with no disagreement about which bars are defined. That is the textbook relationship these two are supposed to have, and being able to show it as an identity rather than as a claim is rare.
The edges the source handles
Every one of these came out of reading the function rather than a description of it.
A flat window — high equal to low across all fourteen bars — divides by zero, so the crate branches and returns 0.0. Its Python twin agrees for this function and disagrees for batch_stochastic, where the same input yields 100.0 on the Python side. Neither branch fired once across 344,012 real bars. That is exactly why a disagreement of this kind survives for years without anybody tripping over it.
Their guards differ too, in a way that reads as sloppiness and is not: batch_stochf tests rng != 0.0 and batch_stochastic tests rng > 0.0. On well-formed bars those are the same test. Meanwhile %K hit exactly 100.0 on 142 bars, where the close was the fourteen-bar high, and exactly 0.0 on 267. Those pins are real readings, not clipping.
Where the stochastic oscillator fails
An oscillator that measures position gets destroyed by trends, and the tape says so plainly.
Longest unbroken stretch above 80 in this sample: 37 bars. Below 20: 42. Anyone treating "overbought" as a sell signal was therefore wrong for six straight weeks in a row, on a real symbol, without the reading ever being incorrect. Both failing examples below carry the run length at the moment the crossing fired, so you can see the trend the reading was arguing with.
There is a subtler failure hiding in the arithmetic. Because only the extreme high and extreme low of the window enter the calculation, a single outlier bar sets the channel for the next fourteen readings, and the twelve ordinary bars between them contribute nothing whatsoever. When I first started using this, the mistake I made was assuming a mid-range value described the middle of recent trade. It describes the middle of two prices, which is not the same statement.
Frequently asked questions
What are the best stochastic settings? Fourteen and three are the crate's defaults, and everything measured here uses them. A shorter window makes the channel jumpier because a single new extreme rewrites it immediately.
How do the fast and slow versions differ?
The slow one smooths %K before returning it. Measured on our own tape, batch_stochf's signal line matches batch_stochastic's first output to 0.000e+00, so "slow" amounts to one extra averaging pass under a new label.
Is the stochastic oscillator better than the RSI? They measure different quantities — position in a range against the balance of up and down moves — so pairing them adds less than people hope. The Stochastic Momentum Index is a third answer again, despite the shared word.
Why do the first bars have no value?
A fourteen-bar window needs fourteen bars. %K begins at bar 13 and %D at bar 15, and the crate returns nothing earlier rather than a partial average dressed up as a reading.
Does it work on any timeframe? The arithmetic does. Whether it means anything depends on whether the market is ranging or trending, which is why a volatility measure such as the ATR or the raw true range is a more useful companion than a second momentum line.
This is educational material, not financial advice. Every figure here was measured on bars that have already closed, past behavior generalizes poorly, and trading carries real risk of loss — size any position so that being wrong stays survivable.