# Implementation Plan: Override Trade Tab

## Overview

Add a manual order entry interface to the Stonks Oracle trading engine. The implementation spans backend (FastAPI endpoint + auto-registration module), frontend (Override tab with order form and positions display, navigation button, API hook), MSW test handlers, property-based tests for the 3 correctness properties, and frontend component tests. Each task builds incrementally, wiring components together as they are created.

## Tasks

- [x] 1. Backend: Override order validation model and endpoint
  - [x] 1.1 Add `OverrideOrderRequest` and `OverrideOrderResponse` Pydantic models to `services/trading/app.py`
    - `OverrideOrderRequest` with fields: `ticker` (str), `side` (Literal["buy","sell"]), `quantity` (float > 0), `order_type` (Literal["market","limit","stop","stop_limit"], default "market"), `limit_price` (Optional[float]), `stop_price` (Optional[float])
    - Add a `@field_validator("ticker")` that uppercases and validates against `^[A-Z]{1,10}$`
    - Add a `@model_validator(mode="after")` that enforces `limit_price` required when order_type is "limit" or "stop_limit", and `stop_price` required when order_type is "stop" or "stop_limit"
    - `OverrideOrderResponse` with fields: `job_id` (str), `status` (str), `ticker` (str), `side` (str), `quantity` (float), `auto_registered` (bool)
    - _Requirements: 2.1, 2.2, 3.1, 3.5_

  - [x] 1.2 Add `POST /api/trading/override/order` endpoint to `services/trading/app.py`
    - Accept `OverrideOrderRequest` body
    - Call `auto_register_symbol()` from `services/trading/override.py` if ticker is untracked (check via Symbol Registry `GET /companies?ticker=...`)
    - Generate idempotency key as `"override-{uuid4()}"`
    - Build job payload with `ticker`, `side`, `quantity`, `order_type`, `limit_price`, `stop_price`, `source: "manual_override"`, and `idempotency_key`
    - Enqueue job to Redis `stonks:queue:broker` via `RPUSH`
    - Return 202 with `OverrideOrderResponse`
    - Return 503 if Redis is unreachable
    - Use `engine.redis` for Redis access and `config` for registry base URL
    - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 9.1_

- [x] 2. Backend: Auto-registration module
  - [x] 2.1 Create `services/trading/override.py` with `auto_register_symbol()` function
    - Use `httpx.AsyncClient` to call Symbol Registry HTTP endpoints
    - Check if ticker exists via `GET /companies?ticker={ticker}`
    - If not found, `POST /companies` to create company (legal_name = ticker, active = true)
    - Handle 409 conflict gracefully (fetch existing company and proceed)
    - Create two default sources: `market_api` and `news_api` via `POST /companies/{id}/sources`
    - Fetch active watchlists via `GET /watchlists`; add company to first active watchlist, or create "Manual Overrides" watchlist if none exist
    - Source and watchlist failures are logged but do not block order enqueuing (best-effort)
    - Return `(auto_registered: bool, company_id: str)`
    - Registry base URL derived from env var or defaults to `http://symbol-registry:8000`
    - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6_

  - [x] 2.2 Write unit tests for `auto_register_symbol()` in `tests/test_override.py`
    - Mock httpx calls to Symbol Registry
    - Test new symbol registration (company + sources + watchlist)
    - Test existing symbol skip
    - Test 409 conflict handling
    - Test source/watchlist failure tolerance
    - _Requirements: 4.1, 4.2, 4.3, 4.5, 4.6_

- [x] 3. Backend: Override endpoint unit tests and property-based tests
  - [x] 3.1 Write unit tests for override endpoint in `tests/test_override.py`
    - Test valid order returns 202 with correct response shape
    - Test invalid ticker returns 422
    - Test missing limit_price for limit order returns 422
    - Test missing stop_price for stop order returns 422
    - Test non-positive quantity returns 422
    - Test enqueued job has correct structure and `source: "manual_override"`
    - _Requirements: 3.1, 3.2, 3.4, 3.5, 9.1_

  - [x] 3.2 Write property test for ticker validation and normalization in `tests/test_pbt_override.py`
    - **Property 1: Ticker validation and normalization**
    - Use Hypothesis `@settings(max_examples=100)` to generate arbitrary strings
    - Assert: after uppercasing, accepted iff matches `^[A-Z]{1,10}$`; normalized output is always uppercased input
    - **Validates: Requirements 2.2, 8.1**

  - [x] 3.3 Write property test for override job payload completeness in `tests/test_pbt_override.py`
    - **Property 2: Override job payload completeness**
    - Use Hypothesis to generate valid override order requests
    - Assert: enqueued payload contains all required fields, `source == "manual_override"`, `idempotency_key` starts with `"override-"`
    - **Validates: Requirements 3.2, 9.1**

  - [x] 3.4 Write property test for invalid override order rejection in `tests/test_pbt_override.py`
    - **Property 3: Invalid override order rejection**
    - Use Hypothesis to generate orders violating at least one validation rule
    - Assert: endpoint returns 422 with at least one descriptive error message
    - **Validates: Requirements 3.5, 2.6**

- [x] 4. Checkpoint — Backend tests
  - Ensure all backend tests pass with `.venv/bin/python -m pytest tests/test_override.py tests/test_pbt_override.py -x --tb=short -q`, ask the user if questions arise.

