El Alpha Trading es la forma de modelar Alpha Factors en escenarios de exposicion a la rentabilidad.
Alpha Factor en Python
Con el amuento de la accesibilidad a la tecnologia disponible hacia todo tipo de inversores,el Alpha Factor han ido evolucionado diferentes estilos de inversion. Las como hablamos ( LINK ESTILOS ). que vamos a estudiar, es relativamente nuevo fuera del ambito no profesional.
Siempre se han utilizado pinceladas de los Alpha Factors en muchos otros estilos o metodologias, pero recientemente el numero de captital funcionando con este estilo de estrategias, y el numero de publicaciones y descubrimientos esta en aumento.
Que es un Alpha Factor
Es un concepto relativamente nuevo, donde las explicaciones no son acordes a una realidad dinamica, donde esta nueva modalidad de inversion se esta estableciendo.Ademas el mundo de los criptoactivos ha traido a individuos muy inteligente a investigar sobre las finanzas, generando nuevos modelos de backtesting, con criterios distintos a los clasicos. Procesos de obtencion de rentabilidad, en un paradigma donde cualquier inversor de la vieja escuela, o un simple analista tecnico, serian incapaces de entender, como pueda ser la utilizacion de series generadas sinteticamente en funcion a unas series con caracteristicas de mercado.

El Alpha factor es un estilo de inversor que asume:
- Que los mercados son procesos estocasticos, que no siguen una distribucion normal, y que retornos pasados no garantizan retornos futuros. Por consecuencia, los metodos de seleccion de universos de activos, son aleatorios en muchas fases del proceso de research, incluso utilizando solo un 20% de datos reales, y un 80% de series temporales generadas tecnicas de replicacion estocastica sobre las caracteristicas del activo. Generando una fuente de “ruido” constante, que podemos interpretar como una aproximacion a el “desordern del mercado”
- Procesos avanzados de overfit, y de position sizing: Para poder asegurar aun mas la robustez del modelo. Los modelos se evaluan, por ratios donde estandarizan niveles de rentabilidad y rangos de invariabilidad del modelo ante eventos estocasticos etc..
- La matematizacion total del proceso de generacion de señales, para realizar estudios de estado actual (nowcasting) y evalucion de la continuidad de la robustez del modelo.
El Alpha Factor, al tener una una fuerte asociacion con el metodo cientifico, realiza modelos de inferencia basado en un nivel de rigor cientifico aceptabe

En esta tabla de Marcos Lopez Prado, vemos distintos metodos de investigacion. Dentro del Alpha Factor, existen tantas metodologias, como gente trabajando en ello, dado que existe el llamado secreto industrial, y altas dosis de opacidad ante
Para poder definir un Alpha Factor con propiedad deberiamos definir que es el alpha
Podriamos definir la rentabilidad de un modelo alpha como =
$$R_t = R_t * \alpha – R_t * \beta + e$$
Dividendo la rentabilidad en el factor beta (rentabilidad de mercado), y el factor alpha ( rentabilidad adicional al mercado).
Y definimos alpha como: La rentabilidad proveniente de la exposicion, y que no corresponde en tiempo con el comportamiento del activo subyacente.
Una estrategia 100% beta, seria comprar y olvidar. Una estrategia 100% Alpha, podria ser una estrategia oportunista donde unicamente opera bajo ciertas condiciones.
Como definimos el Alpha Factor ?
Conjunto de reglas y procedimientos, que partiendo de una formulacion matematica, genera unas exposiciones a retornos, utilizando metodologias alternativas para la extracion de ventajas.
Alpha Encodings
Los alpha enconders, es una forma de estandarizar y matematizar las señales. Dentro de una filosofia de sistemas de trading, la premisa principal consite en la generacion de señales basadas en reglas. Las reglas son esencialmente parte de la formula del alpha, pero no todo. Tambien pueden existir otros factores, como los contextos operativos, o directamente la creacion de universos.
Por ejemplo, podriamos generar señales de un sistema trend following basico basico, basado en un crude de medias como:
$$sign(minus(ma_{30}(close),ma_{200}(close)))$$
generando una respuesta binaria, en funcion del resultado. Pues esta formula lo que realiza es, una resta entre 2 medias (30,200) y transforma su resultado a positivo o negativo.
La entropia que puede generar estas señales, es demasiado alta, siendo aproximadamente similar utilizar entradas y salidas aleatorias de un activo. Por consecuencia, debemos mejorar nuestras señales. Vamos a utilizar mas formulas analogas a la principal. En este caso podriamos utilizar muchas tecnicas, pero vamos a utilizar la variacion y concadenacion de parametros.
$$plus( \\ sign(minus(ma_{20}(close),ma_{50}(close))), \\ sign(minus(ma_{50}(close),ma_{100}(close))), \\ sign(minus(ma_{100}(close),ma_{200}(close))) \\ )$$
De esta forma proporcinamos robustez a la señal primaria, mediante la valiadacion de señales anteriores. Dado que hay caracteristicas analogas concadenadas, generando un pequeño comite que exige mayores umbrales para realizar el cambio de estado. (De 0 a 1, De positivo a negativo…)
Como vemos, existen posibilidades infinitas para codificar alpha, y generar posteriores señales. Usualmente, los grandes bancos suelen utilizar este tipo de encodings, en sus estrategias de momentum, y arbitrajes long/short. podriamos programar, el efecto-calendario en un punto concreto, o evaluaciones automatizadas de las estrategias, en tiempo real, para saber si se han caido, o sigen operativas, etc..
Podriamos realizar una evaluacion automatizada sobre nuestras estrategias, o monitorear el mercado mediante tecnicas de nowcasting para encontrar una anomalia, sin escribir una linea de codigo explicito.
Por ejemplo, podriamos testear el anuncio post-beneficios y su efecto de una forma extremadamente sencilla:
$$div(epsDifference,std_{12}(epsDifference))$$
Universos

