The model runs as a Python script every Tuesday. Here are the core calculations.
Adjusted Slope
This is the heart of the ranking. The idea is to find stocks in strong and consistent uptrends — not just stocks that went up a lot, but stocks where the move was clean and steady.
To measure this, I fit an exponential regression to the last 90 closing prices (in log scale). The slope tells me how fast the stock is rising. I then multiply it by R² — a measure of how closely the prices actually follow the trend line. A stock that’s up 50% in a straight line will score much higher than one that’s up 50% erratically.
slope, _, r_value, _, _ = linregress(
frame['RN'][-REGRESSION_DAYS:], frame['LN'][-REGRESSION_DAYS:]
)
annualized = np.power(np.exp(slope), 250) - 1
rsqr = r_value ** 2
adjusted_slope = annualized * rsqr
The slope is annualised using 250 trading days so the values are comparable across stocks.
Position Sizing
Position size is determined by how volatile the stock is, not by gut feel. The formula is:
shares = AccountValue × RiskFactor / ATR
ATR (Average True Range) measures the stock’s average daily price swing. A more volatile stock gets a smaller position, so all positions carry roughly the same dollar risk regardless of the stock’s price or volatility.
atr = talib.ATR(frame.high, frame.low, frame.close, timeperiod=ATR_PERIOD).iloc[-1]
base_shares = math.trunc(AccountValue * RiskFactor / atr)
The base size is then reduced in certain situations:
if float(beta) > 2 and distance200 > DISTANCE200_THRESHOLD:
shares = int(0.6 * base_shares) # high-beta and extended above MA200: 60%
elif float(beta) > 2 and not highbeta:
shares = int(0.75 * base_shares) # high-beta in risk-off environment: 75%
elif int(mc) < SMALL_CAP_THRESHOLD:
shares = int(0.75 * base_shares) # small-cap stock: 75%
There is one more reduction: if the position value exceeds $2,000 but the short-term trend is already deteriorating (EMA8 crossed below EMA21), the size gets cut in half.
if (shares * lastPrice > 2000) and (lastPrice / ema21 - 1 < -0.01) and (ema8 < ema21):
shares = shares * 0.5
Stock Filters
Before a stock can enter the portfolio it has to pass a few checks. It must be trading above its MA100 — if it has already broken its medium-term trend it does not qualify, regardless of its ranking position.
sma100 = talib.SMA(frame['adjusted close'], timeperiod=STOCK_MA_FILTER).iloc[-1]
# stock is only eligible if lastPrice > sma100
The EMA8 and EMA21 are also calculated each week. They feed into the position-size reduction above and into the sell rule: if a position drops below the EMA21, it gets cut in half.
ema8 = talib.EMA(frame['adjusted close'], timeperiod=EMA_FAST).iloc[-1]
ema21 = talib.EMA(frame['adjusted close'], timeperiod=EMA_SLOW).iloc[-1]