Skip to content

Commit 028bd03

Browse files
committed
Add strategy execution pipeline, research notebooks, and CI/CD
Phase 3 - Strategy Execution: - src/stonks/strategy/ module with executor, rebalancer, scheduler - scripts/run_strategy.py CLI with momentum, mean-reversion, equal-weight strategies - 17 new tests for strategy module Phase 4 - Research Notebooks: - 01_factor_exploration.qmd - factor distributions and correlations - 02_garch_volatility.qmd - R GARCH analysis with rugarch - 03_pairs_trading.qmd - cointegration and pairs signals - 04_backtest_analysis.qmd - strategy performance comparison Phase 5 - CI/CD & R Tests: - .github/workflows/test.yml - GitHub Actions for Python + R - tests/R/test_db.R - 14 tests for R database functions - tests/R/test_garch.R - 18 tests for GARCH modeling Code Quality: - Applied ruff linting and formatting - Fixed unused imports and variables - R code formatted with styler, lintr clean Total: 73 Python tests passing, 2 skipped
1 parent 9bf8326 commit 028bd03

38 files changed

Lines changed: 3690 additions & 219 deletions

.github/workflows/test.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
python-tests:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
lfs: true
17+
18+
- name: Install uv
19+
uses: astral-sh/setup-uv@v4
20+
with:
21+
version: "latest"
22+
23+
- name: Set up Python
24+
run: uv python install 3.12
25+
26+
- name: Install dependencies
27+
run: uv sync --dev
28+
29+
- name: Run linting
30+
run: uv run ruff check src/ tests/
31+
32+
- name: Run type checking
33+
run: uv run mypy src/stonks --ignore-missing-imports
34+
continue-on-error: true
35+
36+
- name: Run tests
37+
run: uv run pytest tests/ -v --tb=short --ignore=tests/R/
38+
39+
- name: Run tests with coverage
40+
run: uv run pytest tests/ --cov=src/stonks --cov-report=xml --ignore=tests/R/
41+
42+
- name: Upload coverage
43+
uses: codecov/codecov-action@v4
44+
with:
45+
files: ./coverage.xml
46+
fail_ci_if_error: false
47+
48+
r-tests:
49+
runs-on: ubuntu-latest
50+
51+
steps:
52+
- uses: actions/checkout@v4
53+
with:
54+
lfs: true
55+
56+
- name: Set up R
57+
uses: r-lib/actions/setup-r@v2
58+
with:
59+
r-version: "4.3"
60+
61+
- name: Install system dependencies
62+
run: |
63+
sudo apt-get update
64+
sudo apt-get install -y libcurl4-openssl-dev libssl-dev libxml2-dev
65+
66+
- name: Install R packages
67+
run: |
68+
install.packages(c("testthat", "duckdb", "arrow", "data.table", "rugarch", "urca", "vars", "moments", "tseries"))
69+
shell: Rscript {0}
70+
71+
- name: Run R tests
72+
run: |
73+
testthat::test_dir("tests/R", reporter = "summary")
74+
shell: Rscript {0}
75+
76+
integration:
77+
needs: [python-tests, r-tests]
78+
runs-on: ubuntu-latest
79+
80+
steps:
81+
- uses: actions/checkout@v4
82+
with:
83+
lfs: true
84+
85+
- name: Install uv
86+
uses: astral-sh/setup-uv@v4
87+
88+
- name: Set up Python
89+
run: uv python install 3.12
90+
91+
- name: Install dependencies
92+
run: uv sync --dev
93+
94+
- name: Verify module imports
95+
run: |
96+
uv run python -c "
97+
from stonks.db import query, get_connection
98+
from stonks.factors import momentum, volatility
99+
from stonks.backtest import Backtest, BacktestResult
100+
from stonks.trading import AlpacaTrader, Portfolio
101+
from stonks.strategy import StrategyExecutor, Rebalancer
102+
print('All module imports successful')
103+
"
104+
105+
- name: Test CLI help
106+
run: |
107+
uv run python scripts/run_strategy.py --help

