Skip to content

feat(broker): add Alpaca paper order draft - #310

Merged
Melly-999 merged 1 commit into
mainfrom
feature/alpaca-paper-order-draft-001
Jun 16, 2026
Merged

feat(broker): add Alpaca paper order draft#310
Melly-999 merged 1 commit into
mainfrom
feature/alpaca-paper-order-draft-001

Conversation

@Melly-999

@Melly-999 Melly-999 commented Jun 16, 2026

Copy link
Copy Markdown
Owner

⚠️ Runtime/backend code PR with a POST route. The POST verb only accepts a request body — no external broker state is mutated. This is a local-only, draft-only surface: it does not submit orders to Alpaca, does not call the Alpaca SDK, and performs no network I/O.

Scope

Backend local draft-only route + schema + service + tests + guardrail allowlist registration + task doc. No frontend, scripts, workflows, package, Docker/Tauri, cloud config, or env files.

  • Baseline SHA: bc6f24584ef6e3f6aedea65cb787c55a71aa90c4
  • Commit SHA: 1def558c60907dcd4aa1a324efbc25e0cbe917c1

Files changed

  • app/schemas/alpaca_paper_order_draft.py (new)
  • app/services/alpaca_paper_order_draft_service.py (new)
  • app/api/routes/alpaca_paper.py (modified — new POST route)
  • tests/app/test_alpaca_paper_order_draft.py (new)
  • tests/app/test_safety_invariants.py (modified — admin allowlist)
  • tests/app/test_paper_sandbox_guardrails.py (modified — safe non-execution allowlist)
  • docs/tasks/alpaca_paper_order_draft_001.md (new)

Implementation summary

  • SchemaAlpacaPaperOrderDraftRequest / AlpacaPaperOrderDraft / AlpacaPaperOrderDraftResponse (extra="forbid"). Response carries draft_only=true, order_submission_enabled=false, execution_enabled=false, live_orders_blocked=true, dry_run=true, read_only=true, requires_human_review=true, message "Draft only — not submitted to Alpaca."
  • Servicebuild_alpaca_paper_order_draft: validates side / order_type / time_in_force, exactly-one positive quantity|notional, required positive entry_price/stop_loss/take_profit, BUY/SELL geometry, and max_risk_pct <= 1.0. No Alpaca SDK import, no network, no env reads. Local deterministic paper-draft-* ids only (never a broker order id).
  • RoutePOST /api/alpaca-paper/order-draftAlpacaPaperOrderDraftResponse. Validation failures return valid=false (HTTP 200), mirroring the existing order-preview pattern.

Guardrail changes (narrow, additive only)

  • test_safety_invariants.py — added ("POST", "/api/alpaca-paper/order-draft") to ADMIN_NON_GET_ALLOWLIST with justification (mirrors the existing /api/paper/tickets/draft).
  • test_paper_sandbox_guardrails.py — added the path to SAFE_ADMIN_NON_EXECUTION_PATHS and made guard-2 honor that allowlist exactly like guard-4 already does.
  • No broad weakening: changes are path-specific (+7 lines, 0 deletions). No wildcard exemption; all live-execution / order-submission protections remain intact and still reject unsafe paths.

Tests / validation summary

  • New draft suite: 21 tests (valid BUY/SELL, notional; invalid side/order_type/time_in_force; both/neither/non-positive quantity-notional; missing stop_loss/take_profit; bad geometry; max_risk_pct > 1%; all safety flags incl. draft_only/order_submission_enabled=false/execution_enabled=false; no forbidden fields; service makes no network call; source has no SDK/submission references; route POST 200 valid+blocked; GET → 405; OpenAPI POST-only draft path).
  • Full tests/app suite: 2415 passed.
  • python scripts/validate_safety_config.py → OVERALL: PASS.
  • black --check, flake8, mypy (new files) → clean. git diff --check → clean.

Static scan summary

No secrets / token-shaped strings / DB URLs / API keys / broker credentials / account IDs / emails / phones / Neon identifiers. No safety-flip values (the autotrade=true matches are the pre-existing forbidden-token denylist in the guardrail test). No from alpaca. SDK import and no mutating-order calls (submit_order/place_order/cancel_order/replace_order/TradingClient/MarketOrderRequest/OrderSide) in new code — matches exist only in prohibition comments, denylists, and tests asserting absence.

Out of scope

  • Alpaca SDK usage
  • network / broker API calls
  • order submission / cancellation / replacement
  • live broker connection / live credentials
  • frontend trading controls (Buy/Sell/Order/Execute)

