Skip to content

Commit

Permalink
Correct portfolio quality filters. Update portfolio analysis.
Browse files Browse the repository at this point in the history
  • Loading branch information
Francisco Silva authored and Francisco Silva committed Dec 27, 2024
1 parent d2f4edd commit e746073
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 179 deletions.
38 changes: 33 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,49 @@ The project uses a trading date observation window, which sets 4 portfolio rebal

First, update the stock database:
```bash
stocksense --update
stocksense update
```

### Model Training

Train the model for a given trade date:
Train the model for a specific trade date:
```bash
stocksense --train --trade-date YYYY-MM-DD
stocksense train --trade-date YYYY-MM-DD
```
For example:
```bash
stocksense train --trade-date 2024-03-01
```
Note: Trade date must be the 1st of March, June, September, or December.


Generate an investment portfolio:
```bash
stocksense portfolio --trade-date YYYY-MM-DD [--weighting STRATEGY] [--n-stocks N]
```

Options:
- `--weighting`: Portfolio weighting strategy (`market_cap` or `equal`). Default: `market_cap`
- `--n-stocks`: Number of stocks to include in portfolio. Default: 30

Score stocks for a given trade date:
Example:
```bash
stocksense --score --trade-date YYYY-MM-DD
# Generate a market-cap weighted portfolio with 25 stocks
stocksense portfolio --trade-date 2024-03-01 --weighting market_cap --n-stocks 25

# Generate an equal-weighted portfolio with default 30 stocks
stocksense portfolio --trade-date 2024-03-01 --weighting equal
```

#### Command Reference

```bash
stocksense --help # Show available commands
stocksense update --help # Show help for update command
stocksense train --help # Show help for train command
stocksense portfolio --help # Show help for portfolio command
```

In order to evaluate for the last trading date, don't specify a trade date.

### Streamlit App
Expand Down
78 changes: 78 additions & 0 deletions notebooks/portfolio_analysis.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"\n",
"import polars as pl\n",
"\n",
"DATE = \"2023-12-01\"\n",
"REPORT_DIR = Path(\"../reports/scores\")\n",
"PORTFOLIO_DIR = Path(\"../reports/portfolios\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Portfolio: ../reports/portfolios/portfolio_2023-06-01.xlsx\n",
"Average return: 32.73% (80.0)\n",
"\n",
"Portfolio: ../reports/portfolios/portfolio_2023-03-01.xlsx\n",
"Average return: 59.51% (100.0)\n",
"\n",
"Portfolio: ../reports/portfolios/portfolio_2023-12-01.xlsx\n",
"Average return: 21.08% (80.0)\n"
]
}
],
"source": [
"for portfolio in PORTFOLIO_DIR.glob(\"portfolio_*.xlsx\"):\n",
" df = pl.read_excel(portfolio, sheet_name=\"Full Portfolio\")\n",
" top = df.head(5)\n",
"\n",
" if \"fwd_return_4Q\" not in df.columns:\n",
" continue\n",
"\n",
" top_freturn = top.select(pl.col(\"fwd_return_4Q\")).mean().item()\n",
" top_hits = top.select(pl.col(\"fwd_return_4Q\") > 0).sum().item()\n",
"\n",
" top_hitrate = (top_hits / len(top)) * 100\n",
"\n",
" print(f\"\\nPortfolio: {portfolio}\")\n",
" print(f\"Average return: {top_freturn:.2f}% ({top_hitrate:.1f})\")"
]
}
],
"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.11.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
172 changes: 0 additions & 172 deletions notebooks/report_analysis.ipynb

This file was deleted.

3 changes: 1 addition & 2 deletions stocksense/model/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,9 @@ def _filter_candidates(self, df: pl.DataFrame) -> pl.DataFrame:

quality_filters = (
(pl.col("pe") > 0)
& (pl.col("ev_ebitda") < 50)
& (pl.col("saleq_yoy") > -20)
& (pl.col("fcf_yoy") > -50)
& (pl.col("price_mom") > -20)
& (pl.col("price_mom") > -25)
)
return df.filter(quality_filters)

Expand Down

0 comments on commit e746073

Please sign in to comment.