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
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
Nos bajamos dichos tickers de Yahoo Finance
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
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)
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):
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.
Veamos los pesos
Ahora, imaginemos que queremos meter 10k, dejemos que nos diga exactamente cómo
Nos sobran 8 eurillos que dejaremos para que DeGiro se cobre las comisiones de acceso a Xetra.
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)
- Lyxor Commodities Refinitiv/CoreCommodity CRB EX-Energy TR UCITS ETF (CRBN.MI)
- VanEck Rare Earth and Strategic Metals UCITS ETF (REMX.L)
- WisdomTree Agriculture (AIGA.L)
- Rize Sustainable Future of Food UCITS ETF (FOOD.L)
- 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)
- 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
- 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.