cafci-fondos-comunes-argentina

Installation
SKILL.md

CAFCI Fondos Comunes Argentina

Skill para consultar información pública de fondos comunes de inversión en Argentina combinando tres fuentes oficiales de CAFCI. La API REST anterior (api.pub.cafci.org.ar/tipo-renta, /fondo/..., /estadisticas/...) fue discontinuada en 2026-04 (HTTP 403 desde CloudFront edge). Estas tres fuentes la reemplazan.

Fuentes de datos

# Endpoint Para qué sirve Formato Tamaño
1 https://estadisticas.cafci.org.ar/consulta-de-fondos.json Catálogo: fondos + clases + IDs + honorarios/fees + metadata JSON ~2.7 MB
2 https://api.pub.cafci.org.ar/pb_get Snapshot diario: vcp, patrimonio, market share, variaciones XLSX ~900 KB
3 https://defuddle.md/https://estadisticas.cafci.org.ar/fondos/{fondoId}?clase={claseId} Ficha individual: rendimientos por período (7d/1m/90d/180d/YTD/12m) Markdown ~2 KB
4 https://estadisticas.cafci.org.ar/fondos/{fondoId}?clase={claseId} (HTML directo) Composición de cartera (top activos + porcentaje) embebida como atributo del <canvas> HTML ~23 KB

Sin auth, sin token. Sólo /pb_get requiere headers de browser para no devolver 403.

Setup inicial (una vez por día)

DATE=$(date +%F)
TMP="${TMPDIR:-/tmp}"
SKILL_DIR="${SKILL_DIR:-/Users/ripio/Documents/Github/agent-skills/skills/cafci-fondos-comunes-argentina}"

CATALOG="${TMP}/cafci-catalog-${DATE}.json"
DAILY_XLSX="${TMP}/cafci-daily-${DATE}.xlsx"
DAILY="${TMP}/cafci-daily-${DATE}.json"

# 1. Catálogo (1134 fondos, 4549 clases, fees, IDs, metadata)
[ -s "$CATALOG" ] || \
  curl -s "https://estadisticas.cafci.org.ar/consulta-de-fondos.json" -o "$CATALOG"

# 2. Snapshot diario (XLSX requiere headers)
[ -s "$DAILY_XLSX" ] || \
  curl -s "https://api.pub.cafci.org.ar/pb_get" \
    -H "origin: https://www.cafci.org.ar" \
    -H "referer: https://www.cafci.org.ar/" \
    -H "user-agent: Mozilla/5.0" \
    -o "$DAILY_XLSX"

# 3. Parsear XLSX a JSON normalizado
[ -s "$DAILY" ] || \
  python3 "${SKILL_DIR}/parse_cafci.py" "$DAILY_XLSX" > "$DAILY"

parse_cafci.py requiere openpyxl (pip3 install openpyxl).

Operaciones comunes

1) Listar tipos de renta (catálogo de categorías)

jq '.filtros.tipo_renta' "$CATALOG"
# [{"id":2,"nombre":"Renta Variable"}, {"id":3,"nombre":"Renta Fija"}, ...]

2) Buscar fondo por nombre + listar clases con fees

jq '.fondos[]
    | select(.nombre | ascii_downcase | contains("ahorro"))
    | {id, nombre,
       tipo_renta: .tipo_renta.nombre,
       sociedad_gerente: .sociedad_gerente.nombre,
       clases: [.clases[] | {id, nombre,
                             fee_gerente: .honorarios.administracion_gerente,
                             fee_depositaria: .honorarios.administracion_depositaria,
                             ingreso: .honorarios.ingreso,
                             rescate: .honorarios.rescate}]}' "$CATALOG"

3) Listar fondos activos por tipo de renta

# tipo_renta.id 4 = Mercado de Dinero (ver paso 1 para el mapeo)
jq '.fondos[]
    | select(.tipo_renta.id == 4 and .estado == 1)
    | {id, nombre, sociedad_gerente: .sociedad_gerente.nombre, codigo_cnv}' "$CATALOG"

4) Top N por patrimonio en una categoría (usa DAILY)

# OJO: las categorías de DAILY combinan tipo_renta + moneda + región como string.
jq '[.fondos[] | select(.categoria == "Mercado de Dinero Peso Argentina")]
    | sort_by(.patrimonio) | reverse | .[0:10]
    | .[] | {nombre, patrimonio, market_share, variacion_dia_pct, variacion_mes_pct}' "$DAILY"

5) Ficha completa de un fondo+clase (combina las 3 fuentes)

FONDO_ID=1717
CLASE_ID=5772

