---
title: "0.1.2 Realistic expectations: costs, time and risk"
description: "How much costs eat, why a loss weighs more than a gain, how many bad years a good strategy has and how much data you need to tell it from luck."
url: "https://pythonparatrading.com/en/python-trading-course/level-0/realistic-trading-expectations-costs-risk/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-0/realistic-trading-expectations-costs-risk.png"
---

# 0.1.2 · Realistic expectations: costs, time and risk of loss

> Before looking for an edge, you need to know how much it costs to have one. Four simple calculations (costs, losses, bad years and years of data) that explain why most strategies never work live.

- Course: [Level 0: Python from scratch and a data mindset](https://pythonparatrading.com/en/python-trading-course/level-0/)
- Module: 0.1 · Welcome and expectations
- Duration: 35 min
- Prerequisites: [0.1.1 · Systematic, algorithmic and quantitative trading versus discretionary trading](https://pythonparatrading.com/en/python-trading-course/level-0/systematic-algorithmic-quantitative-trading/)

## By the end of this lesson

- Compute a strategy's net return from its gross return, its turnover and the cost per trade.
- Explain why recovering from a loss requires a gain larger than the loss itself.
- Use the Sharpe ratio to estimate how many losing years are normal for a strategy that works.
- Compute how many years of data are needed to tell a strategy apart from chance.

## What it costs to trade

A strategy does not earn what its backtest says, but what is left after paying to trade it. Costs have five sources,
and only the first one appears on the broker's fee schedule:

- **Commissions**: what the broker charges for each order. It is the most visible cost and, in liquid stocks, the
  smallest.
- **Bid-ask spread**: the difference between the price at which you can buy and the price at which you can sell. Each
  round-trip trade pays it once.
- **Slippage**: the difference between the signal price and the price actually executed. It grows with the size of the
  order and with the volatility of the moment.
- **Financing**: interest on leveraged positions, securities borrowing on short positions, *rollover* in forex and
  *funding* on crypto perpetuals.
- **Taxes**: in Spain, gains are taxed in the savings base of personal income tax (IRPF; level 7). They do not change
  the strategy, but they do change what is left at the end.

```mermaid
flowchart TB
  B[Gross return] --> C1[− commissions] --> C2[− spread and slippage] --> C3[− financing] --> C4[− taxes] --> N[Net return]
```

If the first four are grouped into a cost per round-trip trade (buy and sell) $c$, and the strategy makes $n$ round
trips a year, the net return is approximately:

$$
r_{\text{net}} \approx r_{\text{gross}} - n \cdot c
$$

The formula is simple and its consequence harsh: the cost of each trade is multiplied by the **turnover**. In Python:

```python
import pandas as pd

def rentabilidad_neta(bruta: float, operaciones: int,
                      coste: float) -> float:
    """Net annual return.

    `coste`: cost of one round-trip trade, as a fraction.
    """
    return bruta - operaciones * coste

operaciones = [1, 5, 10, 25, 50, 100]
costes = [0.001, 0.002, 0.005]
tabla = pd.DataFrame(
    {f"cost {c:.1%}": [rentabilidad_neta(0.08, n, c) for n in operaciones]
     for c in costes},
    index=pd.Index(operaciones, name="trades/year"),
)
tabla.round(3)
```
```output
             cost 0.1%  cost 0.2%  cost 0.5%
trades/year
1                0.079      0.078      0.075
5                0.075      0.070      0.055
10               0.070      0.060      0.030
25               0.055      0.030     -0.045
50               0.030     -0.020     -0.170
100             -0.020     -0.120     -0.420
```

With 8% gross and 0.2% per trade, the strategy is still good at 10 trades a year and loses money from 40 on:

*Fig. — Net return of an 8% gross strategy by turnover (0.2% cost per round trip).* Bars with the net annual return by round trips per year: 7.8% with 1, 7% with 5, 6% with 10, 3% with 25, −2% with 50 (highlighted, the first negative one) and −12% with 100.

That is why high-turnover strategies need a much larger gross edge, and why a backtest without costs says nothing. From
level 2 on, every backtest in the course includes costs.

## Losses are not symmetric

If a €10,000 account loses 50%, €5,000 is left. Getting back to €10,000 does not take a 50% gain: you have to double
what is left, a 100% gain. In general, after a loss $p$ the required gain is:

$$
g = \frac{1}{1 - p} - 1
$$

```python
for perdida in (0.10, 0.20, 0.50, 0.75):
    subida = 1 / (1 - perdida) - 1
    print(f"After losing {perdida:.0%}, you need a gain of {subida:.1%}")
```
```output
After losing 10%, you need a gain of 11.1%
After losing 20%, you need a gain of 25.0%
After losing 50%, you need a gain of 100.0%
After losing 75%, you need a gain of 300.0%
```

The figure at the top shows the full curve. Up to 20% the asymmetry is small; from 40% on, recovery requires returns
that few strategies deliver in years. This is why risk management (level 4) matters more than looking for the rule
that earns the most: a 60% drawdown is not fixed by a good streak.

## Bad years with a good strategy

The **Sharpe ratio** measures the average return per unit of volatility: an annual Sharpe of 1 means that the average
return of a year equals its standard deviation. If annual returns were normal, the probability of closing a year at a
loss would be $\Phi(-\text{SR})$, where $\Phi$ is the cumulative distribution function of the standard normal:

```python
from statistics import NormalDist

for sharpe in (0.5, 1.0, 1.5):
    p = NormalDist().cdf(-sharpe)
    print(f"Sharpe {sharpe}: {p:.1%} of years at a loss")
```
```output
Sharpe 0.5: 30.9% of years at a loss
Sharpe 1.0: 15.9% of years at a loss
Sharpe 1.5: 6.7% of years at a loss
```

*Fig. — Probability of a losing year by Sharpe ratio.* Bars with the probability that a year ends in a loss for each annual Sharpe ratio, assuming normal returns: 40.1% at 0.25; 30.9% at 0.5; 22.7% at 0.75; 15.9% at 1 (highlighted); 6.7% at 1.5 and 2.3% at 2.

A Sharpe of 1 sustained for years is an excellent result for a systematic strategy, and even so one year in six closes
negative. Real returns have fatter tails than the normal distribution (level 1), so the real figure is usually somewhat
worse.

A simulation makes it more concrete. Ten thousand ten-year paths of a strategy with a Sharpe of 0.5 (a 5% average
return and 10% volatility), with independent, normal years:

```python
import numpy as np

rng = np.random.default_rng(42)
anos = rng.normal(loc=0.05, scale=0.10, size=(10_000, 10))
en_perdidas = anos < 0
tres_seguidos = (
    en_perdidas[:, :-2] & en_perdidas[:, 1:-1] & en_perdidas[:, 2:]
).any(axis=1)
capital_final = np.prod(1 + anos, axis=1)

print(f"At least one losing year: {en_perdidas.any(axis=1).mean():.1%}")
print(f"Three losing years in a row: {tres_seguidos.mean():.1%}")
print(f"Lose money over ten years: {(capital_final < 1).mean():.1%}")
```
```output
At least one losing year: 97.5%
Three losing years in a row: 16.8%
Lose money over ten years: 7.8%
```

The strategy has a real edge by construction, and even so almost every path has losing years, and a non-negligible
share has streaks of three losing years in a row. Whoever abandons a strategy after a bad year is, most of the time,
abandoning a strategy that works.

## Time: how much data you need

The other side of the problem: if bad years are normal, how many good years does it take to believe a backtest? A
useful approximation relates the annual Sharpe to the **t-statistic** of the average return after $T$ years of data:

$$
t \approx \text{SR} \cdot \sqrt{T}
$$

A t-statistic of 2 is the usual threshold for saying that a mean differs from zero with 95% confidence. Solving for
$T$:

```python
for sharpe in (0.5, 0.75, 1.0):
    anos_necesarios = (2 / sharpe) ** 2
    print(f"Sharpe {sharpe}: {anos_necesarios:.0f} years of data")
```
```output
Sharpe 0.5: 16 years of data
Sharpe 0.75: 7 years of data
Sharpe 1.0: 4 years of data
```

With a Sharpe of 0.5 (a good strategy at low frequency), you need about 16 years of data for the result not to be
compatible with chance. And that is for **one** strategy: if you test a hundred variants and pick the best, the
threshold rises (level 2, multiple testing). This calculation explains two ideas of the course: backtests of two or
three years prove nothing, and parameters are chosen from hypotheses, not by searching for the maximum.

Learning time counts too. The full syllabus is about 750–850 hours: at 10–15 hours a week, between 12 and 18 months.
Skipping levels 0 to 2 does not shorten that path; it only makes it more expensive.

## Risk of loss and leverage

Leverage multiplies exposure and, with it, losses. With 10-to-1 leverage, a 10% fall in the asset wipes out 100% of
the capital. Leveraged retail products (CFDs, options, futures, crypto perpetuals) make it possible to lose the entire
deposit in a very short time.

<aside class="callout" data-kind="riesgo">

In 2018, when it restricted the sale of CFDs to retail clients, ESMA reported that between 74% and 89% of retail CFD
accounts lose money. Since then, EU providers have to show their own percentage in every marketing communication: look
for it on your broker's website before opening an account (link in "Resources").

</aside>

The course does not recommend trading with real money until level 4, and always after weeks of *paper trading*
(simulated trading with real prices) and with automatic risk limits.

## Exercises

1. A strategy makes 12% gross per year, makes 40 round-trip trades and each one costs 0.3%. What is its net return? At
   most, how many trades a year could it make without losing money?

**Solution.**

Net: 12% − 40 × 0.3% = 12% − 12% = 0%. The break-even point is exactly 40 trades: 0.12 / 0.003 = 40. With any higher
turnover, the strategy loses money even if its signal is good.

2. A fund falls 35% in a crisis. What gain does it need to recover? If it then makes 8% a year, how many years does it
   take?

**Solution.**

```python
import math

subida = 1 / (1 - 0.35) - 1
anos_recuperacion = math.log(1 / (1 - 0.35)) / math.log(1.08)
print(f"Required gain: {subida:.1%}")
print(f"Years at 8% a year: {anos_recuperacion:.1f}")
```
```output
Required gain: 53.8%
Years at 8% a year: 5.6
```

It needs somewhat more than 50% and, at 8% a year compounded, more than five and a half years just to get back to where
it started.

3. A strategy has an annual Sharpe of 0.8. What fraction of years closes at a loss, if annual returns are normal? How
   many years of data are needed for a t-statistic of 2?

**Solution.**

```python
from statistics import NormalDist

sharpe = 0.8
print(f"Losing years: {NormalDist().cdf(-sharpe):.1%}")
print(f"Years of data: {(2 / sharpe) ** 2:.2f}")
```
```output
Losing years: 21.2%
Years of data: 6.25
```

Roughly one year in five closes at a loss, and you need a little over six years of data for a t-statistic of 2: more
than most published backtests.

## Self-assessment

1. A strategy makes 10% gross per year with 30 round-trip trades and a cost of 0.25% per trade. What is its approximate net return?

   - A. 9.75%
   - B. 7.5%
   - C. 2.5%
   - D. 0%

   *Answer C: 2.5%.* Costs subtract 30 × 0.25% = 7.5 points: 10% − 7.5% = 2.5%. Turnover multiplies the cost of each trade.

2. An account falls 40%. What gain does it need to get back to where it started?

   - A. 40%
   - B. 50%
   - C. 66.7%
   - D. 80%

   *Answer C: 66.7%.* 1 / (1 − 0.40) − 1 = 0.667. After the fall, 60% of the capital is left, and that 60% has to grow by two thirds to get back to 100%.

3. With normally distributed annual returns, what fraction of years does a strategy with a Sharpe of 1 close at a loss?

   - A. Almost none
   - B. Roughly one in six
   - C. Half
   - D. It depends only on volatility

   *Answer B: Roughly one in six.* P(R < 0) = Φ(−1) ≈ 15.9%: one year in six. A good strategy has bad years; abandoning it because of one of them is a frequent mistake.

4. Roughly how many years of data are needed for a strategy with a Sharpe of 0.5 to have a t-statistic of 2?

   - A. 2
   - B. 4
   - C. 8
   - D. 16

   *Answer D: 16.* t ≈ SR · √T, so T = (2 / 0.5)² = 16 years. With modest Sharpe ratios, a few years of backtest cannot tell an edge apart from chance.

## Key points

- Net return is gross return minus turnover times the cost of each trade: the more you trade, the more edge you need.
- Losses are not symmetric: after losing 50% you need 100% to recover. Limiting drawdown matters more than maximizing gains.
- With a Sharpe of 1, one year in six closes at a loss. Bad years do not prove that a strategy has stopped working.
- Telling a Sharpe of 0.5 apart from chance takes about 16 years of data: most short backtests prove nothing.
- Leverage multiplies losses just as it multiplies gains. In the EU, most retail CFD accounts lose money.

## Resources

### Research

- [Volatility Targeting in Python](https://pythonparatrading.com/en/volatility-targeting-2/) · Research-0004 · 7 min

### Documentation

- [ESMA: CFD restriction and binary options ban for retail investors (2018)](https://www.esma.europa.eu/press-news/esma-news/esma-agrees-prohibit-binary-options-and-restrict-cfds-protect-retail-investors) · esma.europa.eu
- [Andrew W. Lo, "The Statistics of Sharpe Ratios", Financial Analysts Journal 58(4), 2002](https://doi.org/10.2469/faj.v58.n4.2453) · doi.org

### Books

- [Robert Carver, Systematic Trading (Harriman House, 2015)](https://www.systematicmoney.org/systematic-trading) · systematicmoney.org

Previous lesson: [0.1.1 · Systematic, algorithmic and quantitative trading versus discretionary trading](https://pythonparatrading.com/en/python-trading-course/level-0/systematic-algorithmic-quantitative-trading/)

Next lesson: [0.1.3 · Course structure, entry tests, personal repository and learning with AI](https://pythonparatrading.com/en/python-trading-course/level-0/course-structure-and-learning-with-ai/)