Los universos son subgrupos de activos, candidatos a la exposicion de alpha. Hablando claro, son cestas de activos que utilizaremos para impactar contra nuestros Alpha.
Las condiciones para la generacion de universos no tienen limites. Desde universos 100% aleatorios. Hasta con criterios de capitalizacion bursatil, sectores, criterios fundamentales, macroeconomicos, basados en caracteristicas estadisticas, etc… Usualmente se utilizan un comite de varios criterios para la seleccion de los mismos.
Los universos de seleccion dinamicos consisten en reevaluar el universo utilizado cada cierto tiempo. Es una metodologia muy implantada. Por ejemplo en la generacion de indices. Vamos a entrar en detalle, la principal creadora de indices a nivel mundial es S&P Dow Jones Indices.
Calculan una gran variedad de indices, pero el mas relevante es el S&P500. Donde :
S&P 500. El índice mide el desempeño de gran capitalización de mercado (Large Cap). Considerado
https://www.spglobal.com/spdji/es/documents/methodologies/methodology-sp-us-indices-spanish.pdf
como el indicador más descriptivo del mercado de capitales de los EE.UU. El índice se compone por 500
compañías.
Para formar porte de sus indices, debes cumplir unos criterios de elegibilidad. En este caso es que:
- La compañia cumpla los requisitos SEC de 1934
- Que se negoce en EEUIU
- Que generen la mayoria de sus beneficios en EEUU
Cada 6 meses, rebalancean los indices, añadiendo o eliminado acciones que no cumplan sus criterios del prospecto. Ademas para calcular el peso en el indice de cada activo se realiza mediante la formula
$$p_a = MktCap / GlobalMktCap$$
De esta forma medimos del global de la capitalzacion total de los activos que conforman el indice, que porcentaje pertence en relacion a la accion analizada.
Siendo la formula total
\begin{aligned} &\text{Index Level} = \frac { \sum_{i = 1}^n P_i \times Q_i }{ \text{Divisor} } \ &\textbf{where:} \ &P_i = \text{Price} \ &Q_i = \text{Free-float shares} \ \end{aligned}
El indice SP500, es generado mediante un formula matematica, que cumple todos los requisitos de los alpha encoderes y la creacion de un universo, de todos los activos disponible.
Generando un indice que puede ser considerado como un monitor del conjunto general del mercado, como una estrategia de inversion replicable. Incluso mas alla de comprar un ETF, al tener un Alpha Encoding publico, y las reglas de composicion de universos, podrias comprar el indice en tu cartera, y ajustar exactamente en cada momento la misma ponderacion que tendria el original, sin ningun tipo de ambiguedad o necesidades de deducciones matematicas.
Seleccion de Universos en Python
Vamos a empezar a crear universos, cestas de activos candidatos a ser expuestos. Para ello vamos a utilizar diferentes criterios, el primero vamos a realializarlo de forma 100% aleatoria. Esta es la forma mas pura para evitar sesgos como el sesgo de seleccion. Consistente en elegir activos que conocemos con anterioridad que han sido ganadores, para empezar nuestro research.
import pandas as pd
def random_tickers(x):
tables=pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
tickers = tables[0]['Symbol'].tolist()
return random.choices(tickers,k=x)
Mediante esta funcion, descargamos los tickers del SP500 de la wiklipedia, y seleccionamos aleatoriamente un numero concreto. Nos aseguramos el elegir siempre acciones suficientemente liquidas como para poder aventurarse en el research. Dado que si utilizaramos un crieterio sin filtros de liquidez en algun punto, podriamos tener candidatas con menos de 1M de volumen diario, incluyendo mayores riesgos a la operativa, y mayor aletoriedad en los resultados.
Ahora vamos a refinar un poco mas el proceso, para evitar crear universos especialmente extensos, vamos a utilizar como punto de referencia alguna cesta de emisores de etfs. Con esto nos verificamos, que las empresas disponen de la suficiente liquidez y confianza para poder invertir en ellas, delegando parte del proceso de reduccion de riesgos a entidades con amplia experiencia. En este caso vamos a utilizar los ETFs de Blackrock (iShares). SPX, NDX, World Min Vol, SPHC.
import pandas as pd
urls = ['https://www.blackrock.com/es/profesionales/productos/253743/ishares-sp-500-b-ucits-etf-acc-fund/1497267045693.ajax?fileType=csv&fileName=CSPX_holdings&dataType=fund',
'https://www.blackrock.com/es/profesionales/productos/253741/ishares-nasdaq-100-ucits-etf/1497267045693.ajax?fileType=csv&fileName=CSNDX_holdings&dataType=fund',
'https://www.blackrock.com/es/profesionales/productos/251382/ishares-msci-world-minimum-volatility-ucits-etf/1497267045693.ajax?fileType=csv&fileName=MVOL_holdings&dataType=fund',
'https://www.blackrock.com/es/profesionales/productos/280507/ishares-sp-500-health-care-sector-ucits-etf/1497267045693.ajax?fileType=csv&fileName=IUHC_holdings&dataType=fund']
allz = pd.DataFrame()
for x in range(len(urls)):
tempz = pd.read_csv(urls[x],skiprows=2)
allz = pd.concat([tempz,allz])
allz = allz[allz['Asset Class'] == 'Equity']
print('Cargados',len(allz['Ticker'].unique()),'tickers en un total de ',len(allz['Sector'].unique()),'Sectores')
allz = allz.set_index('Ticker')
allz['Market Value'] = allz['Market Value'].str.replace('.', '').str.replace(',', '.')
allz['Market Value'] = allz['Market Value'].astype(float)
Dando como resultado 670 tickers candidatos. Para transformar los candidatos a un formato lista seria mediante:
tickers_candidatos = allz.index.tolist()
O para acceder a los tickers dentro de un sensor en concreto dentro de nuestro universo
allz[allz['Sector'] == 'Cuidado de la Salud'].index.tolist()
La elaboracion de universos dispone de infinitas herramientas para su construccion. Usualmente se suelen utilziar metoodologias diferentes a la metainformacion como el sector, o su capitalizacion, con otras caracteristicas estadisticas relevantes que proporcionen un escenario de impacto favorable del alpha sobre los activos.
OJO. No estamos hablando de utilizar las acciones que mejor me cuadren en el albaran, es decir, si mi sistema funciona en ella, la añado dentro del universo. Eso es introducir overfit y sesgos dentro del modelo. Hablamos de crear modelos que impacten contra la cantidad total de activos, y seleccione los mas oportunos, minizimando el overfit en la seleccion de activos hasta niveles minimos.
Introduccion a los Alpha Factors en Python
Vamos a tratar, de forma introductiva, diferentes logicas para el research de los Alpha Factors. Este articulo no pretende ser un framework sobre el research, sino la presentacion de ciertas herramientas. Y como desde una formula matematica, acabamos realizando un modelo del factor. Para el framework completo, esta disponible para miembros de la QUANT ARMY
Plan de Ataque
- 1 Cargar las librerias y herramientas necesarias
- 2 Programar las funciones axuliares
- 3 Programar el motor principal de optimizacion
- Integtrar todas las piezas a la cadeina
Una vez introducidos estos cuatro conceptos. Llegaremos un poco mas alla, revelando algo mas sobre la implantacion de los nuevos factores Alpha, con las estrategias tipicas de Market Neutral.
Herramientas Utilizadas
Para esta primera parte, vamos a utilziar librerias standard de python. Para agilizar la parte del proceso de descarga de datos, utilizaremos yfinance. Para simplificar el proceso de descarga de datos, vamos a utilizar la librerida de yfinance
Programacion
El objetivo es crear una logica que calcule el Alpha Factor sobre un activo, para su posterior evaluacion. Todo el codigo completo, esta disponible para miembros del QUANTARMY
Matematicas
El Alpha Factor que vamos a utilizar es:
$$\delta(\left|\gamma_s R_t\right|_tx) \rightarrow z > threshold $$
Siendo
Las formas de obtencion de alphas, son secretas. Usualmente se pueden leer en publicaciones especializadas, pero tras entender los concpetos generales del Alpha Trading, entraremos en profundidad sobre los metodos de research de Alpha Factors en Python, para los miembros de nuestro quant army.
En el QUANT ARMY, hablaremos largo y tendido, dando metodos avanzados de research sobre alpha factors.
Explicacion “subjetiva”
Dado que este alpha ha sido definido utilizando procesos avanzados de big data, intentar justificar cualquiera de las causas de su extracion de valor de mercado, seria caer en una falacia narrativa sin sin sentido. De forma personal, y calibrando unos pequeños ajustes que no expondre en este articulo,(si en el repositorio), se puede convertir en un modelo totalmente funcional a dia de hoy.
Calculamos la delta del valor absoluto del skewness de los retornos. Pasando todos los test de invariabilidad, tanto por retornos como por desviaciones tipicias, tanto con datos targeteados clasicos (seleccionar los etfs mas liquidiso), como por procesos aleatorios de seleccion (eligendo tickers random, y tratandolos) ademas de ser totalmente funcional y con coeficientes de invariabilidad extremadamente bajos con data sintetica.
En contra de las funciones objetivo de los Backtesters clasicos de las plataformas. Una optimizacion es encontrar la invariabilidad mediante parametros en una serie de precios fija. El objeto principal de las optimizaciones de los alpha factos es encontrar la mayor rentabilidad, con el menor indice de invariabilidad entre diferentes muestras.
Para extraer estas alphas,se utilizan multitud de metodos, desde inverse equity targeting, hasta machine learning aplicado a copulas de parametros y objetivos de robustez. La creatividad humana en oportunidades de rentabilidad es infinita.
Podriamos definirlo de una forma mas humana como:
Delta(ValorAbsoluto(PearsonSkewness(RetornosTPeriodos)))
Este tipo de Alphas suelen utilizarse bajo las siguientes condiciones de framework:
- Universos acotados o definidos
- Metodologia on-track de portfolio. Este tipo de alphas deben ser evaluados contra un un numero de al menos 30 activos o series temporales (cuantos mas mejor).
- Posicionamientos Dinamicos tras evaluar el ratio de cada una de ellas, nos posicionaremos en un sentido alcista en las primeras n posicoones o x% de capital.
- Se asume un trabajo robusto de correlacion de activos, mediante factores , procesos de pca o cualquier otra tecnica, pues al aplicar una estrategia long/short se genera el denominado “retorno absoluto”, que radica en la obtencion de alpha, neutralizando la beta, y descorrelacionando totalmente la cartera del mercado.
- El tamaño de las posiciones iria definido mediante la volatilidad realizada inversa, mediante un invertor estandar formato -1 *, y el riesgo iria targeteado a un 20% el 95% de las veces.
- Target de volatilidad general de la estrategia aceptable (-20%)
Para la primera parte de esta publicacion, asumimos que e
Para llevar a cabo este primer modulo asumimos que las reglas para crear nuestro Alpha Factor en Python para Tradin son:
- Trabajaremos con un unico activo.
- El tamaño de la posicion sera siempre un 100%
- El unico cometido es observacion de fenomenos en la formacion de precios y procesos de mercado, no nos preocupamos por problemas en tipos de divisa, problemas regulatiros, o caulqier otro tipo.
- No se aplicaran procesos de solidificacion de la metodologia, unicamente se pretende observar.
Programando Alpha Factors en Python
Vamos a mostar como transformar desde la notacion matematica, a lenguaje de programacion Python, un Alpha Factor. De una forma sencilla.
Para poder empezar con nuestro modelo, utilizamos librerias estandar de python. Ademas de las habituales, utilziamos itertools, para hacer futuras permutaciones entre listas.
import pandas as pd
import numpy as np
import scipy as sc
import matplotlib.pyplot as plt
import yfinance as yf
import itertools
from IPython.display import clear_output
plt.style.use("quantarmy")
Programacion de funciones auxiliares
Una vez tenemos definidas las librerias basicas con las que vamos a trabajar, vamos a empezar a crear funciones, sobre tareas que imagenamos seran repetitivas.
Lo optimo con las funciones auxiliares sea incluirlas todas en una libreria, para su posterior uso, dado su caracter generalista. Pero al tratarse de una introduccion al Alpha Factor en Python, vamos a definirlas dentro de nuestro entorno Jupyter, como si una celda mas se tratase.
Para calcular el skew rollante:
def calc_skew(data,y):
skew = data.rolling(y).skew()
return skew
Para calcular los retornos de n periodos
def calc_rets(data,y):
rets = data.pct_change(y)
return rets
Para calcular el delta de x sobre y periodos
def calc_delta(d1,d2):
delta = d1 - d1.shift(d2)
return delta
Una vez tenemos listas las funciones , vamos a pasar nuestro factor alpha a codigo, y generar un entorno favorable para poder investigar en profundidad.
Programacion del Primer Alpha con enfoque Multiactivo
Pese a que hemos comentado que en esta primera parte unicamente lidiaremos con un unico activo. Vamos a probar el mismo alpha sobre diferentes activos (Como alphas independientes). Con ello conseguimos estimar los resultados de un alpha ante diferentes activos.
Vamos a crear una funcion auxiliar, que testeara nuestro factor alpha, contra los los ETFs confiderados como proxies de exposicion a diferentes assets.
Los ETFs que utilizamo son $SPY $QQQ $IWM $TLT $GLD
La funcion descargara los datos, los transformara en nuestro factor alpha, y ejecutara la condicion de z>alpha.
Posteriormente se calulcan las variaciones porcentuales, se suman y se plotean
El objetivo de este alpha, es mostrar una forma sencilla de modelizar estos alphas. No se puede considerar esta funcion mas a alla de un simpe modelo, pues no extrae ningun tipo de coste, ni asume ningun tipo de contingencia. Pero es una aproximacion a al research. Cualquier alpha, que no pase por esta fase con resultados robustos y favorables, es automaticamente descartada.
def backtest_alpha_01(data,ret_x=10,days_delta=10,rolling_skew=10,p=1.0):
for d in ['SPY','QQQ','IWM','TLT','GLD']:
data = yf.download(d,progress=False)[['Adj Close']].round(2)
df = data[['Adj Close']].round(2)
df['c_ret'] = df['Adj Close'].pct_change(ret_x)
df['skew'] = df['c_ret'].rolling(rolling_skew).skew()
df['abs'] = np.abs(df['skew'])
df['alpha'] = calc_delta(df['abs'],days_delta)
df['signal'] = np.where(df['alpha'] > p,1,0)
df['pct'] = df['Adj Close'].pct_change()
df['ret'] = df['pct'] * df['signal'].shift(1)
df['ret'].cumsum().plot()
plt.title('Basic Backtest over assets main assets')
plt.legend(labels=['SPY','QQQ','IWM','TLT','GLD'])
plt.show()
return

