Appearance
CRTP
The Curiously Recurring Template Pattern (CRTP) is a powerful C++ template technique that enables static polymorphism and compile-time interface enforcement. It's particularly valuable in low-latecy systems where runtime polymorphism overhead is unacceptable.
What is CRTP?
CRTP is a pattern where a base class template takes the derived class as a template parameter, allowing the base class to access the derived class's interface at compile time.
cpp
template<typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
void common_operation() {
// Common functionality
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() {
// Derived-specific implementation
}
};Key Benefits of CRTP
1. Static Polymorphism
Unlike virtual functions, CRTP provides polymorphism without runtime overhead:
cpp
template<typename Derived>
class Shape {
public:
double area() const {
return static_cast<const Derived*>(this)->area_impl();
}
double perimeter() const {
return static_cast<const Derived*>(this)->perimeter_impl();
}
};
class Circle : public Shape<Circle> {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area_impl() const {
return 3.14159 * radius * radius;
}
double perimeter_impl() const {
return 2 * 3.14159 * radius;
}
};
class Rectangle : public Shape<Rectangle> {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area_impl() const {
return width * height;
}
double perimeter_impl() const {
return 2 * (width + height);
}
};2. Compile-Time Interface Enforcement
CRTP ensures that derived classes implement required methods:
cpp
template<typename Derived>
class MarketDataHandler {
public:
void process_data(const MarketData& data) {
// Common processing logic
static_cast<Derived*>(this)->validate_data(data);
static_cast<Derived*>(this)->transform_data(data);
static_cast<Derived*>(this)->store_data(data);
}
private:
// Force derived classes to implement these
void validate_data(const MarketData& data) = delete;
void transform_data(const MarketData& data) = delete;
void store_data(const MarketData& data) = delete;
};
class EquityHandler : public MarketDataHandler<EquityHandler> {
public:
void validate_data(const MarketData& data) {
// Equity-specific validation
}
void transform_data(const MarketData& data) {
// Equity-specific transformation
}
void store_data(const MarketData& data) {
// Equity-specific storage
}
};Example Applications of CRTP
cpp
template<typename Strategy>
class TradingEngine {
private:
Strategy strategy;
public:
void execute_order(const Order& order) {
strategy.execute(order);
}
void cancel_order(OrderId id) {
strategy.cancel(id);
}
};
class AggressiveStrategy {
public:
void execute(const Order& order) {
// Aggressive execution logic
}
void cancel(OrderId id) {
// Aggressive cancellation logic
}
};
class ConservativeStrategy {
public:
void execute(const Order& order) {
// Conservative execution logic
}
void cancel(OrderId id) {
// Conservative cancellation logic
}
};
// Usage
TradingEngine<AggressiveStrategy> aggressive_engine;
TradingEngine<ConservativeStrategy> conservative_engine;Limitations and Considerations
1. No Runtime Polymorphism
CRTP provides static polymorphism, so you cannot change behavior at runtime:
cpp
// This doesn't work with CRTP
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5.0));
shapes.push_back(std::make_unique<Rectangle>(3.0, 4.0));
// Must use templates
std::vector<Circle> circles;
std::vector<Rectangle> rectangles;2. Template Code Bloat
Each derived class creates a new instantiation of the base class template:
cpp
// Creates separate code for each derived class
Circle circle(5.0); // Shape<Circle> instantiation
Rectangle rect(3.0, 4.0); // Shape<Rectangle> instantiation3. Compilation Time
CRTP can increase compilation time due to template instantiation overhead.
Summary
CRTP is a powerful technique that provides:
- Zero-cost abstraction for performance-critical code
- Compile-time interface enforcement without runtime overhead
- Static polymorphism for HFT systems
- Type safety at compile time
- Flexible design patterns for complex systems
Understanding CRTP is essential for writing high-performance C++ code, especially in domains like HFT where every nanosecond matters.
Your Task
Implement a CRTP-based trading strategy framework.
- Create a base class template 'TradingStrategy' that takes the derived strategy as a template parameter.
- The base class should provide common functionality like position sizing and risk management.
- Each derived strategy must implement three methods: calculate_position() (returns position size), should_trade() (returns bool), and get_risk_level() (returns risk level enum). Implement three derived strategies: MomentumStrategy, MeanReversionStrategy, and ArbitrageStrategy, each with their own trading logic.
Create the base class template 'TradingStrategy' and provides common functionality like position sizing and risk management.
cpp
Medium,
High
};
// Market data structure
struct MarketData {
double price;
double volume;
double momentum;
double mean_price;
double spread;
MarketData(double p, double v, double m, double mp, double s)
: price(p), volume(v), momentum(m), mean_price(mp), spread(s) {}
};
// TODO: Implement CRTP-based trading strategy framework
//
// Requirements:
// 1. Create a base class template 'TradingStrategy' that takes the derived strategy as a template
parameter
// 2. The base class should provide common functionality like position sizing and risk management
// 3. Each derived strategy must implement three methods:
// - calculate_position() (returns position size as int)
// - should_trade() (returns bool)
// - get_risk_level() (returns RiskLevel enum)
// 4. Implement three derived strategies:
// - MomentumStrategy: trades based on price momentum
// - MeanReversionStrategy: trades based on mean reversion
// - ArbitrageStrategy: trades based on price spreads
//
// This demonstrates:
// - CRTP pattern for static polymorphism
// - Compile-time interface enforcement
// - Zero-cost abstraction for trading systems
// - Template class design for strategy patterns
// Base class template using CRTP
template<typename Derived>
class TradingStrategy {
public:
// Common functionality provided by base class
void execute_strategy(const MarketData& data) {
// TODO: calculate position and risk level from derived class
// TODO: apply position sizing and risk management using obtained values
}
// Common methods that derived classes can use
void apply_position_sizing(int position) {
std::cout << "Position: " << position << std::endl;
}
void apply_risk_management(RiskLevel risk) {
std::cout << "Risk level: ";
switch (risk) {
case RiskLevel::Low: std::cout << "Low"; break;
case RiskLevel::Medium: std::cout << "Medium"; break;
case RiskLevel::High: std::cout << "High"; break;
}
std::cout << std::endl;
}
private:
// TODO: Force derived classes to implement these methods
};
// TODO: Implement MomentumStrategy
// This strategy should:
// - calculate_position(): Return positive position if momentum > 0, negative if < 0
// - should_trade(): Return true if momentum is significant (abs > 0.1)
// - get_risk_level(): Return Medium risk level
class MomentumStrategy : public TradingStrategy<MomentumStrategy> {
private:
MarketData current_data;
public:
MomentumStrategy(const MarketData& data) : current_data(data) {}
// TODO: Implement these three methods
int calculate_position() {
// Your implementation here
}
bool should_trade() {
// Your implementation here
}
RiskLevel get_risk_level() {
// Your implementation here
}
};
// TODO: Implement MeanReversionStrategy
// This strategy should:
// - calculate_position(): Return negative position if price > mean_price, positive if < mean_price
// - should_trade(): Return true if price deviation from mean is significant (abs > 0.05)
// - get_risk_level(): Return Low risk level
class MeanReversionStrategy : public TradingStrategy<MeanReversionStrategy> {
private:
MarketData current_data;
public:
MeanReversionStrategy(const MarketData& data) : current_data(data) {}
// TODO: Implement these three methods
int calculate_position() {
// Your implementation here
}
bool should_trade() {
// Your implementation here
}
RiskLevel get_risk_level() {
// Your implementation here
}
};
// TODO: Implement ArbitrageStrategy
// This strategy should:
// - calculate_position(): Return large position (200) if spread is significant
// - should_trade(): Return true if spread is significant (abs > 0.02)
// - get_risk_level(): Return High risk level
class ArbitrageStrategy : public TradingStrategy<ArbitrageStrategy> {
private:
MarketData current_data;
public:
ArbitrageStrategy(const MarketData& data) : current_data(data) {}
// TODO: Implement these three methods
int calculate_position() {
// Your implementation here
}
bool should_trade() {
// Risk level enum
enum class RiskLevel {
Low,
#include <string>
#include <iostream>
#include <vector>