Appearance
Strategy Global Variables
The backtesting engine provides full support for strategy-level global variables, allowing you to define configuration parameters, constants, and settings directly in your strategy code.
Overview
Strategy global variables are defined at the top level of your strategy code (outside of any functions) and are accessible within all lifecycle functions (on_strategy_start, on_bar_close, etc.).
python
# Define globals at the top of your strategy
g_fast_period = 12
g_slow_period = 26
DEFAULT_SYMBOLS = ["AAPL", "GOOGL"]
def on_strategy_start(portfolio, state):
# Globals are accessible here
print(f"Using SMA periods: {g_fast_period}/{g_slow_period}")
def on_bar_close(data_contexts, portfolio, state):
# And here too!
for symbol in DEFAULT_SYMBOLS:
sma_fast = data_contexts[symbol].sma(g_fast_period)Supported Variable Types
All Python data types are supported as strategy globals:
python
# Numbers
g_fast_period = 20
g_risk_percentage = 0.02
g_max_drawdown = 0.15
# Strings
g_primary_symbol = "AAPL"
g_strategy_name = "SMA Crossover"
# Lists and Tuples
g_symbols = ["AAPL", "GOOGL", "MSFT"]
g_sma_periods = (20, 50, 200)
# Dictionaries
g_position_sizes = {
"AAPL": 100,
"GOOGL": 50,
"MSFT": 75
}
# Booleans
g_trading_enabled = True
g_use_stop_loss = FalseNaming Conventions
While any valid Python variable name works, these conventions improve code readability:
Recommended Patterns
python
# Prefix with 'g_' for global parameters
g_fast_period = 20
g_slow_period = 50
g_position_size = 100
# Use 'DEFAULT_' for fallback values
DEFAULT_SYMBOLS = ["AAPL"]
DEFAULT_TIMEFRAME = "1min"
# ALL_CAPS for true constants
MAX_POSITIONS = 5
MIN_PRICE = 1.0
COMMISSION_RATE = 0.001
# lowercase_with_underscores for settings
trading_enabled = True
use_trailing_stops = False
debug_mode = TrueVariable Name Examples
python
# Technical Analysis Parameters
g_rsi_period = 14
g_bollinger_period = 20
g_macd_fast = 12
g_macd_slow = 26
# Risk Management
g_max_risk_per_trade = 0.02 # 2% per trade
g_max_portfolio_risk = 0.10 # 10% total portfolio
g_stop_loss_pct = 0.05 # 5% stop loss
# Position Sizing
g_base_position_size = 100
g_max_position_value = 10000
g_position_sizing_method = "fixed"
# Strategy Behavior
g_allow_short_selling = False
g_rebalance_frequency = "daily"
g_market_hours_only = TrueAccess Patterns
Direct Global Access (Recommended)
The simplest approach is to use globals directly:
python
g_fast_sma = 20
g_slow_sma = 50
def on_bar_close(data_contexts, portfolio, state):
"""Direct access to globals - clean and simple"""
data = data_contexts['AAPL']
fast_sma = data.sma(g_fast_sma) # Direct global access
slow_sma = data.sma(g_slow_sma) # Direct global access
if fast_sma > slow_sma:
return {"symbol": "AAPL", "action": "buy", "quantity": 100}
elif fast_sma < slow_sma:
return {"symbol": "AAPL", "action": "sell", "quantity": "all"}Hybrid: Globals + State Overrides
For maximum flexibility, combine globals with state parameter access:
python
g_default_symbols = ["AAPL", "GOOGL"]
g_default_period = 20
def on_strategy_start(portfolio, state):
"""Allow API parameters to override strategy defaults"""
# Use API parameter if provided, otherwise use global default
symbols = state.get('symbols', g_default_symbols)
period = state.get('sma_period', g_default_period)
print(f"Strategy running on {symbols} with period {period}")
# Store final configuration in state
state['active_symbols'] = symbols
state['active_period'] = period
def on_bar_close(data_contexts, portfolio, state):
"""Use either approach based on your needs"""
# Option 1: Direct global (if no API overrides expected)
fast_sma = data_contexts['AAPL'].sma(g_default_period)
# Option 2: From state (if API overrides are expected)
period = state['active_period']
fast_sma = data_contexts['AAPL'].sma(period)Complex Configuration Examples
Multi-Asset Strategy
python
# Symbol-specific configuration
g_symbols_config = {
"AAPL": {
"position_size": 100,
"sma_fast": 10,
"sma_slow": 30,
"risk_multiplier": 1.0
},
"GOOGL": {
"position_size": 50,
"sma_fast": 15,
"sma_slow": 45,
"risk_multiplier": 1.2
}
}
def on_bar_close(data_contexts, portfolio, state):
"""Use symbol-specific configuration"""
for symbol, config in g_symbols_config.items():
if symbol not in data_contexts:
continue
data = data_contexts[symbol]
fast_sma = data.sma(config["sma_fast"])
slow_sma = data.sma(config["sma_slow"])
if fast_sma > slow_sma:
quantity = config["position_size"] * config["risk_multiplier"]
return {"symbol": symbol, "action": "buy", "quantity": int(quantity)}Time-Based Configuration
python
# Different settings for different market conditions
g_market_regimes = {
"trending": {
"sma_fast": 10,
"sma_slow": 30,
"position_size": 150
},
"sideways": {
"sma_fast": 5,
"sma_slow": 15,
"position_size": 75
}
}
g_current_regime = "trending" # Default regime
def on_strategy_start(portfolio, state):
"""Initialize regime detection"""
state['market_regime'] = g_current_regime
print(f"Starting in {g_current_regime} market regime")
def on_bar_close(data_contexts, portfolio, state):
"""Use regime-specific parameters"""
regime = state.get('market_regime', g_current_regime)
config = g_market_regimes[regime]
data = data_contexts['AAPL']
fast_sma = data.sma(config["sma_fast"])
slow_sma = data.sma(config["sma_slow"])
# Trading logic using regime-specific config
if fast_sma > slow_sma:
return {
"symbol": "AAPL",
"action": "buy",
"quantity": config["position_size"]
}Best Practices
✅ Do This
python
# Clear, descriptive names
g_sma_fast_period = 20
g_sma_slow_period = 50
g_max_position_size = 1000
# Group related configuration
g_risk_management = {
"max_drawdown": 0.15,
"stop_loss": 0.05,
"position_limit": 0.10
}
# Use meaningful defaults
g_default_symbols = ["AAPL", "GOOGL", "MSFT"]
g_trading_hours = {"start": "09:30", "end": "16:00"}❌ Avoid This
python
# Unclear names
a = 20
b = 50
x = 1000
# Magic numbers buried in code
def on_bar_close(data_contexts, portfolio, state):
sma = data_contexts['AAPL'].sma(20) # What is 20? Use a global!
# Hardcoded values
symbols = ["AAPL"] # Should be g_symbols or DEFAULT_SYMBOLSError Handling
The engine provides helpful error messages for undefined variables:
python
def on_strategy_start(portfolio, state):
# If you reference an undefined global variable:
period = UNDEFINED_VARIABLE # This will give a clear error messageError Message Example:
Variable 'UNDEFINED_VARIABLE' is not defined. Make sure to define 'UNDEFINED_VARIABLE' as a global variable at the top of your strategy code, before any function definitions.Migration from State-Only Approach
If you're currently using only state variables, you can easily migrate:
Before (State-Only)
python
def on_strategy_start(portfolio, state):
state['fast_period'] = 20
state['slow_period'] = 50
def on_bar_close(data_contexts, portfolio, state):
fast_sma = data_contexts['AAPL'].sma(state['fast_period'])After (With Globals)
python
g_fast_period = 20
g_slow_period = 50
def on_strategy_start(portfolio, state):
# Globals are automatically available - no setup needed!
print(f"Using periods: {g_fast_period}/{g_slow_period}")
def on_bar_close(data_contexts, portfolio, state):
fast_sma = data_contexts['AAPL'].sma(g_fast_period) # Cleaner!Summary
Strategy global variables provide:
- Natural Python syntax for defining configuration
- Automatic availability in all lifecycle functions
- Clear separation between configuration and logic
- Easy maintenance and parameter adjustment
- Full compatibility with existing API parameter injection
Define your strategy parameters as globals for the cleanest, most maintainable code!