Implementing Risk Management Techniques
Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.
Risk Before Speed
Why this lesson matters
In Developing Trading Algorithms, you built signals that turn market data into buy/sell decisions. That is exciting — but a fast strategy without risk controls is just a fast way to make mistakes.
Risk management is the layer that asks:
- Should this order be allowed?
- How large can it be?
- What if market data is stale or abnormal?
- When should the system stop trading?
In C++, this layer must be both safe and low-latency. The goal is not to calculate every possible financial risk metric. The goal is to enforce clear guardrails in milliseconds before an order leaves your application.

Risk Gate Model
The risk manager is a gate
Your strategy may say: BUY 500 shares.
The risk manager may respond:
- ✅ Allow it unchanged
- ✂️ Reduce the quantity
- ❌ Reject it
- 🛑 Stop all trading
A practical risk gate sits after the strategy and before order sending:
Market data -> Strategy -> Risk Manager -> Order Sender
For an intermediate C++ trading application, start with simple deterministic checks:
- Max order quantity
- Max position per symbol
- Max notional exposure
- Price sanity check
- Daily loss limit
- Kill switch
These checks are valuable because they are fast, understandable, and easy to test.
Basic Risk Checks
A minimal C++ risk manager
This example checks an order before it is accepted. It uses simple in-memory state so each check is quick.
Notice the important habit: the strategy creates an order request, but the risk manager owns the final permission decision.
```cpp
#include <iostream>
#include <string>
#include <unordered_map>
#include <cmath>
struct Order {
std::string symbol;
int quantity; // positive for buy, negative for sell
double price;
};
struct RiskLimits {
int maxOrderQty = 1000;
int maxPosition = 2000;
double maxNotional = 100000.0;
double maxPriceDeviationPct = 5.0;
};
class RiskManager {
public:
RiskManager(RiskLimits limits) : limits_(limits) {}
void setPosition(const std::string& symbol, int position) {
positions_[symbol] = position;
}
bool approve(const Order& order, double referencePrice) const {
if (order.quantity == 0) {
std::cout << "Rejected: zero quantity\n";
return false;
}
if (std::abs(order.quantity) > limits_.maxOrderQty) {
std::cout << "Rejected: order quantity too large\n";
return false;
}
int currentPosition = 0;
auto it = positions_.find(order.symbol);
if (it != positions_.end()) currentPosition = it->second;
int projectedPosition = currentPosition + order.quantity;
if (std::abs(projectedPosition) > limits_.maxPosition) {
std::cout << "Rejected: projected position limit exceeded\n";
return false;
}
double notional = std::abs(order.quantity * order.price);
if (notional > limits_.maxNotional) {
std::cout << "Rejected: notional limit exceeded\n";
return false;
}
double deviationPct = std::abs(order.price - referencePrice) / referencePrice * 100.0;
if (deviationPct > limits_.maxPriceDeviationPct) {
std::cout << "Rejected: price too far from reference\n";
return false;
}
std::cout << "Approved: order passed risk checks\n";
return true;
}
private:
RiskLimits limits_;
std::unordered_map<std::string, int> positions_;
};
int main() {
RiskLimits limits;
RiskManager risk(limits);
risk.setPosition("AAPL", 1500);
Order order{"AAPL", 700, 101.0};
double lastFairPrice = 100.0;
bool ok = risk.approve(order, lastFairPrice);
std::cout << "Final decision: " << (ok ? "SEND" : "BLOCK") << "\n";
return 0;
}
```
Gate Placement Quiz
Where should a basic pre-trade risk manager sit in the order flow?
Position Sizing
Risk is not only rejection
A good risk layer does not merely say yes or no. It can also calculate a safer order size.
A simple sizing rule is:
shares = money_at_risk / risk_per_share
Example:
- Account capital: ₹1,000,000
- Risk per trade: 1%
- Entry price: ₹500
- Stop price: ₹490
- Risk per share: ₹10
- Money at risk: ₹10,000
- Position size: 1,000 shares
This does not guarantee profit. It simply limits the planned loss if the stop level is hit.

Sizing Function
Convert risk budget into quantity
This standalone example calculates the maximum quantity based on account size, risk percentage, entry price, and stop price.
In a real system, this sizing result should still pass through the risk gate from the previous screen.
```cpp
#include <iostream>
#include <cmath>
#include <algorithm>
int calculatePositionSize(double accountCapital,
double riskPercent,
double entryPrice,
double stopPrice,
int maxOrderQty) {
double riskBudget = accountCapital * (riskPercent / 100.0);
double riskPerShare = std::abs(entryPrice - stopPrice);
if (riskPerShare <= 0.0) {
return 0;
}
int rawQty = static_cast<int>(riskBudget / riskPerShare);
return std::min(rawQty, maxOrderQty);
}
int main() {
double accountCapital = 1000000.0;
double riskPercent = 1.0;
double entryPrice = 500.0;
double stopPrice = 490.0;
int maxOrderQty = 800;
int qty = calculatePositionSize(accountCapital, riskPercent, entryPrice, stopPrice, maxOrderQty);
std::cout << "Calculated position size: " << qty << " shares\n";
std::cout << "Reason: risk budget and max order quantity were both considered\n";
return 0;
}
```
Paper Trading Risk
True or false: In paper trading, risk checks can be skipped because no real money is being used.
Fail Safe Blank
Complete the safety rule:
If the risk state is unavailable, stale, or corrupted, the trading system should _____.
Loss Limits
Add a daily loss guard
Pre-trade checks handle individual orders. You also need account-level protection.
A common rule is:
if realized_pnl + unrealized_pnl <= -daily_loss_limit:
block new orders
This protects the system from continuing to trade during abnormal behavior, broken data, or a market regime your algorithm is not handling well.
```cpp
#include <iostream>
class DailyLossGuard {
public:
explicit DailyLossGuard(double maxDailyLoss)
: maxDailyLoss_(maxDailyLoss), killSwitch_(false) {}
void updatePnl(double realizedPnl, double unrealizedPnl) {
double totalPnl = realizedPnl + unrealizedPnl;
if (totalPnl <= -maxDailyLoss_) {
killSwitch_ = true;
std::cout << "Kill switch activated. Total PnL: " << totalPnl << "\n";
} else {
std::cout << "Trading allowed. Total PnL: " << totalPnl << "\n";
}
}
bool canTrade() const {
return !killSwitch_;
}
private:
double maxDailyLoss_;
bool killSwitch_;
};
int main() {
DailyLossGuard guard(5000.0);
guard.updatePnl(-1200.0, -800.0);
std::cout << "Can trade? " << (guard.canTrade() ? "yes" : "no") << "\n";
guard.updatePnl(-3000.0, -2500.0);
std::cout << "Can trade? " << (guard.canTrade() ? "yes" : "no") << "\n";
return 0;
}
```
Risk Checklist
Your risk layer checklist
Before moving into Backtesting and Strategy Optimization, your trading application should have a clear risk boundary.
Use this checklist:
- Strategy cannot send orders directly
- Every order passes through a risk manager
- Quantity, position, notional, and price sanity are checked
- Position sizing is based on planned risk, not emotion
- Daily loss limits can block new trades
- Missing or stale risk state causes the system to fail closed
- Rejections are logged with clear reasons
The main idea: speed matters in trading, but controlled speed matters more. A C++ system should be fast enough to act — and disciplined enough to stop.
