dennisthiessen 8750aac6d9
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 57s
Deploy / deploy (push) Successful in 2m24s
fix: carry action/risk_level onto backtest candidates for the gate ablation
_window_setups computed them but _replay_ticker dropped them, so the
ablation's NEUTRAL/tightener checks saw None for every candidate and the
'without confidence floor' / 'without R:R floor' rows collapsed to 0
setups (impossible — removing a floor can only add setups). Regression
test now goes through the real _replay_ticker path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 08:07:27 +02:00
2026-03-03 15:20:18 +01:00
2026-03-03 20:45:28 +01:00
2026-02-27 16:08:09 +01:00
2026-02-20 17:31:01 +01:00
2026-03-03 20:35:11 +01:00

Signal Dashboard

Investing-signal platform for NASDAQ stocks. Surfaces the best trading opportunities through weighted multi-dimensional scoring — technical indicators, support/resistance quality, sentiment, fundamentals, and momentum — with asymmetric risk:reward scanning.

Philosophy: Don't predict price. Find the path of least resistance, key S/R zones, and asymmetric R:R setups.

How It Works

Scheduled pipelines turn raw prices into a ranked, gated list of tradeable setups. Everything downstream of OHLCV is recomputed from stored data, so each refresh is cheap and idempotent. Job timing is cron-based and configurable in Admin → Jobs (default timezone Europe/Berlin).

Daily Load — the full refresh

Once a day (default 07:00). Steps run in dependency order, each consuming the previous step's fresh output:

  1. OHLCV — fetch the latest daily bars for every tracked ticker (Alpaca); new tickers backfill ~5 years.
  2. Sentiment — fetch sentiment for the names that matter and are stale (> 5 days): top-pick feeders (momentum leaders with a tradeable long setup), the watchlist, and open paper trades, plus a top-N-by-composite discovery net. Runs before the scan so the scan sees fresh sentiment.
  3. R:R Scan — recompute S/R zones, the 5-dimension scores and long/short setups (ATR stops, S/R targets) for every ticker, and attach each ticker's 121 momentum percentile.
  4. Outcome Eval — resolve setups that hit target/stop or expired (default 30 trading days) and close paper trades that hit a level.
  5. Market Regime — recompute the regime index (breadth/trend).
  6. Regime Monitor — observational early-warning snapshot (VIX, credit spreads via FRED); feeds nothing else.

A failing step is logged; the pipeline continues with the next.

Intraday — light refresh

Hourly across the US session (MonFri): only OHLCV → Outcome Eval, to keep prices current and close paper trades intraday. No scan/sentiment — the dashboard recomputes live R:R from the latest price, so fresh prices are enough.

Other jobs

Fundamentals (weekly, early Monday) · Alerts (hourly, Telegram) · Backtest (weekly) · Ticker-universe sync (daily). Deep history backfill and event study are manual-only (Admin → Jobs).

From score to "top pick"

  1. Composite score — technical, S/R-quality, sentiment, fundamental and momentum sub-scores (0100) combine into a weighted composite (weights configurable; missing dimensions re-normalize).
  2. Setups — the scanner builds long/short setups with ATR stops and S/R targets, then adds a confidence score, conflict flags and a target reach-probability.
  3. Activation gate — a setup qualifies only if it clears the R:R and confidence floors and ranks in the top momentum percentile of the universe (the validated edge is long-only momentum).
  4. Top pick — the highest-momentum qualified setup; highlighted on the Dashboard and labelled on the ticker page.

Strategy Status — What's Validated and What Isn't

Read this before touching scoring, gating, or setup logic. The platform measures itself — a weekly-replay backtest plus a factor rank-IC harness (app/services/backtest_service.py) — and the verdicts below come from those reports (June 2026, ~5 years of OHLCV), not from opinion.

