AI-powered stock analysis with event-driven architecture using Motia framework and Next.js 15
AI-powered stock analysis platform with event-driven backend (Motia) and Next.js 15 frontend. Provides real-time DCF analysis, business quality ratings, and portfolio recommendations.
This is a Turborepo monorepo with two independent services sharing a database:
**Critical Rule**: Backend and web communicate ONLY via HTTP APIs, real-time streams, and shared database. Never import backend code into web or vice versa—use shared packages (`@repo/types`, `@repo/db`).
Every backend feature is a **Step** (`*.step.ts`) with two exports:
1. **API Steps**: HTTP endpoints that emit events and return immediately
2. **Event Steps**: Background processing triggered by events (AI analysis, DCF calculations)
3. **Cron Steps**: Scheduled tasks
4. **Stream Steps**: Real-time WebSocket updates (`.stream.ts`)
```typescript
// API Step: Emit event immediately
await emit({ topic: "process-stock-analysis", data: { symbol } });
// Event Step: Subscribe to topic
export const config = {
subscribes: ["process-stock-analysis"],
// ...
};
```
**Write state:**
```typescript
await state.set("dcf-results", traceId, dcfData);
```
**Read state (ALWAYS use validation):**
```typescript
import { getValidatedState } from "./lib/statehooks";
const dcf = await getValidatedState(
"dcf-results",
dcfResultSchema,
state,
traceId,
logger
);
```
**Why validation is required**: Ensures type safety and runtime correctness between decoupled event-driven steps.
Update clients via WebSocket streams:
```typescript
streams["stock-analysis-stream"].set("analysis", traceId, {
status: "Calculating DCF...",
progress: 60
});
```
Clients automatically receive updates via `@motiadev/stream-client-react`.
After ANY config change in backend:
```bash
cd apps/backend
npx motia generate-types
```
This auto-generates `types.d.ts` with type-safe `Handlers` interface. Every step MUST export its Zod schema to `packages/types`.
1. **Business Quality** (`BusinessQualityCard`): Moat strength, tier rating, market structure
2. **Valuation Reality** (`FeasibilityGauge`): Price vs. growth feasibility (5 scenarios)
3. **Action** (`AllocationCard`): Buy/Hold/Sell recommendation, portfolio role, risk level
4. **Sensitivity** (`SensitivityTable`): DCF matrix (discount rate × terminal growth)
Tailwind CSS with `lucide-react` icons for consistency.
Vercel AI SDK with Gemini models:
```typescript
import { google } from "@ai-sdk/google";
import { generateObject } from "ai";
const result = await generateObject({
model: google("gemini-2.0-flash-exp"),
schema: growthJudgementSchema,
prompt: "Analyze growth trajectory...",
});
```
**Access Pattern**: Backend writes via `@repo/db`, frontend reads via server actions/API routes.
```bash
pnpm dev # Root (runs both backend + web)
cd apps/backend
pnpm dev # Starts Motia Workbench at :3001
npx motia generate-types # Regenerate types after config changes
cd packages/db
npx prisma migrate dev # Create migration
npx prisma studio # Visual DB browser
pnpm run check-types # Validates all workspace types
```
Always export schemas from `packages/types/src/*.ts`:
```typescript
// packages/types/src/dcf.ts
export const dcfResultSchema = z.object({...});
// Backend or frontend
import { dcfResultSchema } from "@repo/types";
```
Never use `state.get()` directly—use `getValidatedState()`:
```typescript
const dcf = await getValidatedState(
"dcf",
dcfResultSchema,
state,
traceId,
logger
);
```
Declare all emitted events in step config:
```typescript
export const config = {
emits: ["finish-dcf"],
// ...
};
// Downstream step subscribes
export const otherConfig = {
subscribes: ["finish-dcf"],
// ...
};
```
Stream status updates at each step boundary:
```typescript
streams["stock-analysis-stream"].set("analysis", traceId, {
status: "Calculating DCF...",
progress: 60,
currentStep: "dcf"
});
```
**⚠️ CRITICAL**: Frontend and backend MUST be deployed separately.
```
Frontend (apps/web) → Vercel (Next.js serverless)
Backend (apps/backend) → Motia Cloud (managed Motia hosting)
Database → Vercel Postgres (shared)
```
```
Frontend (apps/web) → Vercel
Backend (apps/backend) → Railway/Render (Docker + Redis)
Database → Vercel Postgres
```
See `.github/DEPLOYMENT.md` for step-by-step deployment instructions.
1. **Missing Types**: If TypeScript errors appear in handlers, run `npx motia generate-types`
2. **Direct State Access**: Using `state.get()` without validation causes silent type mismatches
3. **Heavy API Logic**: API steps should emit events, not perform compute (use Event steps)
4. **Schema Drift**: Always re-export schemas from `packages/types` to keep frontend/backend in sync
5. **Overlapping Concerns**: Keep "Quality" (business strength) separate from "Valuation" (price vs. growth math)
6. **Deploying Backend to Vercel**: Will fail silently or timeout—use Docker/Railway instead
```typescript
// apps/backend/steps/new-analysis.step.ts
import { getValidatedState } from "./lib/statehooks";
import { analysisSchema } from "@repo/types";
export const config = {
type: "event" as const,
subscribes: ["start-analysis"],
emits: ["analysis-complete"],
schemas: {
data: z.object({ symbol: z.string() }),
output: analysisSchema,
},
};
export const handler: Handlers["new-analysis"] = async ({
data,
state,
traceId,
logger,
emit,
streams,
}) => {
// Update UI
streams["stock-analysis-stream"].set("analysis", traceId, {
status: "Running analysis...",
});
// Perform analysis
const result = await performAnalysis(data.symbol);
// Save to state
await state.set("analysis", traceId, result);
// Emit completion
await emit({
topic: "analysis-complete",
data: { symbol: data.symbol },
});
return result;
};
```
```typescript
// apps/web/app/analysis/[symbol]/page.tsx
"use client";
import { useStream } from "@motiadev/stream-client-react";
export default function AnalysisPage({ params }) {
const { data, isConnected } = useStream({
url: `${process.env.NEXT_PUBLIC_BACKEND_URL}/streams/stock-analysis-stream`,
traceId: params.symbol,
});
return (
<div>
<p>Status: {data?.status}</p>
<p>Progress: {data?.progress}%</p>
</div>
);
}
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/cfai-stock-analysis-platform/raw