Burbuja.quant: Portfolio de ETFs eficientes con Python Portfolio Optimization, caso práctico

ciberobrero

Madmaxista
Desde
15 Abr 2017
Mensajes
27.507
Reputación
70.436
Buenas,

Pacientemente me hallo cargando balas, eligiendo a qué disparar y cómo gastarlas

Mis supuestos son: crash deflacionario a medio plazo (seguramente haya una subida antes), recesión brutal y vuelta de la impresora para 2023, que creará un mercado alcista secular en commodities. El euro estará mucho más bajo para cuando haga las inversiones así que en principio necesito fondos y activos valorados en euros para la vuelta al riesgo se revaloricen posiblemente más que los denominados en dólares.

Otras restricciones son: entrar una vez y hold, quiero mínima complicación fiscal. Mejor un sólo mercado.

Tengo echado el ojo a estos (están todos en Xetra)

Energy
  • Amundi ETF MSCI Europe Energy UCITS ETF (ANRJ.PA)
  • SPDR MSCI Europe Energy UCITS ETF (STNX.SW)
Commodities
  • Lyxor Commodities Refinitiv/CoreCommodity CRB EX-Energy TR UCITS ETF (CRBN.MI)
  • VanEck Rare Earth and Strategic Metals UCITS ETF (REMX.L)
Agriculture
  • WisdomTree Agriculture (AIGA.L)
  • Rize Sustainable Future of Food UCITS ETF (FOOD.L)
Tech
  • Lyxor MSCI Robotics & AI ESG Filtered UCITS ETF (ROAI.SW)
  • Global X Robotics and Artificial Intelligence UCITS ETF (XB0T.F)
  • WisdomTree Artificial Intelligence UCITS ETF (WTI2.DE)
Water
  • L&G Clean Water Ucits Etf (RENW.F)
  • iShares Global Water UCITS ETF USD (Dist) (IQQQ.F)
  • Lyxor World Water (DR) UCITS ETF Dist (WAT.PA)
  • iShares Global Water UCITS ETF (IH2O.L) - Dividend Distribution
SRIs
  • Amundi MSCI Europe SRI UCITS ETF DR (EUSRI.PA)


Como me da igual en un principio la asignación de fondos, he estado jugueteando con una lib de Python muy cuca llamada PyPortOpt

Os pongo aquí el Jupyther Notebook con anotaciones

Primero importamos a memoria lo necesario

Python:
from pypfopt.expected_returns import mean_historical_return, returns_from_prices
from pypfopt.risk_models import risk_matrix, fix_nonpositive_semidefinite
from pypfopt.objective_functions import L2_reg
from pypfopt.efficient_frontier import EfficientFrontier
from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices

import yfinance as yf


Nos bajamos dichos tickers de Yahoo Finance

Python:
etfs = yf.download(tickers="ANRJ.PA STNX.SW CRBN.MI REMX.L AIGA.L FOOD.L ROAI.SW XB0T.F WTI2.DE RENW.F IQQQ.F WAT.PA EUSRI.PA", auto_adjust=True, period="2y")

He visto 4RTR.SG que es apalancado x2 en agricultura, pero Yahoo no tiene datos, así que uso AIGA.L como proxy y le multiplico x2. El copy es porque prices etfs['Close'] es una vista inmutable sobre etfs, por eso necesitamos copiarla en otra parte de la memoria para poder modificar la columa

Python:
# AIGA.L proxy for  4RTR.SG
prices = etfs['Close'].copy()
prices[['AIGA.L']] = 2*prices[['AIGA.L']]

No entraré en detalles acerca de la optimización de portfolio (optimización estadística, generalmente optimización convexa), pero como no tengo ni fruta idea de cómo van a subir, pues reseteo los retornos a todos más o menos lo mismo. Si alguien tiene una idea de cómo mejorar este paso lo agradecería. El tema aquí está en la matriz de varianzas-covarianzas. Con los ETFs que tengo, resulta que 3 sorprendentemente dan correlación cercana a uno. Esto introduce multicolinealidad, la matrix tiene autovalores cero (o incluso negativos por problemas numéricos) y no es invertible. Lo que hacemos es símplemente ponerlos a cero o a un valor pequeño (en la diagonal de la factorización)