R/cointegration.R

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ find_cointegrated_pairs <- function(prices_wide, symbols, alpha = 0.05) {
6161
# Skip if missing data
6262
if (any(is.na(y)) || any(is.na(x))) next
6363

64-
result <- tryCatch({
65-
test_cointegration_eg(y, x)
66-
}, error = function(e) NULL)
64+
result <- tryCatch(
65+
{
66+
test_cointegration_eg(y, x)
67+
},
68+
error = function(e) NULL
69+
)
6770

6871
if (!is.null(result) && result$cointegrated) {
6972
pairs <- rbind(pairs, data.table(
@@ -112,15 +115,15 @@ generate_pairs_signals <- function(spread, entry_z = 2, exit_z = 0, lookback = 2
112115
for (i in (lookback + 1):n) {
113116
if (position == 0) {
114117
if (z_score[i] > entry_z) {
115-
signal[i] <- -1L # Short spread
118+
signal[i] <- -1L # Short spread
116119
position <- -1L
117120
} else if (z_score[i] < -entry_z) {
118-
signal[i] <- 1L # Long spread
121+
signal[i] <- 1L # Long spread
119122
position <- 1L
120123
}
121124
} else {
122125
if ((position == 1L && z_score[i] >= exit_z) ||
123-
(position == -1L && z_score[i] <= -exit_z)) {
126+
(position == -1L && z_score[i] <= -exit_z)) {
124127
signal[i] <- 0L
125128
position <- 0L
126129
} else {

R/garch.R

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,15 @@ rolling_garch <- function(returns, window = 252, model = "sGARCH",
4545

4646
for (i in window:n) {
4747
window_returns <- returns[(i - window + 1):i]
48-
tryCatch({
49-
fit <- fit_garch(window_returns, model = model, distribution = distribution)
50-
vol[i] <- tail(sigma(fit), 1)
51-
}, error = function(e) {
52-
vol[i] <<- NA_real_
53-
})
48+
tryCatch(
49+
{
50+
fit <- fit_garch(window_returns, model = model, distribution = distribution)
51+
vol[i] <- tail(sigma(fit), 1)
52+
},
53+
error = function(e) {
54+
vol[i] <<- NA_real_
55+
}
56+
)
5457
}
5558

5659
vol
@@ -68,17 +71,20 @@ select_garch <- function(returns) {
6871

6972
for (m in models) {
7073
for (d in distributions) {
71-
tryCatch({
72-
fit <- fit_garch(returns, model = m, distribution = d)
73-
ic <- infocriteria(fit)
74-
results <- rbind(results, data.table(
75-
model = m,
76-
distribution = d,
77-
AIC = ic[1],
78-
BIC = ic[2],
79-
LogLik = likelihood(fit)
80-
))
81-
}, error = function(e) NULL)
74+
tryCatch(
75+
{
76+
fit <- fit_garch(returns, model = m, distribution = d)
77+
ic <- infocriteria(fit)
78+
results <- rbind(results, data.table(
79+
model = m,
80+
distribution = d,
81+
AIC = ic[1],
82+
BIC = ic[2],
83+
LogLik = likelihood(fit)
84+
))
85+
},
86+
error = function(e) NULL
87+
)
8288
}
8389
}
8490

TODO.md

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,91 @@
11
# TODO - stonks-lab
22

3-
## Current Sprint
3+
## Project Status: ~95% Feature Complete
44

5-
### In Progress
6-
- [ ] Initial project setup
5+
All core phases implemented. Ready for production use with paper trading.
76

8-
### Up Next
9-
- [ ] Configure OpenBB providers
10-
- [ ] Download S&P 500 historical data
11-
- [ ] Implement basic factors (momentum, value)
7+
### Completed
128

13-
## Backlog
9+
#### Phase 1 & 2: Foundation
10+
- [x] Testing foundation (56 tests)
11+
- [x] Factor expansion (quality, size, macro, technical)
12+
- [x] Paper trading (Alpaca integration)
13+
- [x] Backtesting engine + walk-forward validation
1414

15-
### Data Pipeline
15+
#### Phase 3: Strategy Execution Pipeline
16+
- [x] `src/stonks/strategy/executor.py` - orchestration
17+
- [x] `src/stonks/strategy/rebalancer.py` - weight → orders
18+
- [x] `src/stonks/strategy/scheduler.py` - automated runs
19+
- [x] `scripts/run_strategy.py` - CLI entry point
20+
- [x] 17 strategy tests (73 total Python tests)
21+
22+
#### Phase 4: Research Notebooks
23+
- [x] `notebooks/01_factor_exploration.qmd` - factor distributions, correlations
24+
- [x] `notebooks/02_garch_volatility.qmd` - R GARCH analysis
25+
- [x] `notebooks/03_pairs_trading.qmd` - cointegration + signals
26+
- [x] `notebooks/04_backtest_analysis.qmd` - strategy performance
27+
28+
#### Phase 5: CI/CD & R Tests
29+
- [x] `.github/workflows/test.yml` - GitHub Actions for Python + R
30+
- [x] `tests/R/test_db.R` - 14 tests for R database functions
31+
- [x] `tests/R/test_garch.R` - 18 tests for GARCH modeling
32+
33+
## Quick Commands
34+
35+
```bash
36+
# Run all Python tests
37+
uv run pytest tests/ -v --ignore=tests/R/
38+
39+
# Run R tests
40+
Rscript -e "testthat::test_dir('tests/R')"
41+
42+
# Strategy dry run
43+
uv run python scripts/run_strategy.py --strategy momentum --dry-run
44+
45+
# Strategy execution (requires Alpaca credentials)
46+
uv run python scripts/run_strategy.py --strategy momentum --execute
47+
48+
# Render notebooks
49+
quarto preview notebooks/
50+
51+
# Daily data update
52+
uv run python scripts/update_data.py
53+
```
54+
55+
## Remaining Backlog
56+
57+
### Data Pipeline Enhancements
1658
- [ ] FRED macro data loader
1759
- [ ] SEC EDGAR filings integration
1860
- [ ] Universe management (S&P 500, Russell 2000)
1961

20-
### Factor Library
21-
- [ ] Classic factors (momentum, value, quality, size)
22-
- [ ] Macro factors from FRED
23-
- [ ] GARCH volatility (R)
24-
25-
### Bayesian/Stats (R)
62+
### Advanced Bayesian/Stats (R)
2663
- [ ] brms hierarchical factor model
27-
- [ ] Cointegration pairs detection
64+
- [ ] Dynamic hedge ratio via Kalman filter
2865

29-
### ML
30-
- [ ] LightGBM factor selection
31-
- [ ] SHAP interpretability
32-
- [ ] Walk-forward validation
66+
### ML Enhancements
67+
- [ ] Feature importance dashboard
68+
- [ ] Hyperparameter tuning pipeline
69+
- [ ] Ensemble methods
3370

34-
### Backtesting
35-
- [ ] Transaction cost model
36-
- [ ] Performance metrics (Sharpe, Calmar)
71+
### Production Readiness
72+
- [ ] Alerting/monitoring (Slack/email notifications)
73+
- [ ] Position sizing optimization
74+
- [ ] Multi-strategy portfolio allocation
3775

38-
## Blockers
76+
## Architecture Summary
3977

40-
None currently.
78+
```
79+
Data Flow:
80+
OpenBB → Parquet (raw/) → DuckDB → Factors → Signals → Backtest
81+
82+
Paper Trading
83+
(Alpaca API)
4184
42-
## Notes
85+
Languages:
86+
Python: Data ingestion, factors, ML, backtesting, execution
87+
R: GARCH volatility, Bayesian models, cointegration
88+
```
4389

4490
---
4591
*Last updated: 2026-01-24*

0 commit comments

Comments
 (0)