A solo retail trading system is roughly 1,000 lines of Python, four launchd plists, one DuckDB file, and one decision log. The Trading System Blueprinter generates the file tree, the Mermaid topology, and the launchd schedule for an Alpaca-data-and-broker stack with Claude Opus 4.7 for price-blind research, fractional-Kelly sizing, and a heartbeat-JSON observability layer. (Databento is one of the selectable data sources; the default blueprint below resolves data to Alpaca for the cleanest single-vendor integration.) The output is opinionated — the blueprint maps to a working system rather than an exhaustive options menu.

TL;DR

  • A working solo trading system is small: ~1,000 LOC Python, 4 launchd plists, one DuckDB, one decision log.
  • The blueprint covers seven decision points: data source, broker, research model, sizing rule, scheduler, persistence, alerting.
  • Generated topology (Mermaid): launchd → data fetch → DuckDB → LLM research → fractional-Kelly sizer → broker execute → heartbeat log.
  • File tree includes test scaffolding (price-blind check, sizing-cap check, idempotent-order check): these belong in v1, not v2.
  • Total infrastructure cost on a Mac Mini: ~€0/month. Data + broker fees as separate line items.

The seven decision points

The Blueprinter resolves seven choices in a defensible order:

# Decision Default for solo retail
1 Data source Alpaca (free IEX or paid SIP)
2 Broker Alpaca
3 Research model Claude Opus 4.7, price-blind
4 Sizing rule Fractional Kelly
5 Scheduler launchd
6 Persistence DuckDB local
7 Alerting Heartbeat JSON + Telegram

The Blueprinter's first selection — Alpaca for both data and broker — is the cleanest pattern at retail scale. Single API surface, bundled auth, no separate market-data pipeline. The trade-off is options coverage and SIP feed depth; the Data Vendor TCO tool ranks Alpaca first on the medium-universe minute-resolution scenario at €1,188/year all-in.

When to split data off Alpaca to a dedicated vendor

Decision 1 (data source) and decision 2 (broker) are independent. The bundled Alpaca default collapses them for simplicity, but the Blueprinter treats data as its own selectable axis precisely because Alpaca's bundled feed has two limits a serious system outgrows: tick-level depth and historical reach. A dedicated market-data vendor — Databento is the one this blueprint names as the canonical alternative — decouples the feed from the broker. You keep Alpaca (or swap to IBKR/Tradier) for execution and route bars/book events through the data vendor instead. Pick this split when you need order-book depth Alpaca's bundled feed does not expose, or a longer historical backfill than the broker's data plan covers. The cost is a second API surface, a second auth path, and the rate-limited ingestion loop in Rate-Limited, Resumable Market-Data Ingestion. The default below stays single-vendor Alpaca because most solo systems never hit those limits; the FAQ covers how to flip the data axis when they do.

The Mermaid topology

The Blueprinter returns a Mermaid diagram of the topology:

flowchart LR
  SCHED[launchd] -->|tick| DATA[alpaca]
  DATA -->|bars / book events| NORM[Normalize + persist]
  NORM --> STORE[duckdb]
  STORE --> RESEARCH[claude-opus-4-7 · price-blind research]
  RESEARCH -->|proposal + confidence| RISK[fractional-kelly]
  RISK -->|sized order| EXEC[alpaca]
  EXEC -->|fills + errors| LOG[heartbeat-json]
  LOG --> STORE
  LOG -.-> ALERT[Telegram / email]

Reading the topology: launchd ticks; data fetch normalises and persists; the LLM runs price-blind research against the persisted history; the sizer caps the bet; the broker executes; the heartbeat logs the outcome. The control flow is one-way — research never sees post-decision data.

Three properties to note:

  1. Persistence sits between data and research. The LLM call never reads from the live API directly. It reads from DuckDB. That makes the research step reproducible: the same prompt + same DB state produces the same output.
  2. Sizing is a separate step. The LLM proposes; the sizer disposes. The sizer's input is the proposal + confidence; its output is the sized order. The LLM never produces a dollar amount.
  3. Heartbeat closes the loop. The execution result writes back to persistence and triggers alerts. Without this, the system runs blind to its own failures.

The file tree

The Blueprinter returns a generated tree:

trading-system/
├── README.md                    # start here
├── .env.example                 # API keys (never commit real values)
├── scripts/
│   ├── fetch-bars.py            # data source → local duckdb
│   ├── research.py              # LLM call (price-blind context builder)
│   ├── decide.py                # risk sizing + decision logger
│   ├── execute.py               # broker call (idempotent)
│   └── heartbeat.py             # health probe + alert
├── data/
│   ├── heartbeat.json           # last-heartbeat timestamp (watchdog reads)
│   ├── circuit.json             # {"paused": false, "reason": ...}
│   └── tickers/                 # per-ticker markdown dossiers
├── memory/
│   └── decisions.jsonl          # append-only decision log
├── plists/
│   └── com.you.trader-*.plist   # launchd-based schedules
├── tests/
│   ├── test_research_no_price_leak.py
│   ├── test_sizing_caps.py
│   └── test_idempotent_orders.py
└── pyproject.toml

Five scripts, two data files, one memory file, launchd plists, three tests. The tree fits comfortably in a single repo and a single Mac Mini's resource budget.

The three tests

The Blueprinter ships three tests by default because they catch the three failure modes most likely to lose money in production:

  1. test_research_no_price_leak.py: the research script must not produce different outputs based on price level. Run the research with the same input but a perturbed price; assert the proposal is unchanged. Catches the most common price-blind-harness bug.
  2. test_sizing_caps.py: the sizer must respect the per-trade cap and per-day cap regardless of LLM confidence. Run with confidence=0.99, verify size ≤ cap. Catches the LLM-says-99% case that blows the account.
  3. test_idempotent_orders.py: the executor must not place duplicate orders on retry. Submit the same order twice with the same idempotency key; assert one fill. Catches the network-retry double-trade pattern.