Python:
expected_returns = mean_historical_return(prices)
undefined_returns = (expected_returns + 1000)/1000

cov_matrix = fix_nonpositive_semidefinite(risk_matrix(prices))

Con esta matriz vamos a detectar ETFs correlados y asignar fondos de manera que bajaremos esa correlación (riesgo) del porfolio final


Ahora vienen cosas chulis. Tengo "cierta idea", o mejor dicho, "me gustaría" cierta asignación mínima a ciertos sectores, y dentro de ellos, quisiera asegurar una presencia mínima de ciertos ETFs. Lo hacemos así (porcentajes en tanto por 1):

Python:
sector_mapper = {
    "ANRJ.PA": "energy",
    "STNX.SW": "energy",
    "CRBN.MI": "commodities",
    "REMX.L": "commodities",
    "AIGA.L": "agriculture",
    "FOOD.L": "agriculture",
    "ROAI.SW": "tech",
    "XB0T.F" : "tech",
    "WTI2.DE" : "tech",
    "RENW.F" : "water",
    "IQQQ.F" : "water",
    "WAT.PA" : "water",
    "EUSRI.PA" : "sri"
    
}

sector_lower = {"energy" : 0.1,
                "commodities": 0,
                "agriculture":0.3,
                "tech": 0.1,
                "water": 0.2,
                "sri": 0}
sector_upper = {"energy" : 0.3,
                "commodities": 0.2,
                "agriculture": 0.4,
                "tech": 0.3,
                "water": 0.5,
                "sri": 0.1}


weight_bounds=[(0,0.2),    # AIGA.L   - agriculture
               (0,0.3),    # ANRJ.PA  - energy
               (0,0.2),    # CRBN.MI  - commodities
               (0,0.1),    # EUSRI.PA - sri
               (0.05,0.3), # FOOD.L   - agriculture
               (0,0.3),    # IQQQ.F   - water
               (0,0.1),    # REMX.L   - commodities (rare earths)
               (0,0.3),    # RENW.F   - water
               (0,0.3),    # ROAI.SW  - tech
               (0,0.3),    # STNX.SW  - energy
               (0,0.3),    # WAT.PA   - water
               (0.05,0.2), # WTI2.DE  - tech
               (0.05,0.2)  # XB0T.F   - tech
              ]


Ahora es cuando ejecutamos el solver numérico. No entraré en detalles acerca de la frontera eficiente, aunque no estamos manejando retornos esperados en realidad, sólo volatilidad. La regularización L2 sirve para intentar hacer más coeficientes no cero y que no colapse todo a un par de ETFs.

Python:
ef = EfficientFrontier(undefined_returns, cov_matrix, weight_bounds=weight_bounds)
ef.add_objective(L2_reg)  # add a secondary objective
ef.add_sector_constraints(sector_mapper, sector_lower, sector_upper)
weights = ef.min_volatility()


Veamos los pesos

Python:
OrderedDict([('AIGA.L', 0.2),
             ('ANRJ.PA', 0.0),
             ('CRBN.MI', 0.0),
             ('EUSRI.PA', 0.0),
             ('FOOD.L', 0.1),
             ('IQQQ.F', 0.1180261431377704),
             ('REMX.L', 0.1),
             ('RENW.F', 0.0),
             ('ROAI.SW', 0.0),
             ('STNX.SW', 0.1),
             ('WAT.PA', 0.0819738568622296),
             ('WTI2.DE', 0.0999999999999999),
             ('XB0T.F', 0.2)])


Ahora, imaginemos que queremos meter 10k, dejemos que nos diga exactamente cómo

