execution and training fixes

This commit is contained in:
Dobromir Popov
2025-07-04 20:45:39 +03:00
parent 0c4c682498
commit ed42e7c238
10 changed files with 879 additions and 79 deletions

View File

@ -4716,8 +4716,50 @@ class CleanTradingDashboard:
return 0
def _get_trading_statistics(self) -> Dict[str, Any]:
"""Calculate trading statistics from closed trades"""
"""Get trading statistics from trading executor"""
try:
# Try to get statistics from trading executor first
if self.trading_executor:
executor_stats = self.trading_executor.get_daily_stats()
closed_trades = self.trading_executor.get_closed_trades()
if executor_stats and executor_stats.get('total_trades', 0) > 0:
# Calculate largest win/loss from closed trades
largest_win = 0.0
largest_loss = 0.0
if closed_trades:
for trade in closed_trades:
try:
# Handle both dictionary and object formats
if isinstance(trade, dict):
pnl = trade.get('pnl', 0)
else:
pnl = getattr(trade, 'pnl', 0)
if pnl > 0:
largest_win = max(largest_win, pnl)
elif pnl < 0:
largest_loss = max(largest_loss, abs(pnl))
except Exception as e:
logger.debug(f"Error processing trade for statistics: {e}")
continue
# Map executor stats to dashboard format
return {
'total_trades': executor_stats.get('total_trades', 0),
'winning_trades': executor_stats.get('winning_trades', 0),
'losing_trades': executor_stats.get('losing_trades', 0),
'win_rate': executor_stats.get('win_rate', 0.0) * 100, # Convert to percentage
'avg_win_size': executor_stats.get('avg_winning_trade', 0.0), # Correct mapping
'avg_loss_size': abs(executor_stats.get('avg_losing_trade', 0.0)), # Make positive for display
'largest_win': largest_win,
'largest_loss': largest_loss,
'total_pnl': executor_stats.get('total_pnl', 0.0)
}
# Fallback to dashboard's own trade list if no trading executor
if not self.closed_trades:
return {
'total_trades': 0,

View File

@ -96,6 +96,8 @@ class DashboardComponentManager:
total_trades = trading_stats.get('total_trades', 0)
winning_trades = trading_stats.get('winning_trades', 0)
losing_trades = trading_stats.get('losing_trades', 0)
total_fees = trading_stats.get('total_fees', 0)
breakeven_trades = trading_stats.get('breakeven_trades', 0)
win_rate_class = "text-success" if win_rate >= 50 else "text-warning" if win_rate >= 30 else "text-danger"
@ -106,16 +108,20 @@ class DashboardComponentManager:
html.Div([
html.Span("Win Rate: ", className="small text-muted"),
html.Span(f"{win_rate:.1f}%", className=f"fw-bold {win_rate_class}"),
html.Span(f" ({winning_trades}W/{losing_trades}L)", className="small text-muted")
], className="col-4"),
html.Span(f" ({winning_trades}W/{losing_trades}L/{breakeven_trades}B)", className="small text-muted")
], className="col-3"),
html.Div([
html.Span("Avg Win: ", className="small text-muted"),
html.Span(f"${avg_win:.2f}", className="fw-bold text-success")
], className="col-4"),
], className="col-3"),
html.Div([
html.Span("Avg Loss: ", className="small text-muted"),
html.Span(f"${avg_loss:.2f}", className="fw-bold text-danger")
], className="col-4")
], className="col-3"),
html.Div([
html.Span("Total Fees: ", className="small text-muted"),
html.Span(f"${total_fees:.2f}", className="fw-bold text-warning")
], className="col-3")
], className="row"),
html.Hr(className="my-2")
], className="mb-3")
@ -135,6 +141,7 @@ class DashboardComponentManager:
html.Th("Size", className="small"),
html.Th("Entry", className="small"),
html.Th("Exit", className="small"),
html.Th("Hold (s)", className="small"),
html.Th("P&L", className="small"),
html.Th("Fees", className="small")
])
@ -142,7 +149,7 @@ class DashboardComponentManager:
# Create table rows
rows = []
for trade in closed_trades[-20:]: # Last 20 trades
for trade in closed_trades: # Removed [-20:] to show all trades
# Handle both trade objects and dictionary formats
if hasattr(trade, 'entry_time'):
# This is a trade object
@ -153,15 +160,17 @@ class DashboardComponentManager:
exit_price = getattr(trade, 'exit_price', 0)
pnl = getattr(trade, 'pnl', 0)
fees = getattr(trade, 'fees', 0)
hold_time_seconds = getattr(trade, 'hold_time_seconds', 0.0)
else:
# This is a dictionary format
entry_time = trade.get('entry_time', 'Unknown')
side = trade.get('side', 'UNKNOWN')
size = trade.get('size', 0)
size = trade.get('quantity', trade.get('size', 0)) # Try 'quantity' first, then 'size'
entry_price = trade.get('entry_price', 0)
exit_price = trade.get('exit_price', 0)
pnl = trade.get('pnl', 0)
fees = trade.get('fees', 0)
hold_time_seconds = trade.get('hold_time_seconds', 0.0)
# Format time
if isinstance(entry_time, datetime):
@ -179,6 +188,7 @@ class DashboardComponentManager:
html.Td(f"{size:.3f}", className="small"),
html.Td(f"${entry_price:.2f}", className="small"),
html.Td(f"${exit_price:.2f}", className="small"),
html.Td(f"{hold_time_seconds:.0f}", className="small text-info"),
html.Td(f"${pnl:.2f}", className=f"small {pnl_class}"),
html.Td(f"${fees:.3f}", className="small text-muted")
])
@ -188,11 +198,17 @@ class DashboardComponentManager:
table = html.Table([headers, tbody], className="table table-sm table-striped")
# Wrap the table in a scrollable div
scrollable_table_container = html.Div(
table,
style={'maxHeight': '300px', 'overflowY': 'scroll', 'overflowX': 'hidden'}
)
# Combine statistics header with table
if stats_header:
return html.Div(stats_header + [table])
return html.Div(stats_header + [scrollable_table_container])
else:
return table
return scrollable_table_container
except Exception as e:
logger.error(f"Error formatting closed trades: {e}")