Files
Place-Order-Trading-Bot/trading_dashboard.py
T
cbazzaandClaude Sonnet 4.6 dd2611eaef fix: stage missing code review fixes (7 files)
Files were edited but not staged in earlier commits:
- adaptive_rhythm_manager.py: mt→mt5, pytz→timezone, get_volatility_level, shutdown()
- check_market_regime.py: ADX_THRESHOLD, Wilder EWM, try/finally, UTC timestamp, sys import
- check_system_status.py: remove duplicate cursor.execute
- drawdown_protection.py: float(inf), persist pause state, DB save_setting, Markdown fix
- performance_analysis.py: KeyError export fix, profit factor, drawdown positive, SQL filter
- performance_analysis_simple.py: fromisoformat, numeric bin sort, profit factor
- trading_dashboard.py: st.rerun(), session_state auto-refresh, pathlib DB path, errors=coerce

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 12:25:11 +02:00

441 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
📊 Trading Bot Dashboard - Streamlit
Real-time monitoring für V1.8 Trading Bot
"""
import streamlit as st
import sqlite3
import pandas as pd
from datetime import datetime, timedelta
import plotly.express as px
import plotly.graph_objects as go
from pathlib import Path
# ==========================================
# PAGE CONFIG
# ==========================================
st.set_page_config(
page_title="Trading Bot Dashboard",
page_icon="📊",
layout="wide"
)
# ==========================================
# DATABASE CONNECTION
# ==========================================
DB_PATH = Path(__file__).parent / "trading_bot.db"
@st.cache_resource
def get_connection():
if not DB_PATH.exists():
st.error(f"Database not found: {DB_PATH}")
st.stop()
return sqlite3.connect(str(DB_PATH), check_same_thread=False)
conn = get_connection()
# ==========================================
# HEADER
# ==========================================
st.title("📊 Trading Bot Dashboard V1.8")
st.markdown("---")
# Controls
col1, col2, col3 = st.columns([1, 1, 2])
with col1:
if st.button("🔄 Refresh Data"):
st.cache_data.clear()
st.rerun()
with col2:
auto_refresh = st.toggle("Auto-refresh (30s)")
with col3:
trade_filter = st.selectbox(
"📊 Filter Trades:",
["All Trades", "Live Trades Only", "Historical Only"],
index=1 # Default to "Live Trades Only"
)
if auto_refresh:
if "last_refresh" not in st.session_state:
st.session_state.last_refresh = datetime.now()
elapsed = (datetime.now() - st.session_state.last_refresh).total_seconds()
if elapsed >= 30:
st.session_state.last_refresh = datetime.now()
st.rerun()
else:
st.markdown(f"*Auto-refresh in {30 - int(elapsed)}s...*")
# ==========================================
# LOAD DATA
# ==========================================
@st.cache_data(ttl=30)
def load_all_trades():
query = """
SELECT
ticket, position_id, symbol, strategy_name, type, volume,
entry_price, sl_price, tp_price, entry_time, exit_time,
session, regime, quality, confidence, timeframe_alignment,
risk_amount, risk_pct, net_profit, profit_pct, rr_ratio,
status, exit_reason
FROM trades
ORDER BY entry_time DESC
"""
try:
return pd.read_sql_query(query, conn)
except Exception as e:
st.error(f"Error loading trades: {e}")
return pd.DataFrame()
@st.cache_data(ttl=30)
def load_bot_status():
try:
return pd.read_sql_query("SELECT * FROM bot_status ORDER BY timestamp DESC LIMIT 1", conn)
except Exception as e:
st.error(f"Error loading bot status: {e}")
return pd.DataFrame()
# Load data
df_trades_raw = load_all_trades()
df_status = load_bot_status()
# ==========================================
# APPLY TRADE FILTER
# ==========================================
if trade_filter == "Live Trades Only":
df_trades = df_trades_raw[df_trades_raw['status'] != 'historical'].copy()
st.info(f"📊 Showing **Live Trades Only** (excluding {(df_trades_raw['status'] == 'historical').sum()} historical imports)")
elif trade_filter == "Historical Only":
df_trades = df_trades_raw[df_trades_raw['status'] == 'historical'].copy()
st.info(f"📚 Showing **Historical Trades Only** ({len(df_trades)} trades)")
else: # All Trades
df_trades = df_trades_raw.copy()
live_count = (df_trades['status'] != 'historical').sum()
hist_count = (df_trades['status'] == 'historical').sum()
st.info(f"📊 Showing **All Trades** ({live_count} live + {hist_count} historical)")
# ==========================================
# TOP METRICS
# ==========================================
st.subheader("📈 Key Metrics")
col1, col2, col3, col4, col5 = st.columns(5)
total_trades = len(df_trades)
closed_trades = len(df_trades[df_trades['status'] == 'closed'])
open_trades = len(df_trades[df_trades['status'] == 'open'])
if closed_trades > 0:
winning_trades = len(df_trades[(df_trades['status'] == 'closed') & (df_trades['net_profit'] > 0)])
win_rate = (winning_trades / closed_trades) * 100
total_profit = df_trades[df_trades['status'] == 'closed']['net_profit'].sum()
else:
win_rate = 0
total_profit = 0
with col1:
st.metric("Total Trades", total_trades)
with col2:
st.metric("Open Positions", open_trades)
with col3:
st.metric("Win Rate", f"{win_rate:.1f}%")
with col4:
profit_color = "normal" if total_profit >= 0 else "inverse"
st.metric("Total Profit", f"${total_profit:.2f}", delta=None)
with col5:
if closed_trades > 0:
avg_profit = total_profit / closed_trades
st.metric("Avg Profit/Trade", f"${avg_profit:.2f}")
else:
st.metric("Avg Profit/Trade", "N/A")
st.markdown("---")
# ==========================================
# SESSION FILTER CHECK
# ==========================================
st.subheader("🎯 Session Filter Status")
# Calculate session distribution
session_counts = df_trades['session'].value_counts()
col1, col2 = st.columns([1, 2])
with col1:
st.markdown("#### Session Distribution")
for session in ['ny', 'london', 'asian', 'overlap']:
count = session_counts.get(session, 0)
pct = (count / total_trades * 100) if total_trades > 0 else 0
if session == 'ny':
if count == total_trades:
st.success(f"✅ NY: {count} ({pct:.1f}%) - PERFECT!")
else:
st.warning(f"⚠️ NY: {count} ({pct:.1f}%)")
else:
if count > 0:
st.error(f"❌ {session.upper()}: {count} ({pct:.1f}%) - VIOLATION!")
else:
st.success(f"✅ {session.upper()}: {count} (0%) - Blocked")
with col2:
st.markdown("#### Verdict")
ny_count = session_counts.get('ny', 0)
violation_count = total_trades - ny_count
if total_trades == 0:
st.info("️ No trades yet - waiting for data...")
elif violation_count == 0:
st.success("✅ **SESSION FILTER WORKING PERFECTLY!**")
st.markdown("All trades are in NY session (13:00-21:00 UTC)")
else:
st.error(f"❌ **SESSION FILTER NOT WORKING!**")
st.markdown(f"**{violation_count} trades** ({violation_count/total_trades*100:.1f}%) outside NY session!")
st.markdown("**Action Required:** See FIX_DUPLICATE_SCHEDULER.md")
st.markdown("---")
# ==========================================
# HOURLY DISTRIBUTION
# ==========================================
st.subheader("⏰ Trades by Hour (UTC)")
if not df_trades.empty:
# Extract hour from entry_time (handle both ISO8601 and standard format)
df_trades['hour_utc'] = pd.to_datetime(df_trades['entry_time'], errors='coerce').dt.hour
# Count trades by hour
hourly_dist = df_trades.groupby('hour_utc').size().reset_index(name='count')
# Create visualization
fig = go.Figure()
# Add bars
colors = ['red' if (h < 13 or h >= 21) else 'green' for h in hourly_dist['hour_utc']]
fig.add_trace(go.Bar(
x=hourly_dist['hour_utc'],
y=hourly_dist['count'],
marker_color=colors,
text=hourly_dist['count'],
textposition='outside'
))
# Add NY session marker
fig.add_vrect(x0=13, x1=21, fillcolor="green", opacity=0.1, layer="below", line_width=0)
fig.add_annotation(x=17, y=hourly_dist['count'].max(), text="NY Session (13-21 UTC)", showarrow=False)
fig.update_layout(
xaxis_title="Hour (UTC)",
yaxis_title="Number of Trades",
height=400,
showlegend=False,
xaxis=dict(dtick=1, range=[-0.5, 23.5])
)
st.plotly_chart(fig, use_container_width=True)
# Check for violations
violations = df_trades[(df_trades['hour_utc'] < 13) | (df_trades['hour_utc'] >= 21)]
if not violations.empty:
st.error(f"⚠️ **{len(violations)} trades outside NY hours detected!**")
with st.expander("Show violation details"):
st.dataframe(violations[['ticket', 'session', 'entry_time', 'hour_utc', 'type', 'net_profit']])
else:
st.info("No trades to display")
st.markdown("---")
# ==========================================
# SESSION PERFORMANCE
# ==========================================
st.subheader("📊 Performance by Session")
if closed_trades > 0:
session_perf = df_trades[df_trades['status'] == 'closed'].groupby('session').agg({
'ticket': 'count',
'net_profit': ['sum', 'mean']
}).round(2)
session_perf.columns = ['Trades', 'Total Profit', 'Avg Profit']
# Calculate win rate per session
win_rates = []
for session in session_perf.index:
session_trades = df_trades[(df_trades['status'] == 'closed') & (df_trades['session'] == session)]
wins = len(session_trades[session_trades['net_profit'] > 0])
wr = (wins / len(session_trades) * 100) if len(session_trades) > 0 else 0
win_rates.append(wr)
session_perf['Win Rate %'] = win_rates
# Color code
def color_sessions(row):
if row.name == 'ny':
return ['background-color: #90EE90'] * len(row) # Light green
else:
return ['background-color: #FFB6C6'] * len(row) # Light red
st.dataframe(session_perf.style.apply(color_sessions, axis=1), use_container_width=True)
else:
st.info("No closed trades yet")
st.markdown("---")
# ==========================================
# RECENT TRADES
# ==========================================
st.subheader("📋 Recent Trades (Last 20)")
if not df_trades.empty:
recent = df_trades.head(20).copy()
# Color code session
def highlight_session(row):
if row['session'] == 'ny':
return ['background-color: #90EE90'] * len(row)
else:
return ['background-color: #FFB6C6'] * len(row)
# Select columns
display_cols = ['ticket', 'type', 'session', 'entry_time', 'confidence', 'quality', 'regime', 'status', 'net_profit']
recent_display = recent[display_cols]
st.dataframe(
recent_display.style.apply(highlight_session, axis=1),
use_container_width=True,
height=400
)
else:
st.info("No trades yet")
st.markdown("---")
# ==========================================
# PROFIT OVER TIME
# ==========================================
st.subheader("💰 Cumulative Profit Over Time")
if closed_trades > 0:
profit_timeline = df_trades[df_trades['status'] == 'closed'].copy()
profit_timeline['exit_time'] = pd.to_datetime(profit_timeline['exit_time'], errors='coerce')
profit_timeline = profit_timeline.sort_values('exit_time')
profit_timeline['cumulative_profit'] = profit_timeline['net_profit'].cumsum()
fig = px.line(
profit_timeline,
x='exit_time',
y='cumulative_profit',
title='Cumulative Profit',
labels={'exit_time': 'Date', 'cumulative_profit': 'Profit ($)'}
)
fig.update_traces(line_color='green' if profit_timeline['cumulative_profit'].iloc[-1] > 0 else 'red')
fig.add_hline(y=0, line_dash="dash", line_color="gray")
st.plotly_chart(fig, use_container_width=True)
else:
st.info("No closed trades yet")
st.markdown("---")
# ==========================================
# CONFIDENCE & QUALITY ANALYSIS
# ==========================================
st.subheader("🎯 Signal Quality Analysis")
col1, col2 = st.columns(2)
with col1:
st.markdown("#### Confidence Distribution")
if not df_trades.empty:
fig = px.histogram(
df_trades,
x='confidence',
nbins=20,
title='Trade Confidence Distribution'
)
st.plotly_chart(fig, use_container_width=True)
else:
st.info("No data")
with col2:
st.markdown("#### Quality Breakdown")
if not df_trades.empty:
quality_counts = df_trades['quality'].value_counts()
fig = px.pie(
values=quality_counts.values,
names=quality_counts.index,
title='Signal Quality Distribution'
)
st.plotly_chart(fig, use_container_width=True)
else:
st.info("No data")
st.markdown("---")
# ==========================================
# BOT STATUS
# ==========================================
st.subheader("🤖 Bot Status")
if not df_status.empty:
status = df_status.iloc[0]
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("**Version:**")
st.code(status.get('version', 'N/A'))
with col2:
st.markdown("**Status:**")
bot_status = status.get('status', 'unknown')
if bot_status == 'running':
st.success("🟢 Running")
elif bot_status == 'stopped':
st.error("🔴 Stopped")
else:
st.warning("⚠️ Unknown")
with col3:
st.markdown("**Last Update:**")
st.code(status.get('timestamp', 'N/A'))
if 'config' in status and status['config']:
with st.expander("Show Configuration"):
st.json(status['config'])
else:
st.warning("No bot status available")
st.markdown("---")
# ==========================================
# FOOTER
# ==========================================
st.markdown("---")
st.caption(f"Dashboard last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
st.caption("📊 Trading Bot V1.8 - Aggressive Mode (NY Only)")