6.1.2Algorithmic & Quant Trading

Learn the components of a trading system

2,878 words13 min readdifficulty · medium

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 150150 → 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 * signal

Why 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:

  1. Market order: Immediate fill at current price

    • Use case: Small size, urgent entry (e.g., breaking news)
    • Cost: Pays the spread + potential slippage
  2. Limit order: Only fill at specified price or better

    • Use case: Large size, patient entry
    • Risk: May not fill (adverse selection—price moves away)
  3. VWAP (Volume-Weighted Average Price): VWAP=(Pricei×Volumei)Volumei\text{VWAP} = \frac{\sum (\text{Price}_i \times \text{Volume}_i)}{\sum \text{Volume}_i} Slice order across the day to match historical volume distribution.

    Why? Minimizes market impact by "hiding" in natural flow.

  4. 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

Market DatacleanData LayerfeedStrategy Engine\boxed{\text{Market Data}} \xrightarrow{\text{clean}} \boxed{\text{Data Layer}} \xrightarrow{\text{feed}} \boxed{\text{Strategy Engine}} Strategy EnginesignalRisk ManagerapproveExecution Engine\boxed{\text{Strategy Engine}} \xrightarrow{\text{signal}} \boxed{\text{Risk Manager}} \xrightarrow{\text{approve}} \boxed{\text{Execution Engine}} Execution EngineorderBroker APIfillMonitor/Logger\boxed{\text{Execution Engine}} \xrightarrow{\text{order}} \boxed{\text{Broker API}} \xrightarrow{\text{fill}} \boxed{\text{Monitor/Logger}}

Critical timing constraint:

tdata→decision+tdecision→order+torder→fill<talpha decayt_{\text{data→decision}} + t_{\text{decision→order}} + t_{\text{order→fill}} < t_{\text{alpha decay}}

where talpha decayt_{\text{alpha decay}} = 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:

  1. Look-ahead bias: Using future data (e.g., daily close at 9:30 AM)
  2. Survivorship bias: Backtest on stocks still listed (ignores bankruptcies)
  3. Overfitting: Strategy tuned to historical noise, not real patterns
  4. 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?
To handle dirty market data (gaps, spikes, delays), synchronize multiple timeframes/assets, and ensure backtests use identical data format as live trading—preventing backtest-live discrepancies.
What is the Kelly Criterion formula for position sizing?
f=p×bqbf^* = \frac{p \times b - q}{b} where pp = win probability, q=1pq =1-p, bb = win/loss ratio. It calculates the optimal fraction of capital to risk per trade. Most traders use half-Kelly for safety.
What is VWAP and why use it for execution?
Volume-Weighted Average Price—slices a large order across the day to match historical volume distribution. Minimizes market impact by "hiding" in natural trading flow, preventing price movement against you.
Name3 pre-trade risk checks.
1) Position limit (max % per symbol), 2) Portfolio delta (directional exposure), 3) VaR (value-at-risk under X% confidence), 4) Drawdown check (halt if current loss exceeds threshold).
What is slippage in execution?
Slippage=Average fill priceDecision price\text{Slippage} = \text{Average fill price} - \text{Decision price} The difference between the price when you decided to trade vs. the actual execution price. Caused by market impact, latency, and spread crossing.
Why separate Strategy Engine from Execution Engine?
Strategy decides "what to trade" (signals, sizing); execution decides "how to trade" (order type, slicing, venue). Separation allows: A/B testing multiple strategies, optimizing execution independently, and backtesting without live infrastructure.
What causes the backtest-live performance gap?
1) Look-ahead bias (using future data), 2) Survivorship bias (ignoring delisted stocks), 3) Overfitting (tuning to noise), 4) Ignoring transaction costs (slippage, fees, spread), 5) Assuming perfect fills.
What should monitoring systems alert on?
1) Performance: drawdown breaches, Sharpe ratio drops, 2) System health: high latency (>100ms), low fill rates, data gaps, 3) Anomalies: position drift (actual ≠ expected), sudden volatility, order rejections.
What is the critical timing constraint for a trading system?
tdata→decision+tdecision→order+torder→fill<talpha decayt_{\text{data→decision}} + t_{\text{decision→order}} + t_{\text{order→fill}} < t_{\text{alpha decay}} where alpha decay = time before signal becomes public. Total system latency must be faster than signal lifespan (microseconds for HFT, minutes for daily strategies).

Concept Map

contains

contains

contains

contains

contains

feeds

sends orders to

performs

serves

produces

requires

observes

guards

Trading System

Data Management Layer

Strategy Engine

Execution

Monitoring

Risk Management

Clean and Validate Ticks

Tick OHLCV OrderBook Ref

Signal Generation

Identical Pipeline Backtest and Live

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

Test yourself — Algorithmic & Quant Trading