Quant Trading Systems: Architecture & Infrastructure › Key Parameters

Michael BrenndoerferJanuary 21, 20264 min read
Quantitative FinanceAll chapters
Part 15 of 15Trading Systems and Production Deployment
Chapter 77Quant Trading Systems: Architecture & Infrastructure › Key Parameters
Reading section 6 of 14 · Key Parameters · 4 minSection 6 of 14 · 4 min

Book contents

Section 6 of 14 · 4 min read

Quant Trading Systems: Architecture & Infrastructure › Key ParametersOne section · Sections in this chapter6 / 14 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

The key parameters for the Pre-Trade Risk Checker define the boundaries of acceptable risk:

  • max_position_size Maximum notional value allowed for a single position. This limits concentration risk and ensures that no single position can cause catastrophic losses.
  • max_order_size Maximum notional value allowed for a single order, preventing accidental large trades. A "fat finger" error that submits an order for 10,000 shares instead of 100 shares is caught by this limit.
  • max_concentration Maximum percentage of portfolio value allowed in a single asset. Even if position size limits are respected, concentration limits ensure the portfolio remains diversified.
  • max_daily_loss P&L threshold that triggers a suspension of trading. When losses accumulate beyond this level, the system stops trading to prevent further damage and allow human review.

Real-Time Portfolio Risk Monitoring

Beyond individual order checks, the system must continuously monitor aggregate portfolio risk. This includes calculating metrics we covered in Part V, such as Value at Risk and expected shortfall. These metrics capture portfolio-wide risk that individual order checks cannot.

For example, a portfolio might consist entirely of positions that individually pass all risk checks, yet be dangerously exposed to a single risk factor. Real-time monitoring at the portfolio level catches these aggregate risks.

In[20]:
Code
from scipy import stats


class PortfolioRiskMonitor:
    """Real-time monitoring of portfolio risk metrics."""

    def __init__(self, confidence_level: float = 0.95):
        self.confidence_level = confidence_level
        self.returns_history: list[float] = []
        self.peak_value: float = 0
        self.current_drawdown: float = 0

    def update(self, portfolio_value: float, portfolio_return: float):
        """Update risk metrics with new portfolio state."""
        self.returns_history.append(portfolio_return)

        # Update peak and drawdown
        if portfolio_value > self.peak_value:
            self.peak_value = portfolio_value
        self.current_drawdown = (
            self.peak_value - portfolio_value
        ) / self.peak_value

    def calculate_var(self, horizon_days: int = 1) -> float:
        """Calculate parametric VaR assuming normal returns."""
        if len(self.returns_history) < 20:
            return float("inf")  # Not enough data

        returns = np.array(self.returns_history)
        mu = returns.mean() * horizon_days
        sigma = returns.std() * np.sqrt(horizon_days)

        var = -(mu + sigma * stats.norm.ppf(1 - self.confidence_level))
        return var

    def calculate_expected_shortfall(self, horizon_days: int = 1) -> float:
        """Calculate Expected Shortfall (CVaR)."""
        if len(self.returns_history) < 20:
            return float("inf")

        returns = np.array(self.returns_history)
        mu = returns.mean() * horizon_days
        sigma = returns.std() * np.sqrt(horizon_days)

        alpha = 1 - self.confidence_level
        var = -(mu + sigma * stats.norm.ppf(1 - self.confidence_level))
        es = -mu + sigma * stats.norm.pdf(stats.norm.ppf(alpha)) / alpha
        return es

    def get_risk_summary(self) -> dict:
        """Get current risk metric summary."""
        return {
            "current_drawdown": self.current_drawdown,
            "var_1d_95": self.calculate_var(1),
            "es_1d_95": self.calculate_expected_shortfall(1),
            "realized_vol": np.std(self.returns_history) * np.sqrt(252)
            if len(self.returns_history) > 1
            else 0,
        }
In[21]:
Code
# Simulate portfolio monitoring
np.random.seed(42)
monitor = PortfolioRiskMonitor(confidence_level=0.95)

# Simulate 60 days of returns
initial_value = 1000000
portfolio_value = initial_value

for day in range(60):
    # Simulate daily return
    daily_return = np.random.normal(0.0005, 0.015)  # ~24% annual vol
    portfolio_value *= 1 + daily_return
    monitor.update(portfolio_value, daily_return)

risk_summary = monitor.get_risk_summary()
Out[22]:
Console
Portfolio Risk Summary (60-day simulation):
---------------------------------------------
Current Drawdown:         16.99%
1-Day VaR (95%):           2.40%
1-Day ES (95%):            2.97%
Realized Volatility:      21.45%

Final Portfolio Value: $891,555
Out[23]:
Visualization
Histogram of simulated daily portfolio returns with an orange dashed vertical line marking VaR at the 5th percentile and a red solid vertical line marking Expected Shortfall further in the left tail.
Distribution of simulated daily returns highlighting Value at Risk (VaR) and Expected Shortfall (ES) at the 95% confidence level. These metrics summarize the threshold and average severity of downside tail outcomes.