# Candle Annotator A web-based tool for manually annotating candlestick charts with pattern labels and trend lines. Built for creating labeled training data for machine learning models in trading analysis. ## Overview Candle Annotator provides a TradingView-like charting interface that allows traders and researchers to: - Upload historical OHLC (Open, High, Low, Close) candle data from CSV files - Visualize candlestick charts with interactive zoom and pan - Mark breakout patterns (Break Up, Break Down) directly on candles - Draw custom trend lines with two-click interaction - Delete annotations with a dedicated tool - Export all annotations as CSV for ML training pipelines ## Features ### Data Management - **CSV Upload**: Import OHLC data with support for both Unix timestamps and date strings - **SQLite Storage**: All candle data and annotations stored locally in SQLite database - **Data Persistence**: Annotations and candles persist between sessions ### Chart Visualization - **Interactive Candlestick Chart**: Powered by lightweight-charts library - **Dark Theme**: Eye-friendly slate color scheme - **Zoom & Pan**: Mouse wheel zoom and drag-to-pan functionality - **Crosshair**: Precise price and time tracking ### Annotation Tools - **Break Up Markers**: Green arrow markers below candles indicating upward breakouts - **Break Down Markers**: Red arrow markers above candles indicating downward breakouts - **Trend Lines**: Two-click line drawing with real-time preview - **Delete Tool**: Remove any annotation (markers or lines) by clicking on them - **Tool Toggle**: Click tool button again to deactivate ### Export - **CSV Export**: Download all annotations with timestamp, label type, and price data - **ML-Ready Format**: Structured data suitable for training ML models ## Tech Stack - **Frontend**: Next.js 16 (App Router), React 19, TypeScript - **Styling**: Tailwind CSS 3, shadcn/ui components - **Charting**: lightweight-charts 4.x (TradingView) - **Icons**: lucide-react - **Backend**: Next.js API Routes - **Database**: SQLite with better-sqlite3 - **ORM**: Drizzle ORM - **CSV Parsing**: papaparse ## Getting Started ### Prerequisites - Node.js 18.x or higher - npm 9.x or higher - Build tools for native modules (see DEPLOYMENT.md) ### Installation 1. Clone the repository: ```bash git clone cd candle_annotator ``` 2. Install dependencies: ```bash npm install ``` 3. Start the development server: ```bash npm run dev ``` 4. Open http://localhost:3000 in your browser ### Usage 1. **Upload Data**: Click "Choose CSV File" and select a CSV with columns: `time,open,high,low,close` 2. **View Chart**: The candlestick chart renders automatically after upload 3. **Add Annotations**: - Click "Label: Break Up" or "Label: Break Down" then click on a candle - Click "Draw Line" then click two points to draw a trend line - Press Escape to cancel line drawing 4. **Delete Annotations**: Click "Delete" tool, then click on markers or lines to remove them 5. **Export**: Click "Export CSV" to download all annotations ## CSV File Format ### Input Format Your CSV file should have these columns: ```csv time,open,high,low,close 1700000000,1.0500,1.0520,1.0490,1.0510 1700000060,1.0510,1.0530,1.0505,1.0525 ``` **Time column** accepts: - Unix timestamps (seconds): `1700000000` - Date strings: `2024-01-15`, `2024-01-15 10:30:00` ### Export Format The exported CSV includes: ```csv timestamp,label_type,price 1700000000,break_up,1.0510 1700000120,break_down,1.0505 1700000000,line,1.0500 ``` - **timestamp**: Unix timestamp of the annotation - **label_type**: `break_up`, `break_down`, or `line` - **price**: Close price for markers, start price for lines ## Database Schema ### Candles Table ```typescript { id: integer (PK, auto-increment), time: integer (Unix timestamp, unique), open: real, high: real, low: real, close: real } ``` ### Annotations Table ```typescript { id: integer (PK, auto-increment), timestamp: integer (Unix timestamp), label_type: text ('break_up' | 'break_down' | 'line'), geometry: text (JSON string for line coordinates, null for markers), created_at: integer (Unix timestamp) } ``` ## API Endpoints ### POST /api/upload Upload CSV file and store candle data **Request**: multipart/form-data with `file` field **Response**: `{ success: true, count: number }` or `{ error: string }` ### GET /api/candles Retrieve all candle records **Response**: Array of candle objects ordered by time ### GET /api/annotations Retrieve all annotations **Response**: Array of annotation objects with parsed geometry ### POST /api/annotations Create a new annotation **Request**: `{ timestamp: number, label_type: string, geometry?: object }` **Response**: Created annotation object with ID ### DELETE /api/annotations/[id] Delete an annotation by ID **Response**: `{ success: true }` or `{ error: string }` ### GET /api/export Export annotations as downloadable CSV **Response**: CSV file download with Content-Disposition header ## Architecture ### Component Structure - **page.tsx**: Main page composition, manages active tool state - **Toolbox.tsx**: Sidebar with tool buttons and export functionality - **FileUpload.tsx**: CSV upload component with status messages - **CandleChart.tsx**: Core chart wrapper with lightweight-charts integration - Initializes chart with dark theme - Handles marker annotations (Break Up/Down) - Manages click events for annotation creation - Exposes `refreshData()` method for parent updates - **SvgOverlay.tsx**: Transparent SVG layer for line drawing - Coordinate transformation between data and pixels - Two-click line drawing with preview - Line hit detection for deletion ### Data Flow 1. User uploads CSV → POST /api/upload → SQLite storage 2. Chart mounts → GET /api/candles + GET /api/annotations → Render 3. User clicks with active tool → POST /api/annotations → Refresh chart 4. User deletes → DELETE /api/annotations/[id] → Refresh chart 5. User exports → GET /api/export → CSV download ## Development ### Project Structure ``` candle_annotator/ ├── src/ │ ├── app/ │ │ ├── api/ # API route handlers │ │ │ ├── upload/ │ │ │ ├── candles/ │ │ │ ├── annotations/ │ │ │ └── export/ │ │ ├── globals.css # Tailwind styles │ │ ├── layout.tsx # Root layout with dark theme │ │ └── page.tsx # Main page │ ├── components/ │ │ ├── ui/ # shadcn/ui components │ │ ├── CandleChart.tsx │ │ ├── SvgOverlay.tsx │ │ ├── Toolbox.tsx │ │ └── FileUpload.tsx │ └── lib/ │ ├── db/ │ │ ├── index.ts # Drizzle client │ │ ├── schema.ts # Table definitions │ │ └── migrate.ts # Migration runner │ └── utils.ts # Utility functions ├── data/ # SQLite database directory ├── drizzle/ # Migration files ├── DEPLOYMENT.md # Deployment instructions └── README.md # This file ``` ### Key Technical Decisions 1. **lightweight-charts v4**: Stable API with good candlestick and marker support 2. **SQLite with better-sqlite3**: Synchronous access, perfect for single-user local apps 3. **SVG Overlay for Lines**: Maintains separate rendering layer from chart, easier coordinate management 4. **Drizzle ORM**: Type-safe queries with minimal overhead 5. **Next.js App Router**: Server-side API routes co-located with frontend code ### Known Limitations - **Single User**: No authentication or concurrent access support - **No Undo**: Can only delete annotations, not undo placement - **Memory**: Large CSV files (100k+ rows) may cause slow uploads - **Line Snapping**: Lines don't snap to candles, free-form placement only ## Troubleshooting See [DEPLOYMENT.md](./DEPLOYMENT.md) for detailed troubleshooting steps. Common issues: - **better-sqlite3 binding errors**: Run `npm rebuild better-sqlite3` - **Port 3000 in use**: Use `PORT=3001 npm run dev` - **Database corruption**: Delete `data/candles.db` and restart ## License ISC ## Contributing This is a focused tool for a specific use case. For questions or issues, please open a GitHub issue.