sync: ml-ui-connection delta specs to main specs

This commit is contained in:
Marko Djordjevic 2026-02-18 10:21:05 +01:00
parent 9a549b8c7a
commit c0f5654450
15 changed files with 369 additions and 6 deletions

View file

@ -1,6 +1,6 @@
# Code Review: Candle Annotator # Code Review: Candle Annotator
**Date**: 2026-02-17 **Date**: 2026-02-18
**Reviewer**: Claude Code (Automated Audit) **Reviewer**: Claude Code (Automated Audit)
**Scope**: Full codebase - frontend (Next.js), backend (FastAPI), configuration, deployment **Scope**: Full codebase - frontend (Next.js), backend (FastAPI), configuration, deployment
**Verdict**: **Request Changes** - Critical and major issues must be fixed before production deployment. **Verdict**: **Request Changes** - Critical and major issues must be fixed before production deployment.
@ -9,7 +9,7 @@
## Executive Summary ## Executive Summary
85 issues identified across the entire codebase: **15 Critical**, **30 Major**, **40 Minor**. 93 issues identified across the entire codebase: **13 Critical**, **35 Major**, **45 Minor**.
The application has **zero SQL injection vulnerabilities** (Drizzle ORM and SQLAlchemy parameterize all queries), good error handling structure (every route has try/catch), and well-implemented timeouts on proxy routes. The application has **zero SQL injection vulnerabilities** (Drizzle ORM and SQLAlchemy parameterize all queries), good error handling structure (every route has try/catch), and well-implemented timeouts on proxy routes.
@ -99,7 +99,13 @@ However, the codebase has **no authentication anywhere**, hardcoded credentials
- **Impact**: Async click handler captures `drawingState`, `selectedLineId`, `dragState`, `annotations` from closure. 12+ item dependency array causes excessive re-subscriptions and stale values between re-subscriptions. - **Impact**: Async click handler captures `drawingState`, `selectedLineId`, `dragState`, `annotations` from closure. 12+ item dependency array causes excessive re-subscriptions and stale values between re-subscriptions.
- **Fix**: Use refs for frequently-changing values that the click handler reads, reducing re-subscription frequency. - **Fix**: Use refs for frequently-changing values that the click handler reads, reducing re-subscription frequency.
### C-12. Pervasive `any` Types Disable Type Safety ### C-12. Stale Closure in SpanAnnotationManager Keyboard Handler
- **File**: `src/components/SpanAnnotationManager.tsx:461-535`
- **Impact**: Keyboard handler references `handleDeleteSpan` (depends on `selectedSpanId`) but it's not in the dependency array and not wrapped in `useCallback`. Pressing Delete uses stale `selectedSpanId`, potentially deleting wrong span.
- **Fix**: Wrap `handleDeleteSpan` in `useCallback` with proper dependencies, or use a ref for `selectedSpanId`.
### C-13. Pervasive `any` Types Disable Type Safety
- **Files**: `page.tsx:134,147,196-198`, `CandleChart.tsx:62,389`, `SpanAnnotationManager.tsx:25`, `Toolbox.tsx:23`, `SpanAnnotationList.tsx:15`, `TalibPatternPanel.tsx:19`, `FileUpload.tsx:45`, `rectangle-drawing.ts:60,63` - **Files**: `page.tsx:134,147,196-198`, `CandleChart.tsx:62,389`, `SpanAnnotationManager.tsx:25`, `Toolbox.tsx:23`, `SpanAnnotationList.tsx:15`, `TalibPatternPanel.tsx:19`, `FileUpload.tsx:45`, `rectangle-drawing.ts:60,63`
- **Impact**: Type errors at runtime go undetected at compile time. Especially dangerous for `geometry`, `sub_spans`, and prediction cache values. - **Impact**: Type errors at runtime go undetected at compile time. Especially dangerous for `geometry`, `sub_spans`, and prediction cache values.
@ -277,6 +283,37 @@ However, the codebase has **no authentication anywhere**, hardcoded credentials
- **File**: `src/app/page.tsx:412-425` - **File**: `src/app/page.tsx:412-425`
- **Fix**: Add confirmation dialog before destructive action. - **Fix**: Add confirmation dialog before destructive action.
### M-31. No React Error Boundaries
- **File**: `src/app/layout.tsx`
- **Impact**: No Error Boundary anywhere in the component tree. If CandleChart (canvas APIs) or any child throws during render, the entire app crashes to a white screen.
- **Fix**: Add Error Boundary wrapping `{children}` in `layout.tsx` or wrapping CandleChart and sidebar sections individually.
### M-32. Hardcoded 1-Minute Candle Interval Assumption
- **File**: `src/components/CandleChart.tsx:452`
- **Code**: `for (let t = span.start_time; t <= span.end_time; t += 60)`
- **Impact**: For 5m/15m/1h/daily timeframes, this creates orders of magnitude more iterations than necessary, potentially freezing the browser.
- **Fix**: Determine actual candle interval from data or iterate over actual candle times.
### M-33. Excessive Prop Drilling (~25+ State Items)
- **File**: `src/app/page.tsx`
- **Impact**: Home component manages ~25+ pieces of state and passes them through layers. CandleChart receives 20+ props, Toolbox receives 15+.
- **Fix**: Consider React Context or Zustand for shared state (prediction, span annotations, chart selection).
### M-34. SpanAnnotationManager Recreates ALL Primitives on Every Change
- **File**: `src/components/SpanAnnotationManager.tsx:104-161`
- **Impact**: O(n) detach + O(n) attach on every selection change or annotation modification.
- **Fix**: Only update selection state of existing primitives instead of full recreation.
### M-35. Preview Primitive Cleanup Missing on Unmount
- **File**: `src/components/SpanAnnotationManager.tsx:265-324`
- **Impact**: Mouse move handler creates `SpanRectanglePrimitive` on every move. If component unmounts mid-draw, last preview is never detached.
- **Fix**: Add cleanup in useEffect return that detaches `previewPrimitive`.
--- ---
## 3. Minor Issues ## 3. Minor Issues
@ -340,6 +377,11 @@ However, the codebase has **no authentication anywhere**, hardcoded credentials
| m-38 | `TalibPatternPanel.tsx:39` | Re-fetches when API returns empty array | Use `hasLoaded` flag | | m-38 | `TalibPatternPanel.tsx:39` | Re-fetches when API returns empty array | Use `hasLoaded` flag |
| m-39 | `CandleChart.tsx:202-221` | `useImperativeHandle` missing dependency array | Add `[candles]` dep | | m-39 | `CandleChart.tsx:202-221` | `useImperativeHandle` missing dependency array | Add `[candles]` dep |
| m-40 | `CandleChart.tsx:1059-1110` | Missing `activeChartId` in primitive cleanup effects | Add to dependency arrays | | m-40 | `CandleChart.tsx:1059-1110` | Missing `activeChartId` in primitive cleanup effects | Add to dependency arrays |
| m-41 | `ChartSelector.tsx:48` | Dropdown does not close on outside click | Add click-outside handler |
| m-42 | `annotation-types/page.tsx`, `span-label-types/page.tsx` | Dark theme not applied - hardcoded light colors | Use theme-aware CSS variables |
| m-43 | `PredictionPanel.tsx:148-155` | No debounce on confidence slider - re-renders chart every pixel | Debounce or use `onPointerUp` |
| m-44 | `globals.css:1` | External Google Font `@import` blocks rendering | Use `next/font` for optimization |
| m-45 | `CandleChart.tsx` | ~1100 line component - too large | Extract into custom hooks (`useChartInit`, `useChartAnnotations`) |
--- ---
@ -396,6 +438,9 @@ However, the codebase has **no authentication anywhere**, hardcoded credentials
| Centralize duplicate type definitions | `src/types/` (new files), 6 component files | Medium | | Centralize duplicate type definitions | `src/types/` (new files), 6 component files | Medium |
| Remove `fitContent()` from reconciliation effect | `src/components/SpanAnnotationManager.tsx` | Low | | Remove `fitContent()` from reconciliation effect | `src/components/SpanAnnotationManager.tsx` | Low |
| Use refs for click handler closure values | `src/components/CandleChart.tsx` | Medium | | Use refs for click handler closure values | `src/components/CandleChart.tsx` | Medium |
| Add React Error Boundary | `src/app/layout.tsx` or new component | Low |
| Fix hardcoded 1-min candle interval | `src/components/CandleChart.tsx` | Low |
| Fix SpanAnnotationManager full primitive recreation | `src/components/SpanAnnotationManager.tsx` | Medium |
| Move dev deps to `devDependencies` | `package.json` | Low | | Move dev deps to `devDependencies` | `package.json` | Low |
| Remove dead code (`migrate.ts`, `get_db_session`, dead filter) | Multiple files | Low | | Remove dead code (`migrate.ts`, `get_db_session`, dead filter) | Multiple files | Low |
@ -411,6 +456,12 @@ However, the codebase has **no authentication anywhere**, hardcoded credentials
| Implement proper health checks (ML service) | `services/ml/app/main.py` | Low | | Implement proper health checks (ML service) | `services/ml/app/main.py` | Low |
| Replace deprecated Python APIs | `services/ml/app/main.py`, `db.py` | Low | | Replace deprecated Python APIs | `services/ml/app/main.py`, `db.py` | Low |
| Extract magic numbers to constants | `CandleChart.tsx`, `page.tsx` | Low | | Extract magic numbers to constants | `CandleChart.tsx`, `page.tsx` | Low |
| Fix dark theme on settings pages | `annotation-types/page.tsx`, `span-label-types/page.tsx` | Low |
| Add debounce to confidence slider | `PredictionPanel.tsx` | Low |
| Use `next/font` instead of CSS `@import` | `globals.css`, `layout.tsx` | Low |
| Break up CandleChart into custom hooks | `CandleChart.tsx` | High |
| Add click-outside handler to chart dropdown | `ChartSelector.tsx` | Low |
| Consider React Context for shared state | `page.tsx`, multiple components | Medium |
| Write tests | New test files | High | | Write tests | New test files | High |
--- ---
@ -430,9 +481,18 @@ However, the codebase has **no authentication anywhere**, hardcoded credentials
| Category | Critical | Major | Minor | Total | | Category | Critical | Major | Minor | Total |
|----------|----------|-------|-------|-------| |----------|----------|-------|-------|-------|
| API Routes (TS) | 3 | 7 | 5 | 15 | | API Routes (TS) | 3 | 7 | 5 | 15 |
| React Components | 4 | 9 | 18 | 31 | | React Components | 5 | 14 | 23 | 42 |
| Python ML Service | 2 | 7 | 10 | 19 | | Python ML Service | 2 | 7 | 10 | 19 |
| Config & Deployment | 3 | 7 | 7 | 17 | | Config & Deployment | 3 | 7 | 7 | 17 |
| **Total** | **12** | **30** | **40** | **82** | | **Total** | **13** | **35** | **45** | **93** |
> Note: Some issues overlap across categories (e.g., auth affects both frontend and backend). > Note: Some issues overlap across categories (e.g., auth affects both frontend and backend).
---
## No XSS or Client-Side Secret Issues Found
- No usage of `dangerouslySetInnerHTML` anywhere in the codebase.
- All user content rendered through React JSX (auto-escaped).
- No API keys, tokens, or sensitive data stored in client-side state.
- All API calls use relative URLs to same origin.

