Claude-skill-registry dashboard-pnl-visualization
Trading dashboard P&L visualization with profit tracker integration, win-rate overlays, R-multiples, and configurable settings
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/dashboard-pnl-visualization" ~/.claude/skills/majiayu000-claude-skill-registry-dashboard-pnl-visualization && rm -rf "$T"
manifest:
skills/data/dashboard-pnl-visualization/SKILL.mdsource content
dashboard-pnl-visualization - Research Notes
Experiment Overview
| Item | Details |
|---|---|
| Date | 2025-12-13 |
| Goal | Enhance trading dashboard with comprehensive P&L tracking, win-rate overlays from profit tracker, and R-multiple position visualization |
| Environment | Python 3.10, matplotlib, alpaca-py, Alpaca Trading API |
| Status | Success |
Context
Trading dashboards often show only gross P&L without breaking down realized vs unrealized, cost coverage, or win-rate context. This skill documents patterns for integrating profit tracker data into dashboard visualizations.
Verified Workflow
1. DashboardConfig Dataclass
Create a configurable runtime settings system:
from dataclasses import dataclass, field from typing import List import json from pathlib import Path @dataclass class DashboardConfig: """Runtime configuration for the monitoring dashboard.""" symbols: List[str] = field(default_factory=lambda: ["SPY", "QQQ", "AAPL"]) lookback_days: int = 30 interval_seconds: int = 60 output_dir: str = "dashboards" cost_data: float = 3.33 # Daily data cost cost_software: float = 6.67 # Daily software cost risk_limit_pct: float = 10.0 # Portfolio risk limit profit_log_dir: str = "logs/live" save_latest: bool = True @classmethod def from_json(cls, path: str) -> "DashboardConfig": if not path or not Path(path).exists(): return cls() return cls(**json.loads(Path(path).read_text()))
2. Broker Equity History Integration
Fetch real portfolio equity data from Alpaca:
def get_equity_history( self, period: str = "1M", # '1D', '1W', '1M', '3M', '1A' timeframe: str = "1D", # '1Min', '5Min', '15Min', '1H', '1D' ) -> List[Dict[str, Any]]: """Return historical equity curve from the broker.""" from alpaca.trading.requests import GetPortfolioHistoryRequest request = GetPortfolioHistoryRequest(period=period, timeframe=timeframe) history = self._client.get_portfolio_history(request) if hasattr(history, "equity") and hasattr(history, "timestamp"): return [ {"timestamp": ts, "value": eq} for ts, eq in zip(history.timestamp, history.equity) ] return []
3. Win-Rate Overlay from Profit Tracker
Load profit summary for live metrics overlay:
def load_profit_summary(log_dir: str) -> Dict: """Load profit tracker summary for win-rate overlays.""" path = Path(log_dir) summary_files = sorted(path.glob("*/profit_summary.json")) if not summary_files: return {} with summary_files[-1].open() as f: summary = json.load(f) trades = summary.get("trades", []) pnls = [t.get("pnl", 0.0) for t in trades] wins = [p for p in pnls if p > 0] # Calculate loss streak loss_streak = 0 for pnl in reversed(pnls): if pnl <= 0: loss_streak += 1 else: break return { "win_rate": len(wins) / len(pnls) if pnls else 0.0, "loss_streak": loss_streak, "trade_count": len(pnls), }
4. Realized vs Unrealized P&L Split
def get_pnl_breakdown(broker, cfg, profit_overlay, open_pnl=0.0) -> dict: """Get P&L breakdown with realized/unrealized split.""" account = broker.get_account() equity = float(account['equity']) last_equity = float(account.get('last_equity', equity)) daily_pnl = equity - last_equity unrealized_pnl = float(open_pnl) realized_pnl = daily_pnl - unrealized_pnl breakdown = { 'gross_pnl': daily_pnl, 'unrealized_pnl': unrealized_pnl, 'realized_pnl': realized_pnl, 'data_cost': cfg.cost_data, 'software_cost': cfg.cost_software, } breakdown['net_pnl'] = breakdown['gross_pnl'] - cfg.cost_data - cfg.cost_software # Add win-rate overlay if profit_overlay: breakdown['win_rate'] = profit_overlay.get('win_rate') breakdown['loss_streak'] = profit_overlay.get('loss_streak') return breakdown
5. R-Multiple Display for Positions
def calculate_r_multiple(entry_price, current_price, stop_price, side): """Calculate R-multiple for position risk-reward.""" if not stop_price: return None if side != 'short': risk_per_share = entry_price - stop_price profit_per_share = current_price - entry_price else: risk_per_share = stop_price - entry_price profit_per_share = entry_price - current_price if risk_per_share <= 0: return None return profit_per_share / risk_per_share # Color coding for R-multiples def get_r_color(r_multiple): if r_multiple is None: return 'gray' elif r_multiple >= 2: return 'darkgreen' # Excellent elif r_multiple >= 1: return 'green' # Good else: return 'orange' # Below target
Failed Attempts (Critical)
| Attempt | Why it Failed | Lesson Learned |
|---|---|---|
Using directly | Doesn't separate realized from unrealized | Pass from position calculations |
| Hardcoding cost values | Different users have different infrastructure costs | Use DashboardConfig dataclass |
| Reading profit_summary from fixed path | Path varies by session date | Use glob to find most recent summary |
| Displaying R-multiple without stop price | Division by zero / meaningless value | Return None and show "--" in display |
Final Parameters
# CLI arguments for dashboard parser.add_argument('--config', type=str, help='JSON config path') parser.add_argument('--symbols', type=str, help='Comma-separated symbols') parser.add_argument('--lookback-days', type=int, default=30) parser.add_argument('--data-cost', type=float, default=3.33) parser.add_argument('--software-cost', type=float, default=6.67) parser.add_argument('--risk-limit', type=float, default=10.0) parser.add_argument('--profit-log-dir', type=str, default='logs/live')
Key Insights
- Realized/unrealized split requires tracking open position P&L separately
- Win-rate overlays provide crucial context for daily P&L interpretation
- R-multiples only meaningful when stop prices are set
- Cost coverage percentage helps evaluate if trading covers infrastructure costs
- JSON config files allow per-environment customization without code changes
- Profit tracker summaries should be written with timestamp directories for history
References
-alpaca_trading/broker.py
methodget_equity_history()
- R-multiple renderingalpaca_trading/visualization/dashboard.py
- Full implementationscripts/monitor_dashboard.py
- Feature documentationdocs/dashboard_improvements.md