Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,21 +1,17 @@
|
|
1 |
import gradio as gr
|
2 |
import yfinance as yf
|
3 |
-
from datetime import datetime # ์ด ๋ถ๋ถ์ ์ถ๊ฐํ์ธ์.
|
4 |
from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices
|
5 |
from pypfopt import EfficientFrontier
|
6 |
from pypfopt import risk_models
|
7 |
from pypfopt import expected_returns
|
8 |
-
import matplotlib.pyplot as plt
|
9 |
-
if "seaborn-deep" in plt.style.available:
|
10 |
-
plt.style.use("seaborn-deep")
|
11 |
-
else:
|
12 |
-
print("'seaborn-deep' ์คํ์ผ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ ์คํ์ผ์ ์ฌ์ฉํฉ๋๋ค.")
|
13 |
-
plt.style.use("default")
|
14 |
from pypfopt import plotting
|
15 |
import copy
|
16 |
import numpy as np
|
17 |
import pandas as pd
|
18 |
import plotly.express as px
|
|
|
|
|
|
|
19 |
|
20 |
def plot_cum_returns(data, title, initial_capital=1000):
|
21 |
# ์ผ์ผ ๋์ ์์ต๋ฅ ๊ณ์ฐ ๋ฐ ์๊ฐํ
|
@@ -44,10 +40,105 @@ def plot_weights(weights):
|
|
44 |
ax.axis('equal')
|
45 |
return fig
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
def output_results(start_date, end_date, tickers_string):
|
48 |
tickers = tickers_string.split(',')
|
49 |
stocks_df = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
|
50 |
-
|
51 |
tickers_info = {}
|
52 |
news_data = {}
|
53 |
for ticker in tickers:
|
@@ -62,7 +153,6 @@ def output_results(start_date, end_date, tickers_string):
|
|
62 |
fig_cum_returns = px.line(daily_cum_returns, title='๊ฐ๋ณ ์ฃผ์์ ๋์ ์์ต๋ฅ ($1,000 ์์)')
|
63 |
corr_df = stocks_df.corr().round(2)
|
64 |
fig_corr = px.imshow(corr_df, text_auto=True, title='์ฃผ์ ๊ฐ ์๊ด ๊ด๊ณ')
|
65 |
-
|
66 |
mu = expected_returns.mean_historical_return(stocks_df)
|
67 |
S = risk_models.sample_cov(stocks_df)
|
68 |
ef = EfficientFrontier(mu, S)
|
@@ -79,59 +169,48 @@ def output_results(start_date, end_date, tickers_string):
|
|
79 |
for ticker in tickers
|
80 |
])
|
81 |
|
|
|
|
|
|
|
|
|
82 |
return fig_cum_returns, fig_efficient_frontier, fig_corr, fig_indiv_prices, fig_weights, \
|
83 |
-
f"{expected_annual_return*100:.2f}%", f"{annual_volatility*100:.2f}%", f"{sharpe_ratio:.2f}",
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
analyze_button.click(fn=output_results, inputs=[start_date, end_date, tickers_string], outputs=[fig_cum_returns, fig_efficient_frontier, fig_corr, fig_indiv_prices, fig_weights, expected_annual_return, annual_volatility, sharpe_ratio, ticker_info_output])
|
124 |
-
|
125 |
-
with gr.TabItem("๋ฏธ๋ ์ฃผ๊ฐ ์์ธก"):
|
126 |
-
with gr.Column():
|
127 |
-
ticker_input = gr.Textbox(value="AAPL", label="Enter Stock Ticker")
|
128 |
-
forecast_button = gr.Button("Generate Forecast")
|
129 |
-
forecast_output = gr.Textbox(label="Forecast Data")
|
130 |
-
forecast_chart = gr.Plot(label="Forecast Chart")
|
131 |
-
|
132 |
-
forecast_button.click(fn=predict_stock, inputs=[ticker_input], outputs=[forecast_output, forecast_chart])
|
133 |
-
|
134 |
-
return app
|
135 |
-
|
136 |
-
app = setup_ui()
|
137 |
-
app.launch()
|
|
|
1 |
import gradio as gr
|
2 |
import yfinance as yf
|
|
|
3 |
from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices
|
4 |
from pypfopt import EfficientFrontier
|
5 |
from pypfopt import risk_models
|
6 |
from pypfopt import expected_returns
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
from pypfopt import plotting
|
8 |
import copy
|
9 |
import numpy as np
|
10 |
import pandas as pd
|
11 |
import plotly.express as px
|
12 |
+
import matplotlib.pyplot as plt
|
13 |
+
from matplotlib import gridspec
|
14 |
+
from datetime import datetime
|
15 |
|
16 |
def plot_cum_returns(data, title, initial_capital=1000):
|
17 |
# ์ผ์ผ ๋์ ์์ต๋ฅ ๊ณ์ฐ ๋ฐ ์๊ฐํ
|
|
|
40 |
ax.axis('equal')
|
41 |
return fig
|
42 |
|
43 |
+
def plot_top_ticker_graphs(top_ticker, stock_data, portfolio_data, weights):
|
44 |
+
fig = plt.figure(figsize=(20, 20))
|
45 |
+
gs = gridspec.GridSpec(5, 2, wspace=0.3, hspace=0.3)
|
46 |
+
|
47 |
+
ax1 = plt.subplot(gs[0, 0])
|
48 |
+
# ํฌํธํด๋ฆฌ์ค ๋ด ์์ฐ ๋ฐฐ๋ถ ๋น์จ ๊ทธ๋ํ (๋ง๋ ๊ทธ๋ํ ๋๋ ํ์ด ์ฐจํธ)
|
49 |
+
ax1.pie(list(weights.values()), labels=list(weights.keys()), autopct='%1.1f%%')
|
50 |
+
ax1.axis('equal')
|
51 |
+
ax1.set_title('Portfolio Asset Allocation')
|
52 |
+
|
53 |
+
ax2 = plt.subplot(gs[0, 1])
|
54 |
+
# ํฌํธํด๋ฆฌ์ค์ ์ฐ๊ฐ ์์ต๋ฅ ๋ฐ ๋ณ๋์ฑ ์ถ์ด ๊ทธ๋ํ
|
55 |
+
portfolio_returns = portfolio_data.pct_change().dropna()
|
56 |
+
portfolio_returns = (portfolio_returns + 1).cumprod()
|
57 |
+
portfolio_returns.plot(ax=ax2)
|
58 |
+
ax2.set_title('Portfolio Annual Returns and Volatility')
|
59 |
+
ax2.set_ylabel('Cumulative Returns')
|
60 |
+
|
61 |
+
ax3 = plt.subplot(gs[1, 0])
|
62 |
+
# ํฌํธํด๋ฆฌ์ค์ ์คํ ๋น์จ(Sharpe Ratio) ์ถ์ด ๊ทธ๋ํ
|
63 |
+
sharpe_ratio = portfolio_returns.mean() / portfolio_returns.std() * np.sqrt(252)
|
64 |
+
sharpe_ratio.plot(ax=ax3)
|
65 |
+
ax3.set_title('Portfolio Sharpe Ratio')
|
66 |
+
ax3.set_ylabel('Sharpe Ratio')
|
67 |
+
|
68 |
+
ax4 = plt.subplot(gs[1, 1])
|
69 |
+
# ๊ฐ๋ณ ์ฃผ์์ ์๋ณ ์์ต๋ฅ ๊ทธ๋ํ
|
70 |
+
stock_data[top_ticker].resample('M').last().pct_change().plot(ax=ax4)
|
71 |
+
ax4.set_title(f'{top_ticker} Monthly Returns')
|
72 |
+
ax4.set_ylabel('Returns')
|
73 |
+
|
74 |
+
ax5 = plt.subplot(gs[2, 0])
|
75 |
+
# ๊ฐ๋ณ ์ฃผ์์ ์ฐ๊ฐ ์์ต๋ฅ ๊ทธ๋ํ
|
76 |
+
stock_data[top_ticker].resample('Y').last().pct_change().plot(ax=ax5)
|
77 |
+
ax5.set_title(f'{top_ticker} Annual Returns')
|
78 |
+
ax5.set_ylabel('Returns')
|
79 |
+
|
80 |
+
ax6 = plt.subplot(gs[2, 1])
|
81 |
+
# ๊ฐ๋ณ ์ฃผ์์ ์ด๋ํ๊ท ์ (์: 20์ผ, 50์ผ, 200์ผ) ๊ทธ๋ํ
|
82 |
+
stock_data[top_ticker].plot(ax=ax6)
|
83 |
+
stock_data[top_ticker].rolling(window=20).mean().plot(ax=ax6, label='20-day MA')
|
84 |
+
stock_data[top_ticker].rolling(window=50).mean().plot(ax=ax6, label='50-day MA')
|
85 |
+
stock_data[top_ticker].rolling(window=200).mean().plot(ax=ax6, label='200-day MA')
|
86 |
+
ax6.set_title(f'{top_ticker} Moving Averages')
|
87 |
+
ax6.set_ylabel('Price')
|
88 |
+
ax6.legend()
|
89 |
+
|
90 |
+
ax7 = plt.subplot(gs[3, 0])
|
91 |
+
# ํฌํธํด๋ฆฌ์ค์ ๋๋ก๋ค์ด(Drawdown) ๊ทธ๋ํ
|
92 |
+
portfolio_drawdown = (portfolio_returns.cummax() - portfolio_returns) / portfolio_returns.cummax()
|
93 |
+
portfolio_drawdown.plot(ax=ax7)
|
94 |
+
ax7.set_title('Portfolio Drawdown')
|
95 |
+
ax7.set_ylabel('Drawdown')
|
96 |
+
|
97 |
+
ax8 = plt.subplot(gs[3, 1])
|
98 |
+
# ๊ฐ๋ณ ์ฃผ์์ RSI(Relative Strength Index) ๊ทธ๋ํ
|
99 |
+
delta = stock_data[top_ticker].diff()
|
100 |
+
up = delta.clip(lower=0)
|
101 |
+
down = -1 * delta.clip(upper=0)
|
102 |
+
ema_up = up.ewm(com=13, adjust=False).mean()
|
103 |
+
ema_down = down.ewm(com=13, adjust=False).mean()
|
104 |
+
rs = ema_up / ema_down
|
105 |
+
rsi = 100 - (100 / (1 + rs))
|
106 |
+
rsi.plot(ax=ax8)
|
107 |
+
ax8.set_title(f'{top_ticker} RSI')
|
108 |
+
ax8.set_ylabel('RSI')
|
109 |
+
|
110 |
+
ax9 = plt.subplot(gs[4, 0])
|
111 |
+
# ๊ฐ๋ณ ์ฃผ์์ MACD(Moving Average Convergence Divergence) ๊ทธ๋ํ
|
112 |
+
exp1 = stock_data[top_ticker].ewm(span=12, adjust=False).mean()
|
113 |
+
exp2 = stock_data[top_ticker].ewm(span=26, adjust=False).mean()
|
114 |
+
macd = exp1 - exp2
|
115 |
+
signal = macd.ewm(span=9, adjust=False).mean()
|
116 |
+
ax9.plot(macd, label='MACD')
|
117 |
+
ax9.plot(signal, label='Signal')
|
118 |
+
ax9.set_title(f'{top_ticker} MACD')
|
119 |
+
ax9.set_ylabel('MACD')
|
120 |
+
ax9.legend()
|
121 |
+
|
122 |
+
ax10 = plt.subplot(gs[4, 1])
|
123 |
+
# ๊ฐ๋ณ ์ฃผ์์ ๋ณผ๋ฆฐ์ ๋ฐด๋(Bollinger Bands) ๊ทธ๋ํ
|
124 |
+
rolling_mean = stock_data[top_ticker].rolling(window=20).mean()
|
125 |
+
rolling_std = stock_data[top_ticker].rolling(window=20).std()
|
126 |
+
upper_band = rolling_mean + (rolling_std * 2)
|
127 |
+
lower_band = rolling_mean - (rolling_std * 2)
|
128 |
+
ax10.plot(stock_data[top_ticker], label='Price')
|
129 |
+
ax10.plot(rolling_mean, label='Moving Average')
|
130 |
+
ax10.plot(upper_band, label='Upper Band')
|
131 |
+
ax10.plot(lower_band, label='Lower Band')
|
132 |
+
ax10.set_title(f'{top_ticker} Bollinger Bands')
|
133 |
+
ax10.set_ylabel('Price')
|
134 |
+
ax10.legend()
|
135 |
+
|
136 |
+
plt.tight_layout()
|
137 |
+
return fig
|
138 |
+
|
139 |
def output_results(start_date, end_date, tickers_string):
|
140 |
tickers = tickers_string.split(',')
|
141 |
stocks_df = yf.download(tickers, start=start_date, end=end_date)['Adj Close']
|
|
|
142 |
tickers_info = {}
|
143 |
news_data = {}
|
144 |
for ticker in tickers:
|
|
|
153 |
fig_cum_returns = px.line(daily_cum_returns, title='๊ฐ๋ณ ์ฃผ์์ ๋์ ์์ต๋ฅ ($1,000 ์์)')
|
154 |
corr_df = stocks_df.corr().round(2)
|
155 |
fig_corr = px.imshow(corr_df, text_auto=True, title='์ฃผ์ ๊ฐ ์๊ด ๊ด๊ณ')
|
|
|
156 |
mu = expected_returns.mean_historical_return(stocks_df)
|
157 |
S = risk_models.sample_cov(stocks_df)
|
158 |
ef = EfficientFrontier(mu, S)
|
|
|
169 |
for ticker in tickers
|
170 |
])
|
171 |
|
172 |
+
# ๊ฐ์ฅ ์์ต๋ฅ ๊ณผ ๋น์ค์ด ํฐ ์ข
๋ชฉ ์ ํ
|
173 |
+
top_ticker = max(cleaned_weights, key=cleaned_weights.get)
|
174 |
+
fig_top_ticker = plot_top_ticker_graphs(top_ticker, stocks_df, daily_cum_returns[top_ticker], cleaned_weights)
|
175 |
+
|
176 |
return fig_cum_returns, fig_efficient_frontier, fig_corr, fig_indiv_prices, fig_weights, \
|
177 |
+
f"{expected_annual_return*100:.2f}%", f"{annual_volatility*100:.2f}%", f"{sharpe_ratio:.2f}", \
|
178 |
+
ticker_info_output, fig_top_ticker
|
179 |
+
|
180 |
+
css = """footer { visibility: hidden; }"""
|
181 |
+
|
182 |
+
with gr.Blocks(css=css) as app:
|
183 |
+
gr.Markdown("""
|
184 |
+
<style>
|
185 |
+
.markdown-text h2 {
|
186 |
+
font-size: 18px; # ํฐํธ ํฌ๊ธฐ๋ฅผ 18px๋ก ์ค์
|
187 |
+
}
|
188 |
+
</style>
|
189 |
+
<h2>AIQ ์์ฐ ํฌํธํด๋ฆฌ์ค: ๊ธ๋ก๋ฒ ์์ฐ(์ฃผ์, ์ง์, BTC, ์ํ ๋ฑ) AI ํฌํธํด๋ฆฌ์ค ์ต์ ํ ์๋น์ค</h2>
|
190 |
+
<h2>์ ์ธ๊ณ ๋ชจ๋ ํฐ์ปค ๋ณด๊ธฐ(์ผํ ํ์ด๋ธ์ค): <a href="https://finance.yahoo.com/most-active" target="_blank">์ฌ๊ธฐ๋ฅผ ํด๋ฆญ</a></h2>
|
191 |
+
""")
|
192 |
+
with gr.Row():
|
193 |
+
start_date = gr.Textbox("2013-01-01", label="์์ ์ผ์")
|
194 |
+
end_date = gr.Textbox(datetime.now().date(), label="์ข
๋ฃ ์ผ์")
|
195 |
+
tickers_string = gr.Textbox("NVDA,^GSPC,GC=F,MSFT,BTC-USD", label="์ฃผ์ ํฐ์ปค๋ฅผ ์ผํ๋ก ๊ตฌ๋ถํ์ฌ ์
๋ ฅํ์ธ์")
|
196 |
+
btn = gr.Button("ํฌํธํด๋ฆฌ์ค ์ต์ ํ ๊ฒฐ๊ณผ ๋ณด๊ธฐ")
|
197 |
+
|
198 |
+
with gr.Row():
|
199 |
+
expected_annual_return = gr.Text(label="์์ ์ฐ๊ฐ ์์ต๋ฅ ")
|
200 |
+
annual_volatility = gr.Text(label="์ฐ๊ฐ ๋ณ๋์ฑ")
|
201 |
+
sharpe_ratio = gr.Text(label="์คํ ๋น์จ")
|
202 |
+
|
203 |
+
with gr.Column():
|
204 |
+
fig_cum_returns = gr.Plot(label="์ต์ ํ๋ ํฌํธํด๋ฆฌ์ค์ ๋์ ์์ต๋ฅ (์์ ๊ฐ๊ฒฉ $1,000)")
|
205 |
+
fig_efficient_frontier = gr.Plot(label="ํจ์จ์ ํฌ์์ ")
|
206 |
+
fig_corr = gr.Plot(label="์ฃผ์ ๊ฐ ์๊ด ๊ด๊ณ")
|
207 |
+
fig_indiv_prices = gr.Plot(label="๊ฐ๋ณ ์ฃผ์ ๊ฐ๊ฒฉ")
|
208 |
+
fig_weights = gr.Plot(label="ํฌํธํด๋ฆฌ์ค ์ต์ ํฌ์ ๋น์จ")
|
209 |
+
fig_top_ticker = gr.Plot(label="๊ฐ์ฅ ์์ต๋ฅ ๊ณผ ๋น์ค์ด ํฐ ์ข
๋ชฉ์ ๊ทธ๋ํ")
|
210 |
+
ticker_info_output = gr.Textbox(label="ํฐ์ปค ์ ๋ณด ๋ฐ ๋ด์ค")
|
211 |
+
|
212 |
+
btn.click(fn=output_results, inputs=[start_date, end_date, tickers_string],
|
213 |
+
outputs=[fig_cum_returns, fig_efficient_frontier, fig_corr, fig_indiv_prices, fig_weights,
|
214 |
+
expected_annual_return, annual_volatility, sharpe_ratio, ticker_info_output, fig_top_ticker])
|
215 |
+
|
216 |
+
app.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|