---
name: to_returns
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: pure
signature: "def to_returns(values: list, method: str = 'log') -> dict"
description: "Convierte una serie de niveles (precios) a retornos: 'log' (ln(p_t/p_{t-1})) o 'simple' (p_t/p_{t-1}-1). Para correlacionar/modelar series financieras sobre retornos (aprox.) estacionarios en vez de niveles no estacionarios, evitando la regresion espuria (Granger-Newbold, Lopez de Prado). Devuelve la serie de retornos mas stats basicas. Maneja ceros/negativos en log marcando el paso invalido. Descarta None/NaN; <2 puntos validos -> nota."
tags: [timeseries, returns, finance, stationarity, log-returns, eda, python]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [math]
params:
  - name: values
    desc: "serie de niveles (precios) en orden cronologico. None/NaN/infinitos/no-numericos se descartan antes de calcular."
  - name: method
    desc: "'log' (default) para retornos logaritmicos ln(p_t/p_{t-1}), o 'simple' para retornos aritmeticos p_t/p_{t-1}-1."
output: "dict con 'returns' (lista, un retorno por par consecutivo; None si el paso es invalido), 'method', 'n_levels', 'n_returns', 'n_skipped', y stats 'mean'/'std'/'min'/'max' de los retornos validos (None si todos invalidos). method invalido o <2 puntos: dict con 'note' y 'returns': []. Nunca lanza excepcion."
tested: true
tests: ["test_log_returns_valores_conocidos", "test_simple_returns_valores_conocidos", "test_log_marca_no_positivo_como_invalido", "test_simple_admite_negativos", "test_method_invalido_devuelve_nota", "test_un_solo_punto_devuelve_nota", "test_descarta_none_y_nan", "test_stats_de_retornos"]
test_file_path: "python/functions/datascience/to_returns_test.py"
file_path: "python/functions/datascience/to_returns.py"
---

## Ejemplo

```python
from datascience import to_returns

# Retornos logaritmicos de una serie de precios
precios = [100.0, 105.0, 103.0, 108.0]
res = to_returns(precios, method="log")
res["returns"]      # -> [0.0488, -0.0192, 0.0474]  (ln(105/100), ln(103/105), ...)
res["n_returns"]    # -> 3

# Retornos simples (porcentuales)
to_returns(precios, method="simple")["returns"]   # -> [0.05, -0.0190, 0.0485]

# Un precio <= 0 invalida ese paso en log (no peta)
to_returns([100.0, 0.0, 50.0], method="log")["n_skipped"]   # -> 2
```

## Cuando usarla

Antes de correlacionar, medir volatilidad o modelar una serie financiera de
precios. Los precios son no estacionarios (tienen raiz unitaria): correlacionar
dos series de precios da correlaciones altas pero espurias. Los retornos son
(aproximadamente) estacionarios, asi que son la unidad correcta. Encadena con
`adf_kpss_stationarity` para confirmar que los retornos ya son estacionarios, y
luego con `spearman_corr`/`pearson` o un modelo. Usa `log` para modelar (aditivo
en el tiempo) y `simple` cuando necesites interpretar el retorno como porcentaje.

## Gotchas

- Es pura (solo `math`, sin dependencias externas).
- `method="log"` exige precios estrictamente positivos: un valor <= 0 invalida
  ese paso (queda `None` en `returns` y suma a `n_skipped`) en lugar de lanzar
  `ValueError`. Revisa `n_skipped` si tu serie puede tener ceros/negativos.
- La serie de retornos tiene **un elemento menos** que la de niveles (no hay
  retorno para el primer punto).
- Los huecos (None/NaN) se eliminan ANTES de emparejar, asi que el retorno se
  calcula entre puntos validos consecutivos en el tiempo-indice original, no
  rellenando el hueco. Si necesitas tratar huecos como saltos reales, limpia tu
  la serie antes.
- `simple` solo invalida el paso cuando el precio previo es exactamente 0
  (division por cero); admite precios y retornos negativos.
