diff --git a/realtime.py b/realtime.py
index c6744f1..6aa908c 100644
--- a/realtime.py
+++ b/realtime.py
@@ -1321,6 +1321,16 @@ class RealTimeChart:
total_volume = sum(volumes)
avg_volume = total_volume / len(volumes) if volumes else 0
+ # Calculate additional volume stats
+ max_volume = max(volumes) if volumes else 0
+ min_volume = min(volumes) if volumes else 0
+ median_volume = sorted(volumes)[len(volumes)//2] if volumes else 0
+
+ # Calculate trade value in USDT
+ trade_values = [p * v for p, v in zip(prices, volumes)]
+ total_value = sum(trade_values)
+ avg_trade_value = total_value / len(trade_values) if trade_values else 0
+
first_timestamp = filtered_ticks[0]['timestamp']
last_timestamp = filtered_ticks[-1]['timestamp']
time_span_ms = last_timestamp - first_timestamp
@@ -1383,6 +1393,18 @@ class RealTimeChart:
html.Div("Total Volume", style=label_style),
html.Div(f"{total_volume:.8f}", style=value_style),
html.Div(f"Avg: {avg_volume:.8f}", style=label_style)
+ ], style=card_style),
+
+ html.Div([
+ html.Div("Max Volume", style=label_style),
+ html.Div(f"{max_volume:.8f}", style=value_style),
+ html.Div("Median: {:.8f}".format(median_volume), style=label_style)
+ ], style=card_style),
+
+ html.Div([
+ html.Div("Total Value", style=label_style),
+ html.Div(f"{total_value:.2f}", style=value_style),
+ html.Div(f"Avg: {avg_trade_value:.2f}", style=label_style)
], style=card_style)
]
@@ -1499,8 +1521,14 @@ class RealTimeChart:
# Get filtered ticks
filtered_ticks = self.tick_storage.get_ticks_from_time(start_time_ms=start_time)
- # Create figure
- fig = go.Figure()
+ # Create figure with 2 subplots - price and volume
+ fig = make_subplots(
+ rows=2, cols=1,
+ shared_xaxes=True,
+ vertical_spacing=0.03,
+ row_heights=[0.7, 0.3],
+ subplot_titles=(f"Price Movement (Last {window_seconds // 60} minutes)", "Volume")
+ )
if not filtered_ticks:
fig.add_annotation(
@@ -1516,31 +1544,101 @@ class RealTimeChart:
prices = [tick['price'] for tick in filtered_ticks]
volumes = [tick.get('volume', 0) for tick in filtered_ticks]
- # Scale volumes for better visibility
- max_volume = max(volumes) if volumes else 1
- scaled_volumes = [vol * (max(prices) - min(prices)) / max_volume * 0.2 + min(prices) for vol in volumes]
+ # Add price scatter plot
+ fig.add_trace(
+ go.Scatter(
+ x=timestamps,
+ y=prices,
+ mode='lines',
+ name='Price',
+ line=dict(color='#4CAF50', width=1.5)
+ ),
+ row=1, col=1
+ )
- # Add price line
- fig.add_trace(go.Scatter(
- x=timestamps,
- y=prices,
- mode='lines',
- name='Price',
- line=dict(color='#4CAF50', width=1.5)
- ))
+ # Create a volume profile on the right side of the price chart
+ if len(prices) > 5: # Only create profile if we have enough data
+ # Group prices into bins
+ price_min = min(prices)
+ price_max = max(prices)
+ # Create approximately 20 bins based on price range
+ bin_size = max(0.01, (price_max - price_min) / 20)
+
+ # Create a dictionary to hold volume by price level
+ volume_by_price = {}
+
+ # Group volumes by price bins
+ for p, v in zip(prices, volumes):
+ bin_key = round(p / bin_size) * bin_size
+ if bin_key in volume_by_price:
+ volume_by_price[bin_key] += v
+ else:
+ volume_by_price[bin_key] = v
+
+ # Sort by price level
+ sorted_bins = sorted(volume_by_price.items())
+ profile_prices = [p for p, _ in sorted_bins]
+ profile_volumes = [v for _, v in sorted_bins]
+
+ # Add separate volume profile trace
+ fig.add_trace(
+ go.Bar(
+ y=profile_prices,
+ x=profile_volumes,
+ orientation='h',
+ name='Volume Profile',
+ marker=dict(
+ color=['#33CC33' if p <= latest_price else '#FF4136' for p in profile_prices],
+ opacity=0.5
+ ),
+ showlegend=True,
+ hovertemplate='Price: %{y:.2f}
Volume: %{x:.8f}'
+ ),
+ row=1, col=1
+ )
+
+ # Add a line marking the latest price
+ fig.add_shape(
+ type="line",
+ y0=latest_price, y1=latest_price,
+ x0=0, x1=max(profile_volumes) * 1.1,
+ line=dict(color="yellow", width=1, dash="dash"),
+ row=1, col=1
+ )
- # Add volume bars
- fig.add_trace(go.Bar(
- x=timestamps,
- y=scaled_volumes,
- name='Volume',
- marker=dict(color='rgba(128, 128, 255, 0.3)'),
- opacity=0.5,
- yaxis='y2'
- ))
+ # Add volume bars in separate subplot
+ # Color volume bars green for price increases, red for decreases
+ if len(timestamps) > 1:
+ # Compare each price with the previous to determine color
+ colors = []
+ for i in range(len(prices)):
+ if i == 0:
+ colors.append('#33CC33') # Default to green for first tick
+ else:
+ if prices[i] >= prices[i-1]:
+ colors.append('#33CC33') # Green for price increase/same
+ else:
+ colors.append('#FF4136') # Red for price decrease
+ else:
+ colors = ['#33CC33'] # Default green if only one tick
+
+ fig.add_trace(
+ go.Bar(
+ x=timestamps,
+ y=volumes,
+ name='Volume',
+ marker=dict(color=colors)
+ ),
+ row=2, col=1
+ )
+
+ # Compute stats for annotations
+ latest_price = prices[-1] if prices else 0
+ total_volume = sum(volumes)
+ max_volume = max(volumes) if volumes else 0
+ avg_volume = total_volume / len(volumes) if volumes else 0
# Add annotations for latest price
- latest_price = prices[-1] if prices else 0
fig.add_annotation(
x=timestamps[-1] if timestamps else 0,
y=latest_price,
@@ -1551,41 +1649,63 @@ class RealTimeChart:
arrowwidth=2,
arrowcolor="#4CAF50",
font=dict(size=12, color="#4CAF50"),
- xshift=50
+ xshift=50,
+ row=1, col=1
+ )
+
+ # Add annotations for volume stats
+ fig.add_annotation(
+ x=timestamps[-1] if timestamps else 0,
+ y=max_volume,
+ text=f"Max: {max_volume:.8f}",
+ showarrow=False,
+ font=dict(size=10, color="rgba(128, 128, 255, 1)"),
+ xshift=50,
+ row=2, col=1
)
# Update layout
fig.update_layout(
- title=f"{self.symbol} Price Movement (Last {window_seconds // 60} minutes)",
+ title_text=f"{self.symbol} Tick Data",
title_x=0.5,
- xaxis=dict(
- title="Time",
- showgrid=True,
- gridcolor='rgba(128,128,128,0.2)'
- ),
- yaxis=dict(
- title="Price (USDT)",
- showgrid=True,
- gridcolor='rgba(128,128,128,0.2)'
- ),
- yaxis2=dict(
- title="Volume",
- overlaying='y',
- side='right',
- showgrid=False,
- showticklabels=False
- ),
template='plotly_dark',
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(25,25,50,1)',
- height=500,
- margin=dict(l=40, r=40, t=50, b=40),
+ height=600, # Increased height for better visualization
+ margin=dict(l=40, r=40, t=80, b=40),
legend=dict(
- yanchor="top",
- y=0.99,
- xanchor="left",
- x=0.01
- )
+ orientation="h",
+ yanchor="bottom",
+ y=1.02,
+ xanchor="right",
+ x=1
+ ),
+ hovermode="x unified" # Show all data points at the same x-coordinate
+ )
+
+ # Update x-axis to be shared
+ fig.update_xaxes(
+ showgrid=True,
+ gridwidth=1,
+ gridcolor='rgba(128,128,128,0.2)',
+ rangeslider_visible=False
+ )
+
+ # Update y-axes
+ fig.update_yaxes(
+ title_text="Price (USDT)",
+ showgrid=True,
+ gridwidth=1,
+ gridcolor='rgba(128,128,128,0.2)',
+ row=1, col=1
+ )
+
+ fig.update_yaxes(
+ title_text="Volume",
+ showgrid=True,
+ gridwidth=1,
+ gridcolor='rgba(128,128,128,0.2)',
+ row=2, col=1
)
return fig