Skip to content

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 = False

Naming Conventions

While any valid Python variable name works, these conventions improve code readability:

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 = True

Variable 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 = True

Access Patterns

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_SYMBOLS

Error 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 message

Error 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!

Test your trading strategies risk-free with professional backtesting.