A distributed, event-driven microservices architecture for automated cryptocurrency paper trading (BTC/USDT) with high-performance asynchronous execution, a real-time data bridge via WebSockets, a relational database, and a reactive control terminal.
- Global System Architecture
- Quantitative Analysis & Mathematical Modeling of Bots
- Bot 1 : Alpha — Scalper (Order Book Imbalance)
- Bot 2 : Beta — Momentum (Double EMA Crossover)
- Bot 3 : Gamma — Mean Reversion (AI Oracle & News Sentiment)
- Bot 4 : Delta — Breakout (Bollinger Bands & Volume Filter)
- Bot 5 : Epsilon — Swing Hybrid (EMA + RSI + Groq Llama3)
- Bot 6 : Zeta — Pure AI News Sentiment (Groq Llama3)
- Bot 7 : Eta — Daytrade Technical (EMA + RSI + ATR)
- Bot 8 : Theta — Mean Reversion (Bollinger Bands + RSI)
- Prop Firm Architecture & Financial Isolation
- Database Schema (PostgreSQL / Supabase)
- Step-by-Step Replication Guide
The platform is built on an event-driven decoupled architecture (EDA) using Redis as the shared message bus (Pub/Sub) to eliminate direct coupling between data fetching, algorithmic brains, and the execution engine.
graph TD
%% Data Source
BinanceAPI[Binance API & WebSockets] -- Depth & Klines stream --> DataFetcher[Binance Data Fetcher Service]
%% Redis Bus
DataFetcher -- Publish: depth5@100ms --> RedisBus{Redis Pub/Sub}
%% Algorithmic Bots
RedisBus -- Subscribe: depth5@100ms --> Bot1[Bot 1: Alpha - OBI Scalper]
BinanceAPI -- REST Klines --> Bot2[Bot 2: Beta - EMA Momentum]
CoinTelegraph[CoinTelegraph RSS] -- News headlines --> Bot3[Bot 3: Gamma - AI Oracle]
BinanceAPI -- REST Klines --> Bot4[Bot 4: Delta - BB Breakout]
%% Signal Publishing
Bot1 -- "Signal (Buy/Sell)" --> RedisBus
Bot2 -- "Signal (Buy/Sell)" --> RedisBus
Bot3 -- "Signal (Buy/Sell)" --> RedisBus
Bot4 -- "Signal (Buy/Sell)" --> RedisBus
%% Real-time Logs
Bot1 & Bot2 & Bot3 & Bot4 -- "Logs (bot:log:*)" --> RedisBus
%% Execution Engine
RedisBus -- "Subscribe: bot:signal:*" --> ExecEngine[Execution Engine Service]
ExecEngine -- Order Simulation & Fees --> PaperExchange[Paper Exchange Module]
%% Supabase Database
ExecEngine -- Fills & Traces --> Supabase[(Supabase PostgreSQL)]
%% Dashboard API & UI
RedisBus -- "Subscribe: bot:*" --> DashAPI[FastAPI WebSocket Bridge]
DashAPI -- "WebSockets (ws://)" --> ReactUI[React Dashboard UI]
binance-data-fetcher: Asynchronous Python service that connects to the Binance WebSocket stream to collect real-time 5-level order book snapshots (depth5@100ms). It publishes these snapshots directly to the Redis channelbinance:stream:btcusdt:depth5@100ms.crypto-redis: Redis instance configured as pure in-memory storage without disk persistence to minimize event routing latency (latency profile < 1ms).- Algorithmic Bots (
bot1tobot4): Isolated Python processes containing the quantitative models. They run in the background and publish their simulated orders (TradeSignal) to their respective Redis channelsbot:signal:<bot_id>. crypto-execution-engine: Central order processing service. It listens to signals via a generic subscription (bot:signal:*). For each signal:- It loads the associated portfolio from Supabase.
- It validates order compliance (available balance, signal type).
- It simulates trade fill on its virtual order book (
PaperExchange), applying a configurable brokerage fee rate (Taker fee). - It transactionally writes the trade history to
tradesand updates the balance inbot_configin Supabase.
crypto-dashboard-api: FastAPI server acting as a bidirectional gateway. It subscribes to Redis (bot:*) and streams signals and structured debug logs to all connected WebSocket clients.dashboard-ui: Real-time React front-end application (built with Vite, TypeScript, and TailwindCSS) displaying consolidated bot performance, capital allocation, P&L curves, complete transaction history, and log streams.
- Strategy Type: High-frequency statistical arbitrage / Unilateral Market Making
- Instrument: Binance Spot BTCUSDT order book (5 levels of depth)
- Redis Source Channel:
binance:stream:btcusdt:depth5@100ms - Redis Signal Channel:
bot:signal:bot1_scalper
The bot exploits the microstructural phenomenon of Order Book Imbalance (OBI), postulating that massive volume imbalances between immediate supply and demand instantly precede micro price jumps (short-term price impact).
Let
-
$p_{b,i}(t)$ and$q_{b,i}(t)$ : the price and quantity of the$i$ -th best bid level, ordered such that$p_{b,1} > p_{b,2} > \dots > p_{b,L}$ . -
$p_{a,i}(t)$ and$q_{a,i}(t)$ : the price and quantity of the$i$ -th best ask level, ordered such that$p_{a,1} < p_{a,2} < \dots < p_{a,L}$ .
The cumulative bid volume
The Order Book Imbalance ratio
-
Entry Rule (Long Buy): The system initiates a virtual long position of size
$Q$ if the imbalance confirms extreme buying pressure:$$\text{Entry Signal} = \text{BUY} \iff OBI(t) > \theta \quad \text{with} \quad \theta = 0.75 \quad \text{and} \quad \text{Position} = \text{FLAT}$$ -
Exit Rule (Closing Sell): From entry at an average price
$P_{\text{entry}}$ , the bot monitors at each update (100ms) the latent return based on the best bid price$p_{b,1}(t)$ to ensure immediate market execution (Taker):$$R(t) = \frac{p_{b,1}(t) - P_{\text{entry}}}{P_{\text{entry}}}$$ The trade is closed under two conditions: $$\text{Exit Signal} = \text{SELL} \iff \begin{cases} R(t) \ge \text{TP} & \text{(Take Profit: } \text{TP} = 0.0020 \text{, i.e. } +0.20% \text{)} \ R(t) \le -\text{SL} & \text{(Stop Loss: } \text{SL} = 0.0015 \text{, i.e. } -0.15% \text{)} \end{cases}$$
- Strategy Type: Macro trend-following
- Instrument: Binance Spot BTCUSDT daily candles (1D)
- Frequency: Analysis every 24 hours (with 250-day warmup)
- Redis Signal Channel:
bot:signal:beta_momentum
The Exponential Moving Average (EMA) is an infinite impulse response (IIR) filter applying exponentially decreasing weighting factors.
For a daily closing price
We use two time horizons:
- A fast average:
$k_{\text{fast}} = 50 \implies \alpha_{50} \approx 0.0392$ - A slow average:
$k_{\text{slow}} = 200 \implies \alpha_{200} \approx 0.00995$
The trend differential (or "Spread") at time
To eliminate false signals induced by noisy oscillations around the trend line, crossover detection is performed on the last two confirmed and closed candles (
-
Entry Signal (Golden Cross): Buy when the short-term trend crosses back above the long-term trend:
$$\text{Signal} = \text{BUY} \iff S(t-2) \le 0 \quad \text{and} \quad S(t-1) > 0 \quad \text{and} \quad \text{Position} = \text{FLAT}$$ -
Exit Signal (Death Cross): Sell when the fast trend crosses downward through the slow trend:
$$\text{Signal} = \text{SELL} \iff S(t-2) \ge 0 \quad \text{and} \quad S(t-1) < 0 \quad \text{and} \quad \text{Position} = \text{HOLDING}$$
- Strategy Type: Contrarian swing trading based on behavioral finance
- Instrument: RSS news feed (CoinTelegraph) + Gemini API
- Frequency: Analysis every hour
- Redis Signal Channel:
bot:signal:gamma_oracle
This strategy models investors' emotional behavior in response to market news. According to the Adaptive Markets Hypothesis (AMH), general sentiment tends to overreact in the short term. Excessive euphoria indicates an overbought market (imminent peak), while collective panic signals a capitulation (attractive low point for a buy).
Let
-
$S_t \in [0, 20]$ : Capitulation, extreme fear. -
$S_t \in [80, 100]$ : FOMO, extreme greed.
The model adopts a mean reversion stance:
-
Systematic Buy:
$$\text{Signal} = \text{BUY} \iff S_t < 20 \quad \text{and} \quad \text{Position} = \text{FLAT}$$ -
Protective Sell:
$$\text{Signal} = \text{SELL} \iff S_t > 80 \quad \text{and} \quad \text{Position} = \text{HOLDING}$$
- Strategy Type: Volatility channel breakout with flow confirmation
- Instrument: Binance Spot BTCUSDT 15-minute candles (15m)
- Redis Signal Channel:
bot:signal:delta_breakout
Bollinger Bands describe a statistical fluctuation range based on the hypothesis of local return distribution.
For a computation window of
The upper (
The volatility compression indicator (Bandwidth) measures the relative spread of the bands:
Since false breakout signals (fake-outs) frequently occur during low liquidity periods, we apply a filter on the simple moving average of trading volume over the same period
-
Long Entry Rule (Confirmed bullish breakout): The signal is validated only if the break above the upper band is accompanied by a volume spike greater than a factor
$\lambda = 1.5$ of the average: $$\text{Signal} = \text{BUY} \iff \begin{cases} P_{t-1} > U_{t-1} \ V_{t-1} > 1.5 \cdot V_{\text{SMA}, 20}(t-1) \ \text{Position} = \text{FLAT} \end{cases}$$ -
Technical Exit Rule (Exit stop): As soon as the price closes below the lower band, the position is liquidated. For strict risk management reasons, no volume filter is required at exit:
$$\text{Signal} = \text{SELL} \iff P_{t-1} < L_{t-1} \quad \text{and} \quad \text{Position} = \text{HOLDING}$$
- Strategy Type: AI-gated swing trading with hard ATR stop-loss
- Instrument: Binance Spot BTCUSDT hourly candles (1h)
- Frequency: Analysis every hour (250-candle warmup for EMA 200)
- Redis Signal Channel:
bot:signal:bot5_swing - External API: Groq Cloud (
llama-3.3-70b-versatile)
Before any AI call is made, the bot applies a two-condition quant gate on the latest closed 1h candle:
-
EMA 200 trend filter:
$P_t > EMA_{200}(t)$ — price must be above the long-term trend to only take long positions in a bull structure. -
RSI 14 overbought filter:
$RSI_{14}(t) < 70$ — prevents chasing overbought momentum.
When the quant gate passes, the last 10 close prices, EMA 200, RSI 14, and ATR 14 are sent to Groq Llama3 with a structured JSON prompt. The model returns {"decision": "BUY" | "PASS", "reasoning": "..."}.
On entry, the ATR-based hard stop-loss is set at:
-
Hard Stop-Loss: immediate SELL if
$P_t \le SL$ -
AI Take-Profit (when unrealised profit
$\ge 5%$ and$RSI_{14} > 70$ ): Groq is queried for a SELL/HOLD decision:$$\text{Signal} = \text{SELL} \iff \frac{P_t - P_{\text{entry}}}{P_{\text{entry}}} \ge 0.05 \quad \text{and} \quad RSI_{14} > 70 \quad \text{and} \quad \Phi_{\text{Groq}} = \text{SELL}$$
- Strategy Type: 100% AI-driven entry, ATR hard stop-loss, AI take-profit
- Instrument: Binance Spot BTCUSDT hourly candles (1h)
- Frequency: Analysis every hour
- Redis Signal Channel:
bot:signal:bot6_news - External API: Groq Cloud (
llama-3.3-70b-versatile)
Unlike Bot 5, there is no quant pre-filter. On every cycle, the current market state (price, EMA 200, RSI 14, ATR 14, last 10 closes) is sent directly to Groq Llama3 acting as a sentiment-aware trader. Entry is 100% determined by the model:
The stop-loss uses a wider ATR multiplier than Bot 5 to accommodate a more volatile AI-driven entry:
-
Hard Stop-Loss: immediate SELL if
$P_t \le SL$ -
AI Take-Profit (triggered at
$\ge 5%$ unrealised profit, without RSI constraint): Groq is queried and returns SELL/HOLD:$$\text{Signal} = \text{SELL} \iff \frac{P_t - P_{\text{entry}}}{P_{\text{entry}}} \ge 0.05 \quad \text{and} \quad \Phi_{\text{Groq}} = \text{SELL}$$
Bot 3 uses Gemini to score news headlines into a sentiment index
- Strategy Type: Purely mathematical short-term daytrade
- Instrument: Binance Spot BTCUSDT 15-minute candles (15m)
- Frequency: Analysis every 15 minutes (250-candle warmup)
- Redis Signal Channel:
bot:signal:bot7_daytrade
The bot looks for a short-term RSI dip into oversold territory while price remains above the EMA 200 (confirming the macro uptrend). This is a classic pullback-in-uptrend pattern:
Both take-profit and stop-loss targets are set at entry using ATR 14 to scale them to current volatility:
This gives a built-in Risk/Reward ratio of 1 : 1.33 per trade.
-
Hard Stop-Loss:
$P_t \le SL$ -
Hard Take-Profit:
$P_t \ge TP$
No external API required — entirely self-contained.
- Strategy Type: Statistical mean reversion on short time frame
- Instrument: Binance Spot BTCUSDT 15-minute candles (15m)
- Frequency: Analysis every 15 minutes
- Redis Signal Channel:
bot:signal:bot8_meanrev
The strategy bets that when price extends significantly below its rolling mean (lower Bollinger Band) and momentum is weak (RSI oversold), it will revert to the mean (middle band / upper band). This is the inverse of Bot 4's breakout approach.
For a window of
The double-condition (below lower band and RSI < 35) filters out false signals during high-volatility breakdowns where price hugs the lower band without bouncing.
Stop-loss is placed below the lower band by one ATR unit to survive normal noise:
-
Hard Stop-Loss:
$P_t \le SL$ -
Mean Reversion Take-Profit (price reaches upper band or RSI becomes overbought):
$$\text{Signal} = \text{SELL} \iff \left( P_t \ge U_t \quad \text{or} \quad RSI_{14}(t) > 70 \right) \quad \text{and} \quad \text{Position} = \text{HOLDING}$$
| Bot 4 — Delta (Breakout) | Bot 8 — Theta (Mean Reversion) | |
|---|---|---|
| Entry trigger | Price breaks above upper band + volume spike | Price touches lower band + RSI < 35 |
| Underlying thesis | Momentum continuation | Reversion to the mean |
| AI gate | None | None |
| Time frame | 15m | 15m |
In order to mimic the account evaluation rules of Prop Firm type companies (proprietary trading firms), the platform implements a strict, isolated capital allocation model.
[ STARTING GLOBAL CAPITAL: $100,000 USDT ]
│
┌────────────┬───────┴──────────┬────────────┐
▼ ▼ ▼ ▼
[ Bot 1 ] [ Bot 2 ] [ Bot 3 ] [ Bot 4 ]
$25,000 USDT $25,000 USDT $25,000 USDT $25,000 USDT
Max Drawdown Max Drawdown Max Drawdown Max Drawdown
5% ($1,250) 8% ($2,000) 6% ($1,500) 20% ($5,000)
- Strict Compartmentalization: The global equity of $100,000.00 is segmented into four independent sub-accounts each endowed with $25,000.00 USDT. No bot can borrow or use another bot's margin (isolated exposure limit).
- Real Maximum Drawdown Calculation (DD): The execution engine permanently verifies that a bot's cumulative loss does not breach its limit defined in the database.
-
Dynamic Update: With each persisted transaction, the engine recalculates the available balance and updates the bot's state. If a bot breaches its maximum allowed loss, its status immediately switches to
error(orstopped) and no further entry signals are processed by the engine.
The schema relies on three relational tables optimized for complex time-series queries and high availability.
Records the operational and financial configuration of each entity.
CREATE TABLE public.bot_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL UNIQUE, -- Must match exactly the Python code
description TEXT,
strategy_type strategy_type_enum NOT NULL,
status bot_status_enum NOT NULL DEFAULT 'initializing',
allocated_capital NUMERIC(18, 8) NOT NULL CHECK (allocated_capital > 0),
max_drawdown NUMERIC(5, 4) NOT NULL CHECK (max_drawdown > 0 AND max_drawdown <= 1),
exchange_account_id TEXT,
is_paper_trading BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);Records all executed orders over time.
CREATE TABLE public.trades (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bot_id UUID NOT NULL REFERENCES public.bot_config(id) ON DELETE RESTRICT,
symbol TEXT NOT NULL, -- e.g.: 'BTCUSDT'
side trade_side_enum NOT NULL, -- 'buy' or 'sell'
execution_price NUMERIC(18, 8) NOT NULL CHECK (execution_price > 0),
quantity NUMERIC(18, 8) NOT NULL CHECK (quantity > 0),
quote_quantity NUMERIC(18, 8) GENERATED ALWAYS AS (execution_price * quantity) STORED,
fee NUMERIC(18, 8) NOT NULL DEFAULT 0 CHECK (fee >= 0),
fee_asset TEXT NOT NULL DEFAULT 'USDT',
net_pnl NUMERIC(18, 8) NOT NULL DEFAULT 0, -- Net P&L in USDT after fees
exchange_order_id TEXT,
is_paper_trade BOOLEAN NOT NULL DEFAULT TRUE,
executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);To guarantee response times under 10ms on the React Dashboard during P&L aggregations, the following composite indexes are configured:
-- Primary temporal index for P&L calculations by bot
CREATE INDEX idx_trades_bot_time ON public.trades (bot_id, executed_at DESC);
-- Partial index to quickly filter profitable trades (Win-Rate calculation)
CREATE INDEX idx_trades_profitable ON public.trades (bot_id, executed_at DESC) WHERE net_pnl > 0;Follow these rigorous instructions to compile, configure, and run the complete infrastructure.
- Docker Engine v24.0+ & Docker Compose v2.20+
- A Supabase account (with PostgreSQL database)
- A Google Gemini AI Studio API key (for Bot 3)
Create the main configuration file .env at the project root:
# Global infrastructure variables
REDIS_URL=redis://redis:6379/0
CORS_ALLOW_ORIGINS=http://localhost:3000,http://localhost:5173
LOG_LEVEL=INFO
LOG_FORMAT=jsonThen configure the execution service .env file (services/execution_engine/.env):
REDIS_URL=redis://redis:6379/0
SUPABASE_URL=https://<your-supabase-id>.supabase.co
SUPABASE_SERVICE_KEY=<your-admin-service-role-key>
EXECUTION_FEE_RATE=0.0010 # Brokerage fee at 0.10% (Binance Spot Taker)
DEFAULT_STARTING_BALANCE_USDT=25000.00Configure the data collector file (services/data_fetcher/.env):
REDIS_URL=redis://redis:6379/0
BINANCE_WS_BASE_URL=wss://stream.binance.com:9443Configure the API keys for individual bots. The AI Oracle file (services/bots/.env.bot3) must include your Gemini key:
REDIS_URL=redis://redis:6379/0
GEMINI_API_KEY=AIzaSyD-YourGoogleGeminiKeyHere
BOT3_GEMINI_MODEL=gemini-2.5-flash
BOT3_TRADE_QUANTITY_BTC=0.001
BOT3_BUY_THRESHOLD=20
BOT3_SELL_THRESHOLD=80- Log in to your Supabase database console.
- Open the SQL Editor.
- Import and execute the supabase_schema.sql file.
- This command will create the enums, tables, temporal indexes, security policies (RLS), and insert the 4 starter bots with capital initialized to
active/initializing.
To recompile the entire ecosystem with new dependencies, run the following command at the project root:
docker compose up -d --buildThis command launches 8 orchestrated containers in the background:
- The Redis bus (
crypto-redis) - The Binance real-time fetcher (
binance-data-fetcher) - The transactional execution engine (
crypto-execution-engine) - The 4 algorithmic bots (
bot1,bot2,bot3,bot4) - The FastAPI gateway API (
crypto-dashboard-api)
To validate the proper functioning of the orchestration, you can audit the containers:
-
Check overall health status:
docker compose ps
All containers should display the
Upstatus. -
Read consolidated signal streams:
docker compose logs -f execution-engine
The service should confirm loading Supabase states and indicate it is listening on the Redis pattern
bot:signal:*. -
Validate WebSocket broadcasting: Open a terminal and query the dashboard health API:
curl http://localhost:8000/health
Should return
{"status": "ok", "service": "dashboard-api", "connections": 0, ...}. -
Access the user interface: In the project tree, launch the React development server if you want to view the local interface:
cd services/dashboard_ui npm install npm run devNavigate to http://localhost:5173 to monitor all quant activity.

