lines between trade actions
This commit is contained in:
parent
774debbf75
commit
c6386a3718
187
web/dashboard.py
187
web/dashboard.py
@ -1415,6 +1415,193 @@ class TradingDashboard:
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
# Add closed trades markers with profit/loss styling and connecting lines
|
||||
if self.closed_trades and not df.empty:
|
||||
# Get the timeframe of displayed chart
|
||||
chart_start_time = df.index.min()
|
||||
chart_end_time = df.index.max()
|
||||
|
||||
# Convert chart times to UTC for comparison
|
||||
if isinstance(chart_start_time, pd.Timestamp):
|
||||
chart_start_utc = chart_start_time.tz_localize(None) if chart_start_time.tz is None else chart_start_time.tz_convert('UTC').tz_localize(None)
|
||||
chart_end_utc = chart_end_time.tz_localize(None) if chart_end_time.tz is None else chart_end_time.tz_convert('UTC').tz_localize(None)
|
||||
else:
|
||||
chart_start_utc = pd.to_datetime(chart_start_time).tz_localize(None)
|
||||
chart_end_utc = pd.to_datetime(chart_end_time).tz_localize(None)
|
||||
|
||||
# Filter closed trades to only those within chart timeframe
|
||||
chart_trades = []
|
||||
for trade in self.closed_trades:
|
||||
if not isinstance(trade, dict):
|
||||
continue
|
||||
|
||||
entry_time = trade.get('entry_time')
|
||||
exit_time = trade.get('exit_time')
|
||||
|
||||
if not entry_time or not exit_time:
|
||||
continue
|
||||
|
||||
# Convert times to UTC for comparison
|
||||
if isinstance(entry_time, datetime):
|
||||
entry_time_utc = entry_time.astimezone(timezone.utc).replace(tzinfo=None) if entry_time.tzinfo else entry_time
|
||||
else:
|
||||
continue
|
||||
|
||||
if isinstance(exit_time, datetime):
|
||||
exit_time_utc = exit_time.astimezone(timezone.utc).replace(tzinfo=None) if exit_time.tzinfo else exit_time
|
||||
else:
|
||||
continue
|
||||
|
||||
# Check if trade overlaps with chart timeframe
|
||||
entry_time_pd = pd.to_datetime(entry_time_utc)
|
||||
exit_time_pd = pd.to_datetime(exit_time_utc)
|
||||
|
||||
if (chart_start_utc <= entry_time_pd <= chart_end_utc) or (chart_start_utc <= exit_time_pd <= chart_end_utc):
|
||||
chart_trades.append(trade)
|
||||
|
||||
logger.debug(f"[CHART] Showing {len(chart_trades)} closed trades on chart")
|
||||
|
||||
# Plot closed trades with profit/loss styling
|
||||
profitable_entries_x = []
|
||||
profitable_entries_y = []
|
||||
profitable_exits_x = []
|
||||
profitable_exits_y = []
|
||||
losing_entries_x = []
|
||||
losing_entries_y = []
|
||||
losing_exits_x = []
|
||||
losing_exits_y = []
|
||||
|
||||
# Collect trade points for display
|
||||
for trade in chart_trades:
|
||||
entry_price = trade.get('entry_price', 0)
|
||||
exit_price = trade.get('exit_price', 0)
|
||||
entry_time = trade.get('entry_time')
|
||||
exit_time = trade.get('exit_time')
|
||||
net_pnl = trade.get('net_pnl', 0)
|
||||
side = trade.get('side', 'LONG')
|
||||
|
||||
if not all([entry_price, exit_price, entry_time, exit_time]):
|
||||
continue
|
||||
|
||||
# Convert times to local timezone for display
|
||||
entry_time_local = self._to_local_timezone(entry_time)
|
||||
exit_time_local = self._to_local_timezone(exit_time)
|
||||
|
||||
# Determine if trade was profitable
|
||||
is_profitable = net_pnl > 0
|
||||
|
||||
if is_profitable:
|
||||
profitable_entries_x.append(entry_time_local)
|
||||
profitable_entries_y.append(entry_price)
|
||||
profitable_exits_x.append(exit_time_local)
|
||||
profitable_exits_y.append(exit_price)
|
||||
else:
|
||||
losing_entries_x.append(entry_time_local)
|
||||
losing_entries_y.append(entry_price)
|
||||
losing_exits_x.append(exit_time_local)
|
||||
losing_exits_y.append(exit_price)
|
||||
|
||||
# Add connecting dash line between entry and exit
|
||||
line_color = '#00ff88' if is_profitable else '#ff6b6b'
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=[entry_time_local, exit_time_local],
|
||||
y=[entry_price, exit_price],
|
||||
mode='lines',
|
||||
line=dict(
|
||||
color=line_color,
|
||||
width=2,
|
||||
dash='dash'
|
||||
),
|
||||
name="Trade Path",
|
||||
showlegend=False,
|
||||
hoverinfo='skip'
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
# Add profitable trade markers (filled triangles)
|
||||
if profitable_entries_x:
|
||||
# Entry markers (triangle-up for LONG, triangle-down for SHORT - filled)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=profitable_entries_x,
|
||||
y=profitable_entries_y,
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
color='#00ff88', # Green fill for profitable
|
||||
size=12,
|
||||
symbol='triangle-up',
|
||||
line=dict(color='white', width=1)
|
||||
),
|
||||
name="Profitable Entry",
|
||||
showlegend=True,
|
||||
hovertemplate="<b>PROFITABLE ENTRY</b><br>Price: $%{y:.2f}<br>Time: %{x}<extra></extra>"
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
if profitable_exits_x:
|
||||
# Exit markers (triangle-down for LONG, triangle-up for SHORT - filled)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=profitable_exits_x,
|
||||
y=profitable_exits_y,
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
color='#00ff88', # Green fill for profitable
|
||||
size=12,
|
||||
symbol='triangle-down',
|
||||
line=dict(color='white', width=1)
|
||||
),
|
||||
name="Profitable Exit",
|
||||
showlegend=True,
|
||||
hovertemplate="<b>PROFITABLE EXIT</b><br>Price: $%{y:.2f}<br>Time: %{x}<extra></extra>"
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
# Add losing trade markers (border only triangles)
|
||||
if losing_entries_x:
|
||||
# Entry markers (triangle-up for LONG, triangle-down for SHORT - border only)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=losing_entries_x,
|
||||
y=losing_entries_y,
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
color='rgba(255, 107, 107, 0)', # Transparent fill
|
||||
size=12,
|
||||
symbol='triangle-up',
|
||||
line=dict(color='#ff6b6b', width=2) # Red border for losing
|
||||
),
|
||||
name="Losing Entry",
|
||||
showlegend=True,
|
||||
hovertemplate="<b>LOSING ENTRY</b><br>Price: $%{y:.2f}<br>Time: %{x}<extra></extra>"
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
if losing_exits_x:
|
||||
# Exit markers (triangle-down for LONG, triangle-up for SHORT - border only)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=losing_exits_x,
|
||||
y=losing_exits_y,
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
color='rgba(255, 107, 107, 0)', # Transparent fill
|
||||
size=12,
|
||||
symbol='triangle-down',
|
||||
line=dict(color='#ff6b6b', width=2) # Red border for losing
|
||||
),
|
||||
name="Losing Exit",
|
||||
showlegend=True,
|
||||
hovertemplate="<b>LOSING EXIT</b><br>Price: $%{y:.2f}<br>Time: %{x}<extra></extra>"
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
# Update layout with current timestamp and streaming status
|
||||
current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
||||
latest_price = df['close'].iloc[-1] if not df.empty else 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user