- Set up Next.js with App Router, TypeScript, Tailwind CSS - Configure shadcn/ui with dark theme - Install dependencies: lightweight-charts, papaparse, lucide-react - Set up Drizzle ORM with better-sqlite3 - Create database schema for candles and annotations tables - Generate migration SQL
8.4 KiB
8.4 KiB
1. Project Setup & Configuration
- 1.1 Initialize Next.js project with App Router, TypeScript, and Tailwind CSS using
npx create-next-app@latest(with src/ directory, App Router, Tailwind CSS enabled) - 1.2 Install core dependencies:
lightweight-charts,papaparse,lucide-react, and their type packages (@types/papaparse) - 1.3 Install database dependencies:
drizzle-orm,better-sqlite3,@types/better-sqlite3, anddrizzle-kit(dev dependency) - 1.4 Initialize shadcn/ui with dark theme defaults (
npx shadcn-ui@latest initwith slate color scheme) - 1.5 Add shadcn/ui components needed: Button, Tooltip (via
npx shadcn-ui@latest add button tooltip) - 1.6 Configure
drizzle.config.tspointing to local SQLite database file (./data/candles.db)
2. Database Schema & ORM Setup
- 2.1 Create
src/lib/db/schema.tsdefining thecandlestable: id (integer PK auto-increment), time (integer, unique), open (real), high (real), low (real), close (real) - 2.2 Add
annotationstable tosrc/lib/db/schema.ts: id (integer PK auto-increment), timestamp (integer), label_type (text), geometry (text, nullable), created_at (integer) - 2.3 Create
src/lib/db/index.tswith Drizzle client instance using better-sqlite3 driver, connecting to./data/candles.db - 2.4 Create
src/lib/db/migrate.tsto run Drizzle migrations on app startup (ensure tables exist) - 2.5 Generate initial migration with
npx drizzle-kit generateand verify schema creates correctly - 2.6 Test database connection by running the migration and confirming tables exist in SQLite
3. Backend API — Data Ingestion
- 3.1 Create
src/app/api/upload/route.tswith POST handler that accepts multipart form data containing a CSV file - 3.2 Implement CSV parsing using papaparse: validate required headers (time, open, high, low, close), handle both date strings and Unix timestamps for the time column
- 3.3 Implement batch insert of parsed candle records into the
candlestable within a single SQLite transaction, with upsert behavior on duplicate timestamps - 3.4 Return JSON response:
{ success: true, count: N }on success,{ error: "message" }with HTTP 400 on failure - 3.5 Test upload endpoint manually with a sample CSV file
4. Backend API — Annotations CRUD
- 4.1 Create
src/app/api/annotations/route.tswith GET handler returning all annotations as JSON array (parsing geometry from JSON string) - 4.2 Add POST handler to the annotations route: validate required fields (timestamp, label_type), serialize geometry to JSON string if present, insert into database, return created object with HTTP 201
- 4.3 Create
src/app/api/annotations/[id]/route.tswith DELETE handler: remove annotation by ID, return 200 on success, 404 if not found - 4.4 Create
src/app/api/candles/route.tswith GET handler returning all candle records ordered by time ascending as JSON array
5. Backend API — Export
- 5.1 Create
src/app/api/export/route.tswith GET handler that queries all annotations and formats them as CSV (columns: timestamp, label_type, price) - 5.2 For marker annotations (break_up, break_down), look up the candle's close price by timestamp; for line annotations, use startPrice from geometry
- 5.3 Set response headers:
Content-Type: text/csvandContent-Disposition: attachment; filename="annotations.csv" - 5.4 Handle empty annotations case by returning CSV with header row only
6. UI Shell & Layout
- 6.1 Update
src/app/layout.tsxto set dark theme: Slate-900 background, light text, proper font and metadata - 6.2 Create
src/app/page.tsxas the main page with a flexbox layout: fixed-width sidebar on the left, chart area filling remaining space - 6.3 Create
src/components/Toolbox.tsxsidebar component with tool buttons using lucide-react icons: "Label: Break Up" (ArrowUpCircle), "Label: Break Down" (ArrowDownCircle), "Draw Line" (Minus/TrendingUp), "Delete" (Trash2) - 6.4 Implement active tool state management: clicking a tool highlights it, clicking again deactivates it, only one tool active at a time
- 6.5 Add the Export button to the sidebar that triggers a download from GET /api/export (using an anchor tag or
window.location)
7. File Upload Component
- 7.1 Create
src/components/FileUpload.tsxas a client component with a file input that accepts.csvfiles - 7.2 On file selection, send the file to POST /api/upload using FormData and fetch
- 7.3 Display success message with row count or error message from the API response
- 7.4 After successful upload, trigger a callback to refresh candle data on the chart
- 7.5 Place the FileUpload component in the sidebar (inside Toolbox or above it)
8. Candlestick Chart Component
- 8.1 Create
src/components/CandleChart.tsxas a client component ("use client") that initializes a lightweight-charts chart instance in a useEffect/useRef pattern - 8.2 Fetch candle data from GET /api/candles on mount and set it on a CandlestickSeries
- 8.3 Apply dark theme options to the chart: dark background matching Slate-900, subtle grid lines, light crosshair and text colors
- 8.4 Enable built-in interactivity: crosshair, zoom (mouse wheel), pan (drag on time axis)
- 8.5 Implement chart resize handling: use ResizeObserver on the container div to call
chart.resize()when dimensions change - 8.6 Fetch annotations from GET /api/annotations on mount and convert marker annotations (break_up, break_down) to lightweight-charts marker format, apply with
series.setMarkers() - 8.7 Expose a
refreshDatamethod (via useImperativeHandle or callback prop) so parent can trigger re-fetching candles and annotations after upload or annotation changes
9. Annotation Click Handling
- 9.1 Subscribe to chart click events: on chart click, use
chart.timeScale().coordinateToTime(x)andseries.coordinateToPrice(y)to convert pixel coordinates to data coordinates - 9.2 For "break_up" and "break_down" active tools: snap clicked time to nearest candle timestamp, POST annotation to /api/annotations with label_type and timestamp, then refresh markers on the chart
- 9.3 For "delete" active tool on marker click: identify if a marker annotation exists at the clicked candle timestamp, if so call DELETE /api/annotations/[id] and refresh markers
- 9.4 Handle edge cases: clicking outside the data range (no valid time coordinate), clicking when no tool is active (do nothing)
10. Line Drawing — SVG Overlay
- 10.1 Create
src/components/SvgOverlay.tsxas a transparent SVG element absolutely positioned over the chart container, matching the chart's dimensions - 10.2 Implement coordinate transformation functions: convert data coordinates (time, price) to SVG pixel coordinates using
chart.timeScale().timeToCoordinate()andseries.priceToCoordinate() - 10.3 Render existing line annotations by fetching from GET /api/annotations, filtering for label_type "line", and drawing SVG
<line>elements using transformed coordinates - 10.4 Subscribe to chart visible range changes (
timeScale().subscribeVisibleTimeRangeChange()) and re-render SVG lines when user zooms or pans - 10.5 Implement two-click line drawing: first click stores start point (time, price), mouse move draws preview line from start to cursor, second click saves the line via POST /api/annotations with geometry JSON
- 10.6 Implement Escape key to cancel in-progress line drawing (clear preview, reset state)
- 10.7 Implement line deletion: when "delete" tool is active, detect clicks near an existing SVG line (within a pixel tolerance), call DELETE /api/annotations/[id] for that line
11. Integration & Final Wiring
- 11.1 Wire up state flow in page.tsx: active tool state passed from Toolbox to CandleChart and SvgOverlay, upload callback from FileUpload triggers chart refresh
- 11.2 Ensure annotations refresh after every create/delete operation (markers update via setMarkers, lines update via SVG re-render)
- 11.3 Add empty state handling: when no candles are loaded, display a message prompting the user to upload a CSV file
- 11.4 Verify full workflow end-to-end: upload CSV → view candles → place Break Up marker → place Break Down marker → draw a line → delete an annotation → export CSV
- 11.5 Create DEPLOYMENT.md with steps to run the app locally (npm install, database setup, npm run dev)
- 11.6 Create README.md with project overview, tech stack, and usage instructions