diff --git a/README.md b/README.md index 7f44b55..ac5ef46 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/notebooks/portfolio_analysis.ipynb b/notebooks/portfolio_analysis.ipynb new file mode 100644 index 0000000..0652dea --- /dev/null +++ b/notebooks/portfolio_analysis.ipynb @@ -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 +} diff --git a/notebooks/report_analysis.ipynb b/notebooks/report_analysis.ipynb deleted file mode 100644 index e956bb2..0000000 --- a/notebooks/report_analysis.ipynb +++ /dev/null @@ -1,172 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "\n", - "import polars as pl\n", - "\n", - "from stocksense.database import DatabaseHandler\n", - "\n", - "DATE = \"2023-12-01\"\n", - "REPORT_DIR = Path(\"../reports/scores\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\u001b[32m2024-12-14 16:38:06.574\u001b[0m | \u001b[32m\u001b[1mSUCCESS \u001b[0m | \u001b[36mstocksense.database.schema\u001b[0m:\u001b[36mcreate_tables\u001b[0m:\u001b[36m121\u001b[0m - \u001b[32m\u001b[1mTables created successfully\u001b[0m\n" - ] - }, - { - "data": { - "text/html": [ - "
tic | adj_close | pe | saleq_yoy | fwd_return | excess_return | risk_return | pred_risk_return_3Q | rank_risk_return_3Q | pred_risk_return_4Q | rank_risk_return_4Q | avg_score | name | sector | date_added | date_removed |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | f64 | f64 | f64 | f64 | f64 | f64 | f64 | i64 | f64 | i64 | f64 | str | str | date | date |
"SEE" | 33.487507 | 15.771561 | -1.328192 | 6.042746 | -13.100373 | 2.886573 | 5.6585712 | 9 | 9.603384 | 8 | 8.5 | "Sealed Air" | "Materials" | null | 2023-12-18 |
"WBA" | 19.217991 | -5.8298 | 9.16207 | -38.549164 | -57.692283 | -14.640517 | 5.6297507 | 11 | 9.533597 | 18 | 14.5 | "Walgreens Boots Alliance" | "Consumer Staples" | 1979-12-31 | null |
"FMC" | 53.697098 | 14.045465 | -28.703166 | 8.275038 | -10.868082 | 2.731069 | 5.662194 | 8 | 9.511567 | 23 | 15.5 | "FMC Corporation" | "Materials" | 2009-08-19 | null |
"PSA" | 255.414917 | 23.092956 | 5.117858 | 16.228237 | -2.914882 | 10.844826 | 5.594103 | 25 | 9.599665 | 9 | 17.0 | "Public Storage" | "Real Estate" | 2005-08-19 | null |
"LUV" | 26.062613 | 31.94777 | 4.903537 | 3.950928 | -15.192191 | 1.546787 | 5.621783 | 15 | 9.521734 | 21 | 18.0 | "Southwest Airlines" | "Industrials" | 1994-07-01 | null |
… | … | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
"NOW" | 690.789978 | 89.312698 | 24.959039 | 11.744603 | -7.398516 | 5.550115 | 4.9748783 | 432 | 8.223453 | 452 | 442.0 | "ServiceNow" | "Information Technology" | 2019-11-21 | null |
"NVDA" | 46.750851 | 61.055955 | 200.0 | 158.87167 | 100.0 | 52.031133 | 4.9722304 | 433 | 8.100908 | 456 | 444.5 | "Nvidia" | "Information Technology" | 2001-11-30 | null |
"ANET" | 216.639999 | 35.442916 | 28.267736 | 55.722429 | 36.57931 | 22.902473 | 4.84744 | 448 | 8.336229 | 443 | 445.5 | "Arista Networks" | "Information Technology" | 2018-08-28 | null |
"LVS" | 45.990719 | 53.400266 | 178.109453 | -10.031928 | -29.175048 | -5.366387 | 4.9277596 | 440 | 7.871676 | 460 | 450.0 | "Las Vegas Sands" | "Consumer Discretionary" | 2019-10-03 | null |
"NTAP" | 89.583244 | 27.172948 | -6.073361 | 40.742009 | 21.59889 | 21.461724 | 4.8407593 | 449 | 7.989794 | 459 | 454.0 | "NetApp" | "Information Technology" | 1999-06-25 | null |