Three days. That's how long it took to go from an empty repository to a working crypto trading bot executing paper trades on Alpaca. Here's exactly how we did it — including the parts that broke.
The Architecture
The stack has five layers, each built to be independently testable:
- Data Layer — Real-time price feeds via Alpaca's WebSocket API. We pull OHLCV candles at 1-minute and 5-minute intervals for a watchlist of 8 crypto pairs (BTC/USD, ETH/USD, SOL/USD, DOGE/USD, AVAX/USD, LINK/USD, DOT/USD, MATIC/USD).
- Signal Layer — Six intraday strategies running in parallel: momentum scalp, VWAP reversion, mean reversion, EMA crossover, Bollinger bounce, and volume spike. Each strategy outputs a signal with direction, confidence score, and recommended position size.
- Risk Layer — A risk manager that enforces daily loss limits, position concentration limits, and per-trade stop losses. No signal reaches execution without passing three risk checks.
- Execution Layer — Alpaca's trading API for order placement. We use limit orders with a 0.5% slippage tolerance and bracket orders (stop loss + take profit attached at submission).
- Monitoring Layer — Every trade, signal, and risk check is logged. A Telegram bot sends real-time notifications for entries, exits, and daily P&L summaries.
What Broke First
The first bug was subtle and expensive. We were sizing positions using buying_power from the Alpaca account endpoint. For crypto, this is wrong. Crypto settles in actual USD cash, not margin. The correct field is cash. Using buying_power produced a flood of 403 errors on about 50% of orders — the API was rejecting orders that exceeded real available cash.
Fix: six lines of code. But it took two hours to diagnose because the error message from Alpaca just says "insufficient buying power" without specifying which balance it's checking.
The Hold Time vs. Take Profit Trap
Second bug: our momentum scalp strategy had max_hold_minutes: 15 and take_profit_pct: 4%. A 4% move on BTC in 15 minutes is rare outside of black swan events. The result: every position hit the time gate before the take profit, exiting at a small loss after slippage and spread.
The fix is conceptual, not just numerical: your maximum hold time and take profit percentage must be physically achievable together. We switched to max_hold_minutes: 90 and take_profit_pct: 2% for the momentum strategy, and now positions have room to breathe.
The Strategy Competition
We ran all six strategies simultaneously on paper trading for 48 hours. Results:
- VWAP Reversion — Best win rate (62%), lowest average profit per trade
- Momentum Scalp — Highest average profit per trade, but only 44% win rate
- Mean Reversion — Most consistent. 55% win rate, steady equity curve
- EMA Crossover — Too many false signals in ranging markets. Needs a regime filter.
- Bollinger Bounce — Worked well on SOL and DOGE (high volatility), poorly on BTC
- Volume Spike — Best for catching fast moves, but position sizing needs work
What We'd Do Differently
If we were starting over: build the risk manager first. Every other component can be simple or even stubbed out, but the risk layer is what prevents a bad day from becoming a catastrophic one. We built it last and had two hours of unprotected trading before it was online.
The full stack is open-architecture — we'll be releasing the strategy framework as part of Module 5 of the MindSparkStack course. Every line of code, every trade log, every mistake — documented.