Skip to content

Indicator-Based Trading Strategies That Work

Learn how to combine RSI, moving averages, MACD, and support/resistance into proven trading strategies — and when a custom indicator gives you the edge.

💡
Before you start

Python 3 and a terminal. No trading platform, no broker, no account and no money. macOS and Linux already include Python; on Windows install it from python.org with “Add python.exe to PATH” ticked, then check with python3 --version. You do not need MetaTrader for this page.

Step 3 takes a few seconds to run because it searches several hundred parameter combinations twice. That is not a fault; the time it takes is part of the lesson about how easily a search finds something.

Nothing here is trading advice, and no figure below is a result you could trade. Every measurement is on generated data. What transfers is the four checks themselves, which apply to any strategy on any market.

Why Strategy Beats Guessing

Most traders lose money because they make decisions based on emotion, not a system. A trading strategy removes the guesswork — it defines exactly when to enter, when to exit, and how much to risk. The best strategies are built on indicators that objectively measure what the market is doing.

In this tutorial, you'll learn five proven indicator-based strategies that professional traders actually use. More importantly, you'll understand when standard indicators are enough and when a custom indicator gives you a real edge.

💡
Prerequisites

This tutorial assumes you understand moving averages, RSI, and MACD. If not, read those tutorials first from the Technical Analysis branch.

Strategy 1: Moving Average Crossover

The simplest and most widely-used trend-following strategy. It uses two moving averages of different periods — when the fast one crosses above the slow one, buy. When it crosses below, sell.

1
Setup

Add a 20-period EMA (fast) and a 50-period EMA (slow) to your chart.

2
Entry Signal

Buy when the 20 EMA crosses above the 50 EMA. Sell when the 20 EMA crosses below the 50 EMA.

3
Stop-Loss

Place your stop-loss below the most recent swing low (for buys) or above the most recent swing high (for sells).

Best for: Trending markets (forex majors, indices, trending stocks).

Weakness: Generates many false signals in ranging/sideways markets. This is exactly where a custom indicator can help — filtering out signals that occur during low-volatility consolidation periods.

Strategy 2: RSI Reversal with Confirmation

Pure RSI overbought/oversold signals are unreliable alone. This strategy adds a confirmation layer that dramatically improves accuracy.

1
Setup

Add RSI (14) to your chart. Mark the 30 (oversold) and 70 (overbought) levels.

2
Wait for Extreme

Wait for RSI to drop below 30 (buy setup) or rise above 70 (sell setup).

3
Confirmation

Do NOT enter immediately. Wait for RSI to cross back above 30 (for buys) or below 70 (for sells). This confirms the reversal has started.

4
Additional Filter

Only take the trade if price is near a known support level (for buys) or resistance level (for sells).

Best for: Range-bound markets and swing trading on H4 or Daily timeframes.

Weakness: Misses strong trend continuations where RSI stays extreme for extended periods.

Strategy 3: MACD Divergence

Divergence between price and MACD is one of the most powerful reversal signals. When price makes new highs but MACD makes lower highs, the trend is losing momentum.

  • Bullish divergence: Price makes a lower low, but MACD histogram makes a higher low. This suggests selling pressure is weakening — look for a buy.
  • Bearish divergence: Price makes a higher high, but MACD histogram makes a lower high. This suggests buying pressure is weakening — look for a sell.
⚠️
Critical Rule

Divergence signals a potential reversal, but NEVER trade divergence alone. Wait for a confirming candle pattern (engulfing, pin bar) or a break of a minor trend line before entering.

Best for: Spotting trend exhaustion on H1 to Daily charts.

Weakness: Divergence can persist for many bars before price actually reverses. A custom indicator that combines divergence detection with volatility filters dramatically reduces false signals.

Strategy 4: Multi-Indicator Confluence

The most reliable strategy is confluence — when multiple independent indicators agree on the same direction at the same time. Each indicator alone might be 55% accurate, but when three agree, the probability jumps significantly.

Here's a practical confluence setup:

  • Trend filter: 200 EMA — only take buys above it, sells below it
  • Momentum: RSI between 40-50 for buy entries (pulling back in uptrend) or 50-60 for sell entries
  • Timing: MACD histogram turning positive (for buys) or negative (for sells)
  • Level: Price touching or near a key support/resistance level
1
Check the Trend

Is price above or below the 200 EMA? This determines whether you look for buys or sells.

2
Wait for Pullback

In an uptrend, wait for price to pull back toward the 50 EMA. Check that RSI is in the 40-50 zone (not overbought).