These three tests catch roughly 80% of the production failures in retail algo systems1. They are not optional.

launchd as scheduler

The Blueprinter defaults to launchd because the target deployment is a Mac Mini2. Three plists cover the loop:

  • com.you.trader-fetch.plist: every 5 minutes during US session.
  • com.you.trader-research.plist: every hour during US session.
  • com.you.trader-heartbeat.plist: every minute always.

For Linux deployments, systemd timers substitute. For cloud deployments, cron + IAM. The pattern is the same; the scheduler implementation changes.

DuckDB as persistence

DuckDB is the right default for retail because it sits in a single file, requires no daemon, scales to billions of rows on a Mac Mini, and reads / writes faster than SQLite for time-series workloads3. The schema for the trading system:

  • bars: OHLCV by ticker, timestamp.
  • research: LLM proposals with timestamp, model, prompt hash, response.
  • decisions: sized orders with rationale.
  • fills: broker execution results.
  • heartbeat: timestamp + status.

Five tables, no foreign keys, append-only writes. The append-only constraint makes the system trivially auditable for BaFin compliance4.

What the Blueprinter omits

The default blueprint does not include:

  • Risk-overlay layer. A separate process that monitors aggregate exposure and trips a circuit-breaker. Add when running more than 5 simultaneous positions.
  • Backtest harness. Reuses the live pipeline against historical data, but with a separate scheduler and read-only DuckDB. Add when running multiple strategies.
  • Multi-account support. Single account is the default. Multi-account adds tenancy concerns; the Blueprinter does not address them.
  • TLS-protected internal communication. Local file-based IPC suffices on a Mac Mini; networked deployments add this layer.

These are explicit upgrades, not defaults. The base blueprint runs without them.

Failure modes

  • Skipping the heartbeat. Without heartbeat alerting, a silent failure (data source down, broker auth expired) goes unnoticed for hours. Heartbeat with a 5-minute alarm catches every silent failure mode worth catching.
  • Sharing keys across the loop. Each script should read keys from its own env file. A single shared .env makes audit harder and key rotation impossible without coordination.
  • DuckDB on networked storage. DuckDB performs well on local SSD and badly on networked filesystems. Keep the file local.
  • launchd plist syntax errors. Validate with plutil -lint before launchctl load. A malformed plist silently fails and the job never runs.

FAQ

Is Alpaca the only data + broker option?

No. The Blueprinter accepts alternative selections — Databento + IBKR, Polygon + Tradier, etc. The output topology and file tree update to reflect the choice. Alpaca is the default because it has the cleanest single-vendor integration at retail scale.

Why Claude Opus instead of GPT-5 for research?

The blueprint defaults to Opus for price-blind research because the price-blind harness benefits from Opus's stronger reasoning depth on multi-step decompositions. For lower-stakes screens, Sonnet 4.6 or GPT-5 substitute well; the Blueprinter accepts model selection as a parameter.

Can I run this on a Linux box instead of Mac Mini?

Yes. Replace launchd with systemd timers. The scripts, DuckDB, and broker integration are platform-agnostic. The cost profile is similar (€0/month infra on a self-hosted box).

Connects to

References

Footnotes

  1. Lopez de Prado, M. (2018). Advances in Financial Machine Learning. Wiley. Chapter on production deployment patterns.

  2. Apple Developer (2024). "launchd documentation and StartCalendarInterval reference." developer.apple.com

  3. Raasveldt, M., & Mühleisen, H. (2019). "DuckDB: an Embeddable Analytical Database." SIGMOD 2019. duckdb.org/pdf/SIGMOD2019-demo-duckdb.pdf

  4. BaFin (2024). "WpHG record-keeping requirements for algorithmic trading." bafin.de

Verified engine output

Show the recompute-verified inputs and outputs
Default solo-retail blueprint: Alpaca data+broker, Claude Opus 4.7, DuckDB, fractional Kelly, launchd
Inputs
Result
selections (7 items)[...]
mermaidflowchart LR SCHED["launchd"] -->|tick| DATA["alpaca"] DATA -->|bars / book events| NORM[Normalize + persist] NORM --> STORE["duckdb"] STORE --> RESEARCH["claude-opus-4-7 · price-blind research"] RESEARCH -->|proposal + confidence| RISK["fractional-kelly"] RISK -->|sized order| EXEC["alpaca"] EXEC -->|fills + errors| LOG["heartbeat-json"] LOG --> STORE LOG -.-> ALERT[Telegram / email]
file treetrading-system/ ├── README.md # start here ├── .env.example # API keys (never commit real values) ├── scripts/ │ ├── fetch-bars.py # data source → local duckdb │ ├── research.py # LLM call (price-blind context builder) │ ├── decide.py # risk sizing + decision logger │ ├── execute.py # broker call (idempotent) │ └── heartbeat.py # health probe + alert ├── data/ │ ├── heartbeat.json # last-heartbeat timestamp (watchdog reads) │ ├── circuit.json # {"paused": false, "reason": ...} │ └── tickers/ # per-ticker markdown dossiers ├── memory/ │ └── decisions.jsonl # append-only decision log ├── plists/ │ └── com.you.trader-*.plist # launchd-based schedules ├── tests/ │ ├── test_research_no_price_leak.py │ ├── test_sizing_caps.py │ └── test_idempotent_orders.py └── pyproject.toml

Computed live at build time.