Backtest Datetime Visualization
by smith6jt-cop
Converting backtest visualizations from bar indices/timesteps to actual datetime axes for clearer time context
Skill Details
Repository Files
1 file in this skill directory
name: backtest-datetime-visualization description: "Converting backtest visualizations from bar indices/timesteps to actual datetime axes for clearer time context" author: Claude Code date: 2025-12-13
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 thanplt.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_timeas datetime, not bar index - For long time ranges, consider using
mdates.MonthLocator()orYearLocator() - 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_timenotebooks/develop_branch_testing.ipynb- Visualization examples- matplotlib.dates documentation for advanced date formatting
Related Skills
Attack Tree Construction
Build comprehensive attack trees to visualize threat paths. Use when mapping attack scenarios, identifying defense gaps, or communicating security risks to stakeholders.
Grafana Dashboards
Create and manage production Grafana dashboards for real-time visualization of system and application metrics. Use when building monitoring dashboards, visualizing metrics, or creating operational observability interfaces.
Matplotlib
Foundational plotting library. Create line plots, scatter, bar, histograms, heatmaps, 3D, subplots, export PNG/PDF/SVG, for scientific visualization and publication figures.
Scientific Visualization
Create publication figures with matplotlib/seaborn/plotly. Multi-panel layouts, error bars, significance markers, colorblind-safe, export PDF/EPS/TIFF, for journal-ready scientific plots.
Seaborn
Statistical visualization. Scatter, box, violin, heatmaps, pair plots, regression, correlation matrices, KDE, faceted plots, for exploratory analysis and publication figures.
Shap
Model interpretability and explainability using SHAP (SHapley Additive exPlanations). Use this skill when explaining machine learning model predictions, computing feature importance, generating SHAP plots (waterfall, beeswarm, bar, scatter, force, heatmap), debugging models, analyzing model bias or fairness, comparing models, or implementing explainable AI. Works with tree-based models (XGBoost, LightGBM, Random Forest), deep learning (TensorFlow, PyTorch), linear models, and any black-box model
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Query Writing
For writing and executing SQL queries - from simple single-table queries to complex multi-table JOINs and aggregations
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Scientific Visualization
Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib/seaborn/plotly with publication styles. For quick exploration use seaborn or plotly directly.
