> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rail.cl/llms.txt
> Use this file to discover all available pages before exploring further.

# Productos de inversión

> El bloque investment de una cuenta: depósitos a plazo, fondos mutuos y APV.

Un producto de inversión no se describe con un saldo. Tiene **tres montos
distintos** (lo invertido, lo que vale hoy, lo ganado), **una cantidad** de
cuotas, **un precio unitario** y —lo más fácil de perder— **una fecha a la que
ese precio corresponde**, que no es la fecha en que Rail fue al banco.

Todo eso viaja en un bloque `investment` dentro del recurso
[`account`](/concepts/accounts).

<Note>
  **Es un cambio aditivo.** `investment` es un campo nuevo del `account` que ya
  consumes: si tu integración lo ignora, sigue funcionando igual, sin coordinar
  deploys. Viene poblado **sólo** en cuentas `type: "investment"`; en cualquier
  otra es `null`.
</Note>

## El shape

<CodeGroup>
  ```json Fondo mutuo theme={null}
  {
    "object": "account",
    "mode": "live",
    "id": "acc_3f8a1c5d9e2b6741",
    "link_id": "link_8a2f4c6e1b9d5037",
    "name": "Fondo Mutuo M_C_FIN_P1",
    "official_name": "M_C_FIN_P1",
    "number": null,
    "last_4": null,
    "type": "investment",
    "currency": "CLP",
    "balance": {
      "available": 17634521,
      "current": 17634521,
      "limit": null
    },
    "holder_id": "12.345.678-9",
    "holder_name": "JANE DOE EXAMPLE",
    "is_additional_card": null,
    "investment": {
      "kind": "mutual_fund",
      "invested": 17059339,
      "gain": 575182,
      "gain_pct": "3.3716547",
      "units": "14819.8826",
      "unit_price": "1189.9231",
      "priced_at": "2026-10-10",
      "instrument_id": "M_C_FIN_P1",
      "maturity_date": null,
      "maturity_amount": null,
      "rate": null,
      "rate_basis": null,
      "term_days": null
    },
    "refreshed_at": "2026-10-13T12:00:00Z",
    "last_sync_at": "2026-10-13T12:00:00Z",
    "next_sync_at": "2026-10-13T16:00:00Z",
    "link_status": "active",
    "removed_from_link": false,
    "removed_at": null,
    "status": "active",
    "statement": null
  }
  ```

  ```json Depósito a plazo theme={null}
  {
    "object": "account",
    "mode": "live",
    "id": "acc_1b9d5037a2f4c6e8",
    "link_id": "link_8a2f4c6e1b9d5037",
    "name": "DPRI HASTA 30 DS",
    "official_name": "DPRI HASTA 30 DS",
    "number": null,
    "last_4": null,
    "type": "investment",
    "currency": "CLP",
    "balance": {
      "available": 0,
      "current": 256618,
      "limit": null
    },
    "holder_id": "12.345.678-9",
    "holder_name": "JANE DOE EXAMPLE",
    "is_additional_card": null,
    "investment": {
      "kind": "time_deposit",
      "invested": 255901,
      "gain": 717,
      "gain_pct": null,
      "units": null,
      "unit_price": null,
      "priced_at": null,
      "instrument_id": "0001234567",
      "maturity_date": "2026-10-07",
      "maturity_amount": 256618,
      "rate": "0.28",
      "rate_basis": "period",
      "term_days": 30
    },
    "refreshed_at": "2026-10-13T12:00:00Z",
    "last_sync_at": "2026-10-13T12:00:00Z",
    "next_sync_at": "2026-10-13T16:00:00Z",
    "link_status": "active",
    "removed_from_link": false,
    "removed_at": null,
    "status": "active",
    "statement": null
  }
  ```
</CodeGroup>

## Campos

