# Requirements Document

## Introduction

This specification covers three UX improvements to the stock signal platform: (1) balanced support/resistance zone selection that ensures both zone types are represented on the chart, with a filtered levels table; (2) a Trade Scanner page enhanced with an explainer banner and detailed risk/reward analysis columns; and (3) a Rankings page weights form that replaces decimal number inputs with intuitive range sliders and automatic normalization.

## Glossary

- **SR_Service**: The backend service (`sr_service.py`) containing `cluster_sr_zones()` that clusters, scores, and selects S/R zones.
- **SR_API**: The FastAPI router endpoint (`/sr-levels/{symbol}`) that returns S/R levels and zones for a ticker.
- **SRLevelResponse**: The Pydantic response model returned by the SR_API containing levels, zones, and metadata.
- **Zone_Selector**: The interleave-based selection logic within `cluster_sr_zones()` that picks zones from support and resistance pools alternately.
- **Visible_Levels**: The subset of all detected S/R levels whose price falls within the bounds of at least one returned zone.
- **Ticker_Detail_Page**: The frontend page (`TickerDetailPage.tsx`) displaying chart, scores, sentiment, fundamentals, and S/R data for a single ticker.
- **SR_Levels_Table**: The HTML table on the Ticker_Detail_Page that lists individual S/R levels sorted by strength.
- **Scanner_Page**: The frontend page (`ScannerPage.tsx`) displaying trade setups with filtering and sorting.
- **Trade_Table**: The table component (`TradeTable.tsx`) rendering trade setup rows on the Scanner_Page.
- **Explainer_Banner**: A static informational banner at the top of the Scanner_Page describing what the scanner does.
- **Trade_Analysis**: A set of computed fields (risk amount, reward amount, stop percentage, target percentage) derived client-side from each trade setup row.
- **Rankings_Page**: The frontend page displaying ticker rankings with configurable scoring weights.
- **Weights_Form**: The form component (`WeightsForm.tsx`) on the Rankings_Page for adjusting scoring dimension weights.
- **Weight_Slider**: A range input (0–100) replacing the current decimal number input for each scoring weight dimension.
- **Normalization**: The process of dividing each slider value by the sum of all slider values to produce decimal weights that sum to 1.0.

## Requirements

### Requirement 1: Balanced Zone Selection

**User Story:** As a trader, I want the S/R zone selection to include both support and resistance zones when both exist, so that I get a balanced view of key price levels regardless of the ticker's trend direction.

#### Acceptance Criteria

1. WHEN both support and resistance zones exist and `max_zones` is 2 or greater, THE Zone_Selector SHALL return at least one support zone and at least one resistance zone.
2. THE Zone_Selector SHALL select zones by alternating picks from the support pool and the resistance pool, each sorted by strength descending, until `max_zones` is reached.
3. WHEN one pool is exhausted before `max_zones` is reached, THE Zone_Selector SHALL fill the remaining slots from the other pool in strength-descending order.
4. THE Zone_Selector SHALL sort the final selected zones by strength descending before returning them.
5. WHEN no S/R levels are provided, THE SR_Service SHALL return an empty zones list.
6. WHEN `max_zones` is zero or negative, THE SR_Service SHALL return an empty zones list.

### Requirement 2: Visible Levels Filtering

**User Story:** As a trader, I want the API to provide a filtered list of S/R levels that correspond to the zones shown on the chart, so that the levels table only shows relevant data.

#### Acceptance Criteria

1. THE SRLevelResponse SHALL include a `visible_levels` field containing only the S/R levels whose price falls within the bounds of at least one returned zone.
2. THE `visible_levels` field SHALL be a subset of the `levels` field in the same response.
3. THE SRLevelResponse SHALL continue to include the full `levels` field for backward compatibility.
4. WHEN the zones list is empty, THE SRLevelResponse SHALL return an empty `visible_levels` list.

### Requirement 3: S/R Levels Table Filtering

