Files
Celes Renata 913fe8b0b3 feat: override trade tab — manual order entry with auto-registration
Backend:
- OverrideOrderRequest/Response Pydantic models with ticker, quantity, price validators
- POST /api/trading/override/order endpoint (enqueue to Redis broker queue)
- auto_register_symbol() module for untracked ticker registration via Symbol Registry
- Unit tests (17) and property-based tests (3 x 100 examples)

Frontend:
- OverrideTradePanel component (order form + positions display)
- Override tab in TradingEngine page with URL search param navigation
- Override Trade button on Trading Controls page
- useSubmitOverrideOrder mutation hook
- MSW handler and 13 component/integration tests

Steering:
- Updated steering docs for Ubuntu dev machine with nvm/Node 24
2026-04-17 07:02:30 +00:00

10 KiB

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

  • 1. Backend: Override order validation model and endpoint

    • 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
    • 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
  • 2. Backend: Auto-registration module

    • 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
    • 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
  • 3. Backend: Override endpoint unit tests and property-based tests

    • 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
    • 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
    • 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
    • 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
  • 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.
  • 5. Frontend: API hook for submitting override orders

    • 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
  • 6. Frontend: OverrideTradePanel component

    • 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
  • 7. Frontend: Override tab integration in TradingEnginePage

    • 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
  • 8. Frontend: Navigation button on Trading Controls page

    • 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
  • 9. Frontend: MSW handlers for testing

    • 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
  • 10. Frontend: Component and integration tests

    • 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
  • 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