0Pricing
AI Agents · Lección

Herramientas de agentes para analizar carteras

Cálculo de rentabilidades, ratio de Sharpe y análisis de drawdown mediante código ejecutado por el agente.

Herramientas de agentes para analizar carteras es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.

Análisis de carteras como herramientas de agente

Los agentes de análisis financiero necesitan un conjunto de herramientas de cálculo que el LLM pueda invocar: rentabilidades, métricas de riesgo, correlación y sugerencias de rebalanceo. Cada herramienta es una función de Python con una firma clara y devuelve JSON estructurado sobre el que el agente puede razonar.

Cálculo de rentabilidades porcentuales

Es la base del análisis de carteras. pct_change() calcula las rentabilidades de un día respecto al anterior. A partir de ellas, calcule la rentabilidad acumulada durante el periodo de análisis.

import pandas as pd
import yfinance as yf

def calculate_returns(ticker: str, period: str = '1y') -> dict:
    hist = yf.Ticker(ticker).history(period=period)['Close']
    if hist.empty:
        return {'error': f'No data for {ticker}'}

    daily_returns = hist.pct_change().dropna()
    cumulative    = (1 + daily_returns).cumprod() - 1

    return {
        'ticker':            ticker,
        'period':            period,
        'total_return_pct':  round(float(cumulative.iloc[-1]) * 100, 2),
        'avg_daily_return':  round(float(daily_returns.mean()) * 100, 4),
        'best_day_pct':      round(float(daily_returns.max()) * 100, 2),
        'worst_day_pct':     round(float(daily_returns.min()) * 100, 2)
    }

Ratio de Sharpe

El ratio de Sharpe mide la rentabilidad ajustada al riesgo: cuánta rentabilidad excedente se obtiene por unidad de riesgo (volatilidad). Cuanto más alto, mejor. Un Sharpe superior a 1.0 se considera bueno; por encima de 2.0, excelente.

import numpy as np
import yfinance as yf

def sharpe_ratio(ticker: str, period: str = '1y',
                 risk_free_annual: float = 0.05) -> dict:
    hist = yf.Ticker(ticker).history(period=period)['Close']
    daily_returns = hist.pct_change().dropna()

    risk_free_daily = risk_free_annual / 252  # annualized to daily
    excess_returns  = daily_returns - risk_free_daily

    if daily_returns.std() == 0:
        return {'error': 'Zero volatility — cannot compute Sharpe ratio'}

    sharpe = float(excess_returns.mean() / excess_returns.std() * np.sqrt(252))
    return {
        'ticker': ticker,
        'sharpe_ratio': round(sharpe, 3),
        'interpretation': 'excellent' if sharpe > 2 else 'good' if sharpe > 1 else 'acceptable' if sharpe > 0 else 'poor'
    }

Pérdida máxima

La pérdida máxima (MDD) es el mayor descenso desde un máximo hasta un mínimo en el valor de una cartera. Representa la peor pérdida que habría experimentado un inversor si hubiera comprado en el máximo y vendido en el mínimo.

import yfinance as yf
import numpy as np

def max_drawdown(ticker: str, period: str = '1y') -> dict:
    hist = yf.Ticker(ticker).history(period=period)['Close']
    if hist.empty:
        return {'error': 'No data'}

    rolling_max = hist.cummax()
    drawdown    = (hist - rolling_max) / rolling_max
    mdd         = float(drawdown.min())
    mdd_date    = str(drawdown.idxmin().date())

    return {
        'ticker':           ticker,
        'max_drawdown_pct': round(mdd * 100, 2),
        'drawdown_date':    mdd_date,
        'interpretation':   'severe' if mdd < -0.30 else 'moderate' if mdd < -0.15 else 'mild'
    }

Matriz de correlación

Una matriz de correlación muestra cómo se mueven conjuntamente los activos. Una correlación baja o negativa entre activos implica un beneficio de diversificación: si uno cae, el otro podría no hacerlo. Esto orienta la selección de activos y las decisiones de rebalanceo.

import pandas as pd
import yfinance as yf

def correlation_matrix(tickers: list[str], period: str = '1y') -> dict:
    prices = yf.download(tickers, period=period)['Close']
    returns = prices.pct_change().dropna()
    corr = returns.corr().round(3)

    # Convert to JSON-serializable format
    result = {}
    for t1 in tickers:
        result[t1] = {}
        for t2 in tickers:
            if t1 in corr.columns and t2 in corr.index:
                result[t1][t2] = float(corr.loc[t2, t1])
    return {'correlation': result, 'period': period}

