Spaces:
Running
Running
Upload 16 files
Browse files- modules/rebalancing.py +40 -18
- style.css +3 -72
modules/rebalancing.py
CHANGED
@@ -44,32 +44,56 @@ def parse_input(holdings, cash_amount):
|
|
44 |
except Exception as e:
|
45 |
raise ValueError(f"Input parsing error: {e}")
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
def get_portfolio_exchange_rate(currency_code, main_currency):
|
48 |
try:
|
49 |
if main_currency.lower() == 'krw':
|
50 |
if currency_code.upper() != 'KRW':
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
if not exchange_rate_data.empty:
|
57 |
-
return exchange_rate_data['DEXKOUS'].iloc[-1]
|
58 |
-
else:
|
59 |
-
raise ValueError(f"<p style='color: red;'>Failed to retrieve exchange rate data for {currency_code} to KRW</p>")
|
60 |
else:
|
61 |
-
return 1.0
|
62 |
-
|
63 |
else:
|
64 |
if currency_code.lower() == main_currency.lower():
|
65 |
return 1.0
|
66 |
-
|
67 |
ticker = f"{currency_code.upper()}{main_currency.upper()}=X"
|
68 |
data = yf.download(ticker, period='1d', progress=False)
|
69 |
-
if
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
except Exception as e:
|
74 |
raise ValueError(f"<p style='color: red;'>Exchange rate retrieval error: {e}</p>")
|
75 |
|
@@ -193,7 +217,6 @@ def generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_cu
|
|
193 |
# HTML 생성
|
194 |
rebalancing_analysis = css + f"""
|
195 |
<div>
|
196 |
-
|
197 |
<div class='table-container'>
|
198 |
<table>
|
199 |
<thead>
|
@@ -249,7 +272,6 @@ def generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_cu
|
|
249 |
</table>
|
250 |
</div>
|
251 |
<br>
|
252 |
-
|
253 |
</div>
|
254 |
"""
|
255 |
|
|
|
44 |
except Exception as e:
|
45 |
raise ValueError(f"Input parsing error: {e}")
|
46 |
|
47 |
+
# def get_portfolio_exchange_rate(currency_code, main_currency):
|
48 |
+
# try:
|
49 |
+
# if main_currency.lower() == 'krw':
|
50 |
+
# if currency_code.upper() != 'KRW':
|
51 |
+
# fx_ticker = f"FRED:DEXKOUS" if currency_code.upper() == 'USD' else None
|
52 |
+
# if not fx_ticker:
|
53 |
+
# raise ValueError(f"<p style='color: red;'>Unsupported currency pair for KRW: {currency_code}</p>")
|
54 |
+
|
55 |
+
# exchange_rate_data = fdr.DataReader(fx_ticker)
|
56 |
+
# if not exchange_rate_data.empty:
|
57 |
+
# return exchange_rate_data['DEXKOUS'].iloc[-1]
|
58 |
+
# else:
|
59 |
+
# raise ValueError(f"<p style='color: red;'>Failed to retrieve exchange rate data for {currency_code} to KRW</p>")
|
60 |
+
# else:
|
61 |
+
# return 1.0 # KRW to KRW는 환율이 1로 고정됨
|
62 |
+
|
63 |
+
# else:
|
64 |
+
# if currency_code.lower() == main_currency.lower():
|
65 |
+
# return 1.0
|
66 |
+
|
67 |
+
# ticker = f"{currency_code.upper()}{main_currency.upper()}=X"
|
68 |
+
# data = yf.download(ticker, period='1d', progress=False)
|
69 |
+
# if not data.empty:
|
70 |
+
# return data['Close'].iloc[0]
|
71 |
+
# else:
|
72 |
+
# raise ValueError(f"<p style='color: red;'>Failed to retrieve exchange rate data for ticker: {ticker}</p>")
|
73 |
+
# except Exception as e:
|
74 |
+
# raise ValueError(f"<p style='color: red;'>Exchange rate retrieval error: {e}</p>")
|
75 |
+
|
76 |
def get_portfolio_exchange_rate(currency_code, main_currency):
|
77 |
try:
|
78 |
if main_currency.lower() == 'krw':
|
79 |
if currency_code.upper() != 'KRW':
|
80 |
+
ticker = f"{currency_code.upper()}KRW=X"
|
81 |
+
data = yf.download(ticker, period='1d', progress=False)
|
82 |
+
if data.empty:
|
83 |
+
raise ValueError(f"<p style='color: red;'>No exchange rate data for {ticker}</p>")
|
84 |
+
return data['Close'].iloc[-1]
|
|
|
|
|
|
|
|
|
85 |
else:
|
86 |
+
return 1.0
|
|
|
87 |
else:
|
88 |
if currency_code.lower() == main_currency.lower():
|
89 |
return 1.0
|
90 |
+
|
91 |
ticker = f"{currency_code.upper()}{main_currency.upper()}=X"
|
92 |
data = yf.download(ticker, period='1d', progress=False)
|
93 |
+
if data.empty:
|
94 |
+
raise ValueError(f"<p style='color: red;'>No exchange rate data for {ticker}</p>")
|
95 |
+
|
96 |
+
return data['Close'].iloc[-1]
|
97 |
except Exception as e:
|
98 |
raise ValueError(f"<p style='color: red;'>Exchange rate retrieval error: {e}</p>")
|
99 |
|
|
|
217 |
# HTML 생성
|
218 |
rebalancing_analysis = css + f"""
|
219 |
<div>
|
|
|
220 |
<div class='table-container'>
|
221 |
<table>
|
222 |
<thead>
|
|
|
272 |
</table>
|
273 |
</div>
|
274 |
<br>
|
|
|
275 |
</div>
|
276 |
"""
|
277 |
|
style.css
CHANGED
@@ -1,35 +1,5 @@
|
|
1 |
-
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;700&display=swap');
|
2 |
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&display=swap');
|
3 |
-
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;700&display=swap');
|
4 |
-
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;700&display=swap');
|
5 |
-
@import url('https://fonts.googleapis.com/css2?family=Cabin:wght@400;700&display=swap');
|
6 |
-
@import url('https://fonts.googleapis.com/css2?family=Titillium+Web:wght@400;700&display=swap');
|
7 |
-
@import url('https://fonts.googleapis.com/css2?family=Shadows+Into+Light&display=swap');
|
8 |
-
@import url('https://fonts.googleapis.com/css2?family=Amatic+SC:wght@400;700&display=swap');
|
9 |
-
@import url('https://fonts.googleapis.com/css2?family=Patrick+Hand&display=swap');
|
10 |
-
@import url('https://fonts.googleapis.com/css2?family=Baloo+Bhai+2:wght@400;700&display=swap');
|
11 |
-
@import url('https://fonts.googleapis.com/css2?family=Poor+Story&display=swap');
|
12 |
-
@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap");
|
13 |
-
@import url('https://fonts.googleapis.com/css2?family=Patrick+Hand+SC&display=swap');
|
14 |
-
@import url('https://fonts.googleapis.com/css2?family=Luckiest+Guy&display=swap');
|
15 |
-
@import url('https://fonts.googleapis.com/css2?family=League+Spartan:wght@400;700&display=swap');
|
16 |
-
@import url('https://fonts.googleapis.com/css2?family=Edu+AU+VIC+WA+NT+Hand&display=swap');
|
17 |
-
@import url('https://fonts.googleapis.com/css2?family=Honk&display=swap');
|
18 |
-
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;700&display=swap');
|
19 |
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;700&display=swap');
|
20 |
-
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap');
|
21 |
-
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap');
|
22 |
-
/* @font-face {
|
23 |
-
font-family: 'Paperlogy Light';
|
24 |
-
src: url('/file=font/Paperlogy/Paperlogy-3Light.ttf') format('truetype');
|
25 |
-
font-weight: normal;
|
26 |
-
font-style: normal;
|
27 |
-
}
|
28 |
-
|
29 |
-
body {
|
30 |
-
font-family: 'Paperlogy Light', sans-serif;
|
31 |
-
} */
|
32 |
-
|
33 |
|
34 |
:root {
|
35 |
--background-color-light: #ffffff;
|
@@ -46,18 +16,10 @@ body {
|
|
46 |
--highlight-edit-text-color-light: #0000ff;
|
47 |
--highlight-edit-bg-color-dark: hsl(45, 100%, 70%);
|
48 |
--highlight-edit-text-color-dark: hsl(240, 100%, 50%);
|
49 |
-
--highlight-yellow-bg-color-light: #ffeb3b;
|
50 |
-
--highlight-yellow-bg-color-dark: #ffca28;
|
51 |
-
--highlight-yellow-text-color-light: #000000;
|
52 |
-
--highlight-yellow-text-color-dark: #000000;
|
53 |
--highlight-sky-bg-color-light: #ddf5fd;
|
54 |
--highlight-sky-bg-color-dark: #89c9e6;
|
55 |
--highlight-sky-text-color-light: #000000;
|
56 |
--highlight-sky-text-color-dark: #000000;
|
57 |
-
--highlight-black-light: #0c343d;
|
58 |
-
--highlight-black-dark: #ffffff;
|
59 |
-
--total-value-color-light: #000000;
|
60 |
-
--total-value-color-dark: #ffeb3b;
|
61 |
}
|
62 |
|
63 |
#col-container {
|
@@ -108,7 +70,6 @@ body {
|
|
108 |
color: var(--sell-color) !important;
|
109 |
}
|
110 |
|
111 |
-
|
112 |
.highlight-edit {
|
113 |
background-color: var(--highlight-edit-bg-color-light);
|
114 |
color: var(--highlight-edit-text-color-light) !important;
|
@@ -119,26 +80,6 @@ body {
|
|
119 |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
120 |
}
|
121 |
|
122 |
-
.highlight-black {
|
123 |
-
background-color: var(--highlight-black-light);
|
124 |
-
color: var(--text-color-dark);
|
125 |
-
padding: 5px 10px;
|
126 |
-
font-weight: bold;
|
127 |
-
border-radius: 5px;
|
128 |
-
display: inline-block;
|
129 |
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
130 |
-
}
|
131 |
-
|
132 |
-
.highlight-yellow {
|
133 |
-
background-color: var(--highlight-yellow-bg-color-light);
|
134 |
-
color: var(--highlight-yellow-text-color-light);
|
135 |
-
padding: 5px 10px;
|
136 |
-
font-weight: bold;
|
137 |
-
border-radius: 5px;
|
138 |
-
display: inline-block;
|
139 |
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
140 |
-
}
|
141 |
-
|
142 |
.highlight-sky {
|
143 |
background-color: var(--highlight-sky-bg-color-light);
|
144 |
color: var(--highlight-sky-text-color-light);
|
@@ -216,7 +157,7 @@ body {
|
|
216 |
}
|
217 |
|
218 |
.table-container tr:hover td {
|
219 |
-
background: #
|
220 |
color: #000;
|
221 |
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
|
222 |
cursor: pointer;
|
@@ -224,16 +165,6 @@ body {
|
|
224 |
}
|
225 |
|
226 |
@media (prefers-color-scheme: dark) {
|
227 |
-
.highlight-yellow {
|
228 |
-
background-color: var(--highlight-yellow-bg-color-dark);
|
229 |
-
color: var(--highlight-yellow-text-color-dark);
|
230 |
-
}
|
231 |
-
|
232 |
-
.highlight-black {
|
233 |
-
background-color: var(--highlight-black-dark);
|
234 |
-
color: var(--text-color-light);
|
235 |
-
}
|
236 |
-
|
237 |
.highlight-sky {
|
238 |
background-color: var(--highlight-sky-bg-color-dark);
|
239 |
color: var(--highlight-sky-text-color-dark) !important;
|
@@ -263,8 +194,8 @@ body {
|
|
263 |
}
|
264 |
|
265 |
.table-container tr:hover td {
|
266 |
-
background:
|
267 |
-
color: #
|
268 |
}
|
269 |
|
270 |
.table-header-bg-before {
|
|
|
|
|
1 |
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&display=swap');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;700&display=swap');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
:root {
|
5 |
--background-color-light: #ffffff;
|
|
|
16 |
--highlight-edit-text-color-light: #0000ff;
|
17 |
--highlight-edit-bg-color-dark: hsl(45, 100%, 70%);
|
18 |
--highlight-edit-text-color-dark: hsl(240, 100%, 50%);
|
|
|
|
|
|
|
|
|
19 |
--highlight-sky-bg-color-light: #ddf5fd;
|
20 |
--highlight-sky-bg-color-dark: #89c9e6;
|
21 |
--highlight-sky-text-color-light: #000000;
|
22 |
--highlight-sky-text-color-dark: #000000;
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
|
25 |
#col-container {
|
|
|
70 |
color: var(--sell-color) !important;
|
71 |
}
|
72 |
|
|
|
73 |
.highlight-edit {
|
74 |
background-color: var(--highlight-edit-bg-color-light);
|
75 |
color: var(--highlight-edit-text-color-light) !important;
|
|
|
80 |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
81 |
}
|
82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
.highlight-sky {
|
84 |
background-color: var(--highlight-sky-bg-color-light);
|
85 |
color: var(--highlight-sky-text-color-light);
|
|
|
157 |
}
|
158 |
|
159 |
.table-container tr:hover td {
|
160 |
+
background: #f0f9ff;
|
161 |
color: #000;
|
162 |
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
|
163 |
cursor: pointer;
|
|
|
165 |
}
|
166 |
|
167 |
@media (prefers-color-scheme: dark) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
.highlight-sky {
|
169 |
background-color: var(--highlight-sky-bg-color-dark);
|
170 |
color: var(--highlight-sky-text-color-dark) !important;
|
|
|
194 |
}
|
195 |
|
196 |
.table-container tr:hover td {
|
197 |
+
background-color: hsl(200, 30%, 30%) !important;
|
198 |
+
color: #fff;
|
199 |
}
|
200 |
|
201 |
.table-header-bg-before {
|