Skip to content

feat(broker): add Alpaca paper read-only adapter - #309

Merged
Melly-999 merged 1 commit into
mainfrom
feature/alpaca-paper-readonly-adapter-001
Jun 15, 2026
Merged

feat(broker): add Alpaca paper read-only adapter#309
Melly-999 merged 1 commit into
mainfrom
feature/alpaca-paper-readonly-adapter-001

Conversation

@Melly-999

Copy link
Copy Markdown
Owner

⚠️ Runtime/backend code PR — first backend code step toward Alpaca Paper integration. Strictly GET-only / read-only: no order placement, no execution, no live broker, no committed credentials.

Scope

Backend read-only adapter + GET route + tests + task doc. No frontend, scripts, workflows, package, Docker/Tauri, cloud config, or env files.

  • Baseline SHA: 99ff2f11971f5de214d7547f2ea89e465cae573e
  • Commit SHA: bf9a01cbf889367dc462073f74d3d47bb5b8e1aa

Files changed

  • app/api/routes/alpaca_paper.py (1 new GET route)
  • app/schemas/alpaca_paper_readonly.py (new)
  • app/services/alpaca_paper_readonly_adapter.py (new)
  • tests/app/test_alpaca_paper_readonly_adapter.py (new)
  • docs/tasks/alpaca_paper_readonly_adapter_001.md (new)

Implementation summary

  • AlpacaPaperReadOnlyAdapter — optional injected client + env_reader. Resolves a client only when explicitly enabled (ALPACA_PAPER_READONLY_ENABLED=true + ALPACA_ENV=paper + credentials present) or when a client is injected; otherwise a safe degraded fallback. The default client factory lazy-imports the Alpaca SDK with paper=True (paper endpoint only) and degrades on any error. Only the read-only get_all_positions is referenced.
  • GET /api/alpaca-paper/positions-preview — sanitized positions preview (no mutating routes added).
  • Degraded fallback — no creds / not enabled / unsafe ALPACA_ENV / client error → mode=degraded_demo, connected=false, source=fallback, empty positions; never raises.
  • Injected fake client support — treated as a trusted read-only client for tests (no env, no network).
  • Sanitization/redaction — positions reduced to symbol/qty/market_value/unrealized_pl/side; schema extra="forbid" drops broker-internal fields; credentials never stored, logged, or returned.

Read-only boundary

GET-only · no POST/PUT/PATCH/DELETE · no order placement/cancellation/replacement · no live broker endpoint (paper=True only) · no credential UI · no secrets · no frontend trading controls.

Forbidden output fields confirmed absent: account_id, broker_account_id, broker_order_id, execution_id, api_key, secret, token.

Tests / validation summary

  • New adapter tests: 13 passed (degraded default; injected-client sanitization; no forbidden-field leakage even when raw objects carry account_id/asset_id; raising client degrades; malformed positions skip/degrade; unsafe ALPACA_ENV=live and disabled config resolve no client; route GET 200 + safety flags; route no forbidden keys; POST/PUT/PATCH/DELETE → 405; OpenAPI GET-only). No real creds, no network.
  • Full run (new + demo + order-preview + openapi-forbidden-paths + safety suites): 183 passed.
  • python scripts/validate_safety_config.py → OVERALL: PASS.
  • black --check, flake8, mypy (new files) → all 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. No place_order/submit_order/execute_order/cancel_order/replace_order/MarketOrderRequest/OrderSide in new code — matches exist only in the task doc's prohibition/denylist text. Legacy brokers/alpaca_adapter.py left untouched.

Out of scope

  • Paper order submission
  • Paper order cancellation
  • Order replacement
  • Live broker connection
  • Live credentials
  • Frontend trading controls (Buy/Sell/Order/Execute)

Safety confirmation

  • backend read-only only ✅
  • no frontend changes ✅
  • no scripts / workflows / package / env changes ✅
  • no broker credentials / no real secrets ✅
  • no order/execution routes ✅
  • no Buy/Sell/Order/Execute UI ✅
  • no live trading ✅
  • no profit/ROI/win-rate claims ✅
  • no 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

Add a GET/read-only Alpaca Paper adapter foundation (no order placement, no
execution, no live broker, no committed credentials):

- app/services/alpaca_paper_readonly_adapter.py — AlpacaPaperReadOnlyAdapter.
  Injected-client / env-gated; safe degraded_demo fallback when read-only is not
  explicitly enabled or credentials are absent; lazy paper-only SDK import that
  degrades on any error; sanitizes positions to display-safe fields only.
- app/schemas/alpaca_paper_readonly.py — AlpacaPaperPositionsPreview +
  AlpacaPaperReadOnlyPosition (extra="forbid"; safety flags always true,
  order_placement_enabled=false).
- app/api/routes/alpaca_paper.py — new GET /api/alpaca-paper/positions-preview
  (no mutating routes added).
- tests/app/test_alpaca_paper_readonly_adapter.py — 13 tests (degraded default,
  injected client sanitization, no forbidden-field leakage, raising/malformed
  degrade safely, unsafe/disabled config resolves no client, GET-only route).
- docs/tasks/alpaca_paper_readonly_adapter_001.md.

Read-only only. No POST/PUT/PATCH/DELETE, no place/submit/cancel/replace order,
no live endpoint, no secrets. Legacy brokers/alpaca_adapter.py left untouched.
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>

@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.

Sorry @Melly-999, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@Melly-999
Melly-999 temporarily deployed to feature/alpaca-paper-readonly-adapter-001 - alpha_data_scraper_ai PR #309 June 15, 2026 17:30 — with Render Destroyed
@Melly-999
Melly-999 marked this pull request as ready for review June 15, 2026 20:23
@Melly-999
Melly-999 merged commit bc6f245 into main Jun 15, 2026
8 checks passed
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