View file

@ -26,6 +26,127 @@ The system SHALL provide a `POST /api/predict/batch` Next.js API route that prox
- **WHEN** the batch prediction takes longer than INFERENCE_BATCH_TIMEOUT - **WHEN** the batch prediction takes longer than INFERENCE_BATCH_TIMEOUT
- **THEN** the route returns HTTP 504 with `{ "error": "Batch prediction timed out" }` - **THEN** the route returns HTTP 504 with `{ "error": "Batch prediction timed out" }`
### Requirement: Pattern detection endpoint
The FastAPI service SHALL provide a `POST /patterns/detect` endpoint that accepts candle data and a list of CDL pattern names. The endpoint SHALL run the specified TA-Lib CDL functions on the candle data and return detected patterns as span annotation objects. Each returned annotation SHALL include start_time, end_time, label, confidence, and source ("talib").
#### Scenario: Detect specific patterns
- **WHEN** `POST /patterns/detect` is called with `{candles: [...], patterns: ["CDLENGULFING", "CDLHAMMER"]}`
- **THEN** the endpoint runs only Engulfing and Hammer detection and returns matching span annotations
#### Scenario: Detect all patterns
- **WHEN** `POST /patterns/detect` is called with `{candles: [...], patterns: []}` (empty list)
- **THEN** the endpoint runs all available CDL pattern functions
#### Scenario: No patterns found
- **WHEN** detection runs but no patterns match
- **THEN** the endpoint returns `{annotations: [], metadata: {count: 0}}`
#### Scenario: Invalid pattern name
- **WHEN** a pattern name is not a valid TA-Lib CDL function
- **THEN** the endpoint returns HTTP 400 with the invalid pattern name in the error message
### Requirement: Available patterns endpoint
The FastAPI service SHALL provide a `GET /patterns/available` endpoint that returns the list of all supported CDL pattern names with their friendly display names.
#### Scenario: List available patterns
- **WHEN** `GET /patterns/available` is called
- **THEN** the endpoint returns a list of `{function_name, display_name}` for all supported CDL patterns
### Requirement: Training start endpoint
The FastAPI service SHALL provide a `POST /training/start` endpoint that triggers a training run in a background thread. The endpoint SHALL accept `{model_type}` and return immediately with a run_id and status "running". Only one training run SHALL be allowed at a time.
#### Scenario: Start training
- **WHEN** `POST /training/start` is called with `{model_type: "random_forest"}`
- **THEN** the endpoint returns `{run_id, status: "running"}` and training begins in the background
#### Scenario: Training already in progress
- **WHEN** `POST /training/start` is called while a training run is active
- **THEN** the endpoint returns HTTP 409 with `{error: "Training already in progress", run_id: "<active_run_id>"}`
#### Scenario: Invalid model type
- **WHEN** `POST /training/start` is called with an unsupported model type
- **THEN** the endpoint returns HTTP 400 with `{error: "Unsupported model type. Available: random_forest, xgboost"}`
### Requirement: Training runs endpoint
The FastAPI service SHALL provide a `GET /training/runs` endpoint that returns training run history from the database. Each entry SHALL include run_id, model_type, status, created_at, completed_at, and metrics_summary. Results SHALL be sorted by created_at descending.
#### Scenario: List training runs
- **WHEN** `GET /training/runs` is called
- **THEN** the endpoint returns training run records sorted by date descending
#### Scenario: No training runs
- **WHEN** no training runs exist in the database
- **THEN** the endpoint returns `{runs: []}`
### Requirement: Model load endpoint
The FastAPI service SHALL provide a `POST /model/load` endpoint that loads a model by run_id. The endpoint SHALL look up the training run, find the model artifact (MLflow or local), and replace the currently loaded model. The endpoint SHALL return the new model's info.
#### Scenario: Load model by run_id
- **WHEN** `POST /model/load` is called with `{run_id: "abc123"}`
- **THEN** the endpoint loads the model associated with that run, updates the active model, and returns model info
#### Scenario: Run not found
- **WHEN** `POST /model/load` is called with a non-existent run_id
- **THEN** the endpoint returns HTTP 404 with `{error: "Training run not found"}`
#### Scenario: Model artifact missing
- **WHEN** the training run exists but the model file is missing
- **THEN** the endpoint returns HTTP 500 with `{error: "Model artifact not found for run"}`
### Requirement: Dataset info endpoint
The FastAPI service SHALL provide a `GET /training/dataset-info` endpoint that returns information about the training dataset: file path, existence status, file size, and last modified date.
#### Scenario: Dataset exists
- **WHEN** `GET /training/dataset-info` is called and the labeled dataset file exists
- **THEN** the endpoint returns `{path, exists: true, size_bytes, last_modified, row_count}`
#### Scenario: Dataset missing
- **WHEN** `GET /training/dataset-info` is called and the labeled dataset file does not exist
- **THEN** the endpoint returns `{path, exists: false}`
### Requirement: Pattern detection proxy
The Next.js API SHALL provide a `POST /api/patterns/detect` route that proxies to the FastAPI `/patterns/detect` endpoint.
#### Scenario: Proxy pattern detection
- **WHEN** `POST /api/patterns/detect` is called
- **THEN** the route forwards the request to the FastAPI service and returns the response
### Requirement: Available patterns proxy
The Next.js API SHALL provide a `GET /api/patterns/available` route that proxies to the FastAPI `/patterns/available` endpoint.
#### Scenario: Proxy available patterns
- **WHEN** `GET /api/patterns/available` is called
- **THEN** the route forwards to the FastAPI service and returns the pattern list
### Requirement: Training proxy endpoints
The Next.js API SHALL provide proxy routes for training operations: `POST /api/training/start`, `GET /api/training/runs`, and `GET /api/training/dataset-info`.
#### Scenario: Proxy training start
- **WHEN** `POST /api/training/start` is called
- **THEN** the route forwards to the FastAPI service and returns the response
#### Scenario: Proxy training runs
- **WHEN** `GET /api/training/runs` is called
- **THEN** the route forwards to the FastAPI service and returns the run list
### Requirement: Model load proxy
The Next.js API SHALL provide a `POST /api/model/load` route that proxies to the FastAPI `/model/load` endpoint.
#### Scenario: Proxy model load
- **WHEN** `POST /api/model/load` is called with a run_id
- **THEN** the route forwards to the FastAPI service and returns the response
### Requirement: Bulk delete by source
The Next.js API `DELETE /api/span-annotations` endpoint SHALL support a `source` query parameter for bulk deletion. When `source` is provided, all span annotations matching that source (and optionally `label` filter) for the current chart SHALL be deleted.
#### Scenario: Bulk delete TA-Lib annotations
- **WHEN** `DELETE /api/span-annotations?chartId=1&source=talib` is called
- **THEN** all span annotations with `source: "talib"` for chart 1 are deleted
#### Scenario: Bulk delete by source and label
- **WHEN** `DELETE /api/span-annotations?chartId=1&source=talib&label=Engulfing` is called
- **THEN** only TA-Lib annotations containing "Engulfing" in the label for chart 1 are deleted
### Requirement: Model info proxy endpoint ### Requirement: Model info proxy endpoint
The system SHALL provide a `GET /api/model/info` Next.js API route that proxies to `${INFERENCE_API_URL}/model/info`. This endpoint returns model metadata and per-class metrics. The system SHALL provide a `GET /api/model/info` Next.js API route that proxies to `${INFERENCE_API_URL}/model/info`. This endpoint returns model metadata and per-class metrics.

