Claude-skill-registry backtest-datetime-visualization
Converting backtest visualizations from bar indices/timesteps to actual datetime axes for clearer time context
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/backtest-datetime-visualization" ~/.claude/skills/majiayu000-claude-skill-registry-backtest-datetime-visualization && rm -rf "$T"
manifest:
skills/data/backtest-datetime-visualization/SKILL.mdsource content
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 |
|---|---|---|
directly | Width parameter issues with datetime objects | Use plot or convert to matplotlib date numbers |
for x-axis | Loses all time context | Always use |
| Not rotating x-axis labels | Dates overlap and become unreadable | Add |
Using | Affects all subplots | Use 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
handles datetime x-values better thanplt.stem()
without conversionplt.bar()- Always call
after rotating labels to prevent clippingplt.tight_layout() - Backtest DataFrames should use DatetimeIndex, not integer index
- TradeRecord objects should store
as datetime, not bar indexentry_time - For long time ranges, consider using
ormdates.MonthLocator()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
- TradeRecord with entry_timealpaca_trading/backtest/engine.py
- Visualization examplesnotebooks/develop_branch_testing.ipynb- matplotlib.dates documentation for advanced date formatting