---
title: "1.6.2 Stylized facts of asset returns"
description: "Fat tails, volatility clustering, weak autocorrelation and the leverage effect: how to measure them in Python and what they imply for a trading system."
url: "https://pythonparatrading.com/en/python-trading-course/level-1/stylized-facts-of-financial-markets/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-1/stylized-facts-of-financial-markets.png"
---

# 1.6.2 · Stylized facts: fat tails, clustering, autocorrelation and leverage

> The returns of almost any market share a few regularities: extreme days far more frequent than the normal distribution says, calm and turbulence that come in streaks, almost no memory in direction and more volatility after falls. This lesson measures them one by one and explains what they break in a backtest.

- Course: [Level 1: Financial data, markets and analysis](https://pythonparatrading.com/en/python-trading-course/level-1/)
- Module: 1.6 · Financial exploratory analysis
- Duration: 55 min
- Prerequisites: [0.7.2 · Probability and distributions, correlation versus causation and simple regression](https://pythonparatrading.com/en/python-trading-course/level-0/probability-distributions-and-regression/), [1.6.1 · Returns, volatility, rolling correlations and drawdown](https://pythonparatrading.com/en/python-trading-course/level-1/returns-volatility-correlation-and-drawdown/)

## By the end of this lesson

- Measure the tails of a return series: kurtosis, frequency of extreme days versus the normal, quantiles and the Hill tail index.
- Tell the autocorrelation of returns (weak) from that of their absolute value (strong and persistent) with the ACF and the Ljung-Box test.
- Measure the leverage effect: future volatility as a function of the sign and size of today's return.
- Know which part of each fact comes from the model that generated the data and what each one implies for risk and validation.

## Four regularities

A **stylized fact** is a statistical regularity that shows up in the returns of many assets, markets and periods,
even if the details change. Cont (2001) collects them in a catalog that is the classic reference[^1]. This lesson
measures the four that most shape a trading system:

```mermaid
flowchart TB
  H[Stylized facts] --> C[Fat tails]
  H --> A[Clustering]
  H --> D[Weak autocorr.]
  H --> L[Leverage]
  C --> R1[VaR and stops]
  A --> R2[Vol-based sizing]
  D --> R3[Fragile signals]
  L --> R4[Asymmetric risk]
```

Lesson 0.7.2 already compared the tails of a fictional ETF with the normal and with a Student's t. Here the two level 1
indices, ISX and FIX, are used, with seven years of NYSE sessions, and each fact is measured with the tool that suits
it.

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

The data is synthetic. ISX and FIX come from a model (a GJR-GARCH with Student's t innovations on top of a trend with
fixed dates) that **deliberately includes** these four facts. Their showing up here proves nothing about markets: it
teaches you to measure them. On a real asset the figures will be different, and they have to be computed on its data.

</aside>

```python
import numpy as np
import pandas as pd
from scipy import stats

isx = pd.read_csv("etf_indice_diario.csv", parse_dates=["fecha"],
                  index_col="fecha")["indice"]
fix = pd.read_csv("fix_indice_diario.csv", parse_dates=["fecha"],
                  index_col="fecha")["indice"]
r = np.log(pd.DataFrame({"ISX": isx, "FIX": fix})).diff().dropna()

xsn = pd.read_parquet("xsn_1h.parquet").set_index("ts")["spot"]
r_xsn = np.log(xsn.resample("D").last()).diff().dropna()
uni = pd.read_parquet("universo_precios.parquet").pivot(
    index="fecha", columns="ticker", values="cierre")
ru = np.log(uni).diff()

def resumen(s):
    return {"n": s.count(), "asimetria": s.skew(),
            "curtosis_exceso": s.kurt()}

tabla = {c: resumen(r[c]) for c in r}
tabla["XSN (crypto, daily)"] = resumen(r_xsn)
tabla["Universe (median of 57)"] = {
    "n": ru.count().median(), "asimetria": ru.skew().median(),
    "curtosis_exceso": ru.kurt().median()}
pd.DataFrame(tabla).T.round(2)
```
```output
                              n  asimetria  curtosis_exceso
ISX                      1759.0       0.19             6.65
FIX                      1759.0      -0.28             8.54
XSN (crypto, daily)       730.0      -0.01             2.43
Universe (median of 57)  2765.0       0.02             0.59
```

ISX and FIX have an excess kurtosis of 6.7 and 8.5: very far from the normal's 0. The other two rows are the point of
the warning: the XSN token and the 57 stocks of the universe come from other models in the course's generator, with
thinner tails (2.4 for XSN) or almost normal ones (a median of 0.6 in the universe). The stylized facts of synthetic
data are the ones its author decided to put in. The rest of the lesson uses ISX and FIX.

## Fat tails

Kurtosis sums up the tails in a single number, and it is an unstable number (as shown below). What matters for risk
is more direct: how many extreme days there are versus those predicted by a normal with the same mean and standard
deviation.

```python
filas = {}
for c in r:
    z = (r[c] - r[c].mean()) / r[c].std()
    for k in (3, 4, 5):
        esperados = len(z) * 2 * stats.norm.sf(k)
        filas[(c, k)] = {"observados": int((z.abs() > k).sum()),
                         "normal": esperados,
                         "veces": (z.abs() > k).sum() / esperados}
pd.DataFrame(filas).T.round(3)
```
```output
       observados  normal     veces
ISX 3        35.0   4.749     7.370
    4        14.0   0.111   125.651
    5         8.0   0.001  7933.043
FIX 3        24.0   4.749     5.054
    4        12.0   0.111   107.701
    5         9.0   0.001  8924.674
```

ISX has 35 days more than three standard deviations from the mean where the normal expects 4.7; 14 beyond four where
it expects 0.11, and 8 beyond five where it expects one thousandth. The further out in the tail, the larger the
normal's error: seven times at ±3σ, more than a hundred at ±4σ, thousands at ±5σ.

**Quantiles** show it another way. If returns were normal, the 1% quantile of the standardized returns would be
−2.33; the 0.1% quantile, −3.09:

```python
z = (r["ISX"] - r["ISX"].mean()) / r["ISX"].std()
p = [0.001, 0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99, 0.999]
pd.DataFrame({"normal": stats.norm.ppf(p),
              "ISX": z.quantile(p).to_numpy()},
             index=[f"{100 * q:g}%" for q in p]).round(2)
```
```output
       normal   ISX
0.1%    -3.09 -5.56
1%      -2.33 -2.62
5%      -1.64 -1.46
25%     -0.67 -0.44
50%      0.00 -0.01
75%      0.67  0.41
95%      1.64  1.50
99%      2.33  3.25
99.9%    3.09  5.44
```

The figure at the top draws this full comparison (a quantile-quantile plot): if ISX were normal, the points would lie
on the diagonal. In the center they almost do; at the extremes they pull away. And there is a less visible detail: in
the center, ISX's 25% and 75% quantiles are **closer** to zero than the normal's (−0.44 and 0.41 versus ±0.67), while
the 0.1% quantile is at −5.56 instead of −3.09. A fat-tailed distribution with the same standard deviation has more
quiet days and more extreme days; it is short on the ones in between.

### The tail index

The tails of returns look more like a power law than like the normal's exponential tail: the probability of a loss
larger than x falls like x^(−α). The **Hill estimator** estimates α with the k largest losses:

$$
\hat{\alpha} = \left(\frac{1}{k}\sum_{i=1}^{k} \ln\frac{x_{(i)}}{x_{(k+1)}}\right)^{-1}
$$

where x₍₁₎ ≥ x₍₂₎ ≥ … are the losses sorted from largest to smallest.

```python
def hill(perdidas, k):
    x = np.sort(perdidas[perdidas > 0])[::-1]
    return 1 / np.mean(np.log(x[:k] / x[k]))

for c in r:
    est = {k: round(float(hill(-r[c].to_numpy(), k)), 2)
           for k in (25, 50, 100, 200)}
    print(c, est)
```
```output
ISX {25: 2.99, 50: 2.69, 100: 2.25, 200: 1.97}
FIX {25: 2.47, 50: 3.05, 100: 2.45, 200: 1.73}
```

With these samples, α comes out between 1.7 and 3.1, and it depends on how many losses are used (k): the estimator is
sensitive to that choice and it is worth looking at several values. The interpretation matters more than the exact
figure: with a power-law tail of index α, only moments of order lower than α exist. With α below 4, theoretical
kurtosis is not finite and the sample kurtosis never settles:

```python
r.groupby(r.index.year).kurt().round(2).T
```
```output
fecha  2019  2020  2021  2022  2023  2024  2025
ISX    1.40  3.07  3.24  5.31  2.88  5.39  6.39
FIX    5.07  2.84  6.87  5.46  4.34  2.24  6.00
```

From 1.4 to 6.4 in ISX and from 2.2 to 6.9 in FIX depending on the year. A single kurtosis number to describe an asset
says less than it seems: a year without extreme days has low kurtosis and the next, with two of them, a very high one.

### Aggregating does not always normalize

When daily returns are summed into weekly or monthly ones, the central limit theorem pushes toward the normal and
kurtosis should fall. It does, but slowly and noisily:

```python
filas = {}
for h, nombre in [(1, "daily"), (5, "weekly"), (21, "monthly")]:
    agg = np.log(pd.DataFrame({"ISX": isx, "FIX": fix})).diff(h)
    agg = agg.dropna().iloc[::h]
    filas[nombre] = {"n": len(agg), **agg.kurt().round(2)}
pd.DataFrame(filas).T
```
```output
              n   ISX    FIX
daily    1759.0  6.65   8.54
weekly    351.0  2.89  12.69
monthly    83.0  9.95   5.84
```

In ISX kurtosis falls from 6.7 at daily frequency to 2.9 at weekly, but rises again to 9.9 at monthly: with 83
months, a single one (the March 2020 crash) is enough to blow up the estimate. In FIX the opposite happens: it rises to
12.7 at weekly and falls to 5.8 at monthly. Aggregation reduces the tails, but the short samples of long horizons do
not allow it to be measured well. That is why tail risks are studied with daily or intraday data and then aggregated
with a model.

## Volatility clustering and weak autocorrelation

The second and third facts go together. The **autocorrelation** (ACF) of a series at lag k is the correlation between
the series and itself k sessions earlier. For returns it is almost zero: yesterday's direction says next to nothing
about today's. For their absolute value, which measures the size of the move, it is clearly positive and decays
slowly.

```python
from statsmodels.tsa.stattools import acf

n = len(r)
banda = 1.96 / np.sqrt(n)
res = {}
for c in r:
    a, b = acf(r[c], nlags=20), acf(r[c].abs(), nlags=20)
    res[c] = {"r_k1": a[1], "|r|_k1": b[1], "|r|_k5": b[5],
              "|r|_k10": b[10], "|r|_k20": b[20],
              "r_fuera_banda": int((np.abs(a[1:]) > banda).sum()),
              "|r|_fuera_banda": int((np.abs(b[1:]) > banda).sum())}
print(f"95% band: ±{banda:.3f}")
pd.DataFrame(res).round(3)
```
```output
95% band: ±0.047
                    ISX     FIX
r_k1              0.045   0.020
|r|_k1            0.248   0.294
|r|_k5            0.253   0.242
|r|_k10           0.198   0.273
|r|_k20           0.180   0.148
r_fuera_banda     3.000   4.000
|r|_fuera_banda  20.000  20.000
```

*Fig. — Autocorrelation of ISX daily returns (top) and of their absolute value (bottom), lags 1 to 20 sessions, with the 95% noise band.* Two bar panels. Top, the autocorrelation of returns: small bars around zero, almost all inside the shaded ±0.047 band; lag 1 is 0.045. Bottom, the autocorrelation of the absolute value: all bars between 0.15 and 0.26, far above the band, and lag 20, highlighted, still at 0.18.

The autocorrelation of ISX returns at lag 1 is 0.045, just inside the noise band (±0.047), and only 3 of the first 20
lags fall outside it in ISX (4 in FIX). That of |r| is outside the band at all 20 lags in both series: 0.25 at lag 1
and still 0.18 at 20 sessions in ISX. This is **volatility clustering**: turbulent days come in streaks. It is what
makes the EWMA of lesson 1.6.1 work and the basis of GARCH models (lesson 2.1.4).

The **Ljung-Box** test sums up the first lags in a single p-value (null hypothesis: no autocorrelation up to lag 10):

```python
from statsmodels.stats.diagnostic import acorr_ljungbox

lb = {f"{c} {serie}": acorr_ljungbox(x, lags=[10]).iloc[0]
      for c in r for serie, x in [("r", r[c]), ("|r|", r[c].abs())]}
pd.DataFrame(lb).T.round(4)
```
```output
           lb_stat  lb_pvalue
ISX r      22.2468     0.0139
ISX |r|   808.6959     0.0000
FIX r      47.3878     0.0000
FIX |r|  1222.0450     0.0000
```

For |r| the p-value is zero for all practical purposes: the clustering is unmistakable. For r, the test rejects the
absence of autocorrelation at 5% in both series. Is there, then, something to predict in the direction? Little: with
autocorrelations of 0.05-0.10, a rule that follows yesterday's direction would eat that edge in costs (lesson 1.1.5),
and in this data that autocorrelation comes from the model itself (the trend between fixed dates changes the mean
return from one segment to the next). On real data something similar happens: small autocorrelations, which change
sign between periods and which costs usually wipe out. A significant test is not a strategy.

Clustering can also be seen without the ACF, in probabilities:

```python
x = r["ISX"]
extremo = x.abs() >= x.abs().quantile(0.9)    # top decile
p_base = extremo.mean()
p_tras = extremo[extremo.shift(1, fill_value=False)].mean()
print(f"P(extreme day) = {p_base:.1%}")
print(f"P(extreme day | extreme yesterday) = {p_tras:.1%}")
```
```output
P(extreme day) = 10.0%
P(extreme day | extreme yesterday) = 24.4%
```

One day in ten is in the decile of the largest moves by definition; after one of them, the probability of another
more than doubles.

A useful way to think about it is as two states that persist: the market spends long periods calm, a shock moves it
into a turbulent state, and it comes out of it gradually. The EWMA and GARCH models are ways of estimating where in
that cycle the market is today.

```mermaid
stateDiagram-v2
  state "Turbulence" as Turbulent
  [*] --> Calm
  Calm --> Calm: quiet day
  Calm --> Turbulent: shock
  Turbulent --> Turbulent: more shocks
  Turbulent --> Calm: dissipates
```

## Leverage effect

The fourth fact is an asymmetry: volatility tends to rise more after a fall than after a rise of the same size. It is
called the **leverage effect** after a classic explanation (when the share price falls, the company's financial
leverage rises), although in indices and other assets it is also attributed to investors' reaction to losses. It is
measured by relating today's return to **future** volatility:

```python
fut = np.sqrt(252 * x.pow(2).rolling(10).mean().shift(-10))
decil = pd.qcut(x, 10, labels=range(1, 11))
tabla = pd.DataFrame({
    "r_media_%": 100 * x.groupby(decil, observed=True).mean(),
    "vol_futura_%": 100 * fut.groupby(decil, observed=True).mean()})
tabla.round(2)
```
```output
     r_media_%  vol_futura_%
ISX
1        -2.26         26.90
2        -0.94         18.63
3        -0.52         15.86
4        -0.27         15.45
5        -0.06         13.27
6         0.14         14.12
7         0.33         15.14
8         0.58         14.79
9         1.00         17.88
10        2.51         23.82
```

*Fig. — ISX, 2019–2025: annualised volatility of the next 10 sessions by decile of today's return.* Ten bars in an asymmetric U shape. After days in the worst decile (highlighted), the volatility of the next ten sessions is 26.9%; it falls to a minimum of 13.3% in decile 5 and rises to 23.8% after the best decile. Both extremes signal more volatility, falls more than rises.

Both extremes have more future volatility than the center: it is clustering again (a big day, of either sign, heralds
more big days). The asymmetry is at the extremes: after the decile of the worst sessions, the volatility of the next
ten is 26.9% annualized; after that of the best, 23.8%. The minimum, 13.3%, is in decile 5, that of almost flat days.

The cleanest way to measure it removes the effect of the volatility level: standardize each day by its forecast
volatility (EWMA, lesson 1.6.1) and compare what follows a day with z < −1 with what follows one with z > +1.

```python
sig = np.sqrt(x.pow(2).ewm(alpha=0.06, adjust=False).mean().shift(1))
z = (x / sig).iloc[63:]
z2_siguiente = z.shift(-1) ** 2
for nombre, filtro in [("z < -1", z < -1), ("-1 <= z <= 1",
                       z.abs() <= 1), ("z > 1", z > 1)]:
    print(f"{nombre:>13}: E[tomorrow's z²] = "
          f"{z2_siguiente[filtro].mean():.2f}  (n = {filtro.sum()})")
print(f"Kurtosis of r: {x.kurt():.2f}; of z: {z.kurt():.2f}")
```
```output
       z < -1: E[tomorrow's z²] = 1.84  (n = 194)
 -1 <= z <= 1: E[tomorrow's z²] = 1.05  (n = 1264)
        z > 1: E[tomorrow's z²] = 1.28  (n = 238)
Kurtosis of r: 6.65; of z: 6.87
```

If the forecast volatility captured everything, tomorrow's standardized square would average 1 in all three rows.
After a quiet day it is almost exactly that (1.05); after a fall of more than one standard deviation, 1.84; after a
rise of the same magnitude, 1.28. Falls move volatility considerably more than rises, and more than the EWMA, which is
symmetric, has already taken in.

The last line answers a question left open in lesson 1.6.1: standardizing by a changing volatility does **not** remove
the tails. The kurtosis of z stays at around 7. In this data that is so by construction: each day's distribution is a
Student's t, fat-tailed in itself, and the changing volatility adds more tail to the full series. On real data some
kurtosis also tends to remain after standardizing, and that is why risk models use t or historical distributions
instead of the normal.

## What each fact changes in a system

| Fact | What it breaks | What to do |
|---|---|---|
| Fat tails | VaR and stops computed with the normal; ratios based on the standard deviation | Historical quantiles or Student's t; stress tests with the worst real days |
| Clustering | Sizing with a fixed volatility; metrics that mix regimes | Rolling volatility (EWMA) in sizing; analyzing results by regime |
| Weak autocorrelation | Signals based on yesterday's direction | Test with costs and out of sample; distrust small p-values |
| Leverage | Symmetric risk assumed in options and hedges | Asymmetric models (GJR, EGARCH, lesson 2.1.4); expect more risk after falls |

A consequence common to all four: the synthetic data used to test a system (level 2, lesson 2.5.7) has to reproduce
these facts. A random walk with normal, independent returns, the simplest model, has none of them, and a system
tested only on it has never seen a real market.

## Exercises

1. Repeat the extreme days table with the returns standardized by the EWMA (`z` from the last cell). Do they get
   closer to the normal?

**Solution.**

```python
ze = (z - z.mean()) / z.std()
for k in (3, 4, 5):
    esperados = len(ze) * 2 * stats.norm.sf(k)
    print(f"|z| > {k}: {int((ze.abs() > k).sum())} observed, "
          f"{esperados:.3f} expected")
```
```output
|z| > 3: 23 observed, 4.579 expected
|z| > 4: 11 observed, 0.107 expected
|z| > 5: 4 observed, 0.001 expected
```

They get somewhat closer at ±3σ, but at ±4σ and ±5σ they remain far above what the normal predicts. The changing
volatility explains part of the tails; the rest is in each day's distribution. A VaR that uses an EWMA volatility with
a normal still underestimates extreme losses.

2. Compute the correlation between today's return and the 10-session volatility of the following days, in ISX and in
   FIX. What sign does it have and how large is it?

**Solution.**

```python
for c in r:
    f10 = r[c].pow(2).rolling(10).mean().shift(-10)
    print(f"{c}: {r[c].corr(np.sqrt(f10)):.3f}")
```
```output
ISX: -0.066
FIX: -0.083
```

Negative and small in both: the bigger the fall today, the somewhat higher the volatility afterward. It is small
because the linear correlation mixes the two effects of the decile table: clustering, which is symmetric, gives both
extremes more future volatility and dilutes the correlation; the asymmetry only shows up clearly when falls are
separated from rises.

3. A colleague shows you a backtest of a mean-reversion strategy that “wins 9 out of 10 trades”, tested with
   simulated normal, independent returns. Which stylized facts are missing from that simulation and what can they
   change?

**Solution.**

All four are missing. Without fat tails, the −8% day that wipes out the gains of many trades in a single one never
shows up. Without clustering, losses do not come in streaks and the drawdown is shorter than it will be. Without the
leverage effect, falls do not bring more volatility, right when a mean-reversion strategy is usually long. And the
zero autocorrelation of the simulation may not be that of the real asset. A strategy that wins 9 times out of 10 and
loses a lot on the tenth is exactly the one a normal simulation makes look safe. It has to be tested on real data or
with simulations that reproduce these facts (bootstrap, GARCH with t; lesson 2.5.7).

[^1]: R. Cont, “Empirical properties of asset returns: stylized facts and statistical issues”, *Quantitative Finance*
    1(2), 2001, 223-236, <https://doi.org/10.1080/713665670>.

## Self-assessment

1. ISX has 8 days with a return more than five standard deviations from the mean. How many would a normal distribution expect in 1,759 days?

   - A. About 8
   - B. About 0.001: one every several thousand years of sessions
   - C. About 88
   - D. None, the normal has no tails

   *Answer B: About 0.001: one every several thousand years of sessions.* P(|Z| > 5) ≈ 5.7 × 10⁻⁷; times 1,759 days, 0.001. Eight such days are incompatible with the normal: real tails are fat, and any risk measure that assumes normality underestimates extreme days.

2. The autocorrelation of ISX daily returns at lag 1 is 0.045 and that of their absolute value, 0.248. What does it mean?

   - A. Direction has almost no memory, but the size of the move does: a turbulent day tends to be followed by another turbulent day
   - B. The next day's direction can be predicted
   - C. The series is not stationary
   - D. There is an error in the data

   *Answer A: Direction has almost no memory, but the size of the move does: a turbulent day tends to be followed by another turbulent day.* It is volatility clustering. Knowing that tomorrow will be a volatile day does not tell you whether it will be up or down, but it does tell you how much risk you are running: that is what the EWMA and GARCH models exploit.

3. What is the leverage effect?

   - A. Margin accounts earn more
   - B. Leveraged ETFs lose value over time
   - C. Stocks with more debt rise more
   - D. Volatility tends to rise more after a fall than after a rise of the same size

   *Answer D: Volatility tends to rise more after a fall than after a rise of the same size.* The name comes from a classic explanation (when the price falls, the company's financial leverage and its risk rise), but the phenomenon is measured in the relationship between today's return and future volatility. In ISX, after a day with z < −1 the next day's standardized square averages 1.84; after one with z > +1, 1.28.

4. ISX's Hill tail index comes out between 2 and 3 depending on how many losses are used. What does a tail index α imply?

   - A. The series has α extreme days per year
   - B. Volatility doubles every α days
   - C. The probability of a loss larger than x falls like x to the power −α: with a low α, moments of order α or higher (such as kurtosis if α < 4) are not stable
   - D. Nothing: it is a number with no interpretation

   *Answer C: The probability of a loss larger than x falls like x to the power −α: with a low α, moments of order α or higher (such as kurtosis if α < 4) are not stable.* A power-law tail with index α only has finite moments of order lower than α. With α below 4, sample kurtosis does not converge: that is why it changes so much from one year to the next (from 1.4 to 6.4 in ISX).

5. The course data is synthetic. Which conclusion of this lesson can you NOT carry over to a real market?

   - A. That fat tails are measured by the frequency of extreme days versus the normal
   - B. That a real asset's tail index is exactly the same as ISX's
   - C. That the ACF of |r| detects volatility clustering
   - D. That future volatility is measured with data after the day

   *Answer B: That a real asset's tail index is exactly the same as ISX's.* The methods are the same on real data; the ISX and FIX figures come from the generator's parameters (a GJR-GARCH with Student's t innovations). Those of the asset you trade have to be measured on its own data.

## Key points

- Fat tails: ISX has 35 days beyond 3 standard deviations where the normal expects 4.7, and 8 beyond 5 where it expects 0.001.
- Direction has almost no memory (ACF of r of 0.045 at lag 1); size does (ACF of |r| of 0.25, still 0.18 at 20 sessions): volatility comes in streaks.
- Leverage effect: future volatility rises more after falls. In ISX, after the worst decile of days, 26.9% annualized; after the best, 23.8%.
- Standardizing by a changing volatility does not remove all of the tails: the rest is in each day's distribution.
- The course data has these facts because the generator includes them. Always measure those of the real asset you trade; Cont (2001) is the classic reference for the catalog.

## Resources

### Research

- [Stochastic Processes - Random Walks](https://pythonparatrading.com/en/stochastic-processes/) · Research-0005 · 11 min
- [Introduction to Volatility - Liquidity](https://pythonparatrading.com/en/introduction-to-volatility-liquidity/) · Research-0001 · 5 min

### Documentation

- [statsmodels: acf](https://www.statsmodels.org/stable/generated/statsmodels.tsa.stattools.acf.html) · statsmodels.org

### Books

- [Cont, R. (2001): Empirical properties of asset returns: stylized facts and statistical issues. Quantitative Finance 1(2), 223-236](https://doi.org/10.1080/713665670) · doi.org

### Data

- [Level 1 data: FIX price index, 2019-2025 (CSV)](https://pythonparatrading.com/curso/datos/nivel-1/fix_indice_diario.csv) · Download · fix_indice_diario.csv

Previous lesson: [1.6.1 · Returns, volatility, rolling correlations and drawdown](https://pythonparatrading.com/en/python-trading-course/level-1/returns-volatility-correlation-and-drawdown/)

Next lesson: [1.6.3 · Seasonality: day of the week, month, turn of the month and intraday sessions](https://pythonparatrading.com/en/python-trading-course/level-1/seasonality-and-calendar-effects/)
