Strategy trait that allows you to implement custom trading strategies with full control over market events, order management, and portfolio state.
Overview
Custom strategies in GlowBack:- Implement the
Strategytrait with lifecycle methods - Receive market and order events in real-time
- Access portfolio state and market data through
StrategyContext - Return actions (place orders, cancel orders, log messages)
- Maintain internal state and parameters
Strategy lifecycle
A strategy goes through the following phases:- Initialization - Configure parameters and set up state
- Market events - Process incoming market data (bars, ticks, quotes)
- Order events - Handle order fills, cancellations, and rejections
- Day end - Run end-of-day calculations and rebalancing
- Shutdown - Clean up and finalize metrics
use gb_types::strategy::{Strategy, StrategyConfig, StrategyContext, StrategyAction, StrategyMetrics};
use gb_types::market::{MarketEvent, Symbol};
use gb_types::orders::{Order, OrderEvent, Side};
use rust_decimal::Decimal;
#[derive(Debug, Clone)]
pub struct MyCustomStrategy {
config: StrategyConfig,
initialized: bool,
// Your custom state
lookback_period: usize,
threshold: Decimal,
}
impl MyCustomStrategy {
pub fn new(lookback_period: usize, threshold: f64) -> Self {
let mut config = StrategyConfig::new(
"my_strategy".to_string(),
"My Custom Strategy".to_string()
);
config.set_parameter("lookback_period", lookback_period);
config.set_parameter("threshold", threshold);
Self {
config,
initialized: false,
lookback_period,
threshold: Decimal::from_f64_retain(threshold).unwrap_or(Decimal::ZERO),
}
}
}
impl Strategy for MyCustomStrategy {
fn initialize(&mut self, config: &StrategyConfig) -> Result<(), String> {
self.config = config.clone();
// Load parameters from config
self.lookback_period = self.config
.get_parameter("lookback_period")
.unwrap_or(20);
self.threshold = self.config
.get_parameter::<f64>("threshold")
.map(Decimal::from_f64_retain)
.flatten()
.unwrap_or(Decimal::ZERO);
self.initialized = true;
Ok(())
}
fn on_market_event(
&mut self,
event: &MarketEvent,
context: &StrategyContext,
) -> Result<Vec<StrategyAction>, String> {
if !self.initialized {
return Ok(vec![]);
}
// Your trading logic here
let mut actions = Vec::new();
// Example: Access market data
let symbol = event.symbol();
if let Some(buffer) = context.get_market_data(symbol) {
let bars = buffer.get_bars(self.lookback_period);
// Analyze bars and generate signals
}
Ok(actions)
}
fn on_order_event(
&mut self,
event: &OrderEvent,
context: &StrategyContext,
) -> Result<Vec<StrategyAction>, String> {
// Handle order fills, cancellations, etc.
Ok(vec![])
}
fn on_day_end(
&mut self,
context: &StrategyContext,
) -> Result<Vec<StrategyAction>, String> {
// Run end-of-day rebalancing or calculations
Ok(vec![])
}
fn on_stop(
&mut self,
context: &StrategyContext,
) -> Result<Vec<StrategyAction>, String> {
// Clean up and finalize
Ok(vec![])
}
fn get_config(&self) -> &StrategyConfig {
&self.config
}
fn get_metrics(&self) -> StrategyMetrics {
StrategyMetrics::new(self.config.strategy_id.clone())
}
}
// Get current position
if let Some(position) = context.get_position(symbol) {
let quantity = position.quantity;
let avg_price = position.average_price;
}
// Get current price
if let Some(price) = context.get_current_price(symbol) {
// Use price for calculations
}
// Get available cash
let cash = context.get_available_cash();
let portfolio_value = context.get_portfolio_value();
// Get market data buffer
if let Some(buffer) = context.get_market_data(symbol) {
let recent_bars = buffer.get_bars(20);
let latest_bar = buffer.get_latest_bar();
}
let mut actions = Vec::new();
// Market order
let order = Order::market_order(
symbol.clone(),
Side::Buy,
quantity,
self.config.strategy_id.clone()
);
actions.push(StrategyAction::PlaceOrder(order));
// Limit order
let limit_order = Order::limit_order(
symbol.clone(),
Side::Sell,
quantity,
limit_price,
self.config.strategy_id.clone()
);
actions.push(StrategyAction::PlaceOrder(limit_order));
// Cancel order
actions.push(StrategyAction::CancelOrder { order_id });
Ok(actions)
Configuration and parameters
Strategy configuration
Configure your strategy withStrategyConfig:
Reading parameters
Access parameters in your strategy:Best practices
State management
- Keep all mutable state in your strategy struct
- Use the
initializedflag to prevent logic before initialization - Reset state appropriately in
on_stopif reusing strategies
Error handling
- Return descriptive error messages as
Result<_, String> - Handle missing data gracefully (None values from context)
- Validate parameters in
initialize
Performance
- Avoid expensive calculations on every market event
- Use
on_day_endfor heavy computations - Cache calculated indicators rather than recomputing
Testing
- Write unit tests for your strategy logic
- Test with synthetic market data
- Verify parameter handling and edge cases
Next steps
Strategy interface
Complete reference for the Strategy trait
Examples
Real-world strategy implementations