7.9 KiB
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
-
1. Implement balanced S/R zone selection in
cluster_sr_zones()-
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 intosupport_zonesandresistance_zonespools sorted by strength descending - Implement round-robin interleave picking: alternate strongest from each pool until
max_zonesis 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
- In
-
* 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 whenmax_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
-
1.5 Update existing unit tests for balanced selection behavior
- Update
tests/unit/test_cluster_sr_zones.pyto 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
- Update
-
-
2. Implement
visible_levelsfiltering in backend-
2.1 Add
visible_levelsfield toSRLevelResponseschema- In
app/schemas/sr_level.py, addvisible_levels: list[SRLevelResult] = []toSRLevelResponse - Requirements: 2.1, 2.3
- In
-
2.2 Compute
visible_levelsin the SR levels router- In
app/routers/sr_levels.py, after computing zones, filterlevel_resultsto only those whoseprice_levelfalls within the[low, high]range of at least one zone - Set the filtered list as
visible_levelson theSRLevelResponse - When zones list is empty,
visible_levelsshould be empty - Requirements: 2.1, 2.2, 2.4
- In
-
* 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_levelsappears inlevelsand has a price within at least one zone's[low, high]range
-
2.4 Update router unit tests for
visible_levels- In
tests/unit/test_sr_levels_router.py, add tests verifying:visible_levelsis present in response,visible_levelscontains only levels within zone bounds,visible_levelsis empty when zones are empty - Requirements: 2.1, 2.2, 2.4
- In
-
-
3. Checkpoint - Ensure all backend tests pass
- Ensure all tests pass, ask the user if questions arise.
-
4. Update frontend types and S/R levels table filtering
-
4.1 Add
visible_levelsto frontendSRLevelResponsetype- In
frontend/src/lib/types.ts, addvisible_levels: SRLevel[]to theSRLevelResponseinterface - Requirements: 2.1
- In
-
4.2 Update
TickerDetailPageto usevisible_levelsfor the S/R table- In
frontend/src/pages/TickerDetailPage.tsx, changesortedLevelsto derive fromsrLevels.data.visible_levelsinstead ofsrLevels.data.levels - Keep sorting by strength descending
- Hide the S/R Levels Table section when
visible_levelsis empty - Maintain existing color coding (green for support, red for resistance)
- Requirements: 3.1, 3.2, 3.3, 3.4
- In
-
-
5. Add Trade Scanner explainer banner and R:R analysis columns
-
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
- In
-
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
SortColumntype andcolumnsarray to include the new columns - Update
sortTradesinScannerPage.tsxto handle sorting by new computed columns - Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8
- In
-
* 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
-
-
6. Convert Rankings weight inputs to sliders
-
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
- In
-
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
useUpdateWeightsmutation - 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
-
-
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