Learn the components of a trading system
Overview
A trading system is a complete, automated framework that executes trades based on predefined rules without human intervention. Understanding its components is crucial because each piece handles a specific failure mode in algorithmic trading—data errors, execution delays, risk breaches, or logic bugs.
WHY these components matter: Manual trading fails at scale due to emotion, speed limits, and inconsistency. A systematic approach separates concerns: data → logic → execution → monitoring, allowing you to test, optimize, and debug each independently.
Core Components
1. Data Management Layer
WHY separate data handling?
- Market data arrives dirty (missing ticks, spikes, delayed timestamps)
- Strategies need multiple timeframes/assets synchronized
- Backtesting requires historical data in identical format to live data
HOW it works:
Raw Feed → Validation → Normalization → Storage → API
(check gaps) (align timestamps) (DB/cache) (serve strategy)
Raw: AAPL shows price jump from 1500 → $151 in 1 second
def clean_tick(current, previous, threshold=0.10): """Remove ticks > 10% move (likely error)""" if abs(current - previous)/previous > threshold: return previous # Hold last valid price return current
Why this step? Without cleaning, a strategy might:
- Trigger false breakout signal
- Calculate wrong volatility
- Execute at phantom price
**Key data types**:
1. **Tick data**: Every trade/quote (L1 data)
2. **OHLCV bars**: Aggregated candles (1min, 5min, daily)
3. **Order book**: Bid/ask depth (L2/L3 data)
4. **Reference data**: Symbols, contract specs, corporate actions
> [!mistake]
> **Mistake**: Using different data sources for backtest vs live.
> **Why it feels right**: Historical data from vendor X is clean; live feed from broker Y is convenient.
> **The trap**: Vendor X adjusts for splits/dividends retroactively; broker Y doesn't → backtest shows 50% return, live loses money.
> **Fix**: Use identical data pipeline for both, or explicitly reconcile adjustments.
---
### 2. Strategy Engine
> [!definition]
> The ==strategy engine== contains the trading logic: signal generation, parameter calculations, and decision rules. It answers: "Given current state, should I enter/exit/hold?"
**WHY isolate strategy code?**
- Enables **A/B testing** multiple strategies in parallel
- Separates research (strategy dev) from engineering (infrastructure)
- Allows backtesting without touching live systems
**HOW a strategy operates** (example: Mean Reversion):
**Step 1: Calculate signal**
```python
# Moving average deviation strategy
price = data['close']
ma_20 = price.rolling(20).mean()
std_20 = price.rolling(20).std()
z_score = (price - ma_20) / std_20 # Deviation in standard deviations
# Signal: -1 (sell), 0 (neutral), +1 (buy)
signal = np.where(z_score < -2, 1, # Price 2σ below mean → buy
np.where(z_score > 2, -1, 0)) # Price 2σ above → sell
Why this step?
- z_score normalizes price deviation across different stocks/volatility regimes
- -2/+2 thresholds balance frequency (fewer signals) vs conviction (stronger deviations)
Step 2: Position sizing
# Kelly Criterion: optimal fraction = edge / odds
win_rate = 0.55 # Historical: 55% of mean-reversion trades profit
avg_win = 0.02 # Average return when right: +2%
avg_loss = 0.015 # Average loss when wrong: -1.5%
kelly_fraction = (win_rate * avg_win - (1 - win_rate) * avg_loss) / avg_win
# = (0.55*0.02 - 0.45*0.015) / 0.02= 0.2125
position_size = account_value * kelly_fraction * signalWhy this step?
- Fixed dollar amounts ignore changing account size and risk
- Full Kelly is aggressive; traders use half-Kelly for safety
- Signal strength (z_score magnitude) can scale position further
Problem: 5-min chart shows buy, but 1-hour is downtrend → likely false signal
def multi_timeframe_signal(data5m, data_1h): # Short-term: mean reversion z_5m = calculate_zscore(data_5m, window=20) signal_5m = 1 if z_5m < -2 else 0
# Long-term: trend filter
ma_fast1h = data_1h['close'].rolling(20).mean()
ma_slow_1h = data_1h['close'].rolling(50).mean()
uptrend_1h = ma_fast_1h > ma_slow_1h
# Only take5m buy signals if1h trend agrees
return signal_5m if uptrend_1h else 0
Why? Reduces whipsaws in ranging markets within downtrends
---
### 3. Risk Management Module
> [!definition]
> The ==risk management module== enforces hard limits on losses, position sizes, and exposure **before orders reach the market**. It's the system's circuit breaker.
**WHY mandatory risk controls?**
- A bug in strategy code can drain an account in seconds
- Flash crashes create temporary mispricings that trigger mass liquidations
- Regulatory requirements (brokers demand risk limits)
**HOW risk checks work** (executed in sequence):
> [!formula]
> **Pre-trade risk checks:**
1. **Position limit**:
$$\text{Proposed position} \leq \text{Max position per symbol}$$
Prevents concentration risk (e.g., max 10% of portfolio in one stock).
2. **Portfolio delta**:
$$\Delta_{\text{portfolio}} = \sum_{i=1}^{n} \text{position}_i \times \frac{\partial P_i}{\partial S_i}$$
where $P_i$ = position value, $S_i$ = underlying price. Limits directional exposure.
3. **Value-at-Risk (VaR)**:
$$\text{VaR}_{95} = \mu_{\text{portfolio}} - 1.645 \times \sigma_{\text{portfolio}}$$
Estimates max loss over 1 day with 95% confidence. Rejects trades pushing VaR above limit.
4. **Drawdown check**:
$$\text{Current drawdown} = \frac{\text{Equity}_{\text{peak}} - \text{Equity}_{\text{current}}}{\text{Equity}_{\text{peak}}}$$
If $> \text{max allowed}$ (e.g., 15%), halt new trades.
**Post-trade monitoring**:
- **Stop-loss**: Auto-exit if position loses X% (protects from "hoping it recovers")
- **Time-based exit**: Close positions held beyond N periods (prevents dead capital)
> [!example]
> **Example: Risk module rejecting a trade**
> ```python
> class RiskManager:
> def __init__(self, max_position_pct=0.10, max_drawdown=0.15):
> self.max_position_pct = max_position_pct
> self.max_drawdown = max_drawdown
> self.equity_peak = 1000
def check_trade(self, proposed_value, current_equity portfolio_value):
# Check 1: Position size
position_pct = abs(proposed_value) / portfolio_value
if position_pct > self.max_position_pct:
return False, f"Position {position_pct:.1%} exceeds {self.max_position_pct:.1%}"
# Check 2: Drawdown
drawdown = (self.equity_peak - current_equity) / self.equity_peak
if drawdown > self.max_drawdown:
return False, f"Drawdown {drawdown:.1%} exceeds limit"
return True, "Approved"
# Usage
rm = RiskManager()
approved, msg = rm.check_trade(
proposed_value=15000, # Want to buy $15k of stock
current_equity=85000, # Account down from $100k peak
portfolio_value=100000
)
# Result: False, "Drawdown 15.0% exceeds limit" → trade blocked
Why this step? Without pre-trade checks, the strategy might:
- Calculate a $50k position in a $100k account (overleveraged)
- Ignore that the account is already down 20% (violates drawdown rule)
- Get the order through, then face margin call
4. Execution Engine
WHY separate execution?
- Strategy says "buy10,000 shares"; execution decides "how, when, and where"
- A $1M market order moves the price against you (slipage)
- Different venues (exchanges, dark pools) offer different liquidity
HOW execution strategies work:
-
Market order: Immediate fill at current price
- Use case: Small size, urgent entry (e.g., breaking news)
- Cost: Pays the spread + potential slippage
-
Limit order: Only fill at specified price or better
- Use case: Large size, patient entry
- Risk: May not fill (adverse selection—price moves away)
-
VWAP (Volume-Weighted Average Price): Slice order across the day to match historical volume distribution.
Why? Minimizes market impact by "hiding" in natural flow.
-
TWAP (Time-Weighted Average Price): Execute equal slices every N minutes.
Why? Simple, predictable, good when volume profile is flat.
Want to buy 50,000 shares over 4 hours
Historical volume profile: 30% in hour 1, 40% in hour 2, 20% in hour 3, 10% in hour 4
total_shares = 50000 volume_profile = [0.30, 0.40, 0.20, 0.10]
order_slices = [int(total_shares * pct) for pct in volume_profile]
[15000, 20000, 10000, 5000]
Execution:
for hour, shares in enumerate(order_slices, 1): # Further split into 12 orders (every 5 min) for that hour shares_per_interval = shares // 12 for interval in range(12): place_limit_order(shares_per_interval, limit_price=get_mid_price()) wait(5 * 60) # 5 minutes
Why this step?
- Matches market rhythm (low impact)
- Avoids signaling "large buyer here" which frontrunners exploit
- Limit orders at mid-price capture spread (vs market orders paying spread)
**Slipage calculation**:
$$\text{Slippage} = \text{Average fill price} - \text{Decision price}$$
If you decide to buy at \$100, but fills average \$100.20 → 20¢ slippage × 50,000 shares = **\$10,000 hidden cost**.
---
### 5. Monitoring & Logging
> [!definition]
> The ==monitoring system== tracks live performance, system health, and anomalies in real-time. ==Logging== records every decision, order, and fill for post-mortem analysis.
**WHY critical?**
- Live trading has no "undo"—you must detect failures in seconds
- Post-trade analysis requires exact reconstruction of what happened
- Regulatory audits demand complete order trails
**WHAT to monitor**:
1. **Performance metrics**:
- P&L (realized + unrealized)
- Sharpe ratio: $\frac{\text{Mean return}}{\text{Std dev of returns}}$ (risk-adjusted return)
- Win rate, avg win/loss, max drawdown
2. **System health**:
- Latency (data arrival to order submission)
- Fill rates (orders executed / orders placed)
- Data gaps (missed ticks)
3. **Anomaly detection**:
- Sudden volatility spike (circuit breaker trigger)
- Order rejections (margin, halts)
- Position drift (actual vs intended)
> [!example]
> **Example: Real-time alert system**
> ```python
> class Monitor:
> def __init__(self):
> self.alerts = []
def check_system(self, state):
# Alert 1: High latency
if state['latency_ms'] > 100:
self.alert(f"HIGH LATENCY: {state['latency_ms']}ms (threshold: 100ms)")
# Alert 2: Unexpected position
expected_pos = state['strategyposition']
actual_pos = state['broker_position']
if abs(expected_pos - actual_pos) > 10:
self.alert(f"POSITION DRIFT: Expected {expected_pos}, actual {actual_pos}")
# Alert 3: Drawdown breach
if state['drawdown'] > 0.15:
self.alert(f"DRAWDOWN LIMIT: {state['drawdown']:.1%}")
return "HALT_TRADING"
def alert(self, message):
# Send to email, Slack, SMS
print(f"[ALERT] {message}")
self.alerts.append((datetime.now(), message))
# Why these checks?
# - High latency → stale data → wrong signals
# - Position drift → partial fills, or execution bug
# - Drawdown breach → stop digging when in a hole
Log structure (every order):
timestamp, symbol, signal, order_id, order_type, quantity, limit_price, fill_qty, slippage, reason
Enables questions like: "Why did I buy XYZ at 10:23?" → Check log → Signal was z_score = -2.3, limit $100, filled $100.15.
System Architecture
Critical timing constraint:
where = time before your signal becomes public knowledge. For HFT, this is microseconds; for daily strategies, minutes are acceptable.
Practical Considerations
Backtesting vs Live: The Gap
Why it happens:
- Look-ahead bias: Using future data (e.g., daily close at 9:30 AM)
- Survivorship bias: Backtest on stocks still listed (ignores bankruptcies)
- Overfitting: Strategy tuned to historical noise, not real patterns
- Transaction costs: Backtest ignores slippage, fees, spread
Fix checklist:
- Backtest uses point-in-time data only
- Includes realistic slippage model (e.g., 0.05% per trade)
- Tests on out-of-sample period (don't touch test data during dev)
- Assumes bid-ask spread (never trade at mid-price in backtest)
- Accounts for failed orders (10% of limits don't fill)
Latency Budgets
For different strategy speeds:
| Strategy Type | Data Freq | Max Latency | Infra Needs |
|---|---|---|---|
| Daily swing | EOD bars | Minutes | Cloud server OK |
| Intraday mean-rev | 1-min bars | 1-5 seconds | Co-located server |
| High-frequency | Tick-by-tick | <1 millisecond | FPGA, direct exchange feed |
Why latency matters: In a mean-reversion strategy, if your signal triggers at $100 but you execute at $100.10 due to delay, you've already lost 0.1%—potentially your entire edge.
Recall
Explain to a 12-year-old:
Imagine you're running a lemonade stand, but instead of you deciding when to make lemonade, you built a robot to do it automatically.
Data Management = Your robot's eyes and ears. It watches the weather (hot day = more sales), counts customers walking by, and remembers yesterday's sales.
Strategy Engine = The robot's brain. It has rules like: "If temperature > 30°C AND it's Saturday AND we have lemons, make 50 cups."
Risk Management = The safety rules. "Never use more than half our money on lemons" and "If we've lost money3 days in a row, take a break."
Execution = The robot's hands. It doesn't just dump all50 cups at once—it puts out10 cups at a time so they stay cold and fresh.
Monitoring = The logbook. It writes down: "10AM: Made 10 cups. 10:15 AM: Sold 7 cups. Temperature rose to 32°C. Increased next batch to 12 cups."
If one part breaks (let's say the thermometer stops working), the whole robot doesn't crash—it just switches to a backup rule like "make the average amount for a Saturday."
- Data: The fuel
- Strategy: The brains
- Risk: The brakes
- Execution: The hands
- Monitoring: The eyes
Connections
- 6.1.01-Introduction-to-Algorithmic-Trading - Why we need systematic trading
- 6.1.03-Backtesting-Strategies - Testing strategies before live deployment
- 6.1.05-Order-Typesand-Execution - Deep dive into execution layer
- 6.2.01-Market-Microstructure - How exchanges process orders
- 6.3.01-Risk-Management-in-Algo-Trading - Advanced risk controls
#flashcards/stock-market
What are the 5 core components of a trading system? :: 1) Data Management Layer 2) Strategy Engine 3) Risk Management Module 4) Execution Engine 5) Monitoring & Logging
Why must data management be a separate layer?
What is the Kelly Criterion formula for position sizing?
What is VWAP and why use it for execution?
Name3 pre-trade risk checks.
What is slippage in execution?
Why separate Strategy Engine from Execution Engine?
What causes the backtest-live performance gap?
What should monitoring systems alert on?
What is the critical timing constraint for a trading system?
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Trading systemek automated machine hai jo bina human intervention ke apne rules follow karke trades execute karta hai. Socho ki tum ek restaurant chalate ho—har kaam ke liye alag department hona chahiye: ek inventory check kare (data layer), ek recipes decide kare (strategy engine), ek budget dekhe kizyada kharcha toh nahi ho raha (risk management), aur ek actual cooking kare aur serve kare (execution