Appearance
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.closeOHLCV Properties
Price Data
open(float) - Opening price for current barhigh(float) - Highest price for current barlow(float) - Lowest price for current barclose(float) - Closing price for current barvolume(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 NoneMultiple 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'] = histogramCustom 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 NoneIndicator 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_slowMulti-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}