Safety confirmation

  • draft-only; does NOT submit orders to Alpaca
  • no Alpaca SDK ✅ · no network calls ✅ · no broker API calls ✅
  • no order submission / cancellation / replacement / execution ✅
  • no live broker endpoint / no live credentials ✅
  • no frontend changes / no Buy/Sell/Order/Execute UI ✅
  • no scripts / workflows / package / env changes ✅
  • no secrets ✅ · no live-trading / profit/ROI/win-rate / financial-advice claims ✅
  • safety posture unchanged (autotrade=false, dry_run=true, read_only=true, live_orders_blocked=true, max risk <= 1%) ✅

🤖 Generated with Claude Code

Summary by Sourcery

Add a local-only Alpaca paper order draft endpoint that validates trade parameters and returns a non-executing draft response while preserving existing broker safety guarantees.

New Features:

  • Introduce POST /api/alpaca-paper/order-draft endpoint to build Alpaca paper order drafts without submitting them to any broker.
  • Define request, draft, and response schemas for Alpaca paper order drafts with strict safety and field constraints.

Enhancements:

  • Add a dedicated service to validate Alpaca paper order draft inputs, enforce risk and geometry rules, and generate deterministic local draft identifiers.
  • Extend safety guardrails to recognize the new order-draft route as a safe, non-executing admin path.

Documentation:

  • Add a task document describing the Alpaca paper order draft endpoint, its safety constraints, validation rules, and out-of-scope behaviors.

Tests:

  • Add a comprehensive test suite for the Alpaca paper order draft service and route, covering validation behavior, safety flags, forbidden fields, network isolation, and OpenAPI exposure.
  • Update safety and guardrail tests to include the new order-draft path in approved non-execution and admin allowlists.

Add a local-only Alpaca paper order DRAFT surface. It validates user input and
returns a structured draft for human review — it does NOT submit to Alpaca, call
any broker, place/cancel/replace any order, execute, or touch the network.

