Skip to content

Complete Strategy Syntax Reference

This comprehensive guide covers every aspect of writing trading strategies for our backtesting engine.

Table of Contents

Strategy Function Signature

Every strategy must implement this function:

python
def on_bar_close(market, portfolio, memory, session):
    """
    Main strategy function called for each trading bar during market hours
    
    Args:
        market (dict): Market data contexts for each symbol
        portfolio (PortfolioContext): Account and position information  
        memory (dict): Persistent storage between calls
        session (SessionInfo): Current simulation metadata (optional)
        
    Returns:
        dict | list[dict] | None: Order(s) to execute or None
    """
    # Your strategy logic here
    return order_or_none

Context Variables Reference

The strategy function receives four context variables that provide access to market data, portfolio information, persistent state, and session metadata.

Market Data Context

market(DataContext)

Provides market data and technical indicators for a single symbol

Properties

closefloatreadonly

Current bar close price

Example:market["AAPL"].close
highfloatreadonly

Current bar high price

Example:market["AAPL"].high
lowfloatreadonly

Current bar low price

Example:market["AAPL"].low
openfloatreadonly

Current bar open price

Example:market["AAPL"].open
timestampTimestampreadonly

Current bar timestamp

Example:market["AAPL"].timestamp
volumeintreadonly

Current bar volume

Example:market["AAPL"].volume

Methods

ema(period: int) -> float→ float

Exponential Moving Average

Parameters:
  • period(int)- period parameter
Returns:

float - EMA value or NaN if insufficient data

Example:
market["AAPL"].ema(20)
history(field: str, periods: int) -> List[float]→ List[float]

Get historical values for a field

Parameters:
  • field(str)- field parameter
  • periods(int)- periods parameter
Returns:

List[float] - List of historical values (most recent last)

Example:
market["AAPL"].history('close', 20)
rsi(period: int = 14) -> float→ float

Relative Strength Index

Parameters:
  • period(int)optional- period parameterDefault: 14
Returns:

float - RSI value between 0 and 100, or NaN if insufficient data

Example:
market["AAPL"].rsi(20)
set_current_index(index: int)→ Any

Set the current bar index for backtesting

Parameters:
  • index(int)- index parameter
Example:
market["AAPL"].set_current_index(...)
sma(period: int) -> float→ float

Simple Moving Average

Parameters:
  • period(int)- period parameter
Returns:

float - SMA value or NaN if insufficient data

Example:
market["AAPL"].sma(20)

Usage Examples

Basic Price Access
aapl = market['AAPL']
current_price = aapl.close
current_volume = aapl.volume
Technical Indicators
# Moving averages
sma_20 = aapl.sma(20)
sma_50 = aapl.sma(50)

# Momentum indicators  
rsi = aapl.rsi(14)
if rsi < 30:  # Oversold
    return buy('AAPL', 100)
Historical Data
# Get last 10 closing prices
closes = aapl.history('close', 10)
avg_price = sum(closes) / len(closes)

Portfolio Context

portfolio(PortfolioContext)

Provides portfolio information and performance metrics

Properties

buying_powerfloatreadonly

Available buying power (same as cash for now)

Example:portfoliocontext.buying_power
equityfloatreadonly

Total portfolio value (cash + positions market value)

Example:portfoliocontext.equity
total_returnfloatreadonly

Total return percentage

Example:portfoliocontext.total_return

Methods

execute_buy(symbol: str, quantity: int, price: float, timestamp: str) -> bool→ bool

Execute a buy order (cash account behavior)

Parameters:
  • symbol(str)- symbol parameter
  • quantity(int)- quantity parameter
  • price(float)- price parameter
  • timestamp(str)- timestamp parameter
Returns:

bool - True if successful, False if insufficient funds

Example:
portfoliocontext.execute_buy('AAPL', ..., ..., ...)
execute_sell(symbol: str, quantity: int, price: float, timestamp: str) -> bool→ bool

Execute a sell order (cash account behavior)

Parameters:
  • symbol(str)- symbol parameter
  • quantity(int)- quantity parameter
  • price(float)- price parameter
  • timestamp(str)- timestamp parameter
Returns:

bool - True if successful, False if insufficient shares

Example:
portfoliocontext.execute_sell('AAPL', ..., ..., ...)
get_last_price(symbol: str) -> Optional[float]→ Optional[float]

Get the last known price for a symbol

Parameters:
  • symbol(str)- symbol parameter
Returns:

Optional[float] - Last price or None if not available

Example:
portfoliocontext.get_last_price('AAPL')
get_positions() -> Dict[str, int]→ Dict[str, int]

