587 lines
61 KiB
Plaintext
587 lines
61 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d273a5e0",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Factor Construction and Composite Signal\n",
|
|
"\n",
|
|
"## Purpose\n",
|
|
"\n",
|
|
"Notebook 02 established that momentum was the only factor with positive, persistant IC. The other three have negative or insignicant IC with our price-based proxies.\n",
|
|
"\n",
|
|
"In this notebook, our goals are the following.\n",
|
|
"1. **Build the headline signal**: sector-neutralized momentum. We winsoirze, z-score, and project orthogonal to the sector subspace. The same pipeline that would apply to any factor, but applied to the one that works. \n",
|
|
"2. **Tests whether combining helps**: we build a 4-factor equal-weight composite and compare its IC to momentum alone. If the composite IC is worse (it is), that confirms the decision to trade based on momentum alone is the right one. \n",
|
|
"\n",
|
|
"### Terms uesd in this notebook. \n",
|
|
"\n",
|
|
"| Term | Meaning |\n",
|
|
"|------|---------|\n",
|
|
"| **Winsorization** | Clipping extreme values at ±$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",
|
|
"| **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",
|
|
"| **Sector matrix** | A matrix $\\mathbf{D} \\in \\{0,1\\}^{N \\times K}$ encoding sector membership |\n",
|
|
"| **Factor / signal** ↻ | The vector $f_t$ we are orthogonalizing |\n",
|
|
"| **Momentum** ↻ | The headline factor we build the traded signal from |\n",
|
|
"| **Information coefficient (IC)** ↻ | Used to compare momentum-only vs. the 4-factor composite. Recall that it is the Spearman rank correlation between $f_t$ and $r_{t+1}$ — cosine similarity of rank vectors |\n",
|
|
"| **Value / Quality / Low-vol** ↻ | The other factors entering the (rejected) composite |\n",
|
|
"| **Sector** ↻ | The categorical partition we neutralize against |\n",
|
|
"| **Return panel** $\\mathbf{R}$ ↻ | The return matrix the factors are computed from |\n",
|
|
"\n",
|
|
"## Outputs\n",
|
|
"\n",
|
|
"- `momentum_signal.csv` — the **momentum-only** sector-neutralized signal (this is what will backtest trades)\n",
|
|
"- `composite_4factor.csv` — the 4-factor equal weight composite (kept for comparison)\n",
|
|
"- Per-factor neutralized exposures\n",
|
|
"\n",
|
|
"## Notebook Structure\n",
|
|
"1. [Setup and Imports](#setup-and-imports)\n",
|
|
"2. [Winsorization and Z-Scoring](#winsorization-and-z-scoring)\n",
|
|
"3. [Sector and Size Neutralization](#sector-and-size-neutralization)\n",
|
|
"4. [Composite Assembly and Comparison](#composite-assembly-and-comparison)\n",
|
|
"5. [IC Comparison: Momentum vs. Composite](#ic-comparison-momentum-vs-composite)\n",
|
|
"6. [Conclusion](#conclusion)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "75cbb165",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n",
|
|
"## Setup and Imports"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "c72adc67",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Setup and imports\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"import pandas as pd\n",
|
|
"import numpy as np\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"import seaborn as sns\n",
|
|
"import os\n",
|
|
"from scipy.stats import spearmanr\n",
|
|
"\n",
|
|
"pd.set_option('display.max_columns', None)\n",
|
|
"pd.set_option('display.max_rows', 100)\n",
|
|
"\n",
|
|
"palette = ['steelblue', 'coral', 'seagreen']\n",
|
|
"\n",
|
|
"os.makedirs('../data/processed', exist_ok=True)\n",
|
|
"os.makedirs('../images/03_factor_construction', exist_ok=True)\n",
|
|
"\n",
|
|
"RANDOM_STATE = 3"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "0090dab7",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Loaded 4 factors\n",
|
|
"Returns: (251, 501)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Load factor exposures, returns, sectors\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"factor_names = ['momentum', 'value', 'quality', 'lowvol']\n",
|
|
"factor_dict = {n: pd.read_csv(f'../data/processed/factor_{n}.csv', index_col=0, parse_dates=True) for n in factor_names}\n",
|
|
"df_returns = pd.read_csv('../data/processed/returns_monthly.csv', index_col=0, parse_dates=True)\n",
|
|
"df_sector = pd.read_csv('../data/processed/sector_mapping.csv')\n",
|
|
"\n",
|
|
"# Build sector lookup\n",
|
|
"sector_map = dict(zip(df_sector['ticker'], df_sector['sector']))\n",
|
|
"\n",
|
|
"print(f\"Loaded {len(factor_names)} factors\")\n",
|
|
"print(f\"Returns: {df_returns.shape}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "46a37025",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Winsorization and Z-Scoring\n",
|
|
"\n",
|
|
"**Winsorization** is a statiscal technique for limiting the influence of extreme outlies 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",
|
|
"\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",
|
|
"giving it mean 0 and standard deviation 1. This normalizes all factors to a common scale so they can be linearly combined without one dominating due to unit choices."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "83a54dbc",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"momentum: z-scores, shape=(251, 501)\n",
|
|
"value: z-scores, shape=(251, 501)\n",
|
|
"quality: z-scores, shape=(251, 501)\n",
|
|
"lowvol: z-scores, shape=(251, 501)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Winsorize and z-score\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"def winsorize(series, sigma=3):\n",
|
|
" \"\"\"Clip at ±sigma standard deviations around the mean.\"\"\"\n",
|
|
" mu = series.mean()\n",
|
|
" s = series.std()\n",
|
|
" if s == 0 or pd.isna(s):\n",
|
|
" return series\n",
|
|
" return series.clip(lower=mu - sigma*s, upper=mu + sigma * s)\n",
|
|
"\n",
|
|
"def zscore(series):\n",
|
|
" \"\"\"Cross-sectional z-score.\"\"\"\n",
|
|
" mu = series.mean()\n",
|
|
" s = series.std()\n",
|
|
" if s == 0 or pd.isna(s):\n",
|
|
" return series * 0\n",
|
|
" return (series - mu)/s\n",
|
|
"\n",
|
|
"factor_z = {}\n",
|
|
"for name, df_f in factor_dict.items():\n",
|
|
" df_w = df_f.apply(winsorize, axis=1)\n",
|
|
" df_z = df_w.apply(zscore, axis=1)\n",
|
|
" factor_z[name] = df_z\n",
|
|
" print(f\"{name}: z-scores, shape={df_z.shape}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "112a7744",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Sector and Size Neutralization\n",
|
|
"\n",
|
|
"A raw momentum score will have a different distribution in Technology than in Utilities. If we just z-score and combine, the composite will inherit those sector tilts. To get a cleaner signal, we project out the sector component. \n",
|
|
"\n",
|
|
"To **sector-neutralize** the factor, we project it onto the sector indicator matrix and keep only the **residual** — the component orthogonal to all sector directions. After this, the signal has *no net exposure* to any sector: it can't be explained by \"being long tech\" or \"short energy.\" What remains is pure cross-sectional momentum *within* each sector.\n",
|
|
"\n",
|
|
"The term comes from quant finance (\"neutralize the factor against X\"), but mathematically it's just an **orthogonal projection onto a complement subspace**. We subtract out the part of the vector that lives in the sector span, leaving only what's left over.\n",
|
|
"\n",
|
|
"From the linear algebraists' perspective, the sector matrix $D \\in \\{0,1\\}^{N \\times K}$ spans a subspace $S \\subseteq \\mathbb{R}^N$. The orthogonal projection onto $S$ is:\n",
|
|
"$$ P = D(D^TD)^{-1}D^T. $$\n",
|
|
"This is the familiar ordinary least-squares projection matrix, which is symmetric and idempotent. Applying to to a factor vector $f$ gives the component of $f$ which lies in the sector subspace:\n",
|
|
"$$ \\hat{f} = Pf. $$\n",
|
|
"The **neutralized factor** is the residual, i.e., the component orthogonal to $S$:\n",
|
|
"$$ f_\\perp = (I - P)f = f - \\hat{f}. $$\n",
|
|
"This is exactly linear squares regression of $f$ on sectors, returning the residuals. The residual is orthogonal to the sector subspace by construction: $\\langle f_\\perp, \\hat{f} \\rangle = 0$. \n",
|
|
"\n",
|
|
"For size, we use the log of tralining market cap proxied by price $\\times 1$ (best we could do with out data). This is a weak proxy — a real implementation would use actual market cap."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "b7ce2018",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Neutralizing momentum...\n",
|
|
"Neutralizing value...\n",
|
|
"Neutralizing quality...\n",
|
|
"Neutralizing lowvol...\n",
|
|
"Sector neutralization complete.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Sector neutralization via cross-sectional regression on dummies\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"def neutralize_against_sector(factor_row, sector_map):\n",
|
|
" \"\"\"Regress factor on sector dummies, return residuals.\"\"\"\n",
|
|
" tickers = factor_row.index\n",
|
|
" sectors = pd.Series([sector_map.get(t, 'Unknown') for t in tickers], index=tickers)\n",
|
|
"\n",
|
|
" # One-hot encode\n",
|
|
" indicators = pd.get_dummies(sectors, drop_first=True).astype(float)\n",
|
|
" indicators['intercept'] = 1.0\n",
|
|
"\n",
|
|
" # Mask valid\n",
|
|
" mask = factor_row.notna()\n",
|
|
" if mask.sum() < 30:\n",
|
|
" return factor_row\n",
|
|
"\n",
|
|
" y = factor_row[mask].values\n",
|
|
" X = indicators.loc[mask].values\n",
|
|
"\n",
|
|
" # OLS via least squares\n",
|
|
" beta, _, _, _ = np.linalg.lstsq(X,y)\n",
|
|
" resid = y - X @ beta\n",
|
|
"\n",
|
|
" out = pd.Series(np.nan, index=factor_row.index)\n",
|
|
" out[mask] = resid\n",
|
|
" return out\n",
|
|
"\n",
|
|
"factor_neut = {}\n",
|
|
"for name, df_z in factor_z.items():\n",
|
|
" print(f\"Neutralizing {name}...\")\n",
|
|
" df_neut = df_z.apply(lambda row: neutralize_against_sector(row, sector_map), axis=1)\n",
|
|
" # Re-z-score the residuals so they're back on a common scale\n",
|
|
" df_neut = df_neut.apply(zscore, axis=1)\n",
|
|
" factor_neut[name] = df_neut\n",
|
|
"\n",
|
|
"print(\"Sector neutralization complete.\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "9740141a",
|
|
"metadata": {},
|
|
"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 20 filters out those degenerate months while retaining enough valid data to produce a reliable signal.\n",
|
|
"\n",
|
|
"We do this sort of masking throughout."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b4204816",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Composite Assembly and Comparison\n",
|
|
"\n",
|
|
"We construct two signals.\n",
|
|
"1. **Momentum-only**: the sector-neutralized momentum vector $f_{m,\\perp}$, z-scored. This is the signal that we will use in the backtest. \n",
|
|
"2. **4-factor composite (just for comparison)**: a linear combination $$ c_t = \\frac{1}{4} \\sum_{k=1}^4 f_{k, \\perp}(t). $$ If the composite's IC beats momentum (it doesn't), combining helps. If it's worse (it is), then the negative IC factors dilute the signal.\n",
|
|
"\n",
|
|
"\n",
|
|
"The linear-algebra operation is the same in both cases: a linear combination of vectors in the sector-neutralized subspace. The only difference is the weight vector: \n",
|
|
"$$ \\begin{pmatrix} 1 \\\\ 0 \\\\ 0 \\\\ 0 \\end{pmatrix} \\text{ vs } \\begin{pmatrix} \\frac{1}{4} \\\\ \\frac{1}{4} \\\\ \\frac{1}{4} \\\\ \\frac{1}{4} \\end{pmatrix}. $$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "792e547e",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Momentum-only signal: (251, 501)\n",
|
|
"4-factor composite: (251, 501)\n",
|
|
"\n",
|
|
"Our main signal will be momentum-only. The 4-factor composite is kept for comparison.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Build momentum-only signal and 4-factor composite\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"# Momentum only\n",
|
|
"df_momentum_signal = factor_neut['momentum'].apply(zscore, axis=1)\n",
|
|
"print(f\"Momentum-only signal: {df_momentum_signal.shape}\")\n",
|
|
"\n",
|
|
"# 4-factor equal-weight composite\n",
|
|
"common_dates = factor_neut['momentum'].index\n",
|
|
"common_tickers = factor_neut['momentum'].columns\n",
|
|
"\n",
|
|
"for name in factor_names:\n",
|
|
" common_dates = common_dates.intersection(factor_neut[name].index)\n",
|
|
" common_tickers = common_tickers.intersection(factor_neut[name].columns)\n",
|
|
"\n",
|
|
"df_composite_4f = pd.DataFrame(0.0, index=common_dates, columns=common_tickers)\n",
|
|
"count_df = pd.DataFrame(0, index=common_dates, columns=common_tickers)\n",
|
|
"\n",
|
|
"for name in factor_names:\n",
|
|
" df_f = factor_neut[name].loc[common_dates, common_tickers]\n",
|
|
" df_composite_4f = df_composite_4f.add(df_f.fillna(0))\n",
|
|
" count_df = count_df.add(df_f.notna().astype(int))\n",
|
|
"\n",
|
|
"df_composite_4f = df_composite_4f / count_df.replace(0, np.nan)\n",
|
|
"df_composite_4f[count_df < 3] = np.nan\n",
|
|
"df_composite_4f = df_composite_4f.apply(zscore, axis=1)\n",
|
|
"\n",
|
|
"print(f\"4-factor composite: {df_composite_4f.shape}\")\n",
|
|
"print(f\"\\nOur main signal will be momentum-only. The 4-factor composite is kept for comparison.\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f8809b2e",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Composite vs. Single Factors"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "25e7a046",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"IC Comparison: Neutralized Factors, Momentum-Only, and 4-Factor Composite\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<div>\n",
|
|
"<style scoped>\n",
|
|
" .dataframe tbody tr th:only-of-type {\n",
|
|
" vertical-align: middle;\n",
|
|
" }\n",
|
|
"\n",
|
|
" .dataframe tbody tr th {\n",
|
|
" vertical-align: top;\n",
|
|
" }\n",
|
|
"\n",
|
|
" .dataframe thead th {\n",
|
|
" text-align: right;\n",
|
|
" }\n",
|
|
"</style>\n",
|
|
"<table border=\"1\" class=\"dataframe\">\n",
|
|
" <thead>\n",
|
|
" <tr style=\"text-align: right;\">\n",
|
|
" <th></th>\n",
|
|
" <th>mean</th>\n",
|
|
" <th>std</th>\n",
|
|
" <th>ic_ir</th>\n",
|
|
" </tr>\n",
|
|
" </thead>\n",
|
|
" <tbody>\n",
|
|
" <tr>\n",
|
|
" <th>momentum</th>\n",
|
|
" <td>0.0068</td>\n",
|
|
" <td>0.1576</td>\n",
|
|
" <td>0.1505</td>\n",
|
|
" </tr>\n",
|
|
" <tr>\n",
|
|
" <th>value</th>\n",
|
|
" <td>-0.0198</td>\n",
|
|
" <td>0.1344</td>\n",
|
|
" <td>-0.5111</td>\n",
|
|
" </tr>\n",
|
|
" <tr>\n",
|
|
" <th>quality</th>\n",
|
|
" <td>0.0013</td>\n",
|
|
" <td>0.1597</td>\n",
|
|
" <td>0.0282</td>\n",
|
|
" </tr>\n",
|
|
" <tr>\n",
|
|
" <th>lowvol</th>\n",
|
|
" <td>-0.0169</td>\n",
|
|
" <td>0.1772</td>\n",
|
|
" <td>-0.3298</td>\n",
|
|
" </tr>\n",
|
|
" <tr>\n",
|
|
" <th>momentum_only</th>\n",
|
|
" <td>0.0068</td>\n",
|
|
" <td>0.1576</td>\n",
|
|
" <td>0.1505</td>\n",
|
|
" </tr>\n",
|
|
" <tr>\n",
|
|
" <th>4factor_composite</th>\n",
|
|
" <td>-0.0078</td>\n",
|
|
" <td>0.1688</td>\n",
|
|
" <td>-0.1593</td>\n",
|
|
" </tr>\n",
|
|
" </tbody>\n",
|
|
"</table>\n",
|
|
"</div>"
|
|
],
|
|
"text/plain": [
|
|
" mean std ic_ir\n",
|
|
"momentum 0.0068 0.1576 0.1505\n",
|
|
"value -0.0198 0.1344 -0.5111\n",
|
|
"quality 0.0013 0.1597 0.0282\n",
|
|
"lowvol -0.0169 0.1772 -0.3298\n",
|
|
"momentum_only 0.0068 0.1576 0.1505\n",
|
|
"4factor_composite -0.0078 0.1688 -0.1593"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlcelbwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAcG9JREFUeJzt3XlcVPXi//H3ACJuIAIqsrgjLmVuaZpbWlruW5ZpmWZmeq1ri9nqktelLG9atpiVaZpZ7l4tTDIrMszcV9wAQRREUBQRzu+PX8y3CbQBGY8HX8/Hg8dDzuecOe85jAPvOZvNMAxDAAAAAACgyLmZHQAAAAAAgOKK0g0AAAAAgItQugEAAAAAcBFKNwAAAAAALkLpBgAAAADARSjdAAAAAAC4CKUbAAAAAAAXoXQDAAAAAOAilG4AAAAAAFyE0g0AwHU2atQolS9f3uwYKGL8XAtn6tSpstlsSk1NtU+7lm1ZkGXzW7cr8NoAbm6UbgCF5uHhIZvNpm7duuU7/tVXX8lms8lms2nx4sXXPV9BeHh4qGfPnnmmX7hwQe+8845at24tX19feXl5qVatWurWrZu++eYbXb58udDrKy7briBOnz4tm82mN9980+woTvnxxx/Vu3dvBQYGytPTU5UrV1bv3r21adMms6PdMKKjozVo0CBVq1ZNXl5eCggIUNOmTfXKK6/o+PHjZscz3WOPPSZ/f3+zYxSJl19+WTabTfv27TM7SrGVu43z+3rssceKdF2HDh2SzWbT3Llzi/RxAeRF6QZwTcqUKaN169bp5MmTecY+/fRTlSlTxpRcReHEiRNq0aKFpk6dqsGDB2vPnj1KS0vT6tWrFR4erv79+2vdunWFfvzivO2Kg8mTJ6tt27aqUqWKfvjhB507d06bN29WcHCw2rVrp8mTJ5sd0XRvvPGGmjdvLjc3N33zzTc6c+aM9u/frxdeeEFff/21BgwYYHbE62r27Nku32N6s7iWbVkcfg579+6VYRgOX5RjwLoo3QCuyT333KNSpUppwYIFDtMTEhK0fv169e3b17Rs1+r+++/X8ePH9dNPP2no0KH2vZ3h4eF644039N1336ls2bKFfvzivO2sbuXKlXr55Zc1duxYzZ49W2FhYfL09FStWrX0zjvvaOzYsXr55Ze1atUqs6OaZtWqVXr++ef13HPP6bPPPlPjxo1VqlQpVahQQX379tXvv/+uLl26mB0TAADTUboBXJPSpUurb9+++vTTTx2mz58/X+XKlcv3kG1Jys7O1ptvvqkGDRrIy8tLvr6+6tu3r2JiYhzmK1u2rP3QuhIlSqhatWr697//rfT0dPs8cXFxstlsmj17tlauXGl/zPr16xe6FEVEROinn37Sv//9b1WvXj3fedq1a6d27drZv3/ggQdks9mUmJjo1DoKu+3S09M1ZswYVa1aVZ6engoKCtKIESOUnJxsn+ev22TJkiUKDw9XmTJl1LFjRx09elSStHjxYtWtW1deXl66/fbbtX379jzrcubn5Oz2j46OVkBAgCTpueeey3PIZFRUlGw2m5YvX54nR9myZTVq1Kgif35XMnHiRJUvX16vvvpqvuOvvvqqypcvrwkTJtinDRw4UMHBwUpNTdVDDz0kHx8fVahQQY899pgyMjKuur7JkyfLzc1NBw8ezDP2xhtvyGazac+ePfkuO2HCBLm5uenw4cN5xt566y2HZffv369+/fopMDBQZcqU0a233qo33nhDFy9e/Mdtkt96fX19NX78+HzHvby8NG7cOIdp//vf/9S6dWuVLVtWZcqU0Z133qk1a9Y4zJO7HU+dOqW+ffvK29tbwcHBmjNnjiTp5MmT6tevn3x8fBQQEKDXXnstz7pzHyMpKUl9+vSRt7e3/P399fjjjystLS3P/M7kcmbb/f283Y4dO+rjjz9WcnKyw2HCp0+fts+zbds29ezZU35+fipZsqTq16+vDz744B+3vRk/8/wU9HX/xRdfqF69evb3iPz+vyufbVmQ53yl86edXXe7du3UokWLPNNff/112Ww2nTt3Ls+03C8fHx916NBB33///RW32bW4fPmyw/o8PT1Vs2ZNjRs3ThcuXHCYNzMzU5MnT1aDBg1UqlQphYSEaPjw4UpMTFRERIRq164tSRo2bJj98Z599ln78ikpKRo5cqSCg4Pl6emp0NBQPf300w7/h3bt2iWbzaYFCxZo/vz5qlu3rjw8PBQRESHDMPTmm2+qfv36KlOmjIKCgtSnTx/t3LnTJdsGuKEZAFBI7u7uxkMPPWRs2rTJkGRER0fbx8LDw40nnnjC+N///mdIMhYtWmQfy8nJMXr16mX4+voaCxcuNM6cOWPExMQY3bt3NypWrGjEx8fnu7709HRj/fr1RkhIiNG/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c7MfFp6VlaVjx47Z5ylduvQVT1P6Jxs2bFDZsmV19913O0zv3r27PD098/yu+fu2laSGDRvqjz/+0HPPPac//vhDOTk5hcoCFAeUbgBF4pFHHlFKSoqWLFmiL7/8UoMHD77ivImJicrOzpaPj488PDzk7u4uNzc3ubm5afXq1fY/DKOionT33XercuXK+umnn5SRkSHDMOylJCsry+FxAwMD86zL29u7UFexrVq1qiRdl1seFWTbJScnq0KFCvL09MwzVrly5TzF7u/bpFy5cled/tdt5ezP6Urr0jVs/7+7UhG+lud3JaGhoZJk/+P1SnJfGyEhIfZpFStWzFOgvL29nVp3q1at1LBhQ/u5y8ePH9fatWt1//332/NfSY8ePRQQEKCPP/5YkpSRkaHFixerf//+9ov9lS5dWt9++62qVaumwYMHy9/fX7feequmTZumzMzMqz7+3+VuI2f/f+S+VipXrpxnLHfaX1+7AQEBebZjuXLl5OfnpxIlSuSZnt+2rVChgjw8PBymeXl5ydvb274uZ3MV5bbLlXvth4kTJ9r/f+X+H7vzzjsd8uXnev/Mr8aZ133uc6lUqVKe5fOblh9nnnN+imLdusL7UO/evTV//nzNnDlTCQkJysnJkWEYqlatWp7fUUXhu+++U9euXVW3bl1t2bJFFy9elGEYev/996W//F5MSkpSYGCgw4eCBZGcnJzv/ws3NzcFBATk+V0TFBSUZ95x48bppZde0pdffqlGjRrJz89P/fv3144dOwqVCbAySjeAIhEaGqr27dtr9OjROn/+vAYNGnTFef39/VWqVCldvHhRly9fVnZ2tnJycux/rOT+If/FF1/I3d1dH330kerUqaOSJUtKko4cOZLv4xb2j4v85O5B/d///ldkj3klBdl2FSpUUEpKSr5/zJ08eTLP/YCvtE2c2VbO/pwK8phX4+PjI/15obi/Onv27BUvRHYtz2/Xrl0Oe5FyryIfEBCgRo0aaePGjXkuTJTrwoULioyMVJMmTRy2+bVugyeffFK//faboqOj9cEHHyg7O1tDhw79x+VKlCihQYMGaeXKlTp9+rSWLl2qtLS0PMs2adJE69atU2pqqn744Qe1bdtWL7zwgsaOHVugnAEBAWrSpIkiIyOduiBXhQoVpD9fo3+XO82Z7ViQ7ZuSkqLLly87TMvMzFRaWpr8/PwKnKuotl2u3MedPn26/f9X7v+x3Nfk1W65dr1/5lfjzM8ld5tfbVv/E2ef87Wu28fHJ8/7kCTFx8c7fH/ixAlt2LBBzzzzjO677z75+vrKZrPp8uXLiouLc+o5FdTnn38uX19fzZo1S7Vq1bJ/APv334sBAQFKSEhw+sidv6tQoUK+2yYnJ0enTp3K87vm7x+GSVLJkiU1adIkHT9+XDExMXrzzTe1detWtW7d2umfOVBcULoBFJlHH31UZ86cUefOnfP9hDxXt27ddOHCBa1cufIfHzN3D1Cuv5YjV+rYsaNatWqlmTNnXnGPZ2RkpCIjI4tkfc5uuw4dOignJyfPttu0aZNOnz6tDh06FEkeFfDn5Kzce4/nt5etatWqcnd3dzjMWH8e5n69vfrqq0pNTdXEiRPzHZ84caJSU1P1yiuvFOl6c6/+/N///lcff/yx6tSpo1atWjm17GOPPaZLly5pwYIF+vjjjxUeHq6WLVvmO2+pUqXUpk0bzZo1S02aNNGmTZsKnPXVV19VSkqKJk2alO/4xYsXNXXqVElSnTp1FBQUlO/Vor/55htVqlRJ9evXL3CGq8nJyclzBfIVK1bIMAz7/5PC5CrotitTpky+r/c6deooLCxMS5cuLfRht9f7Z34t6tSpoypVquT5/3z58mWtXbvW6ccpyHMu7Lpr1qypo0ePOlylPCsry+Gw9r/K/UA41xdffJHnA5+i5Onp6fBBR1ZWlsNpKZLUtWtXZWRkXPEK7fqH9+MOHTooPT09z2Hkq1ev1qVLlwr8u6ZGjRoaOnSopk6dqrS0tALdTQIoDijdAIrMwIEDZRiGVq9efdX5evXqpd69e2vYsGGaN2+eEhISdO7cOf3xxx96+eWX9Z///Ef68xyxjIwMvfDCC0pNTdXhw4c1cOBA+/m5rrZkyRIFBwerZcuW+uSTT3Ty5EldunRJ+/bt0/PPP6+7777b4Y+ygt4y7K+c3XYPPPCAmjZtqlGjRmn9+vVKT0/Xpk2bNHjwYNWpU0dPPPFEoZ5rfpz9ORVEqVKlVKNGDUVGRuY5JLh06dJ66KGH9NFHH2njxo1KT0/XypUr9f3339v/OLxeevbsqfHjx2vq1Kn617/+pUOHDikrK0sxMTF66qmnNHXqVI0fPz7PufXXqkyZMnrkkUe0YMECnTx50qm93Lnq1q2rO+64QzNmzNCmTZvyLPvDDz/owQcf1MaNG3X69GllZGRo5cqV2rt3r9q3b2+f79NPP5XNZstzK7u/6969u6ZNm6apU6dqyJAh2rZtmy5evKgzZ87o66+/VpMmTeyvZzc3N02fPl3R0dEaNWqU4uPjdeLECT399NP6+eefNX369DyHgl+rwMBAffbZZ9qwYYPS09P13Xff6amnnlLLli3t5586m8vZbZefBg0a6Ny5c9q4caOys7Mdxj788EP98ccfeuCBB7R9+3ZlZGTo+PHjWrp0qVq3bp1n/r+73j/za+Hm5qbJkycrMjJSL774ok6ePKljx45pyJAhatiwodOP80/PuSjWPXToUF24cEHPPvuskpOTdeTIEQ0dOlSNGjVymK9KlSpq0qSJ/vvf/2rbtm1KS0vT0qVL9cknn6hmzZpOP6eC6N69uxITEzVp0iSlp6frwIED6tevn5o2beow36BBg9SuXTsNGzZMCxcu1OnTp3XixAnNnTtXkydPlv5y3Y0NGzbkuZXe0KFDVbduXQ0dOlSRkZH2/0MjRoxQo0aNrnpEVq4BAwbovffe04EDB5SZmakjR47o888/l4+PT55tCRR3lG4A153NZtNXX32liRMnas6cOapVq5aCgoL02GOPqWzZshoxYoT0597mefPmaeXKlQoMDNR9992nDh066KGHHrouOatUqaJff/1Vzz//vObOnas6derI29tbXbp00b59+/Tll1/muZCXq+VewKZ///4aNmyYKlSooAceeEB33323fvzxx6ue11hQzv6cCuqjjz5SUlKSKlWq5HBhPEmaOXOmOnXqpB49eigkJEQrVqzQrFmziuw5FcRrr72mjRs3Ki4uTq1atVLp0qXVsmVLHTt2TBs3bsz3/tBF4cknn5TNZpOHh4cefvjhAi372GOPKS4uTiVKlMizbKtWrdSrVy9NnjxZ4eHhqlSpkl555RVNnDhR06ZNK1TW559/XlFRUcrKylLPnj3l4+OjsLAwTZkyRb1799YXX3xhn3fAgAFauXKltm3bprCwMNWqVUtbtmzRihUrCvw8neHm5qY5c+Zo1qxZqlKlih544AF16dJFa9ascTh6xplc17LtHnnkEQ0aNEh9+/ZViRIlHO7T3bZtW0VHR8vDw0OdO3eWr6+v2rZtq6VLl2rq1Kl5zpPOz/X+mV+LwYMHa/78+Vq2bJlCQ0PV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3XXXXfrll18kSaVKlVJiYqK++OILDRw40OyIlsUtw2C6AQMGyN/fXzNnzuSXA67ZmjVrNGHCBP3666966623lJCQoDfffFP685dJ9erV9cwzz5gdExaVmZmp5cuXa/bs2dq8ebPq1Kmjxx9/XMOGDVO5cuXMjgfgJpadna1169bp/fff1+rVqxUSEqIhQ4Zo5MiRCggIMDsebnDnzp1T5cqVdeTIER0/flzDhw9XdHS0JGnp0qX67LPPtGrVKrNjWhYNB6ZLS0tTp06dKNwoEkeOHFGzZs1ks9nyHDXh6+tr3/MNFMZvv/2m9evXa9u2bapevbo6dOigd955R3Xq1NH+/fvNjgfgJrZz506tX79eP//8sypVqqSuXbvqq6++Uq1atbR582az4+EGd/ToUVWtWlUBAQH8/eQCtByYrkOHDlq7dq3ZMVBMBAUFaffu3ZLk8EsjJydH69evV61atUxMBys6ceKEpkyZorCwMHXo0EHnz5/XsmXLFBMTo3fffVcxMTHq0KGDPvroI7OjwiLWrVunRx55RG3btlXv3r01Y8YMXbx40exYsKCUlBTNnj1bjRs3VpMmTXTo0CF99NFHiouL03vvvafdu3dr5MiRmjlzptlRcYMLDAxUXFyc0tPT85TuNWvW8PfTNeLwcpguKytLbdu2VdmyZdWsWTOVKFHCYXzw4MGqVq2aaflgLRcuXFC9evXUv39/Xb58Wenp6XrggQc0Y8YMRUdH68CBA/L29jY7Jizim2++0f3336+wsDA99thjevjhh+Xv759nvkWLFmnbtm2aPn26KTlhHf/+9781a9Ysde7cWbVq1VJKSorWrVsnPz8/RUVFycfHx+yIsIgtW7aoTZs2qly5sh599FENGTIk32vg/Pjjj3rvvfe0aNEiU3LCOh544AGdO3dOffv21cyZMzV37lwtWrRIs2bN0g8//KA77rjD7IiWRemG6ZYvX67evXsrNDRUQUFBeT5dmzVrlho1amRaPljPwYMHNXjwYP3888/2aQ0aNNDnn3+u2267zdRssJYffvhBZ8+eVffu3fOMnT17VhcuXFDlypVNyQbrOXLkiBo0aKDIyEg1a9bMPv38+fPq0KGDevXqpbFjx5qaEdYRExOjQ4cOccE0FJlz587pySef1BdffKHs7GxJUkBAgP773//qwQcfNDuepVG6YbqePXsq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9erVzY5T7LCnG6bLyspS27ZtVbZsWTVr1kwlSpRwGB88eLCqVatmWj4AAIAbzd69ezVjxowrjterV09jxoy5rplgXUePHlXnzp21b98+s6MUS+zphunWrFmjqKgohYaG6vz583kupNajRw9KNwAAwF9kZ2fr3LlzDtMuXbqk6OhonTlzRi+88IJp2WA9VatWVWZmpnbt2qUGDRqYHafYYU83TNezZ0+FhYVp+vTpZkcBAACwtMzMTHXv3l0jR45U9+7dzY4Di4iPj9fgwYMVFRWlPn36qHLlyg7jwcHBGjVqlGn5rI493TCdh4eH2rRpY3YMAAAAyytZsqSGDRumZcuWUbrhtPPnz8swDDVv3lxxcXGKi4tzGE9PTzctW3FA6YbpWrVqpeXLl6tr165mRwEAALC8xMTEPIeeA1cTFhamiIgIs2MUW5RumM7T01MLFy7Uvn371Lx58zwXUnv88cdVo0YN0/IBAADcaI4fP64lS5Y4TDMMQ8eOHdOnn36qd99917RsABxRumG6w4cPq1WrVpKk7du35xl/8MEHTUgFAABw40pISNCCBQscprm5ualy5cqaNWuWHnnkEdOywbqioqI0d+5cxcTEqFy5cmrSpImeeuoplS9f3uxolsaF1AAAAACLycjIUHJyskJCQgo0BlzJzJkzNWbMGLVr104NGjTQuXPn9P333yszM1NRUVGqWrWq2REti9INAAAAWExERISmTp2a73m4VxsD8pORkaGKFStq0aJF6tatm3365cuX1a9fP1WuXFlz5swxNaOVuZkdANCfV0ScPHmyevTooXbt2mnIkCH68ccfzY4FAABgOefOnVPZsmXNjgELiYmJUeXKlR0Kt/68y9CTTz6pHTt2mJatOOCcbpguISFBzZs3V3Z2tu655x5Vq1ZNe/fuVbt27TRx4kS99NJLZkcEAAC4IRw4cEDvvfeeYmNjdeDAAT399NMO45cuXdL69es5pxsF4uvrq8TERKWlpcnb29thbP/+/apQoYJp2YoDSjdM99Zbb6lu3bpatWqVPD097dMjIiLUvXt3Pfnkk/L19TU1IwAAwI3gwoULOnTokE6fPm3/91+VLl1aQ4YM0b///W/TMsJ6goOD1bRpU91zzz165ZVXVK9ePZ0/f17r16/Xq6++qnnz5pkd0dI4pxum69Spk0aMGKGePXvmGWvYsKHe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7d69WwkJCfZDzPXnfeHbtm1rajYro3QDAAAAFnPbbbcpISFBcXFxCg4ONjsOioFvv/1WgwYNUnJysgzDkM1mU3Z2ttzc3NSqVStt2rTJ7IiWxeHlAAAAgAUtX75cw4cPV79+/eTv7+8wVqNGDS5+Bafl5OQoKChI48eP12233aaRI0fq559/1oYNG/TYY4/piy++YE/3NaB0AwAAABaTk5Ojxo0b68CBA6pXr16eC6k1a9ZMb7/9tmn5YC1HjhxR27Ztdfz4cW3dulVDhgzR9u3bJUnz5s3T+vXr9eWXX5od07I4vBwAAACwmN9//12xsbGKiYlRYGCg2XFgccnJyapYsaL0523CEhMT7WM1a9ZUfHy8iemsz83sAAAAAAAKxsPDQ7Vr16Zwo8gFBwfLw8ND7733ng4ePKi3335btWrVMjuWpVG6AQAAAIupW7eu4uPjdfjwYbOjoBioWLGi+vTpI0lyd3fXe++9p7FjxyosLEy7d+/WK6+8YnZES+OcbgAAAMBiDh06pCFDhmjXrl3q3bt3ngup1a5dW0OHDjUtH258GRkZOnbsmOrWrZvv+OXLl5WYmKigoCDZbLbrnq844ZxuAAAAwGLS09Pl6empxo0b6+jRozp69KjD+F/vsQzkZ8+ePXriiScUHR2tXbt26ZVXXtGyZcvs4x4eHtyOrohQugEAAACLadSokSIiIsyOAQsrX768Tp48qezsbF28eFGxsbFmRyq2OLwcAAAAAG4yubedO336tLy9va96qHm9evU0f/78656xuGBPNwAAAADcZNzc3LR582YtW7ZMv/32m7755hv17ds333m5Sv61YU83AAAAANzEDh8+rA8++EDTpk0zO0qxROkGAAAAANhdunRJycnJ7OEuItynGwAAAABgt2PHDnXr1s3sGMUGe7oBAAAA4CZ0+PBhPf/883mmnzlzRtu2bdNdd90lSapZsyaHnl8D9nQDAAAAwE0oJSVF33zzjc6ePauLFy/avy5duqScnByH71F47OkGAAAAgJtQZmamnnnmGW3atEkLFy7ULbfcIkmKjo7WE088oejoaLMjFgvs6QYAAACAm1DJkiU1e/ZsTZw4UZ06ddJ///tfsU+26FG6AQAAAOAm1rNnT/36669aunSpOnfurISEBLMjFSuUbgAAAAC4yYWEhCgyMlK333677r//frPjFCuc0w0AAAAAsNu2bZvS0tLUtm1bs6MUC5RuAAAAAABchMPLAQAAAABwEUo3AAAAAAAuQukGAAAAAMBFKN0AAAAAALgIpRsAAAAAABehdAMAAAAA4CKUbgAAAAAAXITSDQAAAACAi1C6AQAAAABwkf8HYFhT9UzCVy0AAAAASUVORK5CYII=",
|
|
"text/plain": [
|
|
"<Figure size 1000x600 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Compare IC: momentum-only vs. 4-factor composite vs. individual factors\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"def compute_monthly_ic(factor_df, return_df):\n",
|
|
" common_dates = factor_df.index.intersection(return_df.index)\n",
|
|
" common_tickers = factor_df.columns.intersection(return_df.columns)\n",
|
|
" ic_series = []\n",
|
|
" for date in common_dates:\n",
|
|
" f = factor_df.loc[date, common_tickers]\n",
|
|
" r = return_df.loc[date, common_tickers]\n",
|
|
" mask = f.notna() & r.notna()\n",
|
|
" if mask.sum() < 20:\n",
|
|
" ic_series.append(np.nan)\n",
|
|
" continue\n",
|
|
" ic, _ = spearmanr(f[mask], r[mask])\n",
|
|
" ic_series.append(ic)\n",
|
|
" return pd.Series(ic_series, index=common_dates)\n",
|
|
"\n",
|
|
"# Compute IC for all signals\n",
|
|
"ic_compare = {}\n",
|
|
"for name in factor_names:\n",
|
|
" ic_compare[name] = compute_monthly_ic(factor_neut[name], df_returns)\n",
|
|
"ic_compare['momentum_only'] = compute_monthly_ic(df_momentum_signal, df_returns)\n",
|
|
"ic_compare['4factor_composite'] = compute_monthly_ic(df_composite_4f, df_returns)\n",
|
|
"\n",
|
|
"df_ic_compare = pd.DataFrame(ic_compare)\n",
|
|
"\n",
|
|
"summary = df_ic_compare.describe().T[['mean', 'std']]\n",
|
|
"summary['ic_ir'] = summary['mean'] / summary['std'] * np.sqrt(12)\n",
|
|
"summary = summary.round(4)\n",
|
|
"\n",
|
|
"print(\"IC Comparison: Neutralized Factors, Momentum-Only, and 4-Factor Composite\\n\")\n",
|
|
"display(summary)\n",
|
|
"\n",
|
|
"fig, ax = plt.subplots(figsize=(10, 6))\n",
|
|
"colors = ['coral', 'goldenrod', 'seagreen', 'purple', 'steelblue', 'coral']\n",
|
|
"summary['mean'].plot(kind='bar', ax=ax, color=['coral', 'goldenrod', 'seagreen', 'purple', 'steelblue', 'coral'])\n",
|
|
"ax.set_ylabel('Mean Monthly IC')\n",
|
|
"ax.set_title('Mean IC: Momentum-Only vs. Composite vs. Individual Factors')\n",
|
|
"ax.axhline(y=0, color='black', linewidth=0.5)\n",
|
|
"ax.grid(alpha=0.3, axis='y')\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.savefig('../images/03_factor_construction/composite_vs_single_ic.png', dpi=150, bbox_inches='tight')\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "9c9cc628",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Saved:\n",
|
|
" - momentum_signal.csv (momentum-only, sector-neutralized — THE HEADLINE)\n",
|
|
" - composite_4factor.csv (4-factor equal-weight — comparison only)\n",
|
|
" - factor_momentum_neutralized.csv\n",
|
|
" - factor_value_neutralized.csv\n",
|
|
" - factor_quality_neutralized.csv\n",
|
|
" - factor_lowvol_neutralized.csv\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"\"\"\"\n",
|
|
"==================================\n",
|
|
"Save signals and neutralized factors\n",
|
|
"==================================\n",
|
|
"\"\"\"\n",
|
|
"# The headline signal: momentum-only, sector-neutralized\n",
|
|
"df_momentum_signal.to_csv('../data/processed/momentum_signal.csv')\n",
|
|
"\n",
|
|
"# The 4-factor composite: kept for comparison\n",
|
|
"df_composite_4f.to_csv('../data/processed/composite_4factor.csv')\n",
|
|
"\n",
|
|
"# Individual neutralized factors\n",
|
|
"for name in factor_names:\n",
|
|
" factor_neut[name].to_csv(f'../data/processed/factor_{name}_neutralized.csv')\n",
|
|
"\n",
|
|
"print(\"Saved:\")\n",
|
|
"print(\" - momentum_signal.csv (momentum-only, sector-neutralized — THE HEADLINE)\")\n",
|
|
"print(\" - composite_4factor.csv (4-factor equal-weight — comparison only)\")\n",
|
|
"for name in factor_names:\n",
|
|
" print(f\" - factor_{name}_neutralized.csv\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "daab87e7",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Conclusion\n",
|
|
"\n",
|
|
"The IC comparison confirms what notebook 02 predicted:\n",
|
|
"\n",
|
|
"- **Momentum-only** has the highest IC among all signals. Sector neutralization slightly improves it (IR = 0.15 vs. 0.11 raw).\n",
|
|
"- **The 4-factor composite** has negative IC — combining momentum with two negative-IC factors (value, lowvol) and one near-zero factor (quality) dilutes the signal. This is the linear-algebra intuition made empirical: adding vectors that point in the wrong direction moves the sum away from the target.\n",
|
|
"\n",
|
|
"**The headline signal is momentum-only, sector-neutralized.** The backtest in notebook 04 trades this signal and tests whether it generates alpha net of costs."
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": ".venv",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.14.6"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|