**User Story:** As a trader, I want the S/R levels table below the chart to show only levels that correspond to zones visible on the chart, so that the table and chart are consistent.

#### Acceptance Criteria

1. THE SR_Levels_Table SHALL render levels from the `visible_levels` field of the API response instead of the full `levels` field.
2. THE SR_Levels_Table SHALL sort displayed levels by strength descending.
3. THE SR_Levels_Table SHALL color-code each level row green for support and red for resistance.
4. WHEN `visible_levels` is empty, THE Ticker_Detail_Page SHALL hide the SR_Levels_Table section.

### Requirement 4: Trade Scanner Explainer Banner

**User Story:** As a user, I want to see a brief explanation of what the Trade Scanner does when I visit the page, so that I understand the purpose and methodology of the displayed trade setups.

#### Acceptance Criteria

1. THE Scanner_Page SHALL display an Explainer_Banner above the filter controls.
2. THE Explainer_Banner SHALL contain static text describing that the scanner identifies asymmetric risk-reward trade setups using S/R levels as targets and ATR-based stops.
3. THE Explainer_Banner SHALL be visible on initial page load without requiring user interaction.

### Requirement 5: Trade Scanner R:R Analysis Columns

**User Story:** As a trader, I want to see detailed risk/reward analysis data (risk amount, reward amount, percentage distances, and color-coded R:R ratio) for each trade setup, so that I can evaluate and compare setups at a glance.

#### Acceptance Criteria

1. THE Trade_Table SHALL display the following additional columns: Risk $ (absolute risk amount), Reward $ (absolute reward amount), % to Stop (percentage distance from entry to stop-loss), and % to Target (percentage distance from entry to target).
2. THE Trade_Analysis risk_amount SHALL be computed as the absolute difference between entry_price and stop_loss.
3. THE Trade_Analysis reward_amount SHALL be computed as the absolute difference between target and entry_price.
4. THE Trade_Analysis stop_pct SHALL be computed as risk_amount divided by entry_price multiplied by 100.
5. THE Trade_Analysis target_pct SHALL be computed as reward_amount divided by entry_price multiplied by 100.
6. WHEN the R:R ratio is 3.0 or greater, THE Trade_Table SHALL display the R:R value with green color coding.
7. WHEN the R:R ratio is 2.0 or greater but less than 3.0, THE Trade_Table SHALL display the R:R value with amber color coding.
8. WHEN the R:R ratio is less than 2.0, THE Trade_Table SHALL display the R:R value with red color coding.

### Requirement 6: Rankings Weight Slider Input

**User Story:** As a user, I want to adjust scoring weights using range sliders with whole-number values instead of typing decimal numbers, so that the input is intuitive and less error-prone.

#### Acceptance Criteria

1. THE Weights_Form SHALL render each weight dimension as a Weight_Slider with a range of 0 to 100 and a step of 1.
2. THE Weights_Form SHALL display the current whole-number value next to each Weight_Slider.
3. WHEN the Weights_Form receives API weight values (decimals between 0 and 1), THE Weights_Form SHALL convert each value to the 0–100 scale by multiplying by 100 and rounding to the nearest integer.
4. THE Weights_Form SHALL display a humanized label for each weight dimension by replacing underscores with spaces.

### Requirement 7: Weight Normalization on Submit

**User Story:** As a user, I want my slider values to be automatically normalized to valid decimal weights when I submit, so that I don't need to manually ensure they sum to 1.0.

#### Acceptance Criteria

1. WHEN the user submits the Weights_Form and at least one slider value is greater than zero, THE Weights_Form SHALL normalize each slider value by dividing it by the sum of all slider values.
2. WHEN all slider values are zero, THE Weights_Form SHALL disable the submit button.
3. WHEN all slider values are zero, THE Weights_Form SHALL display a validation message stating that at least one weight must be greater than zero.
4. THE Weights_Form SHALL send the normalized decimal weights to the API using the existing mutation hook.