View file

@ -0,0 +1,42 @@
## Purpose
Provide a dropdown selector component for switching between trained ML models and managing model loading.
## Requirements
### Requirement: Model selector dropdown
The system SHALL display a model selector dropdown in the prediction panel area. The dropdown SHALL list all completed training runs from the backend, showing model type, date, and key metric (F1 macro) for each entry. The currently loaded model SHALL be indicated with a checkmark or "active" badge.
#### Scenario: Display available models
- **WHEN** the user opens the model selector dropdown
- **THEN** completed training runs are listed with model type, training date, and F1 score
#### Scenario: No models available
- **WHEN** no completed training runs exist
- **THEN** the dropdown shows "No trained models available"
#### Scenario: Current model indicated
- **WHEN** a model is currently loaded
- **THEN** the corresponding entry in the dropdown shows an "active" indicator
### Requirement: Model switching
The system SHALL load a different model when the user selects a training run from the model selector. The system SHALL send the run_id to `POST /api/model/load` and update the prediction panel to reflect the newly loaded model's info. Existing cached predictions SHALL be cleared on model switch.
#### Scenario: Switch model
- **WHEN** the user selects a different model from the dropdown
- **THEN** the system sends `POST /api/model/load` with the run_id, shows a loading indicator, and upon success updates the model info display and clears prediction cache
#### Scenario: Model load failure
- **WHEN** model loading fails (e.g., model artifact not found)
- **THEN** the system shows an error message and keeps the previously loaded model active
#### Scenario: Prediction cache cleared
- **WHEN** a new model is successfully loaded
- **THEN** all cached predictions are invalidated and the chart clears any displayed predictions
### Requirement: Model info refresh on switch
The system SHALL refresh the model info display (name, version, type, per-class metrics) after a successful model switch. The prediction panel SHALL reflect the new model's capabilities and labels.
#### Scenario: Model info updates after switch
- **WHEN** a new model is loaded successfully
- **THEN** the prediction panel refreshes to show the new model's name, type, version, and per-class metrics

