Spaces:
Running
Running
Upload 16 files
Browse files- app.py +1 -1
- interface/dollar_cost_averaging_interface.py +4 -4
- interface/retirement_planning_interface.py +3 -3
- modules/dollar_cost_averaging.py +23 -27
- modules/rebalancing.py +3 -11
- modules/retirement_planning.py +2 -8
- style.css +8 -3
app.py
CHANGED
|
@@ -105,7 +105,7 @@ def create_tab(tab_name, inputs, outputs, update_fn, examples, component_rows):
|
|
| 105 |
render_components(component_rows)
|
| 106 |
clear_buttons(inputs)
|
| 107 |
submit_buttons(inputs, update_fn, outputs)
|
| 108 |
-
with gr.Column(scale=
|
| 109 |
outputs.render()
|
| 110 |
with gr.Accordion("๐ Examples", open=False):
|
| 111 |
gr.Examples(examples=examples, cache_examples=False, inputs=inputs)
|
|
|
|
| 105 |
render_components(component_rows)
|
| 106 |
clear_buttons(inputs)
|
| 107 |
submit_buttons(inputs, update_fn, outputs)
|
| 108 |
+
with gr.Column(scale=3):
|
| 109 |
outputs.render()
|
| 110 |
with gr.Accordion("๐ Examples", open=False):
|
| 111 |
gr.Examples(examples=examples, cache_examples=False, inputs=inputs)
|
interface/dollar_cost_averaging_interface.py
CHANGED
|
@@ -2,20 +2,20 @@ import gradio as gr
|
|
| 2 |
from modules.dollar_cost_averaging import dollar_cost_averaging
|
| 3 |
|
| 4 |
examples = [
|
| 5 |
-
[
|
| 6 |
]
|
| 7 |
|
| 8 |
# Define the inputs
|
| 9 |
-
stock_code = gr.Textbox(label="Stock Code", value="368590")
|
| 10 |
first_purchase = gr.Markdown("<h3 class='h3_title'>FIRST PURCHASE</h3>")
|
| 11 |
old_price = gr.Number(label="Old Price", value=18153)
|
| 12 |
old_quantity = gr.Number(label="Quantity", value=122)
|
| 13 |
second_purchase = gr.Markdown("<h3 class='h3_title'>SECOND PURCHASE</h3>")
|
|
|
|
| 14 |
new_quantity = gr.Number(label="Quantity", value=1)
|
| 15 |
|
| 16 |
-
input = [
|
| 17 |
output = gr.HTML()
|
| 18 |
-
component_rows = [
|
| 19 |
|
| 20 |
# Define the update function
|
| 21 |
def update_output(*args):
|
|
|
|
| 2 |
from modules.dollar_cost_averaging import dollar_cost_averaging
|
| 3 |
|
| 4 |
examples = [
|
| 5 |
+
[200, 90, 100, 200]
|
| 6 |
]
|
| 7 |
|
| 8 |
# Define the inputs
|
|
|
|
| 9 |
first_purchase = gr.Markdown("<h3 class='h3_title'>FIRST PURCHASE</h3>")
|
| 10 |
old_price = gr.Number(label="Old Price", value=18153)
|
| 11 |
old_quantity = gr.Number(label="Quantity", value=122)
|
| 12 |
second_purchase = gr.Markdown("<h3 class='h3_title'>SECOND PURCHASE</h3>")
|
| 13 |
+
new_price = gr.Textbox(label="New Price", value="15000")
|
| 14 |
new_quantity = gr.Number(label="Quantity", value=1)
|
| 15 |
|
| 16 |
+
input = [old_price, old_quantity, new_price, new_quantity]
|
| 17 |
output = gr.HTML()
|
| 18 |
+
component_rows = [first_purchase, old_price, old_quantity, second_purchase, new_price, new_quantity]
|
| 19 |
|
| 20 |
# Define the update function
|
| 21 |
def update_output(*args):
|
interface/retirement_planning_interface.py
CHANGED
|
@@ -27,9 +27,9 @@ post_retirement_dividend_yield = gr.Number(label="Dividend Yield (Post-retiremen
|
|
| 27 |
input = [current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, initial_investment, monthly_contribution, annual_increase_in_monthly_contribution, reinvest_dividends, pre_retirement_dividend_growth, post_retirement_dividend_growth, pre_retirement_dividend_yield, post_retirement_dividend_yield]
|
| 28 |
output = gr.HTML()
|
| 29 |
component_rows = [
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
]
|
| 34 |
|
| 35 |
# Define the update function
|
|
|
|
| 27 |
input = [current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, initial_investment, monthly_contribution, annual_increase_in_monthly_contribution, reinvest_dividends, pre_retirement_dividend_growth, post_retirement_dividend_growth, pre_retirement_dividend_yield, post_retirement_dividend_yield]
|
| 28 |
output = gr.HTML()
|
| 29 |
component_rows = [
|
| 30 |
+
[current_age, retirement_age, life_expectancy],[monthly_income_required, inflation_rate],
|
| 31 |
+
[initial_investment, monthly_contribution, annual_increase_in_monthly_contribution], [reinvest_dividends],
|
| 32 |
+
[pre_retirement_dividend_growth, post_retirement_dividend_growth], [pre_retirement_dividend_yield, post_retirement_dividend_yield]
|
| 33 |
]
|
| 34 |
|
| 35 |
# Define the update function
|
modules/dollar_cost_averaging.py
CHANGED
|
@@ -2,20 +2,20 @@ import yfinance as yf
|
|
| 2 |
import FinanceDataReader as fdr
|
| 3 |
from modules.utils import load_css
|
| 4 |
|
| 5 |
-
def get_stock_price(stock_code):
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
|
| 20 |
def calculate_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity):
|
| 21 |
old_avg_price = float(old_avg_price) if old_avg_price else 0.0
|
|
@@ -36,15 +36,15 @@ def calculate_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_
|
|
| 36 |
|
| 37 |
return new_avg_price, total_quantity, total_investment, new_return, additional_investment, old_return
|
| 38 |
|
| 39 |
-
def dollar_cost_averaging(
|
| 40 |
css = load_css()
|
| 41 |
|
| 42 |
# ์ฃผ์ ๊ฐ๊ฒฉ ๊ฐ์ ธ์ค๊ธฐ
|
| 43 |
-
new_price = get_stock_price(stock_code)
|
| 44 |
|
| 45 |
-
new_price = float(new_price) if new_price else 0.0
|
| 46 |
old_avg_price = float(old_avg_price) if old_avg_price else 0.0
|
| 47 |
old_quantity = float(old_quantity) if old_quantity else 0.0
|
|
|
|
| 48 |
new_quantity = float(new_quantity) if new_quantity else 0.0
|
| 49 |
|
| 50 |
new_avg_price, total_quantity, total_investment, new_return, additional_investment, old_return = calculate_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity)
|
|
@@ -52,9 +52,9 @@ def dollar_cost_averaging(stock_code, old_avg_price, old_quantity, new_quantity)
|
|
| 52 |
emoji_return = ""
|
| 53 |
new_return_class = ""
|
| 54 |
old_return_class = ""
|
| 55 |
-
if
|
| 56 |
emoji_return = "๐ง"
|
| 57 |
-
elif
|
| 58 |
emoji_return = "๐ฅ"
|
| 59 |
else:
|
| 60 |
emoji_return = ""
|
|
@@ -77,18 +77,14 @@ def dollar_cost_averaging(stock_code, old_avg_price, old_quantity, new_quantity)
|
|
| 77 |
<div class="wrap-text">
|
| 78 |
<div>
|
| 79 |
<div style="margin-bottom: 1.5rem;">
|
| 80 |
-
<div style="font-size:
|
| 81 |
-
<div style="font-size: 1.5rem; color: #1c75bc;">
|
| 82 |
-
<span style='color: #1678fb; font-weight: bold;'>{new_price:,}</span>
|
| 83 |
-
</div>
|
| 84 |
-
<hr style="margin: 1.5rem 0;">
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
<div>
|
| 88 |
<div style="margin-bottom: 1.5rem;">
|
| 89 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Old Price</div>
|
| 90 |
<div style="font-size: 1.5rem; color: #1c75bc;">
|
| 91 |
-
<span style='color: #1678fb; font-weight: bold;'>{old_avg_price:,.
|
| 92 |
<span style='font-size: 1rem;'>{old_return_class}</span>
|
| 93 |
</div>
|
| 94 |
<hr style="margin: 1.5rem 0;">
|
|
@@ -98,7 +94,7 @@ def dollar_cost_averaging(stock_code, old_avg_price, old_quantity, new_quantity)
|
|
| 98 |
<div style="margin-bottom: 1.5rem;">
|
| 99 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Average Price</div>
|
| 100 |
<div style="font-size: 1.5rem; color: #1c75bc;">
|
| 101 |
-
<span style='color: #1678fb; font-weight: bold;'>{new_avg_price:,.
|
| 102 |
<span style='font-size: 1rem;'>{new_return_class}</span>
|
| 103 |
</div>
|
| 104 |
<hr style="margin: 1.5rem 0;">
|
|
@@ -108,7 +104,7 @@ def dollar_cost_averaging(stock_code, old_avg_price, old_quantity, new_quantity)
|
|
| 108 |
<div style="margin-bottom: 1.5rem;">
|
| 109 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Additional Investment</div>
|
| 110 |
<div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
|
| 111 |
-
<span style='color: #1678fb'>{additional_investment:,.
|
| 112 |
</div>
|
| 113 |
<hr style="margin: 1.5rem 0;">
|
| 114 |
</div>
|
|
|
|
| 2 |
import FinanceDataReader as fdr
|
| 3 |
from modules.utils import load_css
|
| 4 |
|
| 5 |
+
# def get_stock_price(stock_code):
|
| 6 |
+
# try:
|
| 7 |
+
# # ํ๊ตญ ์ฃผ์ ์ฝ๋๊ฐ ์ซ์๋ง ์๋์ง ํ์ธ (ํ๊ตญ ์ฃผ์์ ๋ณดํต ์ซ์ ์ฝ๋)
|
| 8 |
+
# if stock_code.isdigit():
|
| 9 |
+
# df = fdr.DataReader(stock_code)
|
| 10 |
+
# return df['Close'].iloc[-1]
|
| 11 |
+
# else:
|
| 12 |
+
# # ํ๊ตญ ์ธ ์ฃผ์์ yfinance๋ก ์กฐํ
|
| 13 |
+
# stock = yf.Ticker(stock_code)
|
| 14 |
+
# df = stock.history(period="1d")
|
| 15 |
+
# return df['Close'].iloc[-1]
|
| 16 |
+
# except Exception as e:
|
| 17 |
+
# # ์๋ฌ ๋ฐ์ ์ 0.0์ผ๋ก ์ค์ ํ๊ณ , ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฐํ
|
| 18 |
+
# return None
|
| 19 |
|
| 20 |
def calculate_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity):
|
| 21 |
old_avg_price = float(old_avg_price) if old_avg_price else 0.0
|
|
|
|
| 36 |
|
| 37 |
return new_avg_price, total_quantity, total_investment, new_return, additional_investment, old_return
|
| 38 |
|
| 39 |
+
def dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity):
|
| 40 |
css = load_css()
|
| 41 |
|
| 42 |
# ์ฃผ์ ๊ฐ๊ฒฉ ๊ฐ์ ธ์ค๊ธฐ
|
| 43 |
+
# new_price = get_stock_price(stock_code)
|
| 44 |
|
|
|
|
| 45 |
old_avg_price = float(old_avg_price) if old_avg_price else 0.0
|
| 46 |
old_quantity = float(old_quantity) if old_quantity else 0.0
|
| 47 |
+
new_price = float(new_price) if new_price else 0.0
|
| 48 |
new_quantity = float(new_quantity) if new_quantity else 0.0
|
| 49 |
|
| 50 |
new_avg_price, total_quantity, total_investment, new_return, additional_investment, old_return = calculate_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity)
|
|
|
|
| 52 |
emoji_return = ""
|
| 53 |
new_return_class = ""
|
| 54 |
old_return_class = ""
|
| 55 |
+
if new_price < old_avg_price:
|
| 56 |
emoji_return = "๐ง"
|
| 57 |
+
elif new_price > old_avg_price:
|
| 58 |
emoji_return = "๐ฅ"
|
| 59 |
else:
|
| 60 |
emoji_return = ""
|
|
|
|
| 77 |
<div class="wrap-text">
|
| 78 |
<div>
|
| 79 |
<div style="margin-bottom: 1.5rem;">
|
| 80 |
+
<div style="font-size: 2.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">{emoji_return}</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
</div>
|
| 82 |
</div>
|
| 83 |
<div>
|
| 84 |
<div style="margin-bottom: 1.5rem;">
|
| 85 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Old Price</div>
|
| 86 |
<div style="font-size: 1.5rem; color: #1c75bc;">
|
| 87 |
+
<span style='color: #1678fb; font-weight: bold;'>{old_avg_price:,.2f}</span>
|
| 88 |
<span style='font-size: 1rem;'>{old_return_class}</span>
|
| 89 |
</div>
|
| 90 |
<hr style="margin: 1.5rem 0;">
|
|
|
|
| 94 |
<div style="margin-bottom: 1.5rem;">
|
| 95 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Average Price</div>
|
| 96 |
<div style="font-size: 1.5rem; color: #1c75bc;">
|
| 97 |
+
<span style='color: #1678fb; font-weight: bold;'>{new_avg_price:,.2f}</span>
|
| 98 |
<span style='font-size: 1rem;'>{new_return_class}</span>
|
| 99 |
</div>
|
| 100 |
<hr style="margin: 1.5rem 0;">
|
|
|
|
| 104 |
<div style="margin-bottom: 1.5rem;">
|
| 105 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Additional Investment</div>
|
| 106 |
<div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
|
| 107 |
+
<span style='color: #1678fb'>{additional_investment:,.2f}</span>
|
| 108 |
</div>
|
| 109 |
<hr style="margin: 1.5rem 0;">
|
| 110 |
</div>
|
modules/rebalancing.py
CHANGED
|
@@ -192,17 +192,9 @@ def generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_cu
|
|
| 192 |
|
| 193 |
# HTML ์์ฑ
|
| 194 |
rebalancing_analysis = css + f"""
|
| 195 |
-
<div
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Re-Balancing Analysis | Your Portfolio Holdings as of {current_time}</div>
|
| 199 |
-
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{currency_symbol}{format_value(sum(adj['new_value'] for adj in adjustments))}</span>
|
| 200 |
-
(After Trades)
|
| 201 |
-
<hr style="margin: 1.5rem 0;">
|
| 202 |
-
<br>
|
| 203 |
-
</div>
|
| 204 |
-
</div>
|
| 205 |
-
<div class='table-container wrap-text'>
|
| 206 |
<table>
|
| 207 |
<thead>
|
| 208 |
<tr>
|
|
|
|
| 192 |
|
| 193 |
# HTML ์์ฑ
|
| 194 |
rebalancing_analysis = css + f"""
|
| 195 |
+
<div>
|
| 196 |
+
|
| 197 |
+
<div class='table-container'>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
<table>
|
| 199 |
<thead>
|
| 200 |
<tr>
|
modules/retirement_planning.py
CHANGED
|
@@ -229,22 +229,16 @@ def retirement_planning(
|
|
| 229 |
# HTML ๊ฒฐ๊ณผ ์์ฑ
|
| 230 |
result_html = css + f"""
|
| 231 |
<div class="wrap-text">
|
| 232 |
-
<div>
|
| 233 |
-
<div style="margin-bottom: 1.5rem;">
|
| 234 |
-
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Total Shortfall</div>
|
| 235 |
-
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{abs(negative_differences_sum):,.0f}</span>
|
| 236 |
-
<hr style="margin: 1.5rem 0;">
|
| 237 |
-
</div>
|
| 238 |
<div style="margin-bottom: 1.5rem;">
|
| 239 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Needs Overview</div>
|
| 240 |
<iframe src="data:text/html;base64,{svg_base64}" style="width: 100%; height: 400px; border: none;"></iframe>
|
| 241 |
-
<hr style="margin: 1.5rem 0;">
|
| 242 |
</div>
|
| 243 |
</div>
|
| 244 |
</div>
|
| 245 |
<div style="margin-bottom: 2rem;"></div>
|
| 246 |
<div style="display: flex; align-items: center; justify-content: space-between;">
|
| 247 |
-
<h3>Retirement Plan Overview</h3>
|
| 248 |
<a href="data:text/csv;base64,{csv_base64}" download="retirement_planning.csv" style="padding: 10px 20px; border: 1px solid; border-radius: 5px; background-color: #1678fb; color: white; text-decoration: none;">Download CSV</a>
|
| 249 |
</div>
|
| 250 |
""" + table_html + """
|
|
|
|
| 229 |
# HTML ๊ฒฐ๊ณผ ์์ฑ
|
| 230 |
result_html = css + f"""
|
| 231 |
<div class="wrap-text">
|
| 232 |
+
<div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
<div style="margin-bottom: 1.5rem;">
|
| 234 |
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Needs Overview</div>
|
| 235 |
<iframe src="data:text/html;base64,{svg_base64}" style="width: 100%; height: 400px; border: none;"></iframe>
|
|
|
|
| 236 |
</div>
|
| 237 |
</div>
|
| 238 |
</div>
|
| 239 |
<div style="margin-bottom: 2rem;"></div>
|
| 240 |
<div style="display: flex; align-items: center; justify-content: space-between;">
|
| 241 |
+
<h3>Retirement Plan Overview (Total Shortfall :<span style='font-weight: bold; color: #1678fb'>{abs(negative_differences_sum):,.0f}</span>)</h3>
|
| 242 |
<a href="data:text/csv;base64,{csv_base64}" download="retirement_planning.csv" style="padding: 10px 20px; border: 1px solid; border-radius: 5px; background-color: #1678fb; color: white; text-decoration: none;">Download CSV</a>
|
| 243 |
</div>
|
| 244 |
""" + table_html + """
|
style.css
CHANGED
|
@@ -64,6 +64,9 @@ body {
|
|
| 64 |
margin: 0 auto;
|
| 65 |
max-width: 100%;
|
| 66 |
font-family: 'Quicksand', 'ui-sans-serif', 'system-ui', 'sans-serif';
|
|
|
|
|
|
|
|
|
|
| 67 |
}
|
| 68 |
|
| 69 |
.h3_title {
|
|
@@ -86,9 +89,11 @@ body {
|
|
| 86 |
}
|
| 87 |
|
| 88 |
.wrap-text {
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
| 92 |
}
|
| 93 |
|
| 94 |
.buy-sell {
|
|
|
|
| 64 |
margin: 0 auto;
|
| 65 |
max-width: 100%;
|
| 66 |
font-family: 'Quicksand', 'ui-sans-serif', 'system-ui', 'sans-serif';
|
| 67 |
+
word-wrap: break-word;
|
| 68 |
+
overflow-wrap: break-word;
|
| 69 |
+
white-space: normal;
|
| 70 |
}
|
| 71 |
|
| 72 |
.h3_title {
|
|
|
|
| 89 |
}
|
| 90 |
|
| 91 |
.wrap-text {
|
| 92 |
+
margin: auto; /* ๊ฐ์ด๋ฐ ์ ๋ ฌ */
|
| 93 |
+
padding: 20px;
|
| 94 |
+
border: 1px solid #e5e5e5; /* ์์ ํ
๋๋ฆฌ */
|
| 95 |
+
border-radius: 10px; /* ๋ชจ์๋ฆฌ ๋ฅ๊ธ๊ฒ */
|
| 96 |
+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* ๊ทธ๋ฆผ์ */
|
| 97 |
}
|
| 98 |
|
| 99 |
.buy-sell {
|