A research-grade equity backtesting and signal platform.
QuantSense is a single-repo full-stack app for designing equity strategies, backtesting them with realistic execution assumptions, and stress-testing the results with standard statistical methods.
This is not a "paper-trade-with-AI-and-pretty-charts" demo. The focus is on the things a quant researcher actually cares about: no look-ahead bias, realistic costs, walk-forward validation, and significance testing that distinguishes signal from luck.
backtest.py— bar-event-driven simulator. Signals generated on bartexecute at bart+1's open price. Configurable slippage (basis points) and per-share + percentage commissions. No look-ahead, ever.metrics.py— Sharpe, Sortino, Calmar, max drawdown depth + duration, downside deviation, alpha/beta vs benchmark, and Deflated Sharpe Ratio (Bailey & López de Prado 2014) to correct for multiple-testing inflation.walk_forward.py— anchored walk-forward optimization. Searches the parameter grid on an in-sample window, evaluates on the next out-of-sample slice, rolls forward. Reports IS vs OOS Sharpe degradation so overfitting is visible, not hidden.significance.py— bootstrap confidence intervals on Sharpe and a Monte Carlo permutation test against shuffled-return null. Answers: "is this strategy distinguishable from luck on the same return distribution?"strategy.py— momentum, mean reversion, Bollinger bands, MACD, volume momentum.indicators.py— SMA, EMA, RSI, MACD, Bollinger bands, ATR.optimizer.py— thin wrapper that delegates to walk-forward (the old single-pass grid search was an overfitting machine and was removed).screener.py— parallel ticker screener used by/api/market/screener; orthogonal to the backtest engine.
FastAPI; SQLite (default) or any SQLAlchemy-compatible DB.
POST /api/backtest/run— run a backtest with realistic executionPOST /api/backtest/optimize— walk-forward parameter searchPOST /api/backtest/significance— bootstrap CI + permutation test on a backtest's SharpeGET /api/backtest/results— list saved runsPOST /api/backtest/compare— side-by-side comparisonGET /api/sentiment/analyze/{ticker}— VADER sentiment over recent news headlines (research only; not used for signal generation)GET /api/market/...— OHLCV, quotes, search, screenerPOST /api/trading/order— paper-trading positions tracker (educational, not a live broker)
Supporting surfaces (dashboard scaffolding, not part of the research API):
/api/auth/* for login, /api/settings/* for watchlist/preferences,
/api/portfolio/history for equity-curve snapshots, /api/ws for live
quote streaming.
Next.js + Tailwind. Backtest builder, equity-curve charts, trade table, walk-forward results view, significance panel.
The visual identity is an editorial trading terminal — warm-paper light mode, deep-charcoal dark mode, a single ochre accent, hairline borders, and tabular numerals throughout. Numbers should look like numbers, not like decoration.
| Light | Dark | |
|---|---|---|
| Dashboard | ![]() |
![]() |
| Backtest | ![]() |
![]() |
| Charts | ![]() |
![]() |
| Compare | ![]() |
![]() |
| Sentiment | ![]() |
![]() |
| Settings | ![]() |
![]() |
| Login | ![]() |
![]() |
Regenerate via node scripts/screenshots.mjs (requires the backend on :8765 and a built frontend on :3210; defaults overridable with BASE and API env vars).
| Choice | Why |
|---|---|
| Next-bar-open execution | Same-bar-close fills are the most common backtest bug. They make every momentum strategy look profitable. Don't. |
| Slippage in basis points | Realistic for liquid equities. Default 5 bps; configurable per-run. |
| Anchored walk-forward | A single-pass grid search reports the best-in-sample Sharpe, which is meaningless. Walk-forward forces you to look at honest OOS perf. |
| Deflated Sharpe Ratio | Sharpe inflates with the number of strategies tested. DSR penalizes for grid size and reports a probability the true Sharpe > 0. |
| Permutation test | Cheap, intuitive answer to "is the signal real, or is it just the return distribution?" |
| Bootstrap CI on Sharpe | A point estimate is not a result. A CI is. |
| Sentiment is research-only | Mixing fuzzy NLP signals into trading rules is hard to defend statistically. Sentiment endpoints exist for discretionary research. |
| No live broker integration | A personal project that "trades real money" is theatre. Paper trading is the honest scope. |
# Backend
cd backend
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python -c "import nltk; nltk.download('vader_lexicon')"
uvicorn main:app --reload
# Frontend
cd frontend
npm install
npm run dev
# Tests
cd backend && pytestAPI docs at http://localhost:8000/docs once the server is running.
curl -X POST http://localhost:8000/api/backtest/significance \
-H "Content-Type: application/json" \
-d '{
"ticker": "SPY",
"strategy_type": "momentum",
"params": {"sma_period": 20},
"start_date": "2020-01-01",
"end_date": "2024-12-31",
"initial_capital": 100000
}'Returns a Sharpe point estimate, a 95% bootstrap CI and a stationary-block-bootstrap CI (autocorrelation-corrected), a permutation-test p-value, and a one-line interpretation. Same config request → byte-identical CIs forever (every result includes a run_hash; the bootstrap RNG seed is derived from it).
- Multi-asset portfolio backtester with rebalancing
- White's Reality Check / SPA test for strategy comparison
- Factor risk decomposition (Fama-French 3- and 5-factor regression of strategy returns)
- Parquet OHLCV cache via pyarrow
- Vectorized backtester. Engine now walks trade-by-trade with array-level no-look-ahead contracts (entry triggers built from
np.minimum.accumulateoverpending = sig_type[:-1]); golden-output regression test asserts byte-identical fills/PnL/equity/metrics vs the previous loop on all 5 strategies. - Validated against
empyrical. Newtests/test_metrics_vs_empyrical.pyruns Sharpe, Sortino, Calmar, max-DD, downside-deviation, alpha, and beta against the canonical Quantopian-derived reference. 5/7 match exactly; Sortino and alpha match modulo two documented design choices (divisor convention, linear vs compound annualization), each captured as an exact mathematical identity. statsmodelsfor alpha/beta. Hand-rolled OLS replaced withstatsmodels.OLS().fit(cov_type='HC1'). The metrics dict now exposesalpha_t,alpha_pvalue,beta_t,beta_pvalue,r_squared,n_obs— what an interview reviewer expects to see, not just point estimates.- Stationary block bootstrap.
arch.bootstrap.StationaryBootstrapwith the Politis-White optimal block length tuner. The significance endpoint reports both i.i.d. and block CIs side-by-side; the spread is the autocorrelation correction. - Reproducibility contract. Every backtest result carries a
run_hash = sha256(price_data | params | code_version). The bootstrap / block-bootstrap / permutation seeds derive from the hash, so the same config produces byte-identical CIs and p-values forever. Param-grid iteration sorted;np.random.Generatoreverywhere (no globalnp.random.seed());dataclasses.asdictfalls back to__dict__for non-dataclass fields. Newtests/test_reproducibility.pyproves it.
Tests: 116 passing (was 50 before this pass).