View file

@ -63,7 +63,7 @@ The system SHALL allow users to toggle visibility of individual pattern labels v
- **THEN** all "double_bottom" predictions are hidden from the chart - **THEN** all "double_bottom" predictions are hidden from the chart
### Requirement: Prediction controls panel ### Requirement: Prediction controls panel
The system SHALL display a prediction controls panel in the sidebar with: master on/off toggle, model info (name, version, type, training date), action buttons ("Run on Visible", "Predict All"), auto-predict toggle, confidence threshold slider, label checkboxes with per-class precision/recall metrics, prediction count, agreement count, and a "Show only disagreements" filter. The system SHALL display a prediction controls panel in the sidebar with: master on/off toggle, model selector dropdown (listing available trained models), model info (name, version, type, training date), action buttons ("Run on Visible", "Predict All"), auto-predict toggle, confidence threshold slider, label checkboxes with per-class precision/recall metrics, prediction count, agreement count, and a "Show only disagreements" filter.
#### Scenario: Display model info #### Scenario: Display model info
- **WHEN** the prediction panel loads and the inference API is available - **WHEN** the prediction panel loads and the inference API is available
@ -77,6 +77,10 @@ The system SHALL display a prediction controls panel in the sidebar with: master
- **WHEN** model info includes per-class metrics - **WHEN** model info includes per-class metrics
- **THEN** each label checkbox shows precision and recall values (e.g., "bull_flag (P:0.89 R:0.76)") - **THEN** each label checkbox shows precision and recall values (e.g., "bull_flag (P:0.89 R:0.76)")
#### Scenario: Model selector integrated
- **WHEN** the prediction panel renders with trained models available
- **THEN** a model selector dropdown appears above the action buttons, allowing the user to switch the active model
### Requirement: Disagreement detection ### Requirement: Disagreement detection
The system SHALL compare human annotation spans with model prediction spans to identify disagreements. For each human annotation, check if any prediction span overlaps (>50% time overlap). Disagreement types: "missed_by_model" (human annotated, model predicted "O"), "missed_by_human" (model predicted pattern, no human annotation), "label_mismatch" (both see a pattern but different labels). The system SHALL compare human annotation spans with model prediction spans to identify disagreements. For each human annotation, check if any prediction span overlaps (>50% time overlap). Disagreement types: "missed_by_model" (human annotated, model predicted "O"), "missed_by_human" (model predicted pattern, no human annotation), "label_mismatch" (both see a pattern but different labels).
@ -118,6 +122,17 @@ When a user clicks on a "missed_by_human" prediction, the system SHALL open the
- **WHEN** user clicks a "missed_by_human" prediction and clicks "Not a pattern" - **WHEN** user clicks a "missed_by_human" prediction and clicks "Not a pattern"
- **THEN** the system saves a negative annotation with label "O", source "human_correction", and records the model's original prediction and confidence - **THEN** the system saves a negative annotation with label "O", source "human_correction", and records the model's original prediction and confidence
### Requirement: Prediction cache invalidation on model change
The system SHALL cache predictions in memory keyed by `${pair}_${timeframe}_${startTime}_${endTime}_${modelVersion}`. When the user scrolls to a range with cached predictions, the system SHALL use the cache instead of re-fetching. Cache SHALL be invalidated when the model version changes OR when the user switches models via the model selector.
#### Scenario: Cache hit
- **WHEN** user scrolls back to a previously predicted range with the same model version
- **THEN** the system renders cached predictions without making an API call
#### Scenario: Cache invalidation on model switch
- **WHEN** the user switches to a different model via the model selector
- **THEN** all cached predictions are cleared and the chart removes displayed predictions
### Requirement: Inference API connection monitoring ### Requirement: Inference API connection monitoring
The system SHALL poll `/api/model/info` every 30 seconds when the inference API is unavailable. When the API becomes available, the system SHALL auto-reconnect and enable prediction controls. Human annotation SHALL never be blocked by inference API availability. The system SHALL poll `/api/model/info` every 30 seconds when the inference API is unavailable. When the API becomes available, the system SHALL auto-reconnect and enable prediction controls. Human annotation SHALL never be blocked by inference API availability.

