Tested by BacktestingMax
Change of character (CHoCH) and market structure shift (MSS) backtest
Does the change of character (CHoCH) strategy make money?
Mostly no. We backtested it from 2020 to 2025, after trading costs, on 10 markets: 4 currency pairs, gold, silver and 4 stock indices. Each ran on 5-minute, 15-minute, 1-hour and 4-hour charts: 40 tests. All 20 tests on 5-minute and 15-minute charts lost money. On 1-hour and 4-hour charts, 8 of 20 made money, 7 of them with enough trades to judge. Only 5 made money in both 2020–2023 and the newer years, 2024–2025: 2 on USD/JPY, and 1 each on EUR/USD, GBP/USD and the DAX. In a downtrend, each peak on the chart is lower than the one before: a lower high. A change of character is the first candle that closes above the last lower high, a sign that the trend may turn up. In an uptrend, it is the first close below the last higher low (a dip that is higher than the one before). Our standard rules enter at the next candle's open, with the take-profit at 2R: twice the distance to the stop-loss. About 1 trade in 3 reached it, which at 2R only breaks even before trading costs. After costs, 32 of the 40 tests lost money. Other take-profits and the extra rules that smart money concepts (SMC) traders add did not change this much. One of those rules is displacement, a big breaking candle, which many traders say a real market structure shift (MSS) needs.
40 backtests 2020–2025 Trading costs included Tested 26 October 2026
Where it worked 40 backtests- Made money
- 7 of 40 tests, with the standard rules
- Made money in both periods
- 5 2020–2023 and again 2024–2025
- Best backtest
- +66% USD/JPY, 1-hour chart: account growth 2020–2025
- With a big breaking candle (displacement)
- 4 tests made money in both periods, compared with 5 with the standard rules
Made money in both periods
- EUR/USD, 4-hour
- GBP/USD, 1-hour
- USD/JPY, 1-hour
- USD/JPY, 4-hour
- DAX, 1-hour
Lost money on every market
- 5-minute charts
- 15-minute charts
The results
Results on every market
Each box shows one backtest of the change of character (CHoCH) setup with the standard rules, from 2020 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 2020–2023 and 2024–2025, or passed all four of our checks (see "How a test passes" further down).
| Market | 5-minute | 15-minute | 1-hour | 4-hour |
|---|---|---|---|---|
| EUR/USD | −100% Lost money. Account down 90% by May 2022 | −93% Lost money. Account down 90% by Nov 2024 | −46% Lost money | +11% Passed all four checks |
| GBP/USD | −100% Lost money. Account down 90% by Mar 2022 | −80% Lost money | +21% Made money in both periods | −23% Lost money |
| USD/JPY | −96% Lost money. Account down 90% by Jun 2023 | −48% Lost money | +66% Passed all four checks | +25% Passed all four checks |
| GBP/JPY | −100% Lost money. Account down 90% by Nov 2021 | −91% Lost money. Account down 90% by Oct 2025 | +14% Made money overall, lost in one period | +37% Too few trades |
| Gold | −98% Lost money. Account down 90% by Jan 2023 | −59% Lost money | +1% Made money overall, lost in one period | −0.4% Lost money |
| Silver | −99% Lost money. Account down 90% by Apr 2021 | −96% Lost money. Account down 90% by Nov 2022 | −35% Lost money | −14% Lost money |
| Nasdaq 100 | −88% Lost money | −48% Lost money | −35% Lost money | −22% Lost money |
| S&P 500 | −94% Lost money. Account down 90% by Jun 2024 | −59% Lost money | −43% Lost money | −0.9% Lost money |
| Dow Jones | −38% Lost money | −47% Lost money | −45% Lost money | −13% Lost money |
| DAX | −88% Lost money | −36% Lost money | +30% Made money in both periods | −3% Too few trades |
All the numbers, test by test
| Market | Chart | Trades | Win rate | Profit factor | Result | Max drawdown | 2020–2023 | 2024–2025 | Monte Carlo drawdown | Trading costs |
|---|---|---|---|---|---|---|---|---|---|---|
| EUR/USD | 5-minute | 4,259 | 31.9% | 0.87 | −99.5% | −99.6% | −96.6% | −86.1% | −99.7% | $8,764 |
| EUR/USD | 15-minute | 1,694 | 29.9% | 0.75 | −93.1% | −94.1% | −84.0% | −56.6% | −94.5% | $2,774 |
| EUR/USD | 1-hour | 476 | 30% | 0.82 | −46.3% | −49.5% | −21.0% | −32.0% | −57.5% | $988 |
| EUR/USD | 4-hour | 107 | 37.4% | 1.15 | +10.5% | −11.2% | +3.6% | +6.7% | −16.6% | $114 |
| GBP/USD | 5-minute | 4,022 | 32.1% | 0.82 | −99.6% | −99.6% | −98.0% | −77.4% | −99.7% | $6,595 |
| GBP/USD | 15-minute | 1,612 | 32.3% | 0.83 | −80.2% | −81.3% | −66.0% | −41.8% | −86.0% | $3,733 |
| GBP/USD | 1-hour | 478 | 36% | 1.06 | +21.1% | −30.9% | +0.6% | +20.4% | −34.6% | $1,467 |
| GBP/USD | 4-hour | 125 | 27.2% | 0.70 | −22.8% | −23.3% | −14.5% | −9.7% | −31.5% | $137 |
| USD/JPY | 5-minute | 4,615 | 33.7% | 0.90 | −95.5% | −95.9% | −83.5% | −72.9% | −97.5% | $12,040 |
| USD/JPY | 15-minute | 1,614 | 33.8% | 0.94 | −48.3% | −66.2% | −42.9% | −9.5% | −72.5% | $5,940 |
| USD/JPY | 1-hour | 436 | 38.5% | 1.22 | +66.4% | −15.6% | +8.1% | +53.9% | −26.3% | $1,030 |
| USD/JPY | 4-hour | 104 | 41.3% | 1.35 | +24.5% | −11.3% | +13.2% | +10.0% | −13.7% | $89 |
| GBP/JPY | 5-minute | 2,808 | 31.6% | 0.79 | −99.5% | −99.6% | −99.4% | −29.2% | −99.6% | $9,104 |
| GBP/JPY | 15-minute | 1,711 | 31.2% | 0.79 | −91.2% | −91.9% | −81.1% | −53.3% | −93.2% | $4,827 |
| GBP/JPY | 1-hour | 463 | 35.9% | 1.04 | +14.1% | −21.1% | +24.6% | −8.5% | −34.4% | $1,881 |
| GBP/JPY | 4-hour | 94 | 45.7% | 1.59 | +36.6% | −5.9% | +32.8% | +2.8% | −10.8% | $167 |
| Gold | 5-minute | 3,400 | 33% | 0.88 | −97.6% | −98.6% | −96.6% | −29.8% | −98.3% | $9,913 |
| Gold | 15-minute | 1,663 | 33.6% | 0.89 | −59.3% | −74.7% | −66.5% | +21.4% | −75.3% | $4,042 |
| Gold | 1-hour | 436 | 34.4% | 1.00 | +1.4% | −21.2% | +13.8% | −10.9% | −36.2% | $1,148 |
| Gold | 4-hour | 113 | 33.6% | 0.99 | −0.4% | −17.0% | +0.8% | −1.1% | −18.2% | $110 |
| Silver | 5-minute | 1,388 | 30.5% | 0.59 | −99.1% | −99.1% | −99.0% | −6.4% | −99.2% | $7,435 |
| Silver | 15-minute | 1,112 | 27.9% | 0.67 | −96.3% | −96.9% | −95.2% | −22.2% | −96.7% | $5,293 |
| Silver | 1-hour | 451 | 32.8% | 0.86 | −35.1% | −48.5% | −20.6% | −18.3% | −49.5% | $2,405 |
| Silver | 4-hour | 117 | 29.9% | 0.80 | −14.0% | −20.8% | −8.0% | −6.6% | −24.8% | $314 |
| Nasdaq 100 | 5-minute | 4,458 | 33.8% | 0.93 | −88.1% | −89.3% | −80.6% | −38.8% | −94.0% | $10,501 |
| Nasdaq 100 | 15-minute | 1,570 | 33.2% | 0.92 | −48.4% | −63.7% | −45.5% | −5.2% | −70.9% | $2,661 |
| Nasdaq 100 | 1-hour | 483 | 30.8% | 0.87 | −35.1% | −39.0% | −14.7% | −24.0% | −50.3% | $594 |
| Nasdaq 100 | 4-hour | 133 | 27.8% | 0.69 | −22.3% | −25.6% | −22.9% | +0.7% | −30.5% | $56 |
| S&P 500 | 5-minute | 4,393 | 33.5% | 0.91 | −93.8% | −95.5% | −85.3% | −58.0% | −96.6% | $8,850 |
| S&P 500 | 15-minute | 1,547 | 32.8% | 0.92 | −59.1% | −70.0% | −46.1% | −24.2% | −76.0% | $3,742 |
| S&P 500 | 1-hour | 478 | 30.3% | 0.84 | −42.6% | −44.8% | −31.6% | −16.2% | −55.8% | $634 |
| S&P 500 | 4-hour | 113 | 34.5% | 0.99 | −0.9% | −11.2% | −7.6% | +7.2% | −20.5% | $69 |
| Dow Jones | 5-minute | 4,364 | 34.7% | 0.99 | −38.0% | −72.9% | +72.4% | −64.0% | −83.1% | $23,368 |
| Dow Jones | 15-minute | 1,581 | 32.8% | 0.95 | −47.2% | −59.9% | −11.3% | −40.5% | −69.9% | $3,326 |
| Dow Jones | 1-hour | 487 | 29.2% | 0.80 | −45.4% | −53.5% | −29.9% | −22.1% | −55.1% | $415 |
| Dow Jones | 4-hour | 134 | 29.9% | 0.81 | −13.3% | −14.8% | −4.3% | −9.4% | −24.2% | $51 |
| DAX | 5-minute | 4,563 | 33% | 0.95 | −88.3% | −92.8% | −77.9% | −47.0% | −94.7% | $7,902 |
| DAX | 15-minute | 1,482 | 33.1% | 0.95 | −35.5% | −48.0% | −16.6% | −22.7% | −67.7% | $2,074 |
| DAX | 1-hour | 456 | 34.9% | 1.10 | +30.2% | −25.7% | +8.6% | +19.8% | −29.0% | $463 |
| DAX | 4-hour | 95 | 33.7% | 0.95 | −2.5% | −17.9% | −3.6% | +1.2% | −17.2% | $37 |
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. 2020–2023 and 2024–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 40 backtests show
With the standard rules, 8 of the 40 tests made money from 2020 to 2025, after trading costs, all on 1-hour and 4-hour charts. On 5-minute and 15-minute charts, all 10 markets lost money, and 10 of those 20 tests lost more than 90% of the account. On these charts the swings are small, so the stop-loss is close. On EUR/USD's 5-minute chart, half of the stop-losses were 8.1 pips away or closer (a pip is 0.0001 on EUR/USD). That is only 10 times the spread, the gap between the buy and the sell price that every trade pays. Across all 5-minute charts, costs took 7.8% of the money each trade risked, compared with 1.0% on 4-hour charts. The best test was USD/JPY on 1-hour charts: +66.4%. Its biggest fall from a high point was 15.6%. GBP/JPY on 4-hour charts made +36.6%, but with only 94 trades, fewer than the 100 we need to judge a test. That is why the 'Where it worked' box at the top counts 7 tests, not 8.
We also checked the older years (2020–2023) and the newer years (2024–2025) on their own: a strategy that keeps working should make money in both. 5 tests did: USD/JPY on 1-hour and 4-hour charts, EUR/USD on 4-hour charts, and GBP/USD and the DAX on 1-hour charts. Three of them passed all four of our checks (see 'How a test passes'): the two USD/JPY tests and EUR/USD on 4-hour charts. By market, the two yen pairs, USD/JPY and GBP/JPY, were the only ones that made money on both 1-hour and 4-hour charts. GBP/JPY's 4-hour test had only 94 trades, though. With the standard rules, the change of character lost money on every chart of silver and of the three US stock indices: 16 of 16 tests. These indices are the Nasdaq 100, the S&P 500 and the Dow Jones. Before costs, the average trade was close to zero (see the next finding). So even the 5 tests that made money in both periods may be luck. Passing all four checks makes luck less likely, but does not rule it out. Treat these 5 tests as a reason to test further, not as proof.
Did a change of character show which way price would go next? On average, no: with every take-profit and swing size we tried, the share of winning trades sat right at the break-even rate. R is what one trade risks: 1% of the account. With the standard 2R take-profit, 32.8% of the 59,635 trades won, against 33.3% needed to break even, and before costs the average trade made −0.011R. We also checked whether this depends on the settings (see the table 'Other take-profits and swing sizes' below). With a 1R take-profit, 49.3% of the trades won, and 50% is needed to break even. With a 3R take-profit, 24.8% won, and 25% is needed. With bigger swing points (a peak or dip must be higher or lower than the 10 candles on each side, not 3), 32.8% won. With every setting, the average trade before costs was between −0.014R and −0.006R, so after costs most tests lost money. One more split, by direction: 34.2% of buys won, and 31.5% of sells. All 10 markets were higher at the end of 2025 than at the start of 2020, and prices that rise over the years help buy trades.
Did the common SMC rules help? No. With each one, about 1 trade in 3 still won (see the table 'About 1 trade in 3 won with each rule we tested' below). The first rule is a liquidity sweep. Before a buy, price first dips below the swing low that came before the lower high. Many traders keep stop-loss orders just below that low. Only then does price turn up. Most SMC traders count it as a sweep only when price goes below that low with a wick, and no candle closes below it. A wick is the thin line under (or over) a candle's body. After such a sweep, 32.6% of the trades won, compared with 32.9% with no sweep at all. Trades with a big breaking candle (displacement) after a sweep with a wick, close to how ICT describes a market structure shift, won 32.7% of the time. Trades taken only in the direction of the higher-timeframe trend won 33.3%. That is the trend on a chart with longer candles, for example the 4-hour chart for 15-minute trades. Trades that waited for price to come back part of the way (a pullback entry) won 32.7%. Before costs, the average trade of each of these groups was between −0.017R and −0.001R.
ICT traders often trade only in two 'kill zones', the hours when they expect the biggest moves. These are London, 02:00 to 05:00 New York time, and New York, 07:00 to 10:00. On 5-minute and 15-minute charts, the trades that opened in these hours lost less after costs. The average trade after costs was −0.026R in London and −0.049R in New York, compared with −0.102R at other times. Part of the difference was costs: in these busy hours, costs took a smaller share of each trade, because the stop-loss was further away. Our test uses the same spread at every hour. With a real broker, spreads are usually narrower in busy hours and wider in quiet ones. The London kill zone was also a little better before costs: +0.031R per trade, and above zero in both 2020–2023 and 2024–2025. But after costs, both kill zones still lost money.
Does it depend on the settings?
Other take-profits and swing sizes
The first row is the standard rules. Each other row changes one setting, on the same 40 tests (10 markets, each on 5-minute, 15-minute, 1-hour and 4-hour charts), from 2020 to 2025. The win rate is the share of trades that made money. The win rate needed to break even is the share at which the trades would make nothing before costs. The averages are in R: 1R is 1% of the account at the time of the trade.
| Setting | Trades | Win rate | Win rate needed to break even | Average trade before costs | Average trade after costs | Tests that made money (at least 100 trades) | Tests that made money in both periods (at least 100 trades) |
|---|---|---|---|---|---|---|---|
| Standard rules: 2R take-profit, swing points of 3 candles on each side | 59,635 | 32.8% | 33.3% | −0.011R | −0.077R | 7 | 5 |
| 1R take-profit | 77,704 | 49.3% | 50% | −0.009R | −0.069R | 8 | 6 |
| 3R take-profit | 49,027 | 24.8% | 25% | −0.006R | −0.075R | 5 | 2 |
| Swing points of 10 candles on each side | 25,974 | 32.8% | 33.3% | −0.014R | −0.058R | 8 | 5 |
These rows check what the result depends on. They are not versions, and we did not pick a best one from them. A closer take-profit is reached more often, but each winning trade makes less. Bigger swing points give fewer trades, and their stop-loss is further away, so costs take a smaller share of each trade. With the 3R take-profit and with the bigger swing points, many 4-hour tests had fewer than 100 trades. USD/JPY on 1-hour charts made money in both periods with all four settings.
Does any SMC rule help?
About 1 trade in 3 won with each rule we tested
Each row is a group of trades from the 40 tests, from 2020 to 2025. The win rate is the share of trades that made money. With the 2R take-profit, a win rate of 33.3% only breaks even before costs. The averages are in R: 1R is 1% of the account at the time of the trade. The rows that start with 'Version' are the versions under 'Can the change of character (CHoCH) setup be improved?', which take their own trades. The other rows split the trades of the standard rules, or of the version above them.
| Group | Trades | Win rate | Average trade before costs | Average trade after costs |
|---|---|---|---|---|
| All trades of the standard rules | 59,635 | 32.8% | −0.011R | −0.077R |
| No liquidity sweep first | 37,723 | 32.9% | −0.009R | −0.085R |
| A sweep first, with a wick only | 6,176 | 32.6% | −0.017R | −0.074R |
| A close past the earlier swing point first | 15,736 | 32.7% | −0.015R | −0.058R |
| Version: with a big breaking candle (displacement) | 36,072 | 32.9% | −0.010R | −0.071R |
| Big breaking candle after a sweep with a wick only | 4,353 | 32.7% | −0.014R | −0.070R |
| Version: with the higher-timeframe trend | 31,232 | 33.3% | −0.001R | −0.074R |
| Version: with a pullback entry | 51,680 | 32.7% | −0.012R | −0.069R |
| 5- and 15-minute charts: London kill zone (02:00 to 05:00 New York time) | 7,625 | 34.4% | +0.031R | −0.026R |
| 5- and 15-minute charts: New York kill zone (07:00 to 10:00 New York time) | 8,419 | 33.2% | 0.000R | −0.049R |
| 5- and 15-minute charts: all other hours | 37,812 | 32.4% | −0.024R | −0.102R |
For a buy, we checked whether price went below the swing low that came before the lower high, before a candle closed above the lower high. 'A sweep first, with a wick only' means that price went below that low, but no candle closed below it. 'A close past the earlier swing point first' means that at least one candle closed below it. SMC traders call that a break of structure, not a sweep. 'No liquidity sweep first' means that price did not go below that low. For sells, it is the opposite. The row 'Big breaking candle after a sweep with a wick only' is part of the big-breaking-candle version. It is close to how ICT describes a market structure shift: a sweep, then displacement. The kill zone rows go by the time the trade opened. The standard rules skip every change of character that comes while a trade is open, so they traded 59,635 out of 128,958 changes of character in the data. The versions take fewer or shorter trades, so they skip fewer. In the pullback version, the entry is half as far from the stop-loss, so each trade needs twice the size for the same 1% risk. 16,749 out of its 51,680 trades needed a bigger size than our backtest engine allows, and risked less than 1%, which brings its averages in R closer to zero.
The strategy
What the change of character (CHoCH) setup is
Change of character (CHoCH) is an idea from smart money concepts (SMC). SMC is a way of trading built on market structure: the pattern of peaks (swing highs) and dips (swing lows) that price leaves on a chart. SMC started from the teaching of ICT (The Inner Circle Trader, the online name of the trading teacher Michael Huddleston). Many teachers now explain SMC in their own way. In a downtrend, price makes lower highs and lower lows: each peak and each dip is lower than the one before. In an uptrend, price makes higher highs and higher lows. In a downtrend, a candle that closes below the last swing low continues the trend. SMC traders call this a break of structure (BOS). A change of character is the first break the other way. In a downtrend, it is the first candle that closes above the last lower high, a sign that the downtrend may be over. In an uptrend, it is the first close below the last higher low. ICT calls this break a market structure shift (MSS). Many ICT traders count it only when the breaking candle is big and strong, and they call such a candle displacement. Traders buy after a change of character up and sell after one down, with the stop-loss at or just past the low (or high) that the move started from. Teachers define each step a little differently, so 'The rules we tested' lists our exact version.
- Swing points
- 3 candles on each side
- Change of character
- The first candle that closes above the last lower high (buy), or below the last higher low (sell)
- Entry
- At the next candle's open
- Stop-loss
- The lowest price since the lower high (buy), or the highest price since the higher low (sell)
- Take-profit
- 2R: twice the distance from the close of the breaking candle to the stop-loss
- Risk per trade
- 1% of the account balance at the time of the trade
- Trading costs
- The spread: the gap between the buy price and the sell price. Currency pairs: 0.8 pips on EUR/USD, 1.2 on GBP/USD, 0.9 on USD/JPY and 2.5 on GBP/JPY (a pip is 0.0001 on EUR/USD and GBP/USD, and 0.01 on USD/JPY and GBP/JPY). Metals: $0.30 on gold and $0.020 on silver. Stock indices: 1.5 points on the Nasdaq 100, 0.4 on the S&P 500, 2.0 on the Dow Jones and 1.0 on the DAX. For silver, the Nasdaq 100, the Dow Jones and the DAX, these are wider than our backtest engine's own spreads, to match a typical retail broker. Our test treats chart prices as the middle between the buy price and the sell price. So a trade pays half the spread when it opens at the market price, and half when it closes at the stop-loss. A take-profit, and the limit order (an order that waits at a set price) of the pullback version, fill at their own price and pay no spread. With a real broker, a buy's take-profit fills only when the sell price reaches it, and a sell's take-profit only when the buy price does. So winning trades would pay about half a spread more. The limit orders of the pullback version have the same problem, and some of them would not fill. No commission
- Smallest trade size
- A trade that risks 1% can be smaller than the smallest trade size a market allows. The smallest sizes are 1,000 units of a currency pair, 1 ounce of gold and 50 ounces of silver. On an index, it is 0.1 of a contract, which gains or loses 0.10 dollars per point (0.10 euros on the DAX). When a trade would be smaller than these sizes, the test skips it. With the standard rules, this happened 50 times on 1-hour and 4-hour charts, mostly on the Dow Jones and silver, where the stop-loss is far away. On 5-minute and 15-minute charts, it happened 159 times while the account still held more than 10% of its starting balance. It happened 16,665 times after an account had lost 90%, because 1% of what was left was too small for most trades. The tests kept running after a 90% loss. A very close stop-loss can also need a bigger trade than our backtest engine allows. The limit is 30 times the account balance for currency pairs, and 20 times for gold, silver and the indices. Then the trade uses the biggest size allowed and risks less than 1%. This happened in 1,142 trades, all on 5-minute and 15-minute charts
- Overnight (swap) fees
- Not included. Brokers charge (or sometimes pay) a fee for each night a trade stays open. Half of the trades stayed open 13.8 hours or longer on 1-hour charts, and 4.6 days or longer on 4-hour charts. So with a real broker, the results could be higher or lower
- Price data
- CFD prices: the prices a broker quotes for a CFD (contract for difference), the product most brokers outside the US offer for trading these markets. On some Fridays, our S&P 500 and DAX data has a few minutes of prices after the weekly close at 17:00 New York time, which make a tiny extra candle. The rules leave these candles out
The rules we tested
- A swing high is a candle whose high is higher than the highs of the 3 candles before it and the 3 candles after it. A swing low is a candle whose low is lower than the lows of the 3 candles before it and the 3 candles after it. So a swing point is known only 3 candles after it forms.
- We count a swing high as a lower high in a downtrend when two things are true. First, it is lower than the swing high before it. Second, the swing low just before it is lower than the swing low before that one (a lower low).
- Buy when a candle closes above the most recent swing high, if that swing high is a lower high in a downtrend. This candle is the change of character, and we call it the breaking candle. Only the first close above that swing high counts. The trade opens at the next candle's open.
- Put the stop-loss at the lowest price since that swing high: the low that the move up started from.
- R is what one trade risks. In price, 1R is the distance from the close of the breaking candle to the stop-loss. In money, 1R is 1% of the account (see the sizing rule below). Put the take-profit 2R above that close. The trade size and the take-profit are measured from that close. The trade opens a moment later, at the next candle's open, so a loss at the stop-loss can cost a little more or less than 1%.
- Sell (go short: sell first, to profit if price falls) in the opposite case. A swing low is a higher low in an uptrend when it is higher than the swing low before it. The swing high just before it must also be higher than the swing high before that one. Sell when a candle closes below the most recent swing low, if that swing low is a higher low. The stop-loss goes at the highest price since that swing low, and the take-profit 2R below the close.
- Take one trade at a time. A change of character is skipped while a trade is open, and also on the candle where the open trade closes.
- Size every trade so that a loss of 1R (from the close of the breaking candle to the stop-loss) costs 1% of the account balance at that time.
- Leave out flat candles (high = low: no price moved, for example on a holiday). Also leave out candles that open after the weekly close, on a Friday at 17:00 New York time or later. They are never swing points or signals.
The method
How we tested it
The test
- Markets
- EUR/USD, GBP/USD, USD/JPY, GBP/JPY, Gold, Silver, Nasdaq 100, S&P 500, Dow Jones and DAX
- Charts
- 5-minute, 15-minute, 1-hour and 4-hour candles
- Dates
- January 2020 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 2020 to 2023 and again from 2024 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 2024 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 change of character (CHoCH) setup be improved?
We tried three changes that SMC traders often make, one at a time, on the same 10 markets and 4 charts. The table below explains each one. If you pick the best change and judge it on the same years you used to pick it, it almost always looks better than it really is. So we picked the best change using 2020–2023 only. The best change is the one that made money in the most tests (every test counts here, even one with fewer than 100 trades). In a tie, it is the one with the better middle test (the median: half of the tests did better, half did worse). Then we checked it on 2024–2025, the years we did not use to pick it.
| Version | Made money 2020–2023years used to pick the best version | Made money 2024–2025years not used to pick it | Made money in both periods | Passed all four checks |
|---|---|---|---|---|
| Standard rules The rules under 'The rules we tested': enter at the next candle's open after the first close beyond the last lower high (buy) or higher low (sell). | 10 of 40middle test −21.9% | 10 of 40middle test −17.2% | 5 | 3 |
| With a big breaking candle (displacement)Best on 2020–2023 Only trade a change of character when the breaking candle is big. ATR (average true range) is how far price moves in an average candle, over 14 candles. For a buy, the breaking candle must close higher than it opened (for a sell, lower). Its body, from the open to the close, must be at least as big as the ATR of the 14 candles before it. ICT traders call a big, strong move like this displacement, and many say a real market structure shift needs it. | 13 of 40middle test −7.5% | 13 of 40middle test −7.4% | 4 | 1 |
| With a pullback entry Many SMC traders wait for price to come back part of the way (a pullback) instead of buying the break. So we don't enter at the next candle. We place a limit order (an order that waits at a set price) halfway between the close of the breaking candle and the stop-loss. Then we wait up to 10 candles for price to reach it. The stop-loss stays the same, so in price, 1R is now the distance from the limit price to the stop-loss: half as far as with the standard rules. The trade is twice as big, so 1R is still 1% of the account. The take-profit is 2R from the limit price. The order is cancelled if price reaches that take-profit level first, and no other trade is taken while it waits. | 7 of 40middle test −24.6% | 7 of 40middle test −19.9% | 0 | 0 |
| With the higher-timeframe trend Only buy when the higher timeframe is in an uptrend: its last swing high is higher than the one before, and so is its last swing low. Only sell when both are lower. The higher timeframe is the 1-hour chart for 5-minute trades, the 4-hour chart for 15-minute trades, and the daily chart for 1-hour and 4-hour trades. It uses the same 3-candle swing points, and only candles that have closed. SMC traders often call this the higher-timeframe bias (HTF bias). | 12 of 40middle test −13.3% | 14 of 40middle test −9.6% | 3 | 1 |
None of the three versions made the change of character clearly better. Each made money in both periods in fewer tests than the standard rules, which did so in 5. The big breaking candle did so in 4 tests, the pullback entry in 0 and the higher-timeframe trend in 3. Using 2020–2023, we picked the big breaking candle. It made money in 13 of 40 tests in 2020–2023, and in 13 in 2024–2025, compared with 10 and 10 with the standard rules. But many of those were 4-hour tests with fewer than 100 trades: 4 of the 13 in 2020–2023 and 7 of the 13 in 2024–2025. Counting only tests with at least 100 trades, it made money in 9 tests in 2020–2023, the same as the standard rules, and in 6 in 2024–2025, compared with 8. In 2024–2025, its middle test lost 7.4%, compared with 17.2% with the standard rules. Part of the smaller loss comes from taking fewer trades (36,072 instead of 59,635 over all tests and years), so it paid costs fewer times. It passed all four checks in only 1 test (GBP/USD on 1-hour charts), compared with 3. Its trades won 32.9% of the time, and before costs the average trade made −0.010R, almost the same as with the standard rules (−0.011R). So it did not pick better trades. It lost a little less for another reason too: a big candle closes further from the stop-loss. On a typical test, its stop-loss was 10% to 21% further away, depending on the chart, so costs took a smaller share of each trade. On 15-minute charts, it made money on all 4 stock indices from 2020 to 2025: the DAX +46.5%, the Nasdaq 100 +21.3%, the S&P 500 +8.1% and the Dow Jones +0.9%. Only the DAX and the Dow Jones made money in both periods. A pullback entry did worst: only 45% of its orders filled, and no test made money in both periods. The higher-timeframe trend made money in 12 tests in 2020–2023 and 14 in 2024–2025, but 5 and 6 of those had fewer than 100 trades. It made money in both periods in 3 tests: USD/JPY and the DAX on 1-hour charts, and the Nasdaq 100 on 5-minute charts.
Passed all four checks with a big breaking candle (displacement)
- GBP/USD, 1-hour
Questions
Questions traders ask
Does the change of character (CHoCH) strategy work?
Mostly no, in our backtest. With our standard rules, it lost money in all 20 tests on 5-minute and 15-minute charts (10 markets, 2020 to 2025, after trading costs). On 1-hour and 4-hour charts, 8 of 20 tests made money (7 of them with enough trades to judge), and 5 made money in both 2020–2023 and 2024–2025. The best was USD/JPY on 1-hour charts: +66.4%. On the Nasdaq 100, the S&P 500, the Dow Jones and silver, it lost money on every chart. About 1 trade in 3 reached the take-profit at 2R (twice the distance to the stop-loss), which only breaks even before costs. After costs, 32 of the 40 tests lost money.
What is the best timeframe for a change of character (CHoCH)?
In our test, 1-hour and 4-hour charts did best. On 5-minute and 15-minute charts, the change of character lost money on all 10 markets, and 10 of those 20 tests lost more than 90% of the account. On these charts the swings are small, so the stop-loss is close and trading costs take a big share of each trade. Costs took 7.8% of the money each trade risked on 5-minute charts, compared with 1.0% on 4-hour charts. On 1-hour charts, 5 of 10 tests made money (USD/JPY +66.4%, the DAX +30.2%), and on 4-hour charts, 3 of 10. Before costs, the average trade made almost nothing on every chart. So the bigger charts lost less mainly because costs took a smaller share, not because the change of character worked better there.
Is a market structure shift (MSS) with displacement better than a CHoCH?
It lost a little less, but it was not more reliable, in our backtest. Many ICT traders count a market structure shift only when the breaking candle is big and strong (displacement). In our version, the candle's body (open to close) had to be at least as big as the ATR: the average full size of the 14 candles before it. Trades with displacement won 32.9% of the time, and the trades of the standard rules 32.8%. Before costs, both were close to break-even. With displacement after a sweep with a wick only, which is close to how ICT describes a market structure shift, 32.7% of the trades won. These trades entered at the next candle's open. Many ICT traders wait for a pullback instead. Displacement lost less because a big candle closes further from the stop-loss, so trading costs take a smaller share. But it made money in both 2020–2023 and 2024–2025 in 4 tests, compared with 5 with the standard rules. On the DAX's 15-minute chart, it made +46.5% from 2020 to 2025, while the standard rules lost 35.5%.
Should you wait for a pullback after a change of character?
Not in our test. We placed a limit order (an order that waits at a set price) halfway between the stop-loss and the close of the breaking candle. The breaking candle is the candle that made the change of character. We waited up to 10 candles for price to come back to it. The take-profit was twice as far from the limit price as the stop-loss. Only 45% of the orders filled, and the trades that filled won 32.7% of the time, about the same as entering at once. With the pullback entry, no test made money in both 2020–2023 and 2024–2025, compared with 5 with the standard rules. The entry is half as far from the stop-loss, so each trade needs twice the size for the same 1% risk. On 5-minute and 15-minute charts, many trades needed a bigger size than our backtest engine allows, so they risked less than 1%.
Keep exploring
Related strategies we tested
ICT & smart money ICT Power of 3 (AMD) backtest on NQ, ES and gold futures Made money in 4 of 10 tests, 2 of them in both periods. Best: YM (Dow futures), 15-minute, +46% ICT & smart money ICT Silver Bullet backtest on NQ and ES futures Made money in 0 of 10 tests, 0 of them in both periods. Best: MGC (Micro Gold), 5-minute, −2% ICT & smart money Fair value gap (FVG) retest strategy backtest Made money in 5 of 24 tests, 2 of them in both periods. Best: S&P 500, 15-minute, +124%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 MarketStructureShiftChoch(Strategy):
"""Market structure shift / change of character (CHoCH), the smart money concepts (SMC)
reversal entry, plus three changes SMC traders argue about. Educators define it in
different ways; this is ours.
Swing points. A swing high is a candle whose high is higher than the highs of the 3
candles before it and the 3 candles after it, so it is only known 3 candles later. A swing
low is the mirror image.
Lower highs and lower lows. A swing high is a "lower high in a downtrend" when it is lower
than the swing high before it, and the last swing low before it was also lower than the
swing low before that one. A "higher low in an uptrend" is the mirror image: a swing low
above the swing low before it, after a higher high.
Standard (all switches off):
1. Buy when a candle closes above the most recent swing high, if that swing high is a
lower high in a downtrend: the change of character. Only the first close above a
swing high counts. The trade opens at the next candle's open.
2. Stop-loss at the lowest price since that swing high: the low the move up started
from. Take-profit at 2R, where R is the distance from the close of the breaking
candle to the stop-loss.
3. Sell (go short) in the mirror case: the first close below the most recent swing low,
if it is a higher low in an uptrend. Stop-loss at the highest price since that swing
low, take-profit at 2R.
4. One trade at a time. A change of character while a trade is open is skipped.
5. Size every trade so that the stop-loss costs 1% of the account.
Skipped candles: flat candles (high = low: no price moved, e.g. a holiday in the data) and
candles that open on a Friday at 17:00 New York time or later (a few minutes of prices
after the weekly close in some index data). They are never swing points, never signals,
and never set a stop-loss.
The changes, one at a time:
pullback = 1 don't buy at once: a buy limit order waits halfway between the
breaking candle's close and the stop-loss, for up to 10 candles. Same
stop-loss; take-profit 2R from the limit price. The order is
cancelled if price reaches that take-profit level first.
displacement = 1.0 only breaks made by a big candle: its body (open to close) is at least
1 x ATR(14) of the candles before it, in the direction of the break
htf = 1 only buy when the higher chart is in an uptrend (its last two swing
highs and its last two swing lows both rise), and only sell when it is
in a downtrend. Higher chart: M5 -> H1, M15 -> H4, H1 -> D1, H4 -> D1.
A higher-chart candle is skipped when every chart candle in it was.
The page also checks other settings of the standard rules: target_r = 1 and 3 (take-profit
at 1R and 3R) and swing_bars = 10 (swing points with 10 candles on each side).
"""
params = {"swing_bars": Param(type=int, default=3, min=1, max=20),
"target_r": Param(type=float, default=2.0, min=0.5, max=10.0),
"pullback": Param(type=int, default=0, min=0, max=1),
"pullback_bars": Param(type=int, default=10, min=1, max=100),
"displacement": Param(type=float, default=0.0, min=0.0, max=5.0),
"htf": Param(type=int, default=0, min=0, max=1)}
lookback = 5
higher_chart = {"M5": "H1", "M15": "H4", "H1": "D1", "H4": "D1"}
def init(self, ctx):
self.ny = ctx.clock("America/New_York")
self.atr = ctx.atr(period=14)
self.prev_atr = None
self.chart = {"win": [], "highs": [], "lows": []} # swing points on the chart
self.top = None # the most recent swing high: the level a change of character up breaks
self.bottom = None # the most recent swing low
self.order = None # pullback version: [order, side, take-profit, candle it was placed on]
self.view = None
if ctx.params.htf == 1:
self.view = ctx.tf(self.higher_chart[ctx.timeframe])
self.higher = {"win": [], "highs": [], "lows": []}
self.view_seen = -1
self.view_ms = self.view.timeframe_seconds * 1000
self.counted = [] # open times of the last chart candles that were not skipped
@staticmethod
def add_candle(s, n, k, high, low):
"""Add one candle to a chart's swing points. Returns the swing high and the swing low it
confirms (the candle n candles back) as (index, price) or None, and whether each one is
a lower high in a downtrend / a higher low in an uptrend."""
win = s["win"]
win.append((k, high, low))
if len(win) > 2 * n + 1:
win.pop(0)
if len(win) < 2 * n + 1:
return None, None, False, False
ck, ch, cl = win[n]
others = win[:n] + win[n + 1:]
sh = (ck, ch) if all(ch > x[1] for x in others) else None
sl = (ck, cl) if all(cl < x[2] for x in others) else None
highs, lows = s["highs"], s["lows"]
lower_lows = len(lows) == 2 and lows[1][1] < lows[0][1]
higher_highs = len(highs) == 2 and highs[1][1] > highs[0][1]
lower_high = sh is not None and len(highs) > 0 and sh[1] < highs[-1][1] and lower_lows
higher_low = sl is not None and len(lows) > 0 and sl[1] > lows[-1][1] and higher_highs
if sh is not None:
s["highs"] = (highs + [sh])[-2:]
if sl is not None:
s["lows"] = (lows + [sl])[-2:]
return sh, sl, lower_high, higher_low
@staticmethod
def trend(s):
"""+1 when the last two swing highs and the last two swing lows both rise, -1 when both
fall, 0 otherwise."""
hs, ls = s["highs"], s["lows"]
if len(hs) < 2 or len(ls) < 2:
return 0
if hs[1][1] > hs[0][1] and ls[1][1] > ls[0][1]:
return 1
if hs[1][1] < hs[0][1] and ls[1][1] < ls[0][1]:
return -1
return 0
def on_bar(self, ctx):
p = ctx.params
n = p.swing_bars
k = ctx.bar_index
prev_atr, self.prev_atr = self.prev_atr, self.atr.value
o, h, l, c = ctx.open[0], ctx.high[0], ctx.low[0], ctx.close[0]
after_close = (self.ny.weekday == 4 and self.ny.minute_of_day >= 17 * 60) or self.ny.weekday == 5
counts = h > l and not after_close
buy = sell = False
level = None
if counts:
prev_high = self.chart["highs"][-1][1] if self.chart["highs"] else None
prev_low = self.chart["lows"][-1][1] if self.chart["lows"] else None
sh, sl, lower_high, higher_low = self.add_candle(self.chart, n, k, h, l)
after = self.chart["win"][n + 1:]
if sh is not None:
# "since": the lowest price after the swing high. "before": the last swing low
# before it, to see if price went below that low before turning up.
self.top = {"price": sh[1], "choch": lower_high, "broken": False,
"since": min(x[2] for x in after), "before": prev_low}
elif self.top is not None:
self.top["since"] = min(self.top["since"], l)
if sl is not None:
self.bottom = {"price": sl[1], "choch": higher_low, "broken": False,
"since": max(x[1] for x in after), "before": prev_high}
elif self.bottom is not None:
self.bottom["since"] = max(self.bottom["since"], h)
# Each swing point breaks once: the first close beyond it, trade or no trade.
if self.top is not None and not self.top["broken"] and c > self.top["price"]:
self.top["broken"] = True
buy = self.top["choch"]
if self.bottom is not None and not self.bottom["broken"] and c < self.bottom["price"]:
self.bottom["broken"] = True
sell = self.bottom["choch"]
level = self.top if buy else self.bottom
if self.view is not None:
self.read_higher_chart(ctx, n, counts)
# Pullback version: the limit order waiting for price to come back.
if self.order is not None:
order, side, target, placed = self.order
waiting = any(x.order_id == order.order_id for x in ctx.orders)
if not waiting:
self.order = None # filled (or gone)
elif (h >= target if side > 0 else l <= target) or k - placed >= p.pullback_bars:
ctx.cancel_order(order.order_id) # the move went without us, or too late
self.order = None
else:
return # still waiting: one order at a time
if ctx.position is not None:
return
if buy and sell:
return
if not ctx.gate("change_of_character", buy or sell):
return
side = 1 if buy else -1
if p.displacement > 0:
big = prev_atr is not None and (c - o) * side >= p.displacement * prev_atr
if not ctx.gate("displacement", big):
return
if self.view is not None:
if not ctx.gate("with_higher_chart_trend", self.trend(self.higher) == side):
return
stop = level["since"]
risk = (c - stop) * side
if not ctx.gate("stop_beyond_close", risk > 0):
return
# Did price go beyond the swing point before the level (take its liquidity) first?
swept = level["before"] is not None and (stop < level["before"] if side > 0 else stop > level["before"])
tag = ("choch_buy" if side > 0 else "choch_sell") + ("_swept" if swept else "_not_swept")
entry = c
if p.pullback == 1:
entry = c - side * risk / 2
risk = risk / 2
target = entry + side * p.target_r * risk
size = ctx.size_by_risk_pct(0.01, stop=stop, entry=entry)
if not ctx.gate("size_at_least_the_minimum", size > 0):
return
limit = entry if p.pullback == 1 else None
if side > 0:
order = ctx.buy(size=size, limit=limit, sl=stop, tp=target, tag=tag)
else:
order = ctx.sell(size=size, limit=limit, sl=stop, tp=target, tag=tag)
if p.pullback == 1 and order is not None:
self.order = [order, side, target, k]
def read_higher_chart(self, ctx, n, counts):
"""Feed each newly closed higher-chart candle into its own swing points."""
if counts:
self.counted = (self.counted + [ctx.bar_time])[-3:]
v = self.view
if v.bar_index < 0 or v.bar_index == self.view_seen:
return
self.view_seen = v.bar_index
end = v.bar_time # the higher-chart candle's close time
start = end - self.view_ms
if not any(start <= t < end for t in self.counted):
return # every chart candle in it was skipped
if v.high[0] > v.low[0]:
self.add_candle(self.higher, n, v.bar_index, v.high[0], v.low[0])
Your turn
Test the change of character (CHoCH) setup yourself
Change the settings, the market or the stop-loss and see the result in seconds. Ask the BacktestingMax AI: Backtest a change of character (CHoCH) strategy on USD/JPY, 1-hour chart: 3-candle swing points, buy the first close above the last lower high, stop at the lowest price since that lower high, 2R take-profit
.
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.