Appearance
on_market_close
Called at 4:00 PM ET each trading day when the market closes. Use this for end-of-day analysis, position management, or daily reporting.
Signature
python
def on_market_close(portfolio, state, market_data):
# Daily market close logic
passParameters
- portfolio (
PortfolioContext) - Current portfolio state - state (
dict) - Strategy state dictionary - market_data (
dict) - Market session information containing:date- Trading datetimestamp- Market close timestampis_market_close- Always True for this functiontrading_day- Current trading daysession_time- Time of day (4:00 PM ET)
Usage
This function is optional but useful for end-of-day tasks and daily analysis.
Common Use Cases
- Calculate daily P&L and performance
- Close or adjust positions before overnight
- Log daily trading summary
- Update risk management parameters
- Save closing prices for gap analysis
Examples
Basic Daily Summary
python
def on_market_close(portfolio, state, market_data):
"""Log daily trading summary"""
trading_date = market_data['date']
# Calculate daily P&L
if 'today_start_equity' in state:
daily_pnl = portfolio.equity - state['today_start_equity']
daily_return = daily_pnl / state['today_start_equity']
print(f"Market closed - {trading_date}")
print(f"Daily P&L: ${daily_pnl:,.2f} ({daily_return*100:.2f}%)")
# Log position summary
positions = portfolio.positions()
if positions:
print(f"Overnight positions: {positions}")
else:
print("No overnight positions")
# Update state for next day
state['previous_close_equity'] = portfolio.equityPosition Management
python
def on_market_close(portfolio, state, market_data):
"""Manage positions at market close"""
trading_date = market_data['date']
# Close all positions on Friday (no weekend exposure)
if market_data['timestamp'].weekday() == 4: # Friday
positions = portfolio.positions()
if positions:
print(f"Closing all positions for weekend - {len(positions)} symbols")
state['weekend_close_needed'] = True
# Flag positions to close (will be handled in next on_bar_close)
for symbol in positions:
state[f'close_{symbol}'] = True
# Risk management - close losing positions
for symbol, shares in portfolio.positions().items():
if shares > 0: # Only check long positions
unrealized_pnl = portfolio.unrealized_pnl(symbol)
position_value = portfolio.market_value(symbol)
# Close positions with >10% loss
if unrealized_pnl < -0.10 * position_value:
print(f"Marking {symbol} for closure - Large loss: ${unrealized_pnl:.2f}")
state[f'close_{symbol}'] = TruePerformance Analysis
python
def on_market_close(portfolio, state, market_data):
"""Detailed daily performance analysis"""
trading_date = market_data['date']
# Calculate comprehensive daily metrics
start_equity = state.get('today_start_equity', portfolio.initial_capital)
daily_pnl = portfolio.equity - start_equity
daily_return = daily_pnl / start_equity
# Track high/low for the day
state['daily_high'] = max(state.get('daily_high', portfolio.equity), portfolio.equity)
state['daily_low'] = min(state.get('daily_low', portfolio.equity), portfolio.equity)
# Calculate drawdown
peak_equity = state.get('peak_equity', portfolio.initial_capital)
current_drawdown = (peak_equity - portfolio.equity) / peak_equity
# Update peak if new high
if portfolio.equity > peak_equity:
state['peak_equity'] = portfolio.equity
current_drawdown = 0
# Win/loss tracking
if 'win_days' not in state:
state['win_days'] = 0
state['loss_days'] = 0
if daily_pnl > 0:
state['win_days'] += 1
elif daily_pnl < 0:
state['loss_days'] += 1
# Log comprehensive summary
total_days = state['win_days'] + state['loss_days']
win_rate = state['win_days'] / total_days if total_days > 0 else 0
print(f"=== Daily Close Summary - {trading_date} ===")
print(f"Daily P&L: ${daily_pnl:,.2f} ({daily_return*100:.2f}%)")
print(f"Total Equity: ${portfolio.equity:,.2f}")
print(f"Current Drawdown: {current_drawdown*100:.2f}%")
print(f"Win Rate: {win_rate*100:.1f}% ({state['win_days']}/{total_days})")Save Market Data for Analysis
python
def on_market_close(portfolio, state, market_data):
"""Save closing data for next day's analysis"""
trading_date = market_data['date']
# Save closing prices for gap analysis
if 'previous_closes' not in state:
state['previous_closes'] = {}
# This would need to be integrated with data_contexts
# In practice, you'd save this from the last on_bar_close call
# state['previous_closes'][symbol] = last_close_price
# Save portfolio metrics
state['daily_equity_history'] = state.get('daily_equity_history', [])
state['daily_equity_history'].append({
'date': trading_date,
'equity': portfolio.equity,
'return': (portfolio.equity - portfolio.initial_capital) / portfolio.initial_capital
})
# Clean up old data (keep last 30 days)
if len(state['daily_equity_history']) > 30:
state['daily_equity_history'] = state['daily_equity_history'][-30:]Risk Management Updates
python
def on_market_close(portfolio, state, market_data):
"""Update risk management parameters daily"""
trading_date = market_data['date']
# Calculate portfolio volatility (last 10 days)
if 'daily_returns' in state and len(state['daily_returns']) >= 10:
recent_returns = state['daily_returns'][-10:]
avg_return = sum(recent_returns) / len(recent_returns)
volatility = (sum((r - avg_return)**2 for r in recent_returns) / len(recent_returns)) ** 0.5
# Adjust position sizing based on volatility
if volatility > 0.02: # High volatility (>2% daily)
state['position_multiplier'] = 0.5 # Reduce position sizes
print(f"High volatility detected: {volatility*100:.1f}% - Reducing position sizes")
else:
state['position_multiplier'] = 1.0 # Normal position sizes
# Update stop loss levels based on portfolio size
state['stop_loss_pct'] = max(0.02, 1000 / portfolio.equity) # Min 2% or $1000
print(f"Updated risk parameters - Stop loss: {state['stop_loss_pct']*100:.1f}%")Integration Patterns
Flag-based Position Closing
python
def on_market_close(portfolio, state, market_data):
"""Set flags for position management"""
# Mark positions to close tomorrow morning
state['close_all_positions'] = should_close_all_positions()
def on_bar_close(data_contexts, portfolio, state):
"""Handle close flags"""
# Check for close flags from market close
if state.get('close_all_positions', False):
positions = portfolio.positions()
if positions:
orders = []
for symbol, shares in positions.items():
orders.append({
"symbol": symbol,
"action": "sell",
"quantity": "all"
})
# Clear the flag
state['close_all_positions'] = False
return orders
# Regular trading logic...Notes
- This function runs once per trading day at market close (4:00 PM ET)
- Perfect for daily analysis and end-of-day position management
- Use this to prepare for overnight exposure decisions
- All timestamps are in Eastern Time (NYSE timezone)
- This is not called on weekends or holidays
- Any orders returned here won't execute (market is closed)