3
Enter on MACD Confirmation

When MACD histogram turns from negative to positive (in the pullback), enter the trade. This confirms momentum is shifting back in the trend direction.

Best for: All markets, all timeframes. This is the closest thing to a "universal" strategy.

Challenge: Monitoring 4+ indicators simultaneously is mentally exhausting. This is exactly why traders build custom indicators — a single combined indicator that gives you a clear signal when all conditions align.

Strategy 5: Breakout with Volume Confirmation

Breakout trading captures the explosive moves when price breaks through established support or resistance levels. The key is filtering out false breakouts.

1
Identify the Level

Find a clear support or resistance level that price has tested at least 2-3 times.

2
Wait for the Break

Wait for a candle to close beyond the level (not just wick through it).

3
Confirm with Volume

The breakout candle should have higher-than-average volume. Low-volume breakouts frequently reverse.

4
Enter on Retest

The safest entry is waiting for price to break out, pull back to the broken level (now support if it was resistance), and bounce. This confirms the breakout is real.

Best for: Consolidation breakouts, range boundaries, chart pattern completions.

Weakness: Timing is difficult — entering too early catches false breakouts, too late misses the move. A custom breakout indicator with built-in volume filtering and alert notifications solves this.

When Standard Indicators Are Not Enough

The strategies above work well with MT5's built-in indicators. But there are situations where a custom indicator gives you a genuine edge:

  • Multi-condition signals: When your strategy requires 3-4 indicators to align simultaneously, a custom indicator combines them into a single clear buy/sell signal — no more watching multiple windows.
  • Proprietary logic: If you've discovered a pattern or formula that isn't available in standard indicators, a custom indicator implements it exactly as you envision.
  • Alert automation: Custom indicators can send you push notifications, emails, or sound alerts the instant your exact entry conditions are met — even when you're away from the screen.
  • Visual clarity: Instead of interpreting 4 separate indicators, a custom indicator draws arrows, zones, or color-coded bars directly on your chart showing exactly where to act.
  • Backtestable strategies: Custom indicators can be integrated into Expert Advisors for automated backtesting, letting you validate your strategy against years of historical data before risking real money.
🎉
Need a Custom Indicator Built?

If you have a trading strategy that needs a custom MT5 indicator — whether it's combining multiple signals, adding smart alerts, or implementing proprietary logic — we build professional MQL5 indicators. Learn about our MQ5 Indicator Development service.

Choosing the Right Strategy for Your Style

No single strategy works in all market conditions. Choose based on your trading style:

  • Scalping (M1-M15): Moving average crossover with tight stops. Speed matters — custom indicators with instant alerts help.
  • Day trading (M15-H1): Multi-indicator confluence or breakout strategies. You need clear signals during your active hours.
  • Swing trading (H4-D1): RSI reversal or MACD divergence. Patience is key — you hold for days, not minutes.
  • Position trading (D1-W1): Moving average crossover on higher timeframes. Simple, slow, but captures major trends.

Test a Strategy the Way It Would Have to Survive, in Five Steps

Every indicator strategy looks good on the chart it was designed on. Whether it survives contact with anything else comes down to four questions that are almost never asked together: does adding a second indicator actually help, did the settings come from the data or from a search, what happens after costs, and how many trades does the result rest on? In the next half hour you will answer all four with code, on four thousand bars. The most useful result on the page is step 2, where the same search finds a “profitable” strategy in data that contains nothing at all. Every line of output below came from running these files.

1
Build a long series, because this page is about counting

Go: open a terminal in a folder you can write to — cd ~/Desktop on macOS or Linux, cd %USERPROFILE%\Desktop on Windows.

Do: save this as prices.py and run python3 prices.py.

"""A long series, because this page is about statistics and 400 bars is not enough."""
import random

random.seed(11)