| Campo | Tipo | Qué es |
| - | - | - |
| `kind` | enum | Qué clase de producto es (tabla abajo). Nunca `null`. |
| `invested` | integer \| null | Lo invertido (costo de adquisición), en **minor units**. |
| `gain` | integer \| null | Ganancia/pérdida, en **minor units**. **Negativo = pérdida.** |
| `gain_pct` | string \| null | Rentabilidad en %, como **string decimal** (`"3.3716547"`). |
| `units` | string \| null | Cuotas/unidades, como **string decimal** (`"14819.8826"`). |
| `unit_price` | string \| null | Valor cuota / precio unitario, como **string decimal**. |
| `priced_at` | `YYYY-MM-DD` \| null | A qué **día** corresponde la valorización. No es `refreshed_at`. |
| `instrument_id` | string \| null | Identificador del instrumento (nemotécnico del fondo, nº de documento del depósito). Nunca el RUT del titular. |
| `maturity_date` | `YYYY-MM-DD` \| null | Vencimiento. Sólo depósitos a plazo. |
| `maturity_amount` | integer \| null | Monto al vencimiento, en **minor units**. Sólo depósitos a plazo. |
| `rate` | string \| null | Tasa, como **string decimal**. Sólo depósitos a plazo. |
| `rate_basis` | enum \| null | Sobre qué período aplica `rate`: `period`, `monthly` o `annual`. |
| `term_days` | integer \| null | Plazo del depósito en días. |

Todos los campos salvo `kind` pueden venir en `null`: el banco no informa lo
mismo para cada producto — ver **Qué campos trae cada producto** más abajo.

## kind

El producto concreto se distingue con `kind`, **no** con `account.type` — así
el `switch (type)` de tu integración no se rompe cuando aparece un producto
nuevo.

| `kind` | Producto |
| - | - |
| `time_deposit` | Depósito a plazo / pagaré (DPF, DPRI, DPRA, PPF…). |
| `mutual_fund` | Fondo mutuo. |
| `apv` | Ahorro previsional voluntario. |
| `stock` | Acciones. |
| `cash` | Caja del corredor. |
| `other` | Fallback explícito cuando el banco no permite clasificarlo. |

