|
@@ -68,13 +68,45 @@ class PerformanceCalculator:
|
|
# Get entry volume for largest winning/losing trades to calculate percentages
|
|
# Get entry volume for largest winning/losing trades to calculate percentages
|
|
largest_winning_entry_volume = 0.0
|
|
largest_winning_entry_volume = 0.0
|
|
largest_losing_entry_volume = 0.0
|
|
largest_losing_entry_volume = 0.0
|
|
|
|
+ largest_winning_token = "N/A"
|
|
|
|
+ largest_losing_token = "N/A"
|
|
|
|
+
|
|
|
|
+ # Track largest trades by ROE for alternative rankings
|
|
|
|
+ largest_winning_roe = 0.0
|
|
|
|
+ largest_losing_roe = 0.0
|
|
|
|
+ largest_winning_roe_token = "N/A"
|
|
|
|
+ largest_losing_roe_token = "N/A"
|
|
|
|
+ largest_winning_roe_pnl = 0.0
|
|
|
|
+ largest_losing_roe_pnl = 0.0
|
|
|
|
|
|
for token in token_stats:
|
|
for token in token_stats:
|
|
|
|
+ # Track largest absolute dollar amounts
|
|
if token.get('largest_winning_cycle_pnl', 0) == largest_winning_cycle and largest_winning_cycle > 0:
|
|
if token.get('largest_winning_cycle_pnl', 0) == largest_winning_cycle and largest_winning_cycle > 0:
|
|
largest_winning_entry_volume = token.get('largest_winning_cycle_entry_volume', 0)
|
|
largest_winning_entry_volume = token.get('largest_winning_cycle_entry_volume', 0)
|
|
|
|
+ largest_winning_token = token['token']
|
|
|
|
|
|
if token.get('largest_losing_cycle_pnl', 0) == largest_losing_cycle and largest_losing_cycle > 0:
|
|
if token.get('largest_losing_cycle_pnl', 0) == largest_losing_cycle and largest_losing_cycle > 0:
|
|
largest_losing_entry_volume = token.get('largest_losing_cycle_entry_volume', 0)
|
|
largest_losing_entry_volume = token.get('largest_losing_cycle_entry_volume', 0)
|
|
|
|
+ largest_losing_token = token['token']
|
|
|
|
+
|
|
|
|
+ # Track largest ROE-based trades
|
|
|
|
+ winning_pnl = token.get('largest_winning_cycle_pnl', 0)
|
|
|
|
+ winning_entry_volume = token.get('largest_winning_cycle_entry_volume', 0)
|
|
|
|
+ if winning_pnl > 0 and winning_entry_volume > 0:
|
|
|
|
+ winning_roe = (winning_pnl / winning_entry_volume) * 100
|
|
|
|
+ if winning_roe > largest_winning_roe:
|
|
|
|
+ largest_winning_roe = winning_roe
|
|
|
|
+ largest_winning_roe_token = token['token']
|
|
|
|
+ largest_winning_roe_pnl = winning_pnl
|
|
|
|
+
|
|
|
|
+ losing_pnl = token.get('largest_losing_cycle_pnl', 0)
|
|
|
|
+ losing_entry_volume = token.get('largest_losing_cycle_entry_volume', 0)
|
|
|
|
+ if losing_pnl > 0 and losing_entry_volume > 0:
|
|
|
|
+ losing_roe = (losing_pnl / losing_entry_volume) * 100
|
|
|
|
+ if losing_roe > largest_losing_roe:
|
|
|
|
+ largest_losing_roe = losing_roe
|
|
|
|
+ largest_losing_roe_token = token['token']
|
|
|
|
+ largest_losing_roe_pnl = losing_pnl
|
|
|
|
|
|
# Calculate percentages for largest trades
|
|
# Calculate percentages for largest trades
|
|
largest_winning_percentage = (largest_winning_cycle / largest_winning_entry_volume * 100) if largest_winning_entry_volume > 0 else 0
|
|
largest_winning_percentage = (largest_winning_cycle / largest_winning_entry_volume * 100) if largest_winning_entry_volume > 0 else 0
|
|
@@ -162,16 +194,25 @@ class PerformanceCalculator:
|
|
'pnl_percentage': worst_token_pnl_pct,
|
|
'pnl_percentage': worst_token_pnl_pct,
|
|
'volume': worst_token_volume,
|
|
'volume': worst_token_volume,
|
|
'pnl_value': worst_token_pnl_value
|
|
'pnl_value': worst_token_pnl_value
|
|
- }
|
|
|
|
|
|
+ },
|
|
|
|
+ 'largest_winning_token': largest_winning_token,
|
|
|
|
+ 'largest_losing_token': largest_losing_token,
|
|
|
|
+ 'largest_winning_roe': largest_winning_roe,
|
|
|
|
+ 'largest_losing_roe': largest_losing_roe,
|
|
|
|
+ 'largest_winning_roe_token': largest_winning_roe_token,
|
|
|
|
+ 'largest_losing_roe_token': largest_losing_roe_token,
|
|
|
|
+ 'largest_winning_roe_pnl': largest_winning_roe_pnl,
|
|
|
|
+ 'largest_losing_roe_pnl': largest_losing_roe_pnl
|
|
}
|
|
}
|
|
|
|
|
|
def get_token_performance(self, limit: int = 20) -> List[Dict[str, Any]]:
|
|
def get_token_performance(self, limit: int = 20) -> List[Dict[str, Any]]:
|
|
- """Get performance stats by token, sorted by total PnL."""
|
|
|
|
|
|
+ """Get performance stats by token, sorted by ROE (Return on Equity)."""
|
|
formatter = get_formatter()
|
|
formatter = get_formatter()
|
|
|
|
|
|
|
|
+ # Get all token stats first, then sort by ROE in Python
|
|
token_stats = self.db._fetch_query(
|
|
token_stats = self.db._fetch_query(
|
|
- "SELECT * FROM token_stats ORDER BY total_realized_pnl DESC LIMIT ?",
|
|
|
|
- (limit,)
|
|
|
|
|
|
+ "SELECT * FROM token_stats",
|
|
|
|
+ ()
|
|
)
|
|
)
|
|
|
|
|
|
for token in token_stats:
|
|
for token in token_stats:
|
|
@@ -186,6 +227,11 @@ class PerformanceCalculator:
|
|
sum_losing = token.get('sum_of_losing_pnl', 0)
|
|
sum_losing = token.get('sum_of_losing_pnl', 0)
|
|
token['profit_factor'] = sum_winning / sum_losing if sum_losing > 0 else float('inf') if sum_winning > 0 else 0
|
|
token['profit_factor'] = sum_winning / sum_losing if sum_losing > 0 else float('inf') if sum_winning > 0 else 0
|
|
|
|
|
|
|
|
+ # Calculate ROE (Return on Equity) - PnL percentage based on entry volume
|
|
|
|
+ total_pnl = token.get('total_realized_pnl', 0)
|
|
|
|
+ entry_volume = token.get('total_entry_volume', 0)
|
|
|
|
+ token['roe_percentage'] = (total_pnl / entry_volume * 100) if entry_volume > 0 else 0
|
|
|
|
+
|
|
# Format durations
|
|
# Format durations
|
|
total_duration = token.get('total_duration_seconds', 0)
|
|
total_duration = token.get('total_duration_seconds', 0)
|
|
avg_duration = total_duration / total_cycles if total_cycles > 0 else 0
|
|
avg_duration = total_duration / total_cycles if total_cycles > 0 else 0
|
|
@@ -194,7 +240,15 @@ class PerformanceCalculator:
|
|
# Token display name (use token as-is)
|
|
# Token display name (use token as-is)
|
|
token['display_name'] = token['token'].upper()
|
|
token['display_name'] = token['token'].upper()
|
|
|
|
|
|
- return token_stats
|
|
|
|
|
|
+ # Sort by ROE percentage (highest to lowest), then by total PnL as tiebreaker
|
|
|
|
+ sorted_tokens = sorted(
|
|
|
|
+ token_stats,
|
|
|
|
+ key=lambda x: (x.get('roe_percentage', 0), x.get('total_realized_pnl', 0)),
|
|
|
|
+ reverse=True
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ # Return top tokens (limit)
|
|
|
|
+ return sorted_tokens[:limit]
|
|
|
|
|
|
def get_balance_history(self, days: int = 30) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
|
|
def get_balance_history(self, days: int = 30) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
|
|
"""Get balance history for the last N days with detailed statistics."""
|
|
"""Get balance history for the last N days with detailed statistics."""
|