Python:
allocation = DiscreteAllocation(weights, get_latest_prices(prices), total_portfolio_value=10000)
allocation.greedy_portfolio()


({'AIGA.L': 2,
  'XB0T.F': 126,
  'IQQQ.F': 21,
  'FOOD.L': 2,
  'REMX.L': 63,
  'STNX.SW': 6,
  'WTI2.DE': 19,
  'WAT.PA': 14},
 7.999001502990723)

Nos sobran 8 eurillos que dejaremos para que DeGiro se cobre las comisiones de acceso a Xetra.
 
Muchas gracias por la aportación y la automatización. Ahora voy a tener que ir a ver viviendas para comprar pero cuando pasen estas semanas voy a ver si me hago mi propia version.

Gracias por compartir tu curro desinteresadamente.
 
Evidentemente
Pero ese no es el tema en absoluto
Exactamente. El tema es que nos comentes estas premisas y tu razonamiento para saber si sirve de algo o no todo lo que has hecho despues.....:

Mis supuestos son: crash deflacionario a medio plazo (seguramente haya una subida antes), recesión brutal y vuelta de la impresora para 2023, que creará un mercado alcista secular en commodities. El euro estará mucho más bajo para cuando haga las inversiones así que en principio necesito fondos y activos valorados en euros para la vuelta al riesgo se revaloricen posiblemente más que los denominados en dólares.

Otras restricciones son: entrar una vez y hold, quiero mínima complicación fiscal. Mejor un sólo mercado.
 
Exactamente. El tema es que nos comentes estas premisas y tu razonamiento para saber si sirve de algo o no todo lo que has hecho despues.....:

Voy a comprar una cartera de ETFs

Tengo 2 posibilidades:

- O lo hago al tun tun

- O cago una asignación estadistica de minima volatilidad, que apenas me lleva esfuerzo

De entrada me cuesta menos la opción 2 que pensar cómo repartir los fondos

Mis premisas son un poco independientes para el código y siéntete libre de discrepar, por ejemplo, si habrá crash o no.

Básicamente como estaré en hold a largo plazo, de ahí que esté interesado en mínima volatilidad (aka riesgo).
 
Voy a comprar una cartera de ETFs

Tengo 2 posibilidades:

- O lo hago al tun tun
- O cago una asignación estadistica de minima volatilidad, que apenas me lleva esfuerzo

De entrada me cuesta menos la opción 2 que pensar cómo repartir los fondos

Mis premisas son un poco independientes para el código y siéntete libre de discrepar, por ejemplo, si habrá crash o no.

Básicamente como estaré en hold a largo plazo, de ahí que esté interesado en mínima volatilidad (aka riesgo).
Me parece bien. Pero dudo mucho que en un crash se mantengan ciertas premisas como que las correlaciones pasadas van a mantenerse en el futuro:

"Con esta matriz vamos a detectar ETFs correlados y asignar fondos de manera que bajaremos esa correlación (riesgo) del porfolio final"

Cierto es que otros eligen los fondos por el nombre (Si les gusta o no) y otros metodos y tu tienes el tuyo. Para un entorno estable tu metodo me parece acertado.... para el escenario que estimas lo dudo (pero nadie te puede demostrar que no es el mejor).
 
como huevones se me pasó este hilazo!?????

Goooooder.

conoces Streamlit @ciberobrero ? si lo enchufas a esto que has hecho te queda una web app super guapa :)

arrina hilo leche!

luedes explicar la parte de Discrete Allocation? En que base están esos indices?
saludos
 
Última edición:
como huevones se me pasó este hilazo!?????

Goooooder.

conoces Streamlit @ciberobrero ? si lo enchufas a esto que has hecho te queda una web app super guapa :)

arrina hilo leche!

luedes explicar la parte de Discrete Allocation? En que base están esos indices?
saludos


No estoy trabajando con esto ahora mismo

Es basicamente Markowitz de toda la vida.

