Compare commits

...

4 Commits

Author SHA1 Message Date
be7de0ef19 update 2025-07-31 14:01:52 +02:00
36c7c9f68d vault backup: 2025-06-30 14:08:10 2025-06-30 14:08:10 +02:00
e82f639932 vault backup: 2025-06-30 12:14:14 2025-06-30 12:14:14 +02:00
c60a41d7ff change 2025-06-30 12:07:39 +02:00
8 changed files with 1149 additions and 6 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
/.venv/
.venv
/.vscode/
.vscode
.vscode
.obsidian

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

4
deleteme.md Normal file
View File

@ -0,0 +1,4 @@
[Fetching data](#Fetching%20data)
# Fetching data
fdfd

75
docs.md Normal file
View File

@ -0,0 +1,75 @@
## MCP
### Local vbtpro mcp server
main code in `vectorbtpro.mcp_server` can run either in `stdio` or in `streamable-http mode`
Run mcp server in streamable-http mode (currently manually in terminal as single process for all local mcp clients)
```bash
cd /Users/davidbrazda/Documents/Development/python/vectorbt.pro-2025.6.24
source .venv/bin/activate
python -m vectorbtpro.mcp_server --transport streamable-http
```
Claude desktop (currently doesnt support remotes) can use it by `mcp-remote`
```json
"vectorbtpro_server": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://localhost:8000/mcp"
]
}
```
Other MCPs (Claude code, Gemini CLI)
```json
{
"mcpServers": {
"vectorbtpro_server": {
"transport": "http",
"url": "http://localhost:8000"
}
}
}
```
## Claude Desktop
**Settings:**
`/Users/davidbrazda/Library/Application Support/Claude/claude_desktop_config.json`
**Logs:**
`/Users/davidbrazda/Library/Logs/Claude` (main.log,mcp.log, mcp_server-name.log)
## Claude code
[Doc](https://docs.anthropic.com/en/docs/claude-code/mcp)
**Settings:**
- User/global settings (`~/.claude.json`)
- Project-specific MCP file (`.mcp.json`)
**Dedicated MCP file:** ~/.claude/mcp_servers.json
**Logs:**
## Claude Code Router
[doc](https://github.com/musistudio/claude-code-router)
**Settings:**
To set models for each tasks
`~/.claude-code-router/config.json`
**Logs:**
`~/.claude-code-router/claude-code-router.log`

View File

@ -1,5 +1,21 @@
Here goes the target features
> [!NOTE] Poznámka
> Contents
> [!note]- poznám ka
> neco
> neco
> [!example]- Graph: voaltility averagae slope
> ![[Pasted image 20250630140635.png]]
> [!example]- Graph: volatility average slope across 1d to 30d range of windows
> ![[Volatility_average_slope.png]]
# Things to try
TODO:

View File

@ -1,5 +1,5 @@
- [DEBUGGING](#debugging)
- [FETCHING DATA](#fetching-data)
- [Fetching Data](#fetching-data)
- [REINDEX to main session](#reindex-to-main-session)
- [Smart indexing](#smart-indexing)
- [Data manipulation](#data-manipulation)
@ -9,7 +9,7 @@
- [RESAMPLING](#resampling)
- [config](#config)
- [REALIGN](#realign)
- [REALIGN\_CLOSING accessors](#realign_closing-accessors)
- [REALIGN\_CLOSING accessors](#realignclosing-accessors)
- [SIGNALS](#signals)
- [Comparing](#comparing)
- [GENERATE SIGNALS IRERATIVELY (numba)](#generate-signals-ireratively-numba)
@ -28,11 +28,11 @@
- [Stoploss/Takeprofit](#stoplosstakeprofit)
- [SL - ATR based](#sl---atr-based)
- [EXIT after time](#exit-after-time)
- [CALLBACKS -](#callbacks--)
- [CALLBACKS -](#callbacks)
- [MEMORY](#memory)
- [Portfolio](#portfolio)
- [from signals](#from-signals)
- [CALLBACKS](#callbacks)
- [CALLBACKS](#callbacks-1)
- [Access running total return from sim](#access-running-total-return-from-sim)
- [Staticization](#staticization)
- [Grouping](#grouping)
@ -41,6 +41,15 @@
- [PnL by hour of the day (BOXPLOT)](#pnl-by-hour-of-the-day-boxplot)
- [PF resampling](#pf-resampling)
- [PF Plotting](#pf-plotting)
- [Key Portfolio Analysis Methods \& Properties](#key-portfolio-analysis-methods--properties)
- [1. **Basic Portfolio Metrics**](#1-basic-portfolio-metrics)
- [2. **Comprehensive Stats Method**](#2-comprehensive-stats-method)
- [3. **Trade Analysis**](#3-trade-analysis)
- [4. **Drawdown Analysis**](#4-drawdown-analysis)
- [5. **Order Analysis**](#5-order-analysis)
- [6. **Custom Metrics**](#6-custom-metrics)
- [7. **Visualization Methods**](#7-visualization-methods)
- [8. **Advanced Analysis Examples**](#8-advanced-analysis-examples)
- [Entries/exits visual analysis](#entriesexits-visual-analysis)
- [Configuration](#configuration)
- [Optimalization](#optimalization)
@ -104,7 +113,7 @@ ollCov.run(ts1, ts2, [2, 3], some_arg="some_value")
```
# FETCHING DATA
# Fetching Data
```python
#fetching from remote db
from lib.db import Connection
@ -984,6 +993,150 @@ pf.trades.plot_expanding_mfe_returns().show() #expanding mea/mfe returns
```
[Plot Edge ratio](http://5.161.179.223:8000/static/js/vbt/features/analysis/index.html#edge-ratio) `pf.trades.plot_running_edge_ratio()`
I'll research VectorBT PRO's portfolio analysis capabilities for you. Let me search for information about analyzing Portfolio objects, particularly those created by `Portfolio.from_signal`.Let me search for the correct Portfolio creation methods and analysis capabilities:Now let me search specifically for Portfolio analysis methods and attributes:Let me search for more specific information about Portfolio methods and examples:Let me search for specific Portfolio methods that provide analysis capabilities:Now let me find information about the Portfolio.from_signals method:Let me run some code to test the Portfolio analysis capabilities:Let me search for more specific Portfolio analysis examples and metrics:Based on my research of VectorBT PRO's Portfolio analysis capabilities, here's a comprehensive overview of how to analyze Portfolio objects (including those created by `Portfolio.from_signals`):
## Key Portfolio Analysis Methods & Properties
### 1. **Basic Portfolio Metrics**
The Portfolio object provides numerous built-in properties for analysis:
- **Returns & Performance:**
- `pf.returns` - Portfolio returns time series
- `pf.total_return` - Total return percentage
- `pf.annualized_return` - Annualized return
- `pf.cumulative_returns` - Cumulative returns time series
- **Risk Metrics:**
- `pf.sharpe_ratio` - Sharpe ratio
- `pf.sortino_ratio` - Sortino ratio
- `pf.max_drawdown` - Maximum drawdown
- `pf.annualized_volatility` - Annualized volatility
- `pf.value_at_risk` - Value at Risk
- `pf.cond_value_at_risk` - Conditional Value at Risk
- **Portfolio Value & Cash:**
- `pf.value` - Portfolio value time series
- `pf.final_value` - Final portfolio value
- `pf.cash` - Cash holdings over time
- `pf.asset_value` - Asset value over time
### 2. **Comprehensive Stats Method**
The most powerful analysis tool is the `stats()` method:
```python
# Get default statistics
pf.stats()
# Get specific metrics
pf.stats(['total_return', 'sharpe_ratio', 'max_dd', 'total_trades'])
# Get all available metrics
pf.stats('all')
```
**Common metric names for `stats()`:**
- `'total_return'` - Total return percentage
- `'total_trades'` - Number of trades
- `'win_rate'` - Winning trade percentage
- `'sharpe_ratio'` - Sharpe ratio
- `'sortino_ratio'` - Sortino ratio
- `'max_dd'` - Maximum drawdown (note: `max_dd`, not `max_drawdown`)
- `'calmar_ratio'` - Calmar ratio
- `'omega_ratio'` - Omega ratio
- `'expectancy'` - Expected value per trade
- `'profit_factor'` - Profit factor
- `'best_trade'` - Best trade return
- `'worst_trade'` - Worst trade return
- `'avg_winning_trade'` - Average winning trade
- `'avg_losing_trade'` - Average losing trade
### 3. **Trade Analysis**
Access detailed trade information through the `trades` property:
```python
# Trade statistics
pf.trades.stats()
pf.trades.count() # Total number of trades
pf.trades.win_rate # Win rate
pf.trades.profit_factor # Profit factor
# Direction-specific analysis
pf.trades.direction_long.count() # Number of long trades
pf.trades.direction_short.count() # Number of short trades
pf.trades.direction_long.pnl.sum() # Total long P&L
pf.trades.direction_short.pnl.sum() # Total short P&L
# Trade records
pf.trades.records_readable # Human-readable trade records
pf.trade_history # Detailed trade history DataFrame
```
### 4. **Drawdown Analysis**
Analyze drawdowns using the `drawdowns` property:
```python
pf.drawdowns.stats() # Drawdown statistics
pf.drawdowns.max_duration # Maximum drawdown duration
pf.drawdown # Drawdown time series
```
### 5. **Order Analysis**
Examine order execution details:
```python
pf.orders.stats() # Order statistics
pf.orders.records_readable # Human-readable order records
```
### 6. **Custom Metrics**
You can add custom metrics to the Portfolio class:
```python
# Add custom metrics
vbt.Portfolio.metrics["skew"] = dict(title="Skew", calc_func="returns.skew")
vbt.Portfolio.metrics["kurtosis"] = dict(title="Kurtosis", calc_func="returns.kurtosis")
# Then use in stats
pf.stats(['skew', 'kurtosis'])
```
### 7. **Visualization Methods**
Portfolio objects have built-in plotting capabilities:
```python
pf.plot() # Default portfolio plot
pf.plot_value() # Portfolio value over time
pf.plot_cumulative_returns() # Cumulative returns
pf.plot_drawdowns() # Drawdown plot
pf.plot_underwater() # Underwater plot
pf.trades.plot() # Trade analysis plots
```
### 8. **Advanced Analysis Examples**
**Multi-parameter analysis:**
```python
# Combine multiple metrics into DataFrame
metrics = [
pf.total_return,
pf.trades.win_rate,
pf.sharpe_ratio,
pf.max_drawdown,
pf.trades.count()
]
keys = ['total_return', 'win_rate', 'sharpe_ratio', 'max_drawdown', 'trade_count']
combined_stats = pd.concat(metrics, axis=1, keys=keys)
```
**Performance optimization:**
- Use `save_returns=True` in `Portfolio.from_signals()` to pre-calculate returns and speed up metric calculations
- Enable caching for repeated analysis
This comprehensive analysis framework allows you to thoroughly evaluate portfolio performance from multiple angles - returns, risk, trade efficiency, and drawdown characteristics.
Would you like me to help you implement any specific analysis workflow, or do you have questions about particular metrics or methods?
## Entries/exits visual analysis
```python
@ -1471,3 +1624,4 @@ nyse = get_calendar('NYSE')
# Create a CustomBusinessDay object using the NYSE trading calendar
custom_bd = CustomBusinessDay(holidays=nyse.holidays().holidays, weekmask=nyse.weekmask, calendar=nyse)
```

View File

@ -0,0 +1,893 @@
Custom metrics in VectorBT PRO, which is a powerful feature for extending portfolio analysis beyond the built-in metrics.Now let me provide a comprehensive elaboration on custom metrics in VectorBT PRO:
# Custom Metrics in VectorBT PRO
Custom metrics in VectorBT PRO provide a powerful way to extend portfolio analysis beyond the built-in metrics. They allow you to calculate domain-specific metrics, implement proprietary performance measures, or create metrics tailored to your specific trading strategies.
## 1. Understanding the Metrics System
### Built-in Metrics Structure
VectorBT PRO uses a configuration-based approach where metrics are stored in `Portfolio.metrics` as a `HybridConfig`:
```python
# View all available metrics
print(vbt.Portfolio.metrics)
# Get specific metric configuration
print(vbt.Portfolio.metrics['sharpe_ratio'])
```
### Metric Configuration Structure
Each metric is defined as a dictionary with specific keys:
```python
metric_config = {
'title': 'My Custom Metric', # Display name
'calc_func': calculation_function, # Function to calculate the metric
'tags': ['custom', 'risk'], # Tags for filtering
'apply_to_timedelta': False, # Whether to convert to timedelta
'agg_func': None, # Aggregation function
'resolve_calc_func': True, # Whether to resolve attributes
# ... other configuration options
}
```
## 2. Creating Custom Metrics
### Method 1: Simple Function-Based Metrics
```python
# Add a simple custom metric
vbt.Portfolio.metrics['total_bars'] = dict(
title='Total Bars',
calc_func=lambda self: len(self.wrapper.index)
)
# Add skewness and kurtosis
vbt.Portfolio.metrics['skew'] = dict(
title='Skew',
calc_func='returns.skew'
)
vbt.Portfolio.metrics['kurtosis'] = dict(
title='Kurtosis',
calc_func='returns.kurtosis'
)
```
### Method 2: Complex Custom Calculations
```python
# Custom metric with multiple parameters
def total_return_no_fees(self, orders):
"""Calculate total return without fees"""
return (self.total_profit + orders.fees.sum()) / self.get_init_cash() * 100
vbt.Portfolio.metrics['total_return_no_fees'] = dict(
title='Total Return (No Fees) [%]',
calc_func=total_return_no_fees,
resolve_orders=True # Automatically resolve orders parameter
)
```
### Method 3: Using Lambda Functions with Settings
```python
# PnL in dollar terms (for futures trading)
vbt.Portfolio.metrics['pnl_dollars'] = dict(
title='PnL ($)',
calc_func=lambda self, settings: (self.value.iloc[-1] - self.value.iloc[0]) * 50,
resolve_calc_func=False # Don't resolve attributes automatically
)
```
## 3. Advanced Custom Metrics
### Quantile-Based Metrics
```python
def value_at_risk_custom(returns, confidence_level=0.05):
"""Custom VaR calculation"""
return returns.quantile(confidence_level)
vbt.Portfolio.metrics['custom_var'] = dict(
title='Custom VaR (5%)',
calc_func=value_at_risk_custom,
resolve_returns=True,
confidence_level=0.05
)
```
### Multi-Component Metrics
```python
def comprehensive_trade_stats(trades):
"""Return multiple trade statistics"""
return {
'long_trades': trades.direction_long.count(),
'short_trades': trades.direction_short.count(),
'long_pnl': trades.direction_long.pnl.sum(),
'short_pnl': trades.direction_short.pnl.sum(),
'avg_trade_duration': trades.duration.mean()
}
vbt.Portfolio.metrics['trade_breakdown'] = dict(
title='Trade Breakdown',
calc_func=comprehensive_trade_stats,
resolve_trades=True
)
```
### Time-Based Metrics
```python
def monthly_returns_volatility(returns):
"""Calculate monthly returns volatility"""
monthly_returns = returns.resample('M').sum()
return monthly_returns.std() * np.sqrt(12)
vbt.Portfolio.metrics['monthly_vol'] = dict(
title='Monthly Volatility',
calc_func=monthly_returns_volatility,
resolve_returns=True
)
```
## 4. Metric Resolution and Parameters
### Automatic Parameter Resolution
VectorBT PRO can automatically resolve portfolio attributes as parameters:
```python
# These parameters will be automatically resolved:
vbt.Portfolio.metrics['custom_metric'] = dict(
title='Custom Metric',
calc_func=lambda returns, trades, orders: calculation_logic(returns, trades, orders),
resolve_returns=True, # Passes self.returns
resolve_trades=True, # Passes self.trades
resolve_orders=True # Passes self.orders
)
```
### Common Resolvable Parameters
- `self` - The portfolio instance
- `returns` - Portfolio returns
- `trades` - Trade records
- `orders` - Order records
- `drawdowns` - Drawdown records
- `value` - Portfolio value
- `close` - Close prices
- `init_cash` - Initial cash
- `total_profit` - Total profit
- `wrapper` - Array wrapper (for index/column info)
## 5. Global vs Instance-Level Metrics
### Global Metrics (Class-Level)
```python
# Add to all future Portfolio instances
vbt.Portfolio.metrics['my_metric'] = metric_config
# Or modify settings globally
vbt.settings.portfolio['stats']['metrics'] = list(vbt.Portfolio.metrics.items()) + [
('my_metric', metric_config)
]
```
### Instance-Level Metrics
```python
# Add to specific portfolio instance
pf._metrics['my_metric'] = metric_config
# Then use it
pf.stats(['my_metric'])
```
## 6. Using Custom Metrics
### Basic Usage
```python
# Calculate specific custom metrics
pf.stats(['total_bars', 'skew', 'kurtosis'])
# Calculate all metrics including custom ones
pf.stats('all')
# Filter by tags
pf.stats(tags=['custom'])
```
### Advanced Usage with Settings
```python
# Use custom metrics in optimization
results = []
for param in parameter_combinations:
pf = vbt.Portfolio.from_signals(close, entries, exits, **param)
stats = pf.stats(['total_return', 'sharpe_ratio', 'my_custom_metric'])
results.append(stats)
# Create comparison DataFrame
comparison_df = pd.DataFrame(results)
```
## 7. Real-World Examples
### Futures Trading Metrics
```python
# Point-based P&L for futures
vbt.Portfolio.metrics['pnl_points'] = dict(
title='P&L (Points)',
calc_func=lambda self: (self.value.iloc[-1] - self.value.iloc[0]) / self.close.iloc[0] * 10000
)
# Risk-adjusted return for futures
vbt.Portfolio.metrics['risk_adjusted_return'] = dict(
title='Risk Adjusted Return',
calc_func=lambda self, returns: self.total_return / returns.std() * np.sqrt(252),
resolve_returns=True
)
```
### Intraday Strategy Metrics
```python
# Time-of-day analysis
def intraday_performance(orders):
"""Analyze performance by hour of day"""
order_df = orders.records_readable
order_df['hour'] = order_df.index.hour
return order_df.groupby('hour')['PnL'].mean()
vbt.Portfolio.metrics['hourly_performance'] = dict(
title='Hourly Performance',
calc_func=intraday_performance,
resolve_orders=True
)
```
### Market Regime Metrics
```python
def regime_performance(returns, benchmark_returns):
"""Performance in different market regimes"""
bull_mask = benchmark_returns > benchmark_returns.quantile(0.6)
bear_mask = benchmark_returns < benchmark_returns.quantile(0.4)
return {
'bull_return': returns[bull_mask].mean(),
'bear_return': returns[bear_mask].mean(),
'bull_sharpe': returns[bull_mask].mean() / returns[bull_mask].std() * np.sqrt(252),
'bear_sharpe': returns[bear_mask].mean() / returns[bear_mask].std() * np.sqrt(252)
}
vbt.Portfolio.metrics['regime_analysis'] = dict(
title='Market Regime Analysis',
calc_func=regime_performance,
resolve_returns=True,
resolve_bm_returns=True
)
```
## 8. Best Practices
### 1. Naming Conventions
- Use descriptive names: `monthly_volatility` instead of `mv`
- Include units in title: `'Max Drawdown [%]'`
- Use consistent naming patterns
### 2. Error Handling
```python
def robust_metric(returns):
"""Metric with error handling"""
try:
if len(returns) < 2:
return np.nan
return returns.std() * np.sqrt(252)
except Exception as e:
print(f"Error calculating metric: {e}")
return np.nan
```
### 3. Performance Optimization
```python
# Use vectorized operations
def efficient_metric(returns):
"""Efficient vectorized calculation"""
return returns.rolling(30).std().mean()
# Avoid loops when possible
def inefficient_metric(returns):
"""Avoid this approach"""
results = []
for i in range(len(returns)):
results.append(some_calculation(returns.iloc[i]))
return np.mean(results)
```
### 4. Documentation
```python
vbt.Portfolio.metrics['documented_metric'] = dict(
title='Well Documented Metric',
calc_func=lambda returns: returns.std() * np.sqrt(252),
resolve_returns=True,
tags=['custom', 'risk', 'volatility'],
# Add description in comments or docstrings
)
```
## 9. Common Pitfalls and Solutions
### Pitfall 1: Metric Not Available After Creation
```python
# ❌ Wrong: Metric added after portfolio creation
pf = vbt.Portfolio.from_signals(...)
vbt.Portfolio.metrics['my_metric'] = metric_config
pf.stats(['my_metric']) # KeyError!
# ✅ Correct: Add metric before portfolio creation
vbt.Portfolio.metrics['my_metric'] = metric_config
pf = vbt.Portfolio.from_signals(...)
pf.stats(['my_metric']) # Works!
```
### Pitfall 2: Incorrect Parameter Resolution
```python
# ❌ Wrong: Using external variables
portfolio_instance = some_portfolio
vbt.Portfolio.metrics['bad_metric'] = dict(
calc_func=lambda self: portfolio_instance.total_return # External reference
)
# ✅ Correct: Using self parameter
vbt.Portfolio.metrics['good_metric'] = dict(
calc_func=lambda self: self.total_return # Self reference
)
```
### Pitfall 3: Missing Error Handling
```python
# ❌ Wrong: No error handling
def risky_metric(trades):
return trades.pnl.sum() / trades.duration.mean() # Division by zero possible
# ✅ Correct: With error handling
def safe_metric(trades):
if len(trades) == 0 or trades.duration.mean() == 0:
return np.nan
return trades.pnl.sum() / trades.duration.mean()
```
Custom metrics in VectorBT PRO provide unlimited flexibility to analyze your trading strategies exactly how you need. They integrate seamlessly with the existing stats system and can be used in optimization, comparison, and reporting workflows.
I'll provide a comprehensive analysis of VectorBT PRO's `pf.trades` analysis capabilities, with a focus on the specific metrics you mentioned.# Comprehensive VectorBT PRO `pf.trades` Analysis
The `pf.trades` object in VectorBT PRO provides extensive capabilities for analyzing trading performance. Here's a comprehensive guide focusing on directional analysis, temporal patterns, and advanced trade analytics.
## 1. Basic Trade Analysis
### Trade Counts by Direction
```python
# Basic trade counts
total_trades = pf.trades.count()
long_trades = pf.trades.direction_long.count()
short_trades = pf.trades.direction_short.count()
print(f"Total trades: {total_trades}")
print(f"Long trades: {long_trades}")
print(f"Short trades: {short_trades}")
# Alternative using records
trade_records = pf.trades.records_readable
direction_counts = trade_records['Direction'].value_counts()
print(f"\nDirection breakdown:\n{direction_counts}")
```
### P&L Analysis by Direction
```python
# Total P&L by direction
long_pnl = pf.trades.direction_long.pnl.sum()
short_pnl = pf.trades.direction_short.pnl.sum()
total_pnl = pf.trades.pnl.sum()
print(f"Long P&L: {long_pnl:.2f}")
print(f"Short P&L: {short_pnl:.2f}")
print(f"Total P&L: {total_pnl:.2f}")
# P&L statistics by direction
long_stats = pf.trades.direction_long.pnl.describe()
short_stats = pf.trades.direction_short.pnl.describe()
```
## 2. Daily P&L Analysis
### Daily P&L Calculation
```python
# Method 1: Using trade records with date grouping
trade_records = pf.trades.records_readable
trade_records['exit_date'] = trade_records.index.date
# Daily P&L overall
daily_pnl = trade_records.groupby('exit_date')['PnL'].sum()
# Daily P&L by direction
daily_pnl_by_direction = trade_records.groupby(['exit_date', 'Direction'])['PnL'].sum().unstack(fill_value=0)
print("Daily P&L by Direction:")
print(daily_pnl_by_direction.head())
```
### Daily P&L for Each Direction
```python
# Separate long and short daily P&L
long_trades_records = trade_records[trade_records['Direction'] == 'Long']
short_trades_records = trade_records[trade_records['Direction'] == 'Short']
daily_long_pnl = long_trades_records.groupby('exit_date')['PnL'].sum()
daily_short_pnl = short_trades_records.groupby('exit_date')['PnL'].sum()
# Combine into comprehensive daily analysis
daily_analysis = pd.DataFrame({
'Total_PnL': daily_pnl,
'Long_PnL': daily_long_pnl,
'Short_PnL': daily_short_pnl,
'Long_Trades': long_trades_records.groupby('exit_date').size(),
'Short_Trades': short_trades_records.groupby('exit_date').size()
}).fillna(0)
print("Daily Trade Analysis:")
print(daily_analysis.head())
```
## 3. Hourly P&L Analysis by Exit Time
### Hourly P&L by Direction
```python
# Extract hour from exit time
trade_records = pf.trades.records_readable
trade_records['exit_hour'] = trade_records.index.hour
# Hourly P&L analysis
hourly_pnl_analysis = trade_records.groupby(['exit_hour', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count'],
'Return': ['mean', 'std']
}).round(4)
print("Hourly P&L Analysis by Direction:")
print(hourly_pnl_analysis)
# Separate analysis for each direction
hourly_long_pnl = trade_records[trade_records['Direction'] == 'Long'].groupby('exit_hour')['PnL'].agg(['sum', 'mean', 'count'])
hourly_short_pnl = trade_records[trade_records['Direction'] == 'Short'].groupby('exit_hour')['PnL'].agg(['sum', 'mean', 'count'])
print("\nHourly Long P&L:")
print(hourly_long_pnl)
print("\nHourly Short P&L:")
print(hourly_short_pnl)
```
### Advanced Hourly Analysis
```python
# Create comprehensive hourly performance matrix
def hourly_performance_analysis(trades_records):
"""Comprehensive hourly performance analysis"""
# Add time components
trades_records['exit_hour'] = trades_records.index.hour
trades_records['entry_hour'] = pd.to_datetime(trades_records['Entry Index']).dt.hour
# Hourly exit analysis
hourly_stats = trades_records.groupby(['exit_hour', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count', 'std'],
'Return': ['mean', 'std'],
'Size': 'mean'
}).round(4)
return hourly_stats
hourly_performance = hourly_performance_analysis(trade_records)
```
## 4. Day of Week Analysis
### P&L by Day of Week and Direction
```python
# Add day of week analysis
trade_records['exit_day_of_week'] = trade_records.index.day_name()
trade_records['exit_weekday'] = trade_records.index.weekday # 0=Monday, 6=Sunday
# Day of week P&L analysis
dow_analysis = trade_records.groupby(['exit_day_of_week', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count'],
'Return': ['mean', 'std'],
'Size': 'mean'
}).round(4)
print("Day of Week Analysis:")
print(dow_analysis)
# Pivot for easier viewing
dow_pivot = trade_records.pivot_table(
index='exit_day_of_week',
columns='Direction',
values='PnL',
aggfunc=['sum', 'mean', 'count'],
fill_value=0
)
print("\nDay of Week Pivot Analysis:")
print(dow_pivot)
```
### Advanced Day of Week Patterns
```python
# Create comprehensive day of week analysis
def day_of_week_analysis(trades_records):
"""Comprehensive day of week performance analysis"""
# Ensure we have day names in proper order
day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
trades_records['exit_day_name'] = trades_records.index.day_name()
# Group by day and direction
dow_stats = trades_records.groupby(['exit_day_name', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count', 'std'],
'Return': ['mean', 'std'],
'Size': 'mean',
'Entry Fees': 'mean',
'Exit Fees': 'mean'
}).round(4)
# Reorder by day
dow_stats = dow_stats.reindex(day_order, level=0)
return dow_stats
dow_comprehensive = day_of_week_analysis(trade_records)
```
## 5. Advanced Temporal Analysis
### Combined Time Pattern Analysis
```python
# Create comprehensive time pattern analysis
def comprehensive_time_analysis(pf):
"""Complete temporal analysis of trades"""
trades_records = pf.trades.records_readable
# Add all time components
trades_records['exit_hour'] = trades_records.index.hour
trades_records['exit_day_name'] = trades_records.index.day_name()
trades_records['exit_month'] = trades_records.index.month
trades_records['exit_date'] = trades_records.index.date
# 1. Hourly analysis
hourly_stats = trades_records.groupby(['exit_hour', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count'],
'Return': ['mean', 'std']
}).round(4)
# 2. Daily analysis
daily_stats = trades_records.groupby(['exit_day_name', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count'],
'Return': ['mean', 'std']
}).round(4)
# 3. Monthly analysis
monthly_stats = trades_records.groupby(['exit_month', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count'],
'Return': ['mean', 'std']
}).round(4)
# 4. Combined hour-day analysis
hour_day_stats = trades_records.groupby(['exit_day_name', 'exit_hour', 'Direction']).agg({
'PnL': ['sum', 'mean', 'count']
}).round(4)
return {
'hourly': hourly_stats,
'daily': daily_stats,
'monthly': monthly_stats,
'hour_day': hour_day_stats
}
# Execute comprehensive analysis
time_analysis = comprehensive_time_analysis(pf)
# Display results
print("=== HOURLY ANALYSIS ===")
print(time_analysis['hourly'])
print("\n=== DAILY ANALYSIS ===")
print(time_analysis['daily'])
print("\n=== MONTHLY ANALYSIS ===")
print(time_analysis['monthly'])
```
## 6. Custom Metrics for Trade Analysis
### Custom Trade Metrics
```python
# Add custom metrics to Portfolio for directional analysis
vbt.Portfolio.metrics['long_trade_count'] = dict(
title='Long Trade Count',
calc_func=lambda trades: trades.direction_long.count(),
resolve_trades=True
)
vbt.Portfolio.metrics['short_trade_count'] = dict(
title='Short Trade Count',
calc_func=lambda trades: trades.direction_short.count(),
resolve_trades=True
)
vbt.Portfolio.metrics['long_pnl_total'] = dict(
title='Long P&L Total',
calc_func=lambda trades: trades.direction_long.pnl.sum(),
resolve_trades=True
)
vbt.Portfolio.metrics['short_pnl_total'] = dict(
title='Short P&L Total',
calc_func=lambda trades: trades.direction_short.pnl.sum(),
resolve_trades=True
)
# Temporal metrics
vbt.Portfolio.metrics['best_hour_pnl'] = dict(
title='Best Hour P&L',
calc_func=lambda trades: trades.records_readable.groupby(trades.records_readable.index.hour)['PnL'].sum().max(),
resolve_trades=True
)
vbt.Portfolio.metrics['worst_hour_pnl'] = dict(
title='Worst Hour P&L',
calc_func=lambda trades: trades.records_readable.groupby(trades.records_readable.index.hour)['PnL'].sum().min(),
resolve_trades=True
)
```
## 7. Performance Analysis Functions
### Comprehensive Trade Performance Function
```python
def analyze_trade_performance(pf):
"""Comprehensive trade performance analysis"""
trades = pf.trades
records = trades.records_readable
# Basic directional statistics
direction_stats = {
'Long': {
'count': trades.direction_long.count(),
'total_pnl': trades.direction_long.pnl.sum(),
'avg_pnl': trades.direction_long.pnl.mean(),
'win_rate': trades.direction_long.win_rate,
'profit_factor': trades.direction_long.profit_factor
},
'Short': {
'count': trades.direction_short.count(),
'total_pnl': trades.direction_short.pnl.sum(),
'avg_pnl': trades.direction_short.pnl.mean(),
'win_rate': trades.direction_short.win_rate,
'profit_factor': trades.direction_short.profit_factor
}
}
# Temporal analysis
records['exit_hour'] = records.index.hour
records['exit_day'] = records.index.day_name()
records['exit_date'] = records.index.date
# Hourly P&L by direction
hourly_pnl = records.groupby(['exit_hour', 'Direction'])['PnL'].agg(['sum', 'mean', 'count'])
# Daily P&L by direction
daily_pnl = records.groupby(['exit_day', 'Direction'])['PnL'].agg(['sum', 'mean', 'count'])
# Date-based P&L
date_pnl = records.groupby(['exit_date', 'Direction'])['PnL'].agg(['sum', 'mean', 'count'])
# Best/worst performing times
best_hours = records.groupby(['exit_hour', 'Direction'])['PnL'].sum().groupby('Direction').idxmax()
worst_hours = records.groupby(['exit_hour', 'Direction'])['PnL'].sum().groupby('Direction').idxmin()
return {
'direction_stats': direction_stats,
'hourly_pnl': hourly_pnl,
'daily_pnl': daily_pnl,
'date_pnl': date_pnl,
'best_hours': best_hours,
'worst_hours': worst_hours
}
# Execute analysis
performance_analysis = analyze_trade_performance(pf)
```
## 8. Visualization Functions
### Trade Performance Visualization
```python
def plot_trade_performance(pf):
"""Create comprehensive trade performance plots"""
import plotly.graph_objects as go
from plotly.subplots import make_subplots
records = pf.trades.records_readable
records['exit_hour'] = records.index.hour
records['exit_day'] = records.index.day_name()
# Create subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=['Hourly P&L by Direction', 'Daily P&L by Direction',
'P&L Distribution', 'Cumulative P&L by Direction'],
specs=[[{"secondary_y": True}, {"secondary_y": True}],
[{"secondary_y": False}, {"secondary_y": False}]]
)
# Hourly P&L
hourly_long = records[records['Direction'] == 'Long'].groupby('exit_hour')['PnL'].sum()
hourly_short = records[records['Direction'] == 'Short'].groupby('exit_hour')['PnL'].sum()
fig.add_trace(go.Bar(x=hourly_long.index, y=hourly_long.values, name='Long Hourly', marker_color='green'), row=1, col=1)
fig.add_trace(go.Bar(x=hourly_short.index, y=hourly_short.values, name='Short Hourly', marker_color='red'), row=1, col=1)
# Daily P&L
day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
daily_long = records[records['Direction'] == 'Long'].groupby('exit_day')['PnL'].sum().reindex(day_order, fill_value=0)
daily_short = records[records['Direction'] == 'Short'].groupby('exit_day')['PnL'].sum().reindex(day_order, fill_value=0)
fig.add_trace(go.Bar(x=daily_long.index, y=daily_long.values, name='Long Daily', marker_color='lightgreen'), row=1, col=2)
fig.add_trace(go.Bar(x=daily_short.index, y=daily_short.values, name='Short Daily', marker_color='lightcoral'), row=1, col=2)
# P&L Distribution
fig.add_trace(go.Histogram(x=records[records['Direction'] == 'Long']['PnL'], name='Long Distribution', opacity=0.7), row=2, col=1)
fig.add_trace(go.Histogram(x=records[records['Direction'] == 'Short']['PnL'], name='Short Distribution', opacity=0.7), row=2, col=1)
# Cumulative P&L
long_cumulative = records[records['Direction'] == 'Long']['PnL'].cumsum()
short_cumulative = records[records['Direction'] == 'Short']['PnL'].cumsum()
fig.add_trace(go.Scatter(y=long_cumulative.values, mode='lines', name='Long Cumulative', line=dict(color='green')), row=2, col=2)
fig.add_trace(go.Scatter(y=short_cumulative.values, mode='lines', name='Short Cumulative', line=dict(color='red')), row=2, col=2)
fig.update_layout(height=800, title_text="Comprehensive Trade Analysis")
return fig
# Create visualization
# trade_plot = plot_trade_performance(pf)
# trade_plot.show()
```
## 9. Advanced Analytics
### Trade Streaks and Patterns
```python
def analyze_trade_patterns(pf):
"""Analyze trade patterns and streaks"""
trades = pf.trades
records = trades.records_readable
# Winning and losing streaks
winning_streaks = trades.winning_streak.records_readable
losing_streaks = trades.losing_streak.records_readable
# Pattern analysis
patterns = {
'longest_winning_streak': winning_streaks['Duration'].max() if len(winning_streaks) > 0 else 0,
'longest_losing_streak': losing_streaks['Duration'].max() if len(losing_streaks) > 0 else 0,
'avg_winning_streak': winning_streaks['Duration'].mean() if len(winning_streaks) > 0 else 0,
'avg_losing_streak': losing_streaks['Duration'].mean() if len(losing_streaks) > 0 else 0,
}
# Direction-specific patterns
long_patterns = analyze_direction_patterns(trades.direction_long)
short_patterns = analyze_direction_patterns(trades.direction_short)
return {
'overall_patterns': patterns,
'long_patterns': long_patterns,
'short_patterns': short_patterns
}
def analyze_direction_patterns(direction_trades):
"""Analyze patterns for specific direction"""
if direction_trades.count() == 0:
return {}
return {
'total_trades': direction_trades.count(),
'win_rate': direction_trades.win_rate,
'profit_factor': direction_trades.profit_factor,
'avg_winner': direction_trades.winning.pnl.mean() if direction_trades.winning.count() > 0 else 0,
'avg_loser': direction_trades.losing.pnl.mean() if direction_trades.losing.count() > 0 else 0,
'largest_winner': direction_trades.pnl.max(),
'largest_loser': direction_trades.pnl.min(),
'total_pnl': direction_trades.pnl.sum()
}
# Execute pattern analysis
pattern_analysis = analyze_trade_patterns(pf)
```
## 10. Summary Report Function
### Comprehensive Trade Report
```python
def generate_trade_report(pf):
"""Generate comprehensive trade analysis report"""
print("="*80)
print("COMPREHENSIVE TRADE ANALYSIS REPORT")
print("="*80)
# Basic Statistics
trades = pf.trades
total_trades = trades.count()
long_trades = trades.direction_long.count()
short_trades = trades.direction_short.count()
print(f"\n📊 BASIC STATISTICS")
print(f"Total Trades: {total_trades}")
print(f"Long Trades: {long_trades} ({long_trades/total_trades*100:.1f}%)")
print(f"Short Trades: {short_trades} ({short_trades/total_trades*100:.1f}%)")
# P&L Analysis
print(f"\n💰 P&L ANALYSIS")
print(f"Total P&L: ${trades.pnl.sum():.2f}")
print(f"Long P&L: ${trades.direction_long.pnl.sum():.2f}")
print(f"Short P&L: ${trades.direction_short.pnl.sum():.2f}")
print(f"Average P&L per Trade: ${trades.pnl.mean():.2f}")
# Temporal Analysis
records = trades.records_readable
records['exit_hour'] = records.index.hour
records['exit_day'] = records.index.day_name()
print(f"\n⏰ TEMPORAL ANALYSIS")
# Best/Worst Hours
hourly_pnl = records.groupby('exit_hour')['PnL'].sum()
best_hour = hourly_pnl.idxmax()
worst_hour = hourly_pnl.idxmin()
print(f"Best Hour: {best_hour}:00 (${hourly_pnl[best_hour]:.2f})")
print(f"Worst Hour: {worst_hour}:00 (${hourly_pnl[worst_hour]:.2f})")
# Best/Worst Days
daily_pnl = records.groupby('exit_day')['PnL'].sum()
best_day = daily_pnl.idxmax()
worst_day = daily_pnl.idxmin()
print(f"Best Day: {best_day} (${daily_pnl[best_day]:.2f})")
print(f"Worst Day: {worst_day} (${daily_pnl[worst_day]:.2f})")
# Direction Performance
print(f"\n📈 DIRECTION PERFORMANCE")
if long_trades > 0:
print(f"Long Win Rate: {trades.direction_long.win_rate:.2%}")
print(f"Long Profit Factor: {trades.direction_long.profit_factor:.2f}")
if short_trades > 0:
print(f"Short Win Rate: {trades.direction_short.win_rate:.2%}")
print(f"Short Profit Factor: {trades.direction_short.profit_factor:.2f}")
print("="*80)
# Generate report
generate_trade_report(pf)
```
This comprehensive analysis framework provides all the tools needed to analyze `pf.trades` with particular focus on:
1. **Direction-specific analysis** - Separate analysis for long and short trades
2. **Daily P&L patterns** - Understanding daily performance patterns
3. **Hourly P&L by direction** - Identifying optimal trading hours for each direction
4. **Day of week analysis** - Finding the best/worst days for different directions
5. **Custom metrics** - Extending the analysis with domain-specific metrics
6. **Visualization tools** - Creating comprehensive performance visualizations
7. **Pattern recognition** - Identifying winning/losing streaks and patterns
8. **Comprehensive reporting** - Generating detailed performance reports
The framework is designed to be modular, allowing you to pick and choose the specific analyses most relevant to your trading strategy evaluation needs.