diff --git a/src/ui/components/__init__.py b/src/ui/components/__init__.py new file mode 100644 index 0000000..592e9b9 --- /dev/null +++ b/src/ui/components/__init__.py @@ -0,0 +1 @@ +__all__ = ['charts'] \ No newline at end of file diff --git a/src/ui/components/charts.py b/src/ui/components/charts.py new file mode 100644 index 0000000..64f42ea --- /dev/null +++ b/src/ui/components/charts.py @@ -0,0 +1,189 @@ +""" +Charting components for the Streamlit Quant application. +""" +import streamlit as st +import pandas as pd +import numpy as np +import plotly.graph_objects as go +import plotly.express as px +from datetime import datetime, timedelta + +def plot_stock_price(price_data, ticker_column='종목코드', date_column='날짜', + price_column='종가', name_column='종목명'): + """ + Plot stock price chart for selected stocks. + + Args: + price_data: DataFrame with price data + ticker_column: Column name for ticker code + date_column: Column name for date + price_column: Column name for price + name_column: Column name for stock name + """ + if price_data.empty: + st.warning("가격 데이터가 없습니다.") + return + + # Get unique tickers + tickers = price_data[ticker_column].unique() + + if len(tickers) == 0: + st.warning("표시할 종목이 없습니다.") + return + + # Create figure + fig = go.Figure() + + # Add traces + for ticker in tickers: + ticker_data = price_data[price_data[ticker_column] == ticker] + + name = ticker + if name_column in ticker_data.columns: + name = ticker_data[name_column].iloc[0] if not ticker_data.empty else ticker + name = f"{name} ({ticker})" + + fig.add_trace( + go.Scatter( + x=ticker_data[date_column], + y=ticker_data[price_column], + mode='lines', + name=name + ) + ) + + # Update layout + fig.update_layout( + title='주가 차트', + xaxis_title='날짜', + yaxis_title='가격', + legend_title='종목', + hovermode='x unified' + ) + + st.plotly_chart(fig, use_container_width=True) + + +def plot_returns_comparison(returns_data, benchmark_data=None, + date_column='날짜', returns_column='누적수익률'): + """ + Plot returns comparison chart for strategy vs benchmark. + + Args: + returns_data: DataFrame with strategy returns + benchmark_data: DataFrame with benchmark returns (optional) + date_column: Column name for date + returns_column: Column name for returns + """ + if returns_data.empty: + st.warning("수익률 데이터가 없습니다.") + return + + # Create figure + fig = go.Figure() + + # Add strategy trace + fig.add_trace( + go.Scatter( + x=returns_data[date_column], + y=returns_data[returns_column], + mode='lines', + name='전략' + ) + ) + + # Add benchmark trace if provided + if benchmark_data is not None and not benchmark_data.empty: + fig.add_trace( + go.Scatter( + x=benchmark_data[date_column], + y=benchmark_data[returns_column], + mode='lines', + name='벤치마크' + ) + ) + + # Update layout + fig.update_layout( + title='수익률 비교', + xaxis_title='날짜', + yaxis_title='누적수익률 (%)', + legend_title='전략', + hovermode='x unified' + ) + + st.plotly_chart(fig, use_container_width=True) + + +def plot_factor_distribution(data, factor_column, title=None): + """ + Plot histogram for factor distribution. + + Args: + data: DataFrame with factor data + factor_column: Column name for factor + title: Chart title (optional) + """ + if data.empty: + st.warning("데이터가 없습니다.") + return + + if factor_column not in data.columns: + st.warning(f"{factor_column} 칼럼이, 데이터에 없습니다.") + return + + # Create histogram + fig = px.histogram( + data, + x=factor_column, + nbins=30, + marginal="box", + title=title or f"{factor_column} 분포" + ) + + # Update layout + fig.update_layout( + xaxis_title=factor_column, + yaxis_title='종목 수' + ) + + st.plotly_chart(fig, use_container_width=True) + + +def plot_scatter_matrix(data, columns, color_column=None, size_column=None, title=None): + """ + Plot scatter matrix for multiple factors. + + Args: + data: DataFrame with factor data + columns: List of column names to plot + color_column: Column name for color (optional) + size_column: Column name for point size (optional) + title: Chart title (optional) + """ + if data.empty: + st.warning("데이터가 없습니다.") + return + + # Validate columns + missing_cols = [col for col in columns if col not in data.columns] + if missing_cols: + st.warning(f"다음 칼럼이 데이터에 없습니다: {', '.join(missing_cols)}") + return + + # Create scatter matrix + fig = px.scatter_matrix( + data, + dimensions=columns, + color=color_column if color_column in data.columns else None, + size=size_column if size_column in data.columns else None, + title=title or "팩터 상관관계" + ) + + # Update layout + fig.update_layout( + height=600, + width=800 + ) + + st.plotly_chart(fig, use_container_width=True) \ No newline at end of file