A portfolio research and hypothesis-testing platform. Built in 24 h for the Colosseum Idearum hackathon.
- Portfolio monitor — live prices, P/L, earnings, key stats. Yahoo-backed, with a Gemini-scored news feed for every position.
- Profile interview — short scenario-based quiz that derives three personality scores (volatility tolerance, uncertainty comfort, trajectory taste) and turns them into constraint axes for the agent.
- News pipeline — background worker polls Yahoo, scores articles per ticker through Gemini (sentiment + materiality + impact), persists to SQLite.
- Hypothesis Lab — Math mode — free-text strategy ("Buy AAPL when PE < 27, sell at PE > 30") → Gemini parser → vectorised backtest with S&P 500 benchmark, indicators, fundamentals.
- Hypothesis Lab — Complex mode — qualitative research ("Should I buy BYD?") via an evidence-graph fan-out: parses the prompt, runs 4 branches in parallel (current news, historical analogues + per-event backtests, technical signals, peer comparison), and synthesises a verdict + certainty + actionable recommendation. Optionally "watched" for daily re-evaluation as fresh news arrives.
- AI portfolio scoring — Python FastAPI service that uses AWS Bedrock (Claude Sonnet 4.6) to interpret user constraints, derive scoring axes ("spider web"), and grade your portfolio against them with a recommendations + risk report.
┌──────────────────────┐ ┌──────────────────────────────────────────┐
│ React 19 + Vite SPA │──/api/*──► Express (TypeScript) │
│ - Recharts │ │ - news poller + Gemini scorer │
│ - Framer Motion │ │ - backtest engine + price cache │
│ - Tailwind v4 │ │ - complex hypothesis orchestrator │
└─────────┬────────────┘ │ - watch worker (daily re-eval) │
│ │ - better-sqlite3 (WAL) │
│ └──────────────────────────────────────────┘
│
└──/analyze/*──► Python FastAPI / uvicorn
- Bedrock (Claude Sonnet 4.6)
- yfinance + pandas
Two LLMs intentionally: Gemini 2.5 Flash via Vertex AI for the high-throughput news/backtest/complex stack (cheap, fast), Claude Sonnet 4.6 via AWS Bedrock for the qualitative portfolio scoring + recommendation flow.
backend/ Express + TypeScript API + workers
src/news/ Yahoo poller, Gemini scorer, SQLite
src/backtest/ strategy parser + simulator + indicators
src/complex/ evidence-graph hypothesis engine + watch worker
src/portofolioAnalysis/ Python FastAPI service (Bedrock)
frontend/ React 19 + Vite + Tailwind v4
deploy/ EC2 bootstrap + systemd units + Caddy + push.sh
- Node 20+
- Python 3.11+
gcloudCLI signed in (gcloud auth application-default login) for Vertex AI access- AWS credentials with Bedrock model access to
us.anthropic.claude-sonnet-4-6(for the Python service)
cd backend
npm install
cp deploy/.env.example .env # or create one manually:
# GEMINI_PROJECT=<your-gcp-project>
# GEMINI_LOCATION=global
# POLL_INTERVAL_MS=300000
npm run dev # http://localhost:3001cd frontend
npm install
npm run dev # http://localhost:5173Vite's dev proxy forwards /api/* to :3001 and /analyze/* to :8000.
cd backend/portofolioAnalysis
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -m uvicorn api:app --host 127.0.0.1 --port 8000AWS credentials come from your environment (AWS_PROFILE, ~/.aws/credentials, or env vars). Region must be us-west-2.
For a public demo URL backed by a single EC2 instance. End-to-end takes ~10 min.
AWS Console → EC2 → Launch instances:
- AMI: Ubuntu Server 22.04 LTS (64-bit x86)
- Instance type:
t3.small(ort3.mediumfor headroom) - Storage: 20 GiB gp3
- Key pair: pick or create one — save the
.pem,chmod 400it - Security group inbound:
22SSH from My IP80HTTP from Anywhere-IPv4443HTTPS from Anywhere-IPv4
Once running, note the Public IPv4 DNS (e.g. ec2-x-x-x-x.us-west-2.compute.amazonaws.com).
From your laptop:
scp -i ~/your-key.pem deploy/bootstrap.sh ubuntu@<host>:/tmp/
ssh -i ~/your-key.pem ubuntu@<host> "sudo bash /tmp/bootstrap.sh"Installs Node 20, build tools (for the better-sqlite3 native compile), Caddy, and creates the idearum system user.
rsync -avz --exclude node_modules --exclude .git --exclude '*.db' --exclude '*.db-*' \
-e "ssh -i ~/your-key.pem" \
./ ubuntu@<host>:/tmp/idearum/
ssh -i ~/your-key.pem ubuntu@<host> \
"sudo mv /tmp/idearum /opt/idearum/app && sudo chown -R idearum:idearum /opt/idearum/app"On the EC2 box, create the two .env files:
/opt/idearum/app/backend/.env (Node service):
GEMINI_PROJECT=<your-gcp-project>
GEMINI_LOCATION=global
GOOGLE_APPLICATION_CREDENTIALS=/opt/idearum/app/backend/adc.json
PORT=3001
Copy your local ADC file up:
scp -i ~/your-key.pem ~/.config/gcloud/application_default_credentials.json \
ubuntu@<host>:/tmp/adc.json
ssh -i ~/your-key.pem ubuntu@<host> \
"sudo mv /tmp/adc.json /opt/idearum/app/backend/adc.json && \
sudo chown idearum:idearum /opt/idearum/app/backend/adc.json && \
sudo chmod 600 /opt/idearum/app/backend/adc.json"/opt/idearum/app/backend/portofolioAnalysis/.env (Python service):
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_SESSION_TOKEN=...
AWS_DEFAULT_REGION=us-west-2
(In a Workshop Studio setup, click "Get AWS CLI credentials" in the sidebar and paste those values — without the export prefix.)
On the EC2 box:
# Backend (TS → dist + native better-sqlite3 build, ~2 min)
sudo -u idearum bash -c 'cd /opt/idearum/app/backend && npm ci && npm run build'
# Frontend (bake the /api base into the bundle)
sudo -u idearum bash -c 'cd /opt/idearum/app/frontend && npm ci && VITE_API_BASE=/api npm run build'
# Python venv + deps
sudo apt-get install -y python3 python3-venv python3-pip
sudo -u idearum python3 -m venv /opt/idearum/app/backend/portofolioAnalysis/.venv
sudo -u idearum /opt/idearum/app/backend/portofolioAnalysis/.venv/bin/pip install \
-r /opt/idearum/app/backend/portofolioAnalysis/requirements.txt
# systemd units (one for Node, one for Python uvicorn)
sudo install -m 644 /opt/idearum/app/deploy/idearum.service /etc/systemd/system/idearum.service
sudo install -m 644 /opt/idearum/app/deploy/idearum-py.service /etc/systemd/system/idearum-py.service
# Caddy reverse-proxy (HTTP-only on :80; for auto-TLS pass a real domain)
sudo bash -c 'sed "s|{{DOMAIN}}|:80|g" /opt/idearum/app/deploy/Caddyfile > /etc/caddy/Caddyfile'
sudo systemctl daemon-reload
sudo systemctl enable --now idearum idearum-py
sudo systemctl restart caddycurl http://localhost:3001/health # Node API
curl http://localhost:8000/health # Python API
curl http://localhost/api/health # through Caddy
sudo journalctl -u idearum -u idearum-py -n 30 --no-pagerOpen http://<public-DNS>/ in your browser.
After bootstrap, push changes from your laptop with one command:
HOST=<public-DNS> KEY=~/your-key.pem bash deploy/push.sh # all
HOST=<public-DNS> KEY=~/your-key.pem bash deploy/push.sh backend # node only
HOST=<public-DNS> KEY=~/your-key.pem bash deploy/push.sh frontend # SPA only
HOST=<public-DNS> KEY=~/your-key.pem bash deploy/push.sh python # FastAPI onlyThe script rsyncs the relevant tree, runs npm ci / pip install only when lockfiles change, rebuilds, and restarts the right services. Typical round-trip 15–30 s.
- Yahoo Finance from cloud IPs: foreign-exchange tickers (
*.SW,*.DE,*.CO,*.AS) are sometimes refused from US-region EC2 IPs. US tickers work fine. Mitigation: stop/start the instance for a fresh IP, or demo with US tickers. - Workshop AWS credentials expire (typically a few hours). Re-pull from the workshop sidebar and rewrite
portofolioAnalysis/.env, thensudo systemctl restart idearum-py. - Bedrock model access for Claude Sonnet 4.6 may need a one-time use-case form submission in the Bedrock console (model catalog page).
- SQLite lives at
/opt/idearum/app/backend/news.dbon the box. It persists across deploys;news,articles,hypothesis_runs,analysis_cache,portfolio_positionstables are auto-created viaCREATE TABLE IF NOT EXISTS.
Hackathon project — no license, all rights reserved (for now).