Planteas un problema de asignar pesos a cada activo, el problema es cuadratico, por que? porque x^2 se optimiza facilmente (en version matricial), y la variabilidad se modela bien con las covarianzas, luego el problema cuadratico es

max p'*w - k w'*C*w

Donde p es el vector de precios estimados futuros para tus activos, C es la matriz de covarianzas, k es una constante arbitraria para ponderar la volatilidad y w es el vector de pesos a encontrar. El anterior problema cuadratico quiere decir: encuentra unos pesos de activos tal que maximicen el rendimiento pero minimizando la volatilidad (restando)

w saldra fraccionario, pero en la vida real necesitamos comprar unidades de acciones, de ahi el discrete allocation, te saca la asignacion discreta, de acuerdo al valor por unidad de accion, mas cercano al portfolio calculado.

Es facil ver que
1. No conocemos p
2. C cambia con el tiempo
 
Y no has hecho nada de backtesting?
Ni has mirado el maximum drawdown para saber cuanto te aleja de la caída máxima cada componente?
Incluso el grado de overlapping de cada ETF, seguramente hay holdings repetidos, nse...p.ej estoy seguro que los ETFs tematicos 'Tech' los 10 primeros holdings son idénticos en todos los ETFs que llevas, vamos, segurísimo....incluso con pesos similares roto2:

Está guapo, pero veo lagunas, pero que son fruto de mi sesgo himbersor.

Yo he hecho centenares de matrices de correlación de indices, acciones, paises, continentes, etc...te entretienes.

La pvtada de usar estos metodos de optimización es que te comes la cabeza y te alejas de la filosofia boglehead que es la que funciona de verdad, comprar....y no hacer nada roto2:
 
@ciberobrero
¿Qué tal te salió el experimento?

Y no has hecho nada de backtesting?
Ni has mirado el maximum drawdown para saber cuanto te aleja de la caída máxima cada componente?
Incluso el grado de overlapping de cada ETF, seguramente hay holdings repetidos, nse...p.ej estoy seguro que los ETFs tematicos 'Tech' los 10 primeros holdings son idénticos en todos los ETFs que llevas, vamos, segurísimo....incluso con pesos similares roto2:

Está guapo, pero veo lagunas, pero que son fruto de mi sesgo himbersor.

Yo he hecho centenares de matrices de correlación de indices, acciones, paises, continentes, etc...te entretienes.

La pvtada de usar estos metodos de optimización es que te comes la cabeza y te alejas de la filosofia boglehead que es la que funciona de verdad, comprar....y no hacer nada roto2:


No seguí con esto, conocí a uno que usaba métodos más avanzados basados en información mutua y que incorporaba fuentes que yo no tenía y me deprimí.

Pasé a inversión discrecional y me dediqué a estudiar macro/mercado de bonos etc.

Si no puedes hacer rightcurving, tendrás que hacer leftcurving.

@Ds_84 "seguramente hay holdings repetidos, nse...p.ej estoy seguro que los ETFs tematicos 'Tech' los 10 primeros holdings son idénticos en todos los ETFs que llevas," eso se controla con la matriz de varianzas y covarianzas.
 
No seguí con esto, conocí a uno que usaba métodos más avanzados basados en información mutua y que incorporaba fuentes que yo no tenía y me deprimí.

Pasé a inversión discrecional y me dediqué a estudiar macro/mercado de bonos etc.

Si no puedes hacer rightcurving, tendrás que hacer leftcurving.

@Ds_84 "seguramente hay holdings repetidos, nse...p.ej estoy seguro que los ETFs tematicos 'Tech' los 10 primeros holdings son idénticos en todos los ETFs que llevas," eso se controla con la matriz de varianzas y covarianzas.
Que has aprendido del macro/mercado de bonos? Dinos algunos consejos
 
Que has aprendido del macro/mercado de bonos? Dinos algunos consejos



 
Última edición:
Volver