Programacion del backtester del optimizador
En el proximo modulo, vamos a crear un motor de optimizacion, y necesitara llamar a una funcion backtester, para calcular los retornos y la gestionamos como una funcion de backtest que: devuelva a la funcion optimizadora los resultados del backtest.
def backtest_alpha_01(data,ret_x=10,days_delta=10,rolling_skew=10,p=1.0):
for d in ['SPY','QQQ','IWM','TLT','GLD']:
data = yf.download(d,progress=False)[['Adj Close']].round(2)
df = data[['Adj Close']].round(2)
df['c_ret'] = df['Adj Close'].pct_change(ret_x)
df['skew'] = df['c_ret'].rolling(rolling_skew).skew()
df['abs'] = np.abs(df['skew'])
df['alpha'] = calc_delta(df['abs'],days_delta)
df['signal'] = np.where(df['alpha'] > p,1,0)
df['pct'] = df['Adj Close'].pct_change()
df['ret'] = df['pct'] * df['signal'].shift(1)
df['ret'].cumsum().plot()
plt.title('Basic Backtest over assets main assets')
plt.legend(labels=['SPY','QQQ','IWM','TLT','GLD'])
plt.show()
return
Programacion del motor del optimizador
Ahora creamos una funcion, que recorra todo el espectro de la funcion, y nos muestre una radiografia del impacto del factor alpha. Para poder evalur el espectro, vamos a creear una media de todos los puntos de los resultados, en un momento temporal concreto y posteriormente se plotea.
def explore_alpha_01(df,ret_x=10,ret_y=16,days_delta_x=10,days_delta_y=16,rolling_skew_x=10,rolling_skew_y=16,p_x=1,p_y=2.1):
eqs = pd.DataFrame()
res = []
i = 1
retz = range(ret_x,ret_y,1)
deltaz = range(days_delta_x,days_delta_y,1)
skewz = range(rolling_skew_x,rolling_skew_y,1)
pz = np.arange(p_x,p_y,0.1)
combos = itertools.product(retz,deltaz,skewz,pz,)
paramz = []
len_p = sum(1 for ignore in combos)
print('Total Permutations aviable:',len_p,'| fomrula = ',len(retz),'*',len(deltaz),'*',len(skewz),'*',len(pz))
print(25*('---'))
for v in list(itertools.product(retz,deltaz,skewz,pz,)):
print(10*('---'),'[',i/len_p*100,'%]','||||||>',i,'/',len_p)
print('0 | Lookback Returns ->', v[0])
print('1 | T2 over Delta ->', v[1])
print('2 | Rolling Skew over R ->', v[2])
print('3 | Alpha Cutoff ->', round(float(v[3]),2))
return_temp = backtest_alpha_01_opt(df,v[0],v[1],v[2],v[3])
print('sum of r ->',round(return_temp.cumsum()[-1],2) * 100,'%')
print(35*('---'))
clear_output(wait=True)
eqs = pd.concat((eqs, return_temp.rename('ret_'+str(i))), axis=1)
i = i+1
return eqs
Tras recorrer todo el espectro, obtenemos el dataframe results, con las curvas de equities de cada una de las iteraciones que hemos hecho en la funcion de optimizacion.
Los resultados, sin seprarar combinaciones ganadoras o perdedoras.

