# Implementation Plan: UX Improvements

## Overview

Implement four UX improvements: balanced S/R zone selection in the backend, visible levels filtering (backend + frontend), Trade Scanner explainer banner and R:R analysis columns, and Rankings weight slider form. Backend changes use Python (FastAPI/Pydantic), frontend changes use TypeScript (React).

## Tasks

- [x] 1. Implement balanced S/R zone selection in `cluster_sr_zones()`
  - [x] 1.1 Refactor `cluster_sr_zones()` to use interleave-based balanced selection
    - In `app/services/sr_service.py`, after clustering and computing zones, split zones into `support_zones` and `resistance_zones` pools sorted by strength descending
    - Implement round-robin interleave picking: alternate strongest from each pool until `max_zones` is reached or both pools exhausted
    - If one pool is exhausted, fill remaining slots from the other pool
    - Sort final selected zones by strength descending before returning
    - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6_

  - [ ]* 1.2 Write property test: balanced zone selection guarantees both types
    - **Property 1: Balanced zone selection guarantees both types**
    - **Validates: Requirement 1.1**
    - In `tests/unit/test_cluster_sr_zones.py`, use Hypothesis to generate sets of levels with at least one support and one resistance zone, verify output contains at least one of each type when `max_zones >= 2`

  - [ ]* 1.3 Write property test: interleave selection correctness
    - **Property 2: Interleave selection correctness**
    - **Validates: Requirements 1.2, 1.3**
    - Verify the selected zones match the expected round-robin interleave from support and resistance pools

  - [ ]* 1.4 Write property test: zone output sorted by strength
    - **Property 3: Zone output is sorted by strength**
    - **Validates: Requirement 1.4**
    - For any input, verify returned zones are sorted by strength descending

  - [x] 1.5 Update existing unit tests for balanced selection behavior
    - Update `tests/unit/test_cluster_sr_zones.py` to add tests for: mixed support/resistance input produces balanced output, all-support input fills from support only, all-resistance input fills from resistance only, single zone edge case
    - _Requirements: 1.1, 1.2, 1.3, 1.5, 1.6_

- [x] 2. Implement `visible_levels` filtering in backend
  - [x] 2.1 Add `visible_levels` field to `SRLevelResponse` schema
    - In `app/schemas/sr_level.py`, add `visible_levels: list[SRLevelResult] = []` to `SRLevelResponse`
    - _Requirements: 2.1, 2.3_

  - [x] 2.2 Compute `visible_levels` in the SR levels router
    - In `app/routers/sr_levels.py`, after computing zones, filter `level_results` to only those whose `price_level` falls within the `[low, high]` range of at least one zone
    - Set the filtered list as `visible_levels` on the `SRLevelResponse`
    - When zones list is empty, `visible_levels` should be empty
    - _Requirements: 2.1, 2.2, 2.4_

  - [ ]* 2.3 Write property test: visible levels subset within zone bounds
    - **Property 4: Visible levels are a subset within zone bounds**
    - **Validates: Requirements 2.1, 2.2**
    - Verify every entry in `visible_levels` appears in `levels` and has a price within at least one zone's `[low, high]` range

  - [x] 2.4 Update router unit tests for `visible_levels`
    - In `tests/unit/test_sr_levels_router.py`, add tests verifying: `visible_levels` is present in response, `visible_levels` contains only levels within zone bounds, `visible_levels` is empty when zones are empty
    - _Requirements: 2.1, 2.2, 2.4_

- [x] 3. Checkpoint - Ensure all backend tests pass
  - Ensure all tests pass, ask the user if questions arise.

