Skip to content

Project · 2026

Fidash

Self-hosted market research and prediction-market trading dashboard backed by a FastAPI service and scheduled data pipelines.

Role: Creator
  • Python
  • FastAPI
  • Streamlit
  • SQLAlchemy
  • PostgreSQL
  • Plotly
  • APScheduler
  • Docker

Project snapshot

FiDash Terminal showing the live S&P 500 scanner workspace and historical runs.
Live deployment screenshot.
Status
Active self-hosted research platform; one capped live pilot bot trades on Kalshi while the rest run in paper mode.
Runtime
Private Python service with a dashboard and scheduled data pipelines.
Data
Scheduled ingestion from Polygon, NewsAPI, AlphaVantage, Alpaca, RSS feeds, and Kalshi markets.
Ops
PostgreSQL with Alembic migrations, Docker Compose monitoring, Prometheus, and Grafana.

Technical proof

  • Layered FastAPI app with routers for tickers, news, sentiment, scans, backtests, alerts, watchlists, paper trading, and Kalshi bots.
  • Trading bots follow a data-loader/model/bot pattern with walk-forward backtesting and cross-restart deduplication.
  • Options scanner generates strategy cards for spreads, iron condors, strangles, and straddles.

What it is

Fidash is a private financial research platform that aggregates market news, runs sentiment analysis, and evaluates automated trading strategies. A Streamlit dashboard communicates with a FastAPI backend over a private service boundary.

Architecture tour

Three processes, one codebase

Fidash ships as one Python package (senti_stocks) with three separate runtime entry points: a FastAPI API (api/main.py), a Streamlit dashboard (ui/dashboard.py), and an APScheduler worker (scheduler/worker.py). The dashboard never touches the database directly. It talks to the API over HTTP through an async client (ui/dashboard_api.py), the same boundary any external caller would use. That split exists so the scheduler can keep running background jobs even if the dashboard process is restarted or redeployed independently.

Request handling follows one path everywhere: api/v1/endpoints/* parses the HTTP request and wires dependencies, domains/*/controller.py or a services/*.py module does the orchestration and business logic, dal/repositories*.py reads and writes through SQLAlchemy, and dal/models.py plus schemas/*.py keep the persisted shape and the API contract as separate concerns. I counted 35 endpoint router files under api/v1/endpoints/, grouped into domain packages (domains/auth, domains/kalshi_bot, domains/news, domains/scans, domains/sentiment, domains/tickers, domains/workflows, and more) so a feature change usually stays inside one vertical slice instead of touching a shared file.

Why the bots are their own subsystem

The trading bots (weather_bot, oil_bot, gas_bot, jobless_claims_bot, fed_rate_bot, cpi_bot, sp500_bot, plus the kalshi_arb and polymarket_arb scanners) live outside the domain layer, under services/, each following a data_loader.py -> model.py -> bot.py pattern. I split it that way because bots aren’t request/response code: they run on their own loop cadence, hold open state across restarts, and need to survive a process crash without double-submitting an order. bot_runtime.py is the single registry that knows how to auto-start every bot from its saved config in data/bot_configs/ on API restart, and order_identity.py plus JSONL-backed dedup keys stop a bot from re-placing the same order if it restarts mid-cycle. Going from paper to live isn’t a flag flip: promotion_gate.py enforces six checks (sample size, calibration, Sharpe, max drawdown, cohort win rate, filter stability) before a bot is allowed to place a real order.

Live-order authorization used to be a check each bot implemented on its own, and one of those per-bot copies had a bug that let it pass vacuously. I replaced all of them with a single fail-closed gate, authorize_live_start() in services/live_authorization.py, that every bot now calls through the same code path instead of reimplementing. On top of that gate sits a locked live-pilot profile (services/live_pilot.py) that only one bot is configured to use: it hardcodes that bot’s identity, requires an explicit pilot-profile id, and caps simultaneous downside as a fixed percentage of a settled-cash baseline captured at pilot start. That’s the only bot currently trading with real money on Kalshi; the rest run in paper mode against the same promotion gate.

Data plane and validation

APScheduler jobs (scheduler/jobs/bots.py, modeling.py, portfolio.py, workflows.py, maintenance.py) pull from Polygon, NewsAPI, Alpha Vantage, Alpaca, RSS feeds, and Kalshi, then land in Postgres (SQLite locally) through Alembic-migrated models. Sentiment scoring runs on VADER plus onnxruntime-served models, and the options scanner builds strategy cards (put_credit_spread, iron_condor, short_strangle, short_straddle) off scan output. I validate the whole thing with 253 test files under tests/ (pytest, covering everything from test_promotion_gate.py to bot-specific loop characterization tests like test_gas_bot_loop_characterization.py) plus a Playwright suite under e2e/ for browser-level dashboard flows. test_backtest_leakage.py exists specifically to catch a model seeing data from the future during a backtest, which is the failure mode that would otherwise make a strategy look better than it is.

Where it’s at

The project is in active development, with strategies evaluated through a mix of live and paper workflows under defined risk limits.