more typos

This commit is contained in:
2026-07-03 08:54:41 -04:00
parent 572fd0e47d
commit 4dd5a4c2cf
5 changed files with 16 additions and 17 deletions
+8 -9
View File
@@ -33,11 +33,11 @@ Notebook numbering: **01** Data & market stats · **02** Factor diagnostics · *
| Term | Linear-algebra meaning | Notebooks | | Term | Linear-algebra meaning | Notebooks |
|------|------------------------|:---------:| |------|------------------------|:---------:|
| **Return** | Fractional price change: $r_{t} = p_t / p_{t-1} - 1$ | 01 | | **Return** | Fractional price change: $r_{t} = p_t / p_{t-1} - 1$ | 01 |
| **Return panel** $\mathbf{R}$ | Matrix $\mathbf{R}\in\mathbb{R}^{T\times N}$: months (rows) × stocks (columns); sparse (NaN where a stock didn't trade yet) | 0106 | | **Return panel** $\mathbf{R}$ | Matrix $\mathbf{R}\in\mathbb{R}^{T\times N}$: months (rows) $\times$ stocks (columns); sparse (NaN where a stock didn't trade yet) | 0106 |
| **Cross-section** | A row $r_t \in \mathbb{R}^{N_t}$ — all stock returns at one date $t$ | 01, 02, 03 | | **Cross-section** | A row $r_t \in \mathbb{R}^{N_t}$ — all stock returns at one date $t$ | 01, 02, 03 |
| **One stock's history** | A single column of $\mathbf{R}$ | 01 | | **One stock's history** | A single column of $\mathbf{R}$ | 01 |
| **Universe** | The set of columns we're allowed to hold — here the S&P 500 large-caps | 01 | | **Universe** | The set of columns we're allowed to hold — here the S&P 500 large-caps | 01 |
| **Sector** | A categorical partition of the column index set (Technology, Financials, …); one-hot form indicator matrix $\mathbf{D}$ | 01, 03 | | **Sector** | A categorical partition of the column index set (Technology, Financials, …); one-hot form $\rightarrow$ indicator matrix $\mathbf{D}$ | 01, 03 |
| **Dispersion** | Cross-sectional std dev — the spread of returns across a row of $\mathbf{R}$ | 01, 06 | | **Dispersion** | Cross-sectional std dev — the spread of returns across a row of $\mathbf{R}$ | 01, 06 |
| **Equal-weight index** | Mean of a row: $\bar{r}_t = \tfrac{1}{N_t}\mathbf{1}^\top r_t$ (portfolio with $w_i = 1/N_t$) | 01, 04 | | **Equal-weight index** | Mean of a row: $\bar{r}_t = \tfrac{1}{N_t}\mathbf{1}^\top r_t$ (portfolio with $w_i = 1/N_t$) | 01, 04 |
| **Survivorship bias** | Columns are a *non-random* sample: delisted/bankrupt names are missing, biasing returns upward | 01, 04 | | **Survivorship bias** | Columns are a *non-random* sample: delisted/bankrupt names are missing, biasing returns upward | 01, 04 |
@@ -49,11 +49,11 @@ Notebook numbering: **01** Data & market stats · **02** Factor diagnostics · *
| **Factor / signal** | Vector $f_t \in \mathbb{R}^{N_t}$ — one score per stock; we ask whether $f_t$ predicts $r_{t+1}$ | 02, 03, 04 | | **Factor / signal** | Vector $f_t \in \mathbb{R}^{N_t}$ — one score per stock; we ask whether $f_t$ predicts $r_{t+1}$ | 02, 03, 04 |
| **Momentum (12-1)** | Trailing 12-month return skipping the last month — the headline signal; persistent drift | 02, 03, 04 | | **Momentum (12-1)** | Trailing 12-month return skipping the last month — the headline signal; persistent drift | 02, 03, 04 |
| **Value / Quality / Low-vol** | Other candidate factor vectors (proxied by price data here) | 02, 03 | | **Value / Quality / Low-vol** | Other candidate factor vectors (proxied by price data here) | 02, 03 |
| **Information coefficient (IC)** | Cosine similarity $\cos\theta = \dfrac{f_t^\top r_{t+1}}{\|f_t\|\,\|r_{t+1}\|}$ (ranks Spearman IC) | 02, 03 | | **Information coefficient (IC)** | Cosine similarity $\cos\theta = \dfrac{f_t^\top r_{t+1}}{\|f_t\|\ \|r_{t+1}\|}$ (ranks $\rightarrow$ Spearman IC) | 02, 03 |
| **Information ratio (IR)** | Mean IC / std(IC) — a signal-to-noise ratio for the factor | 02, 04 | | **Information ratio (IR)** | Mean IC / std(IC) — a signal-to-noise ratio for the factor | 02, 04 |
| **Rank** | A permutation of $\{1,\dots,N\}$; makes factors comparable and robust to outliers | 02 | | **Rank** | A permutation of $\{1,\dots,N\}$; makes factors comparable and robust to outliers | 02 |
| **Walk-forward validation** | Split the sample into sub-windows and test stability of IC / performance across regimes | 02, 04 | | **Walk-forward validation** | Split the sample into sub-windows and test stability of IC / performance across regimes | 02, 04 |
| **Winsorize** | Clip extreme entries of $f$ at ±3σ to tame outliers | 03 | | **Winsorize** | Clip extreme entries of $f$ at $\pm 3\sigma$ to tame outliers | 03 |
| **Z-score** | Center each cross-section to mean 0, std 1 | 03 | | **Z-score** | Center each cross-section to mean 0, std 1 | 03 |
| **Sector neutralization** | Orthogonal projection $f_\perp = (I - P)f$ with $P = \mathbf{D}(\mathbf{D}^\top\mathbf{D})^{-1}\mathbf{D}^\top$ onto sector dummies | 03, 04 | | **Sector neutralization** | Orthogonal projection $f_\perp = (I - P)f$ with $P = \mathbf{D}(\mathbf{D}^\top\mathbf{D})^{-1}\mathbf{D}^\top$ onto sector dummies | 03, 04 |
| **Composite signal** | A linear combination $c_t = \sum_k w_k f_{k,\perp}$ of neutralized factor vectors | 03 | | **Composite signal** | A linear combination $c_t = \sum_k w_k f_{k,\perp}$ of neutralized factor vectors | 03 |
@@ -182,7 +182,7 @@ We pull the current S&P 500 constituents and download adjusted close prices. Thi
Key findings: Key findings:
* **Universe breadth** rises from ~385 to ~501 stocks over the sample — but the column set is fixed to *today's* constituents, so this counts how many of today's survivors had price data in month $t$. The matrix isn't truly "getting wider"; its survivor-only columns fill in over time. * **Universe breadth** rises from ~385 to ~501 stocks over the sample — but the column set is fixed to *today's* constituents, so this counts how many of today's survivors had price data in month $t$. The matrix isn't truly "getting wider"; its survivor-only columns fill in over time.
* Average monthly cross-sectional return dispersion ~7.5% (the spread across a row of $\mathbf{R}$). * Average monthly cross-sectional return dispersion ~7.5% (the spread across a row of $\mathbf{R}$).
* Equal-weight universe Sharpe (rf=0) 0.98 over the full period. * Equal-weight universe Sharpe (rf=0) $\approx$ 0.98 over the full period.
* Sector composition spans 11 GICS sectors, dominated by Technology, Financials, and Healthcare. * Sector composition spans 11 GICS sectors, dominated by Technology, Financials, and Healthcare.
### 2. Factor Analysis and Diagnostics ### 2. Factor Analysis and Diagnostics
@@ -214,9 +214,9 @@ Notebook 02 established momentum as the only factor with positive IC. This noteb
3. **Sector-neutralizes** via orthogonal projection: $f_\perp = (\mathbf{I} - \mathbf{P})f$ with $\mathbf{P} = \mathbf{D}(\mathbf{D}^\top\mathbf{D})^{-1}\mathbf{D}^\top$ 3. **Sector-neutralizes** via orthogonal projection: $f_\perp = (\mathbf{I} - \mathbf{P})f$ with $\mathbf{P} = \mathbf{D}(\mathbf{D}^\top\mathbf{D})^{-1}\mathbf{D}^\top$
4. **Compares** momentum-only vs. a 4-factor equal-weight composite 4. **Compares** momentum-only vs. a 4-factor equal-weight composite
Sector neutralization slightly improves momentum (IR 0.11 raw 0.15 neutralized). Combining momentum with two negative-IC factors (value, lowvol) and one near-zero factor (quality) **dilutes** the signal — the 4-factor composite has negative IC. This is the linear-algebra intuition made empirical: adding vectors that point in the wrong direction moves the sum away from the target. Sector neutralization slightly improves momentum (IR 0.11 raw $\rightarrow$ 0.15 neutralized). Combining momentum with two negative-IC factors (value, lowvol) and one near-zero factor (quality) **dilutes** the signal — the 4-factor composite has negative IC. This is the linear-algebra intuition made empirical: adding vectors that point in the wrong direction moves the sum away from the target.
**The headline signal is momentum-only, sector-neutralized** saved as `momentum_signal.csv`. This is what the backtest trades. **The headline signal is momentum-only, sector-neutralized** $\rightarrow$ saved as `momentum_signal.csv`. This is what the backtest trades.
--- ---
@@ -241,7 +241,7 @@ A portfolio is a weight vector $w$. We form a top-decile long-only portfolio at
| EW Universe | 15.9% | 0.95 | -47% | 1.27 | | EW Universe | 15.9% | 0.95 | -47% | 1.27 |
| Long-Short (net) | -0.7% | -0.04 | -70% | — | | Long-Short (net) | -0.7% | -0.04 | -70% | — |
| Portfolio | FF 4-factor alpha (ann.) | t-stat | MKT β | MOM β | | Portfolio | FF 4-factor alpha (ann.) | t-stat | MKT $\beta$ | MOM $\beta$ |
|-----------|-------------------------:|-------:|------:|------:| |-----------|-------------------------:|-------:|------:|------:|
| **Long-Only** | **+5.95%** | **3.95** | 1.19 | 0.25 | | **Long-Only** | **+5.95%** | **3.95** | 1.19 | 0.25 |
| Long-Short | -2.95% | -1.41 | 0.15 | 0.91 | | Long-Short | -2.95% | -1.41 | 0.15 | 0.91 |
@@ -295,7 +295,6 @@ The momentum long-only portfolio's risk is **~90.7% systematic** and **~9.3% idi
* **No intraday execution modeling.** Transaction costs are a flat 5 bps. Real slippage depends on order size, liquidity, and volatility. * **No intraday execution modeling.** Transaction costs are a flat 5 bps. Real slippage depends on order size, liquidity, and volatility.
* **Monthly rebalance only.** Daily/weekly rebalancing might capture different signals but would dramatically increase turnover. * **Monthly rebalance only.** Daily/weekly rebalancing might capture different signals but would dramatically increase turnover.
* **Momentum crash risk.** The 20062011 walk-forward window shows negative active return, driven by the 200809 momentum crash. A crash-protection overlay (e.g. volatility scaling) would improve robustness. * **Momentum crash risk.** The 20062011 walk-forward window shows negative active return, driven by the 200809 momentum crash. A crash-protection overlay (e.g. volatility scaling) would improve robustness.
* **Sector neutralization only.** A full Barra-style risk model would neutralize against more risk factors (size, beta, liquidity).
--- ---
@@ -20,7 +20,7 @@
"| Term | Meaning |\n", "| Term | Meaning |\n",
"|------|---------|\n", "|------|---------|\n",
"| **Return** | Fractional price change: $r_{t} = p_t / p_{t-1} - 1$ |\n", "| **Return** | Fractional price change: $r_{t} = p_t / p_{t-1} - 1$ |\n",
"| **Return panel** $\\mathbf{R}$ | The $T \\times N$ matrix of monthly returns — months (rows) × stocks (columns); the project's central object |\n", "| **Return panel** $\\mathbf{R}$ | The $T \\times N$ matrix of monthly returns — months (rows) $\\times$ stocks (columns); the project's central object |\n",
"| **Cross-section** | A row of $\\mathbf{R}$ — all stocks at one date |\n", "| **Cross-section** | A row of $\\mathbf{R}$ — all stocks at one date |\n",
"| **One stock's history** | A column of $\\mathbf{R}$ |\n", "| **One stock's history** | A column of $\\mathbf{R}$ |\n",
"| **Universe** | The set of stocks (columns) we're allowed to hold — here the S&P 500 |\n", "| **Universe** | The set of stocks (columns) we're allowed to hold — here the S&P 500 |\n",
@@ -372,7 +372,7 @@
"id": "5659b941", "id": "5659b941",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Note that we added a sample size filter with `mask.sum() < 20`. The `mask` identifies stocks that have both a valid factor score and a valid forward return in a given month, and `mask.sum()` counts how many usable pairs you actually have. The `< 20` threshold prevents the code from computing a correlation on a tiny sample, which is statistically meaningless and numerically unstable (e.g., Spearman correlation on 2 stocks is always exactly ±1). If you don't gate this, early-history months, mass delistings, or data gaps inject garbage ±1.0 values into your IC time series, which then contaminate every downstream statistic like the mean IC, Information Ratio, and decay curves. Setting a floor of 20 filters out those degenerate months while retaining enough valid data to produce a reliable signal.\n", "Note that we added a sample size filter with `mask.sum() < 20`. The `mask` identifies stocks that have both a valid factor score and a valid forward return in a given month, and `mask.sum()` counts how many usable pairs you actually have. The `< 20` threshold prevents the code from computing a correlation on a tiny sample, which is statistically meaningless and numerically unstable (e.g., Spearman correlation on 2 stocks is always exactly $\\pm$1). If you don't gate this, early-history months, mass delistings, or data gaps inject garbage $\\pm 1.0$ values into your IC time series, which then contaminate every downstream statistic like the mean IC, Information Ratio, and decay curves. Setting a floor of 20 filters out those degenerate months while retaining enough valid data to produce a reliable signal.\n",
"\n", "\n",
"We do this sort of masking throughout." "We do this sort of masking throughout."
] ]
@@ -19,7 +19,7 @@
"\n", "\n",
"| Term | Meaning |\n", "| Term | Meaning |\n",
"|------|---------|\n", "|------|---------|\n",
"| **Winsorization** | Clipping extreme values at ±$k$ standard deviations — a soft truncation to limit outlier influence |\n", "| **Winsorization** | Clipping extreme values at $\\pm k$ standard deviations — a soft truncation to limit outlier influence |\n",
"| **Z-scoring** | Centering to mean 0 and scaling to std 1, so each factor vector lives on the same scale |\n", "| **Z-scoring** | Centering to mean 0 and scaling to std 1, so each factor vector lives on the same scale |\n",
"| **Neutralization** | Projecting out unwanted components (sector) — an orthogonal projection onto a complement subspace |\n", "| **Neutralization** | Projecting out unwanted components (sector) — an orthogonal projection onto a complement subspace |\n",
"| **Composite signal** | A linear combination $c_t = \\sum_k w_k f_{k,\\perp}$ of neutralized factor vectors |\n", "| **Composite signal** | A linear combination $c_t = \\sum_k w_k f_{k,\\perp}$ of neutralized factor vectors |\n",
@@ -125,7 +125,7 @@
"source": [ "source": [
"## Winsorization and Z-Scoring\n", "## Winsorization and Z-Scoring\n",
"\n", "\n",
"**Winsorization** is a statistical technique for limiting the influence of extreme outliers in a dataset by capping them at a specified threshold rather than removing them entirely. Raw factor exposures can have extreme outliers — a stock that returned 500% in the trailing year, for instance. We **Winsorize** at ±3 standard deviations, so that we don't have to drop them.\n", "**Winsorization** is a statistical technique for limiting the influence of extreme outliers in a dataset by capping them at a specified threshold rather than removing them entirely. Raw factor exposures can have extreme outliers — a stock that returned 500% in the trailing year, for instance. We **Winsorize** at $\\pm 3$ standard deviations, so that we don't have to drop them.\n",
"\n", "\n",
"After winsorization we **z-score** cross-sectionally: for each date $t$, the factor vector $f_t$ is transformed to\n", "After winsorization we **z-score** cross-sectionally: for each date $t$, the factor vector $f_t$ is transformed to\n",
"$$\\tilde{f}_t = (f_t - \\bar{f}_t) / \\text{std}(f_t),$$\n", "$$\\tilde{f}_t = (f_t - \\bar{f}_t) / \\text{std}(f_t),$$\n",
@@ -268,7 +268,7 @@
"id": "9740141a", "id": "9740141a",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Much like in Notebook 02, we note that we added a sample size filter with `mask.sum() < 30`. The `mask` identifies stocks that have both a valid factor score and a valid forward return in a given month, and `mask.sum()` counts how many usable pairs you actually have. The `< 30` threshold prevents the code from computing a correlation on a tiny sample, which is statistically meaningless and numerically unstable (e.g., Spearman correlation on 2 stocks is always exactly ±1). If you don't gate this, early-history months, mass delistings, or data gaps inject garbage ±1.0 values into your IC time series, which then contaminate every downstream statistic like the mean IC, Information Ratio, and decay curves. Setting a floor of 30 filters out those degenerate months while retaining enough valid data to produce a reliable signal.\n", "Much like in Notebook 02, we note that we added a sample size filter with `mask.sum() < 30`. The `mask` identifies stocks that have both a valid factor score and a valid forward return in a given month, and `mask.sum()` counts how many usable pairs you actually have. The `< 30` threshold prevents the code from computing a correlation on a tiny sample, which is statistically meaningless and numerically unstable (e.g., Spearman correlation on 2 stocks is always exactly $\\pm$1). If you don't gate this, early-history months, mass delistings, or data gaps inject garbage $\\pm 1.0$ values into your IC time series, which then contaminate every downstream statistic like the mean IC, Information Ratio, and decay curves. Setting a floor of 30 filters out those degenerate months while retaining enough valid data to produce a reliable signal.\n",
"\n", "\n",
"We do this sort of masking throughout." "We do this sort of masking throughout."
] ]
+3 -3
View File
@@ -787,7 +787,7 @@
"\n", "\n",
"### When Sortino > Sharpe\n", "### When Sortino > Sharpe\n",
"\n", "\n",
"A strategy with occasional large positive surprises will have Sortino noticeably higher than Sharpe. A strategy with symmetric volatility (gains and losses of similar magnitude) will have Sortino Sharpe. Big gap between them tells you the return distribution is positively skewed.\n", "A strategy with occasional large positive surprises will have Sortino noticeably higher than Sharpe. A strategy with symmetric volatility (gains and losses of similar magnitude) will have Sortino $\\approx$ Sharpe. Big gap between them tells you the return distribution is positively skewed.\n",
"\n", "\n",
"---\n", "---\n",
"\n", "\n",
@@ -808,8 +808,8 @@
"$$V_t = \\prod_{s=1}^{t}(1 + r_{p,s}) = (1+r_1)(1+r_2)\\cdots(1+r_t)$$\n", "$$V_t = \\prod_{s=1}^{t}(1 + r_{p,s}) = (1+r_1)(1+r_2)\\cdots(1+r_t)$$\n",
"\n", "\n",
"This is **compounding**. Start with $\\$1$. Each month, multiply by $(1 + r_s)$:\n", "This is **compounding**. Start with $\\$1$. Each month, multiply by $(1 + r_s)$:\n",
"- Return $+5\\%$ multiply by $1.05$\n", "- Return $+5\\%$ $\\rightarrow$ multiply by $1.05$\n",
"- Return $-3\\%$ multiply by $0.97$\n", "- Return $-3\\%$ $\\rightarrow$ multiply by $0.97$\n",
"\n", "\n",
"$V_t$ is the value of your $\\$1$ at the end of month $t$. If $V_t = 1.50$, your $\\$1$ has grown to $\\$1.50$.\n", "$V_t$ is the value of your $\\$1$ at the end of month $t$. If $V_t = 1.50$, your $\\$1$ has grown to $\\$1.50$.\n",
"\n", "\n",