El enum está cerrado y completo, pero **no todos los valores se emiten hoy** —
ver [cobertura](#cobertura).

## Precisión: la plata es entera, las cuotas son strings

Esto es lo que más fácil rompe a un integrador.

| Dato | Representación |
| - | - |
| `invested`, `gain`, `maturity_amount`, `balance.*` | **integer en minor units**, igual que todo el resto de la API (CLP sin decimales, USD en centavos). |
| `units`, `unit_price`, `gain_pct`, `rate` | **string decimal**. No son plata: tienen más decimales que la moneda. |

```js theme={null}
// ❌ pierdes precisión en silencio: el total deja de cuadrar con la cartola
const total = Number(inv.units) * Number(inv.unit_price);

// ✅ aritmética decimal (Decimal.js, big.js, lo que uses) o guárdalos como string
const total = new Decimal(inv.units).times(inv.unit_price);
```

Formato canónico de los strings decimales: `^-?\d+(\.\d+)?$` — punto como
separador decimal, sin separador de miles, sin notación exponencial, y **con
los dígitos que informó el banco** (no los normalizamos ni recortamos ceros).

<Warning>
  No conviertas las cuotas ni el valor cuota a `float` para después
  multiplicarlos: el error se acumula y el total que muestres no va a cuadrar con
  lo que el titular ve en el portal del banco.
</Warning>

## Las dos fechas

El valor cuota de un fondo mutuo chileno es **T-1**: se publica por día hábil
cerrado, no en vivo. Por eso hay dos relojes y publicamos los dos.

| Campo | Qué fecha es |
| - | - |
| `investment.priced_at` | A qué **día corresponde la valorización** — la fecha del valor cuota que informa el banco. |
| `account.refreshed_at` | Cuándo **Rail fue al banco** a leer este producto. |

<Warning>
  Si muestras "ganaste \$575.182" fechado con `refreshed_at`, estás fechando mal
  la plata: puede ser el número del viernes mostrado un lunes. Para la
  valorización usa `priced_at`.
</Warning>

Cuando el banco no informa la fecha de valorización, `priced_at` viene `null`:
preferimos el hueco antes que ponerle la fecha del sync, que sería mentir.

## Balance de una cuenta de inversión

`balance` no cambia de semántica, pero sí cambia lo que significa "disponible"
según el producto:

| `kind` | `current` | `available` | `limit` |
| - | - | - | - |
| `time_deposit` | Valor de liquidación hoy (capital + interés devengado). | `0` — la plata está inmovilizada hasta el vencimiento. | `null` |
| `mutual_fund` | Valor de mercado hoy. | `= current` — es rescatable a la vista. | `null` |
| `apv` | Valor de mercado hoy. | `0` — inmovilizado. | `null` |

`limit` es **siempre `null`**: un producto de captación no tiene cupo.

<Note>
  `available: 0` significa "hoy no puede retirar esta plata", y es distinto de
  `null`, que significaría "el banco no lo informa".
</Note>

## Una cuenta de inversión no tiene movimientos

`GET /v1/accounts/{id}/movements` sobre una cuenta `investment` devuelve una
**lista vacía**, en live y en sandbox. No es un bug: un DAP, un fondo mutuo o un
APV no tienen cartola — el sync baja cartola para las cuentas vista/corriente y
estado de cuenta para las tarjetas.

Para detectar que algo cambió, compara `investment.priced_at` y el delta de
`balance.current` entre syncs.

<Note>
  Los aportes y rescates (la "cartola" del producto de inversión) **no están
  disponibles** por la API. Si los agregamos van a ser un recurso aparte, no estos
  movimientos.
</Note>

## Qué campos trae cada producto

Lo que el banco informa no es lo mismo para un depósito que para un fondo. Hoy,
con Banco de Chile:

| Campo | Depósito a plazo | Fondo mutuo |
| - | - | - |
| `invested` | capital inicial | costo de adquisición |
| `gain` | interés devengado (lo derivamos: liquidación − capital) | lo informa el banco |
| `gain_pct` | `null` | ✅ |
| `units` / `unit_price` | `null` — un depósito no tiene cuotas | ✅ |
| `priced_at` | `null` — el banco no informa a qué día valorizó | ✅ fecha del valor cuota (T-1) |
| `instrument_id` | nº de documento del depósito | nemotécnico de la serie |
| `maturity_date` / `maturity_amount` | ✅ | `null` |
| `rate` / `rate_basis` | ✅ `rate_basis: "period"` | `null` |
| `term_days` | ✅ best-effort; `null` si el banco no lo expone | `null` |

### Dos cosas que sorprenden en los depósitos a plazo

<Warning>
  **`rate_basis: "period"` quiere decir que la tasa aplica sobre el plazo del
  depósito**, no al mes ni al año. Es lo que informa Banco de Chile: 0,28% sobre
  un producto "HASTA 30 DS" es el rendimiento del período completo. No la
  anualices asumiendo que es mensual.
</Warning>

<Warning>
  **`maturity_date` puede estar en el pasado con el depósito vigente.** Un
  Depósito Renovable Indefinido (DPRI) se renueva solo y el banco muestra el
  vencimiento del período corriente. Si filtras los depósitos por fecha de
  vencimiento vas a borrar productos vivos.
</Warning>

`term_days` sale best-effort del nombre del producto cuando el banco no lo
entrega como número (`"DPRI HASTA 30 DS"` → `30`). Si no se puede leer viene
`null`, nunca un número inventado.

## Cobertura

| Institución | Productos con bloque `investment` |
| - | - |
| Banco de Chile (`banco_chile`) | Fondos mutuos y depósitos a plazo. |

Hoy **ninguna otra institución** emite el bloque. De los `kind`, en live sólo
verás `time_deposit` y `mutual_fund`: el `apv` está implementado en el adapter
de Banco de Chile pero **todavía sin validar** contra un producto real, y
`stock` y `cash` no los emite ningún adapter. El sandbox sí emite un APV, para
que puedas integrarlo antes de que llegue.

<Note>
  Una cuenta puede venir con `type: "investment"` y `investment: null`: hay
  instituciones que clasifican el producto (porque su nombre dice "inversión" o
  "fondo") sin que todavía podamos leer su detalle. Trata `investment: null` como
  "no tenemos el detalle", no como "no es una inversión".
</Note>

## Probarlo sin un banco real

En [test mode](/test-mode) el RUT mágico `11.111.111-1` devuelve un DAP, un
fondo mutuo y un APV con la **misma forma que live**, y con `priced_at` de ayer
a propósito para que veas la diferencia con `refreshed_at`. El RUT de empresa
`22.222.222-2` trae un DAP.

Ojo con un detalle: el depósito a plazo del sandbox sí informa `gain_pct` y
`priced_at`, mientras que en live Banco de Chile los deja en `null`. Trata todo
campo como nullable y no te apoyes en que venga poblado.

<CardGroup cols={2}>
  <Card title="Cuentas" icon="wallet" href="/concepts/accounts">
    El recurso `account` completo y sus tipos.
  </Card>

  <Card title="Test mode" icon="flask" href="/test-mode">
    Los productos de inversión del sandbox.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.