Volatilidad y beta

La volatilidad anualizada (la desviación estándar escalada a 252 días de negociación) mide el riesgo absoluto. La beta mide el riesgo relativo al mercado (normalmente el S&P 500): beta > 1 = más volátil que el mercado.

import numpy as np
import yfinance as yf

def volatility_and_beta(ticker: str, benchmark: str = 'SPY',
                        period: str = '1y') -> dict:
    data = yf.download([ticker, benchmark], period=period)['Close'].pct_change().dropna()

    if ticker not in data.columns or benchmark not in data.columns:
        return {'error': 'Insufficient data'}

    t_ret = data[ticker]
    b_ret = data[benchmark]

    vol_annualized = float(t_ret.std() * np.sqrt(252))

    cov = np.cov(t_ret, b_ret)
    beta = float(cov[0][1] / cov[1][1]) if cov[1][1] != 0 else 0.0

    return {
        'ticker':          ticker,
        'annual_vol_pct':  round(vol_annualized * 100, 2),
        'beta':            round(beta, 3),
        'market_sensitivity': 'high' if beta > 1.3 else 'market' if beta > 0.7 else 'low'
    }

Rentabilidad y riesgo a nivel de cartera

Analice una cartera completa calculando las rentabilidades ponderadas y la volatilidad. La cartera puede tener menos riesgo que cualquiera de sus posiciones individuales gracias a la diversificación.

import pandas as pd, numpy as np
import yfinance as yf

def portfolio_metrics(holdings: dict, period: str = '1y') -> dict:
    tickers  = list(holdings.keys())
    weights  = np.array(list(holdings.values()))
    weights /= weights.sum()  # normalize to 1.0

    prices  = yf.download(tickers, period=period)['Close'].pct_change().dropna()
    port_ret = (prices * weights).sum(axis=1)
    cov_matrix = prices.cov() * 252  # annualized

    port_return  = float(port_ret.mean() * 252)
    port_vol     = float(np.sqrt(weights @ cov_matrix.values @ weights))
    port_sharpe  = port_return / port_vol if port_vol > 0 else 0

    return {
        'annual_return_pct':  round(port_return * 100, 2),
        'annual_vol_pct':     round(port_vol * 100, 2),
        'sharpe_ratio':       round(port_sharpe, 3),
        'holdings':           {t: round(float(w), 3) for t, w in zip(tickers, weights)}
    }

Sugerencia de rebalanceo

La desviación de la cartera se produce cuando los distintos activos crecen a ritmos diferentes. La herramienta de rebalanceo calcula las ponderaciones actuales y las objetivo, y sugiere operaciones para restablecer la asignación objetivo.

def rebalancing_suggestion(current_values: dict, target_weights: dict) -> dict:
    total_value = sum(current_values.values())
    current_weights = {t: v / total_value for t, v in current_values.items()}
    trades = {}

    for ticker in target_weights:
        current_w = current_weights.get(ticker, 0)
        target_w  = target_weights[ticker]
        diff_w    = target_w - current_w
        diff_usd  = diff_w * total_value

        if abs(diff_w) > 0.01:  # only suggest if drift > 1%
            trades[ticker] = {
                'action':      'BUY' if diff_usd > 0 else 'SELL',
                'amount_usd':  round(abs(diff_usd), 2),
                'from_weight': round(current_w, 4),
                'to_weight':   round(target_w, 4)
            }
    return {'total_portfolio': round(total_value, 2), 'trades': trades}

if __name__ == '__main__':
    current = {'AAPL': 6000, 'BND': 2000, 'VTI': 2000}
    target = {'AAPL': 0.4, 'BND': 0.3, 'VTI': 0.3}
    result = rebalancing_suggestion(current, target)
    print('Total portfolio: $%s' % result['total_portfolio'])
    for ticker, trade in result['trades'].items():
        print(f"  {ticker}: {trade['action']} ${trade['amount_usd']} "
              f"(weight {trade['from_weight']} -> {trade['to_weight']})")

Registro de herramientas de análisis para el LLM

Registre cada función de análisis como herramienta del agente. El LLM las invoca en secuencia: primero recupera los datos, después calcula las métricas y, por último, genera la sugerencia de rebalanceo.

