Claude Code Plugins

Community-maintained marketplace

Feedback

backtest-datetime-visualization

@smith6jt-cop/Skills_Registry
0
0

Converting backtest visualizations from bar indices/timesteps to actual datetime axes for clearer time context

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name backtest-datetime-visualization
description Converting backtest visualizations from bar indices/timesteps to actual datetime axes for clearer time context
author Claude Code
date Sat Dec 13 2025 00:00:00 GMT+0000 (Coordinated Universal Time)

backtest-datetime-visualization - Research Notes

Experiment Overview

Item Details
Date 2025-12-13
Goal Convert backtest result visualizations from using bar indices (0, 1, 2...) to actual datetime values for equity curves, drawdowns, and trade distributions
Environment Python 3.10, matplotlib, pandas, Jupyter notebooks
Status Success

Context

Backtest visualizations often use bar indices or timestep numbers on the x-axis, which makes it difficult to correlate results with actual market periods. Converting to datetime axes provides:

  • Clear understanding of when drawdowns occurred
  • Correlation with known market events
  • Proper time-proportional spacing

Verified Workflow

1. Equity Curve with DateTime Index

When equity_curve is a pandas Series with DatetimeIndex:

# equity_series is pd.Series with DatetimeIndex
ax.plot(equity_series.index, equity_series.values, 'b-', label='Strategy')
ax.set_xlabel('Date')
ax.tick_params(axis='x', rotation=45)  # Rotate for readability

2. Drawdown Plot with Timestamps

# Extract timestamps from equity series
equity = equity_series.values
timestamps = equity_series.index  # DatetimeIndex
running_max = np.maximum.accumulate(equity)
drawdown = (running_max - equity) / running_max * 100

# Use timestamps for x-axis
ax.fill_between(timestamps, 0, drawdown, color='red', alpha=0.5)
ax.set_xlabel('Date')
ax.tick_params(axis='x', rotation=45)
ax.invert_yaxis()  # Drawdown goes down

3. Trade P&L with Entry Times

For trade distributions, use entry_time from TradeRecord:

if len(result.trades) > 0:
    trade_pnls = [t.pnl for t in result.trades]
    trade_times = [t.entry_time for t in result.trades]

    # Use stem plot for datetime x-axis (more robust than bar)
    markerline, stemlines, baseline = ax.stem(trade_times, trade_pnls, basefmt='k-')

    # Color stems based on profit/loss
    for stem, pnl in zip(stemlines, trade_pnls):
        stem.set_color('green' if pnl > 0 else 'red')
        stem.set_alpha(0.7)

    ax.set_xlabel('Date')
    ax.tick_params(axis='x', rotation=45)

4. Bar Charts with DateTime (Alternative)

If you must use bar charts with datetime:

import matplotlib.dates as mdates

# Convert datetime to matplotlib date numbers
trade_dates = mdates.date2num(trade_times)
width = 0.5  # Width in days

ax.bar(trade_dates, trade_pnls, width=width, color=colors)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.AutoDateLocator())

Failed Attempts (Critical)

Attempt Why it Failed Lesson Learned
ax.bar(trade_times, trade_pnls) directly Width parameter issues with datetime objects Use stem plot or convert to matplotlib date numbers
range(len(drawdown)) for x-axis Loses all time context Always use equity_series.index
Not rotating x-axis labels Dates overlap and become unreadable Add ax.tick_params(axis='x', rotation=45)
Using plt.xticks(rotation=45) Affects all subplots Use ax.tick_params() for specific axis

Final Parameters

# Standard pattern for backtest visualization
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Equity curve
ax = axes[0, 0]
ax.plot(equity_series.index, equity_series.values)
ax.set_xlabel('Date')
ax.tick_params(axis='x', rotation=45)

# Drawdown
ax = axes[0, 1]
ax.fill_between(equity_series.index, 0, drawdown)
ax.set_xlabel('Date')
ax.tick_params(axis='x', rotation=45)

# Trade P&L (use stem for datetime compatibility)
ax = axes[1, 0]
ax.stem(trade_times, trade_pnls, basefmt='k-')
ax.set_xlabel('Date')
ax.tick_params(axis='x', rotation=45)

plt.tight_layout()  # Prevent label overlap

Key Insights

  • plt.stem() handles datetime x-values better than plt.bar() without conversion
  • Always call plt.tight_layout() after rotating labels to prevent clipping
  • Backtest DataFrames should use DatetimeIndex, not integer index
  • TradeRecord objects should store entry_time as datetime, not bar index
  • For long time ranges, consider using mdates.MonthLocator() or YearLocator()
  • The observation window plots (100-step windows) should keep "Time Step" labels since they represent relative position, not calendar time

Data Requirements

Ensure your backtest engine stores proper timestamps:

@dataclass
class TradeRecord:
    symbol: str
    entry_time: datetime  # Not int!
    exit_time: Optional[datetime]
    entry_price: float
    exit_price: Optional[float]
    pnl: float

References

  • alpaca_trading/backtest/engine.py - TradeRecord with entry_time
  • notebooks/develop_branch_testing.ipynb - Visualization examples
  • matplotlib.dates documentation for advanced date formatting