Get all current positions

Returns:

Dict[str, int] - Dictionary of symbol to share count (excludes zero positions)

Example:
portfoliocontext.get_positions()
market_value(symbol: str) -> float→ float

Current market value of position

Parameters:
  • symbol(str)- symbol parameter
Returns:

float - Current market value of the position

Example:
portfoliocontext.market_value('AAPL')
position(symbol: str) -> int→ int

Get current position size for a symbol

Parameters:
  • symbol(str)- symbol parameter
Returns:

int - Number of shares held (0 if no position)

Example:
portfoliocontext.position('AAPL')
realized_pnl() -> float→ float

Total realized P&L from completed trades

Returns:

float - Sum of realized P&L from trade history

Example:
portfoliocontext.realized_pnl()
record_equity_snapshot(timestamp: str)→ Any

Record current equity for equity curve

Parameters:
  • timestamp(str)- timestamp parameter
Example:
portfoliocontext.record_equity_snapshot(...)
shares_for_dollars(dollars: float, price: float) -> int→ int

Calculate shares for given dollar amount

Parameters:
  • dollars(float)- dollars parameter
  • price(float)- price parameter
Returns:

int - Number of shares that can be bought (whole shares only)

Example:
portfoliocontext.shares_for_dollars(..., ...)
unrealized_pnl(symbol: Optional[str] = None) -> float→ float

Unrealized P&L for a symbol or total portfolio

Parameters:
  • symbol(Optional[str])optional- symbol parameterDefault: None
Returns:

float - Unrealized profit/loss

Example:
portfoliocontext.unrealized_pnl('AAPL')
update_prices(prices: Dict[str, float])→ Any

Update last known prices for positions

Parameters:
  • prices(Dict[str, float])- prices parameter
Example:
portfoliocontext.update_prices(...)

Usage Examples

Position Information
# Check current positions
aapl_shares = portfolio.position('AAPL')
total_equity = portfolio.equity
available_cash = portfolio.cash
Performance Metrics
# Calculate performance
total_return_pct = portfolio.total_return * 100
current_value = portfolio.equity
profit_loss = portfolio.equity - portfolio.initial_capital

Memory Context

memory(dict)

Persistent dictionary for storing strategy state between function calls. Use this to track variables that need to persist across bars.

Methods

get(key, default=None)→ Any

Get value for key, or return default if key not found

Parameters:
  • key(str)- Dictionary key to look up
  • default(Any)optional- Default value if key not found
Returns:

Any - Value associated with key, or default

Example:
entry_price = memory.get('entry_price', 0)
setdefault(key, default)→ Any

Get value for key, or set and return default if key not found

Parameters:
  • key(str)- Dictionary key
  • default(Any)- Default value to set if key not found
Returns:

Any - Value associated with key

Example:
trade_count = memory.setdefault('trade_count', 0)

Usage Examples

Initializing Memory Variables
if 'initialized' not in memory:
    memory['initialized'] = True
    memory['entry_price'] = 0
    memory['trade_count'] = 0
Tracking Trade State
# Store entry information
if entering_position:
    memory['entry_price'] = market['AAPL'].close
    memory['entry_time'] = session.current_time
    
# Track performance
memory['trade_count'] = memory.get('trade_count', 0) + 1

Session Context

session(SessionInfo)

Immutable container for simulation metadata and timing information. All fields are read-only to prevent accidental modification by strategies.

Methods

to_dict() -> dict→ dict

Convert to dictionary for backward compatibility

Example:
sessioninfo.to_dict()

Usage Examples

Time Information
# Access current time
current_time = session.current_time
current_date = session.current_date
print(f"Trading at {current_time} on {current_date}")
Progress Tracking
# Monitor backtest progress
progress = session.progress_pct
remaining = session.bars_remaining
print(f"Backtest {progress:.1f}% complete, {remaining} bars remaining")

Data Access (Legacy Examples)

Getting Symbol Data

python
# Access data for a specific symbol
aapl = market['AAPL']
googl = market['GOOGL']

# Available symbols: AAPL, GOOGL, MSFT, TSLA, AMZN

OHLCV Properties

python
# Current bar data
current_open = aapl.open      # Opening price
current_high = aapl.high      # High price
current_low = aapl.low        # Low price
current_close = aapl.close    # Closing price
current_volume = aapl.volume  # Volume

# Typical price calculations
typical_price = (aapl.high + aapl.low + aapl.close) / 3
vwap_estimate = (aapl.high + aapl.low) / 2  # Simplified VWAP

