AM (Alpha Model)
Creating Your Portfolio Position in a Very Simple Way: Embracing a Bias-Free Approach
Develop AM (Alpha Model)
Helper Function: Calculating Previous Start Date
from datetime import datetime, timedelta
def calculate_previous_start_date(start_date, lookback_days):
start = datetime.strptime(str(start_date), "%Y%m%d")
previous_start = start - timedelta(days=lookback_days)
return int(previous_start.strftime("%Y%m%d"))
Very simple AM script
from finter import BaseAlpha
# Define the lookback period in days
LOOKBACK_DAYS = 365
start, end = 20200101, 20220101
# Alpha class inheriting from BaseAlpha
class Alpha(BaseAlpha):
# Method to generate alpha
def get(self, start, end):
# Calculate the start date for data retrieval
pre_start = calculate_previous_start_date(start, LOOKBACK_DAYS)
# Retrieve daily closing prices
self.close = self.get_cm(
"content.fnguide.ftp.price_volume.price_close.1d"
).get_df(pre_start, end)
# Calculate momentum
momentum_21d = self.close.pct_change(21)
# Rank stocks by momentum
stock_rank = momentum_21d.rank(pct=True, axis=1)
# Select top 10% of stocks
stock_top10 = stock_rank[stock_rank>=0.9]
# Apply rolling mean to smooth data
stock_top10_rolling = stock_top10.rolling(21).apply(lambda x: x.mean())
# Normalize and scale to position sizes
stock_ratio = stock_top10_rolling.div(stock_top10_rolling.sum(axis=1), axis=0)
position = stock_ratio * 1e8
# Shift positions to avoid look-ahead bias
alpha = position.shift(1)
# Alpha position must be a value between start and end
return alpha.loc[str(start): str(end)]
# Run the alpha generation process
alpha = Alpha().get(start, end)
Detail code description.
Alpha Class Definition
LOOKBACK_DAYS = 365
class Alpha(BaseAlpha):
We define a constant LOOKBACK_DAYS
set to 365, representing one year of historical data. The Alpha
class inherits from BaseAlpha
, which contains methods and properties useful for alpha development.
Alpha Generation Method
def get(self, start, end):
pre_start = calculate_previous_start_date(start, LOOKBACK_DAYS)
The get
method is the core function that generates the alpha. It takes a start and end date for the analysis period. The calculate_previous_start_date
function is used to extend the start date further back by the number of LOOKBACK_DAYS
.
Data Retrieval
# Retrieve daily closing prices for the specified date range
self.close = self.get_cm(
"content.fnguide.ftp.price_volume.price_close.1d"
).get_df(pre_start, end)
This line retrieves the daily closing prices of stocks between the pre_start
and end
dates. The get_cm
method is a part of the BaseAlpha
class and is used to call data modules.
Momentum Calculation
# Calculate 21-day momentum for closing prices
momentum_21d = self.close.pct_change(21)
We calculate the 21-day momentum of the closing prices, which is the percentage change over the last 21 days.
Stock Ranking
# Rank stocks based on their momentum
stock_rank = momentum_21d.rank(pct=True, axis=1)
The momentum values are then ranked on a percentile scale (0 to 1) across all stocks for each day.
Selecting Top 10% Stocks
# Filter to select the top 10% of stocks by momentum
stock_top10 = stock_rank[stock_rank>=0.9]
We filter the stocks to select only the top 10% based on their momentum ranks.
Rolling Mean of Top Stocks
# Apply a rolling mean to smooth the top stocks' data
stock_top10_rolling = stock_top10.rolling(21).apply(lambda x: x.mean())
A rolling mean with a 21-day window is applied to the top 10% stocks to smooth out the data.
Position Sizing
# Normalize the rolling mean values and scale to position sizes
stock_ratio = stock_top10_rolling.div(stock_top10_rolling.sum(axis=1), axis=0)
position = stock_ratio * 1e8
The rolling mean values are normalized by dividing by the sum across all stocks to get a ratio. This ratio is then scaled up to determine the position sizes, here represented by a hypothetical amount of 100 million (1e8).
Generating Alpha
# Shift positions to avoid look-ahead bias and generate the alpha
alpha = position.shift(1)
# Alpha position must be a value between start and end
return alpha.loc[str(start): str(end)]
The final alpha is generated by shifting the positions by one day to avoid look-ahead bias. This means we use yesterday's positions to determine today's trades.
Running the Alpha
# Create an instance of the Alpha class and run the alpha generation
alpha = Alpha().get(20230101, 20230201)
Finally, we create an instance of the Alpha
class and call the get
method with a specific start and end date to run the alpha generation process.
This guide provides a basic framework for writing an alpha in Python. It is important to note that this is a simplified example, and real-world alpha development involves more complex data analysis, risk management, and performance evaluation.
Last updated