StratCraft is a trading strategy backtesting, optimization and paper/live execution app. It is designed to trade the entire market of more than 10,000 instruments on Alpaca. The TypeScript/Node app provides the API, web UI and schedules jobs. The Rust engine runs the heavy optimization and backtesting workloads. Strategy optimization can run in parallel on multiple ad-hoc headless Hetzner machines.
Disclaimer
StratCraft is not financial advice. Most retail traders lose money. Use at your own risk DISCLAIMER.md.
Core ideas
These are working assumptions and observations that motivated the project (not guarantees):
- Over long horizons, holding mostly cash and nominal bonds can lose purchasing power after inflation
- Discretionary stock/crypto/metal picking and frequent trading tend to underperform for many retail investors
- Broad equities have historically outperformed high-quality bonds over long horizons (with higher volatility)
- Concentration risk (single country/sector/asset) is a major failure mode; diversification is preferred
- Public equities offer liquid proxies/tilts to many themes (commodities, real estate, crypto-related)
- Stock returns are highly skewed: many names underperform, few drive most of the gains
- For most retail participants, intraday edges are hard to sustain after spreads, slippage and fees
- Reduce human bias with backtests, out-of-sample validation, and automation
Preliminary conclusions after optimizing dozens of strategies
These are empirical observations from my dataset, assumptions, and execution model; treat them as hypotheses.
- Complex strategies overfit easily: great performance in-sample, poor performance out-of-sample
- In backtests, beating QQQ appeared possible by diversifying over many independent outperformers (implementation-dependent)
- Trend-following that holds rising assets with wide stops over sufficient holding periods can outperform
- More stable strategies (high Sharpe) are typically easier to stick with than maximum-return ones (high CAGR)
- Equity markets tend to drift upward over time, and robust short strategies are hard to come by
- "Sell everything" logic during drawdowns did not outperform in my tests
- Filtering tickers by minimum volume did not outperform in my tests
- Selecting tickers where a strategy performed well in the past did not outperform in my tests
- Raising stops did not outperform in my tests
User interface screenshots
Repo layout
engine/Rust CLI engine for optimization, verification, signal generation, backtests, trade planning (seeengine/README.md).src/server/Express server, API routes, jobs, services, and database access.src/views/Handlebars templates for the web UI.src/public/Static assets served by the web app.src/shared/Shared config and types between server modules.data/Market data snapshots used by the Rust engine.scripts/Build and helper scripts.dist/Compiled server output (generated).target/Rust build output (generated).
Overview
Dataset Split, Optimization, and Scoring
Community, discussions, Q&A
Setup
Prereqs: Node.js + npm, Rust toolchain (for engine), and a Postgres instance.
From-zero (localhost):
- Copy
.env.exampleto.envand set at leastDATABASE_URLandDATABASE_KEY. - Ensure Postgres is running and the role in
DATABASE_URLexists. StratCraft will attempt to create the database named inDATABASE_URLon startup (requires DB create privileges); otherwise create the database manually. - Start the server:
- Open
http://localhost:3000/and request an access code for your email.- The first user becomes an admin.
- If email delivery isn't configured yet, StratCraft will show the OTP in the UI for bootstrap login.
- As an admin, configure a candle data provider in
Admin -> Settings(Tiingo/EODHD/Alpaca). StratCraft needs candle data to backtest/optimize and generate signals. To support the project, consider using Tiingo as your candle source. Tiingo is an excellent daily candle provider and our partner. - Create a dedicated Alpaca paper account for loading the universe and enter provided keys in
Admin -> Settings -> Alpaca - For email delivery, get a Resend API key, save it in
Admin -> Settings -> Email, and add the 3 TXT DNS records for your sending domain exactly as instructed by Resend.
Server initializes the Postgres schema from src/server/database/pg.sql and builds the Rust engine from source on each start.
Intended usage
Once you sign in as an admin and fill in all the missing Settings, restart the app and let it cook for half a day. It will:
- Load the trading universe from Alpaca
- Load daily candle history for 11 years from the candle provider
- Create default strategies from existing templates
- Backtest all strategies
- Start optimizing strategies. Once a strategy finishes optimizing, it will be deleted to be re-created on next app start with new parameters
- Once all strategies are optimized, it will verify out-of-sample
- Rank templates based on backtest performance, parameter stability and out-of-sample verification performance
To execute a strategy on a paper/live Alpaca account, add an account using the link on the dashboard and attach a strategy to it.
Every night the app will sync new candles a few hours after the market close, calculate strategy signals, update backtests, and enter/adjust/exit account trades. Account owner is notified by email once the trades are sent to the broker. There's plenty of time to cancel them if needed since they execute on market open.
Running on Hetzner with world-wide access
StratCraft is primarily intended for personal/home use: run it on an always-on VPS (e.g. Hetzner) so your home PC does not need to stay on, heavy computations run in the cloud, and the web UI is reachable from mobile.
- Deploy to Hetzner and confirm HTTPS works (scripts/DEPLOYMENT.md).
- Enable mTLS and install provided client certificate on your desktop and mobile.
- Configure email delivery, set a recognizable Site Name and Email Security Emoji in
Admin -> Settings(phishing mitigation). - Connect an Alpaca paper account first, run it for a while, and only then add/link a live account.
Security
StratCraft is a hobby/personal project, not a hardened multi-tenant service. Bugs and vulnerabilities are possible. Because this is an open-source codebase, an internet-exposed deployment can be easier to target; assume your instance will be scanned and probed.
- Use mTLS (client certificate), it can be enabled via
Admin -> Users -> Server Access Lockdown (Client Certificate). - The Hetzner deploy script sets up HTTPS (Let's Encrypt), firewalling, fail2ban, basic security headers, rate limiting, and a manual update trigger (Admin -> Deployment); see
scripts/DEPLOYMENT.md. - Do not expose Postgres to the public internet; keep it bound to localhost or a private network.
- If you ever plan to restore the database, keep
DATABASE_KEYbacked up; it encrypts broker credentials and other secrets stored in Postgres. - Treat email as an attack surface: verify the Email Security Emoji and Site Name on every StratCraft email subject before clicking links.
- Keep invite/session lifetimes reasonable (
Invite Link Valid Days,Session Cookie Valid Days) and review the user list periodically. - Avoid adding live Alpaca keys until they are actually needed.
- Change Alpaca live account keys if you notice unusual activity.
Rust engine
NodeJS app compiles and calls engine automatically. See engine/README.md for more info.
Contributing
We currently do not accept pull requests. Please open a GitHub issue or discussion instead. For security vulnerabilities, see SECURITY.md.
LightGBM (optional)
StratCraft supports creating LightGBM-based strategies trained via Web UI, without modifying the code. You only need the LightGBM CLI if you want to train models (engine train-lightgbm or the train-lightgbm job in the web UI). Running/inferencing an already-trained model does not require the CLI.
Windows (localhost): the repo includes a prebuilt LightGBM CLI at engine/vendor/lightgbm.exe (with engine/vendor/lib_lightgbm.dll). engine train-lightgbm uses it automatically, so you typically do not need to install anything or modify PATH. If Windows blocks the executable or you see a missing-DLL error, ensure both files exist in engine/vendor/ and are allowed to run.
Adding strategies and customizing
Strategies have two pieces: a TypeScript-side template (UI/parameters) and a Rust-side implementation (signals/execution).
To add a new built-in strategy template:
- Add a new JSON template under
src/server/strategies/(copy an existing one; pick a newid). - Implement the matching Rust strategy in
engine/src/strategies/and register it inengine/src/strategy.rsusing the same template id. - Run tests (and start in paper trading) before deploying anywhere near a live account.
If you want to modify StratCraft with Codex, work in your own private fork/repo and deploy from that fork.
Bundled third-party software
This repository redistributes prebuilt LightGBM binaries for Windows under engine/vendor/. LightGBM is licensed under the MIT License; see THIRD_PARTY_NOTICES.md and LICENSE-LightGBM-MIT.txt.
License
StratCraft is available for free personal use under the terms in LICENSE. Commercial use requires a separate commercial license; see STRATCRAFT-COMMERCIAL.txt. Third-party notices for vendored assets are documented in THIRD_PARTY_NOTICES.md.
Architecture Diagram
flowchart LR
subgraph UI["Web UI"]
Views[Handlebars templates]
Assets[Static assets]
end
subgraph Server["Node TS Server"]
API[Express API]
Services[Email + account services]
DBTS["Postgres access: src/server/database"]
subgraph Jobs["Job Scheduler (src/server/jobs/JobScheduler.ts)"]
subgraph JobOps["Operations"]
J1["1. engine-compile"]
J2["2. candle-sync"]
J6["6. plan-operations"]
J7["7. dispatch-operations"]
J9["export-market-data (manual/settings)"]
end
subgraph JobStrategies["Strategies"]
J3["3. generate-signals"]
J8["optimize (idle maintenance)"]
J10["train-lightgbm (manual)"]
end
subgraph JobTrades["Trades"]
J4["4. reconcile-trades"]
end
subgraph JobBacktests["Backtests"]
J5["5. backtest-active (+ backtest-accounts)"]
end
subgraph JobAccounts["Accounts"]
A1["Manual account reconcile -> reconcile-trades (skipPlanOperations)"]
end
end
end
subgraph Engine["Rust Engine"]
CLI[CLI commands]
Signals[Signal generation]
Backtests[Backtests + optimization]
DBRS["Postgres access: engine/src/database.rs"]
end
DB[(Postgres)]
Snapshots[(Market data snapshots)]
Broker[(Alpaca)]
DataAPI[(Market data providers)]
UI --> API
API --> DBTS
API -. queue jobs .-> Jobs
Jobs --> DBTS
Services --> Broker
J1 --> J2 --> J3 --> J4 --> J5 --> J6 --> J7
J2 -. daily requeue .-> J2
Jobs -. idle scheduler .-> J8
API -. settings update .-> J9
API -. train request .-> J10
A1 -. account page trigger .-> J4
DataAPI --> J2
J7 --> Services
J9 --> CLI
J10 --> CLI
J8 --> CLI
J3 --> CLI
J4 --> CLI
J5 --> CLI
J6 --> CLI
DBTS <--> DB
DBRS <--> DB
CLI --> Signals
CLI --> Backtests
Snapshots --> CLI
Integration Test Coverage
| Component | Integration Tests | Notes |
|---|---|---|
| Engine Postgres-backed pipeline (signal generation + backtest + persistence) | engine/tests/pipeline.rs::pipeline_smoke_small_dataset, engine/tests/pipeline.rs::full_pipeline_snapshot |
Exercises AppContext, MarketData loading, generate_signals, backtest_active, backtester, engine, performance, and engine DB persistence for signals/backtest results/trades. |
| Engine account backtests (account-linked strategies) | engine/tests/pipeline.rs::backtest_accounts_smoke |
Exercises backtest_accounts command using account-linked strategies, signals, and persisted backtest results. |
| Engine market data snapshot export | engine/tests/pipeline.rs::export_market_data_smoke |
Exercises export_market_data command and validates snapshot contents. |
| Engine local optimization (snapshot + DB updates) | engine/tests/pipeline.rs::optimize_smoke |
Exercises optimize command with a minimal local-search configuration and validates template version + default strategy cleanup. |
| Engine operation planning (account operations) | engine/tests/pipeline.rs::plan_operations_smoke |
Exercises plan_operations command with a stubbed Alpaca account state. |
| Engine trade reconciliation | engine/tests/pipeline.rs::reconcile_trades_smoke |
Exercises reconcile_trades command with stubbed Alpaca order responses. |
| Engine backtest cache verification | engine/tests/pipeline.rs::verify_smoke |
Exercises verify command and persists verification metrics. |
| Engine Postgres reads for backtest results | engine/tests/pipeline.rs::pipeline_smoke_small_dataset, engine/tests/pipeline.rs::full_pipeline_snapshot |
Validates load_latest_backtest_result and stored snapshots/trades. |
Engine snapshot file output (test snapshots under engine/tests/snapshots*) |
engine/tests/pipeline.rs::full_pipeline_snapshot |
Writes snapshot bundles for regression tracking. |
Not Covered By Integration Tests Yet
| Component | Gaps |
|---|---|
Server API endpoints + middleware (src/server/) |
No integration tests; only unit tests present. |
Server Postgres layer (src/server/database/Database.ts) |
No integration tests of SQL reads/writes or migrations. |
| Job handlers (candle sync, backtest orchestration, plan/dispatch operations, reconciliation) | No integration tests for job flows or scheduling. |
| External integrations (Alpaca, EODHD, Resend email) | No integration tests; would require fakes or sandbox accounts. |
Engine training command (train-lightgbm) |
Not exercised by integration tests. |
Live-trade backtest refresh (backtest_active::update_live_backtests) |
Not exercised by integration tests. |
UI rendering and static assets (src/views, src/public) |
Not covered by integration tests. |
Note: This list focuses on integration tests only; unit tests exist but are not enumerated here. Snapshot bundles under engine/tests/snapshots* are generated from synthetic test data and do not contain third-party market data.
