feat: 구조 리팩토링

This commit is contained in:
Ayuriel 2025-03-30 21:19:46 +09:00
parent 67a9b23fa5
commit 23cada68f7
2 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1 @@
__all__ = ['charts']

189
src/ui/components/charts.py Normal file
View File

@ -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)