Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -3,6 +3,7 @@ import yfinance as yf
|
|
3 |
import numpy as np
|
4 |
import pandas as pd
|
5 |
import matplotlib.pyplot as plt
|
|
|
6 |
import scipy.optimize as sco
|
7 |
|
8 |
def get_stock_data(tickers, start, end):
|
@@ -56,88 +57,84 @@ def generate_efficient_frontier(returns, cov_matrix, num_portfolios=5000):
|
|
56 |
|
57 |
return results
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
-
def
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
-
|
72 |
-
invalid_tickers = [t for t in tickers if yf.Ticker(t).history(period='1d').empty]
|
73 |
-
if invalid_tickers:
|
74 |
-
st.warning(f"Ticker tidak valid atau tidak memiliki data: {', '.join(invalid_tickers)}")
|
75 |
-
return False
|
76 |
-
return True
|
77 |
|
78 |
st.write("Rekomendasi Saham yang Bertahan Saat COVID-19:")
|
79 |
-
st.write(
|
80 |
|
81 |
tickers_list = st.text_input("Masukkan ticker saham", "KLBF.JK, SIDO.JK, KAEF.JK").split(", ")
|
82 |
start_date = st.date_input("Pilih tanggal mulai", pd.to_datetime("2020-01-01"))
|
83 |
end_date = st.date_input("Pilih tanggal akhir", pd.to_datetime("2023-12-31"))
|
84 |
|
85 |
if st.button("Analisis Portofolio"):
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
optimal_weights = optimize_portfolio(mean_returns, cov_matrix)
|
91 |
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
if small_weights:
|
106 |
-
large_weights["Others"] = sum(small_weights.values())
|
107 |
-
|
108 |
-
fig, ax = plt.subplots()
|
109 |
-
ax.pie(large_weights.values(), labels=large_weights.keys(), autopct='%1.1f%%', startangle=140)
|
110 |
-
ax.axis('equal')
|
111 |
-
st.pyplot(fig)
|
112 |
-
|
113 |
-
st.markdown("""
|
114 |
-
**Interpretasi Pie Chart:**
|
115 |
-
- Pie chart ini menunjukkan distribusi bobot optimal dari setiap saham dalam portofolio.
|
116 |
-
- Saham dengan bobot signifikan ditampilkan dengan persentase masing-masing.
|
117 |
-
- Saham dengan bobot kecil (<5%) digabung ke dalam kategori "Others".
|
118 |
-
- Portofolio optimal didasarkan pada perhitungan rasio Sharpe, yang mencari keseimbangan terbaik antara return dan risiko.
|
119 |
-
""")
|
120 |
-
|
121 |
-
# Efficient Frontier
|
122 |
-
results = generate_efficient_frontier(mean_returns, cov_matrix)
|
123 |
-
|
124 |
-
st.subheader("Efficient Frontier")
|
125 |
-
fig, ax = plt.subplots()
|
126 |
-
scatter = ax.scatter(results[1, :], results[0, :], c=results[2, :], cmap="viridis", marker='o')
|
127 |
-
ax.set_xlabel("Risiko (Standar Deviasi)")
|
128 |
-
ax.set_ylabel("Return Tahunan")
|
129 |
-
ax.set_title("Efficient Frontier")
|
130 |
-
fig.colorbar(scatter, label="Sharpe Ratio")
|
131 |
-
st.pyplot(fig)
|
132 |
-
|
133 |
-
st.markdown("""
|
134 |
-
**Interpretasi:**
|
135 |
-
- **Bobot Portofolio Optimal**: Menunjukkan alokasi dana ke setiap saham.
|
136 |
-
- **Grafik Efficient Frontier**:
|
137 |
-
- Sumbu **X**: Risiko (diukur dengan standar deviasi return portofolio).
|
138 |
-
- Sumbu **Y**: Return tahunan dari berbagai kombinasi portofolio.
|
139 |
-
- Warna titik menunjukkan **Sharpe Ratio** – semakin terang warna, semakin optimal kombinasi risiko dan return.
|
140 |
-
- **Pemilihan Portofolio Terbaik**: Portofolio di frontier efisien memberikan return terbaik untuk tingkat risiko tertentu.
|
141 |
-
""")
|
142 |
-
else:
|
143 |
-
st.error("Optimasi portofolio gagal. Coba dengan saham yang berbeda.")
|
|
|
3 |
import numpy as np
|
4 |
import pandas as pd
|
5 |
import matplotlib.pyplot as plt
|
6 |
+
import seaborn as sns
|
7 |
import scipy.optimize as sco
|
8 |
|
9 |
def get_stock_data(tickers, start, end):
|
|
|
57 |
|
58 |
return results
|
59 |
|
60 |
+
def plot_correlation_heatmap(data):
|
61 |
+
correlation_matrix = data.corr()
|
62 |
+
fig, ax = plt.subplots()
|
63 |
+
sns.heatmap(correlation_matrix, annot=True, cmap="coolwarm", ax=ax)
|
64 |
+
st.pyplot(fig)
|
65 |
|
66 |
+
def plot_moving_averages(data, stock):
|
67 |
+
fig, ax = plt.subplots()
|
68 |
+
data[stock].plot(label='Harga', ax=ax)
|
69 |
+
data[stock].rolling(window=50).mean().plot(label='SMA 50', ax=ax)
|
70 |
+
data[stock].rolling(window=200).mean().plot(label='SMA 200', ax=ax)
|
71 |
+
ax.legend()
|
72 |
+
st.pyplot(fig)
|
73 |
|
74 |
+
def simulate_dca(data, investment_amount=100):
|
75 |
+
investment_dates = data.index[::30]
|
76 |
+
dca_values = data.loc[investment_dates].mean(axis=1) * investment_amount
|
77 |
+
total_value = dca_values.cumsum()
|
78 |
+
fig, ax = plt.subplots()
|
79 |
+
total_value.plot(ax=ax, title='Simulasi Investasi DCA')
|
80 |
+
st.pyplot(fig)
|
81 |
|
82 |
+
st.title("Analisis Portofolio Saham Optimal (Model Markowitz)")
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
st.write("Rekomendasi Saham yang Bertahan Saat COVID-19:")
|
85 |
+
st.write("KLBF.JK, SIDO.JK, KAEF.JK, TLKM.JK, UNVR.JK")
|
86 |
|
87 |
tickers_list = st.text_input("Masukkan ticker saham", "KLBF.JK, SIDO.JK, KAEF.JK").split(", ")
|
88 |
start_date = st.date_input("Pilih tanggal mulai", pd.to_datetime("2020-01-01"))
|
89 |
end_date = st.date_input("Pilih tanggal akhir", pd.to_datetime("2023-12-31"))
|
90 |
|
91 |
if st.button("Analisis Portofolio"):
|
92 |
+
stock_data = get_stock_data(tickers_list, start_date, end_date)
|
93 |
+
if stock_data is not None:
|
94 |
+
mean_returns, cov_matrix = calculate_returns(stock_data)
|
95 |
+
optimal_weights = optimize_portfolio(mean_returns, cov_matrix)
|
|
|
96 |
|
97 |
+
st.subheader("Statistik Saham")
|
98 |
+
st.write(stock_data.describe())
|
99 |
+
|
100 |
+
st.subheader("Heatmap Korelasi Saham")
|
101 |
+
plot_correlation_heatmap(stock_data)
|
102 |
+
|
103 |
+
st.subheader("Moving Average untuk Prediksi Tren")
|
104 |
+
selected_stock = st.selectbox("Pilih saham untuk analisis MA", stock_data.columns)
|
105 |
+
plot_moving_averages(stock_data, selected_stock)
|
106 |
+
|
107 |
+
st.subheader("Simulasi Investasi Dollar Cost Averaging (DCA)")
|
108 |
+
simulate_dca(stock_data)
|
109 |
+
|
110 |
+
if optimal_weights is not None:
|
111 |
+
st.subheader("Bobot Portofolio Optimal")
|
112 |
+
portfolio_weights = {stock: weight for stock, weight in zip(stock_data.columns, optimal_weights)}
|
113 |
+
st.write(portfolio_weights)
|
114 |
+
|
115 |
+
# Pie Chart dengan pengelompokan saham kecil ke "Others"
|
116 |
+
threshold = 0.05
|
117 |
+
large_weights = {k: v for k, v in portfolio_weights.items() if v >= threshold}
|
118 |
+
small_weights = {k: v for k, v in portfolio_weights.items() if v < threshold}
|
119 |
+
|
120 |
+
if small_weights:
|
121 |
+
large_weights["Others"] = sum(small_weights.values())
|
122 |
+
|
123 |
+
fig, ax = plt.subplots()
|
124 |
+
ax.pie(large_weights.values(), labels=large_weights.keys(), autopct='%1.1f%%', startangle=140)
|
125 |
+
ax.axis('equal')
|
126 |
+
st.pyplot(fig)
|
127 |
+
|
128 |
+
# Efficient Frontier
|
129 |
+
results = generate_efficient_frontier(mean_returns, cov_matrix)
|
130 |
|
131 |
+
st.subheader("Efficient Frontier")
|
132 |
+
fig, ax = plt.subplots()
|
133 |
+
scatter = ax.scatter(results[1, :], results[0, :], c=results[2, :], cmap="viridis", marker='o')
|
134 |
+
ax.set_xlabel("Risiko (Standar Deviasi)")
|
135 |
+
ax.set_ylabel("Return Tahunan")
|
136 |
+
ax.set_title("Efficient Frontier")
|
137 |
+
fig.colorbar(scatter, label="Sharpe Ratio")
|
138 |
+
st.pyplot(fig)
|
139 |
+
else:
|
140 |
+
st.error("Optimasi portofolio gagal. Coba dengan saham yang berbeda.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|