Skip to content

DataContext

The data_context objects provide access to market data and technical indicators for each symbol. Available in data_contexts dictionary in on_bar_close.

Structure

python
# Access data for specific symbols
aapl = data_contexts['AAPL']
googl = data_contexts['GOOGL']

# Iterate over all symbols
for symbol, data in data_contexts.items():
    price = data.close

OHLCV Properties

Price Data

  • open (float) - Opening price for current bar
  • high (float) - Highest price for current bar
  • low (float) - Lowest price for current bar
  • close (float) - Closing price for current bar
  • volume (int) - Trading volume for current bar
python
def on_bar_close(data_contexts, portfolio, state):
    aapl = data_contexts['AAPL']
    
    print(f"AAPL - O:{aapl.open} H:{aapl.high} L:{aapl.low} C:{aapl.close}")
    print(f"Volume: {aapl.volume:,}")

Technical Indicators

Moving Averages

sma(period: int) -> float

Simple Moving Average of closing prices.

python
# Basic moving average signals
aapl = data_contexts['AAPL']
sma_20 = aapl.sma(20)
sma_50 = aapl.sma(50)

# Always check for NaN before using
if sma_20 != sma_20:  # NaN check
    return None

# Golden cross strategy
if sma_20 > sma_50 and portfolio.position('AAPL') == 0:
    return {"symbol": "AAPL", "action": "buy", "quantity": 100}

ema(period: int) -> float

Exponential Moving Average of closing prices.

python
# EMA is more responsive to recent prices
ema_12 = aapl.ema(12)
ema_26 = aapl.ema(26)

# MACD-style crossover
if ema_12 > ema_26 and state.get('prev_ema_12', 0) <= state.get('prev_ema_26', 0):
    print("EMA bullish crossover detected")

Momentum Indicators

rsi(period: int = 14) -> float

Relative Strength Index (0-100 scale).

python
rsi = aapl.rsi(14)

# Classic RSI strategy
if rsi < 30:  # Oversold
    return {"symbol": "AAPL", "action": "buy", "quantity": 100}
elif rsi > 70:  # Overbought
    return {"symbol": "AAPL", "action": "sell", "quantity": "all"}

macd(fast: int = 12, slow: int = 26, signal: int = 9) -> tuple[float, float, float]

MACD line, Signal line, and Histogram.

python
macd_line, signal_line, histogram = aapl.macd()

# MACD crossover strategy
if macd_line > signal_line and histogram > 0:
    return {"symbol": "AAPL", "action": "buy", "quantity": 100}

stochastic(k_period: int = 14, d_period: int = 3) -> tuple[float, float]

Stochastic %K and %D values.

python
k_percent, d_percent = aapl.stochastic()

# Stochastic oversold/overbought
if k_percent < 20 and d_percent < 20:
    return {"symbol": "AAPL", "action": "buy", "quantity": 100}

Volatility Indicators

bollinger_bands(period: int = 20, std_dev: float = 2) -> tuple[float, float, float]

Upper band, Middle (SMA), Lower band.

python
upper, middle, lower = aapl.bollinger_bands()

# Bollinger Band squeeze strategy
if aapl.close < lower:  # Price below lower band
    return {"symbol": "AAPL", "action": "buy", "quantity": 100}
elif aapl.close > upper:  # Price above upper band
    return {"symbol": "AAPL", "action": "sell", "quantity": "all"}

atr(period: int = 14) -> float

Average True Range - measures volatility.

python
atr = aapl.atr(14)

# Use ATR for position sizing
volatility_multiplier = max(0.5, min(2.0, 0.02 / (atr / aapl.close)))
base_quantity = int(100 * volatility_multiplier)

Historical Data Access

history(field: str, periods: int) -> list[float]

Get historical data for calculations.

python
# Get last 20 closing prices
closes = aapl.history('close', 20)

# Calculate custom indicator
if len(closes) >= 20:
    custom_sma = sum(closes) / len(closes)
    
# Available fields: 'open', 'high', 'low', 'close', 'volume'
highs = aapl.history('high', 10)
volumes = aapl.history('volume', 5)

Usage Examples

Multi-Timeframe Analysis

python
def on_bar_close(data_contexts, portfolio, state):
    """Use multiple timeframes for better signals"""
    aapl = data_contexts['AAPL']
    
    # Short-term trend
    sma_5 = aapl.sma(5)
    sma_20 = aapl.sma(20)
    
    # Long-term trend
    sma_50 = aapl.sma(50)
    
    # Only buy if long-term trend is up
    if sma_20 > sma_50 and sma_5 > sma_20:
        if portfolio.position('AAPL') == 0:
            return {"symbol": "AAPL", "action": "buy", "quantity": 100}
    
    # Sell if short-term trend breaks
    elif sma_5 < sma_20 and portfolio.position('AAPL') > 0:
        return {"symbol": "AAPL", "action": "sell", "quantity": "all"}