Alpha Trading a nivel cesta de activos.
Nuestro objetivo actual, es poder evaluar entre una cesta de activos, dos activos para crear una poscion market-neutral. Es decir, un activo este comprado (en largo) y otro activo esta vendido (o en corto). Para ello utilizaremos un estimador, y rankearemos los resultados de este estimador, contra una cesta de activos.
Universo de activos
Pese a que hemos explicado como crear universos, vamos a utilziar la llamada asset-basket, donde incluye exposicion a los assets mas negociados del mercado, mediante ETFs. Para esta parte, y siendo un articulo introductorio, vamos a elegir una cestas compuestas por:
tickers = ['SPY','GLD','TLT','IEF','AGG','QQQ','IWM','SLV','VNQ']
datalake = yf.download(tickers,interval='1d')
Proceso de los datos
Utilizamos dataframes multindex, para pdoer tratar los dataframes devueltos desde yahoo finance.
returns = datalake.xs('Adj Close', level=0, axis=1).pct_change().dropna()
close = datalake.xs('Adj Close', level=0, axis=1).dropna()
openz =datalake.xs('Open', level=0, axis=1).dropna()
high = datalake.xs('High', level=0, axis=1).dropna()
low =datalake.xs('Low', level=0, axis=1).dropna()
volume = datalake.xs('Volume', level=0, axis=1).dropna()
Generacion de señales
Para generar las señales, se utiliza un bucle, que recorra todas las columnas y aplique la transformacion del alpha factor.
unique_values = datalake.columns.get_level_values(1).unique()
signals = pd.DataFrame()
for s in unique_values:
y = datalake.xs(s,level=1,axis=1).dropna()
openz = y['Open']
low = y['Low']
close = y['Close']
high = y['High']
volume = y['Volume']# Remove rows with missing values
minus_ho = qa.minus(high,close)
mvwap_5 = qa.mvwap(openz,low,close,volume)
mult1 = qa.mult(mvwap_5,minus_ho)
adx_20 = qa.adx(high,low,close,20)
mm2 = qa.mult(mult1,adx_20)
gsret = qa.gret(close)
mm3 = qa.mult(gsret.dropna(),mm2.dropna())
last = qa.neg(mm3)
signals[s] = last
Ranking de señales
Una vez estimados todos los valores del Alpha factor, vamos a rannkear todas las señales con orden numero. Dado que el modelo siempre esta invertido, procedera a seleccionar las mejores candidatas en funcion al objetivo seleccionado (El Alpha Factor). La misma logica se utilizara para el lado corto
ranks = scipy.stats.rankdata(signals,axis=1,method='average',nan_policy="omit")
ranked = pd.DataFrame(ranks,columns=tickers,index=signals.index).dropna()
El cual nos devuelve un dataframe tal que:
SPY | GLD | TLT | IEF | AGG | QQQ | IWM | SLV | VNQ | |
---|---|---|---|---|---|---|---|---|---|
Date | |||||||||
2006-04-28 | 8.0 | 1.0 | 8.0 | 5.0 | 4.0 | 8.0 | 3.0 | 6.0 | 2.0 |
2006-05-01 | 6.0 | 2.0 | 7.0 | 1.0 | 8.0 | 9.0 | 3.0 | 4.0 | 5.0 |
2006-05-02 | 3.0 | 7.0 | 4.0 | 8.0 | 5.0 | 9.0 | 6.0 | 2.0 | 1.0 |
2006-05-03 | 6.0 | 1.0 | 5.0 | 4.0 | 7.0 | 9.0 | 3.0 | 2.0 | 8.0 |
2006-05-04 | 6.0 | 2.0 | 4.0 | 8.5 | 7.0 | 8.5 | 3.0 | 1.0 | 5.0 |
… | … | … | … | … | … | … | … | … | … |
2023-06-05 | 8.0 | 6.0 | 7.0 | 3.0 | 2.0 | 9.0 | 1.0 | 5.0 | 4.0 |
2023-06-06 | 9.0 | 4.0 | 7.0 | 3.0 | 1.0 | 8.0 | 2.0 | 6.0 | 5.0 |
2023-06-07 | 6.0 | 3.0 | 5.0 | 7.0 | 1.0 | 9.0 | 2.0 | 4.0 | 8.0 |
2023-06-08 | 9.0 | 4.0 | 7.0 | 3.0 | 2.0 | 8.0 | 1.0 | 6.0 | 5.0 |
2023-06-09 | 8.0 | 4.0 | 7.0 | 3.0 | 1.0 | 9.0 | 2.0 | 6.0 | 5.0 |
4309 rows × 9 columns
En el resultado, podemos ver las clasificaciones diarias de los Alpha Factors. Nuestro objetivo consiste, en seleccionar en cada periodo temporal, el mejor activo para largos (1) y el peor activo para cortos (9). En funcion de las dimensiones del universo de activos hay que modificar los parametros. Pero la esencia de la idea y la metodologia, subyace en este concepto.
Retornos de los Alphas
Para poder analizar los retornos de los Alpha Factor, lo primero que debemos hacer, es saber que valor es el candidato para posiciones alcistas, y cual es el candidato para posiciones bajistas.
Para ello, vamos a crear dos bucles, uno para añadir los largos, y otro para añadir los cortos. Atencion, esta forma de programar es altamente ineficiente. No se recomienda mas alla de formar parte de un articulo divulgativo. La metodologia correcta para realziar esta conversion es la vectorizacion directa.(En zona de QUANT ARMY esta lista para su uso)
dtop = []
longs = []
for fecha, fila in ranked.iterrows():
dtop = fila[fila == 1].index.tolist()
if columna_con_1_dia:
top = {'idx':fecha,'value':dtop[0]}
longs.append(top)
dshort = []
shorts = []
for fecha, fila in ranked.iterrows():
dshort = fila[fila == 8].index.tolist()
if dshort:
top = {'idx':fecha,'value':dshort[0]}
shorts.append(top)
shorts = pd.DataFrame(shorts).set_index('idx')
longs = pd.DataFrame(longs).set_index('idx')
Cada uno de los dataframes creados denominados shorts y longs, generan una estructura de este estilo
value | |
---|---|
idx | |
2006-04-28 | SPY |
2006-05-01 | AGG |
2006-05-02 | IEF |
2006-05-03 | VNQ |
2006-05-05 | AGG |
… | … |
2023-06-05 | SPY |
2023-06-06 | QQQ |
2023-06-07 | VNQ |
2023-06-08 | QQQ |
2023-06-09 | SPY |
Entonces disponemos del precio de los activos, y que activo es el seleccionado para cada dia. El siguiente paso, es la composicion de la posicion sintetica, y la evaluacion de sus resultados.
Composicion de portfolio Market Neutral
Primero, vamos a crear un dataframe, con toda la informacion unificada, para evitar errores y problemas a futuro
close = datalake.xs('Adj Close', level=0, axis=1).dropna()
strategy = pd.DataFrame(index=longs.index)
strategy['LONG'] = longs['value']
strategy['SHORT'] = shorts['value']
close = close.reindex(strategy.index) # Reindex strategy DataFrame
pct = close.pct_change().shift(1).dropna().reindex(strategy.index)
Obteniendo como resultado:
LONG | SHORT | RET_LONGS | RET_SHORTS | |
---|---|---|---|---|
idx | ||||
2006-05-02 | IEF | VNQ | -0.005415 | 0.012040 |
2006-05-03 | VNQ | GLD | -0.010182 | -0.021332 |
2006-05-05 | AGG | SLV | -0.000716 | 0.030630 |
2006-05-08 | AGG | IWM | 0.001329 | -0.017857 |
2006-05-09 | TLT | IEF | 0.000599 | -0.000499 |
… | … | … | … | … |
2023-06-05 | SPY | IWM | 0.014461 | -0.036265 |
2023-06-06 | QQQ | AGG | 0.000705 | 0.000102 |
2023-06-07 | VNQ | AGG | 0.009583 | -0.001223 |
2023-06-08 | QQQ | IWM | -0.016965 | -0.018711 |
2023-06-09 | SPY | AGG | 0.006049 | -0.005323 |
Para evaluar las operaciones teoricas de su lado largo se realizaria mediante:
strategy['RET_LONGS'].cumsum().plot()
Dando como resultado la siguente curva, expresada en rentabilidad porcentual