Historical Data Access

python
# Get historical values
close_history = aapl.history('close', 10)  # Last 10 closing prices
volume_history = aapl.history('volume', 5)  # Last 5 volume values

# Calculate custom metrics
avg_volume = sum(volume_history) / len(volume_history)
price_change = close_history[-1] - close_history[0]

Portfolio Management

Account Information

python
# Basic account metrics
cash_available = portfolio.cash           # Available cash
total_equity = portfolio.equity          # Total portfolio value
initial_capital = portfolio.initial_capital  # Starting capital
buying_power = portfolio.buying_power    # Available for trading

# Performance metrics
total_return = portfolio.total_return    # Percentage return
current_pnl = portfolio.equity - portfolio.initial_capital

Position Management

python
# Check positions
aapl_shares = portfolio.position('AAPL')  # Number of shares held
has_position = aapl_shares > 0           # Boolean check

# Get all positions
all_positions = portfolio.positions      # Dict of all positions
total_positions = sum(all_positions.values())

# Position metrics
if aapl_shares > 0:
    position_value = aapl_shares * aapl.close
    position_weight = position_value / portfolio.equity

Order Creation

Basic Order Format

python
# Standard order dictionary
order = {
    'symbol': 'AAPL',      # Stock symbol
    'action': 'buy',        # 'buy' or 'sell'
    'quantity': 100         # Number of shares
}

# Return single order
return order

# Return multiple orders
return [order1, order2, order3]

# No action
return None

Quantity Specifications

python
# Fixed quantity
return {'symbol': 'AAPL', 'action': 'buy', 'quantity': 100}

# All shares (for selling)
return {'symbol': 'AAPL', 'action': 'sell', 'quantity': 'all'}

# Dollar amount
return {'symbol': 'AAPL', 'action': 'buy', 'quantity': '$5000'}

# Portfolio percentage
return {'symbol': 'AAPL', 'action': 'buy', 'quantity': '10%'}

# Position percentage (for selling)
return {'symbol': 'AAPL', 'action': 'sell', 'quantity': '50%:position'}

# Maximum affordable
return {'symbol': 'AAPL', 'action': 'buy', 'quantity': 'max'}

# Half of available cash
return {'symbol': 'AAPL', 'action': 'buy', 'quantity': 'half'}

Technical Indicators

Moving Averages

python
# Simple Moving Average (SMA)
sma_20 = aapl.sma(20)      # 20-period SMA
sma_50 = aapl.sma(50)      # 50-period SMA
sma_200 = aapl.sma(200)    # 200-period SMA

# Exponential Moving Average (EMA)
ema_12 = aapl.ema(12)      # 12-period EMA
ema_26 = aapl.ema(26)      # 26-period EMA

# Moving average crossover
golden_cross = sma_50 > sma_200 and previous_sma_50 <= previous_sma_200
death_cross = sma_50 < sma_200 and previous_sma_50 >= previous_sma_200

Momentum Indicators

python
# Relative Strength Index (RSI)
rsi = aapl.rsi(14)         # 14-period RSI (0-100)

# RSI levels
oversold = rsi < 30        # Potential buy signal
overbought = rsi > 70      # Potential sell signal
neutral = 30 <= rsi <= 70  # Neutral zone

# RSI divergence (manual calculation)
price_higher = aapl.close > previous_close
rsi_lower = rsi < previous_rsi
bearish_divergence = price_higher and rsi_lower

Custom Indicators

python
# Bollinger Bands (manual calculation)
sma = aapl.sma(20)
closes = aapl.history('close', 20)
std_dev = np.std(closes) if 'np' in globals() else 0
upper_band = sma + (2 * std_dev)
lower_band = sma - (2 * std_dev)

# Price channels
highest_20 = max(aapl.history('high', 20))
lowest_20 = min(aapl.history('low', 20))
channel_width = highest_20 - lowest_20

# Volume indicators
current_volume = aapl.volume
avg_volume = sum(aapl.history('volume', 20)) / 20
volume_ratio = current_volume / avg_volume
high_volume = volume_ratio > 1.5

State Management

Initializing Memory

python
# Initialize memory variables
if 'initialized' not in memory:
    memory['initialized'] = True
    memory['entry_price'] = 0
    memory['highest_price'] = 0
    memory['trade_count'] = 0
    memory['winning_trades'] = 0
    memory['losing_trades'] = 0

Tracking Trade Information

python
# Store entry information
if entering_position:
    memory['entry_price'] = aapl.close
    memory['entry_time'] = session.current_time
    memory['entry_reason'] = 'SMA crossover'