- app/schemas/alpaca_paper_order_draft.py — request/draft/response models
  (extra="forbid"; draft_only=true, order_submission_enabled=false,
  execution_enabled=false, live_orders_blocked=true, dry_run=true,
  read_only=true, requires_human_review=true; message "Draft only — not
  submitted to Alpaca.").
- app/services/alpaca_paper_order_draft_service.py — validation + draft builder.
  Validates side/order_type/time_in_force, exactly-one quantity|notional,
  required positive stop_loss/take_profit/entry, geometry, and max_risk_pct<=1%.
  No Alpaca SDK import, no network, no env reads. paper-draft-* ids only.
- app/api/routes/alpaca_paper.py — POST /api/alpaca-paper/order-draft
  (local-only; the only mutating method; clearly documented as not submitted).
- tests/app/test_alpaca_paper_order_draft.py — 21 tests (valid/blocked drafts,
  forbidden-field absence, no-network proof, source has no SDK/submission, route
  POST 200, GET 405, OpenAPI POST-only draft path).
- tests/app/test_safety_invariants.py & test_paper_sandbox_guardrails.py —
  register the reviewed safe non-execution POST route in the admin allowlists
  (mirrors the existing /api/paper/tickets/draft precedent).
- docs/tasks/alpaca_paper_order_draft_001.md.

Does NOT submit orders to Alpaca. No live broker, no credentials, no secrets,
no frontend trading controls. Safety posture unchanged: autotrade=false,
dry_run=true, read_only=true, live_orders_blocked=true, max risk <= 1%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Melly-999
Melly-999 temporarily deployed to feature/alpaca-paper-order-draft-001 - alpha_data_scraper_ai PR #310 June 16, 2026 01:02 — with Render Destroyed
@sourcery-ai

sourcery-ai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a new backend Alpaca paper order draft POST endpoint that validates trade parameters and returns a local-only draft object, plus supporting schema, service, tests, and narrow guardrail/allowlist updates to keep it classified as a non-execution, dry-run-only route.

Sequence diagram for the new Alpaca paper order draft POST flow

sequenceDiagram
    actor Client
    participant Router as AlpacaPaperRouter
    participant Service as AlpacaPaperOrderDraftService

    Client->>Router: POST /api/alpaca-paper/order-draft
    Router->>Service: build_alpaca_paper_order_draft(AlpacaPaperOrderDraftRequest)
    alt [all validations pass]
        Service-->>Router: AlpacaPaperOrderDraftResponse(valid=true, draft=AlpacaPaperOrderDraft)
    else [any validation fails]
        Service-->>Router: AlpacaPaperOrderDraftResponse(valid=false, draft=None)
    end
    Router-->>Client: 200 OK (JSON AlpacaPaperOrderDraftResponse)
Loading

File-Level Changes

Change Details Files
Introduce Alpaca paper order draft request/draft/response schemas with strict safety flags and forbidden extras.
  • Define AlpacaPaperOrderDraftRequest with free-form side/order_type/time_in_force, mutually exclusive quantity/notional, and required price fields for geometry checks.
  • Define AlpacaPaperOrderDraft with deterministic local draft_id, BUY/SELL side, constrained order_type/time_in_force, and status='draft'.
  • Define AlpacaPaperOrderDraftResponse extending AlpacaPaperSafetyFlags, adding draft_only/order_submission_enabled/message fields and forbidding extra keys.
app/schemas/alpaca_paper_order_draft.py
Add a dedicated service to build Alpaca paper order drafts with validation, deterministic IDs, and no broker/network interaction.
  • Implement build_alpaca_paper_order_draft to normalize inputs, validate enums and quantity/notional, enforce price positivity, risk cap, and BUY/SELL geometry, and return valid=false drafts on failure.
  • Generate a paper-draft-* ID using a short SHA-256 hash of a canonical key plus current UTC timestamp, ensuring local-only, deterministic IDs.
  • Ensure the service performs no Alpaca SDK imports, network I/O, env reads, or inclusion of forbidden broker/account fields in its models.
app/services/alpaca_paper_order_draft_service.py
Expose the draft builder via a new POST /api/alpaca-paper/order-draft route wired to the service and documented as draft-only.
  • Add OpenAPI description/summary emphasizing local-only behavior, no submission to Alpaca, no network/DB writes, and safety invariants.
  • Define post_alpaca_paper_order_draft endpoint that accepts AlpacaPaperOrderDraftRequest and returns AlpacaPaperOrderDraftResponse via the service function.
  • Ensure validation failures are represented as HTTP 200 responses with valid=false, mirroring existing order-preview behavior.
app/api/routes/alpaca_paper.py
Update safety guardrails and admin allowlists to recognize the new draft route as a non-execution, dry-run-only POST endpoint.
  • Add the POST /api/alpaca-paper/order-draft tuple to ADMIN_NON_GET_ALLOWLIST with detailed justification comments.
  • Add the path to SAFE_ADMIN_NON_EXECUTION_PATHS and make the non-paper live-execution guard skip these reviewed safe paths before applying execution heuristics.
tests/app/test_safety_invariants.py
tests/app/test_paper_sandbox_guardrails.py
Add a focused test suite covering draft validation behavior, safety flags, absence of forbidden fields, network isolation, and OpenAPI shape for the new endpoint.
  • Test valid BUY/SELL and notional-based drafts, and that invalid side/order_type/time_in_force, quantity/notional combinations, missing prices, bad geometry, and excessive max_risk_pct all yield valid=false with no draft.
  • Assert that all safety flags (draft_only, order_submission_enabled=false, execution_enabled=false, dry_run, read_only, live_orders_blocked, requires_human_review, paper_only) are set on responses and that no forbidden keys appear anywhere in the payload.
  • Verify the service does not open sockets or reference Alpaca SDK/submission symbols, that the route is POST-only (GET 405), and that OpenAPI advertises only POST and a non-execution path name.
  • Document the task and behavior in a new public-safe task markdown file for reviewers and future work references.
tests/app/test_alpaca_paper_order_draft.py
docs/tasks/alpaca_paper_order_draft_001.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Melly-999
Melly-999 marked this pull request as ready for review June 16, 2026 01:33
@Melly-999
Melly-999 merged commit 60b0a34 into main Jun 16, 2026
7 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The allowed order_type/time_in_force values are defined both as Literal aliases in alpaca_paper_order_draft.py and as _ALLOWED_* frozensets in the service; consider deriving one from the other (or centralizing them) to avoid drift between schema and validation logic.
  • In build_alpaca_paper_order_draft, side, order_type, and time_in_force are normalized but then passed to the draft model with # type: ignore[arg-type]; you could narrow their types (e.g., via small helper functions or casts after validation) so the AlpacaPaperOrderDraft construction is type-safe without ignores.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The allowed `order_type`/`time_in_force` values are defined both as `Literal` aliases in `alpaca_paper_order_draft.py` and as `_ALLOWED_*` frozensets in the service; consider deriving one from the other (or centralizing them) to avoid drift between schema and validation logic.
- In `build_alpaca_paper_order_draft`, `side`, `order_type`, and `time_in_force` are normalized but then passed to the draft model with `# type: ignore[arg-type]`; you could narrow their types (e.g., via small helper functions or casts after validation) so the `AlpacaPaperOrderDraft` construction is type-safe without ignores.

Fix all in Cursor


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant