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
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
OverrideOrderRequestandOverrideOrderResponsePydantic models toservices/trading/app.pyOverrideOrderRequestwith 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 enforceslimit_pricerequired when order_type is "limit" or "stop_limit", andstop_pricerequired when order_type is "stop" or "stop_limit" OverrideOrderResponsewith 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/orderendpoint toservices/trading/app.py- Accept
OverrideOrderRequestbody - Call
auto_register_symbol()fromservices/trading/override.pyif ticker is untracked (check via Symbol RegistryGET /companies?ticker=...) - Generate idempotency key as
"override-{uuid4()}" - Build job payload with
ticker,side,quantity,order_type,limit_price,stop_price,source: "manual_override", andidempotency_key - Enqueue job to Redis
stonks:queue:brokerviaRPUSH - Return 202 with
OverrideOrderResponse - Return 503 if Redis is unreachable
- Use
engine.redisfor Redis access andconfigfor registry base URL - Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 9.1
- Accept
-
-
2. Backend: Auto-registration module
-
2.1 Create
services/trading/override.pywithauto_register_symbol()function- Use
httpx.AsyncClientto call Symbol Registry HTTP endpoints - Check if ticker exists via
GET /companies?ticker={ticker} - If not found,
POST /companiesto create company (legal_name = ticker, active = true) - Handle 409 conflict gracefully (fetch existing company and proceed)
- Create two default sources:
market_apiandnews_apiviaPOST /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
- Use
-
2.2 Write unit tests for
auto_register_symbol()intests/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_keystarts 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.
- Ensure all backend tests pass with
-
5. Frontend: API hook for submitting override orders
- 5.1 Add
useSubmitOverrideOrdermutation hook tofrontend/src/api/tradingHooks.ts- Define
OverrideOrderRequestinterface:ticker,side("buy"|"sell"),quantity,order_type("market"|"limit"|"stop"|"stop_limit"), optionallimit_price, optionalstop_price - Define
OverrideOrderResponseinterface:job_id,status,ticker,side,quantity,auto_registered - Use
apiPost<OverrideOrderResponse>('trading', '/api/trading/override/order', body) - On success, invalidate
orders,positions, andcompaniesquery keys - Requirements: 2.5, 3.4, 7.1
- Define
- 5.1 Add
-
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
useSubmitOverrideOrdermutation - 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 fromapi/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
- 6.1 Create
-
7. Frontend: Override tab integration in TradingEnginePage
- 7.1 Update
frontend/src/pages/TradingEngine.tsxto add Override tab- Add
{ id: 'override', label: 'Override' }to the TABS array - Move tab state from
useStateto URL search params using TanStack RouteruseSearch/useNavigatefor the/trading/engineroute - Default to
'overview'when notabparam is present - Import and render
OverrideTradePanelwhenactiveTab === 'override' - Update the route definition in
frontend/src/routes.tsxto accepttabsearch param viavalidateSearch - Requirements: 1.1, 1.2, 1.3
- Add
- 7.1 Update
-
8. Frontend: Navigation button on Trading Controls page
- 8.1 Add "Override Trade" button to
frontend/src/pages/Trading.tsx- Add a TanStack Router
Linkcomponent inside the Trading Mode card section - Navigate to
/trading/enginewith 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
- Add a TanStack Router
- 8.1 Add "Override Trade" button to
-
9. Frontend: MSW handlers for testing
- 9.1 Add MSW handler for
POST /trading/api/trading/override/orderinfrontend/src/test/mocks/handlers.ts- Return 202 with mock
OverrideOrderResponsecontainingjob_id,status: "queued", echoedticker,side,quantity, andauto_registered: false - Requirements: 3.4, 7.1
- Return 202 with mock
- 9.1 Add MSW handler for
-
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
- 10.1 Write frontend tests for Override tab in
-
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 -qand frontend withcd frontend && npx vitest --run, ask the user if questions arise.
- Ensure all tests pass: backend with
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_traceJSONB - No new database migrations are required; override orders use existing
orders,order_events, andrisk_evaluationstables