diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fa004b6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +# ── Stage 1: Build the SvelteKit UI ────────────────────────────────────────── +FROM node:22-alpine AS ui-builder +WORKDIR /app + +COPY ui/package*.json ./ui/ +RUN cd ui && npm ci --legacy-peer-deps + +# UI source + shared server types (needed for $types alias) +COPY ui/ ./ui/ +COPY server/ ./server/ + +WORKDIR /app/ui +ENV NODE_ENV=production +RUN npm run build + +# ── Stage 2: Runtime (API + compiled UI) ───────────────────────────────────── +FROM node:22-alpine +WORKDIR /app + +# API dependencies (tsx needed at runtime for ESM TypeScript) +COPY package*.json ./ +RUN npm ci + +# API source +COPY bin/ ./bin/ +COPY server/ ./server/ +COPY tsconfig*.json ./ + +# Pre-built UI from stage 1 +COPY --from=ui-builder /app/ui/build ./ui/build +COPY --from=ui-builder /app/ui/package*.json ./ui/ +RUN cd ui && npm ci --omit=dev --legacy-peer-deps + +# SQLite volume mount point +RUN mkdir -p /app/data + +ENV NODE_ENV=production +ENV DB_PATH=/app/data/market-screener.db +ENV PORT=3000 +ENV UI_PORT=3001 + +EXPOSE 3000 3001 + +# Run both processes; if either dies the container exits +CMD ["npx", "concurrently", \ + "--kill-others", \ + "--names", "api,ui", \ + "tsx bin/server.ts", \ + "node ui/build/index.js"] diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..f7848ad --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,19 @@ +FROM node:22-alpine +WORKDIR /app + +# Install all deps (tsx is needed at runtime for ESM + TypeScript) +COPY package*.json ./ +RUN npm ci + +# Copy source +COPY bin/ ./bin/ +COPY server/ ./server/ +COPY tsconfig*.json ./ + +# SQLite database lives here — mount a volume at /app/data in compose +RUN mkdir -p /app/data +ENV DB_PATH=/app/data/market-screener.db +ENV NODE_ENV=production +EXPOSE 3000 + +CMD ["npx", "tsx", "bin/server.ts"] diff --git a/Dockerfile.ui b/Dockerfile.ui new file mode 100644 index 0000000..97f4c58 --- /dev/null +++ b/Dockerfile.ui @@ -0,0 +1,31 @@ +FROM node:22-alpine AS builder +WORKDIR /app + +# Copy UI package files and install +COPY ui/package*.json ./ui/ +RUN cd ui && npm ci --legacy-peer-deps + +# Copy UI source + shared server types (needed for $types alias resolution) +COPY ui/ ./ui/ +COPY server/ ./server/ + +WORKDIR /app/ui + +# adapter-auto picks adapter-node when NODE_ENV=production in a container +ENV NODE_ENV=production + +RUN npm run build + +# --- Runtime stage --- +FROM node:22-alpine +WORKDIR /app + +COPY --from=builder /app/ui/build ./build +COPY --from=builder /app/ui/package*.json ./ +RUN npm ci --omit=dev --legacy-peer-deps + +EXPOSE 3001 +ENV PORT=3001 +ENV HOST=0.0.0.0 + +CMD ["node", "build"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b39e7ec --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +services: + app: + build: . + restart: unless-stopped + ports: + - "127.0.0.1:3000:3000" + - "127.0.0.1:3001:3001" + environment: + NODE_ENV: production + DB_PATH: /app/data/market-screener.db + API_KEY: ${API_KEY:-} + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + SIMPLEFIN_ACCESS_URL: ${SIMPLEFIN_ACCESS_URL:-} + SIMPLEFIN_SETUP_TOKEN: ${SIMPLEFIN_SETUP_TOKEN:-} + CLIENT_ORIGIN: ${CLIENT_ORIGIN:-http://localhost} + volumes: + - db_data:/app/data + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"] + interval: 30s + timeout: 5s + retries: 3 + +volumes: + db_data: diff --git a/glossary-prototype.html b/glossary-prototype.html new file mode 100644 index 0000000..bdcf016 --- /dev/null +++ b/glossary-prototype.html @@ -0,0 +1,1368 @@ + + +
+ + +TICKER ? |
+ PRICE ? |
+ SIGNAL ? |
+ SCORE ? |
+ CAP ? |
+ STYLE ? |
+ FLAGS ? |
+
|---|---|---|---|---|---|---|
| + | + | + + | ++ | + + | ++ + | ++ |
| WDAY | +$137.88 | ++ ⚡ Speculation + why? + | +
+
+
+
+
+
+
+
+ 14
+ |
+ LARGE CAP | +HIGH GROWTH | +
+
+
+ ⚠ 3
+
+
+ Risk Flags
+ ⚠ Significant drawdown: -43% in 52 weeks
+ ⚠ 46% off 52-week high
+ ⚠ DCF: 79% margin of safety (undervalued)
+ |
+
|
+
+
+
+
+
+
+
+
+ Metrics
+
+
+
+ P/E ? 43.0 PEG ? 0.57 ROE% ? 10.9% OP MGN% ? 13.3% GROSS M% ? 75.8% FCF YLD% ? 11.3% D/E ? 0.57 52W CHG ? -43.1% FROM HIGH ? -46.4% ANALYST ? Buy UPSIDE ? +24.1% DCF SAFETY ? +79.4%
+ MKT ✓
+ GRAHAM ✗ — P/E 43 > 15×
+
+
+ Risk Flags
+
+ ⚠ -43% in 52W
+ ⚠ 46% off high
+ ⚠ DCF +79% (undervalued)
+
+
+
+
+ Factor Scores
+ (Mkt-Adj) — click any factor to learn more
+
+
+
+
+
+
+
+
+ PEG Ratio
+ +3 STRONG
+
+ PEG 0.57 — below 1.0 threshold. Paying less than growth justifies. (Gate: < 1.0)
+
+
+
+
+
+ FCF Yield
+ +3 STRONG
+
+ FCF yield 11.3% — strongly positive free cash flow. High weight metric. (Gate: > 0%)
+
+
+
+
+
+ Analyst Consensus
+ +2 GOOD
+
+ Rated Buy by Wall St. (Yahoo mean ≤ 2.5). Requires ≥ 3 analysts. Upside: +24.1% to target.
+
+
+
+
+
+ DCF Margin of Safety
+ +2 GOOD
+
+ Intrinsic value 79% above current price. Stock appears undervalued vs DCF model. (Gate: ≥ 20%)
+
+
+
+
+
+ Return on Equity
+ +1 WEAK
+
+ ROE 10.9% — above minimum but below the 15% preferred threshold. Partial score.
+
+
+
+
+
+ Operating Margin
+ +1 WEAK
+
+ Op margin 13.3% — positive but below strong threshold for software sector. (Preferred: > 20%)
+
+
+
+
+
+ Gross Margin
+ +1 GOOD
+
+ Gross margin 75.8% — strong for SaaS. High pricing power and product economics.
+
+
+
+
+
+ Revenue Growth
+ +1 MODERATE
+
+ Revenue growth is positive but below the high-growth threshold. Contributes partial score.
+
+ |
+ ||||||
| NVDA | +$208.64 | +✅ Strong Buy | +13 |
+ MEGA CAP | +HIGH GROWTH | +⚠ 3 Risk Flags ⚠ High volatility (β 2.20) ⚠ Analyst 43% upside to target ⚠ DCF: 153% above intrinsic value |
+
| BAC | +$53.63 | +⊙ Neutral | +3 |
+ MEGA CAP | +GROWTH | +— | +
| TRET | +$0.35 | +✕ Avoid | +0 |
+ MICRO CAP | +HIGH GROWTH | +— | +
Passes market-adjusted gates but fails Graham's strict fundamental rules. This stock is attractive at today's market valuations, but would not meet a value investor's stricter criteria. Treat as a momentum or growth play — not a deep value position.
+