# (a) Metadata + fees desde CATALOG
jq --argjson fid $FONDO_ID --argjson cid $CLASE_ID '
  .fondos[] | select(.id == $fid)
  | {id, nombre, codigo_cnv, objetivo, inicio, dias_liquidacion,
     sociedad_gerente: .sociedad_gerente.nombre,
     sociedad_depositaria: .sociedad_depositaria.nombre,
     tipo_renta: .tipo_renta.nombre, region: .region.nombre,
     horizonte: .horizonte.nombre, duration: .duration.nombre,
     clase: (.clases[] | select(.id == $cid)
             | {id, nombre, inversion_minima, honorarios,
                ticker_bloomberg, ticker_isin})}' "$CATALOG"

# (b) Rendimientos por período + valor cuotaparte (defuddle markdown)
curl -s "https://defuddle.md/https://estadisticas.cafci.org.ar/fondos/${FONDO_ID}?clase=${CLASE_ID}"

# (c) Snapshot diario por nombre exacto de la clase
CLASE_NOMBRE=$(jq -r --argjson fid $FONDO_ID --argjson cid $CLASE_ID \
  '.fondos[] | select(.id == $fid) | .clases[] | select(.id == $cid) | .nombre' "$CATALOG")
jq --arg n "$CLASE_NOMBRE" '.fondos[] | select(.nombre == $n)' "$DAILY"

6) Composición de cartera de un fondo+clase

La composición está embebida en el HTML como atributo data-pie-chart-items-value del <canvas>. defuddle no la captura.

FONDO_ID=1717
CLASE_ID=5772

curl -s "https://estadisticas.cafci.org.ar/fondos/${FONDO_ID}?clase=${CLASE_ID}" | python3 -c "
import sys, re, html, json
h = sys.stdin.read()
m_date = re.search(r'class=\"valores\">Valores al ([^<]+)<', h)
m = re.search(r'data-pie-chart-items-value=\"([^\"]+)\"', h)
print(json.dumps({
    'fecha_cartera': m_date.group(1) if m_date else None,
    'composicion': json.loads(html.unescape(m.group(1))) if m else []
}, ensure_ascii=False, indent=2))
"

Output:

{
  "fecha_cartera": "10/04/2026",
  "composicion": [
    {"nombre": "Bonos Rep Argentina 2030", "porcentaje": 11.5},
    {"nombre": "FCI IAM Liquidez en Dólares - Clase B", "porcentaje": 10.0},
    ...
    {"nombre": "Resto de Activos", "porcentaje": 29.2}
  ]
}

7) Resolver fondoId/claseId desde un nombre

jq -r '.fondos[]
       | select(.nombre | ascii_downcase | contains("ieb estrategico"))
       | "\(.id)\t\(.nombre)\t" + ([.clases[] | "\(.id)=\(.nombre)"] | join(" | "))' "$CATALOG"

Workflows recomendados

A) Top N por patrimonio en categoría con fees

  1. Top N desde $DAILY filtrando categoria (XLSX usa "Mercado de Dinero Peso Argentina", "Renta Fija Peso Argentina", etc).
  2. Para cada nombre resultante, buscar la clase en $CATALOG por clases[].nombre exacto.
  3. Joinear patrimonio + variaciones (DAILY) con fees + sociedad gerente (CATALOG).

B) Ficha "todo en uno"

  1. (fondoId, claseId) desde catálogo (paso 6).
  2. Tres queries en paralelo: CATALOG (metadata + fees), DAILY (patrimonio actual + variaciones), defuddle (rendimientos por período).
  3. Resumen accionable: nombre, sociedad gerente/depositaria, tipo de renta, fees, patrimonio, rendimientos.

C) Si el usuario no especifica clase

  • Mostrar las clases disponibles en $CATALOG y pedir cuál.
  • Si hay una sola, continuar automáticamente.

Mapeo de campos

CATALOG (/consulta-de-fondos.json)

.generated_at        — timestamp
.total_fondos        — 1134
.total_clases        — 4549
.filtros             — catálogos: tipo_renta, region, horizonte, duration,
                       benchmark, moneda, sociedad_gerente, tipo_dinero,
                       tipo_renta_mixta (todos arrays de {id, nombre})