View file

@ -0,0 +1,61 @@
## Purpose
Provide a UI panel for detecting TA-Lib candlestick patterns on the current chart and managing the resulting annotations.
## Requirements
### Requirement: TA-Lib pattern selection panel
The system SHALL display a collapsible "TA-Lib Patterns" panel in the sidebar. The panel SHALL list all available CDL pattern functions grouped by category (single-candle, multi-candle). Each pattern SHALL have a checkbox for selection. The panel SHALL include a "Select All" / "Deselect All" toggle and a "Detect Patterns" action button.
#### Scenario: Display available patterns
- **WHEN** the user expands the TA-Lib Patterns panel
- **THEN** all available CDL pattern names are listed with checkboxes, grouped by category, all unchecked by default
#### Scenario: Select specific patterns
- **WHEN** the user checks "Engulfing" and "Hammer" checkboxes
- **THEN** those two patterns are selected and the "Detect Patterns" button shows the count "(2 selected)"
#### Scenario: Select all patterns
- **WHEN** the user clicks "Select All"
- **THEN** all pattern checkboxes become checked
### Requirement: Pattern detection execution
The system SHALL send selected patterns and current chart candles to the backend for detection when the user clicks "Detect Patterns". The system SHALL display a loading state during detection and show results as span annotations on the chart upon completion.
#### Scenario: Run pattern detection
- **WHEN** the user has selected patterns and clicks "Detect Patterns"
- **THEN** the system sends current chart candles and selected pattern names to `POST /api/patterns/detect`, shows a loading spinner, and upon response saves returned annotations to the database via `POST /api/span-annotations`
#### Scenario: No patterns selected
- **WHEN** the user clicks "Detect Patterns" with no patterns selected
- **THEN** the button is disabled and nothing happens
#### Scenario: No chart loaded
- **WHEN** the user clicks "Detect Patterns" with no chart loaded
- **THEN** the system shows an error message "Load a chart first"
### Requirement: Pattern results as span annotations
The system SHALL save detected patterns as span annotations with `source: "talib"`. Each annotation SHALL include the pattern label (e.g., "Bullish Engulfing"), confidence from TA-Lib, start_time, and end_time. The annotations SHALL appear immediately on the chart after detection.
#### Scenario: Pattern saved as span annotation
- **WHEN** TA-Lib detects a "Bullish Engulfing" pattern at candles T10-T12
- **THEN** a span annotation is created with `label: "Bullish Engulfing"`, `source: "talib"`, `start_time: T10`, `end_time: T12`, and it renders on the chart
#### Scenario: Multiple patterns detected
- **WHEN** detection finds 5 Engulfing and 3 Hammer patterns
- **THEN** 8 span annotations are created, each with appropriate label and timestamps
### Requirement: TA-Lib annotation management
The system SHALL allow users to bulk delete all TA-Lib-generated annotations or delete them by pattern type. The panel SHALL show a count of detected patterns and a "Clear All TA-Lib" button. Individual TA-Lib annotations SHALL also be deletable from the SpanAnnotationList.
#### Scenario: Bulk delete all TA-Lib annotations
- **WHEN** the user clicks "Clear All TA-Lib"
- **THEN** all span annotations with `source: "talib"` for the current chart are deleted
#### Scenario: Delete by pattern type
- **WHEN** the user clicks the delete icon next to "Engulfing" in the results summary
- **THEN** all span annotations with `source: "talib"` and label containing "Engulfing" for the current chart are deleted
#### Scenario: Detection results summary
- **WHEN** pattern detection completes with results
- **THEN** the panel shows a summary: total patterns found, grouped by pattern name with counts (e.g., "Engulfing: 5, Hammer: 3")