- [x] 5. Frontend: API hook for submitting override orders
  - [x] 5.1 Add `useSubmitOverrideOrder` mutation hook to `frontend/src/api/tradingHooks.ts`
    - Define `OverrideOrderRequest` interface: `ticker`, `side` ("buy"|"sell"), `quantity`, `order_type` ("market"|"limit"|"stop"|"stop_limit"), optional `limit_price`, optional `stop_price`
    - Define `OverrideOrderResponse` interface: `job_id`, `status`, `ticker`, `side`, `quantity`, `auto_registered`
    - Use `apiPost<OverrideOrderResponse>('trading', '/api/trading/override/order', body)`
    - On success, invalidate `orders`, `positions`, and `companies` query keys
    - _Requirements: 2.5, 3.4, 7.1_

- [x] 6. Frontend: OverrideTradePanel component
  - [x] 6.1 Create `frontend/src/pages/trading/OverrideTradePanel.tsx`
    - Order form section with controlled inputs: ticker (text, auto-uppercase), side (buy/sell toggle), quantity (number), order type (select: market/limit/stop/stop_limit), conditional limit_price and stop_price fields
    - Client-side validation: ticker 1-10 alpha chars, positive quantity, required price fields based on order type
    - Inline validation error display on invalid inputs
    - Submit via `useSubmitOverrideOrder` mutation
    - Success: show green toast/banner with job_id and "queued" status, reset form
    - 422 error: display validation errors inline
    - 503 error: show "Broker service is unavailable" banner
    - Network error: show connectivity error banner
    - Submit button disabled during flight with loading state
    - Positions display section using existing `usePositions()` hook from `api/hooks.ts`
    - Positions table showing: ticker, quantity, avg entry price, current price, unrealized P&L
    - Loading spinner while positions load, "No current positions" message when empty
    - _Requirements: 1.2, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 6.1, 6.2, 6.3, 6.4, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 8.1_

- [x] 7. Frontend: Override tab integration in TradingEnginePage
  - [x] 7.1 Update `frontend/src/pages/TradingEngine.tsx` to add Override tab
    - Add `{ id: 'override', label: 'Override' }` to the TABS array
    - Move tab state from `useState` to URL search params using TanStack Router `useSearch` / `useNavigate` for the `/trading/engine` route
    - Default to `'overview'` when no `tab` param is present
    - Import and render `OverrideTradePanel` when `activeTab === 'override'`
    - Update the route definition in `frontend/src/routes.tsx` to accept `tab` search param via `validateSearch`
    - _Requirements: 1.1, 1.2, 1.3_

- [x] 8. Frontend: Navigation button on Trading Controls page
  - [x] 8.1 Add "Override Trade" button to `frontend/src/pages/Trading.tsx`
    - Add a TanStack Router `Link` component inside the Trading Mode card section
    - Navigate to `/trading/engine` with search param `{ tab: 'override' }`
    - Style with `rounded-md bg-brand-600 px-4 py-2 text-sm font-medium text-white hover:bg-brand-700`
    - _Requirements: 5.1, 5.2, 5.3_

- [x] 9. Frontend: MSW handlers for testing
  - [x] 9.1 Add MSW handler for `POST /trading/api/trading/override/order` in `frontend/src/test/mocks/handlers.ts`
    - Return 202 with mock `OverrideOrderResponse` containing `job_id`, `status: "queued"`, echoed `ticker`, `side`, `quantity`, and `auto_registered: false`
    - _Requirements: 3.4, 7.1_

- [x] 10. Frontend: Component and integration tests
  - [x] 10.1 Write frontend tests for Override tab in `frontend/src/test/override.test.tsx`
    - Test override tab renders in tab bar
    - Test override tab shows form and positions sections
    - Test override tab accessible via URL param `?tab=override`
    - Test order form fields are present (ticker, side, quantity, order type)
    - Test conditional price fields show/hide based on order type
    - Test form validation errors for invalid inputs
    - Test successful order submission shows success message and resets form
    - Test 422 error display
    - Test submit button loading state during submission
    - Test positions table renders with mock data
    - Test positions loading state
    - Test positions empty state
    - Test "Override Trade" button on Trading page exists and links correctly
    - _Requirements: 1.1, 1.2, 1.3, 2.1, 2.3, 2.4, 2.6, 5.1, 5.2, 6.1, 6.2, 6.3, 6.4, 7.1, 7.2, 7.5, 7.6_

- [x] 11. Final checkpoint
  - Ensure all tests pass: backend with `.venv/bin/python -m pytest tests/test_override.py tests/test_pbt_override.py -x --tb=short -q` and frontend with `cd frontend && npx vitest --run`, ask the user if questions arise.

## Notes

- Tasks marked with `*` are optional and can be skipped for faster MVP
- Each task references specific requirements for traceability
- Checkpoints ensure incremental validation
- Property tests validate universal correctness properties from the design document
- Unit tests validate specific examples and edge cases
- The broker_service does not need modification — it already processes the full job payload and persists `decision_trace` JSONB
- No new database migrations are required; override orders use existing `orders`, `order_events`, and `risk_evaluations` tables