FINANCIAL_TOOLS = [
    {
        'type': 'function',
        'function': {
            'name': 'calculate_returns',
            'description': 'Calculate total and daily returns for a ticker.',
            'parameters': {'type': 'object',
                           'properties': {
                               'ticker': {'type': 'string'},
                               'period': {'type': 'string', 'default': '1y'}
                           }, 'required': ['ticker']}
        }
    },
    {
        'type': 'function',
        'function': {
            'name': 'sharpe_ratio',
            'description': 'Compute the annualized Sharpe ratio.',
            'parameters': {'type': 'object',
                           'properties': {
                               'ticker': {'type': 'string'},
                               'period': {'type': 'string', 'default': '1y'}
                           }, 'required': ['ticker']}
        }
    }
]

if __name__ == '__main__':
    print('Registered financial tools for the LLM:')
    for tool in FINANCIAL_TOOLS:
        fn = tool['function']
        print(f"  {fn['name']}: {fn['description']}")

Generación de un resumen de cartera en lenguaje sencillo

Después de calcular las métricas, haga que el LLM traduzca los números a un lenguaje práctico. Las métricas son datos de entrada; la salida final debe ser comprensible para un inversor que no sea especialista en análisis cuantitativo.

import openai, json

client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')

def portfolio_narrative(metrics: dict, holdings: dict) -> str:
    prompt = (
        f'Portfolio metrics:\n{json.dumps(metrics, indent=2)}\n\n'
        f'Holdings: {json.dumps(holdings)}\n\n'
        f'Write a 3-sentence plain-language portfolio assessment for a retail investor.\n'
        f'Include: overall performance, main risk, and one recommendation.'
    )
    resp = client.chat.completions.create(
        model='gpt-4o',
        messages=[{'role': 'user', 'content': prompt}]
    )
    return resp.choices[0].message.content

Almacenamiento en caché de métricas calculadas

Las métricas calculadas a partir de los mismos datos subyacentes serán idénticas. Almacene las métricas calculadas en caché junto con los datos sin procesar para evitar recalcular los ratios de Sharpe y las pérdidas máximas en cada turno del agente.

import time, hashlib, json

metrics_cache = {}

def cached_metric(func, *args, ttl: int = 3600):
    key = hashlib.md5(f'{func.__name__}:{args}'.encode()).hexdigest()
    entry = metrics_cache.get(key)
    if entry and time.time() - entry['ts'] < ttl:
        return entry['data']
    result = func(*args)
    metrics_cache[key] = {'data': result, 'ts': time.time()}
    return result

# Usage:
# sharpe = cached_metric(sharpe_ratio, 'AAPL', '1y')

if __name__ == '__main__':
    def slow_average(*nums):
        return sum(nums) / len(nums)

    r1 = cached_metric(slow_average, 1, 2, 3)
    r2 = cached_metric(slow_average, 1, 2, 3)
    print('First call result:', r1)
    print('Second call result (served from cache):', r2)
    print('Cache entries:', len(metrics_cache))

¿Qué indica un ratio de Sharpe superior a 2.0?

El ratio de Sharpe es una métrica estándar para comparar el rendimiento ajustado al riesgo entre carteras y activos individuales.

Resumen de las herramientas de análisis de carteras

El conjunto de herramientas de un agente financiero incluye: pct_change() para las rentabilidades, ratio de Sharpe para el rendimiento ajustado al riesgo, pérdida máxima para la peor pérdida, matriz de correlación para la diversificación, beta para la sensibilidad al mercado y sugerencias de rebalanceo para el mantenimiento de la cartera.

Registre cada una como herramienta del agente y almacene en caché los resultados calculados para mejorar la eficiencia.

Preguntas frecuentes

¿La lección «Herramientas de agentes para analizar carteras» es gratis?

Sí — el texto completo de «Herramientas de agentes para analizar carteras» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.

¿Qué aprenderé en «Herramientas de agentes para analizar carteras»?

Cálculo de rentabilidades, ratio de Sharpe y análisis de drawdown mediante código ejecutado por el agente. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Agents?

No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Herramientas de agentes para analizar carteras»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Agents?

Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Integración de API de datos de mercado
  2. Herramientas de agentes para analizar carteras
  3. Controles de riesgo y cumplimiento normativo
  4. Backtesting de decisiones de agentes
← Volver a AI Agents