Bot Engine Deep Dive
Overview
BlockbotX supports eight bot types, each with a dedicated execution engine, API routes, and UI dashboard:
| Bot Type | Engine File | Description |
|---|---|---|
| AI Bot | bot-executor.ts | Technical analysis with configurable strategies |
| DCA Bot | dca-bot.ts | Dollar-cost averaging at fixed intervals |
| Signal Bot | signal-bot.ts | External signal following (webhook, Telegram) |
| Arbitrage Bot | arbitrage-bot.ts | Cross-exchange price differential detection |
| Pump Bot | pump-screener.ts | Volume spike and price surge detection |
| Scalping Bot | scalping-manager.ts | High-frequency micro-profit trades |
| Grid Bot | bot-executor.ts | Grid trading on price oscillations |
| Combo Bot | bot-executor.ts | Multi-strategy orchestration |
All bots execute through a central scheduler (scheduler.ts) that manages execution timing and delegates to the appropriate engine. Redis distributed locks prevent duplicate concurrent executions of the same bot.
Execution Pipeline
The full execution flow proceeds as a numbered sequence:
-
Scheduler polls every 60 seconds. The
BotSchedulersingleton runscheckAndExecute()on a 60-secondsetInterval, comparing each bot'snextExecutionAtagainst the current time. -
Identifies bots where
nextExecutionAt <= now. The scheduler iterates its in-memoryscheduledBotsmap and collects all bots due for execution. -
Acquires Redis lock (
bot:execution:lock:{botId}, 120-second TTL). TheacquireExecutionLock()function usesredis.set(lockKey, "locked", "EX", 120, "NX"). If the lock cannot be acquired (another instance is already executing), the bot is skipped. If Redis is unavailable, execution proceeds without a lock but logs a warning. -
Routes to the appropriate engine by bot type. The
executeBot()function normalizesbot.typeto lowercase and dispatches:"dca"-->DCABot"arbitrage"-->ArbitrageBot"pump"-->PumpScreener"signal"--> skipped by scheduler (processed via API endpoint)- All others --> AI/Strategy bot flow (the default)
-
Engine executes strategy/scan. Each engine runs its core logic: strategy analysis for AI bots, price scanning for arbitrage, market scanning for pump screener, or scheduled purchase logic for DCA.
-
Risk validation (pre-trade checks). For AI/Strategy bots, the
validateTrade()function checks: max open positions, daily trade limit, daily loss limit, max drawdown, position size, balance sufficiency, and signal confidence. -
Trade execution (paper or live). Based on
bot.paperTrading, trades execute through either thePaperTradingEngine(simulated) or real exchange APIs (Binance, OKX) viaplaceMarketOrder(). -
Position tracking update. New BUY trades create
Positionrecords in both the in-memoryPositionManagerand the database. Existing positions are checked for stop-loss and take-profit triggers. -
Execution record saved to database. An
Executionrecord is created with signal action, confidence, reason, price, indicators, and status. -
Lock released. The
finallyblock callsreleaseLock()to delete the Redis key, ensuring the lock is always released even if an error occurs.
Bot Types
AI/Strategy Bot
The AI/Strategy bot is the most comprehensive bot type. It uses technical analysis strategies to generate BUY, SELL, or HOLD signals.
Strategy Factory Pattern:
The createStrategy() function in strategy-factory.ts accepts a BotConfig and returns the appropriate BaseStrategy subclass:
// lib/bot/strategies/strategy-factory.ts
export function createStrategy(config: BotConfig): BaseStrategy {
switch (config.strategy) {
case "trend_following":
return new TrendFollowingStrategy(config.strategyParams);
case "mean_reversion":
return new MeanReversionStrategy(config.strategyParams);
case "breakout":
return new BreakoutStrategy(config.strategyParams);
default:
throw new Error(`Unsupported strategy type: ${config.strategy}`);
}
}
Each strategy implements an analyze(context: ExecutionContext) method that returns a Signal with action (BUY/SELL/HOLD), confidence (0-100), reason, price, and indicator values.
Execution Flow (AI/Strategy):
- Load bot configuration and normalize strategy name (e.g., "trend following" -> "trend_following").
- Check exchange connection availability for live trading.
- Decrypt API keys from
ExchangeConnectionusing AES-256-GCM. - Fetch 100 candles from Binance for the configured trading pair and timeframe.
- Build
ExecutionContextwith candles, indicators, positions, and balance. - Check existing positions for stop-loss and take-profit triggers (including trailing stop updates).
- Run strategy analysis to generate a
Signal. - If signal is BUY or SELL, calculate position size, validate trade against risk rules, and execute.
- Record execution and update
lastExecutionAt.
Concurrency Control:
The executeBots() function processes bots in batches with a configurable concurrency limit (default: 5) to avoid overwhelming exchange rate limits. Batches run in parallel via Promise.allSettled().
DCA Bot
Dollar-cost averaging buys a fixed amount of an asset at regular intervals regardless of price.
Configuration:
// lib/bot/engine/dca-bot.ts
export interface DCAConfig {
tradingPair: string;
investmentAmount: number; // Amount in quote currency (e.g., USDT)
frequency: "hourly" | "daily" | "weekly" | "monthly";
frequencyValue: number; // e.g., every 2 weeks
startDate: string;
endDate?: string;
maxTotalInvestment?: number;
orderType: "market" | "limit";
limitPriceOffset?: number;
}
shouldExecute() Checks:
- Bot must be active.
- Current date must be past
startDate. - If
endDateis set, current date must not exceed it (auto-stops bot). - If
maxTotalInvestmentis set, total invested must not exceed it (auto-stops bot). - Time since
lastExecutionAtmust exceed the configured interval.
Execution:
- Fetches current price from the user's Binance connection.
- Calculates quantity as
investmentAmount / price. - For paper trading: records trade with 0.1% simulated fee.
- For live trading: validates order, places market buy using
quoteOrderQty(spend a fixed USDT amount), and records the trade with actual fill data. - Updates
lastExecutionAton the bot record.
Arbitrage Bot
Detects and optionally executes cross-exchange price differentials.
Configuration:
// lib/bot/engine/arbitrage-bot.ts
export interface ArbitrageConfig {
exchanges: string[]; // e.g., ["binance", "okx"]
tradingPairs: string[]; // e.g., ["BTCUSDT", "ETHUSDT"]
minProfitPercent: number; // Minimum profit threshold
maxTradeAmount: number; // Maximum amount per trade
includeTransferFees: boolean; // Account for transfer fees
transferFeePercent: number; // Average transfer fee
executionMode: "auto" | "notify";
checkIntervalSeconds: number;
}
Price Scanning:
The scanOnce() method (called by bot-executor.ts during scheduled execution) fetches prices from all configured exchanges and compares every pair of exchanges for each trading pair in both directions.
- Binance prices: Fetched via the authenticated user client using
client.prices(). - OKX prices: Fetched via the public API (no auth needed) using
getOkxPrices(). Symbol format is converted between Binance (BTCUSDT) and OKX (BTC-USDT) usingbinanceToOkx().
Opportunity Calculation:
The calculateOpportunity() method accounts for:
- 0.1% trading fee per side (buy and sell).
- Configurable transfer fee percentage (if
includeTransferFeesis true). - Net profit =
(sellPrice * (1 - fee)) - (buyPrice * (1 + fee)) - transferFee. - Only positive-profit opportunities are returned.
Execution Modes:
notify: Records the opportunity as an execution with signalARBITRAGE_NOTIFY. No trades are placed.auto: Executes trades viaArbitrageExecutionCoordinator(for live) or records paper trades with simulated buy/sell pairs. Live execution includes anArbitrageRiskManagercircuit breaker with configurable max position size, max daily loss, max consecutive failures, min profit threshold, max slippage, and max execution time.
Signal Bot
Receives and executes trading signals from external providers.
Configuration:
// lib/bot/engine/signal-bot.ts
export interface SignalConfig {
tradingPairs: string[];
positionSizeType: "fixed" | "percentage";
positionSize: number;
maxOpenPositions: number;
followStopLoss: boolean;
followTakeProfit: boolean;
stopLossPercent?: number;
takeProfitPercent?: number;
signalProviders: SignalProvider[];
}
export interface SignalProvider {
id: string;
type: "telegram" | "discord" | "manual" | "webhook";
name: string;
enabled: boolean;
config: {
channelId?: string;
webhookUrl?: string;
apiKey?: string;
};
}
Signal Processing:
Signal bots are not executed by the scheduler. Instead, signals arrive via the send-signal API endpoint and are processed by processSignal():
- Validate the signal (must have symbol and valid action: BUY, SELL, or CLOSE).
- Verify the trading pair is in the bot's allowed list.
- For BUY: check
maxOpenPositionslimit, calculate position size (fixed or percentage of balance), determine stop-loss/take-profit (from signal or config defaults), and execute. - For SELL/CLOSE: find the open position for the symbol, execute the sell, calculate P&L, and close the position record.
Provider Types:
- Telegram: Messages are parsed by
parseTelegramSignal(), which supports formats likeBUY BTCUSDT @ 45000andLONG BTC Entry: 45000 SL: 44000 TP: 47000. - Webhook: External services POST signals directly to the API.
- Discord: Similar to Telegram with channel-based signal parsing.
- Manual: User-submitted signals through the UI.
Position Size Calculation:
"fixed": Uses the configuredpositionSizeas the dollar amount."percentage": Calculatesbalance * (positionSize / 100)using either the paper trading balance (from database) or the real Binance account balance.
Pump Screener
Detects sudden volume spikes and price surges across the market.
Configuration:
// lib/bot/engine/pump-screener.ts
export interface PumpScreenerConfig {
priceChangePercent: number; // Minimum price change % to trigger (default: 5)
volumeMultiplier: number; // Volume must be X times average (default: 3)
timeframeMinutes: number; // Timeframe to measure changes (default: 15)
minVolume: number; // Minimum 24h volume in USDT (default: 100000)
excludeSymbols: string[];
onlySymbols: string[]; // If set, only monitor these symbols
autoTrade: boolean;
tradeAmount: number; // Amount to trade in USDT
takeProfitPercent: number;
stopLossPercent: number;
checkIntervalSeconds: number;
}
Detection Logic:
The scanOnce() method (called by the scheduler):
- Fetches 24h ticker data from Binance (
dailyStats()). - Filters by: minimum volume, USDT pairs only, exclude/only symbol lists.
- Updates an in-memory price history for each symbol.
- For each symbol, compares current price to the oldest price in the history window:
- If
priceChangePercent >= config.priceChangePercentandvolumeMultiplier >= config.volumeMultiplier, a pump is detected.
- If
- Records detection as an execution with signal
PUMP_DETECTEDand confidence scaled by price change magnitude.
Auto-Trade:
When autoTrade is enabled, the screener automatically enters positions on pump detection:
- Calculates quantity as
tradeAmount / currentPrice. - Sets stop-loss and take-profit based on configured percentages.
- Executes via paper or live trading.
- Creates a position record with SL/TP for monitoring.
Trading Strategies
Trend Following
File: lib/bot/strategies/trend-following.ts
Uses EMA (Exponential Moving Average) crossovers with volume confirmation and multi-timeframe analysis.
BUY Signal Logic:
- Fast EMA crosses above Slow EMA (bullish crossover).
- Volume must be above the threshold multiplier times the 20-period average volume.
- Higher timeframe trend must not be bearish (candles are aggregated by 4x factor to synthesize a higher timeframe).
- Optional strong uptrend continuation entry: if Fast EMA is more than 1% above Slow EMA with volume confirmation and a consistent uptrend.
SELL Signal Logic:
- Fast EMA crosses below Slow EMA (bearish crossover).
- Price drops more than 2% below Slow EMA (trend reversal).
Confidence Calculation:
- Base confidence of 60 for crossover detection.
- Bonuses for: strong EMA spread (>0.5% or >1%), high volume ratio (>1.5x or >2x), consistent uptrend, higher timeframe alignment (+10).
- Capped at 100.
Configurable Parameters:
| Parameter | Default | Description |
|---|---|---|
fastEMA | 9 | Short EMA period |
slowEMA | 21 | Long EMA period |
volumeThreshold | 1.2 | Volume must be X times 20-bar average |
confirmationCandles | 1 | Candles to wait for confirmation |
Mean Reversion
File: lib/bot/strategies/mean-reversion.ts
Combines RSI (Relative Strength Index) with Bollinger Bands to identify oversold/overbought conditions.
BUY Signal Logic:
- Primary: RSI below oversold level + price at or below lower Bollinger Band + bounce confirmation (current price > previous close).
- Secondary: RSI crossing back above oversold level + price near lower band (%B < 0.2).
- Tertiary: Extreme oversold (RSI < oversold - 5) with %B < 0.15.
SELL Signal Logic:
- RSI reaches overbought level (mean reversion target reached).
- Price at or above upper Bollinger Band.
- Price returns to middle Bollinger Band (conservative exit when RSI > 50).
- RSI crosses back above oversold while position is still losing (cut loss).
Configurable Parameters:
| Parameter | Default | Description |
|---|---|---|
rsiPeriod | 14 | RSI calculation period |
oversoldLevel | 30 | RSI threshold for oversold |
overboughtLevel | 70 | RSI threshold for overbought |
bollingerPeriod | 20 | Bollinger Bands period |
bollingerStdDev | 2 | Standard deviations for bands |
Breakout
File: lib/bot/strategies/breakout.ts
Detects Bollinger Band breakouts with volume confirmation and squeeze detection.
BUY Signal Logic:
- Price breaks above the upper Bollinger Band (previous close was below, current is above).
- Volume must exceed
volumeMultipliertimes the 20-period average. - Confirmation candles must show pressure near the upper band.
- Bonus confidence for recent Bollinger squeeze (bandwidth < 2%) and trending up.
- Continuation entry: price already walking the upper band with sustained volume.
SELL Signal Logic:
- Price breaks below the lower Bollinger Band (aggressive exit, confidence >= 80).
- Price drops below the middle Bollinger Band (momentum faded).
- Three consecutive declining candles with price retreating from the upper band.
Configurable Parameters:
| Parameter | Default | Description |
|---|---|---|
bollingerPeriod | 20 | Bollinger Bands period |
bollingerStdDev | 2 | Standard deviations for bands |
volumeMultiplier | 1.5 | Volume must be X times 20-bar average |
confirmationCandles | 1 | Candles to confirm breakout |
Key Type Definitions
The following interfaces from lib/bot/types.ts define the core data structures used throughout the bot system.
BotConfig
export interface BotConfig {
strategy: "trend_following" | "mean_reversion" | "breakout";
tradingPair: string; // e.g., "BTCUSDT"
timeframe: "1m" | "5m" | "15m" | "30m" | "1h" | "4h" | "1d";
paperTrading: boolean;
strategyParams: StrategyParams;
executionInterval?: number; // Minutes between executions, default 5
enableStopLoss: boolean;
enableTakeProfit: boolean;
trailingStop?: boolean;
}
Signal
export interface Signal {
action: "BUY" | "SELL" | "HOLD";
confidence: number; // 0-100
price: number; // Current market price
quantity?: number; // Suggested quantity (optional)
reason: string; // Human-readable explanation
indicators: IndicatorValues; // Indicator values that led to this signal
metadata?: Record<string, unknown>;
}
ExecutionContext
export interface ExecutionContext {
bot: {
id: string;
userId: string;
name: string;
config: BotConfig;
riskSettings: RiskSettings;
};
symbol: string;
timeframe: string;
candles: Candle[]; // Historical candles (100-500 bars)
currentPrice: number;
currentVolume?: number;
indicators: IndicatorValues;
positions: Position[];
balance: {
asset: string; // e.g., "USDT"
free: number;
locked: number;
total: number;
};
apiKey: string;
apiSecret: string;
executionId: string;
timestamp: Date;
}
RiskSettings
export interface RiskSettings {
positionSizePercent: number; // % of portfolio per trade
maxPositionSizePercent: number; // Max % in a single position
stopLossPercent: number; // % below entry for stop loss
takeProfitPercent: number; // % above entry for take profit
maxPositions: number; // Max concurrent positions
maxDailyTrades: number; // Max trades per day
maxDailyLossPercent: number; // Pause bot if daily loss exceeds this %
maxDrawdownPercent: number; // Pause bot if drawdown exceeds this %
trailingStopPercent?: number;
partialTakeProfitPercent?: number;
partialTakeProfitSize?: number;
}
Position
export interface Position {
id: string;
botId: string;
symbol: string;
side: "BUY" | "SELL";
entryPrice: number;
quantity: number;
currentPrice?: number;
unrealizedPnL?: number;
stopLoss: number;
takeProfit: number;
highestPrice?: number; // For trailing stop
tradeId: string; // Link to Trade record
enteredAt: Date;
}
Technical Indicators
All indicators are implemented in lib/bot/indicators/ and used by the strategy classes.
SMA (Simple Moving Average)
Arithmetic mean of prices over a period. Used internally by Bollinger Bands for the middle band.
EMA (Exponential Moving Average)
Weighted moving average that gives more importance to recent prices. Crossovers between fast and slow EMAs form the basis of the trend-following strategy. Key functions:
calculateEMA(prices, period): Returns the current EMA value.isBullishEMACrossover(fastEMA, prevFastEMA, slowEMA, prevSlowEMA): Detects fast EMA crossing above slow EMA.isBearishEMACrossover(...): Detects fast EMA crossing below slow EMA.
RSI (Relative Strength Index)
Momentum oscillator measuring speed and magnitude of price changes on a 0-100 scale. Used by the mean reversion strategy to identify oversold (< 30) and overbought (> 70) conditions.
calculateRSI(prices, period): Returns the current RSI value.
MACD (Moving Average Convergence Divergence)
Trend-following momentum indicator showing the relationship between two EMAs. The IndicatorValues type includes MACD line, signal line, and histogram.
Bollinger Bands
Volatility bands placed above and below a moving average. Used by both mean reversion and breakout strategies.
calculateBollingerBands(prices, period, stdDev, currentPrice?): Returns{ upper, middle, lower, bandwidth, percentB }.- Bandwidth: Measures the distance between upper and lower bands as a percentage of the middle band. A bandwidth < 2% indicates a "squeeze" (low volatility), which often precedes a breakout.
- %B: Indicates where the current price sits relative to the bands (0 = at lower band, 1 = at upper band).
Indicator Caching
Indicator results are cached using CacheEntry<T> and CacheKey types to avoid redundant calculations within the same execution cycle. Cache keys include symbol, indicator name, period, timeframe, and a timestamp rounded to the nearest minute.
Risk Management
Position Sizing
File: lib/bot/risk/position-sizer.ts
Uses the Fixed Percentage Risk Model:
- Calculate the risk amount:
accountBalance * (positionSizePercent / 100). - Calculate the price risk per unit:
|entryPrice - stopLossPrice|. - Calculate the base quantity:
riskAmount / priceRiskPerUnit. - Apply maximum position size cap:
accountBalance * (maxPositionSizePercent / 100) / entryPrice. - Apply minimum order size from the exchange.
- Verify sufficient balance.
- Round to exchange precision (default: 8 decimal places).
Example:
- Account balance: $10,000
- Risk per trade: 2% = $200
- Entry price: $50,000, Stop loss at 4% = $48,000
- Price risk per unit: $2,000
- Base position: $200 / $2,000 = 0.1 BTC
- Max position (20%): $2,000 / $50,000 = 0.04 BTC
- Final position: 0.04 BTC (capped by max)
The result includes limitApplied indicating which constraint was binding: "none", "max_position", "min_order", or "insufficient_balance".
Stop-Loss
File: lib/bot/risk/stop-loss-manager.ts
Fixed Stop-Loss:
- Calculated as
entryPrice * (1 - stopLossPercent / 100). - Triggers when
currentPrice <= stopLossPrice. - On trigger, the position is closed via market sell and P&L is calculated.
Trailing Stop-Loss:
- Follows price upward, locking in gains as the position moves favorably.
updateTrailingStopLoss()calculates:newStop = highestPrice * (1 - trailingPercent / 100).- The stop only moves up, never down:
finalStop = max(newStop, currentStop, entryPrice). - The
highestPriceis tracked on the Position record and updated each execution cycle.
Additional Stop-Loss Features:
- Break-even stop: moves stop to entry price + fees once price rises by a trigger percentage.
- Time-based stop: closes position if held longer than
maxHoldTimeMinutes(default: 1440 = 24 hours). - ATR-based stop: adapts to market volatility using
entryPrice - (ATR * multiplier). - Emergency stop: safety net at 10% loss regardless of configured stop level.
Take-Profit
File: lib/bot/risk/take-profit-manager.ts
- Fixed Take-Profit: Calculated as
entryPrice * (1 + takeProfitPercent / 100). Triggers whencurrentPrice >= takeProfitPrice. - Partial Take-Profit: The
calculatePartialTakeProfits()function creates multiple levels. For example, with 3 levels and a 15% target: close 33% at 5%, 33% at 10%, and 34% at 15%.
Drawdown Tracking
File: lib/bot/risk/drawdown-tracker.ts
The DrawdownTracker is an in-memory singleton that monitors peak-to-trough decline for each bot:
- Tracks
peakBalance,currentBalance,troughBalance,currentDrawdownPercent, andmaxDrawdownPercent. - Updated after each position close via
drawdownTracker.updateDrawdown(botId, balance, isLoss). - State is rebuilt from database trade records on server startup.
- Issues warnings at configurable thresholds:
- Warning: drawdown approaching limit.
- Critical: drawdown at or near limit.
- Emergency: drawdown exceeded, bot should be paused.
- The
maxDrawdownPercentacts as a circuit breaker -- when exceeded, the risk validator blocks new trades.
Pre-Trade Validation
File: lib/bot/risk/risk-validator.ts
The validateTrade() function performs seven checks before any BUY trade:
- Max positions limit:
currentPositions.length >= riskSettings.maxPositions. - Daily trade limit: Count of trades today >=
riskSettings.maxDailyTrades. - Daily loss limit: Today's P&L loss >=
accountBalance * (maxDailyLossPercent / 100). - Max drawdown limit: Current drawdown >=
riskSettings.maxDrawdownPercent. - Position size validation: Quantity and value must not be zero.
- Max position size: Position value must not exceed
accountBalance * (maxPositionSizePercent / 100). - Balance sufficiency: Position value must not exceed available balance.
- Signal confidence: Must be >= 50%.
SELL signals always pass validation (exits are never blocked).
If any check fails, the trade is rejected with specific violation messages. The shouldPauseBot() function can also auto-pause bots that have exceeded daily loss or drawdown limits.
Paper Trading
File: lib/bot/engine/paper-trading.ts
The PaperTradingEngine simulates order execution without placing real trades on any exchange.
Key Characteristics:
- In-memory balance tracking: Maintains
Map<botId, Map<asset, PaperBalance>>with free, locked, and total amounts. - Default initial balance: $10,000 USDT per bot.
- Fee simulation: 0.1% trading fee (Binance standard rate).
- Market order simulation: BUY orders deduct
(quantity * price) + feefrom quote balance and add quantity to base balance. SELL orders do the inverse. - Balance checks: Validates sufficient balance before execution, returning an error with the specific shortfall if insufficient.
- Persistence: All paper trades are saved to the database
Tradetable withisPaperTrade: true, enabling identical reporting and analytics as live trades. - State rebuild: On server startup,
rebuildPaperTradingStates()loads the latest trade for each active paper-trading bot and initializes the in-memory balance from the database.
Usage:
The simplified executePaperTrade() function is called by bot-executor.ts and all bot engines when paperTrading is true:
export async function executePaperTrade(
botId: string,
symbol: string,
side: "BUY" | "SELL",
quantity: number,
currentPrice: number
): Promise<PaperTradeResult>
Paper trading produces identical tracking and reporting as live trading. It is the recommended mode for strategy testing before committing real capital.
Backtesting
File: lib/bot/backtest/backtester.ts
The backtesting engine replays strategies against historical data to evaluate performance before live deployment.
Process:
-
Fetch historical data:
fetchHistoricalData()retrieves OHLCV candles for the requested symbol, timeframe, and date range. An additional lookback of 100 periods is added for indicator warmup. Data is cached to avoid redundant API calls. -
Validate dataset:
validateHistoricalDataset()checks for data completeness and consistency. -
Execute backtest:
executeBacktest()iterates through candles chronologically, running the configured strategy at each bar and simulating trades with configurable commission (default: 0.1%) and slippage (default: 0.05%). -
Generate report:
generateBacktestReport()produces a comprehensive report including:- Final balance and total return
- Win rate and trade count
- Sharpe ratio
- Max drawdown
- Profit factor
- Equity curve data points
Advanced Backtesting Modes:
- Parameter Optimization:
runParameterOptimization()tests a grid of strategy parameter combinations, sorting results by total return. - Walk-Forward Analysis:
runWalkForwardAnalysis()tests the strategy across sequential time periods (e.g., 30-day windows), measuring consistency as the percentage of profitable periods. - Quick Backtest:
quickBacktest()runs a 30-day backtest with a $10,000 initial balance for rapid validation.
Performance Tracking
File: lib/bot/performance/performance-tracker.ts
Metrics
The PerformanceMetrics interface captures comprehensive trading statistics:
export interface PerformanceMetrics {
totalTrades: number;
winningTrades: number;
losingTrades: number;
winRate: number; // 0-100
totalProfit: number;
totalLoss: number;
netProfit: number;
netProfitPercent: number;
profitFactor: number; // totalProfit / totalLoss
sharpeRatio: number; // Risk-adjusted return
sortinoRatio?: number; // Downside risk-adjusted return
avgWin: number;
avgLoss: number;
avgTrade: number;
largestWin: number;
largestLoss: number;
avgTradeDuration?: number; // Hours
maxDrawdown: number;
maxDrawdownPercent?: number;
currentDrawdown: number;
maxWinStreak: number;
maxLossStreak: number;
currentStreak: number;
startDate: Date;
endDate: Date;
totalDays: number;
}
Snapshots
recordPerformanceSnapshot() calculates metrics for a bot and saves a PerformanceSnapshot record with:
- Computed metrics via
calculatePerformanceMetrics(). - Risk score via
calculateRiskScore(). - Rating classification: "excellent", "good", "fair", or "poor" via
getPerformanceRating().
Snapshots are recorded periodically (daily) via recordAllBotSnapshots() for all active bots.
Trend Analysis
The getPerformanceHistory() function retrieves historical snapshots and calculates trends using simple linear regression:
- Return trend: improving, stable, or declining.
- Win rate trend: improving, stable, or declining.
- Risk trend: improving, stable, or worsening.
Alerts
getPerformanceAlerts() identifies bots with concerning performance:
- Critical: Profit factor < 1 (losing money) with 10+ trades, or drawdown > 30%.
- Warning: Risk score > 60, or win rate < 30% with 20+ trades.
- Info: Fewer than 5 trades (insufficient history).
Bot Comparison
compareBots() ranks bots using a weighted composite score:
- 40% weight on net profit percentage.
- 30% weight on Sharpe ratio.
- 20% weight on profit factor.
- 10% weight on low risk score.
Position Management
File: lib/bot/engine/position-manager.ts
The PositionManager is an in-memory singleton that tracks open positions for all bots.
Responsibilities:
- Add/remove positions: Maintains
Map<botId, Position[]>. - Update prices:
updatePosition()setscurrentPriceand recalculatesunrealizedPnL. - Enforce limits:
hasReachedMaxPositions()checks against configured position limits. - Exposure tracking:
getTotalExposure()sums all position values;getTotalUnrealizedPnL()sums unrealized P&L. - Trigger detection:
checkStopLosses()andcheckTakeProfits()scan for triggered levels. - Rebuild on startup:
rebuildPositions()loads all open trades from the database, groups by bot and symbol, calculates net quantity and average entry price, fetches current prices, and reconstructs Position objects.
Positions are ephemeral in memory but backed by database Position and Trade records for durability. The in-memory layer exists for performance -- avoiding database queries on every price check during execution cycles.