Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/workflows/deploy-dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ on:
- "site/**"
- "src/experiments/public_dashboard_validation.py"
- ".github/workflows/deploy-dashboard.yml"
pull_request:
branches:
- main
paths:
- "site/**"
- "src/experiments/public_dashboard_validation.py"
- ".github/workflows/deploy-dashboard.yml"
workflow_dispatch:

permissions:
Expand All @@ -23,27 +30,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: "3.12"

- name: Validate sanitized public bundle
run: python -m src.experiments.public_dashboard_validation --site-dir site

- name: Configure GitHub Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v6

- name: Upload dashboard artifact
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@v5
with:
path: site

deploy:
name: Deploy dashboard
needs: validate
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
pages: write
Expand All @@ -54,4 +62,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-2563eb)
![Research status](https://img.shields.io/badge/status-paper--live_research-d97706)
![Tests](https://img.shields.io/badge/tests-215_passing-0f766e)
![Tests](https://img.shields.io/badge/tests-218_passing-0f766e)
![License](https://img.shields.io/badge/license-MIT-64748b)

The project asks a narrow investment question:
Expand Down Expand Up @@ -209,6 +209,31 @@ before forward use. The six observed sessions will not choose the threshold.

[Review the rebalance-buffer experiment draft →](docs/design/REBALANCE_BUFFER_EXPERIMENT.md)

### Frozen-cohort dashboard

The legacy one-session experiment is now a frozen-cohort monitoring track rather
than a source of new daily portfolios. Its daily command refreshes prices,
extends the existing July 20–27 Ridge/XGBoost cohorts, rebuilds the local
dashboard, and regenerates the sanitized GitHub Pages bundle:

```bash
python -m src.cli run-live-forward-daily --as-of YYYY-MM-DD
```

The **Holding-period laboratory** can keep any dated list unchanged or compare
custom 1–60-session replacement horizons, including 5/10/20-session presets.
Ridge and XGBoost can be toggled independently while SPY remains permanently
visible from the shared decision-close baseline. It reports net return,
SPY-relative return, replacement-only turnover, and rebalance counts under
identical execution assumptions.

[Open the deployed live-forward dashboard →](https://hansun103.github.io/AI-Driven-Portfolio-Optimization-Backtesting-System/)

This monitoring track does not replace the primary horizon-matched 5/10/20
experiment described above. The former one-session fitting workflow remains
available only through the explicit `--retrain` flag and is not part of the
normal monitoring run.

## Architecture

The historical system contained many experimental sleeves. Its full architecture
Expand Down
37 changes: 26 additions & 11 deletions docs/GITHUB_PAGES_DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ Yahoo Finance and cached inputs
|
v
Daily live-forward pipeline
- evaluate prior frozen portfolios
- tune and refit Ridge and XGBoost
- freeze tomorrow's top-40 lists
- calculate stability diagnostics
- refresh official closes for archived cohorts
- extend frozen Ridge and XGBoost portfolios
- replay 1-60-session replacement policies
- calculate returns, costs, and turnover
|
+--> reports/live_forward/ private, ignored research evidence
|
Expand All @@ -25,10 +25,13 @@ Daily live-forward pipeline
GitHub Actions / Pages
```

`reports/live_forward/` remains the full local evidence store. The publisher uses explicit field
allowlists to create `site/data/dashboard.json`; it does not copy local paths, cached input names,
raw feature rows, model files, credentials, or candidate-trial details. The public bundle is then
validated for required files, duplicate holdings, forbidden private strings, and a SHA-256 digest.
`reports/live_forward/` remains the full local evidence store. The publisher uses
separate explicit field allowlists for model snapshots and frozen-cohort policy
paths when it creates `site/data/dashboard.json`; it does not copy local paths,
cached input names, raw price/feature rows, model files, credentials, or
candidate-trial details. The public bundle is then validated for required files,
snapshot and cohort schemas, duplicate identifiers or holdings, forbidden
private strings, and a SHA-256 digest.

## Daily publication

Expand All @@ -38,6 +41,16 @@ The standard daily command now refreshes both the local dashboard and the public
python -m src.cli run-live-forward-daily --as-of YYYY-MM-DD
```

This command is monitoring-only by default and reports
`"new_model_lists": 0`. It rebuilds:

- `reports/live_forward/cohort_monitor.json`;
- `reports/live_forward/dashboard.html`; and
- the tracked `site/` deployment bundle.

The historical one-session retraining path requires the explicit `--retrain`
flag. It should not be used during the frozen-cohort policy experiment.

To rebuild the public bundle without retraining:

```bash
Expand All @@ -52,9 +65,11 @@ Validate exactly what will be published:
python -m src.cli validate-live-dashboard-site --site-dir site
```

Commit `site/` after reviewing the generated changes. A push to `main` that changes the public
bundle starts `.github/workflows/deploy-dashboard.yml`. The workflow validates the bundle again,
uploads it as a Pages artifact, and deploys it to the `github-pages` environment.
Commit `site/` after reviewing the generated changes. A pull request that
changes the public bundle runs the validation and packaging job without
deploying. After merge, the resulting push to `main` runs the same validation,
uploads the Pages artifact, and deploys it to the `github-pages` environment.
The deploy job is explicitly restricted to `refs/heads/main`.

## One-time repository setting

Expand Down
66 changes: 35 additions & 31 deletions docs/LIVE_FORWARD_MONITOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,57 @@ yet a production trading recommendation. Ridge is the stable linear benchmark an
ranker is the nonlinear challenger. Both select 40 equal-weight names and share SPY as the market
benchmark.

## Daily after-close workflow
## Daily after-close workflow: monitoring phase

Run the production-style workflow after Yahoo Finance publishes the final regular-session bar:
This dashboard track preserves the legacy one-session selections for the
rebalancing-policy study; it is separate from the primary horizon-matched
5/10/20-session live-forward experiment. The July 20–27 Ridge and XGBoost
selections are frozen as permanent cohorts. After Yahoo Finance publishes the
official daily close, run:

```bash
python -m src.cli run-live-forward-daily --as-of YYYY-MM-DD
```

The command performs these steps in order:

1. Waits for the final 15:30 hourly bar and verifies the official daily close is available.
2. Evaluates portfolios frozen before the current session, including one holding-level return
record per selected stock.
3. Immediately rebuilds the local dashboard and sanitized `site/` bundle before model tuning starts.
4. Incrementally refreshes daily market data, split events, and trailing sector-ETF classifications
through the decision date.
5. Expands sector observations backward-as-of so each historical feature and label uses only the
classification available on that date.
6. Builds forward-return training labels only where the future outcome was already known before the
decision date.
7. Tunes Ridge and XGBoost independently on temporal validation data, refits the selected
configurations, and freezes both top-40 lists for the next evaluation session.
8. Freezes a separate shadow portfolio by blending the two full-universe percentile rankings
with the historically selected 25% Ridge / 75% XGBoost weight and applying the same top-40
sector cap.
9. Writes model-agreement, day-over-day selection-stability diagnostics, and candidate split
features that are not yet eligible for model fitting.
10. Rebuilds both dashboard outputs again so they include the newly frozen pending portfolios.

If final market data is unavailable, the command retries and then fails closed. It must not create a
snapshot from a partial trading day.

## Tuning profiles

Use a full focused retune after changing a feature definition, repairing historical inputs, or as
a periodic model audit:
Monitoring-only is the default. The command:

1. requests adjusted daily prices only for SPY and securities present in the archived cohorts;
2. verifies the requested official close exists and fails closed if Yahoo still has partial data;
3. extends every unchanged July 20–27 portfolio through the new close;
4. replays archived rebalance policies for every interval from 1 through 60 sessions;
5. recalculates net return, SPY-relative return, cumulative turnover, and trades; and
6. rebuilds the local dashboard and sanitized GitHub Pages bundle.

It does **not** fit a model or create a new stock list. The output explicitly reports
`"new_model_lists": 0`.

The dashboard's **Holding-period laboratory** supports selecting any July 20–27 list, displaying
Ridge and XGBoost together with clickable model chips, or replaying a 1–60-session rebalance
interval. Five-, 10-, and 20-session presets are provided, and the horizon comparison table reports
every 1–20-session candidate side by side. SPY is permanent and begins from the same zero-return
decision-close baseline as each portfolio.

The displayed **replacement turnover** excludes the unavoidable initial purchase. An unchanged
cohort therefore correctly shows 0% replacement turnover. Scheduled policies use the same
$1 million capital, 8 bps one-way cost, $10 fixed trade fee, and 0.25% minimum trade-weight rule.

## Explicit archived retraining workflow

The old production-style retraining path remains available for reproducibility, but now requires
the explicit `--retrain` flag:

```bash
python -m src.cli run-live-forward-daily \
--as-of YYYY-MM-DD \
--retrain \
--tuning-folds 5 \
--ridge-optuna-trials 60 \
--xgb-optuna-trials 50 \
--optuna-timeout-seconds 7200
```

The focused parameter ranges come from earlier completed live runs. The current decision date and
Do not use this flag during the frozen-cohort monitoring phase. The focused parameter ranges come
from earlier completed live runs. The current decision date and
its future return remain excluded, so narrowing the search does not introduce look-ahead leakage.
Five purged temporal folds provide a more stable selection estimate, while the separate Ridge and
XGBoost trial budgets reflect their very different fitting costs.
Expand Down
3 changes: 2 additions & 1 deletion site/assets/dashboard.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading