Spaces:
Running
Running
| """ | |
| 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}<br>" | |
| f"Daily Volume: {row['volume']:.2f}<br>" | |
| f"Cumulative Volume: {row['cumulative_volume']:.2f}<br>" | |
| f"Day-over-Day Change: {row['day_over_day_pct']:.1f}%<br>" | |
| 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}<br>7-Day SMA: %{y:.1f}%<extra></extra>', | |
| 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 | |