---
title: "1.3.6 Brókeres como fuente de datos"
description: "Interactive Brokers con ib_async, MetaTrader 5 y OANDA como fuentes de datos: arquitectura, límites de ritmo, suscripciones y formato de sus respuestas."
url: "https://pythonparatrading.com/curso-python-trading/nivel-1/brokers-como-fuente-de-datos/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-1/brokers-como-fuente-de-datos.png"
---

# 1.3.6 · Brókeres como fuente de datos

> El bróker con el que operas ya te da datos: históricos, cotizaciones y, a veces, el libro. Son los precios a los que ejecutas, pero llegan con reglas propias: un programa intermedio, límites de ritmo estrictos y suscripciones de mercado. Esta lección los recorre con Interactive Brokers, MetaTrader 5 y OANDA, y simula el ritmo de descarga sin conectarse a nada.

- Curso: [Nivel 1: Datos financieros, mercados y análisis](https://pythonparatrading.com/curso-python-trading/nivel-1/)
- Módulo: 1.3 · Fuentes de datos de mercado
- Duración: 50 min
- Requisitos: [1.2.4 · Forex: pares, pips, lotes, rollover y CFD](https://pythonparatrading.com/curso-python-trading/nivel-1/forex-pares-pips-lotes-y-rollover/), [1.3.1 · Tipos de datos de mercado: EOD, barras, ticks, libro y referencia](https://pythonparatrading.com/curso-python-trading/nivel-1/tipos-de-datos-de-mercado/), [1.3.3 · APIs de datos retail y prosumer](https://pythonparatrading.com/curso-python-trading/nivel-1/apis-de-datos-retail-y-prosumer/)

## Al terminar esta lección

- Explicar la arquitectura de la API de Interactive Brokers (TWS o IB Gateway, puertos, `clientId`) y qué es `ib_async` respecto a `ib_insync`.
- Aplicar los límites de ritmo de los datos históricos de IBKR y estimar cuánto tarda una descarga con ellos.
- Leer el formato de las velas de OANDA v20 y de `copy_rates_*` de MetaTrader 5 y pasarlo a un DataFrame en UTC.
- Decidir cuándo usar el bróker como fuente de datos y cuándo un proveedor independiente.

## El bróker también es un proveedor

Cualquier bróker que tenga API da, además de la ejecución, datos: cotizaciones en tiempo real, velas históricas y, en
algunos casos, profundidad de libro. Para un sistema que opera en ese bróker tienen una ventaja que ningún otro
proveedor iguala: son **los precios del sitio donde se ejecuta**, con su horquilla, su horario y sus comisiones. Un
sistema de divisas que calcula señales con los precios de su bróker y ejecuta en él no tiene que preocuparse de que la
fuente de datos y el mercado de ejecución no coincidan.

Los inconvenientes son los contrarios: historia corta, límites de peticiones pensados para operar y no para descargar
años, y ningún valor deslistado (el bróker solo da lo que se puede operar hoy). Esta lección recorre tres brókeres con
API muy usados en Python: Interactive Brokers, MetaTrader 5 (una plataforma que ofrecen muchos brókeres) y OANDA.

## Interactive Brokers: TWS, Gateway y `ib_async`

La API de Interactive Brokers (IBKR) tiene una arquitectura que sorprende la primera vez: el programa no se conecta a
los servidores de IBKR, sino a una aplicación que corre en la propia máquina, **TWS** (Trader Workstation, la
plataforma de escritorio) o **IB Gateway** (la misma conexión sin interfaz gráfica, más ligera para un servidor). Esa
aplicación mantiene la sesión con IBKR y expone un puerto local:

```mermaid
flowchart LR
  P[Tu programa] --> A[ib_async]
  A --> G[TWS o Gateway]
  G --> S[Servidores IBKR]
```

| Aplicación | Cuenta real | Cuenta de papel |
|---|---|---|
| TWS | 7496 | 7497 |
| IB Gateway | 4001 | 4002 |

Son los puertos por defecto según la documentación de IBKR[^1]; cada aplicación los permite cambiar. Cada conexión
lleva además un `clientId`: varios programas pueden conectarse a la misma TWS a la vez si usan identificadores
distintos. Conectarse a la cuenta de papel (7497 o 4002) es la forma segura de probar: los mismos datos y la misma API,
sin dinero real.

La librería oficial de IBKR para Python (`ibapi`) es de bajo nivel: callbacks y mensajes. La que usa casi todo el mundo
es **`ib_async`**, la continuación de `ib_insync`. Según su README, Ewald de Wit la creó en 2017 y la mantuvo hasta su
fallecimiento a principios de 2024; los mantenedores la renombraron como `ib_async` en una organización nueva de GitHub
(`ib-api-reloaded`) porque ya no podían modificar los repositorios originales. Requiere Python 3.10 o superior y no
necesita el paquete `ibapi`[^2]. El código de `ib_insync` funciona casi igual cambiando el import.

```python skip title="ibkr_historico.py"
from ib_async import IB, Stock, util     # uv add ib_async

ib = IB()
ib.connect("127.0.0.1", 7497, clientId=1)   # TWS, cuenta de papel

contrato = Stock("AAPL", "SMART", "USD")
barras = ib.reqHistoricalData(
    contrato, endDateTime="", durationStr="30 D",
    barSizeSetting="1 hour", whatToShow="MIDPOINT", useRTH=True,
)
df = util.df(barras)
ib.disconnect()
```

`durationStr` es cuánto tiempo hacia atrás (`"30 D"`), `barSizeSetting` el tamaño de la barra, `whatToShow` qué precio
(`TRADES`, `MIDPOINT`, `BID`, `ASK`, `BID_ASK`…) y `useRTH=True` limita a la sesión regular. `util.df` convierte la
lista de barras en un DataFrame.

### Suscripciones de mercado

Que la API funcione no significa que haya datos. Según la documentación de IBKR, salvo los datos diferidos, recibir
datos de mercado por la API **requiere una suscripción de pago** al mercado correspondiente; para pedir datos diferidos
sin suscripción, `ib.reqMarketDataType(3)` antes de pedir cotizaciones. Cada usuario tiene además un número de **líneas
de datos de mercado** (100 por defecto) que comparten la plataforma y la API: cada símbolo que se sigue en tiempo real
ocupa una[^3].

## Límites de ritmo: lo que decide la duración de una descarga

IBKR limita con cuidado lo que se le pide. Hay dos tipos de límite:

- **General.** El número máximo de peticiones por segundo es el de líneas de datos de mercado dividido entre dos: con
  las 100 líneas por defecto, **50 peticiones por segundo**[^3].
- **Histórico de barras pequeñas.** Para barras de 30 segundos o menos, la documentación de la versión anterior de la
  API (marcada como obsoleta, pero la única que publica las reglas) considera violación de ritmo: repetir una petición
  **idéntica en menos de 15 segundos**; hacer **6 o más** peticiones del mismo contrato, bolsa y tipo de dato **en 2
  segundos**; y hacer **más de 60 peticiones en cualquier periodo de 10 minutos**. Con `BID_ASK`, cada petición cuenta
  dos veces. Para barras de 1 minuto o más, esas reglas se levantaron, aunque el servidor sigue frenando a quien pide
  demasiado[^4].

Además, cada petición tiene una duración máxima según el tamaño de barra: la documentación actual da, por ejemplo,
2.000 segundos para barras de 1 segundo y 86.400 segundos (un día) para barras de 5 segundos[^5]. Con esas reglas se
puede simular un descargador que nunca las viola, con un reloj virtual:

```python
import math
from collections import deque

class RitmoIBKR:
    """Reglas para barras de 30 s o menos (documentación anterior)."""

    def __init__(self):
        self.hechas = deque()          # (t, contrato, clave, peso)

    def espera(self, t, contrato, clave, peso=1):
        while self.hechas and self.hechas[0][0] <= t - 600:
            self.hechas.popleft()
        e = 0.0
        # más de 60 en 10 minutos (BID_ASK pesa 2)
        usadas = sum(h[3] for h in self.hechas)
        if usadas + peso > 60:
            acum = 0
            for h in self.hechas:
                acum += h[3]
                if usadas - acum + peso <= 60:
                    e = max(e, h[0] + 600 - t)
                    break
        # 6 o más del mismo contrato en 2 segundos
        mismo = [h[0] for h in self.hechas if h[1] == contrato]
        if len(mismo) >= 5:
            e = max(e, mismo[-5] + 2 - t)
        # idéntica en menos de 15 segundos
        for h in self.hechas:
            if h[2] == clave:
                e = max(e, h[0] + 15 - t)
        return max(e, 0.0)

    def registra(self, t, contrato, clave, peso=1):
        self.hechas.append((t, contrato, clave, peso))

def descarga(n_peticiones, peso=1, latencia=0.5):
    ritmo, t, horas = RitmoIBKR(), 0.0, []
    for i in range(n_peticiones):
        clave = ("NOVA", i)        # cada petición, otro tramo
        while (e := ritmo.espera(t, "NOVA", clave, peso)) > 0:
            t += e
        ritmo.registra(t, "NOVA", clave, peso)
        horas.append(t)
        t += latencia
    return horas

horas = descarga(252)                  # un día por petición
print(f"252 peticiones: {horas[-1] / 60:.1f} minutos")
print("primeras:", [round(h, 1) for h in horas[:7]])
```
```output
252 peticiones: 40.1 minutos
primeras: [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
```

La simulación supone que cada respuesta tarda medio segundo. A ese paso salen cuatro peticiones cada dos segundos, así
que la regla de las 6 en 2 segundos no llega a activarse (con respuestas más rápidas sí lo haría). La que manda es la de
60 en 10 minutos: el descargador hace 60 peticiones en medio minuto y espera el resto de la ventana. Cuatro ventanas
completas y 12 peticiones más: 40,1 minutos (figura de la cabecera).

Ahora la cuenta que importa: cuánto tarda un año de barras de cada tamaño para **un** contrato, con la duración máxima
de cada petición:

```python
sesion = int(6.5 * 3600)              # 23.400 s de sesión regular
casos = {
    "5 s, TRADES": (86_400, 1),
    "5 s, BID_ASK": (86_400, 2),
    "1 s, TRADES": (2_000, 1),
    "1 s, BID_ASK": (2_000, 2),
}
for nombre, (max_seg, peso) in casos.items():
    por_dia = math.ceil(sesion / max_seg)
    n = 252 * por_dia
    h = descarga(n, peso=peso)
    print(f"{nombre:13} {n:5} peticiones  {h[-1] / 3600:5.2f} h")
```
```output
5 s, TRADES     252 peticiones   0.67 h
5 s, BID_ASK    252 peticiones   1.33 h
1 s, TRADES    3024 peticiones   8.34 h
1 s, BID_ASK   3024 peticiones  16.67 h
```

*Fig. — Tiempo de descarga de un año de barras pequeñas de un contrato con las reglas de ritmo de IBKR, según el tamaño de barra y el tipo de dato.* Barras horizontales: barras de 5 segundos con TRADES, 0,67 horas; con BID_ASK, 1,33; barras de 1 segundo con TRADES, 8,34 horas, destacado; con BID_ASK, 16,67.

Un año de barras de 5 segundos cabe en 252 peticiones y unos 40 minutos; el mismo año en barras de 1 segundo necesita
12 peticiones por día (23.400 / 2.000, redondeado hacia arriba), 3.024 en total y más de 8 horas, o el doble si se
piden bid y ask. Para cien contratos, multiplica. Por eso los históricos largos de barras pequeñas no se bajan del
bróker sino de un proveedor (lecciones 1.3.3 y 1.3.4), y el bróker se reserva para lo reciente y para el tiempo real.

### Qué pasa si no se respeta

Un bucle que dispara las peticiones sin limitador no sabe que está violando las reglas hasta que IBKR responde con un
error de violación de ritmo, y con violaciones repetidas la conexión puede acabar cortada. Con la misma clase se puede
contar cuántas peticiones de un bucle ingenuo serían violaciones:

```python
def violaciones(n, latencia=0.5):
    ritmo, t, malas = RitmoIBKR(), 0.0, 0
    for i in range(n):
        clave = ("NOVA", i)
        if ritmo.espera(t, "NOVA", clave) > 0:
            malas += 1
        ritmo.registra(t, "NOVA", clave)
        t += latencia
    return malas

print(violaciones(252), "de 252 peticiones fuera de las reglas")
```
```output
192 de 252 peticiones fuera de las reglas
```

Las 60 primeras caben en la ventana de 10 minutos; las 192 siguientes la violan. El bucle termina en dos minutos, pero
habría recibido 192 errores (y, mucho antes, probablemente una desconexión). La solución no es reintentar más deprisa,
sino esperar lo que dicen las reglas antes de pedir.

## MetaTrader 5: el terminal como servidor de datos

MetaTrader 5 es una plataforma de trading que ofrecen muchos brókeres de divisas y CFD. Su paquete de Python
(`MetaTrader5` en PyPI) no habla con ningún servidor: se conecta a un **terminal MetaTrader 5 instalado** en la misma
máquina, que es el que tiene la sesión con el bróker. `initialize()` busca el terminal o recibe la ruta de su
ejecutable; las ruedas publicadas en PyPI son solo para Windows (`win_amd64`)[^6].

```python skip title="mt5_velas.py"
from datetime import datetime, timezone

import MetaTrader5 as mt5            # solo Windows, con el terminal

if not mt5.initialize():
    raise RuntimeError(mt5.last_error())
desde = datetime(2025, 6, 2, tzinfo=timezone.utc)
velas = mt5.copy_rates_from("EURUSD", mt5.TIMEFRAME_H1, desde, 500)
ultimo = mt5.symbol_info_tick("EURUSD")
mt5.shutdown()
```

`copy_rates_from` devuelve las velas anteriores a la fecha dada como un **array de numpy** con campos con nombre:
`time`, `open`, `high`, `low`, `close`, `tick_volume`, `spread` y `real_volume`. La documentación avisa de dos cosas:
el terminal guarda la hora de apertura en **UTC** (por eso la fecha debe crearse en UTC) y el número de velas que puede
devolver está limitado por el parámetro «Max. bars in chart» del terminal[^7]. Con un array de esa forma, construido con
el EURUSD sintético del curso:

```python
import numpy as np
import pandas as pd

eur = pd.read_parquet("eurusd_1h.parquet").set_index("ts")
h1 = eur.loc["2025-06-02", "mid"]
tipo = np.dtype([("time", "<i8"), ("open", "<f8"), ("high", "<f8"),
                 ("low", "<f8"), ("close", "<f8"),
                 ("tick_volume", "<u8"), ("spread", "<i4"),
                 ("real_volume", "<u8")])
rates = np.zeros(len(h1) - 1, dtype=tipo)
rates["time"] = h1.index[:-1].astype("int64") // 10**6   # segundos
rates["open"] = h1.values[:-1]
rates["close"] = h1.values[1:]
rates["high"] = np.maximum(rates["open"], rates["close"])
rates["low"] = np.minimum(rates["open"], rates["close"])

velas = pd.DataFrame(rates)
velas["time"] = pd.to_datetime(velas["time"], unit="s", utc=True)
velas.set_index("time")[["open", "close"]].head(3)
```
```output
                               open     close
time
2025-06-02 00:00:00+00:00  1.172300  1.172725
2025-06-02 01:00:00+00:00  1.172725  1.173615
2025-06-02 02:00:00+00:00  1.173615  1.173730
```

El campo `time` viene en **segundos** desde 1970 (no en milisegundos como en ccxt, lección 1.3.5), y
`pd.DataFrame(rates)` respeta los nombres de los campos. Aquí el máximo y el mínimo salen de la apertura y el cierre
porque el EURUSD del curso es una cotización por hora, no velas; en el terminal, cada vela trae los suyos. Dos
detalles propios de esta fuente: `tick_volume` es el número de cambios de precio, no un volumen negociado (en divisas
al contado no hay cinta consolidada, lección 1.2.4), y `spread` es la horquilla en puntos del bróker, que cambia de un
bróker a otro.

## OANDA: una API REST con velas bid, ask y medio

OANDA tiene una API REST, la **v20**, con dos entornos: práctica (`api-fxpractice.oanda.com`) y real
(`api-fxtrade.oanda.com`), y servidores de streaming aparte (`stream-fxpractice.oanda.com`,
`stream-fxtrade.oanda.com`). La autenticación es un token en la cabecera `Authorization: Bearer`. Sus límites, según la
guía de desarrollo: **120 peticiones por segundo** en la API REST (por encima, 429), **20 streams** activos y **no más de
2 conexiones nuevas por segundo**; la página de buenas prácticas recomienda, en una conexión ya abierta, no pasar de
100 por segundo[^8].

Las velas se piden en `GET /v3/accounts/{accountID}/instruments/{instrument}/candles`, con `granularity` (por defecto
`S5`, cinco segundos), `count` (por defecto 500, máximo 5.000) y `price`, que combina `M` (medio), `B` (bid) y `A`
(ask)[^9].

```python skip title="oanda_velas.py"
import os
from urllib.parse import urlencode
from urllib.request import Request, urlopen

cuenta = os.environ["OANDA_ACCOUNT"]
url = (f"https://api-fxpractice.oanda.com/v3/accounts/{cuenta}"
       "/instruments/EUR_USD/candles?"
       + urlencode({"granularity": "H4", "count": 500, "price": "MBA"}))
req = Request(url, headers={
    "Authorization": f"Bearer {os.environ['OANDA_TOKEN']}"})
with urlopen(req, timeout=10) as r:
    crudo = r.read()
```

La respuesta trae `instrument`, `granularity` y una lista `candles`. Cada vela tiene `time` (el inicio de la vela, en
RFC 3339 con nanosegundos: `AAAA-MM-DDTHH:MM:SS.nnnnnnnnnZ`), `volume` (el número de precios creados en la vela),
`complete` y un bloque `mid`, `bid` o `ask` con `o`, `h`, `l` y `c`. Los precios llegan **como texto**, con la precisión
del instrumento. Una respuesta con ese esquema, construida con el EURUSD sintético en velas de 4 horas:

```python
import json

def bloque(s):
    return {"o": f"{s.iloc[0]:.5f}", "h": f"{s.max():.5f}",
            "l": f"{s.min():.5f}", "c": f"{s.iloc[-1]:.5f}"}

dia = eur.loc["2025-06-02 00:00":"2025-06-02 15:00"]
velas_json = []
for inicio, g in dia.groupby(pd.Grouper(freq="4h")):
    velas_json.append({
        "time": inicio.strftime("%Y-%m-%dT%H:%M:%S.000000000Z"),
        "volume": len(g), "complete": len(g) == 4,
        "bid": bloque(g["bid"]), "ask": bloque(g["ask"]),
        "mid": bloque(g["mid"]),
    })
velas_json[-1]["complete"] = False          # la vela en curso
respuesta = json.dumps({"instrument": "EUR_USD",
                        "granularity": "H4", "candles": velas_json})
print(respuesta[:118], "...")
```
```output
{"instrument": "EUR_USD", "granularity": "H4", "candles": [{"time": "2025-06-02T00:00:00.000000000Z", "volume": 4, "co ...
```

Y su lectura: texto a número, solo velas completas y horquilla de cierre en pips.

```python
datos = json.loads(respuesta)
filas = []
for v in datos["candles"]:
    if not v["complete"]:
        continue                            # nunca al histórico
    fila = {"time": v["time"], "volume": v["volume"]}
    for lado in ("bid", "ask"):
        for k, x in v[lado].items():
            fila[f"{lado}_{k}"] = float(x)
    filas.append(fila)

oanda = pd.DataFrame(filas)
oanda["time"] = pd.to_datetime(oanda["time"], utc=True)
oanda["spread_pips"] = 1e4 * (oanda["ask_c"] - oanda["bid_c"])
oanda.set_index("time")[["bid_c", "ask_c", "spread_pips"]].round(5)
```
```output
                             bid_c    ask_c  spread_pips
time
2025-06-02 00:00:00+00:00  1.17370  1.17376          0.6
2025-06-02 04:00:00+00:00  1.17570  1.17572          0.2
2025-06-02 08:00:00+00:00  1.17466  1.17468          0.2
```

Tres velas completas; la cuarta, marcada como en curso, se descarta. Las velas de un bróker de divisas traen bid y ask
por separado, que es justo lo que hace falta para un backtest con costes realistas: la compra al ask, la venta al bid
(lección 1.1.5). Las velas de «precio medio» que dan muchas fuentes gratuitas no permiten eso.

## Cuándo usar el bróker como fuente

```mermaid
flowchart TB
  Q{¿Para qué?} -- señal en real --> B[Datos del bróker]
  Q -- backtest largo --> P[Proveedor]
  Q -- universo acciones --> P
  B --> C[Contrastar]
  P --> C
```

| Uso | ¿Bróker? | Por qué |
|---|---|---|
| Señales y ejecución en tiempo real | Sí | Son los precios donde se ejecuta |
| Costes de un sistema de divisas o CFD | Sí | Su horquilla y su financiación son las que se pagan |
| Histórico largo de barras pequeñas | No | Límites de ritmo y duración máxima por petición |
| Universo de acciones para un backtest | No | Solo tiene lo que cotiza hoy: sesgo de supervivencia (lección 1.3.8) |
| Validar un proveedor | Sí, como contraste | Comparar unos días de precios de ambos detecta diferencias de horario o de ajustes |

La combinación habitual es un proveedor para la historia y el research y el bróker para la operativa, con una
comprobación periódica de que los dos cuentan la misma historia en los días que se solapan.

## Ejercicios

1. Con `descarga`, ¿cuánto tarda un año de barras de 5 segundos con `BID_ASK` para 10 contratos, si se piden uno detrás
   de otro con el mismo limitador? (Pista: las reglas de 10 minutos cuentan todas las peticiones, no por contrato.)

**Solución.**

```python
h = descarga(10 * 252, peso=2)
print(f"{len(h)} peticiones: {h[-1] / 3600:.1f} horas")
```
```output
2520 peticiones: 13.8 horas
```

2.520 peticiones que cuentan doble son 5.040 unidades de las 60 por ventana de 10 minutos: unas 84 ventanas, casi 14
horas. Con diez contratos la descarga ya no es «un momento antes de empezar», sino un trabajo de noche que hay que poder
reanudar si se corta (lección 1.4.8).

2. En la respuesta de OANDA, calcula la horquilla media de las velas completas en pips usando el cierre, y compárala con
   la de la apertura. ¿Por qué no tienen por qué coincidir?

**Solución.**

```python
oanda["spread_ap"] = 1e4 * (oanda["ask_o"] - oanda["bid_o"])
oanda[["spread_ap", "spread_pips"]].mean().round(2)
```
```output
spread_ap      0.53
spread_pips    0.33
dtype: float64
```

Las horquillas de apertura y de cierre son dos fotos en dos momentos distintos de la vela; la horquilla cambia con la
sesión (más ancha en Asia y en el rollover, más estrecha en el solape de Londres y Nueva York, lección 1.2.4). Para un
backtest, la horquilla que importa es la del momento de la orden: si el sistema opera al cierre de la vela, la de cierre.

3. Tu sistema opera EURUSD con velas de 4 horas en un bróker con MetaTrader 5 y quieres 15 años de historia para
   probarlo. ¿De dónde sacarías los datos y qué comprobarías?

**Solución.**

El histórico del terminal está limitado por «Max. bars in chart» y por lo que el bróker tenga guardado; para 15 años
conviene un proveedor de divisas con historia larga (lección 1.3.3) y usar el bróker para los años recientes y la
operativa. Comprobaciones: que las horas sean UTC en ambas fuentes (MT5 guarda la apertura en UTC, pero muchos brókeres
muestran la hora del servidor en otra zona), que las velas de 4 horas empiecen en las mismas horas, la diferencia de
precios en los días que se solapan (precio medio frente a bid), y que la horquilla del backtest sea la del bróker
(`spread`), no la del proveedor.

[^1]: IBKR: [Configure the Connecting Socket Port](https://www.interactivebrokers.com/docs/excel/dde/dde-socket-bridge/configuring-the-socket-bridge/configure-the-connecting-socket-port), consultada el 21/10/2026.
[^2]: README de [ib_async](https://github.com/ib-api-reloaded/ib_async), consultado el 21/10/2026; versión 2.1.0 en [PyPI](https://pypi.org/project/ib_async/).
[^3]: IBKR: [Pacing Limitations](https://www.interactivebrokers.com/docs/tws-api/doc/pacing-limitations/introduction), [Market Data Lines](https://www.interactivebrokers.com/docs/general/market-data-subscriptions/market-data-lines/introduction) y [Market Data](https://www.interactivebrokers.com/docs/tws-api/doc/md/introduction), consultadas el 21/10/2026.
[^4]: IBKR, documentación anterior de la API: [Historical Data Limitations](https://interactivebrokers.github.io/tws-api/historical_limitations.html) («This documentation is now deprecated»), consultada el 21/10/2026.
[^5]: IBKR: [Historical Bars (OHLC)](https://www.interactivebrokers.com/docs/tws-api/doc/md/historical-bars), tabla de duración máxima por tamaño de barra.
[^6]: [MetaTrader 5: Python Integration](https://www.mql5.com/en/docs/python_metatrader5) e [initialize](https://www.mql5.com/en/docs/python_metatrader5/mt5initialize_py); ruedas `win_amd64` de la versión 5.0.6231 en [PyPI](https://pypi.org/project/MetaTrader5/).
[^7]: [MetaTrader 5: copy_rates_from](https://www.mql5.com/en/docs/python_metatrader5/mt5copyratesfrom_py).
[^8]: OANDA v20: [Development Guide](https://developer.oanda.com/rest-live-v20/development-guide/), [Best Practices](https://developer.oanda.com/rest-live-v20/best-practices/) y [Troubleshooting](https://developer.oanda.com/rest-live-v20/troubleshooting-errors/), consultadas el 21/10/2026.
[^9]: OANDA v20: [Pricing](https://developer.oanda.com/rest-live-v20/pricing-ep/) (endpoint de velas), [Instrument](https://developer.oanda.com/rest-live-v20/instrument-df/) (esquema `Candlestick`) y [Primitives](https://developer.oanda.com/rest-live-v20/primitives-df/) (`DateTime`).

## Autoevaluación

1. Conectas `ib_async` al puerto 7497. ¿A qué te conectas, según la tabla de puertos de IBKR?

   - A. A TWS con la cuenta real
   - B. A TWS con la cuenta de papel (simulada)
   - C. A IB Gateway con la cuenta real
   - D. Directamente a los servidores de IBKR

   *Respuesta B: A TWS con la cuenta de papel (simulada).* Los puertos por defecto son 7496 (TWS real), 7497 (TWS papel), 4001 (Gateway real) y 4002 (Gateway papel). La API nunca habla directamente con los servidores: siempre pasa por TWS o por IB Gateway en tu máquina.

2. ¿Qué relación hay entre `ib_async` e `ib_insync`?

   - A. Son librerías rivales sin relación
   - B. `ib_async` es la continuación de `ib_insync`, renombrada en una organización nueva de GitHub tras el fallecimiento de su autor en 2024
   - C. `ib_async` es la librería oficial de IBKR
   - D. `ib_insync` es la versión nueva

   *Respuesta B: `ib_async` es la continuación de `ib_insync`, renombrada en una organización nueva de GitHub tras el fallecimiento de su autor en 2024.* Su README cuenta que Ewald de Wit creó `ib_insync` en 2017 y la mantuvo hasta su fallecimiento a principios de 2024; los mantenedores la renombraron porque ya no podían modificar el repositorio original.

3. Con las reglas de ritmo de IBKR para barras de 30 segundos o menos, ¿cuántas peticiones de histórico puedes hacer en 10 minutos?

   - A. Ilimitadas
   - B. 60
   - C. 600
   - D. 6

   *Respuesta B: 60.* Más de 60 peticiones en cualquier periodo de 10 minutos es una violación de ritmo. Además no se puede repetir una petición idéntica en 15 segundos ni hacer 6 o más del mismo contrato en 2 segundos. Con `BID_ASK`, cada petición cuenta doble.

4. OANDA devuelve una vela con `"complete": false`. ¿Qué significa?

   - A. Que faltan precios
   - B. Que es la vela en curso: su final está en el futuro y sus valores cambiarán
   - C. Que el mercado estaba cerrado
   - D. Que hubo un error

   *Respuesta B: Que es la vela en curso: su final está en el futuro y sus valores cambiarán.* La documentación define una vela completa como aquella cuyo final no está en el futuro. Una vela incompleta no se guarda como histórico.

5. ¿Cuál es la principal ventaja de usar el bróker como fuente de datos?

   - A. Que siempre es gratis y sin límites
   - B. Que son los precios del sitio donde ejecutas, con su horquilla y su horario, y no hace falta otro contrato
   - C. Que tiene más historia que cualquier proveedor
   - D. Que no tiene sesgo de supervivencia

   *Respuesta B: Que son los precios del sitio donde ejecutas, con su horquilla y su horario, y no hace falta otro contrato.* Para un sistema que opera en ese bróker, sus precios son los ejecutables. A cambio, la historia suele ser más corta, los límites más estrictos y no hay valores deslistados (lección 1.3.8).

## Ideas clave

- La API de IBKR no habla con los servidores: habla con TWS o IB Gateway en tu máquina, por un puerto (7496/7497 en TWS, 4001/4002 en Gateway) y con un `clientId`.
- `ib_async` es la continuación de `ib_insync`; requiere Python 3.10 o superior y no necesita el paquete `ibapi` de IBKR.
- Los datos por API necesitan una suscripción de mercado de pago (salvo los diferidos), y las peticiones están limitadas: 50 por segundo en general y reglas propias para el histórico de barras pequeñas.
- El límite de ritmo decide cuánto tarda una descarga histórica: un año de barras de 1 segundo de un contrato son unas 3.000 peticiones y más de 8 horas.
- MetaTrader 5 entrega arrays de numpy con hora en UTC desde un terminal instalado (solo hay ruedas para Windows); OANDA, JSON con precios como texto, hora RFC 3339 y una marca `complete`.
- El bróker es la mejor fuente de los precios a los que ejecutas, y una mala fuente de historia larga y de universos sin sesgo.

## Recursos

### Documentación

- [ib_async (GitHub, ib-api-reloaded)](https://github.com/ib-api-reloaded/ib_async) · github.com
- [IBKR TWS API: Pacing Limitations](https://www.interactivebrokers.com/docs/tws-api/doc/pacing-limitations/introduction) · interactivebrokers.com
- [IBKR TWS API (versión anterior): Historical Data Limitations](https://interactivebrokers.github.io/tws-api/historical_limitations.html) · interactivebrokers.github.io
- [MetaTrader 5: integración con Python (MQL5 Reference)](https://www.mql5.com/en/docs/python_metatrader5) · mql5.com
- [OANDA v20 REST: Development Guide](https://developer.oanda.com/rest-live-v20/development-guide/) · developer.oanda.com

### Datos

- [Datos del nivel 1: EURUSD sintético por horas (Parquet)](https://pythonparatrading.com/curso/datos/nivel-1/eurusd_1h.parquet) · Descarga · eurusd_1h.parquet

Lección anterior: [1.3.5 · Datos de cripto y on-chain](https://pythonparatrading.com/curso-python-trading/nivel-1/datos-de-cripto-y-on-chain/)

Lección siguiente: [1.3.7 · Licencias y términos de uso de datos](https://pythonparatrading.com/curso-python-trading/nivel-1/licencias-y-terminos-de-uso-de-datos/)
