"""
Volume chart implementations.
"""
import plotly.graph_objects as go
import pandas as pd
import logging
from datetime import datetime
from typing import Dict, Any, Optional, Tuple
from ..config.constants import Y_AXIS_RANGES, FILE_PATHS
from .base_chart import BaseChart
logger = logging.getLogger(__name__)
class VolumeChart(BaseChart):
"""Chart for Volume visualizations."""
def create_chart(self, df: pd.DataFrame, **kwargs) -> go.Figure:
"""Create Cumulative Volume bar chart."""
if df.empty:
return self._create_empty_chart("No volume data available")
# Calculate daily volume changes using the new method
daily_data = self.data_processor.calculate_daily_volume_changes(df)
if daily_data.empty:
return self._create_empty_chart("No daily volume data available")
# Create figure
fig = self._create_base_figure()
# Get time range
min_time = daily_data['date'].min()
max_time = daily_data['date'].max()
# Determine bar colors based on daily volume (all bars will be blue for cumulative volume)
bar_colors = ['blue'] * len(daily_data)
# Create custom hover text for bars
bar_hover_text = []
for _, row in daily_data.iterrows():
date_str = row['date'].strftime('%Y-%m-%d')
bar_hover_text.append(
f"Date: {date_str}
"
f"Daily Volume: {row['volume']:.2f}
"
f"Cumulative Volume: {row['cumulative_volume']:.2f}
"
f"Day-over-Day Change: {row['day_over_day_pct']:.1f}%
"
f"7-Day SMA: {row['sma_7d']:.1f}%"
)
# Add cumulative volume bar chart
fig.add_trace(
go.Bar(
x=daily_data['date'],
y=daily_data['cumulative_volume'],
marker=dict(
color=bar_colors,
line=dict(width=1, color='black'),
opacity=0.7
),
name='Daily Volume Change',
hovertext=bar_hover_text,
hoverinfo='text',
customdata=daily_data[['volume', 'day_over_day_pct']].values
)
)
# Add 7-Day SMA line
fig.add_trace(
go.Scatter(
x=daily_data['date'],
y=daily_data['sma_7d'],
mode='lines',
line=dict(color='red', width=3),
name='7-Day SMA',
hovertemplate='Date: %{x}
7-Day SMA: %{y:.1f}%',
yaxis='y2'
)
)
# Update layout and axes
self._update_layout(
fig,
title="Daily Volume Change (%) with 7-Day SMA",
y_axis_title="Cumulative Volume"
)
# Update axes
self._update_axes(
fig,
x_range=[min_time, max_time],
y_auto=True
)
# Update primary y-axis to show volume format
fig.update_yaxes(tickformat=".2f")
# Add secondary y-axis for SMA
fig.update_layout(
yaxis2=dict(
title="Percentage Change (%)",
overlaying='y',
side='right',
tickformat=".1f"
)
)
# Save chart
self._save_chart(
fig,
FILE_PATHS['volume_graph_html'],
FILE_PATHS['volume_graph_png']
)
return fig
def generate_volume_visualizations(data_processor=None) -> Tuple[go.Figure, Optional[str]]:
"""Generate Volume visualizations."""
from ..data.data_processor import DataProcessor
if data_processor is None:
data_processor = DataProcessor()
# Fetch data
apr_df, _ = data_processor.fetch_apr_data_from_db()
# Filter for records with volume data
volume_df = apr_df[apr_df['volume'].notna()].copy()
# Create chart
volume_chart = VolumeChart(data_processor)
fig, csv_path = volume_chart.generate_visualization(
volume_df,
csv_filename=FILE_PATHS['volume_csv']
)
return fig, csv_path