Volatility-Based Trading

python
def on_bar_close(data_contexts, portfolio, state):
    """Adjust strategy based on volatility"""
    orders = []
    
    for symbol, data in data_contexts.items():
        # Calculate volatility
        atr = data.atr(14)
        volatility_pct = atr / data.close
        
        # Skip high volatility periods
        if volatility_pct > 0.05:  # 5% ATR
            continue
            
        # Use RSI in low volatility
        rsi = data.rsi(14)
        if rsi < 30:
            # Size position based on volatility
            base_size = 100
            adjusted_size = int(base_size * (0.02 / volatility_pct))
            
            orders.append({
                "symbol": symbol,
                "action": "buy",
                "quantity": min(adjusted_size, 200)  # Max 200 shares
            })
    
    return orders if orders else None

Multiple Indicator Confirmation

python
def on_bar_close(data_contexts, portfolio, state):
    """Require multiple indicators to align"""
    aapl = data_contexts['AAPL']
    
    # Get all indicators
    rsi = aapl.rsi(14)
    sma_20 = aapl.sma(20)
    upper, middle, lower = aapl.bollinger_bands()
    macd_line, signal_line, histogram = aapl.macd()
    
    # Check if all ready
    indicators = [rsi, sma_20, upper, lower, macd_line, signal_line]
    if any(ind != ind for ind in indicators):  # Any NaN
        return None
    
    # Bullish conditions (all must be true)
    bullish_conditions = [
        rsi < 40,  # Not overbought
        aapl.close > sma_20,  # Above moving average  
        aapl.close < lower,  # Below lower Bollinger Band
        macd_line > signal_line,  # MACD bullish
        histogram > state.get('prev_histogram', 0)  # Increasing momentum
    ]
    
    if all(bullish_conditions) and portfolio.position('AAPL') == 0:
        return {"symbol": "AAPL", "action": "buy", "quantity": 100}
    
    # Store for next comparison
    state['prev_histogram'] = histogram

Custom Indicator Calculation

python
def calculate_custom_rsi(data_context, periods=14):
    """Custom RSI implementation using history"""
    closes = data_context.history('close', periods + 1)
    
    if len(closes) < periods + 1:
        return float('nan')
    
    # Calculate price changes
    changes = [closes[i] - closes[i-1] for i in range(1, len(closes))]
    
    # Separate gains and losses
    gains = [max(0, change) for change in changes]
    losses = [-min(0, change) for change in changes]
    
    # Calculate averages
    avg_gain = sum(gains) / len(gains)
    avg_loss = sum(losses) / len(losses)
    
    if avg_loss == 0:
        return 100.0
    
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    
    return rsi

def on_bar_close(data_contexts, portfolio, state):
    """Use custom indicator"""
    aapl = data_contexts['AAPL']
    
    custom_rsi = calculate_custom_rsi(aapl)
    builtin_rsi = aapl.rsi(14)
    
    print(f"Custom RSI: {custom_rsi:.2f}, Built-in RSI: {builtin_rsi:.2f}")

Important Notes

  • NaN Handling: Always check for NaN values before using indicators
  • Warm-up Period: Indicators need sufficient historical data to calculate
  • Performance: Built-in indicators are optimized and preferred over custom calculations
  • Data Integrity: OHLCV data is validated and cleaned automatically
  • Time Alignment: All data is properly time-aligned across symbols

Common Patterns

Safe Indicator Usage

python
# Always check for NaN
indicator = data.sma(20)
if indicator != indicator:  # NaN check
    return None

# Or use try/except
try:
    if data.rsi(14) < 30:
        # Safe to use
        pass
except:
    return None

Indicator Crossover Detection

python
# Store previous values in state
current_fast = data.sma(10)
current_slow = data.sma(20)

prev_fast = state.get('prev_fast_sma', current_fast)
prev_slow = state.get('prev_slow_sma', current_slow)

# Detect crossover
if current_fast > current_slow and prev_fast <= prev_slow:
    print("Golden cross detected!")

# Update state
state['prev_fast_sma'] = current_fast
state['prev_slow_sma'] = current_slow

Multi-Symbol Analysis

python
# Compare relative strength across symbols
symbol_scores = {}
for symbol, data in data_contexts.items():
    rsi = data.rsi(14)
    if rsi == rsi:  # Not NaN
        symbol_scores[symbol] = rsi

# Trade the most oversold
if symbol_scores:
    best_symbol = min(symbol_scores.items(), key=lambda x: x[1])
    if best_symbol[1] < 30:  # RSI < 30
        return {"symbol": best_symbol[0], "action": "buy", "quantity": 100}

Test your trading strategies risk-free with professional backtesting.