Files
AITrader/docs/superpowers/plans/2026-05-14-stock-database.md
T
henry 3340fd11ca feat: add stock database with prisma for portfolio persistence
- Initialize Prisma with SQLite and Stock model
- Create database service layer with singleton client
- Add API routes for stock CRUD operations
- Integrate database with analyze page to persist ticker entries
- Add Playwright tests for stock database functionality
2026-05-14 10:23:56 +02:00

6.0 KiB

Stock Portfolio Database Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Store manually added tickers in SQLite database using Prisma ORM with full CRUD operations and tests.

Architecture: Use Prisma ORM with SQLite to persist stock tickers added via the analyze page. Each stock entry stores ticker symbol, optional notes, and timestamps. The analyze route fetches stored tickers from DB and merges with Alpaca positions.

Tech Stack: Prisma ORM, SQLite, TypeScript, React Router 7


Prerequisites Check

  • Check if Prisma is already installed in package.json
  • Check existing database/schema files

Task 1: Initialize Prisma and Create Stock Model

Files:

  • Create: prisma/schema.prisma

  • Create: prisma/migrations/xxxxxxxxxxxx_init/migration.sql (generated)

  • Step 1: Install Prisma dependencies

npm install prisma @prisma/client
npx prisma init --datasource-provider sqlite

Expected: Creates prisma/ directory with schema.prisma

  • Step 2: Define Stock model in schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model Stock {
  id        String   @id @default(cuid())
  ticker    String   @unique
  notes     String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Expected: Schema file saved

  • Step 3: Generate Prisma client and migrate
npx prisma generate
npx prisma migrate dev --name init

Expected: prisma/dev.db created, client generated

  • Step 4: Commit
git add prisma/
git commit -m "feat: initialize prisma with Stock model"

Task 2: Create Database Service Layer

Files:

  • Create: app/lib/db.server.ts

  • Step 1: Create Prisma client singleton

import { PrismaClient } from "@prisma/client";

declare global {
  var prisma: PrismaClient | undefined;
}

export const db = global.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") {
  global.prisma = db;
}

Expected: File created without TypeScript errors

  • Step 2: Commit
git add app/lib/db.server.ts
git commit -m "feat: create prisma client singleton"

Task 3: Create Stock API Routes

Files:

  • Create: app/routes/api/stocks/index.ts

  • Create: app/routes/api/stocks/$ticker.ts

  • Step 1: Create GET /api/stocks route

import { db } from "../../../lib/db.server";

export async function loader() {
  const stocks = await db.stock.findMany({
    orderBy: { ticker: "asc" },
  });
  return Response.json(stocks);
}

export async function action({ request }: { request: Request }) {
  const formData = await request.formData();
  const ticker = formData.get("ticker")?.toString().toUpperCase();

  if (!ticker) {
    return Response.json({ error: "Ticker is required" }, { status: 400 });
  }

  const stock = await db.stock.create({
    data: { ticker },
  });
  return Response.json(stock);
}
  • Step 2: Register routes in routes.ts
route("api/stocks", "routes/api/stocks/index.ts"),
route("api/stocks/$ticker", "routes/api/stocks/\$ticker.ts"),
  • Step 3: Commit
git add app/routes/api/stocks/
git commit -m "feat: add stock CRUD API routes"

Task 4: Modify Analyze Route to Use Database

Files:

  • Modify: app/routes/analyze.tsx

  • Step 1: Merge Alpaca positions with database stocks on load

// In loadPortfolio useEffect, after fetching Alpaca positions:
const [alpacaPositions, dbStocks] = await Promise.all([
  fetch("/api/alpaca/positions").then(r => r.ok ? r.json() : []),
  fetch("/api/stocks").then(r => r.ok ? r.json() : [])
]);

// Build initial stocks from both sources
const initialStocks = await Promise.all([
  // Alpaca positions first
  ...alpacaPositions.map(async (p: { ticker: string; qty: number }) => {
    // ... existing logic
  }),
  // Then DB stocks not in Alpaca
  ...dbStocks.map(async (s: { ticker: string }) => {
    const existing = alpacaPositions.find((p: { ticker: string }) => p.ticker === s.ticker);
    if (existing) return null;
    return {
      id: `db-${s.ticker}`,
      ticker: s.ticker,
      currentPrice: null,
      position: 0,
      rsi: null,
      analysis: null,
      loading: false,
    };
  })
].filter(Boolean));
  • Step 2: Save new ticker to database when added
// In addStock function, after successfully adding ticker:
await fetch("/api/stocks", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ ticker }),
});
  • Step 3: Commit
git add app/routes/analyze.tsx
git commit -m "feat: integrate stock database with analyze page"

Task 5: Write Playwright Tests

Files:

  • Create: tests/stock-db.spec.ts

  • Step 1: Write test for stock CRUD API

import { test, expect } from "@playwright/test";

test.describe("Stock Database", () => {
  test("should add and list stocks", async ({ page }) => {
    // POST to create
    const createRes = await page.request.post("/api/stocks", {
      data: { ticker: "TEST" },
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
    });
    expect(createRes.ok()).toBeTruthy();
    
    // GET to list
    const listRes = await page.request.get("/api/stocks");
    const stocks = await listRes.json();
    expect(stocks).toContainEqual(expect.objectContaining({ ticker: "TEST" }));
  });
});
  • Step 2: Commit
git add tests/stock-db.spec.ts
git commit -m "test: add stock database E2E tests"

Task 6: Verify Installation

Files:

  • Check: package.json

  • Step 1: Run typecheck and tests

npm run typecheck
npm run test:e2e

Expected: All commands succeed

  • Step 2: Commit
git commit -am "chore: verify prisma integration"