Alpha Factor in Python: Modeling for Advanced Trading Strategies
Alpha Trading is the way to model Alpha Factors in return exposure scenarios. These models are gaining popularity among investors of all levels thanks to greater technological accessibility.
What is an Alpha Factor?
It is a relatively new concept where traditional explanations do not reflect the dynamic reality of this investment modality. The world of cryptoassets has attracted brilliant researchers who have generated new backtesting models with criteria different from the classic ones. These return-generation processes operate in a paradigm that traditional investors or technical analysts would find difficult to understand.
 and evaluation of the continuity of model robustness.
The Alpha Factor maintains a strong association with the scientific method, performing inference models with acceptable scientific rigor.
 and alpha factor (return in addition to the market). We define alpha as the return coming from exposure that does not correspond in time with the behavior of the underlying asset.
A 100% beta strategy would be buy and hold, while a 100% Alpha strategy would be opportunistic, operating only under certain specific conditions.
An Alpha Factor is a set of rules and procedures that, starting from a mathematical formulation, generates return exposures using alternative methodologies for extracting advantages.
Alpha Encodings
Alpha encoders standardize and mathematize signals within the philosophy of trading systems. For example, we could generate signals from a basic trend following system with a moving average crossover:
$$sign(minus(ma_{30}(close),ma_{200}(close)))$$
This generates a binary response based on the subtraction between two moving averages (30, 200). However, the entropy of these signals is too high, similar to using random entries and exits. To improve, we can use more analogous formulas:
$$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))) \ )$$
This provides robustness through the validation of previous signals, creating a "committee" that demands higher thresholds to make state changes.
The possibilities for encoding alpha are endless. Large banks typically use these encodings in momentum strategies and long/short arbitrage. We could even test the effect of post-earnings announcements in a simple way:
$$div(epsDifference,std_{12}(epsDifference))$$
Asset Universes
. Considered the most descriptive indicator of the U.S. capital markets. The index is composed of 500 companies.
To calculate the weight of each asset, the following formula is used:
$$p_a = MktCap / GlobalMktCap$$
This allows exactly replicating the same index weighting without ambiguities.
Universe Selection in Python
Let us create universes of candidate assets. We will start with a 100% random selection to avoid bias:
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)
We can also refine the process using established ETFs as a reference:
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)
To convert the candidates to list format:
tickers_candidatos = allz.index.tolist()
Or to access tickers from a specific sector:
allz[allz['Sector'] == 'Cuidado de la Salud'].index.tolist()
It is important to note that we are not selecting stocks where our system performs better (which would introduce overfit), but rather creating models that apply across all assets and select the most suitable ones.
Introduction to Alpha Factors in Python: Practical Implementation
We will explore different logics to research Alpha Factors. This article presents basic tools to transform a mathematical formula into a functional model.
Plan of Attack
- Load the necessary libraries and tools
- Program the helper functions
- Program the main optimization engine
- Integrate all the pieces into the pipeline
Tools Used
We will use standard Python libraries and yfinance to streamline data downloading.
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")
Programming Helper Functions
We create functions for repetitive tasks:
def calc_skew(data,y):
skew = data.rolling(y).skew()
return skew
def calc_rets(data,y):
rets = data.pct_change(y)
return rets
def calc_delta(d1,d2):
delta = d1 - d1.shift(d2)
return delta
Programming the First Alpha with a Multi-Asset Approach
We will test the alpha on different assets to estimate its performance:
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
 are arbitrary. Results will vary depending on the parameters used to calculate the estimator and its cutoff points.*
$$IR = \frac{\mathbb{E}[\alpha]}{\sigma_\alpha} = \frac{\text{IC} \times \text{BR} \times \text{TC}}{\sigma_\alpha}$$
$$IC = \rho_{\alpha, \hat{\alpha}}$$
$$S = \frac{\mathbb{E}[R_p - R_f]}{\sigma_p}$$
Programming the Optimizer Engine
We create a function that traverses the entire spectrum of possible parameters:
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)
delt