Component Verdict Evidence
12-1 cross-sectional momentum (the activation gate, long-only) The only demonstrated edge — in-sample Qualified setups ≈ +0.25R avg vs ≈ 0.05R all-setups baseline; the percentile sweep is cleanly monotonic (cutoff 50 → +0.14R, 70 → +0.21R, 80 → +0.25R). Rank-IC ≈ 0.05, t ≈ 1.6 — right sign and size for the classic factor, not yet statistically significant
S/R setup engine (ATR stops, S/R targets, reach-probability) No selection edge — execution/timing only ≈ breakeven (+0.01R) before the momentum gate. The probability model is honest (calibrated) but does not discriminate winners
Composite score + 5 dimensions Display/ranking only Sub-scores are hand-built heuristics; none has a measured IC. Note: the "momentum" dimension is 5/20-day ROC — NOT the validated 12-1 factor (that lives in momentum_service)
LLM sentiment Display + a bounded composite adjustment (± weight × 100 pts around neutral 50) Deliberately kept out of the setup engine; no point-in-time history to validate against yet
Fundamentals Feeds composite + confidence only Latest values only, no history — same limitation
Short setups Excluded while the momentum gate is active Backtest showed shorts fight the trend and drag expectancy
Expected-value gate (removed June 2026) Degenerate — do not resurrect Structurally favored distant lottery targets; selected worse-than-random setups