En la otraparte, en el lado corto calculamos la rentabilidad acumulada como:
strategy['RET_SHORTS'].cumsum().plot()
Dando como resultados una curva de operaciones cortas :

Construccion de la estrategia de Market Neutral
Para construir nuestra estrategia de Market Neutral, asumiendo que utilizamos activos con una fuerte descorrelacion y que cumplen las necesidades estadisticas necesarias para formar parte del universo
Siendo nuestra posicion Market Neutroal como
$R_neutral$ = % long * retorno long – % short * retorno short
Nuestra fuente de rentabilidad se obtiene de las diferencias entre los dos activos. Consiguiendo.
- Una reduccion teorica de riesgos
- Una reduccion real de garantias
Ademas, debemos considerar en aplicar criterios de risk targeting a nivel activo, a nivel estrategia y a nivel portfolio. Y por descontado la utilizacion de procesos de nowcasting, reevaluaciones constantes, y puntos de solidez estadistica.
Puntos prefijados en fase de diseño, donde se determina cuando un alpha se esta desviando de su cometido original, y por consecuencia debe ser desconectado de produccion(aunque este ganando dinero).
Dado que los retornos esperados y el subconjunto de retornos realizados por el modelo, tienen una desviacion o cualquier otra contingencia.
r_long = 0.5
r_short = 0.5
(r_short * strategy['RET_SHORTS'] + r_long * strategy['RET_LONGS']).cumsum().plot()