price, series = 100.0, []
for bar in range(4000):
    # regimes of 50 bars, cycling up / down / flat
    drift = [0.35, -0.30, 0.0][(bar // 50) % 3]
    price += drift + random.uniform(-1.2, 1.2)
    if price < 20:                      # keep it positive without changing the shape
        price += 20
    series.append(round(price, 2))

with open("closes.txt", "w") as f:
    for p in series:
        f.write("%.2f\n" % p)

print("bars written :", len(series))
print("high / low   : %.2f / %.2f" % (max(series), min(series)))
print()
print("Four thousand bars is roughly sixteen years of daily data, or about")
print("eight months of hourly. Earlier pages used 400 to keep the tables")
print("readable; this one needs enough trades to say anything at all.")

You should see: four thousand bars:

bars written : 4000
high / low   : 167.15 / 92.25

Four thousand bars is roughly sixteen years of daily data, or about
eight months of hourly. Earlier pages used 400 to keep the tables
readable; this one needs enough trades to say anything at all.

The length is the point. Earlier pages used four hundred bars so the tables stayed readable; a crossover rule produces only a handful of signals in that span, and step 5 shows why a handful cannot support a conclusion.

If not: PermissionError means the folder is not writable — cd somewhere you own. This script writes closes.txt for every later step.

2
Add a second indicator and see what it can and cannot do

Go: the same folder.

Do: save this as combine.py and run python3 combine.py.

"""Add a second indicator as 'confirmation'. Count what survives."""

closes = [float(line) for line in open("closes.txt")]

def sma(v, p):
    return [None if i < p-1 else sum(v[i-p+1:i+1])/p for i in range(len(v))]

def rsi(v, period=14):
    out = [None]*len(v)
    g = [max(v[i]-v[i-1], 0.0) for i in range(1, len(v))]
    l = [max(v[i-1]-v[i], 0.0) for i in range(1, len(v))]
    ag, al = sum(g[:period])/period, sum(l[:period])/period
    out[period] = 100.0 if al == 0 else 100 - 100/(1+ag/al)
    for i in range(period, len(g)):
        ag = (ag*(period-1)+g[i])/period; al = (al*(period-1)+l[i])/period
        out[i+1] = 100.0 if al == 0 else 100 - 100/(1+ag/al)
    return out

fast, slow, r = sma(closes, 3), sma(closes, 10), rsi(closes)

def signals(require_rsi, fastp=3, slowp=10):
    out = []
    for i in range(21, len(closes) - 5):
        if None in (fast[i], slow[i], fast[i-1], slow[i-1], r[i]):
            continue
        if not (fast[i-1] <= slow[i-1] and fast[i] > slow[i]):
            continue
        if require_rsi and not (r[i] > 50):
            continue
        out.append(i)
    return out

a = signals(False)
b = signals(True)
print("MA crossover alone            :", len(a), "signals")
print("MA crossover AND RSI above 50 :", len(b), "signals")
print("removed by the filter          :", len(a) - len(b))
print()
overlap = len(b)
print("Of the %d filtered signals, how many were already in the first set? %d"
      % (len(b), overlap))
print("i.e. the filter only ever REMOVES signals -- it can never add one.")
print()
print("That is true of every 'confirmation' rule ever written, and it is the")
print("thing to hold on to: adding a condition cannot improve a bad signal.")
print("It can only reduce how often you act on one. Whether that helps")
print("depends entirely on whether it removes more losers than winners.")

You should see: the filter removing 83 of 156 signals and adding none:

MA crossover alone            : 156 signals
MA crossover AND RSI above 50 : 73 signals
removed by the filter          : 83

Of the 73 filtered signals, how many were already in the first set? 73
i.e. the filter only ever REMOVES signals -- it can never add one.

That is true of every 'confirmation' rule ever written, and it is the
thing to hold on to: adding a condition cannot improve a bad signal.
It can only reduce how often you act on one. Whether that helps
depends entirely on whether it removes more losers than winners.

Every one of the 73 surviving signals was already in the original 156, which is not a property of these two indicators — it is true of every “confirmation” rule that can be written. An extra condition is a filter. It subtracts.

That reframes the question usefully. “Does RSI confirmation improve this strategy?” is unanswerable as stated; “does it remove more losing signals than winning ones?” is a measurement. And the answer is not obvious in advance, because a filter that removes half your losers and half your winners has done nothing except reduce your sample size — which, as step 5 shows, makes the result harder to trust rather than easier.

If not: if both counts are equal, the RSI condition is never binding — check that r[i] > 50 is inside the require_rsi branch. Very low counts mean the periods were left at 5 and 20; the file uses 3 and 10 to produce enough signals to count.

3
Search for the best settings, on data that has none

Go: the same folder. This is the most important step on the page.

Do: save this as overfit.py and run python3 overfit.py. It takes a few seconds — it is searching 406 parameter combinations twice.

"""Optimise on one half, test on the other -- on two different kinds of data."""
import random

def sma(v, p):
    return [None if i < p-1 else sum(v[i-p+1:i+1])/p for i in range(len(v))]

def total_move(values, fp, sp, hold=5):
    f, s = sma(values, fp), sma(values, sp)
    pnl, n = 0.0, 0
    for i in range(sp + 1, len(values) - hold):
        if None in (f[i], s[i], f[i-1], s[i-1]):
            continue
        if f[i-1] <= s[i-1] and f[i] > s[i]:
            pnl += values[i + hold] - values[i]
            n += 1
    return pnl, n

def search(train):
    best = None
    for fp in range(2, 16):
        for sp in range(fp + 2, 60, 2):
            pnl, n = total_move(train, fp, sp)
            if n >= 20 and (best is None or pnl / n > best[0]):
                best = (pnl / n, fp, sp, n)
    return best

def study(name, series):
    half = len(series) // 2
    per, fp, sp, n = search(series[:half])
    pnl2, n2 = total_move(series[half:], fp, sp)
    out = pnl2 / n2 if n2 else 0.0
    print("%-34s best MA(%2d)/MA(%2d)" % (name, fp, sp))
    print("%-34s in-sample  %+.4f per trade over %d trades" % ("", per, n))
    print("%-34s out-of-sample %+.4f per trade over %d trades" % ("", out, n2))
    print("%-34s kept %.0f%% of the edge" % ("", 100.0 * out / per if per else 0.0))
    print()
    return per, out

trending = [float(line) for line in open("closes.txt")]

random.seed(3)
walk, p = [], 100.0
for _ in range(4000):
    p += random.uniform(-1.2, 1.2)        # no drift, no regimes, no pattern
    walk.append(round(p, 2))

print("combinations searched each time :", 14 * 29)
print()
a_in, a_out = study("A: series WITH real trends", trending)
b_in, b_out = study("B: pure random walk", walk)

print("Series A has genuine 50-bar trends built into it, so a trend-following")
print("rule finds something real and it survives into data it never saw.")
print()
print("Series B has no pattern at all. The search still produced a positive")
print("in-sample result -- searching 406 combinations and keeping the best")
print("guarantees that -- and it kept %.0f%% of it out of sample." % (100.0*b_out/b_in))
print()
print("THAT is what out-of-sample testing is for. It cannot tell you a rule")
print("will make money. It can tell you whether what you found was in the")
print("data or was put there by your search.")

You should see: a real pattern surviving and a fitted one collapsing:

combinations searched each time : 406

A: series WITH real trends         best MA( 3)/MA(45)
                                   in-sample  +1.0662 per trade over 21 trades
                                   out-of-sample +0.8286 per trade over 28 trades
                                   kept 78% of the edge

B: pure random walk                best MA(10)/MA(30)
                                   in-sample  +0.6368 per trade over 34 trades
                                   out-of-sample -0.0992 per trade over 36 trades
                                   kept -16% of the edge

Series A has genuine 50-bar trends built into it, so a trend-following
rule finds something real and it survives into data it never saw.

Series B has no pattern at all. The search still produced a positive
in-sample result -- searching 406 combinations and keeping the best
guarantees that -- and it kept -16% of it out of sample.

THAT is what out-of-sample testing is for. It cannot tell you a rule
will make money. It can tell you whether what you found was in the
data or was put there by your search.

Series A has genuine fifty-bar trends built into it. A trend-following rule finds something that is really there, and it keeps 78% of its edge in the half of the data the search never saw.

Series B is a pure random walk — no drift, no regimes, nothing to find. The search still returned a positive in-sample result of +0.6368 per trade, because searching 406 combinations and keeping the winner guarantees one. Out of sample it made −0.0992 per trade.

That gap is the entire value of out-of-sample testing, and note precisely what it does: it cannot tell you a rule will make money in future. It can tell you whether what you found was in the data or was put there by your own search — which is a different question, and the one that separates a strategy from a coincidence.

The practical rule follows directly. Decide the split before you look, optimise only on the first part, and test once on the second. Looking at the second part and then adjusting turns it into the first part, and you have no test left.

If not: if series B also survives, the seed was changed — with a different random walk the fitted edge sometimes survives by luck, which is itself instructive. Run it with three or four seeds and watch how often the “strategy” found in noise happens to hold up.

4
Charge the strategy what trading actually costs

Go: the same folder.

Do: save this as costs.py and run python3 costs.py.

"""The number every backtest leaves out."""

closes = [float(line) for line in open("closes.txt")]

def sma(v, p):
    return [None if i < p-1 else sum(v[i-p+1:i+1])/p for i in range(len(v))]

def run(fp, sp, cost_per_trade, hold=5):
    f, s = sma(closes, fp), sma(closes, sp)
    pnl, n = 0.0, 0
    for i in range(sp + 1, len(closes) - hold):
        if None in (f[i], s[i], f[i-1], s[i-1]):
            continue
        if f[i-1] <= s[i-1] and f[i] > s[i]:
            pnl += (closes[i + hold] - closes[i]) - cost_per_trade
            n += 1
    return pnl, n

print("Strategy: MA(3)/MA(10) crossover, hold 5 bars, on 4000 bars.")
print()
print("%-34s %12s %10s" % ("COST PER ROUND TRIP", "TOTAL", "PER TRADE"))
print("-" * 60)
for cost in (0.00, 0.05, 0.10, 0.20, 0.40):
    pnl, n = run(3, 10, cost)
    label = "none (what backtests show)" if cost == 0 else "%.2f (about %.2f%% of price)" % (cost, 100*cost/100)
    print("%-34s %12.2f %10.4f" % (label, pnl, pnl / n))

pnl0, n = run(3, 10, 0.0)
breakeven = pnl0 / n
print()
print("trades taken :", n)
print("break-even cost per trade : %.4f" % breakeven)
print()
print("On a stock around 100, a spread of 0.10 is one tenth of one per cent --")
print("unremarkable, and smaller than many real spreads once slippage is")
print("included. It removes %.0f%% of the gross result." % (100 * (pnl0 - run(3,10,0.10)[0]) / pnl0))
print()
print("A strategy trading often has to clear its costs %d times." % n)
print("This is why 'high-frequency' ideas that look wonderful on a chart")
print("evaporate: the edge per trade is smaller than the cost per trade.")

You should see: a tenth of one per cent removing most of the result, and a fifth killing it:

Strategy: MA(3)/MA(10) crossover, hold 5 bars, on 4000 bars.

COST PER ROUND TRIP                       TOTAL  PER TRADE
------------------------------------------------------------
none (what backtests show)                25.76     0.1651
0.05 (about 0.05% of price)               17.96     0.1151
0.10 (about 0.10% of price)               10.16     0.0651
0.20 (about 0.20% of price)               -5.44    -0.0349
0.40 (about 0.40% of price)              -36.64    -0.2349

trades taken : 156
break-even cost per trade : 0.1651

On a stock around 100, a spread of 0.10 is one tenth of one per cent --
unremarkable, and smaller than many real spreads once slippage is
included. It removes 61% of the gross result.

A strategy trading often has to clear its costs 156 times.
This is why 'high-frequency' ideas that look wonderful on a chart
evaporate: the edge per trade is smaller than the cost per trade.

A cost of 0.10 on an instrument priced around 100 is one tenth of one per cent — unremarkable, and smaller than many real spreads once slippage is counted. It removes 61% of the gross result. At 0.20 the strategy is losing money.

The break-even figure is the one to internalise: 0.1651 per trade. That is the entire edge, and it has to be larger than the spread, the commission and the slippage combined, 156 times over. Any strategy that trades frequently is really a bet that its per-trade edge exceeds its per-trade cost, and most of them are not.

This is also why the same idea can be profitable on a daily chart and hopeless on a five-minute one. The edge per trade shrinks with the timeframe; the cost per trade does not.

If not: if every row is identical, the cost is not reaching the calculation — it must be subtracted inside the loop, once per trade, not once at the end.

5
Ask how many trades the result rests on

Go: the same folder.

Do: save this as significance.py and run python3 significance.py.

"""How many trades before a win rate means anything?"""
from math import comb

def p_at_least(k, n, p=0.5):
    """Probability of k or more wins in n coin flips."""
    return sum(comb(n, j) * p**j * (1-p)**(n-j) for j in range(k, n + 1))

print("A rule wins 60 per cent of its trades. How likely is that from a coin flip?")
print()
print("%10s %10s %14s" % ("TRADES", "WINS", "P(BY LUCK)"))
print("-" * 38)
for n in (10, 20, 50, 100, 200, 400, 1000):
    k = round(0.6 * n)
    print("%10d %10d %13.2f" % (n, k, 100 * p_at_least(k, n)) + " per cent")

print()
print("Ten trades at 60 per cent happens by accident %.0f times in 100."
      % (100 * p_at_least(6, 10)))
print("A thousand trades at 60 per cent essentially never does.")
print()
print("Now the harder truth: 60 per cent is not the target either.")
print()
print("%-30s %10s %12s" % ("RULE", "WIN RATE", "EXPECTANCY"))
print("-" * 56)
for name, win, avg_win, avg_loss in [
        ("wins often, loses big",   0.70, 1.0, 3.0),
        ("wins rarely, wins big",   0.35, 4.0, 1.0),
        ("balanced",                0.50, 1.2, 1.0)]:
    exp = win * avg_win - (1 - win) * avg_loss
    print("%-30s %9.0f   %+12.3f" % (name, 100 * win, exp))

print()
print("The first rule wins 70 per cent of the time and loses money. A win")
print("rate alone tells you nothing, which is why it is the number always")
print("quoted in an advertisement and never the one in a serious report.")
print()
print("expectancy = win_rate x average_win - loss_rate x average_loss")
print("It is the only figure that answers 'should I do this'.")

You should see: sixty per cent meaning nothing at ten trades and a great deal at a thousand:

A rule wins 60 per cent of its trades. How likely is that from a coin flip?

    TRADES       WINS     P(BY LUCK)
--------------------------------------
        10          6         37.70 per cent
        20         12         25.17 per cent
        50         30         10.13 per cent
       100         60          2.84 per cent
       200        120          0.28 per cent
       400        240          0.00 per cent
      1000        600          0.00 per cent

Ten trades at 60 per cent happens by accident 38 times in 100.
A thousand trades at 60 per cent essentially never does.

Now the harder truth: 60 per cent is not the target either.

RULE                             WIN RATE   EXPECTANCY
--------------------------------------------------------
wins often, loses big                 70         -0.200
wins rarely, wins big                 35         +0.750
balanced                              50         +0.100

The first rule wins 70 per cent of the time and loses money. A win
rate alone tells you nothing, which is why it is the number always
quoted in an advertisement and never the one in a serious report.

expectancy = win_rate x average_win - loss_rate x average_loss
It is the only figure that answers 'should I do this'.

The top table is the answer to every screenshot. A 60% win rate over ten trades arises from pure chance 38 times in 100 — so it is not merely weak evidence, it is no evidence. The same rate over a thousand trades essentially never happens by luck.

The bottom table is the part people find genuinely surprising. A rule that wins 70% of the time loses money, because its losses are three times the size of its wins. Win rate is a component of the answer, not the answer, and it is the component chosen for advertisements precisely because it can be made to look good independently of whether the rule works.

The figure that decides everything is at the bottom: expectancy. Win rate times average win, minus loss rate times average loss. If that is not positive after costs, nothing else about the strategy matters, and if it is positive the remaining question is only whether you have enough trades to believe it.

If not: ImportError: cannot import name 'comb' means Python older than 3.8. The probabilities are exact binomial values, so yours will match this page to the last digit.

🎉
Check yourself before moving on

Without scrolling up: someone offers you a strategy with a backtest showing a 71% win rate over 45 trades on one instrument in the last two years, with the settings described as “optimised”. List the checks you would run before believing any of it, and say which one you would do first. Answer: first, and before anything else, ask for the out-of-sample result — step 3 showed a search over 406 combinations producing a healthy positive result in a pure random walk, so “optimised” is a warning rather than a feature, and settings chosen by looking at results tell you about the search rather than the market. Then the sample: 45 trades is far too few, since step 5 showed 60% over 50 trades arising by chance about a tenth of the time and 71% is not far enough beyond that to survive on 45. Then the costs: step 4 showed a tenth of one per cent removing 61% of a gross result, so I would want the figures net of spread, commission and slippage, not gross. Then expectancy rather than win rate, because a 71% win rate is consistent with losing money if the losses are large. And finally whether the “confirmation” conditions are independent or are two views of the same calculation. If only one check were possible, it would be the out-of-sample split, because it is the one that can invalidate everything else in a single number.

Now do it without the page: take the strategy in costs.py and find the timeframe at which it stops working: multiply the hold period by 2, then 4, then 8, recomputing each time with a cost of 0.10. The edge per trade grows with the hold and the cost does not, so there is a crossing point — find it. Then do the same for your own idea, whatever it is, and write down two numbers before you ever place a trade: your expected edge per trade, and your all-in cost per trade. If you cannot estimate both, you do not yet have a strategy.

Next Steps

Now that you understand these strategies, here's how to put them into practice:

  • Backtest first: Open MT5's Strategy Tester and test each strategy on historical data before using real money. Our Backtesting tutorial shows you how.
  • Start with one: Pick the strategy that fits your trading style and master it before adding others.
  • Keep a trading journal: Record every trade — entry reason, exit reason, result. Patterns emerge over time.
  • Consider automation: Once your strategy is proven, an Expert Advisor can execute it 24/7 without emotional interference.