View file

@ -0,0 +1,64 @@
## Purpose
Provide a UI panel for managing ML model training, selecting model types, monitoring training progress, and viewing training run history.
## Requirements
### Requirement: Training panel
The system SHALL display a collapsible "Training" panel in the sidebar. The panel SHALL contain model type selection, a "Start Training" button, and training run history. The panel SHALL be usable independently of the prediction panel.
#### Scenario: Display training panel
- **WHEN** the user expands the Training panel
- **THEN** the panel shows model type selector, training action button, and recent training history
### Requirement: Model type selection
The system SHALL provide a dropdown to select the ML model type for training. Available options SHALL be "Random Forest" and "XGBoost". The selection SHALL default to "Random Forest".
#### Scenario: Select model type
- **WHEN** the user opens the model type dropdown
- **THEN** "Random Forest" and "XGBoost" are listed as options
#### Scenario: Default selection
- **WHEN** the training panel loads
- **THEN** "Random Forest" is pre-selected
### Requirement: Training execution
The system SHALL trigger a training run via `POST /api/training/start` when the user clicks "Start Training". The request SHALL include the selected model type. The system SHALL show a progress indicator and poll for status updates every 5 seconds while training is active. The "Start Training" button SHALL be disabled while a training run is in progress.
#### Scenario: Start training
- **WHEN** the user selects "XGBoost" and clicks "Start Training"
- **THEN** the system sends `POST /api/training/start` with `{model_type: "xgboost"}`, disables the button, and shows a "Training in progress..." indicator
#### Scenario: Training completes
- **WHEN** a training run status changes from "running" to "completed"
- **THEN** the system shows a success message with key metrics (accuracy, F1 score), re-enables the button, and adds the run to training history
#### Scenario: Training fails
- **WHEN** a training run status changes to "failed"
- **THEN** the system shows an error message with the failure reason and re-enables the button
#### Scenario: Prevent concurrent training
- **WHEN** a training run is already in progress and the user tries to start another
- **THEN** the "Start Training" button remains disabled
### Requirement: Training run history
The system SHALL display a list of recent training runs fetched from `GET /api/training/runs`. Each entry SHALL show: model type, status, date, and key metrics (accuracy, F1 macro). The list SHALL be sorted by date descending (most recent first). The list SHALL show the 5 most recent runs.
#### Scenario: Display training history
- **WHEN** the training panel loads
- **THEN** the system fetches training runs and displays them with model type, status badge, date, and metrics
#### Scenario: No training runs
- **WHEN** no training runs exist
- **THEN** the panel shows "No training runs yet"
### Requirement: Dataset info display
The system SHALL display information about the training dataset before training starts. This SHALL include the dataset file path and whether it exists. The user SHALL be informed that training uses the exported annotation dataset.
#### Scenario: Dataset available
- **WHEN** the training panel loads and the labeled dataset exists
- **THEN** the panel shows the dataset path and a ready indicator
#### Scenario: Dataset missing
- **WHEN** the training panel loads and no labeled dataset exists
- **THEN** the panel shows a warning "No training dataset found. Export annotations first." and the "Start Training" button is disabled