Tested by BacktestingMax
Stochastic oscillator crossover (14, 3, 3) backtest
Does the stochastic oscillator crossover strategy work?
No. In our backtest, the stochastic crossover (14, 3, 3) lost money in 33 of 36 tests with the standard rules, after trading costs. We tested 9 markets from 2019 to 2025 (4 currency pairs, gold, silver and 3 stock indices), each on 15-minute, 1-hour, 4-hour and daily charts. The stochastic oscillator is an indicator that shows where price closed within the range of the last 14 candles, from 0 (the lowest price) to 100 (the highest). %K is this reading averaged over 3 candles, and %D is a slower average of %K. The standard rules buy when %K crosses above %D with both lines below 20 (oversold). They sell short (sell first, to make money if price falls) when %K crosses below %D with both lines above 80 (overbought). A trade closes when %K crosses back over %D or reaches the other zone (80 for a buy, 20 for a sell), or at the stop-loss. All 27 tests on 15-minute, 1-hour and 4-hour charts lost money. On daily charts, 3 of 9 made money, and the best, AUD/USD, grew the account by only 6.2% in 7 years. Why: only about 1 trade in 7 reached the other zone. In 2 trades out of 3, %K crossed back first, and those trades lost money on average. The few big wins did not cover these losses and the stop-losses.
36 backtests 2019–2025 Trading costs included Tested 26 October 2026
Where it worked 36 backtests- Made money
- 3 of 36 tests, using the standard rules
- Made money in both periods
- 1 2019–2022 and again 2023–2025
- Best backtest
- +6% AUD/USD, daily chart: account growth 2019–2025
- With a 200 EMA trend filter
- 1 tests made money in both periods, compared with 1 using the standard rules
Made money in both periods
- AUD/USD, daily
Lost money on every market
- 15-minute charts
- 1-hour charts
- 4-hour charts
The results
Results on every market
Each box shows one backtest of the stochastic crossover (14, 3, 3) using the standard rules, from 2019 to 2025, after trading costs: the % result, and a small chart of the account balance. The solid line is the account balance and the dashed line is the starting balance. Green results made money; red results lost money. Under each result: whether it made money in both 2019–2022 and 2023–2025, or passed all four of our checks (see "How a test passes" further down).
| Market | 15-minute | 1-hour | 4-hour | daily |
|---|---|---|---|---|
| EUR/USD | −100% Lost money. Account down 90% by Feb 2020 | −53% Lost money | −42% Lost money | −0.8% Lost money |
| GBP/USD | −100% Lost money. Account down 90% by Dec 2019 | −81% Lost money | −13% Lost money | +3% Made money overall, lost in one period |
| USD/JPY | −100% Lost money. Account down 90% by Feb 2020 | −93% Lost money. Account down 90% by Jun 2025 | −22% Lost money | −12% Lost money |
| AUD/USD | −100% Lost money. Account down 90% by Nov 2019 | −93% Lost money. Account down 90% by Jun 2025 | −17% Lost money | +6% Passed all four checks |
| Gold | −99% Lost money. Account down 90% by Oct 2019 | −81% Lost money | −34% Lost money | −6% Lost money |
| Silver | −99% Lost money. Account down 90% by Mar 2019 | −93% Lost money. Account down 90% by Jun 2022 | −38% Lost money | −2% Lost money |
| Nasdaq 100 | −99% Lost money. Account down 90% by Jan 2020 | −77% Lost money | −2% Lost money | −13% Lost money |
| S&P 500 | −100% Lost money. Account down 90% by May 2021 | −58% Lost money | −14% Lost money | −17% Lost money |
| DAX | −98% Lost money. Account down 90% by May 2022 | −79% Lost money | −10% Lost money | +0.1% Made money overall, lost in one period |
All the numbers, test by test
| Market | Chart | Trades | Win rate | Profit factor | Result | Max drawdown | 2019–2022 | 2023–2025 | Monte Carlo drawdown | Trading costs |
|---|---|---|---|---|---|---|---|---|---|---|
| EUR/USD | 15-minute | 4,851 | 30.5% | 0.73 | −99.6% | −99.7% | −99.4% | −34.4% | −99.7% | $10,328 |
| EUR/USD | 1-hour | 2,717 | 35.1% | 0.92 | −53.1% | −59.1% | −35.2% | −27.5% | −72.0% | $7,432 |
| EUR/USD | 4-hour | 755 | 32.5% | 0.79 | −42.3% | −44.6% | −44.6% | +4.3% | −53.2% | $1,026 |
| EUR/USD | daily | 132 | 34.1% | 0.98 | −0.8% | −12.6% | +9.1% | −9.0% | −14.9% | $100 |
| GBP/USD | 15-minute | 4,371 | 30.5% | 0.64 | −99.5% | −99.6% | −99.2% | −35.9% | −99.6% | $6,431 |
| GBP/USD | 1-hour | 2,880 | 33.3% | 0.86 | −81.1% | −84.2% | −51.1% | −61.4% | −85.9% | $7,027 |
| GBP/USD | 4-hour | 766 | 33.6% | 0.95 | −12.7% | −24.3% | −4.4% | −8.8% | −39.4% | $1,567 |
| GBP/USD | daily | 140 | 35.7% | 1.05 | +2.6% | −10.7% | +9.4% | −6.3% | −13.3% | $111 |
| USD/JPY | 15-minute | 4,596 | 32.3% | 0.71 | −99.5% | −99.6% | −99.4% | −13.7% | −99.6% | $9,818 |
| USD/JPY | 1-hour | 2,880 | 32.1% | 0.74 | −92.7% | −92.8% | −79.8% | −64.0% | −94.1% | $4,310 |
| USD/JPY | 4-hour | 794 | 33.5% | 0.90 | −22.3% | −36.8% | −26.5% | +5.8% | −44.0% | $1,165 |
| USD/JPY | daily | 127 | 28.3% | 0.71 | −11.9% | −13.2% | −4.5% | −7.7% | −19.3% | $75 |
| AUD/USD | 15-minute | 3,929 | 29.8% | 0.67 | −99.6% | −99.6% | −99.5% | −21.1% | −99.6% | $10,665 |
| AUD/USD | 1-hour | 3,127 | 33.1% | 0.75 | −92.6% | −92.7% | −80.4% | −62.4% | −94.0% | $5,870 |
| AUD/USD | 4-hour | 822 | 34.4% | 0.93 | −16.5% | −33.3% | −28.0% | +16.0% | −38.9% | $1,959 |
| AUD/USD | daily | 136 | 36% | 1.13 | +6.2% | −7.0% | +1.5% | +4.6% | −12.7% | $143 |
| Gold | 15-minute | 2,664 | 28.9% | 0.58 | −98.6% | −98.6% | −98.4% | −12.3% | −98.7% | $10,098 |
| Gold | 1-hour | 2,484 | 34.6% | 0.81 | −80.6% | −82.2% | −61.1% | −50.0% | −84.6% | $5,946 |
| Gold | 4-hour | 748 | 32.8% | 0.83 | −34.4% | −35.5% | −18.0% | −19.9% | −45.6% | $1,258 |
| Gold | daily | 118 | 35.6% | 0.83 | −6.0% | −9.2% | −1.1% | −4.9% | −14.5% | $75 |
| Silver | 15-minute | 724 | 19.1% | 0.18 | −98.8% | −98.8% | −98.8% | 0.0% | −98.8% | $9,361 |
| Silver | 1-hour | 1,259 | 26.8% | 0.43 | −93.2% | −93.8% | −92.2% | −12.6% | −93.6% | $7,060 |
| Silver | 4-hour | 679 | 32.5% | 0.80 | −37.5% | −37.8% | −19.1% | −22.7% | −46.2% | $3,362 |
| Silver | daily | 123 | 30.9% | 0.94 | −2.2% | −7.2% | −3.7% | +1.6% | −13.2% | $232 |
| Nasdaq 100 | 15-minute | 4,126 | 30.5% | 0.71 | −98.6% | −98.6% | −98.1% | −26.3% | −98.8% | $8,615 |
| Nasdaq 100 | 1-hour | 2,857 | 31.8% | 0.81 | −77.3% | −80.7% | −71.2% | −21.3% | −83.5% | $3,460 |
| Nasdaq 100 | 4-hour | 850 | 34.6% | 1.00 | −1.9% | −32.9% | +25.4% | −21.8% | −38.2% | $1,252 |
| Nasdaq 100 | daily | 170 | 28.2% | 0.75 | −13.0% | −14.6% | −8.3% | −5.1% | −21.0% | $66 |
| S&P 500 | 15-minute | 6,814 | 31.5% | 0.83 | −99.7% | −99.7% | −98.9% | −71.9% | −99.8% | $13,547 |
| S&P 500 | 1-hour | 2,865 | 32.6% | 0.89 | −58.4% | −70.5% | −64.1% | +15.8% | −75.8% | $4,325 |
| S&P 500 | 4-hour | 828 | 32.5% | 0.95 | −13.9% | −26.8% | −17.2% | +4.0% | −41.4% | $1,061 |
| S&P 500 | daily | 169 | 26.6% | 0.71 | −16.8% | −21.9% | −6.5% | −11.0% | −25.0% | $81 |
| DAX | 15-minute | 8,260 | 32.8% | 0.86 | −97.6% | −97.8% | −91.3% | −72.1% | −98.4% | $8,044 |
| DAX | 1-hour | 2,806 | 32.6% | 0.87 | −78.6% | −79.3% | −40.7% | −64.0% | −84.3% | $2,764 |
| DAX | 4-hour | 751 | 35.4% | 0.96 | −9.7% | −24.8% | −5.6% | −4.4% | −36.6% | $554 |
| DAX | daily | 125 | 29.6% | 1.00 | +0.1% | −8.0% | +5.1% | −4.8% | −12.9% | $33 |
Profit factor is the money won divided by the money lost. Above 1 means the test made money. Max drawdown is the biggest fall from the highest balance. 2019–2022 and 2023–2025 show the result in each period on its own. Monte Carlo drawdown: we shuffled the trades into a random order 1,000 times; in 95% of those runs, the worst drawdown was this number or less. Result is the change in the account, with the risk of each trade set to 1% of the account at that time.
The account balance
The best test and the middle test
What we found
What the 36 backtests show
All 27 tests on 15-minute, 1-hour and 4-hour charts lost money. Each test started with its own $10,000 account. On 15-minute charts, every account lost more than 97%, and 7 of the 9 were already down 90% by February 2020. On those charts, the spread (the gap between the buy and the sell price) was on average 10.7% of the distance to the stop-loss, and 71% on silver's 15-minute chart. So each trade paid about a tenth of what it risked, just in trading costs. On daily charts, the spread was only 0.8% of that distance. AUD/USD daily was the only test that made money in both 2019–2022 and 2023–2025. It passed all four of our checks (see 'How a test passes' below), with 136 trades, but it grew the account by only 6.2% in 7 years. Our results do not include overnight (swap) fees, which matter most on daily charts.
Most trades closed within a few candles, when %K crossed back over %D. R is 1% of the account when the trade opens: the amount each trade is sized to lose at its stop-loss. Results in R are after trading costs, so −0.16R is a loss of 0.16% of the account. On 1-hour, 4-hour and daily charts, the standard rules made 32,108 trades, and half of them lasted 4 candles or less. 66.0% of them closed at the cross back, and they lost 0.16R on average. Only 15.1% reached the other zone, where they made 1.55R on average. These wins did not cover the cross-back losses and the 18.9% of trades that hit the stop-loss. The same was true in all 36 tests, including the 15-minute charts (see the table below).
Crosses below 20 or above 80 did no better than crosses anywhere else. To check this, we tested a version that takes every cross of %K and %D, with the same exits as the standard rules. In that version, on 1-hour, 4-hour and daily charts, the crosses in the zones lost 0.047R per trade, and the other crosses lost 0.033R per trade. On each of the three charts on its own, the crosses in the zones also lost more (see the comparison below). Taking every cross also meant almost 3 times as many trades as the standard rules. Since the average trade lost money, more trades meant bigger losses.
Holding each trade until %K reached the other zone did not make the average trade better. With this change, a cross back no longer closed the trade. On 1-hour, 4-hour and daily charts, more trades won: 37.3%, compared with 32.9% with the standard rules. But 62.6% of the trades hit the stop-loss, and the average trade was about the same: −0.055R, compared with −0.054R. From 2019 to 2025, 5 of the 36 tests made money with this change, compared with 3 with the standard rules. The best were GBP/USD daily (+14.7%) and AUD/USD daily (+10.4%).
On the 3 stock indices, which rose 132% to 298% from 2019 to 2025, %K was above 80 far more often than below 20, so the rules sold short twice as often as they bought: 7,617 sells and 3,804 buys on 1-hour, 4-hour and daily charts. The sells lost 0.043R per trade, and the buys 0.019R.
How the trades ended
2 trades in 3 closed at the cross back, and on average they lost money
Each row puts together all the trades of the standard rules on one chart, across the 9 markets, from 2019 to 2025. A cross back is when %K crosses below %D during a buy, or above %D during a sell, before %K reaches the other zone. Each cell shows the share of the trades that ended that way, and what they made on average. Results are in R (1% of the account when the trade opened), after trading costs.
| Chart | Closed at the cross back | Reached the other zone (80 or 20) | Hit the stop-loss | Average trade |
|---|---|---|---|---|
| 15-minute (40,335 trades) | 66.5% of trades, −0.21R on average | 15.5%, +1.36R | 18.0%, −0.98R | −0.105R |
| 1-hour (23,875 trades) | 65.7%, −0.16R | 15.1%, +1.52R | 19.1%, −0.98R | −0.062R |
| 4-hour (6,993 trades) | 66.3%, −0.15R | 15.1%, +1.66R | 18.6%, −0.97R | −0.028R |
| Daily (1,240 trades) | 69.8%, −0.19R | 14.8%, +1.52R | 15.0%, −0.87R | −0.033R |
On 15-minute charts, 60% of the trades are from 2019 and 2020, because after that the accounts were too small to take most signals. On daily charts, 4 trades (0.3%) were still open at the end of 2025, and the test closed them then. On daily charts, the trades that hit the stop-loss lost 0.87R on average, less than 1R. This is because on gold, silver, the Nasdaq 100 and the DAX, the trade size was rounded down to a size the market allows.
The 20 and 80 zones
Crosses in the zones did no better than other crosses
This comparison comes from the version that takes every cross of %K and %D, with the same exits as the standard rules. It puts together that version's trades on 1-hour, 4-hour and daily charts, from 2019 to 2025, split by where the two lines were when they crossed. Results are in R (1% of the account when the trade opened), after trading costs.
- Crosses with both lines below 20 (buys) or above 80 (sells)
- −0.047R per trade, over 27,391 trades. 33.0% of them made money.
- All other crosses
- −0.033R per trade, over 61,285 trades. 41.0% of them made money.
On each chart on its own, the crosses in the zones also did worse. On 1-hour charts, they lost 0.055R per trade, and the other crosses lost 0.042R. On 4-hour charts, they lost 0.028R, and the other crosses 0.010R. On daily charts, they lost 0.026R, and the other crosses made +0.004R. In 2019–2022, the crosses in the zones lost 0.055R per trade and the other crosses lost 0.035R. In 2023–2025, they lost 0.032R and 0.029R. This version took fewer zone crosses than the standard rules (27,391, compared with 32,108 trades), almost all of the difference on 1-hour charts. There, its accounts lost so much that 21,738 signals were too small to trade.
Trade by trade
With every version, the average trade lost money
Each row is one version: the standard rules, or one of the three changes under 'Can the stochastic crossover (14, 3, 3) be improved?' below. Each row puts together all the trades of that version on 1-hour, 4-hour and daily charts: 27 tests from 2019 to 2025. The win rate is the share of trades that made money. Results are in R (1% of the account when the trade opened), after trading costs.
| Version | Trades | Win rate | Average winning trade | Average losing trade | Average trade |
|---|---|---|---|---|---|
| Standard rules | 32,108 | 32.9% | +0.94R | −0.54R | −0.054R |
| Every cross, wherever the lines are | 88,676 | 38.5% | +0.62R | −0.45R | −0.037R |
| With a 200 EMA trend filter | 7,842 | 33.0% | +0.96R | −0.57R | −0.064R |
| Hold until the other zone | 24,650 | 37.3% | +1.49R | −0.98R | −0.055R |
We leave out the 15-minute charts here, because those accounts lost 90% early and then skipped most signals, a different number in each version (see 'Smallest trade size' further down). 1-hour charts give most of the trades: 23,875 out of 32,108 with the standard rules. With the 200 EMA filter, the average trade was a little worse than with the standard rules on 1-hour charts: −0.075R, compared with −0.062R. On 4-hour charts, it was −0.031R, compared with −0.028R. It was better on daily charts (+0.021R, compared with −0.033R), but on only 248 trades.
The strategy
What the stochastic crossover (14, 3, 3) is
George Lane, an American trader, made the stochastic oscillator popular in the 1950s. It compares each close with the price range of the last 14 candles, on a scale from 0 to 100. A reading of 100 means the candle closed at the highest price of those 14 candles, and 0 means it closed at the lowest. The 14, 3, 3 settings draw two lines. %K is that reading, averaged over the last 3 candles. %D, the signal line, is the average of the last 3 %K values, so it moves more slowly. This version is often called the slow stochastic. A common way to trade it: below 20 is oversold, so you look for a buy, and above 80 is overbought, so you look for a sell. The crossover strategy waits for the two lines to cross in these zones. It buys when %K crosses above %D with both lines below 20, as a sign that price may stop falling and start to rise. It sells short when %K crosses below %D with both lines above 80.
- Stochastic
- 14, 3, 3, often called the slow stochastic. 14: the range of the last 14 candles. 3: %K is averaged over 3 candles. 3: %D is the average of the last 3 %K values. Levels 20 and 80.
- Entry
- %K crosses above %D with both lines below 20 (buy), or below %D with both lines above 80 (sell short). The candle where the lines cross is the signal candle. The trade opens at the next candle's open.
- Exit
- Whichever comes first: %K crosses back over %D, %K reaches the other zone (80 for a buy, 20 for a sell), or the stop-loss
- Stop-loss
- 1.5 × ATR(14) from the signal candle's close. ATR is how far price moves in an average candle, over the last 14 candles. The stop-loss does not move. We did not test other stop-loss distances.
- Take-profit
- None. The cross back or the other zone closes the trade.
- Trading costs on each market
- Each trade pays the market's spread, the gap between the buy and the sell price. A pip is a price move of 0.0001 on EUR/USD, GBP/USD and AUD/USD, and of 0.01 on USD/JPY. We used: EUR/USD 0.8 pips, GBP/USD 1.2 pips, USD/JPY 0.9 pips, AUD/USD 1.0 pip, gold $0.30 an ounce, silver $0.020 an ounce, the Nasdaq 100 1.5 points, the S&P 500 0.4 points and the DAX 1.0 point. For silver, the Nasdaq 100 and the DAX, we used wider spreads than our backtest engine's own, to match a typical retail broker. Every trade opens at the next candle's open, and closes at the next candle's open or at the stop-loss. Each opening and each closing pays half the spread. There is no commission.
- Risk per trade
- 1% of the account, measured from the signal candle's close to the stop-loss. Two things change the real risk a little. The trade opens at the next candle's open, not at the signal candle's close. And its size is rounded down to a size the market allows.
- Smallest trade size
- The smallest trade size is 1,000 units of a currency, 1 ounce of gold, 50 ounces of silver, or 0.1 of an index contract. On an index, 0.1 of a contract makes or loses 0.10 of a dollar or euro for each point the index moves. If a trade that risks 1% would be smaller, the test skips the signal. With the standard rules on 1-hour, 4-hour and daily charts, it skipped 1,370 out of the 33,478 signals that came while no trade was open. 1,186 of them were on silver's 1-hour chart, where the account had lost 90% by June 2022. On 15-minute charts, 7 of the 9 accounts had lost 90% by February 2020. After that, most signals were too small to trade, and the test skipped 58,570 out of 98,905.
- Overnight (swap) fees
- Not included. Brokers charge (or sometimes pay) a fee for each night a trade stays open. With the standard rules, half of the trades stayed open 4 hours or less on 1-hour charts. On 4-hour charts, half stayed open 16 hours or less, and on daily charts 6 days or less. On 4-hour and daily charts, a trade was open 29% to 37% of the time. So these fees matter most for the daily tests.
- Price data
- We used 1-minute prices from January 2019 to December 2025, and built 15-minute, 1-hour, 4-hour and daily candles from them. 4-hour and daily candles start at 17:00 New York time. We start in 2019 because our index prices before mid-2018 often did not move outside stock exchange hours. Our S&P 500 and DAX data also has a few tiny candles just after the Friday close (13 and 29 in these years), which the strategy skips.
The rules we tested
- The stochastic (14, 3, 3) is worked out in three steps. First, it measures where the candle closed within the range of the last 14 candles, from 0 (at the lowest price) to 100 (at the highest price). Then %K is the average of the last 3 of these readings, and %D is the average of the last 3 %K values. We check each candle when it closes.
- Buy when %K crosses above %D and both lines are below 20. A cross means that %K was at or below %D when the previous candle closed, and is above %D when this candle closes. We call this candle the signal candle. The trade opens at the next candle's open.
- Sell short when %K crosses below %D and both lines are above 80. The trade opens at the next candle's open.
- Close a buy when %K crosses back below %D, or when %K reaches 80 or higher, whichever comes first. Close a sell when %K crosses back above %D, or when %K reaches 20 or lower. The trade closes at the next candle's open.
- ATR is an indicator of how far price moves in an average candle, measured over the last 14 candles. Put the stop-loss 1.5 times the ATR away from the signal candle's close: below it for a buy, above it for a sell. The stop-loss does not move. If price reaches it, the trade closes at the stop price, or at a worse price if price gaps past it (opens beyond it).
- Only one trade is open at a time: a signal that comes while a trade is open is ignored.
- Size every trade so that, if the stop-loss is hit, the trade loses 1% of the account. If that trade would be smaller than the smallest trade size the market allows, skip it.
- We skip two kinds of candles. The first has no price movement at all (a market holiday in our price data). The second is a tiny candle that opens after the Friday close at 17:00 New York time. Skipped candles do not count in the stochastic or the ATR, and they give no signal.
The method
How we tested it
The test
- Markets
- EUR/USD, GBP/USD, USD/JPY, AUD/USD, Gold, Silver, Nasdaq 100, S&P 500 and DAX
- Charts
- 15-minute, 1-hour, 4-hour and daily candles
- Dates
- January 2019 to December 2025
- Account
- $10,000, risking 1% of the account on each trade
- Trading costs
- Included. Every trade pays that market's normal trading cost: the spread, which is the gap between the buy price and the sell price.
How a test passes
We call a test passed only if it meets all four checks. A strategy can make money in one backtest and still fail one of these checks.
- At least 100 trades. With fewer trades, a good result could be luck.
- It made money from 2019 to 2022 and again from 2023 to 2025. This shows whether the strategy kept working in the newer years, not only in the older ones.
- A profit factor (the money won divided by the money lost) of at least 1.2 from 2023 to 2025, after trading costs.
- In 95% of 1,000 random shuffles of the trades, the account must not fall more than 30% from its highest point.
Improvements
Can the stochastic crossover (14, 3, 3) be improved?
Not on most tests. We tested three changes, one at a time, on the same 36 tests. Each one changes a different part of the standard rules. The first takes every cross of %K and %D, wherever the two lines are, instead of only crosses below 20 or above 80. The second trades only in the direction of the trend. It buys only when the signal candle closes above the 200 EMA, and sells short only when it closes below it. The third holds each trade until %K reaches the other zone. If you pick the best change on some years and judge it on the same years, it will almost always look good. You picked it because it did well in those years. So we picked the best one using 2019–2022 only, by the number of tests that made money, and then checked it on 2023–2025.
| Version | Made money 2019–2022years used to pick the best version | Made money 2023–2025years not used to pick it | Made money in both periods | Passed all four checks |
|---|---|---|---|---|
| Standard rules The rules under 'The rules we tested', with nothing changed: only crosses with both lines below 20 (buys) or above 80 (sells). | 5 of 36middle test −38.0% | 7 of 36middle test −12.4% | 1 | 1 |
| Every cross, wherever the lines are Buy every time %K crosses above %D, and sell short every time it crosses below, wherever the two lines are. The exits stay the same. So a cross back closes the trade and also opens a trade the other way, one candle later, once the old trade has closed. | 5 of 36middle test −58.3% | 4 of 36middle test −10.1% | 1 | 0 |
| With a 200 EMA trend filterBest on 2019–2022 The 200 EMA is a moving average of the last 200 closing prices that gives more weight to recent candles. Only buy when the signal candle closes above the 200 EMA, and only sell short when it closes below it. The 200 EMA needs 200 candles, so on daily charts this version could not trade before about October 2019. | 11 of 36middle test −8.8% | 9 of 36middle test −7.5% | 1 | 1 |
| Hold until the other zone Ignore the cross back. Close a buy only when %K reaches 80, and a sell only when %K reaches 20, or at the stop-loss. The entries and the stop-loss stay the same. | 4 of 36middle test −30.6% | 8 of 36middle test −17.6% | 2 | 0 |
The 200 EMA trend filter did best in 2019–2022: 11 of 36 tests made money with it, compared with 5 with the standard rules. But 8 of those 11 were daily tests with only 16 to 38 trades from 2019 to 2025, fewer than the 100 we need to judge a test. In 2019–2022, those 8 daily tests made only +0.2% to +3.4%. In 2023–2025, 9 of 36 tests made money with the filter, compared with 7 with the standard rules. With the filter, only one test with at least 100 trades made money in both periods: the Nasdaq 100 on the 4-hour chart. It made +33.0% from 2019 to 2025, compared with a loss of 1.9% with the standard rules, and it passed all four of our checks. That is 1 test out of 36, so it could be chance. For comparison, the Nasdaq 100 itself rose 298% in these years. With the filter, each trade lost a little more on average: −0.064R, compared with −0.054R, on 1-hour, 4-hour and daily charts. But the filter made 75% fewer trades, so in 32 of 36 tests the result from 2019 to 2025 was better than with the standard rules. Holding until the other zone made money in both periods on 2 tests (GBP/USD and AUD/USD, daily). Taking every cross made money in both periods on 1 test (gold, daily). So none of the three changes made the stochastic crossover make money on most tests.
Passed all four checks with a 200 EMA trend filter
- Nasdaq 100, 4-hour
Questions
Questions traders ask
Does the stochastic crossover strategy work?
No, not in our backtest. From 2019 to 2025, after trading costs, the 14, 3, 3 crossover lost money in 33 of 36 tests: 9 markets, each on 15-minute, 1-hour, 4-hour and daily charts. The stochastic draws two lines, %K and its slower average %D, on a scale from 0 to 100. The standard rules buy when %K crosses above %D with both lines below 20, and sell short when %K crosses below %D with both lines above 80. Only 3 tests made money, all on daily charts. The best, AUD/USD, grew the account by 6.2% in 7 years, and it was the only test that made money in both 2019–2022 and 2023–2025.
What is the best timeframe for the stochastic crossover?
No timeframe made money on most markets in our test. All 27 tests on 15-minute, 1-hour and 4-hour charts lost money. Daily charts had the best results: 3 of 9 tests made money, and the middle daily test (half did better, half did worse) lost 2.2% in 7 years. The middle 4-hour test lost 16.5%. Daily charts made the fewest trades, 118 to 170 per market. Their trading cost (the spread) was on average 0.8% of the distance to the stop-loss, compared with 10.7% on 15-minute charts. Per trade, 4-hour charts lost slightly less than daily charts: 0.028R, compared with 0.033R (R is 1% of the account when the trade opens).
Should you only trade stochastic crosses below 20 and above 80?
No. In our test, waiting for crosses below 20 or above 80 did not help. R is 1% of the account when the trade opens: the amount each trade is sized to lose at its stop-loss. We tested a version that takes every cross of %K and %D, with the same exits as the standard rules. On 1-hour, 4-hour and daily charts, its crosses in the zones lost 0.047R per trade, and its other crosses lost 0.033R. But it made almost 3 times as many trades as the standard rules, so its total losses were bigger. On 1-hour charts, all 9 of its accounts lost between 89.5% and 98.9%.
Should you close a stochastic trade when the lines cross back?
Closing at the cross back made little difference in our test. It kept each loss small, but the strategy still lost money. R is 1% of the account when the trade opens: the amount each trade is sized to lose at its stop-loss. With the standard rules, 66.0% of the trades on 1-hour, 4-hour and daily charts closed at the cross back, and they lost 0.16R on average. When we held each trade until %K reached the other zone (80 for a buy, 20 for a sell), 62.6% of the trades hit the stop-loss instead. The average trade was about the same either way: −0.054R when closing at the cross back, and −0.055R when holding.
The strategy code we ran
This is the exact code from the BacktestingMax strategy engine. Paste it into the strategy editor in the app to run it on any market.
class StochasticCrossover1433(Strategy):
"""The stochastic oscillator crossover (14/3/3, levels 20 and 80), as most traders learn
it, plus three changes traders often make.
The stochastic is worked out here, on the candles the strategy uses (see "Skipped candles"):
raw %K = 100 x (close - lowest low of the last 14 candles)
/ (highest high of the last 14 candles - lowest low of the last 14 candles)
%K = the average of the last 3 raw %K values (the "slow" %K)
%D = the average of the last 3 %K values (the signal line)
This is the slow stochastic that charting platforms show as 14, 3, 3.
Standard (all switches off):
Buy when %K crosses above %D and both lines are below 20 (oversold).
Sell short when %K crosses below %D and both lines are above 80 (overbought).
Close a buy when %K crosses below %D, or when %K closes at 80 or higher, whichever
comes first. Close a sell when %K crosses above %D, or when %K closes at 20 or lower.
Entries and these exits fill at the next candle's open.
Stop-loss 1.5 x ATR(14) from the signal candle's close. It does not move.
Size: 1% of the account lost at the stop. One trade at a time; a signal that comes
while a trade is open is ignored.
Skipped candles: a candle that opens on a Friday at 17:00 New York time or later, and a
flat candle (high = low, a market holiday or a closed hour). The markets in this test are
closed on Friday evenings, but some index and silver data has a few minutes of prices after
the close, which make a tiny extra candle. A skipped candle does not go into the
stochastic, the ATR or the 200 EMA, and gives no signal and no exit. That is why the
indicators are worked out here instead of with the built-in ones. On a market that trades
at the weekend (crypto), remove the Friday skip.
The changes, one at a time:
any_cross = 1 take every cross of %K and %D, wherever the two lines are. The exits
stay the same, so a cross the other way closes the trade and opens the
opposite one, one candle later, once the old trade has closed
trend_filter = 1 only buy when the signal candle closes above the 200 EMA, only sell
short when it closes below it
zone_exit = 1 hold until the other zone: close a buy only when %K reaches 80, and a
sell only when %K reaches 20 (or at the stop-loss). A cross the other
way no longer closes the trade
"""
params = {"k_period": Param(type=int, default=14, min=2, max=100),
"k_smooth": Param(type=int, default=3, min=1, max=20),
"d_period": Param(type=int, default=3, min=1, max=20),
"oversold": Param(type=float, default=20.0, min=1.0, max=49.0),
"overbought": Param(type=float, default=80.0, min=51.0, max=99.0),
"atr_period": Param(type=int, default=14, min=2, max=100),
"atr_mult": Param(type=float, default=1.5, min=0.5, max=5.0),
"ema_period": Param(type=int, default=200, min=2, max=500),
"any_cross": Param(type=int, default=0, min=0, max=1),
"trend_filter": Param(type=int, default=0, min=0, max=1),
"zone_exit": Param(type=int, default=0, min=0, max=1)}
lookback = 2
def init(self, ctx):
self.ny = ctx.clock("America/New_York")
self.highs = [] # highs and lows of the last k_period candles used
self.lows = []
self.raw_k = [] # the last k_smooth raw %K values
self.slow_k = [] # the last d_period %K values
self.prev_k = None # %K and %D on the previous candle used
self.prev_d = None
self.prev_close = None
self.atr = None # Wilder's average of the true range, as the built-in ATR
self.atr_count = 0
self.ema = None # the 200 EMA, started from the average of the first 200 closes
self.ema_seed = []
self.reverse_to = None # any_cross: "long" / "short", once the closing trade is gone
def on_bar(self, ctx):
# A tiny candle after the Friday close, or a flat candle (see above): ignore it.
if self.ny.weekday == 4 and self.ny.hour >= 17:
return
high, low, close = ctx.high[0], ctx.low[0], ctx.close[0]
if high <= low:
return
p = ctx.params
# ATR (Wilder), on the candles used.
if self.prev_close is None:
true_range = high - low
else:
true_range = max(high - low, abs(high - self.prev_close), abs(low - self.prev_close))
self.prev_close = close
self.atr = true_range if self.atr is None else self.atr + (true_range - self.atr) / p.atr_period
self.atr_count += 1
# 200 EMA, on the candles used.
if self.ema is None:
self.ema_seed.append(close)
if len(self.ema_seed) == p.ema_period:
self.ema = sum(self.ema_seed) / p.ema_period
else:
self.ema += (close - self.ema) * 2.0 / (p.ema_period + 1)
# The stochastic 14, 3, 3.
self.highs.append(high)
self.lows.append(low)
del self.highs[:-p.k_period]
del self.lows[:-p.k_period]
k1, d1 = self.prev_k, self.prev_d
k0 = d0 = None
if len(self.highs) == p.k_period:
hh, ll = max(self.highs), min(self.lows)
self.raw_k.append(100.0 * (close - ll) / (hh - ll))
del self.raw_k[:-p.k_smooth]
if len(self.raw_k) == p.k_smooth:
self.slow_k.append(sum(self.raw_k) / p.k_smooth)
del self.slow_k[:-p.d_period]
k0 = self.slow_k[-1]
if len(self.slow_k) == p.d_period:
d0 = sum(self.slow_k) / p.d_period
self.prev_k, self.prev_d = k0, d0
if k0 is None or d0 is None or k1 is None or d1 is None or self.atr_count < p.atr_period:
return
cross_up = k1 <= d1 and k0 > d0 # %K crosses above %D
cross_down = k1 >= d1 and k0 < d0 # %K crosses below %D
pos = ctx.position
if pos is not None:
if pos.is_long:
if k0 >= p.overbought or (p.zone_exit == 0 and cross_down):
ctx.close_position()
if p.any_cross == 1 and cross_down:
self.reverse_to = "short"
else:
if k0 <= p.oversold or (p.zone_exit == 0 and cross_up):
ctx.close_position()
if p.any_cross == 1 and cross_up:
self.reverse_to = "long"
return
# any_cross: a reversal waits one candle for the old trade to close. Drop it if the
# lines crossed back.
pending, self.reverse_to = self.reverse_to, None
if pending == "long" and k0 > d0:
cross_up = True
if pending == "short" and k0 < d0:
cross_down = True
if p.any_cross == 1:
long_zone, short_zone = True, True
else:
long_zone = k0 < p.oversold and d0 < p.oversold
short_zone = k0 > p.overbought and d0 > p.overbought
trend = p.trend_filter == 1
if trend and self.ema is None:
return
entry = close
dist = p.atr_mult * self.atr
if ctx.gate("k_crosses_above_d", cross_up) and ctx.gate("both_lines_oversold", long_zone):
if not ctx.gate("above_200_ema", not trend or entry > self.ema):
return
stop = entry - dist
size = ctx.size_by_risk_pct(0.01, stop=stop, entry=entry)
if ctx.gate("trade_size_allowed", size > 0):
ctx.buy(size=size, sl=stop, tag="stoch_long")
elif ctx.gate("k_crosses_below_d", cross_down) and ctx.gate("both_lines_overbought", short_zone):
if not ctx.gate("below_200_ema", not trend or entry < self.ema):
return
stop = entry + dist
size = ctx.size_by_risk_pct(0.01, stop=stop, entry=entry)
if ctx.gate("trade_size_allowed", size > 0):
ctx.sell(size=size, sl=stop, tag="stoch_short")
Your turn
Test the stochastic crossover (14, 3, 3) yourself
Change the settings, the market or the stop-loss and see the result in seconds. Ask the BacktestingMax AI: Backtest the stochastic crossover (14, 3, 3) on GBP/USD, daily chart, and compare closing at the cross back with holding until %K reaches the other zone
.
Free account. No credit card needed.
See every strategy we tested Browse strategies shared by tradersBacktest on historical data with trading costs, run by BacktestingMax. Past results do not guarantee future results, and nothing here is financial advice. Updated 26 October 2026.