The Model in One Sentence
Each strategy is modelled as a $10,000 equal-weight portfolio that rebalances at every snapshot date — selling stocks that left the screen and buying stocks that entered it, with all positions sized equally.
Step-by-Step: What Happens at Each Snapshot
At each snapshot date, the list of stocks passing the strategy's filter criteria is recorded with their closing prices. This is the raw input.
Each stock from the previous snapshot falls into one of three categories: Retained — still in the screen, Exited — dropped off the screen, or New — just entered the screen.
Retained stocks use actual snapshot-to-snapshot price change. Exited stocks are assumed to have had 0% price change since they were last in the screen — the sell fee is the only cost. New entrants are added at the current snapshot price and start earning returns from the next period onward.
The portfolio value at the start of each period is divided equally across all positions in that period's starting screen. Every stock gets the same dollar allocation.
The ending portfolio value from one period becomes the starting value for the next. Returns compound.
Position Sizing Formula
period_return = Σ (position_size × (1 + stock_return)) − portfolio_value
# stock_return = (exit_price − entry_price) / entry_price for retained
# stock_return = −FEE_PCT for exited
# stock_return = not counted until next period for new entrants
Transaction Fee
A one-way sell fee is applied whenever a stock exits the screen. In the Python script this is controlled by a single constant:
The fee simulates the friction of closing a position: broker commissions, bid-ask spread, and minor market impact. With fee enabled, an exited stock earns −0.1% rather than exactly 0%.
On low-turnover screens (90%+ retention) the impact is negligible. On high-turnover screens like Piotroski F-Score — which replaced 75% of its portfolio in Period 1 — the drag compounds meaningfully over time.
Benchmarks
Two benchmarks are tracked alongside the strategies: the S&P 500 (^GSPC) and the NASDAQ Composite (^IXIC). Both are modelled the same way — $10,000 starting value, compounded using the actual index closing prices at each snapshot date. No dividends are included.
Benchmark prices are hardcoded in the script from the actual closing values on each snapshot date. This makes the analysis fully reproducible with no dependency on live data feeds.
Known Limitations
Exit price assumption. Exited stocks are assumed to have had 0% price movement since the last snapshot. In reality, they may have risen or fallen significantly before being sold. This is a conservative assumption — over many periods it tends to average out, but for any single period it adds noise.
Execution timing. The model assumes you can buy and sell exactly at the snapshot closing price. In practice, trades execute during the day with slippage. The fee partially compensates for this, but precise execution is not modelled.
New entrant lag. Stocks that enter the screen only start earning returns from the next snapshot onward. If a new entrant moves strongly in the period it joined, that return is not captured. This means strategies with high turnover slightly understate potential performance on the entry side.
Equal weight vs optimal weight. Allocating equally across all positions ignores differences in conviction, liquidity, and volatility. A more sophisticated model might size positions based on market cap, volatility, or signal strength. Equal weight is chosen for its simplicity and reproducibility.
Worked Example: S2 · Schloss Dividend, Period 1
The following example walks through the full calculation for the Schloss Dividend screen from the March 10 → April 22, 2026 period. This is real data from the journal.
Starting Value
$10,000
Positions
11
Size Each
$909.09
Period
43 days
The Mar 10 screen contained 11 stocks. Position size: $10,000 ÷ 11 = $909.09 each. By April 22, 8 stocks were retained, 3 had exited the screen, and 2 new entrants joined.
| Ticker | Status | Entry (Mar 10) | Exit (Apr 22) | Return | End Value |
|---|---|---|---|---|---|
| GGB | Retained | $3.58 | $4.30 | +20.11% | $1,091.82 |
| TX | Retained | $39.55 | $42.77 | +8.14% | $982.73 |
| VSNT | Retained | $36.54 | $39.65 | +8.51% | $986.36 |
| CHRD | Retained | $123.22 | $131.13 | +6.42% | $967.27 |
| DAC | Retained | $112.25 | $115.02 | +2.47% | $931.82 |
| MTH | Retained | $68.03 | $69.49 | +2.15% | $928.18 |
| ATHM | Retained | $19.61 | $18.70 | −4.64% | $867.27 |
| MOS | Retained | $26.92 | $24.20 | −10.10% | $817.18 |
| REXR | Exited | $35.66 | — sold | −0.1%* | $908.18 |
| ESNT | Exited | $59.29 | — sold | −0.1%* | $908.18 |
| RDN | Exited | $33.53 | — sold | −0.1%* | $908.18 |
| Portfolio Total | +2.97% | $10,297.17 | |||
* Exited stocks: no price change assumed; −0.1% sell fee applied. | New entrants THO and JOYY joined at the Apr 22 snapshot and are included in Period 2 calculations only.
The Calculation in Full
# retained stocks — actual price change
GGB contribution = $909.09 × (1 + 0.2011) = $1,091.55
MOS contribution = $909.09 × (1 − 0.1011) = $817.18
# ... (same for all 8 retained stocks)
# exited stocks — sell fee only
REXR contribution = $909.09 × (1 − 0.0010) = $908.18
# ... (same for ESNT, RDN)
portfolio_end = sum of all 11 contributions = $10,296.72
period_return = ($10,296.72 − $10,000) / $10,000 = +2.97%
The $10,296.72 then becomes the starting value for Period 2, where it is divided equally across the 10 stocks in the Apr 22 screen (8 retained + 2 new entrants: THO, JOYY).