Llegados a este punto, se ha creado una toolbox, para introducir el research de alpha factor en python, de forma sencilla. Se podria construir un evaluador de pesos de la estrategia, en funcion de la volatilidad realizada inversa, o en fucnion de cualquier otro sistema de posicionamento dinamico.
El resultado obtenido, aplicando los estandares clasicos del Market Neutral, (O Retorno Absoluto en español son:
La ponderacion clasica de este tipo de modelos, esta calibrado, paara tener una exposicion del 100% en en e lado largo, y un 20% en el lado corto
Conclusiones
El Alpha Factor es una modalidad abierta de creacion de frameworks, donde su ventaja radica en la eliminacion de sesgos del proceso constructivo, y la estandarizacion de las fuentes de alpha.
Existen infinitas fuentes de extracion de Alpha, no todas ellas con resultados satisfactorios, pero cualquier estrategia con altos indices de invariabilidad, resultara superior a cualquier estrategia con un indice de invariabilidad inferior, pero mayor rentabilidad. Primaremos el aumento en la estabilidad, mas que en la rentabilidad.
Para descubir Alpha Factors, se utilizan diferentes tecnicas de Machine Learning y Deep Learning, Data Mining, Big Data y un largo etec… Es un estilo de inversion disponible en cualquier tipo de activos.
Ademas, para añadir robustez, hay que realizar procesos de data enginiering avanzados. Que estaremos debiatiendo proximamente en la zona de miembros de QUANTARMY