.fondos[]            — array de fondos
  .id, .nombre, .codigo_cnv, .estado (1=activo), .objetivo, .tipo_dinero,
  .valuacion, .dias_liquidacion, .inicio (YYYY-MM-DD), .mm_puro, .mm_indice,
  .sociedad_gerente {id, nombre}, .sociedad_depositaria {id, nombre},
  .moneda {id, nombre}, .tipo_renta {id, nombre}, .tipo_renta_mixta,
  .region {id, nombre}, .duration {id, nombre},
  .benchmark {id, nombre}, .horizonte {id, nombre}
  .clases[]
    .id, .nombre, .moneda {id, nombre}, .inversion_minima,
    .suscripcion, .liquidez, .rg384, .log_abierto,
    .ticker_bloomberg, .ticker_isin
    .honorarios.{ingreso, rescate, transferencia,
                 administracion_gerente, administracion_depositaria,
                 gasto_ordinario_gestion}     — strings, % anual

DAILY (XLSX → JSON via parse_cafci.py)

.fecha_reporte       — YYYY-MM-DD del último día hábil publicado
.categorias[]        — 27 strings: "Renta Variable Peso Argentina", etc
                       (combina tipo_renta + moneda + región)
.fondos[]            — 4052 clases con datos diarios
  .nombre            — nombre completo de la clase (ej "Galileo Acciones - Clase A")
  .categoria         — categoría XLSX
  .moneda            — ARS / USD / USB
  .region            — Arg / Lat / Glo
  .horizonte         — Cor / Med / Largo / Flex
  .fecha             — fecha del valor cuotaparte
  .vcp_actual        — valor cuotaparte hoy
  .vcp_anterior      — valor cuotaparte día hábil anterior
  .variacion_dia_pct — % vs día anterior
  .vcp_reexp_pesos   — vcp re-expresado en pesos (= vcp_actual para ARS)
  .variacion_mes_pct — % vs último día del mes anterior
  .variacion_ytd_pct — % vs cierre del año anterior
  .variacion_12m_pct — % vs hace 12 meses
  .cantidad_cuotapartes
  .patrimonio        — patrimonio neto en moneda del fondo
  .market_share      — % del total
  .depositaria       — nombre de la sociedad depositaria
  .codigo_cnv        — código CNV

defuddle.md (ficha individual)

Markdown con tablas:

  • Rendimiento histórico: Valor Cuotaparte + 7 días, 1 mes, 90 días, 180 días, En el año, 12 meses.
  • Valores al [fecha]: patrimonio bajo administración, valor cuotaparte.
  • Composición de Cartera: defuddle muestra solo la fecha. La composición real (top activos + porcentaje) está en el HTML directo como atributo data-pie-chart-items-value del <canvas> — ver operación 6.
  • Honorarios y Comisiones: gerente, depositaria, ingreso, egreso, transferencia, gastos ordinarios, comisión de éxito.
  • Datos del Fondo: Administradora, Depositaria, Tipo de Renta, Tipo de DD, Región, Benchmark, Horizonte, Duration, Moneda, Código CNV.
  • Inversión mínima (con moneda).
  • Plazo de Liquidación.

Limitaciones conocidas

  • Series temporales arbitrarias: el snapshot diario sólo expone día actual + ayer + mes anterior + fin de año + año pasado. No hay endpoint público de histórico libre.
  • Composición de cartera detallada: el HTML expone los ~14 activos principales agrupando el resto como "Resto de Activos" (~30%). No se publica el desglose completo.
  • API REST anterior (api.pub.cafci.org.ar/tipo-renta, /fondo/..., /estadisticas/informacion/diaria/...): caída desde 2026-04 (HTTP 403 deliberado en CloudFront). No usar ni intentar reactivar.
  • CATALOG vs DAILY: CATALOG cuenta fondos únicos (1134), DAILY cuenta clases (4052). Para joinear, usar clases[].nombre (CATALOG) ↔ fondos[].nombre (DAILY) — son strings exactos.
  • Tipo de renta: en CATALOG es solo "Renta Variable"; en DAILY es "Renta Variable Peso Argentina" (con moneda+región). No son intercambiables como filtro.

Manejo de errores

  • 403 "Route not allowed" en api.pub.cafci.org.ar: faltan los headers origin, referer, user-agent.
  • total_fondos == 0 en CATALOG: probablemente bajó la API; reintentar.
  • Fondo en CATALOG sin match en DAILY: revisar .estado (debe ser 1) y nombre exacto de la clase.
  • defuddle.md timeout: reintentar 1 vez con espera breve.

Presentación de resultados

  • Empezar con un resumen corto y accionable.
  • Para listados/top N: incluir patrimonio, market share, variación día y fees clave (gerente).
  • Para ficha: incluir nombre, sociedad gerente/depositaria, tipo de renta, fees completos, patrimonio actual, rendimiento del último mes y año.
  • Si los datos del día aún no se publicaron (fin de semana / feriado): aclarar fecha del último reporte.
Related skills
Installs
40
GitHub Stars
35
First Seen
Feb 18, 2026