'Sup, QuantSX.
BLOT (Bottom Line On Top) : ¿Existe un algoritmo limpio y agradable para calcular rápidamente la deriva del peso de la cartera? En R, Python o MATLAB - no me importa cuál.
Detalles
Estoy en las etapas finales de la ampliación de una prueba de estrés script (en MATLAB) que he utilizado durante un tiempo, y he golpeado una pared de rendimiento en lo que debería ser una función de actualización bastante fácil.
Las tripas del script es un Monte Carlo de tamaño medio - 1000 iteraciones de 240 x n[i]
para ~70 carteras, donde n[i]
es el número de componentes de la cartera ith ( n[i]
oscila entre ~5 y ~25). Cópula gaussiana, por lo que genera los pseudodatos superrápidos.
En el pasado, el resultado sólo se ha presentado bajo el supuesto de un equilibrio mensual, por lo que las pseudo-observaciones para la cartera en sí son sólo
$w\cdot R = \sum_i\bar{w}_i \times R_{i,t}$
donde $\bar{w}_i$ son los pesos de la cartera original y de la cartera objetivo. Y, por supuesto, eso también es súper rápido - O (mseg). De hecho, es el módulo más rápido de todo el marco de pruebas de estrés, que se completa en ~35 segundos por cartera.
Una parte de la ampliación consiste en calcular la trayectoria de la cartera bajo una serie de supuestos de reequilibrio alternativos: full-drift (es decir, sin reequilibrio, comprar y mantener); trimestral; anual; y "umbral" por clase de activos.
En las pruebas de la versión de deriva completa, está tomando 2 segundos por iteración para calcular las ponderaciones derivadas; eso parece mucho tiempo para 240 filas de datos.
Y 70 lotes de 1000 × ~2 segundos es mucho tiempo.
La ecuación de transición del peso es realmente sencilla -
$w_{i,t+1} = w_{i,t}\cdot\left(\frac{1+R_{i,t}}{1+R_{p,t}}\right)$
$w_{i,0} = \bar{w}_i$
La ponderación derivada depende de la rentabilidad de la cartera, que es la suma ponderada de las rentabilidades de los componentes en el periodo t -
$R_{p,t} = \sum_i(w_{i,t}\cdot R_{i,t}) = w'̌\cdot R$
Hasta ahora, no hay cirugía de cohetes.
Para hacer esto en MATLAB, la única forma que se me ocurrió fue inicializar un agregador, y acumular las ponderaciones recalculadas (y como subproducto, los rendimientos de la cartera "desviados").
Así que mi función se presenta a continuación (aunque mi función está correctamente sangrada)
function[outPVals, outWts, wtDiff] = DriftWts(inTable, inWts, outName)
%% Calculates Portfolio Value and Component Weights over time if weights drift.
xT = inTable;
xP = inTable;
xT.Properties.DimensionNames{2} = 'Weights';
xT.Weights = zeros(size(xT)); %% pre-allocate weights table
xD = xT; %% pre-allocate weight-delta table
tBeg = min(xT.MthYr);
xT(tBeg,:).Weights = transpose(inWts); %% initialises weights at current AA
xR = inTable(tBeg,:).Returns/100;
xP.(outName)(tBeg,:) = 100*(xR * inWts);
xT = sortrows(xT,'MthYr','ascend');
for mmm = 2:length(xT.MthYr)
m = xT.MthYr(mmm);
m_l = eomdate(m - calmonths(1));
w_l = xT(m_l,:).Weights;
R_l = inTable(m_l,:).Returns/100;
xR = inTable(m,:).Returns/100;
Rp_l = w_l * transpose(R_l);
w_0 = w_l .* (1 + R_l)/( 1 + Rp_l);
xT(m,:).Weights = w_0;
xD(m,:).Weights = w_0 - w_l;
xP.(outName)(m,:) = 100*(w_0 * transpose(xR));
end
outWts = xT;
outPVals = xP(:,outName);
wtDiff = xD;
end
Consideré una alternativa, es decir, generar cumprod(1 +
$R_{i,t}$ /100)
de los rendimientos de los componentes, ponderándolos por $\bar{w}_i$ en t=0, sumándolos, y luego dividiendo todo por la suma de filas.
No me parece obvio que esta alternativa sea más rápida (pero no tengo ni idea de lo que pasa bajo el capó en cumprod()
Así que lo intentaré hoy)