Caveats on the momentum result: in-sample, roughly one market regime, no transaction costs or slippage modeled, and the factor is beta-heavy (6-month volatility posted the top IC — that's beta, not alpha). The out-of-sample proof is the forward paper-trade record: Signals → Track Record compares live qualified expectancy against the backtest.

The iron rule for strategy changes

A signal earns its way into selection only through the factor harness:

  1. Add it as a point-in-time function of past bars in _signal_values() (backtest_service.py).
  2. Run the backtest (Admin → Jobs, or the weekly run) and read the Signal edge table (Signals → Track Record).
  3. Wire it into the gate or ranking only if |mean IC| ≳ 0.03 with a consistent sign and reliable: true (≥ 12 non-overlapping windows).

Corollaries: never let an unvalidated score gate setups; the outcome evaluator must keep scoring all setups (unqualified ones are the control group); LLM output stays display-only in the quant path.

Highest-value next experiments (in order)

  1. Volatility-scaled momentum — add mom_12_1 / vol_6m to _signal_values; risk-adjusted momentum typically beats raw and dampens momentum crashes.
  2. Regime filter on the gate — momentum crashes cluster in post-bear rebounds; market_regime_service already computes the SPY 50/200 trend, so test "qualify only in Risk-On" in the backtest before wiring it live.
  3. Cost haircut in the backtest — subtract a fixed per-trade cost (e.g. 0.1% per side) in the outcome aggregation so expectancy is net; a thin edge must survive costs.
  4. More breadth, not more history — widening the ranked universe (e.g. nasdaq_all) strengthens each week's cross-section and the IC t-stat, even if only the top slice is traded. (Deeper history was considered and declined.)
  5. Exit tuning with the existing sweeps — the report already sweeps fixed take-profits and trailing stops against the S/R-target model; momentum's edge lives in the right tail, so wide trailing exits (already the paper-trade default) tend to beat nearby S/R targets. Also worth testing: a pure time-based exit (hold ~1 month, re-rank) instead of the 30-day target/stop race.

Key Use Cases

  • Find today's best long setup. On the Dashboard, the Top Setups table lists qualified setups ranked by momentum with the #1 flagged "Top pick". Each row opens the ticker page for the chart, scores, S/R targets and entry/stop.
  • Track a trade you took. Mark a setup as a paper trade: it's marked-to-market against the latest close, auto-closed on stop/target, and its sentiment stays fresh while open. Signals → Track Record shows the realized edge.

Stack

Layer Tech
Backend Python 3.12+, FastAPI, Uvicorn, async SQLAlchemy, Alembic
Database PostgreSQL (asyncpg)
Scheduler APScheduler — daily & intraday pipelines, fundamentals, alerts, regime, backtest
Frontend React 18, TypeScript, Vite 5
Styling Tailwind CSS 3 with custom glassmorphism design system
State TanStack React Query v5 (server), Zustand (client/auth)
Charts Canvas 2D candlestick chart with S/R overlays
Routing React Router v6 (SPA)
HTTP Axios with JWT interceptor
Data providers Alpaca (OHLCV); OpenAI / Gemini / DeepSeek / xAI (sentiment, pluggable); Fundamentals chain: FMP → Finnhub → Alpha Vantage; FRED (regime); Telegram (alerts)

Features

Backend

  • Ticker registry with full cascade delete
  • Universe bootstrap for sp500, nasdaq100, nasdaq_all via admin endpoint
  • OHLCV price storage with upsert and validation
  • Technical indicators: ADX, EMA, RSI, ATR, Volume Profile, Pivot Points, EMA Cross
  • Support/Resistance detection with strength scoring and merge-within-tolerance
  • Sentiment analysis with time-decay weighted scoring
  • Fundamental data tracking (P/E, revenue growth, earnings surprise, market cap)
  • 5-dimension scoring engine (technical, S/R quality, sentiment, fundamental, momentum) with configurable weights
  • Risk:Reward scanner — long and short setups, ATR-based stops, S/R-based targets, configurable R:R threshold (default 1.5:1)
  • Activation gate — qualifies setups on a momentum-percentile floor plus R:R/confidence (validated long-only edge); ranks the rest by expected value
  • Recommendation layer — directional confidence, conflict detection, per-target reach-probability
  • Paper trading — take a setup, mark-to-market vs. latest close, auto-close on stop/target, realized track record + outcome evaluation
  • Market-regime index + FRED early-warning monitor (VIX, credit spreads); weekly backtest + manual event study
  • Telegram alerts (e.g. regime-quadrant changes)
  • User-curated watchlist (cap: 20), enriched with composite score, R:R and S/R summary
  • JWT auth with admin role, configurable registration, user access control
  • Cron-scheduled pipelines (admin-configurable) with per-job enable/disable and live status monitoring
  • Admin panel: user management, data cleanup, job control, system settings

Frontend

  • Glassmorphism UI with frosted glass panels, gradient text, ambient glow effects, mesh gradient background
  • Interactive candlestick chart (Canvas 2D) with hover tooltips showing OHLCV values
  • Support/Resistance level overlays on chart (top 6 by strength, dashed lines with labels)
  • Data freshness bar showing availability and recency of each data source
  • Watchlist with composite scores, R:R ratios, and S/R summaries
  • Ticker detail page: chart, scores, sentiment breakdown, fundamentals, technical indicators, S/R table
  • Rankings table with configurable dimension weights
  • Trade scanner showing detected R:R setups
  • Admin page: user management, job status with live indicators, enable/disable toggles, data cleanup, system settings
  • Protected routes with JWT auth, admin-only sections
  • Responsive layout with mobile navigation
  • Toast notifications for async operations

Pages

Route Page Access
/login Login Public
/register Register Public (when enabled)
/ Dashboard — top setups, open trades, regime (default) Authenticated
/market Market — watchlist + rankings tabs Authenticated
/signals Signals — scanner + track record tabs Authenticated
/regime Market Regime Authenticated
/ticker/:symbol Ticker Detail Authenticated
/admin Admin Panel Admin only

Legacy routes redirect: /watchlist/market, /rankings/market?tab=rankings, /scanner/signals, /performance/signals?tab=track.

API Endpoints

All under /api/v1/. Interactive docs at /docs (Swagger) and /redoc.

Group Endpoints
Health GET /health
Auth POST /auth/register, POST /auth/login
Tickers POST /tickers, GET /tickers, DELETE /tickers/{symbol}
OHLCV POST /ohlcv, GET /ohlcv/{symbol}
Ingestion POST /ingestion/fetch/{symbol}
Indicators GET /indicators/{symbol}/{type}, GET /indicators/{symbol}/ema-cross
S/R Levels GET /sr-levels/{symbol}
Sentiment GET /sentiment/{symbol}
Fundamentals GET /fundamentals/{symbol}
Scores GET /scores/{symbol}, GET /rankings, PUT /scores/weights
Trades GET /trades, GET /trades/{symbol}, GET /trades/{symbol}/history, GET /trades/activation, GET /trades/performance
Paper Trades GET /paper-trades, POST /paper-trades, POST /paper-trades/{id}/close
Market / Regime GET /market/regime, GET /regime/monitor, GET/PUT /regime/config, GET /regime/history, GET /regime/event-study, GET/PUT /regime/fundamentals, GET /backtest/report
Jobs GET /jobs/running
Watchlist GET /watchlist, POST /watchlist/{symbol}, DELETE /watchlist/{symbol}
Admin GET /admin/users, POST /admin/users, PUT /admin/users/{id}/access, PUT /admin/users/{id}/password, PUT /admin/settings/registration, GET /admin/settings, PUT /admin/settings/{key}, GET/PUT /admin/settings/recommendations, GET/PUT /admin/settings/ticker-universe, POST /admin/tickers/bootstrap, POST /admin/data/cleanup, GET /admin/jobs, POST /admin/jobs/{name}/trigger, PUT /admin/jobs/{name}/toggle, GET /admin/pipeline/readiness

Development Setup

Prerequisites

  • Python 3.12+
  • PostgreSQL (via Homebrew on macOS: brew install postgresql@17)
  • Node.js 18+ and npm

Backend Setup

# Create and activate virtual environment
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Configure environment
cp .env.example .env
# Edit .env with your values (see Environment Variables below)

# Start PostgreSQL and create database
brew services start postgresql@17
createdb stock_data_backend
createuser stock_backend

# Run migrations
alembic upgrade head

# Start the backend
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

A default admin/admin account is created on first startup. Open http://localhost:8000/docs for Swagger UI.

Frontend Setup

cd frontend
npm install
npm run dev

Open http://localhost:5173 for the Signal Dashboard. The Vite dev server proxies /api/v1/ requests to the backend at http://127.0.0.1:8000.

Frontend Build

cd frontend
npm run build     # TypeScript check + production build → frontend/dist/
npm run preview   # Preview the production build locally

Tests

# Backend tests (in-memory SQLite — no PostgreSQL needed)
pytest tests/ -v

# Frontend: there is no test suite — `npm test` calls vitest, which is not
# installed. The frontend check is the full TypeScript build:
cd frontend
npm run build

Environment Variables

Configure in .env (copy from .env.example):

Variable Required Default Description
DATABASE_URL Yes PostgreSQL connection string (postgresql+asyncpg://...)
JWT_SECRET Yes Random secret for JWT signing
JWT_EXPIRY_MINUTES No 60 JWT token expiry
ALPACA_API_KEY For OHLCV Alpaca Markets API key
ALPACA_API_SECRET For OHLCV Alpaca Markets API secret
GEMINI_API_KEY For sentiment Google Gemini API key
GEMINI_MODEL No gemini-2.0-flash Gemini model name
OPENAI_API_KEY For sentiment (OpenAI path) OpenAI API key
OPENAI_MODEL No gpt-4o-mini OpenAI model name
OPENAI_SENTIMENT_BATCH_SIZE No 5 Micro-batch size for sentiment collector
FMP_API_KEY Optional (fundamentals) Financial Modeling Prep API key (first provider in chain)
FINNHUB_API_KEY Optional (fundamentals) Finnhub API key (fallback provider)
ALPHA_VANTAGE_API_KEY Optional (fundamentals) Alpha Vantage API key (fallback provider)
FRED_API_KEY Optional (regime) FRED key for the regime monitor (VIX, credit spreads)
TELEGRAM_BOT_TOKEN Optional (alerts) Telegram bot token for alerts (can also be set in Admin)
TELEGRAM_CHAT_ID Optional (alerts) Telegram chat id for alerts
DATA_COLLECTOR_FREQUENCY No daily OHLCV collection schedule (legacy — see note below)
SENTIMENT_POLL_INTERVAL_MINUTES No 30 Sentiment polling interval
FUNDAMENTAL_FETCH_FREQUENCY No weekly Fundamentals fetch cadence
RR_SCAN_FREQUENCY No daily R:R scanner schedule
FUNDAMENTAL_RATE_LIMIT_RETRIES No 3 Retries per ticker on fundamentals rate-limit
FUNDAMENTAL_RATE_LIMIT_BACKOFF_SECONDS No 15 Base backoff seconds for fundamentals retry (exponential)
DEFAULT_WATCHLIST_AUTO_SIZE No 10 Auto-watchlist size
DEFAULT_RR_THRESHOLD No 1.5 Minimum R:R ratio for setups
DB_POOL_SIZE No 5 Database connection pool size
LOG_LEVEL No INFO Logging level

Note: Pipeline timing (daily / intraday / fundamentals cron, timezone) is configured at runtime in Admin → Jobs and stored in the DB — the *_FREQUENCY env vars are legacy fallbacks for the few jobs still on interval triggers (alerts, universe sync).

Production Deployment (Debian 12)

Ongoing deploys are automated. Every push to main triggers the Gitea Actions pipeline (.gitea/workflows/deploy.yml): lint → test → rsync to the server → pip installalembic upgrade head → restart signalplatform.service → health check. There is no manual deploy step; the steps below are only for provisioning a new server.

1. Install dependencies

sudo apt update && sudo apt install -y python3.12 python3.12-venv postgresql nginx rsync

2. Create the deploy user

The pipeline connects over SSH as this user; it owns the app directory and needs passwordless permission to restart the service:

sudo useradd -m deploy
sudo mkdir -p /opt/signalplatform
sudo chown deploy:deploy /opt/signalplatform
echo 'deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart signalplatform.service' | sudo tee /etc/sudoers.d/deploy-restart

3. Configure the pipeline (Gitea repo settings)

Variables: DEPLOY_HOST, DEPLOY_USER (deploy), DEPLOY_PATH (/opt/signalplatform), SSH_KNOWN_HOSTS (host fingerprint), SSH_PORT. Secret: SSH_PRIVATE_KEY (matching the deploy user's authorized key).

4. Configure the app

# After the first pipeline run has synced the files:
cp /opt/signalplatform/.env.example /opt/signalplatform/.env
# Edit .env with production values (strong JWT_SECRET, real API keys, etc.)

.env is excluded from the rsync, so it survives every deploy.

5. Database

Either trigger the workflow manually (workflow_dispatch) with run_setup_db: true — the deploy then runs deploy/setup_db.sh instead of plain migrations — or run it once by hand:

DB_NAME=stock_data_backend DB_USER=stock_backend DB_PASS=strong_password ./deploy/setup_db.sh

6. Systemd service

sudo cp deploy/signalplatform.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now signalplatform

The unit runs uvicorn on 127.0.0.1:8998 as the deploy user, with WorkingDirectory=/opt/signalplatform.

7. Nginx reverse proxy

sudo cp deploy/nginx.conf /etc/nginx/sites-available/signalplatform
sudo ln -s /etc/nginx/sites-available/signalplatform /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Nginx serves the frontend static files from frontend/dist/ (built on the CI runner and rsynced) and proxies /api/v1/ to the backend.

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d signal.thiessen.io

Verify

curl https://signal.thiessen.io/api/v1/health

Project Structure

app/
├── main.py              # FastAPI app, lifespan, router wiring
├── config.py            # Pydantic settings from .env
├── database.py          # Async SQLAlchemy engine + session
├── dependencies.py      # DI: DB session, auth guards
├── exceptions.py        # Exception hierarchy
├── middleware.py         # Global error handler → JSON envelope
├── cache.py             # LRU cache with per-ticker invalidation
├── scheduler.py         # APScheduler job definitions
├── models/              # SQLAlchemy ORM models
├── schemas/             # Pydantic request/response schemas
├── services/            # Business logic layer
├── providers/           # External data provider integrations
└── routers/             # FastAPI route handlers

frontend/
├── index.html           # SPA entry point
├── vite.config.ts       # Vite config with API proxy
├── tailwind.config.ts   # Tailwind + glassmorphism theme
├── package.json
└── src/
    ├── App.tsx          # Route definitions
    ├── main.tsx         # React entry point
    ├── api/             # Axios API client modules (one per resource)
    ├── components/
    │   ├── admin/       # User table, job controls, settings, data cleanup
    │   ├── auth/        # Protected route wrapper
    │   ├── charts/      # Canvas candlestick chart
    │   ├── layout/      # App shell, sidebar, mobile nav
    │   ├── rankings/    # Rankings table, weights form
    │   ├── scanner/     # Trade table
    │   ├── ticker/      # Sentiment panel, fundamentals, indicators, S/R overlay
    │   ├── ui/          # Badge, toast, skeleton, score card, confirm dialog
    │   └── watchlist/   # Watchlist table, add ticker form
    ├── hooks/           # React Query hooks (one per resource)
    ├── lib/             # Types, formatting utilities
    ├── pages/           # Page components (Login, Register, Dashboard, Market, Signals, Regime, Ticker, Admin)
    ├── stores/          # Zustand auth store
    └── styles/          # Global CSS with glassmorphism classes

deploy/
├── nginx.conf           # Reverse proxy + static file serving
├── setup_db.sh          # Idempotent DB setup script
└── stock-data-backend.service  # systemd unit

tests/
├── conftest.py          # Fixtures, strategies, test DB
├── unit/                # Unit tests
└── property/            # Property-based tests (Hypothesis)

Maintainer Guide

Context for whoever — human or AI — continues this work. The owner pushes straight to main on a self-hosted Gitea remote (no PRs) and deploys manually.

Invariants — do not break these

  • app/services/qualification.py is mirrored in frontend/src/lib/qualification.ts. Any gate change must land in both, or the UI's "qualified" flags silently disagree with the server.
  • Live scan and backtest share the same pure functions. The backtest replays production logic through DB-free functions (compute_technical_from_arrays, compute_momentum_from_closes, detect_sr_levels, the recommendation helpers). New strategy logic must stay in pure functions consumed by both paths, or the backtest stops measuring what production actually does.
  • One S/R model app-wide: sr_service.detect_sr_levels + cluster_sr_zones (2% tolerance) feed the chart, alerts, and target generation identically.
  • The outcome evaluator evaluates ALL setups, not just qualified ones — unqualified setups are the control group that makes the Track Record meaningful.
  • SystemSetting access goes through app/services/settings_store.py — don't query the model directly.
  • Time-series data gets a real table (see benchmark_prices, regime_snapshots); SystemSetting JSON is only for config and cached reports.
  • Style: surgical changes, minimal new files; extend existing services rather than adding parallel ones.

Where the strategy lives

Concern File
Composite + 5 dimension scores, weights app/services/scoring_service.py
12-1 momentum ranking (the validated factor) app/services/momentum_service.py
Setup construction (ATR stop, S/R targets) app/services/rr_scanner_service.py
Confidence, targets, reach-probability, action app/services/recommendation_service.py
Activation gate predicate (mirrored in TS) app/services/qualification.py
Gate defaults / admin config app/services/admin_service.py (ACTIVATION_DEFAULTS)
Backtest + factor rank-IC harness ("Signal edge") app/services/backtest_service.py
Outcome resolution (target/stop/expired/ambiguous) app/services/outcome_service.py
Paper trades + trailing auto-exit app/services/paper_trade_service.py
S/R detection & zone clustering app/services/sr_service.py
SPY benchmark for paper-trade alpha app/services/benchmark_service.py
Pipelines & job registration app/scheduler.py

Verifying changes

pytest tests/ -q                 # backend; in-memory SQLite, no Postgres needed
cd frontend && npm run build     # full tsc check — this IS the frontend "test"
  • npm test in frontend/ is dead (vitest isn't installed; there are no frontend test files). Use npm run build.
  • Backend tests that exercise services which commit() need a plain session fixture, not the rolling-back db_session — copy the pattern in tests/unit/test_rr_scanner_integration.py.
  • ruff reports ~11 pre-existing errors in old test files; those are not regressions.

Deploying

Automated by Gitea Actions (.gitea/workflows/deploy.yml) on every push to main: lint (ruff check app/) → test (pytest; alembic upgrade head validated against a real Postgres 16 service; frontend npm ci && npm run build) → deploy (frontend built on the runner, rsync to the server excluding .env, pip install -e ., alembic upgrade head, restart signalplatform.service, health check on 127.0.0.1:8998). Deploys are serialized by a concurrency group so overlapping pushes can't race.

Practical consequences:

  • A ruff error in app/ or any failing backend test blocks the deploy. (CI lints only app/, so the pre-existing ruff noise in old test files doesn't.)
  • Migrations run automatically on deploy — no manual alembic step. A migration that only works on SQLite will fail CI against Postgres, by design.
  • Pushing to main is deploying to production — there is no separate release step.
  • After a gate or scanner change ships, trigger an R:R scan (Admin → Jobs) so live setups pick up new fields.

Roadmap (agreed June 2026)

  1. Forward paper-test the momentum book — the out-of-sample proof the backtest can't give. Watch Signals → Track Record (live vs backtest).
  2. Full IBKR integration — read real positions, overlay entries/stops on charts, alert on holdings' score deterioration. (Paper trading, the lighter alternative, is done.)
  3. Strategy experiments in the order listed under Strategy Status above — each one goes through the factor harness first.
S
Description
No description provided
Readme 2.5 MiB
Languages
Python 66.8%
TypeScript 32.6%
CSS 0.4%