# Track highest price (for trailing stops)
if portfolio.position('AAPL') > 0:
    memory['highest_price'] = max(memory.get('highest_price', 0), aapl.close)

# Record trade results
if exiting_position:
    profit = (exit_price - memory['entry_price']) * shares
    memory['trade_count'] += 1
    if profit > 0:
        memory['winning_trades'] += 1
    else:
        memory['losing_trades'] += 1

Pattern Detection with State

python
# Track consecutive signals
if 'consecutive_ups' not in state:
    state['consecutive_ups'] = 0

if aapl.close > aapl.sma(20):
    state['consecutive_ups'] += 1
else:
    state['consecutive_ups'] = 0

# Trigger after 3 consecutive signals
if state['consecutive_ups'] >= 3:
    # Strong uptrend confirmed
    pass

Helper Functions

Buy and Sell Helpers

python
# Import helpers (automatically available)
# from core.order_utils import buy, sell, sell_all

# Simple buy
return buy('AAPL', 100)                    # Buy 100 shares
return buy('AAPL', '$5000')                # Buy $5000 worth
return buy('AAPL', '10%')                  # Buy with 10% of portfolio

# Simple sell
return sell('AAPL', 50)                    # Sell 50 shares
return sell('AAPL', '50%:position')        # Sell half of position
return sell_all('AAPL')                    # Sell entire position

# Multiple orders
orders = []
orders.append(buy('AAPL', 100))
orders.append(sell('GOOGL', 'all'))
return orders

Advanced Order Types

python
# Limit orders
return buy_limit('AAPL', 100, limit_price=150.00)
return sell_limit('AAPL', 'all', limit_price=160.00)

# Stop orders
return sell_stop('AAPL', 'all', stop_price=145.00)

# Scale in/out
return scale_in('AAPL', '$10000', num_orders=3)   # Split into 3 buys
return scale_out('AAPL', num_orders=4)            # Exit in 4 parts

# Bracket orders (entry + stop loss + take profit)
return bracket_order('AAPL', '10%', 
                    stop_loss_pct=0.05,    # 5% stop loss
                    take_profit_pct=0.15)   # 15% take profit

Portfolio Operations

python
# Rebalance to target allocations
target_allocations = {
    'AAPL': 0.40,   # 40% in AAPL
    'GOOGL': 0.35,  # 35% in GOOGL
    'MSFT': 0.25    # 25% in MSFT
}
return rebalance_portfolio(target_allocations, min_trade_amount=500)

# Dollar cost averaging
return dollar_cost_average('AAPL', '$5000', 
                          num_periods=5,    # Split into 5 buys
                          period_days=7)    # One per week

Advanced Features

Multi-Symbol Strategies

python
def strategy(data_contexts, portfolio, state):
    """Trade multiple symbols simultaneously"""
    
    orders = []
    
    # Check each symbol
    for symbol in ['AAPL', 'GOOGL', 'MSFT']:
        data = data_contexts[symbol]
        position = portfolio.position(symbol)
        
        # Apply same logic to each symbol
        if data.rsi(14) < 30 and position == 0:
            orders.append(buy(symbol, '5%'))  # 5% of portfolio each
        elif data.rsi(14) > 70 and position > 0:
            orders.append(sell_all(symbol))
    
    return orders if orders else None

Pairs Trading

python
def strategy(data_contexts, portfolio, state):
    """Trade the spread between two correlated assets"""
    
    aapl = data_contexts['AAPL']
    msft = data_contexts['MSFT']
    
    # Calculate spread
    aapl_norm = aapl.close / aapl.sma(20)
    msft_norm = msft.close / msft.sma(20)
    spread = aapl_norm - msft_norm
    
    # Mean reversion on spread
    if spread > 0.05:  # AAPL expensive relative to MSFT
        return [
            sell('AAPL', '10%:position') if portfolio.position('AAPL') > 0 else None,
            buy('MSFT', '10%')
        ]
    elif spread < -0.05:  # AAPL cheap relative to MSFT
        return [
            buy('AAPL', '10%'),
            sell('MSFT', '10%:position') if portfolio.position('MSFT') > 0 else None
        ]
    
    return None

Conditional Logic Patterns

