“The stock market is a device for transferring money from the impatient to the patient.”
Why a Raspberry Pi?
The premise was simple: build a system that generates consistent returns with minimal infrastructure cost. A Raspberry Pi 5 draws around 5W under normal load, runs Linux natively, costs under $100, and sits silently on a shelf. For a trading bot that needs to be always on but rarely compute-intensive, it's a near-perfect match.
The constraint-first design philosophy shaped every decision. Instead of cloud VMs and managed databases, the system runs on hardware I own, with services I control. The only external dependencies are the exchange API and AI inference endpoints.
Architecture overview
The bot is not a simple loop. It runs 9 concurrent threads, each with a specific responsibility and lifecycle:
| Thread | Frequency | Responsibility |
|---|---|---|
| Main Loop | Every 4h | Technical analysis, regime detection, signal generation |
| Position Monitor | 2–30s adaptive | Real-time SL/TP and trailing stop management |
| MTF Monitor (P15) | 30 min | 1H timeframe confirmation for deferred BUY signals |
| Buy Monitor (P18) | 30 min | Detects BUY signals on 1H, feeds P15 queue |
| Exit Monitor (P17) | 30 min | Early exit detection on open positions |
| Pulse Scanner (P16) | 4h | Detects sharp moves across the full universe |
| Telegram Listener | Long-poll | Real-time commands (/status, /analyze, /ask, etc.) |
| Claude Advisor | Weekly + trigger | AI-driven strategy review and symbol rotation |
| Daily Auditor | 22:00 daily | Health check, DB audit, daily summary |
All threads share mutable state (positions, capital, config) protected by a threading.RLock to prevent race conditions. Every access to shared resources takes a snapshot before processing.
The 4-hour cycle
The main cycle aligns with 4-hour candle closes (00:00, 04:00, 08:00 UTC−4). This is intentional: acting on a closed candle avoids the noise of in-progress bars and makes backtesting results reproducible.
For each symbol in the active portfolio, the cycle:
- Fetches recent 4H and 1D candles for higher-timeframe context
- Calculates indicators: RSI, SMA-20/50, ATR, Bollinger Bands, ADX
- Classifies the market regime: TRENDING, RANGING, or MIXED
- Selects the appropriate strategy based on regime
- Generates a BUY / SELL / HOLD signal with confidence score
- Validates through AI before execution
Auto market-regime strategy selection
The core insight is that no single strategy works in all market conditions. The bot dynamically selects among three strategies based on regime detection:
RSI Reversal (Ranging markets, ATR < 1.5%)
Buys oversold conditions (RSI < 35) with Bollinger Band lower-touch confirmation. Targets the mean reversion in sideways markets. Dynamic SL/TP at 1.5× and 2.5× ATR.
SMA Cross (Trending markets)
Enters on the 20/50 SMA crossover with ADX > 25 momentum confirmation. Rides trends with a wider SL (2× ATR) and aggressive TP (3× ATR). Avoids whipsaws in choppy conditions.
Combined / Voting (Mixed markets)
Both strategies must agree to generate a signal. Higher confidence threshold required. Acts as a conservative filter when regime is ambiguous.
Two-layer AI validation
Technical signals alone are not enough. The system adds two AI layers before any trade executes:
Layer 1: Per-signal validator (Gemini Flash)
Every BUY or SELL signal is sent to Gemini 2.0 Flash with full technical context plus the current Fear & Greed Index. The validator returns CONFIRM, REJECT, WAIT, or REDUCE_SIZE. This layer runs in real time and is optimized for speed and cost.
Layer 2: Weekly strategic advisor (Claude Opus)
Once a week (and after significant events), Claude Opus 4.6 receives a full performance report: win rate, P&L by symbol, recent signals, and backtest results across the 15-symbol universe. It recommends symbol rotations, strategy parameter adjustments, and risk threshold changes. The bot applies these automatically and notifies via Telegram.
The advisor rotated ARB/USDT out of the portfolio after 6 consecutive HOLD cycles and low backtest scores, replacing it with BTC/USDT (pinned) + NEAR/USDT based on higher trending momentum scores.
Trailing stop: 5 escalating phases
Once a position is open, the Position Monitor checks every 2–30 seconds (adaptive frequency based on risk phase) and manages a 5-phase trailing stop:
- Phase 0 — Fixed SL at entry (ATR-based)
- Phase 1 (Break-even) — If profit ≥ 0.5%, move SL to entry price. Never lose a winning position.
- Phase 2 (Immediate trail) — If profit ≥ 0.5×ATR, SL = peak − 0.5×ATR
- Phase 3 (Continuous trail) — If profit ≥ 2.0×ATR, maintains tight trail
- Phase 4 (Hard exit) — Maximum drawdown from peak threshold
As a safety net, stop-loss orders are also registered directly with eToro. If the bot goes offline, the exchange-side SL still protects the position.
State persistence and resilience
This is where most hobby trading bots fail silently. If the Pi loses power mid-write, a corrupted state file means the bot wakes up with no knowledge of open positions. The solution is atomic writes:
# Write to temp file first, then atomically rename
with open(f"{path}.tmp", "w") as f:
json.dump(state, f, indent=2)
os.replace(f"{path}.tmp", path) # atomic on POSIX
On startup, the bot checks for a .tmp file (indicating a crash mid-write) and falls back to the last known-good state. Capital, open positions, and counters are all recovered.
Trade history is persisted to both a local SQLite database and a MySQL instance on Hostinger simultaneously. If the remote DB is unreachable, the local write still succeeds and the MySQL mirror catches up on next connection.
Hardware performance
After months of continuous operation, the Pi 5 metrics are consistently:
- CPU: 0.4–3% (spikes to 15% during cycle processing)
- RAM: ~580MB used of 8GB available
- Temperature: 44–52°C (passive cooling, no fan)
- Uptime: 99.9%+ (only interrupted for OS updates)
The Pi 5 runs significantly hotter than the Pi 4 under the same load. At 52°C+ the thermal governor starts throttling. A small heatsink on the SoC brought idle temps from 58°C down to 44°C without any fan noise.
An honest note on fees
After months of paper trading with real market data, the win rate consistently sits above 50% — the bot does what it was designed to do. Signals are sound, risk management works, and the trailing stop protects gains.
The uncomfortable truth, however, is that broker fees are the silent killer of small-account algorithmic trading. Every eToro operation carries a spread cost plus overnight fees for positions held more than a day. On a $50,000 demo account operating with conservative position sizing, the gross P&L is positive — but net of fees, the margin is razor thin.
A trade that generates a 0.4% gross gain on a $1,000 position produces ~$4. eToro’s spread on crypto can consume $2–3 of that on entry and exit alone. The bot wins the trade; the broker wins the day. This is not unique to eToro — it’s the fundamental math of retail algorithmic trading at small scale.
The path to profitability is either larger capital (where fixed fees become a smaller percentage), less frequent but higher-conviction trades, or moving to a lower-fee venue. The current system is built for the first two: the AI validation layer specifically exists to reduce trade frequency and only act on high-confidence signals. The project remains in paper trading until the fee-adjusted win rate proves itself over a statistically significant sample.
Lessons learned
1. Thread safety is non-negotiable. The first version had no locks. Under high Telegram command load while the main cycle was running, positions would occasionally show stale data. Adding threading.RLock and snapshots eliminated the issue entirely.
2. Atomic state saves. A power outage revealed that a simple json.dump() open/write/close sequence is not safe. The file can be partially written. Switched to the write-to-tmp / rename pattern immediately.
3. Backtesting must use the same code path. Having a separate backtesting module led to subtle divergences between simulated and live behavior. The backtest now feeds data into the same StrategyEngine class the live bot uses.
4. Paper trade longer than you think. Six months in demo mode revealed three edge cases that only appear in specific market conditions (weekend gaps, API timeouts during volatility spikes, eToro rate limit bursts). Each one would have cost real money.
5. Build for the outage, not the happy path. Exchange API down? The bot retries with exponential backoff and logs the event. Telegram unreachable? Silently skips notification and resumes on next cycle. AI API credits exhausted? Falls back to the secondary provider automatically.