Trade journal analytics
Skill mahmoud20138/Tradecraft/plugins/tradecraft/skills/trade-journal-analytics
102 Claude Code skills across 7 categories -- trading strategies, Azure, VSCode extensions, AI prompts, and custom automation skills
npx -y skills add mahmoud20138/Tradecraft --skill trade-journal-analyticsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Complete trade journalling system: trade logging, performance analytics, streak analysis, drawdown tracking, tag drill-down, and report generation. USE FOR: trade journal, log trade, performance report, win rate, expectancy, R-multiple, drawdown, equity curve, streak, P&L, SQN, profit factor, trade analytics, journal, session breakdown.
SKILL.md
13.7 KB, as published. Nobody here has run it
Skill: Trade Journal Analytics | Domain: trading | Category: infrastructure | Level: beginner Tags:
trading,infrastructure,journal,analytics,performance,review
Trade Journal & Analytics Skill
Overview
A full-featured trade journalling and performance analysis system. Handles every step from logging individual trades to generating publication-quality HTML/Markdown reports. Built on pandas for fast aggregation and uses no external report dependencies.
Python Module
xtrading/skills/trade_journal.py
Stack
- pandas — DataFrame aggregation, groupby, pivot tables
- numpy — Equity curve, cumulative P&L, drawdown series
- dataclasses — Typed immutable
TradeRecordstructure - json / pathlib — JSON persistence and cross-platform file paths
- statistics — Pure-Python mean/stdev for small samples
1. TradeRecord — Single Trade Data Structure
from datetime import datetime
from xtrading.skills.trade_journal import TradeRecord
trade = TradeRecord(
trade_id="T001",
symbol="EURUSD",
direction="long",
entry_price=1.1000,
exit_price=1.1050,
quantity=1.0, # lots
entry_time=datetime(2025, 3, 10, 9, 0),
exit_time=datetime(2025, 3, 10, 11, 30),
stop_loss=1.0970,
take_profit=1.1060,
gross_pnl=500.0,
commission=3.5,
swap=0.0,
risk_amount=300.0,
r_multiple=1.65, # net_pnl / risk_amount
setup_tag="OB_pullback",
session_tag="London",
timeframe="H1",
market_condition="trending",
notes="Clean OB retest, strong momentum",
)
# Auto-computed fields:
# trade.net_pnl → 496.5 (gross - commission - swap)
# trade.outcome → "win" (net_pnl > 0)
# trade.duration_minutes → 150
# trade.day_of_week → "Mon"
# trade.hour_of_day → 9
TradeRecord Fields Reference
| Field | Type | Description |
|---|---|---|
trade_id | str | Unique identifier (required) |
symbol | str | Instrument (e.g. "EURUSD") |
direction | "long"|"short" | Trade direction |
entry_price | float | Entry fill price |
exit_price | float | Exit fill price |
quantity | float | Lots / shares / contracts |
gross_pnl | float | P&L before costs |
commission | float | Total broker commission |
swap | float | Overnight swap/rollover cost |
net_pnl | float | Auto-computed if left 0.0 |
risk_amount | float | Dollar risk per trade |
r_multiple | float | net_pnl / risk_amount |
mae | float | Max Adverse Excursion (price) |
mfe | float | Max Favorable Excursion (price) |
setup_tag | str | Setup label (e.g. "FVG_fill") |
session_tag | str | Session (e.g. "London") |
outcome | "win"|"loss"|"breakeven"|"open" | Auto-detected from net_pnl |
2. TradeJournal — CRUD and Persistence
from xtrading.skills.trade_journal import TradeJournal
# Create journal (optional JSON persistence)
journal = TradeJournal(
journal_path="my_journal.json",
account_name="Prop Account",
initial_balance=50_000.0,
)
# Add trades
journal.add_trade(trade) # returns trade_id
# Update a field
journal.update_trade("T001", notes="Updated note", r_multiple=1.70)
# Retrieve
t = journal.get_trade("T001")
# Delete
journal.delete_trade("T001") # returns True if found
# Querying with filters
trades = journal.get_trades(
symbol="EURUSD",
setup_tag="OB_pullback",
session_tag="London",
from_date=date(2025, 1, 1),
to_date=date(2025, 3, 31),
direction="long",
outcome="win",
min_r=1.0, # only trades with R ≥ 1.0
)
# Convert to DataFrame for custom analysis
df = journal.to_dataframe()
# Export to CSV
journal.export_csv("journal_export.csv")
# Persistence is automatic — every add/update/delete saves to JSON
len(journal) # → 42 trades
repr(journal) # → "TradeJournal(account='Prop Account', trades=42)"
File Persistence Format
The JSON file structure allows portable sharing between machines:
{
"account": "Prop Account",
"initial_balance": 50000.0,
"trades": [
{
"trade_id": "T001",
"entry_time": "2025-03-10T09:00:00",
"exit_time": "2025-03-10T11:30:00",
...
}
]
}
3. PerformanceAnalytics — Trading Statistics
from xtrading.skills.trade_journal import PerformanceAnalytics
analytics = PerformanceAnalytics(journal.get_trades())
# Individual metrics
wr = analytics.win_rate() # 0.623 (62.3%)
exp = analytics.expectancy() # +0.42R per trade
pf = analytics.profit_factor() # 2.15
sharpe = analytics.sharpe_ratio() # 1.34 (annualised)
sortino = analytics.sortino_ratio() # 1.89 (downside only)
sqn = analytics.sqn() # 2.87 (> 2 = good)
dur = analytics.avg_trade_duration() # 142.5 minutes
# Full report (all metrics in one call)
report = analytics.full_report()
# {
# "n_trades": 50, "n_wins": 31, "n_losses": 18,
# "win_rate": 0.62, "win_rate_pct": 62.0,
# "expectancy_r": 0.42,
# "profit_factor": 2.15,
# "avg_win_r": 1.85, "avg_loss_r": 1.12,
# "best_trade_r": 4.20, "worst_trade_r": -2.10,
# "total_net_pnl": 8420.0,
# "sharpe_ratio": 1.34, "sortino_ratio": 1.89,
# "sqn": 2.87,
# "max_drawdown_pct": 8.2,
# "calmar_ratio": 5.1,
# "avg_duration_min": 142.5,
# "total_commission": 175.0,
# }
Metric Interpretation Guide
| Metric | Poor | Acceptable | Good | Excellent |
|---|---|---|---|---|
| Win Rate | < 35% | 35–45% | 45–65% | > 65% |
| Expectancy | < 0 | 0–0.2R | 0.2–0.5R | > 0.5R |
| Profit Factor | < 1.0 | 1.0–1.5 | 1.5–2.0 | > 2.0 |
| SQN | < 1.6 | 1.6–2.0 | 2.0–3.0 | > 3.0 |
| Sharpe | < 0.5 | 0.5–1.0 | 1.0–2.0 | > 2.0 |
| Max Drawdown | > 25% | 15–25% | 8–15% | < 8% |
Key Formulas
Expectancy = WR × Avg_Win_R − (1−WR) × Avg_Loss_R
Profit Factor = Gross_Profit / Gross_Loss
SQN = (Expectancy / StdDev_R) × √(n_trades)
Sharpe = (Daily_PnL_mean − rf/252) / Daily_PnL_std × √252
Sortino = (Daily_PnL_mean − rf/252) / Downside_Std × √252
Calmar = Annualised_Return / Max_Drawdown_pct
4. StreakAnalyser — Win/Loss Streak Detection
from xtrading.skills.trade_journal import StreakAnalyser
streaks = StreakAnalyser(journal.get_trades())
# Current active streak
current = streaks.current_streak()
# {"streak": 4, "type": "win", "message": "4-trade WIN streak"}
# Maximum streaks in history
maxima = streaks.max_streaks()
# {
# "max_win_streak": 8,
# "max_loss_streak": 5,
# "current": {"streak": 4, "type": "win", "message": "..."}
# }
# Distribution of streak lengths
dist = streaks.streak_distribution()
# {
# "win_streaks": {1: 12, 2: 8, 3: 4, 4: 2, 8: 1},
# "loss_streaks": {1: 10, 2: 5, 3: 2, 5: 1},
# "avg_win_streak": 1.9,
# "avg_loss_streak": 1.7,
# }
# Revenge trading detection (performance after losing trades)
after_loss = streaks.after_loss_performance()
# {
# "n_after_loss": 18,
# "avg_r_after_loss": -0.15,
# "revenge_trading_risk": True, # True when avg < -0.3R
# "trades_after_loss": [-1.2, 0.8, -0.9, ...]
# }
Revenge Trading Alert: revenge_trading_risk=True indicates the trader is
taking on poor quality trades after losses (avg R < −0.3 in the next trade).
5. DrawdownTracker — Equity Curve & Drawdown Analysis
from xtrading.skills.trade_journal import DrawdownTracker
dd = DrawdownTracker(journal.get_trades(), initial_balance=50_000.0)
# Build equity curve (pd.Series indexed by datetime)
equity = dd.equity_curve()
# 2025-01-02 50000.0
# 2025-01-03 50450.0
# ...
# Full drawdown statistics
result = dd.calculate()
# {
# "max_drawdown": 4200.0, # in dollars
# "max_drawdown_pct": 8.2, # % from peak
# "max_drawdown_duration_days": 12, # days to recovery
# "current_drawdown_pct": 1.5, # current DD from peak
# "peak_equity": 58400.0,
# "current_equity": 57524.0,
# "total_return_pct": 15.05,
# }
# Monthly P&L breakdown
monthly = dd.monthly_pnl()
# net_pnl n_trades avg_pnl
# 2025-01 1842.00 18 102.33
# 2025-02 2315.00 22 105.23
# 2025-03 763.00 10 76.30
6. TagAnalytics — Drill-Down by Any Dimension
from xtrading.skills.trade_journal import TagAnalytics
tag = TagAnalytics(journal.get_trades())
# Performance by any field
by_setup = tag.by_tag("setup_tag")
# setup_tag n_trades win_rate expectancy profit_factor avg_r total_pnl
# 0 OB_pullback 18 0.722 0.845 3.12 1.25 4215.0
# 1 FVG_fill 12 0.583 0.320 1.85 0.72 2100.0
# 2 BOS_entry 8 0.500 0.125 1.42 0.55 800.0
by_session = tag.by_tag("session_tag")
by_symbol = tag.by_tag("symbol")
by_dow = tag.by_tag("day_of_week")
by_tf = tag.by_tag("timeframe")
by_market = tag.by_tag("market_condition")
# Top setups with at least N trades
top_setups = tag.best_setups(min_trades=5)
# Session performance as list of dicts
sessions = tag.session_breakdown()
# [{"session_tag": "London", "n_trades": 22, "win_rate": 0.68, ...},
# {"session_tag": "NewYork", "n_trades": 18, "win_rate": 0.61, ...}]
# Heatmap: average R by hour and day of week
heatmap = tag.time_of_day_heatmap()
# Mon Tue Wed Thu Fri
# hour
# 7 0.82 1.20 0.45 0.91 0.33
# 8 1.42 0.98 1.15 0.72 0.88
# 9 1.85 1.32 1.60 1.45 1.10
# 10 0.65 0.72 0.80 0.55 0.42
Drillable Tag Fields
| Tag Field | Purpose |
|---|---|
setup_tag | ICT model, pattern type |
session_tag | London / NewYork / Asian |
symbol | Per-instrument performance |
timeframe | H1 / H4 / D1 |
direction | Long vs Short bias |
day_of_week | Best trading days |
market_condition | Trending vs Ranging |
7. JournalReporter — Report Generation
from xtrading.skills.trade_journal import JournalReporter
reporter = JournalReporter(journal)
# Console text summary
print(reporter.text_summary())
# ============================================================
# TRADING PERFORMANCE REPORT — Prop Account
# ============================================================
# Trades : 50 total (31W / 18L)
# Win Rate : 62.0%
# Expectancy : +0.42R per trade
# Profit Fac : 2.15
# Total P&L : $8,420.00
# Avg Win : +1.85R | Avg Loss: 1.12R
# Best Trade : +4.20R | Worst: -2.10R
# SQN : 2.87
# Sharpe : 1.34
# Max DD : 8.2%
# Current DD : 1.5%
# Max W Str : 8 | Max L Str: 5
# Current : 4-trade WIN streak
# ============================================================
# Markdown report (for Obsidian, Notion, GitHub)
md = reporter.markdown_report()
# Includes: Summary stats table + Performance by Setup table
# Self-contained HTML report (dark theme, no dependencies)
html = reporter.html_report()
with open("report.html", "w") as f:
f.write(html)
# Report on a subset of trades
london_trades = journal.get_trades(session_tag="London")
print(reporter.text_summary(london_trades))
Full Workflow Example
from datetime import datetime, date
from xtrading.skills.trade_journal import (
TradeRecord, TradeJournal, PerformanceAnalytics,
StreakAnalyser, DrawdownTracker, TagAnalytics, JournalReporter,
)
# 1. Create / load journal
journal = TradeJournal("journal.json", "My Prop Account", 100_000)
# 2. Log trades
for trade_data in my_trade_source:
journal.add_trade(TradeRecord(**trade_data))
# 3. Analyse all trades
analytics = PerformanceAnalytics(journal.get_trades())
report = analytics.full_report()
# 4. Check for patterns
streaks = StreakAnalyser(journal.get_trades())
if streaks.after_loss_performance().get("revenge_trading_risk"):
print("⚠️ Revenge trading detected — consider a break rule")
# 5. Drawdown check
dd = DrawdownTracker(journal.get_trades(), journal.initial_balance).calculate()
if dd["current_drawdown_pct"] > 10:
print(f"⛔ Max daily loss approaching: {dd['current_drawdown_pct']:.1f}% DD")
# 6. Find your best setups
tag = TagAnalytics(journal.get_trades())
print(tag.best_setups(min_trades=5).to_string())
# 7. Generate reports
reporter = JournalReporter(journal)
print(reporter.text_summary())
html = reporter.html_report()
Usage Conventions
- R-multiples — always set
risk_amountfor meaningfulr_multiplevalues - outcome — auto-detected from
net_pnl; explicitly set"open"for live trades - session_tag — use consistent labels:
"London","NewYork","Asian","Overlap" - Sharpe/Sortino — require ≥ 5 closed trades; meaningful only at ≥ 20
- SQN benchmark — > 2.0 good, > 3.0 excellent, > 5.0 exceptional (Van Tharp)
- Persistence — auto-saves on every CRUD operation when
journal_pathis set - Filtering — combine
get_trades()filters then pass subset to any analyser class