- [x] 4. Update frontend types and S/R levels table filtering
  - [x] 4.1 Add `visible_levels` to frontend `SRLevelResponse` type
    - In `frontend/src/lib/types.ts`, add `visible_levels: SRLevel[]` to the `SRLevelResponse` interface
    - _Requirements: 2.1_

  - [x] 4.2 Update `TickerDetailPage` to use `visible_levels` for the S/R table
    - In `frontend/src/pages/TickerDetailPage.tsx`, change `sortedLevels` to derive from `srLevels.data.visible_levels` instead of `srLevels.data.levels`
    - Keep sorting by strength descending
    - Hide the S/R Levels Table section when `visible_levels` is empty
    - Maintain existing color coding (green for support, red for resistance)
    - _Requirements: 3.1, 3.2, 3.3, 3.4_

- [x] 5. Add Trade Scanner explainer banner and R:R analysis columns
  - [x] 5.1 Add explainer banner to `ScannerPage`
    - In `frontend/src/pages/ScannerPage.tsx`, add a static informational banner above the filter controls
    - Banner text: describe that the scanner identifies asymmetric risk-reward trade setups using S/R levels as targets and ATR-based stops
    - Banner should be visible on initial page load without user interaction
    - _Requirements: 4.1, 4.2, 4.3_

  - [x] 5.2 Add R:R analysis columns to `TradeTable`
    - In `frontend/src/components/scanner/TradeTable.tsx`, add computed columns: Risk $ (`|entry_price - stop_loss|`), Reward $ (`|target - entry_price|`), % to Stop (`risk / entry * 100`), % to Target (`reward / entry * 100`)
    - Color-code the existing R:R ratio column: green for ≥ 3.0, amber for ≥ 2.0, red for < 2.0
    - Update the `SortColumn` type and `columns` array to include the new columns
    - Update `sortTrades` in `ScannerPage.tsx` to handle sorting by new computed columns
    - _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8_

  - [ ]* 5.3 Write property test: trade analysis computation correctness
    - **Property 5: Trade analysis computation correctness**
    - **Validates: Requirements 5.2, 5.3, 5.4, 5.5**
    - Using fast-check, for any trade with positive entry_price, stop_loss, and target, verify `risk_amount == |entry_price - stop_loss|`, `reward_amount == |target - entry_price|`, `stop_pct == risk_amount / entry_price * 100`, `target_pct == reward_amount / entry_price * 100`

- [x] 6. Convert Rankings weight inputs to sliders
  - [x] 6.1 Replace number inputs with range sliders in `WeightsForm`
    - In `frontend/src/components/rankings/WeightsForm.tsx`, replace `<input type="number">` with `<input type="range" min={0} max={100} step={1}>`
    - On mount, convert API decimal weights to 0–100 scale: `Math.round(w * 100)`
    - Display current whole-number value next to each slider
    - Show humanized label (replace underscores with spaces)
    - _Requirements: 6.1, 6.2, 6.3, 6.4_

  - [x] 6.2 Implement weight normalization on submit
    - On submit, normalize slider values: divide each by the sum of all values
    - Disable submit button when all sliders are zero
    - Show validation message "At least one weight must be greater than zero" when all are zero
    - Send normalized decimal weights via existing `useUpdateWeights` mutation
    - _Requirements: 7.1, 7.2, 7.3, 7.4_

  - [ ]* 6.3 Write property test: weight conversion round-trip
    - **Property 6: Weight conversion round-trip**
    - **Validates: Requirement 6.3**
    - Using fast-check, verify that converting decimal weights to slider scale and normalizing back preserves relative proportions within floating-point tolerance

  - [ ]* 6.4 Write property test: normalized weights sum to one
    - **Property 7: Normalized weights sum to one**
    - **Validates: Requirement 7.1**
    - Using fast-check, for any set of slider values (integers 0–100) where at least one > 0, verify normalized weights sum to 1.0 within ±1e-9

- [x] 7. Final checkpoint - Ensure all tests pass
  - Ensure all tests pass, ask the user if questions arise.

## Notes

- Tasks marked with `*` are optional and can be skipped for faster MVP
- Backend uses Python (Hypothesis for property tests), frontend uses TypeScript/React (fast-check for property tests)
- Each task references specific requirements for traceability
- Checkpoints ensure incremental validation after backend and full implementation phases
- Property tests validate universal correctness properties from the design document