python
def strategy(data_contexts, portfolio, state):
    """Complex conditional logic example"""
    
    aapl = data_contexts['AAPL']
    
    # Market regime detection
    trending = abs(aapl.sma(10) - aapl.sma(30)) / aapl.sma(30) > 0.02
    volatile = (aapl.high - aapl.low) / aapl.close > 0.03
    
    # Adjust strategy based on market conditions
    if trending and not volatile:
        # Trend following in calm markets
        if aapl.sma(10) > aapl.sma(30):
            return buy('AAPL', '15%')
    elif not trending and volatile:
        # Mean reversion in choppy markets
        if aapl.rsi(14) < 30:
            return buy('AAPL', '5%')
        elif aapl.rsi(14) > 70:
            return sell_all('AAPL')
    
    return None

Risk Management Patterns

python
def strategy(data_contexts, portfolio, state):
    """Comprehensive risk management"""
    
    aapl = data_contexts['AAPL']
    position = portfolio.position('AAPL')
    
    if position > 0:
        # Calculate current P&L
        entry_price = state.get('entry_price', aapl.close)
        current_pnl = (aapl.close - entry_price) / entry_price
        
        # Dynamic exit conditions
        if current_pnl <= -0.05:  # 5% stop loss
            print("Stop loss triggered")
            return sell_all('AAPL')
        
        elif current_pnl >= 0.20:  # Take partial profits at 20%
            print("Taking partial profits")
            return sell('AAPL', '50%:position')
        
        elif current_pnl >= 0.10:  # Tighten stop after 10% gain
            # Implement trailing stop logic
            highest = state.get('highest_price', aapl.close)
            if aapl.close < highest * 0.95:  # 5% trailing stop
                print("Trailing stop triggered")
                return sell_all('AAPL')
            state['highest_price'] = max(highest, aapl.close)
    
    # Entry logic here...
    return None

Global Variables Support

python
# Define strategy-level constants
FAST_PERIOD = 10
SLOW_PERIOD = 30
MAX_POSITION_SIZE = 1000
RISK_PER_TRADE = 0.02

# Define strategy configuration
SYMBOLS_TO_TRADE = ['AAPL', 'GOOGL', 'MSFT']
RSI_OVERSOLD = 30
RSI_OVERBOUGHT = 70

def strategy(data_contexts, portfolio, state):
    """Strategy using global variables"""
    
    # Global variables are automatically accessible
    for symbol in SYMBOLS_TO_TRADE:
        data = data_contexts[symbol]
        
        fast_ma = data.sma(FAST_PERIOD)
        slow_ma = data.sma(SLOW_PERIOD)
        rsi = data.rsi(14)
        
        if rsi < RSI_OVERSOLD:
            shares = min(MAX_POSITION_SIZE, int(portfolio.equity * RISK_PER_TRADE / data.close))
            return buy(symbol, shares)
    
    return None

Debugging and Logging

python
def strategy(data_contexts, portfolio, state):
    """Debugging techniques"""
    
    aapl = data_contexts['AAPL']
    
    # Print current state
    print(f"Time: {aapl.timestamp}")
    print(f"Price: ${aapl.close:.2f}")
    print(f"RSI: {aapl.rsi(14):.1f}")
    print(f"Position: {portfolio.position('AAPL')} shares")
    print(f"Cash: ${portfolio.cash:.2f}")
    print(f"Equity: ${portfolio.equity:.2f}")
    
    # Conditional debugging
    if state.get('debug_mode', False):
        print(f"Detailed state: {state}")
    
    # Track specific events
    if entering_trade:
        print(f"ENTRY: Buying at ${aapl.close:.2f}")
        print(f"  Reason: {entry_reason}")
        print(f"  Size: {shares} shares")
    
    return None

Common Pitfalls to Avoid

python
# WRONG: Checking for None incorrectly
if sma == None:  # Don't use ==
    return None

# RIGHT: Check for NaN
if sma != sma:  # NaN is not equal to itself
    return None

# WRONG: Accessing future data
tomorrow_price = aapl.history('close', -1)  # Can't see future!

# RIGHT: Only use historical data
yesterday_price = aapl.history('close', 2)[-2]

# WRONG: Using unavailable libraries
import numpy as np  # Not available in sandbox

# RIGHT: Use built-in functions
average = sum(values) / len(values)

# WRONG: Modifying data_contexts
data_contexts['AAPL'].close = 100  # Don't modify!

# RIGHT: Use local variables
adjusted_price = aapl.close * 1.01

Performance Tips

  1. Cache calculations in state to avoid recalculating
  2. Check indicators are ready before using (NaN check)
  3. Minimize print statements in production strategies
  4. Use portfolio percentages instead of fixed shares
  5. Always validate orders before returning

This reference covers all available syntax and features. For specific examples, see our Example Strategies section.

Test your trading strategies risk-free with professional backtesting.