Upload 16 files
Browse files- app.py +90 -60
- interface/.DS_Store +0 -0
- interface/about_interface.py +436 -245
- interface/dollar_cost_averaging_interface.py +22 -0
- interface/rebalancing_interface.py +37 -0
- interface/retirement_planning_interface.py +26 -16
- interface/share_price_trend_interface.py +27 -0
- modules/.DS_Store +0 -0
- modules/dollar_cost_averaging.py +122 -0
- modules/rebalancing.py +241 -0
- modules/retirement_planning.py +122 -23
- modules/share_price_trend.py +109 -0
- style.css +24 -6
app.py
CHANGED
|
@@ -1,127 +1,157 @@
|
|
| 1 |
-
|
| 2 |
import gradio as gr
|
| 3 |
-
from interface.
|
| 4 |
-
from interface.
|
| 5 |
-
from interface.
|
| 6 |
-
from interface.retirement_planning_interface import
|
|
|
|
|
|
|
| 7 |
from interface.about_interface import render as render_about_tab
|
| 8 |
|
| 9 |
with gr.Blocks(css='style.css') as demo:
|
| 10 |
with gr.Column(elem_id="col-container"):
|
| 11 |
with gr.Tabs():
|
| 12 |
-
with gr.TabItem("
|
| 13 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 14 |
with gr.Column(): # Column for inputs
|
| 15 |
-
for input_component in
|
| 16 |
input_component.render()
|
| 17 |
# Add the ClearButton below the inputs
|
| 18 |
clear_button = gr.ClearButton(value="Clear")
|
| 19 |
clear_button.click(
|
| 20 |
-
fn=lambda: [None] * len(
|
| 21 |
inputs=[],
|
| 22 |
-
outputs=
|
| 23 |
)
|
| 24 |
|
| 25 |
# Add the Submit Button below the ClearButton
|
| 26 |
submit_button = gr.Button(value="Run", variant="primary")
|
| 27 |
submit_button.click(
|
| 28 |
-
fn=
|
| 29 |
-
inputs=
|
| 30 |
-
outputs=
|
| 31 |
)
|
| 32 |
with gr.Column(): # Column for output
|
| 33 |
-
|
| 34 |
|
| 35 |
-
# Add change event handlers for Portfolio inputs
|
| 36 |
-
# for input_component in portfolio_inputs:
|
| 37 |
-
# input_component.change(
|
| 38 |
-
# fn=portpolio_update_output,
|
| 39 |
-
# inputs=portfolio_inputs,
|
| 40 |
-
# outputs=portpolio_output
|
| 41 |
-
# )
|
| 42 |
# Add examples
|
| 43 |
gr.Examples(
|
| 44 |
examples=examples,
|
| 45 |
cache_examples=False,
|
| 46 |
-
inputs=
|
| 47 |
)
|
| 48 |
-
with gr.TabItem("
|
| 49 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 50 |
with gr.Column(): # Column for inputs
|
| 51 |
-
for input_component in
|
| 52 |
input_component.render()
|
| 53 |
# Add the ClearButton below the inputs
|
| 54 |
clear_button = gr.ClearButton(value="Clear")
|
| 55 |
clear_button.click(
|
| 56 |
-
fn=lambda: [None] * len(
|
| 57 |
inputs=[],
|
| 58 |
-
outputs=
|
| 59 |
)
|
| 60 |
|
| 61 |
# Add the Submit Button below the ClearButton
|
| 62 |
submit_button = gr.Button(value="Run", variant="primary")
|
| 63 |
submit_button.click(
|
| 64 |
-
fn=
|
| 65 |
-
inputs=
|
| 66 |
-
outputs=
|
| 67 |
)
|
| 68 |
with gr.Column(): # Column for output
|
| 69 |
-
|
| 70 |
|
| 71 |
-
|
| 72 |
-
# for input_component in compare_inputs:
|
| 73 |
-
# input_component.change(
|
| 74 |
-
# fn=compare_update_output,
|
| 75 |
-
# inputs=compare_inputs,
|
| 76 |
-
# outputs=compare_output
|
| 77 |
-
# )
|
| 78 |
-
with gr.TabItem("Cost Averaging"):
|
| 79 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 80 |
with gr.Column(): # Column for inputs
|
| 81 |
-
|
| 82 |
old_price.render()
|
| 83 |
old_quantity.render()
|
| 84 |
-
|
| 85 |
new_price.render()
|
| 86 |
new_quantity.render()
|
| 87 |
# Add the ClearButton below the inputs
|
| 88 |
clear_button = gr.ClearButton(value="Clear")
|
| 89 |
clear_button.click(
|
| 90 |
-
fn=lambda: [None] * len(
|
| 91 |
inputs=[],
|
| 92 |
-
outputs=
|
| 93 |
)
|
| 94 |
with gr.Column(): # Column for output
|
| 95 |
-
|
| 96 |
|
| 97 |
# Add change event handlers for cost averaging inputs
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
| 102 |
|
| 103 |
-
with gr.TabItem("
|
| 104 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 105 |
with gr.Column(): # Column for inputs
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
retirement_output.render()
|
| 117 |
-
|
| 118 |
# Add change event handlers for retirement planning inputs
|
| 119 |
-
for input_component in
|
|
|
|
|
|
|
| 120 |
input_component.change(
|
| 121 |
fn=retirement_update_output,
|
| 122 |
-
inputs=
|
|
|
|
|
|
|
| 123 |
outputs=retirement_output
|
| 124 |
)
|
|
|
|
| 125 |
render_about_tab()
|
| 126 |
|
| 127 |
demo.launch(share=True)
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
from interface.rebalancing_interface import examples, rebalancing_inputs, output as rebalancing_output, update_output as rebalancing_update_output
|
| 3 |
+
from interface.share_price_trend_interface import share_price_trend_inputs, output as share_price_trend_output, update_output as share_price_trend_update_output
|
| 4 |
+
from interface.dollar_cost_averaging_interface import dollar_cost_averaging_inputs, output as dollar_cost_averaging_output, update_output as dollar_cost_averaging_update_output, markdown1 as dollar_cost_averaging_markdown1, old_price, old_quantity, markdown2 as dollar_cost_averaging_markdown2, new_price, new_quantity
|
| 5 |
+
from interface.retirement_planning_interface import (
|
| 6 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield, output as retirement_output, update_output as retirement_update_output
|
| 7 |
+
)
|
| 8 |
from interface.about_interface import render as render_about_tab
|
| 9 |
|
| 10 |
with gr.Blocks(css='style.css') as demo:
|
| 11 |
with gr.Column(elem_id="col-container"):
|
| 12 |
with gr.Tabs():
|
| 13 |
+
with gr.TabItem("RE-BALANCING"):
|
| 14 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 15 |
with gr.Column(): # Column for inputs
|
| 16 |
+
for input_component in rebalancing_inputs:
|
| 17 |
input_component.render()
|
| 18 |
# Add the ClearButton below the inputs
|
| 19 |
clear_button = gr.ClearButton(value="Clear")
|
| 20 |
clear_button.click(
|
| 21 |
+
fn=lambda: [None] * len(rebalancing_inputs),
|
| 22 |
inputs=[],
|
| 23 |
+
outputs=rebalancing_inputs
|
| 24 |
)
|
| 25 |
|
| 26 |
# Add the Submit Button below the ClearButton
|
| 27 |
submit_button = gr.Button(value="Run", variant="primary")
|
| 28 |
submit_button.click(
|
| 29 |
+
fn=rebalancing_update_output,
|
| 30 |
+
inputs=rebalancing_inputs,
|
| 31 |
+
outputs=rebalancing_output
|
| 32 |
)
|
| 33 |
with gr.Column(): # Column for output
|
| 34 |
+
rebalancing_output.render()
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# Add examples
|
| 37 |
gr.Examples(
|
| 38 |
examples=examples,
|
| 39 |
cache_examples=False,
|
| 40 |
+
inputs=rebalancing_inputs
|
| 41 |
)
|
| 42 |
+
with gr.TabItem("SHARE PRICE TREND"):
|
| 43 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 44 |
with gr.Column(): # Column for inputs
|
| 45 |
+
for input_component in share_price_trend_inputs:
|
| 46 |
input_component.render()
|
| 47 |
# Add the ClearButton below the inputs
|
| 48 |
clear_button = gr.ClearButton(value="Clear")
|
| 49 |
clear_button.click(
|
| 50 |
+
fn=lambda: [None] * len(share_price_trend_inputs),
|
| 51 |
inputs=[],
|
| 52 |
+
outputs=share_price_trend_inputs
|
| 53 |
)
|
| 54 |
|
| 55 |
# Add the Submit Button below the ClearButton
|
| 56 |
submit_button = gr.Button(value="Run", variant="primary")
|
| 57 |
submit_button.click(
|
| 58 |
+
fn=share_price_trend_update_output,
|
| 59 |
+
inputs=share_price_trend_inputs,
|
| 60 |
+
outputs=share_price_trend_output
|
| 61 |
)
|
| 62 |
with gr.Column(): # Column for output
|
| 63 |
+
share_price_trend_output.render()
|
| 64 |
|
| 65 |
+
with gr.TabItem("DOLLAR-COST AVERAGING"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 67 |
with gr.Column(): # Column for inputs
|
| 68 |
+
dollar_cost_averaging_markdown1.render()
|
| 69 |
old_price.render()
|
| 70 |
old_quantity.render()
|
| 71 |
+
dollar_cost_averaging_markdown2.render()
|
| 72 |
new_price.render()
|
| 73 |
new_quantity.render()
|
| 74 |
# Add the ClearButton below the inputs
|
| 75 |
clear_button = gr.ClearButton(value="Clear")
|
| 76 |
clear_button.click(
|
| 77 |
+
fn=lambda: [None] * len(dollar_cost_averaging_inputs),
|
| 78 |
inputs=[],
|
| 79 |
+
outputs=dollar_cost_averaging_inputs
|
| 80 |
)
|
| 81 |
with gr.Column(): # Column for output
|
| 82 |
+
dollar_cost_averaging_output.render()
|
| 83 |
|
| 84 |
# Add change event handlers for cost averaging inputs
|
| 85 |
+
for input_component in dollar_cost_averaging_inputs:
|
| 86 |
+
input_component.change(
|
| 87 |
+
fn=dollar_cost_averaging_update_output,
|
| 88 |
+
inputs=dollar_cost_averaging_inputs,
|
| 89 |
+
outputs=dollar_cost_averaging_output
|
| 90 |
+
)
|
| 91 |
|
| 92 |
+
with gr.TabItem("RETIREMENT PLANNING"):
|
| 93 |
with gr.Row(): # Use Row to place inputs and output side by side
|
| 94 |
with gr.Column(): # Column for inputs
|
| 95 |
+
|
| 96 |
+
gr.Markdown("### Profile")
|
| 97 |
+
with gr.Row():
|
| 98 |
+
current_age.render()
|
| 99 |
+
retirement_age.render()
|
| 100 |
+
life_expectancy.render()
|
| 101 |
+
monthly_income_required.render()
|
| 102 |
+
inflation_rate.render()
|
| 103 |
+
|
| 104 |
+
gr.Markdown("### Savings")
|
| 105 |
+
with gr.Row():
|
| 106 |
+
current_investment.render()
|
| 107 |
+
monthly_investment.render()
|
| 108 |
+
annual_increase_in_monthly_investment.render() # ์ถ๊ฐ๋ ์
๋ ฅ
|
| 109 |
+
reinvest_dividends.render()
|
| 110 |
+
|
| 111 |
+
gr.Markdown("### Growth")
|
| 112 |
+
with gr.Row():
|
| 113 |
+
pre_retirement_roi.render()
|
| 114 |
+
pre_retirement_dividend_yield.render()
|
| 115 |
+
# retirement_markdown.render()
|
| 116 |
+
post_retirement_roi.render()
|
| 117 |
+
post_retirement_dividend_yield.render()
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
# # Add the ClearButton below the inputs
|
| 121 |
+
# clear_button = gr.ClearButton(value="Clear")
|
| 122 |
+
# clear_button.click(
|
| 123 |
+
# fn=lambda: [None] * len([
|
| 124 |
+
# current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 125 |
+
# ]),
|
| 126 |
+
# inputs=[],
|
| 127 |
+
# outputs=[
|
| 128 |
+
# current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 129 |
+
# ]
|
| 130 |
+
# )
|
| 131 |
+
# # Add the Submit Button below the ClearButton
|
| 132 |
+
# submit_button = gr.Button(value="Run", variant="primary")
|
| 133 |
+
# submit_button.click(
|
| 134 |
+
# fn=retirement_update_output,
|
| 135 |
+
# inputs=[
|
| 136 |
+
# current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 137 |
+
# ],
|
| 138 |
+
# outputs=retirement_output
|
| 139 |
+
# )
|
| 140 |
+
with gr.Column():
|
| 141 |
retirement_output.render()
|
| 142 |
+
|
| 143 |
# Add change event handlers for retirement planning inputs
|
| 144 |
+
for input_component in [
|
| 145 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 146 |
+
]:
|
| 147 |
input_component.change(
|
| 148 |
fn=retirement_update_output,
|
| 149 |
+
inputs=[
|
| 150 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 151 |
+
],
|
| 152 |
outputs=retirement_output
|
| 153 |
)
|
| 154 |
+
|
| 155 |
render_about_tab()
|
| 156 |
|
| 157 |
demo.launch(share=True)
|
interface/.DS_Store
ADDED
|
Binary file (6.15 kB). View file
|
|
|
interface/about_interface.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
|
| 3 |
def render():
|
| 4 |
-
with gr.TabItem("๐ก
|
| 5 |
-
gr.Markdown("## How To Use:")
|
| 6 |
|
| 7 |
def create_tool_section(tool_name, korean_description, english_description):
|
| 8 |
with gr.Accordion(tool_name, open=False):
|
|
@@ -13,311 +12,503 @@ def render():
|
|
| 13 |
|
| 14 |
# Portfolio Rebalancing Tool Section
|
| 15 |
create_tool_section(
|
| 16 |
-
"
|
| 17 |
"""
|
| 18 |
-
|
| 19 |
|
| 20 |
-
์ด ๋๊ตฌ๋
|
| 21 |
|
| 22 |
-
|
| 23 |
-
- **๋ค์ค ํตํ ์ง์**: ๋ชจ๋ ๊ธ์ก์ ๋ฉ์ธ ํตํ๋ก ๋ณํ๋ฉ๋๋ค.
|
| 24 |
-
- **์์ธ ๋ถ์**: ํ์ฌ ํฌํธํด๋ฆฌ์ค, ํตํ ๋ฐฐ๋ถ ๋ฐ ๋ฆฌ๋ฐธ๋ฐ์ฑ ๊ถ์ฅ ์ฌํญ์ ํ์ํฉ๋๋ค. ๋ฆฌ๋ฐธ๋ฐ์ฑ ๊ณ์ฐ์ 1์ฃผ๋จ์๋ก ๊ณ์ฐ๋ฉ๋๋ค.
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
|
| 30 |
-
|
| 31 |
-
```
|
| 32 |
-
AAPL USD 100 2,
|
| 33 |
-
TSLA USD 50 1,
|
| 34 |
-
005930 KRW 200 3
|
| 35 |
-
```
|
| 36 |
-
- `AAPL USD 100 2`: USD๋ก ๋ Apple (AAPL) ์ฃผ์ 100์ฃผ, ๋ชฉํ ๋น์ค 2.
|
| 37 |
-
- `TSLA USD 50 1`: USD๋ก ๋ Tesla (TSLA) ์ฃผ์ 50์ฃผ, ๋ชฉํ ๋น์ค 1.
|
| 38 |
-
- `005930 KRW 200 3`: KRW๋ก ๋ ์ผ์ฑ์ ์ (005930) ์ฃผ์ 200์ฃผ, ๋ชฉํ ๋น์ค 3.
|
| 39 |
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
- ์ฃผ์ ์ฝ๋์ ํตํ ์ฝ๋๊ฐ ์ ํจํ์ง ํ์ธํ์ธ์.
|
| 42 |
- ์ด ๋๊ตฌ๋ ์ฃผ๊ฐ์ ํ์จ์ ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ํ์ํฉ๋๋ค.
|
| 43 |
- Yahoo Finance ๋ฐ FinanceDataReader์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
|
| 44 |
|
| 45 |
-
|
| 46 |
์ด ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ ๋ณด๋ ์ผ๋ฐ์ ์ธ ์ ๋ณด ์ ๊ณต ๋ชฉ์ ๋ง์ ์ํด ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ดํธ์ ๋ชจ๋ ์ ๋ณด๋ ์ ์๋ก ์ ๊ณต๋์ง๋ง, ์ฌ์ดํธ์ ์ ๋ณด์ ์ ํ์ฑ, ์ ์ ์ฑ, ์ ํจ์ฑ, ์ ๋ขฐ์ฑ, ๊ฐ์ฉ์ฑ ๋๋ ์์ ์ฑ์ ๋ํด ๏ฟฝ๏ฟฝ๏ฟฝ์์ ์ด๋ ๋ฌต์์ ์ด๋ ์ด๋ ํ ์ข
๋ฅ์ ์ง์ ์ด๋ ๋ณด์ฆ์ ํ์ง ์์ต๋๋ค. ์ฌ์ดํธ์ ์ฌ์ฉ๊ณผ ์ฌ์ดํธ์ ์ ๋ณด๋ฅผ ์ ๋ขฐํ๋ ๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ฑ
์์
๋๋ค.
|
| 47 |
""",
|
| 48 |
"""
|
| 49 |
-
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
| 52 |
|
| 53 |
-
|
| 54 |
-
- **
|
| 55 |
-
- **
|
| 56 |
|
| 57 |
-
|
| 58 |
-
1. **Enter Portfolio Details**: Enter `stock code currency quantity target ratio` in the specified format. Separate items within a stock with spaces. Separate multiple stock items with commas.
|
| 59 |
-
2. **Set Cash Amount and Ratio**: Specify the cash amount and ratio (%) you want to maintain.
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
-
|
| 68 |
-
- `TSLA USD 50 1`: 50 shares of Tesla (TSLA) in USD, target ratio 1.
|
| 69 |
-
- `005930 KRW 200 3`: 200 shares of Samsung Electronics (005930) in KRW, target ratio 3.
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
- Ensure stock codes and currency codes are valid.
|
| 73 |
- This tool requires an internet connection as it uses real-time stock prices and exchange rates.
|
| 74 |
- Data is sourced from Yahoo Finance and FinanceDataReader.
|
| 75 |
|
| 76 |
-
|
| 77 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 78 |
"""
|
| 79 |
)
|
| 80 |
|
| 81 |
# Stock Comparison Tool Section
|
| 82 |
create_tool_section(
|
| 83 |
-
"
|
| 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 |
-
- **Relative Price
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
-
|
| 125 |
-
1. **Enter Stock Codes**: Provide a comma-separated list of stock codes (e.g., `AAPL, GOOGL, TSLA`).
|
| 126 |
-
2. **Specify Number of Days**: Enter the number of days for the comparison period.
|
| 127 |
-
3. **Output**: Fetches stock prices and displays a comparison graph.
|
| 128 |
|
| 129 |
-
|
| 130 |
-
-
|
| 131 |
-
- `Number of Days`: 30
|
| 132 |
|
| 133 |
-
|
| 134 |
-
- A graph comparing the relative stock prices of Apple (AAPL), Google (GOOGL), and Tesla (TSLA) over the last 30 days.
|
| 135 |
|
| 136 |
-
|
| 137 |
-
- Ensure all stock codes are valid and supported by the data source.
|
| 138 |
-
- The tool fetches data based on trading days, excluding weekends and holidays.
|
| 139 |
-
- An internet connection is required as the tool uses stock price data.
|
| 140 |
-
- Data is sourced from FinanceDataReader.
|
| 141 |
-
- Developed using matplotlib for graphical visualization.
|
| 142 |
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 145 |
"""
|
| 146 |
)
|
| 147 |
|
| 148 |
# Cost Averaging Tool Section
|
| 149 |
create_tool_section(
|
| 150 |
-
"
|
| 151 |
"""
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
-
|
| 163 |
-
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
์ด ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ ๋ณด๋ ์ผ๋ฐ์ ์ธ ์ ๋ณด ์ ๊ณต ๋ชฉ์ ๋ง์ ์ํด ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ดํธ์ ๋ชจ๋ ์ ๋ณด๋ ์ ์๋ก ์ ๊ณต๋์ง๋ง, ์ฌ์ดํธ์ ์ ๋ณด์ ์ ํ์ฑ, ์ ์ ์ฑ, ์ ํจ์ฑ, ์ ๋ขฐ์ฑ, ๊ฐ์ฉ์ฑ ๋๋ ์์ ์ฑ์ ๋ํด ๋ช
์์ ์ด๋ ๋ฌต์์ ์ด๋ ์ด๋ ํ ์ข
๋ฅ์ ์ง์ ์ด๋ ๋ณด์ฆ์ ํ์ง ์์ต๋๋ค. ์ฌ์ดํธ์ ์ฌ์ฉ๊ณผ ์ฌ์ดํธ์ ์ ๋ณด๋ฅผ ์ ๋ขฐํ๋ ๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ฑ
์์
๋๋ค.
|
| 182 |
""",
|
| 183 |
"""
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
This tool helps
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
-
|
| 195 |
-
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 214 |
"""
|
| 215 |
)
|
| 216 |
|
| 217 |
# Retirement Planning Tool Section
|
| 218 |
create_tool_section(
|
| 219 |
-
"
|
| 220 |
"""
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
-
|
| 239 |
-
-
|
| 240 |
-
-
|
| 241 |
-
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
""",
|
| 264 |
"""
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
-
|
| 283 |
-
-
|
| 284 |
-
-
|
| 285 |
-
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 307 |
"""
|
| 308 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
-
|
| 311 |
-
gr.Markdown("""
|
| 312 |
-
## Support Us
|
| 313 |
-
If you find this tool useful and would like to support the development of such projects, please consider making a donation. Your support is greatly appreciated.
|
| 314 |
-
|
| 315 |
-
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M8SBRC396DPBW)
|
| 316 |
|
| 317 |
-
|
| 318 |
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
|
| 3 |
def render():
|
| 4 |
+
with gr.TabItem("๐ก HINT"):
|
|
|
|
| 5 |
|
| 6 |
def create_tool_section(tool_name, korean_description, english_description):
|
| 7 |
with gr.Accordion(tool_name, open=False):
|
|
|
|
| 12 |
|
| 13 |
# Portfolio Rebalancing Tool Section
|
| 14 |
create_tool_section(
|
| 15 |
+
"RE-BALANCING CALCULATOR",
|
| 16 |
"""
|
| 17 |
+
## ๐ ๋ฆฌ๋ฐธ๋ฐ์ฑ ๊ณ์ฐ๊ธฐ
|
| 18 |
|
| 19 |
+
์ด ๋๊ตฌ๋ ์ฃผ์ด์ง ์ฃผ์ ๋ณด์ ๋, ํ๊ธ ๊ธ์ก ๋ฐ ํ๊ธ ๋น์จ์ ๋ฐํ์ผ๋ก ํฌํธํด๋ฆฌ์ค๋ฅผ ๋ถ์ํ๊ณ ์ฌ์กฐ์ ํฉ๋๋ค.
|
| 20 |
|
| 21 |
+
### ์
๋ ฅ๊ฐ
|
|
|
|
|
|
|
| 22 |
|
| 23 |
+
- **์ฃผ์ ๋ณด์ ๋ (holdings)**: ๊ฐ ์ฃผ์์ ์ฝ๋, ํตํ ์ฝ๋, ์๋, ๋ชฉํ ๋น์จ์ ์
๋ ฅํฉ๋๋ค. ์ฌ๋ฌ ์ฃผ์ ์ข
๋ชฉ์ ์ผํ๋ก ๊ตฌ๋ถํ์ฌ ์
๋ ฅํฉ๋๋ค. ์: `AAPL USD 10 0.30,TSLA USD 5 0.20`
|
| 24 |
+
- **ํ๊ธ ๊ธ์ก (cash_amount)**: ํฌํธํด๋ฆฌ์ค์ ํฌํจ๋ ํ๊ธ์ ๊ธ์ก์
๋๋ค. ์: `1000`
|
| 25 |
+
- **ํ๊ธ ๋น์จ (cash_ratio)**: ํฌํธํด๋ฆฌ์ค์์ ํ๊ธ์ด ์ฐจ์งํ๋ ๋น์จ์
๋๋ค. ์: `10` (10%)
|
| 26 |
|
| 27 |
+
### ๊ธฐ๋ฅ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
1. **์
๋ ฅ ํ์ฑ**: ์ฃผ์ ๋ณด์ ๋, ํ๊ธ ๊ธ์ก ๋ฐ ๋น์จ์ ๋ถ์ํ์ฌ ๊ฐ ์ฃผ์์ ๋ชฉํ ๋น์จ์ ๊ณ์ฐํฉ๋๋ค.
|
| 30 |
+
2. **ํ์จ ์กฐํ**: ๊ฐ ์ฃผ์์ ํตํ ์ฝ๋์ ๋ฉ์ธ ํตํ ๊ฐ์ ํ์จ์ ์กฐํํ์ฌ ์ฃผ์ ๊ฐ๊ฒฉ์ ๋ณํํฉ๋๋ค.
|
| 31 |
+
3. **ํฌํธํด๋ฆฌ์ค ๊ตฌ์ถ**: ์ฃผ์๊ณผ ํ๊ธ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ํฌํธํด๋ฆฌ์ค๋ฅผ ๊ตฌ์ถํฉ๋๋ค.
|
| 32 |
+
4. **ํฌํธํด๋ฆฌ์ค ์ฌ์กฐ์ ์ ๋ณด ์ ๊ณต**: ํ์ฌ ํฌํธํด๋ฆฌ์ค์ ๊ฐ์น์ ๋ชฉํ ๋น์จ์ ๋น๊ตํ์ฌ ์ฌ์กฐ์ ํ์์ฑ์ ๋ถ์ํฉ๋๋ค.
|
| 33 |
+
- **ํ์ฌ ๋ณด์ ๋ ๋ฐ ๊ฐ์น**: ๊ฐ ์ฃผ์ ๋ฐ ํ๊ธ์ ํ์ฌ ๋ณด์ ๋๊ณผ ๊ฐ์น๋ฅผ ๊ณ์ฐํฉ๋๋ค.
|
| 34 |
+
- **์ฌ์กฐ์ ํญ๋ชฉ**: ๋ชฉํ ๋น์จ์ ๋ง์ถ๊ธฐ ์ํด ๋งค์ ๋๋ ๋งค๋ํด์ผ ํ ์ฃผ์ ์๋๊ณผ ๊ธ์ก์ ๊ณ์ฐํฉ๋๋ค.
|
| 35 |
+
- **ํตํ๋ณ ์์ฝ**: ํฌํธํด๋ฆฌ์ค์ ํตํ๋ณ ๋ถํฌ๋ฅผ ์์ฝํฉ๋๋ค.
|
| 36 |
+
|
| 37 |
+
### ๊ฒฐ๊ณผ
|
| 38 |
+
|
| 39 |
+
- **ํฌํธํด๋ฆฌ์ค ์ ๋ณด**: ํ์ฌ ํฌํธํด๋ฆฌ์ค์ ์ด ๊ฐ์น ๋ฐ ํ๊ธ ์ ๋ณด๋ฅผ ํฌํจํ HTML ์ฝ๋์
๋๋ค.
|
| 40 |
+
- **ํ์ฌ ๋ณด์ ๋ ๋ฐ ๊ฐ์น**: ๊ฐ ์ฃผ์์ ํ์ฌ ๋น์จ ๋ฐ ๊ฐ์น์ ๋ํ ์ ๋ณด์
๋๋ค.
|
| 41 |
+
- **์ฌ์กฐ์ ๋ถ์**: ์ฃผ์์ ๋งค์/๋งค๋ ์กฐ์ ์ด ํ์ํ ํญ๋ชฉ ๋ฐ ๊ธ์ก์ ๋ํ ์์ธ ๋ถ์์
๋๋ค.
|
| 42 |
+
- **ํตํ๋ณ ์์ฝ**: ํฌํธํด๋ฆฌ์ค์ ํตํ๋ณ ๋น์จ ๋ฐ ์ด ๊ฐ์น๋ฅผ ์์ฝํ ์ ๋ณด์
๋๋ค.
|
| 43 |
+
|
| 44 |
+
์ด ๋๊ตฌ๋ฅผ ํตํด ํฌํธํด๋ฆฌ์ค๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์กฐ์ ํ๊ณ , ์ฃผ์ ๋ฐ ํ๊ธ์ ๋น์จ์ ์ต์ ํํ ์ ์์ต๋๋ค! ๐๐ผ
|
| 45 |
+
|
| 46 |
+
### ์ค์ ์ฌํญ
|
| 47 |
- ์ฃผ์ ์ฝ๋์ ํตํ ์ฝ๋๊ฐ ์ ํจํ์ง ํ์ธํ์ธ์.
|
| 48 |
- ์ด ๋๊ตฌ๋ ์ฃผ๊ฐ์ ํ์จ์ ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ํ์ํฉ๋๋ค.
|
| 49 |
- Yahoo Finance ๋ฐ FinanceDataReader์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
|
| 50 |
|
| 51 |
+
### ๋ฉด์ฑ
์กฐํญ
|
| 52 |
์ด ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ ๋ณด๋ ์ผ๋ฐ์ ์ธ ์ ๋ณด ์ ๊ณต ๋ชฉ์ ๋ง์ ์ํด ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ดํธ์ ๋ชจ๋ ์ ๋ณด๋ ์ ์๋ก ์ ๊ณต๋์ง๋ง, ์ฌ์ดํธ์ ์ ๋ณด์ ์ ํ์ฑ, ์ ์ ์ฑ, ์ ํจ์ฑ, ์ ๋ขฐ์ฑ, ๊ฐ์ฉ์ฑ ๋๋ ์์ ์ฑ์ ๋ํด ๏ฟฝ๏ฟฝ๏ฟฝ์์ ์ด๋ ๋ฌต์์ ์ด๋ ์ด๋ ํ ์ข
๋ฅ์ ์ง์ ์ด๋ ๋ณด์ฆ์ ํ์ง ์์ต๋๋ค. ์ฌ์ดํธ์ ์ฌ์ฉ๊ณผ ์ฌ์ดํธ์ ์ ๋ณด๋ฅผ ์ ๋ขฐํ๋ ๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ฑ
์์
๋๋ค.
|
| 53 |
""",
|
| 54 |
"""
|
| 55 |
+
## ๐ RE-BALANCING CALCULATOR
|
| 56 |
+
|
| 57 |
+
This tool analyzes and rebalances a portfolio based on given stock holdings, cash amount, and cash ratio.
|
| 58 |
|
| 59 |
+
### Input Values
|
| 60 |
|
| 61 |
+
- **Stock Holdings (holdings)**: Enter the stock code, currency code, quantity, and target weight for each stock. Separate multiple stocks with commas. Example: `AAPL USD 10 0.30,TSLA USD 5 0.20`
|
| 62 |
+
- **Cash Amount (cash_amount)**: The amount of cash in the portfolio. Example: `1000`
|
| 63 |
+
- **Cash Ratio (cash_ratio)**: The proportion of cash in the portfolio. Example: `10` (10%)
|
| 64 |
|
| 65 |
+
### Functionality
|
|
|
|
|
|
|
| 66 |
|
| 67 |
+
1. **Parse Input**: Analyzes stock holdings, cash amount, and ratio to calculate the target weights for each stock.
|
| 68 |
+
2. **Exchange Rate Lookup**: Retrieves exchange rates between each stock's currency code and the main currency to convert stock prices.
|
| 69 |
+
3. **Build Portfolio**: Constructs the portfolio based on stock and cash information.
|
| 70 |
+
4. **Get Portfolio Rebalancing Information**: Compares the current portfolio value and target weights to analyze rebalancing needs.
|
| 71 |
+
- **Current Holdings and Value**: Calculates the current holdings and value of each stock and cash.
|
| 72 |
+
- **Rebalancing Items**: Calculates the quantities and amounts of stocks to buy or sell to meet target weights.
|
| 73 |
+
- **Currency Summary**: Summarizes the portfolio's distribution by currency.
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
### Results
|
| 76 |
+
|
| 77 |
+
- **Portfolio Information**: HTML code containing the total value of the current portfolio and cash information.
|
| 78 |
+
- **Current Holdings and Value**: Information on the current weights and values of each stock.
|
| 79 |
+
- **Rebalancing Analysis**: Detailed analysis of the adjustments needed to buy/sell stocks to meet target weights.
|
| 80 |
+
- **Currency Summary**: Summary of portfolio distribution by currency, including total value.
|
| 81 |
+
|
| 82 |
+
Use this tool to efficiently rebalance your portfolio and optimize the ratio of stocks and cash! ๐๐ผ
|
| 83 |
+
|
| 84 |
+
### Important Notes
|
| 85 |
- Ensure stock codes and currency codes are valid.
|
| 86 |
- This tool requires an internet connection as it uses real-time stock prices and exchange rates.
|
| 87 |
- Data is sourced from Yahoo Finance and FinanceDataReader.
|
| 88 |
|
| 89 |
+
### Disclaimer
|
| 90 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 91 |
"""
|
| 92 |
)
|
| 93 |
|
| 94 |
# Stock Comparison Tool Section
|
| 95 |
create_tool_section(
|
| 96 |
+
"STOCK PRICE TREND VISUALIZATION",
|
| 97 |
"""
|
| 98 |
+
## ๐ ์ฃผ์ ๊ฐ๊ฒฉ ์ถ์ธ ์๊ฐํ
|
| 99 |
+
|
| 100 |
+
์ด ์ฝ๋๋ ์ฃผ์ด์ง ์ฃผ์ ์ฝ๋๋ค์ ๊ฐ๊ฒฉ ์ถ์ธ๋ฅผ ์๊ฐํํฉ๋๋ค. ์ฌ๋ฌ ์ฃผ์์ ๊ฐ๊ฒฉ์ ์ถ์ ํ๊ณ ๋น๊ตํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค. ๐
|
| 101 |
+
|
| 102 |
+
### ์
๋ ฅ๊ฐ
|
| 103 |
+
|
| 104 |
+
- **์ฃผ์ ์ฝ๋ (stock_codes)**: ๊ฐ๊ฒฉ์ ์ถ์ ํ ์ฃผ์์ ์ฝ๋๋ค์
๋๋ค. ์ผํ๋ก ๊ตฌ๋ถ๋ ๋ฌธ์์ด ํ์์ผ๋ก ์
๋ ฅํฉ๋๋ค. ์: `AAPL,GOOGL,MSFT`
|
| 105 |
+
- **์ผ์ (days)**: ๊ฐ๊ฒฉ์ ์กฐํํ ๊ธฐ๊ฐ์
๋๋ค. ์: `30` (์ต๊ทผ 30์ผ)
|
| 106 |
+
|
| 107 |
+
### ๊ธฐ๋ฅ
|
| 108 |
+
|
| 109 |
+
1. **์ฃผ์ ๊ฐ๊ฒฉ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ**: ์ฃผ์ด์ง ์ฃผ์ ์ฝ๋์ ๊ธฐ๊ฐ์ ๋ํด ์ฃผ์ ๊ฐ๊ฒฉ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
|
| 110 |
+
2. **๊ฐ๊ฒฉ ๋ณ๋ ์๊ฐํ**: ๊ฐ ์ฃผ์์ ๊ฐ๊ฒฉ ๋ณ๋์ ์๊ฐํํ์ฌ ๋น๊ตํ ์ ์๋ ๊ทธ๋ํ๋ฅผ ์์ฑํฉ๋๋ค.
|
| 111 |
+
- **์๋ ๊ฐ๊ฒฉ**: ๊ฐ ์ฃผ์์ ๊ฐ๊ฒฉ์ ์ฒ์ ๊ฐ๊ฒฉ์ ๋ํด ์๋์ ์ผ๋ก ๋ณ๋์ ๋ณด์ฌ์ค๋๋ค.
|
| 112 |
+
- **์คํ์ผ**: ๊ทธ๋ํ์๋ ์์ ๊ทธ๋ผ๋์ธํธ์ ๊ทธ๋ฆผ์ ํจ๊ณผ๊ฐ ์ ์ฉ๋ฉ๋๋ค.
|
| 113 |
+
- **์ ๋๋ฉ์ด์
**: ์ ์ ์ ๋๋ฉ์ด์
ํจ๊ณผ๊ฐ ์ถ๊ฐ๋์ด ์๊ฐ์ ์ผ๋ก ๋์ฑ ๋งค๋ ฅ์ ์
๋๋ค.
|
| 114 |
+
3. **๋ฐ์ดํฐ ํ
์ด๋ธ ์์ฑ**: ๊ฐ ๋ ์ง์ ์ฃผ์์ ๊ฐ๊ฒฉ์ ํฌํจํ HTML ํ
์ด๋ธ์ ์์ฑํฉ๋๋ค.
|
| 115 |
+
|
| 116 |
+
### ๊ฒฐ๊ณผ
|
| 117 |
+
|
| 118 |
+
- **๊ทธ๋ํ**: ์ต๊ทผ `days` ์ผ ๋์์ ์๋ ์ฃผ์ ๊ฐ๊ฒฉ ์ถ์ธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ทธ๋ํ์
๋๋ค.
|
| 119 |
+
- **๋ฐ์ดํฐ ํ
์ด๋ธ**: ๋ ์ง๋ณ๋ก ๊ฐ ์ฃผ์์ ๊ฐ๊ฒฉ์ ํฌํจํ๋ HTML ํ
์ด๋ธ์
๋๋ค.
|
| 120 |
+
|
| 121 |
+
### ์ฌ์ฉ ์์
|
| 122 |
+
|
| 123 |
+
์ฃผ์ ์ฝ๋์ ๊ธฐ๊ฐ์ ์
๋ ฅํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค:
|
| 124 |
+
|
| 125 |
+
- **์ฃผ์ ์ฝ๋**: `AAPL,GOOGL,MSFT`
|
| 126 |
+
- **์ผ์**: `30`
|
| 127 |
+
|
| 128 |
+
๊ฒฐ๊ณผ๋ ์ต๊ทผ 30์ผ ๋์์ ์ฃผ์ ๊ฐ๊ฒฉ ๋ณ๋์ ๋ณด์ฌ์ฃผ๋ ๊ทธ๋ํ์ ๋ฐ์ดํฐ ํ
์ด๋ธ์ด ํฌํจ๋ฉ๋๋ค.
|
| 129 |
+
|
| 130 |
+
์ด ๋๊ตฌ๋ฅผ ํตํด ์ฃผ์ ๊ฐ๊ฒฉ์ ๋ณ๋ ์ถ์ธ๋ฅผ ํ๋์ ํ์ธํ๊ณ ๋ถ์ํ ์ ์์ต๋๋ค! ๐๐ก
|
| 131 |
+
|
| 132 |
+
### ์ค์ ์ฌํญ
|
| 133 |
+
- ์ฃผ์ ์ฝ๋๊ฐ ์ ํจํ์ง ํ์ธํ์ธ์.
|
| 134 |
+
- ์ด ๋๊ตฌ๋ ์ฃผ๊ฐ์ ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ํ์ํฉ๋๋ค.
|
| 135 |
+
- Yahoo Finance ๋ฐ FinanceDataReader์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
|
| 136 |
+
|
| 137 |
+
### ๋ฉด์ฑ
์กฐํญ
|
| 138 |
์ด ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ ๋ณด๋ ์ผ๋ฐ์ ์ธ ์ ๋ณด ์ ๊ณต ๋ชฉ์ ๋ง์ ์ํด ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ดํธ์ ๋ชจ๋ ์ ๋ณด๋ ์ ์๋ก ์ ๊ณต๋์ง๋ง, ์ฌ์ดํธ์ ์ ๋ณด์ ์ ํ์ฑ, ์ ์ ์ฑ, ์ ํจ์ฑ, ์ ๋ขฐ์ฑ, ๊ฐ์ฉ์ฑ ๋๋ ์์ ์ฑ์ ๋ํด ๋ช
์์ ์ด๋ ๋ฌต์์ ์ด๋ ์ด๋ ํ ์ข
๋ฅ์ ์ง์ ์ด๋ ๋ณด์ฆ์ ํ์ง ์์ต๋๋ค. ์ฌ์ดํธ์ ์ฌ์ฉ๊ณผ ์ฌ์ดํธ์ ์ ๋ณด๋ฅผ ์ ๋ขฐํ๋ ๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ฑ
์์
๋๋ค.
|
| 139 |
""",
|
| 140 |
"""
|
| 141 |
+
## ๐ Stock Price Trend Visualization
|
| 142 |
+
|
| 143 |
+
This script visualizes the price trends of given stock codes over a specified period. It allows you to track and compare the price movements of multiple stocks. ๐
|
| 144 |
+
|
| 145 |
+
### Input Values
|
| 146 |
+
|
| 147 |
+
- **Stock Codes (stock_codes)**: The codes of the stocks you want to track. Enter them as a comma-separated string. Example: `AAPL,GOOGL,MSFT`
|
| 148 |
+
- **Days (days)**: The period for which you want to fetch the price data. Example: `30` (last 30 days)
|
| 149 |
|
| 150 |
+
### Functionality
|
| 151 |
|
| 152 |
+
1. **Fetch Stock Price Data**: Retrieves stock price data for the given stock codes and period.
|
| 153 |
+
2. **Visualize Price Trends**: Creates a graph to visualize and compare the price trends of each stock.
|
| 154 |
+
- **Relative Price**: Shows each stock's price relative to its initial price.
|
| 155 |
+
- **Style**: The graph includes color gradients and shadow effects.
|
| 156 |
+
- **Animation**: Line animation effects are added for visual appeal.
|
| 157 |
+
3. **Generate Data Table**: Creates an HTML table with the price data for each stock and date.
|
| 158 |
|
| 159 |
+
### Results
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
+
- **Graph**: A graph showing the relative stock price trends over the last `days` days.
|
| 162 |
+
- **Data Table**: An HTML table displaying the prices of each stock by date.
|
|
|
|
| 163 |
|
| 164 |
+
### Example Usage
|
|
|
|
| 165 |
|
| 166 |
+
Given stock codes and days, you can obtain results such as:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
+
- **Stock Codes**: `AAPL,GOOGL,MSFT`
|
| 169 |
+
- **Days**: `30`
|
| 170 |
+
|
| 171 |
+
The result will include a graph showing the price trends over the last 30 days and an HTML table with the price data.
|
| 172 |
+
|
| 173 |
+
Use this tool to easily analyze and visualize stock price movements! ๐๐ก
|
| 174 |
+
|
| 175 |
+
### Important Notes
|
| 176 |
+
- Ensure stock codes are valid.
|
| 177 |
+
- This tool requires an internet connection as it uses real-time stock prices.
|
| 178 |
+
- Data is sourced from Yahoo Finance and FinanceDataReader.
|
| 179 |
+
|
| 180 |
+
### Disclaimer
|
| 181 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 182 |
"""
|
| 183 |
)
|
| 184 |
|
| 185 |
# Cost Averaging Tool Section
|
| 186 |
create_tool_section(
|
| 187 |
+
"DOLLAR-COST AVERAGING CALCULATOR",
|
| 188 |
"""
|
| 189 |
+
## ๐ธ ๋ฌผํ๊ธฐ ๊ณ์ฐ๊ธฐ
|
| 190 |
+
|
| 191 |
+
๋ฌผํ๊ธฐ(Dollar-Cost Averaging, DCA) ๊ณ์ฐ๊ธฐ๋ฅผ ์ด์ฉํด ๋ณด์ธ์! ์ด ๊ณ์ฐ๊ธฐ๋ ํน์ ์ฃผ์์ ๋ํ ์ถ๊ฐ ํฌ์๋ฅผ ํ ๋ ํ๊ท ๊ตฌ๋งค ๊ฐ๊ฒฉ์ ๊ณ์ฐํด์ค๋๋ค. ๐
|
| 192 |
+
|
| 193 |
+
### ์
๋ ฅ๊ฐ ์ค์ ๐
|
| 194 |
+
|
| 195 |
+
๋ค์์ ๊ฐ์ ์
๋ ฅํด ์ฃผ์ธ์:
|
| 196 |
+
|
| 197 |
+
- **์ด์ ํ๊ท ๊ฐ๊ฒฉ (old_avg_price)**: ๊ธฐ์กด ์ฃผ์์ ํ๊ท ๊ฐ๊ฒฉ ๐ต
|
| 198 |
+
- **์ด์ ์๋ (old_quantity)**: ๊ธฐ์กด ์ฃผ์์ ์๋ ๐ฆ
|
| 199 |
+
- **์ ๊ฐ๊ฒฉ (new_price)**: ์๋ก ๊ตฌ๋งคํ ์ฃผ์์ ๊ฐ๊ฒฉ ๐ฒ
|
| 200 |
+
- **์ ์๋ (new_quantity)**: ์๋ก ๊ตฌ๋งคํ ์ฃผ์์ ์๋ ๐
|
| 201 |
+
|
| 202 |
+
### ๊ณ์ฐ ๋ฐฉ๋ฒ ๐ข
|
| 203 |
+
|
| 204 |
+
1. **ํ์ฌ ํฌ์ ๊ธ์ก ๊ณ์ฐ**: ์ด์ ํ๊ท ๊ฐ๊ฒฉ๊ณผ ์ด์ ์๋์ ๊ณฑํ์ฌ ํ์ฌ ํฌ์ ๊ธ์ก์ ๊ณ์ฐํฉ๋๋ค.
|
| 205 |
+
2. **์ถ๊ฐ ํฌ์ ๊ธ์ก ๊ณ์ฐ**: ์ ๊ฐ๊ฒฉ๊ณผ ์ ์๋์ ๊ณฑํ์ฌ ์ถ๊ฐ ํฌ์ ๊ธ์ก์ ๊ณ์ฐํฉ๋๋ค.
|
| 206 |
+
3. **์ด ํฌ์ ๊ธ์ก ๋ฐ ์ด ์ฃผ์ ์ ๊ณ์ฐ**: ํ์ฌ ํฌ์ ๊ธ์ก๊ณผ ์ถ๊ฐ ํฌ์ ๊ธ์ก์ ํฉ์ณ์ ์ด ํฌ์ ๊ธ์ก์ ๊ณ์ฐํ๊ณ , ์ด์ ์๋๊ณผ ์ ์๋์ ๋ํ์ฌ ์ด ์ฃผ์ ์๋ฅผ ๊ณ์ฐํฉ๋๋ค.
|
| 207 |
+
4. **์ ํ๊ท ๊ฐ๊ฒฉ ๊ณ์ฐ**: ์ด ํฌ์ ๊ธ์ก์ ์ด ์ฃผ์ ์๋ก ๋๋์ด ์๋ก์ด ํ๊ท ๊ฐ๊ฒฉ์ ๊ณ์ฐํฉ๋๋ค.
|
| 208 |
+
5. **์์ต๋ฅ ๊ณ์ฐ**: ์ด์ ๊ณผ ์๋ก์ด ์์ต๋ฅ ์ ๊ณ์ฐํ์ฌ ํฌ์ ๊ฒฐ๊ณผ๋ฅผ ๋ถ์ํฉ๋๋ค.
|
| 209 |
+
|
| 210 |
+
### ๊ฒฐ๊ณผ ๐ฏ
|
| 211 |
+
|
| 212 |
+
**1) ์ด์ ์์ต๋ฅ (Old Return)**
|
| 213 |
+
|
| 214 |
+
์ด์ ํ๊ท ๊ฐ๊ฒฉ์ ๊ธฐ์ค์ผ๋ก ํ ์์ต๋ฅ ์ ํ์ํฉ๋๋ค.
|
| 215 |
+
|
| 216 |
+
**2) ์๋ก์ด ์์ต๋ฅ (New Return)**
|
| 217 |
+
|
| 218 |
+
์๋ก์ด ํ๊ท ๊ฐ๊ฒฉ์ ๊ธฐ์ค์ผ๋ก ํ ์์ต๋ฅ ์ ํ์ํฉ๋๋ค.
|
| 219 |
+
|
| 220 |
+
**3) ์ถ๊ฐ ํฌ์ ๊ธ์ก (Additional Investment)**
|
| 221 |
+
|
| 222 |
+
์ถ๊ฐ๋ก ํฌ์ํ ๊ธ์ก์ ํ์ํฉ๋๋ค.
|
| 223 |
+
|
| 224 |
+
**4) ์๋ก์ด ํ๊ท ๊ฐ๊ฒฉ (Average Price)**
|
| 225 |
+
|
| 226 |
+
์๋กญ๊ฒ ๊ณ์ฐ๋ ํ๊ท ๊ฐ๊ฒฉ์ ํ์ํฉ๋๋ค.
|
| 227 |
+
|
| 228 |
+
**5) ์ด ์๋ (Total Quantity)**
|
| 229 |
+
|
| 230 |
+
์ด ์ฃผ์ ์๋์ ํ์ํฉ๋๋ค.
|
| 231 |
+
|
| 232 |
+
**6) ์ด ํฌ์ ๊ธ์ก (Total Investment)**
|
| 233 |
+
|
| 234 |
+
์ด ํฌ์ ๊ธ์ก์ ํ์ํฉ๋๋ค.
|
| 235 |
+
|
| 236 |
+
### ์์ ๐
|
| 237 |
+
|
| 238 |
+
- **์ด์ ํ๊ท ๊ฐ๊ฒฉ**: 100
|
| 239 |
+
- **์ด์ ์๋**: 10
|
| 240 |
+
- **์ ๊ฐ๊ฒฉ**: 120
|
| 241 |
+
- **์ ์๋**: 5
|
| 242 |
+
|
| 243 |
+
๊ฒฐ๊ณผ:
|
| 244 |
+
- ์๋ก์ด ํ๊ท ๊ฐ๊ฒฉ: 105
|
| 245 |
+
- ์ด ์๋: 15
|
| 246 |
+
- ์ด ํฌ์ ๊ธ์ก: 1,575
|
| 247 |
+
- ์ด์ ์์ต๋ฅ : 20.00%
|
| 248 |
+
- ์๋ก์ด ์์ต๋ฅ : 14.29%
|
| 249 |
+
- ์ถ๊ฐ ํฌ์ ๊ธ์ก: 600
|
| 250 |
+
|
| 251 |
+
๋ฌผํ๊ธฐ ๊ณ์ฐ๊ธฐ๋ฅผ ํตํด ํฌ์์ ํ๊ท ๊ตฌ๋งค ๊ฐ๊ฒฉ์ ์ฝ๊ฒ ๊ณ์ฐํด ๋ณด์ธ์! ๐๐ฐ
|
| 252 |
+
|
| 253 |
+
### ๋ฉด์ฑ
์กฐํญ
|
| 254 |
์ด ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ ๋ณด๋ ์ผ๋ฐ์ ์ธ ์ ๋ณด ์ ๊ณต ๋ชฉ์ ๋ง์ ์ํด ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ดํธ์ ๋ชจ๋ ์ ๋ณด๋ ์ ์๋ก ์ ๊ณต๋์ง๋ง, ์ฌ์ดํธ์ ์ ๋ณด์ ์ ํ์ฑ, ์ ์ ์ฑ, ์ ํจ์ฑ, ์ ๋ขฐ์ฑ, ๊ฐ์ฉ์ฑ ๋๋ ์์ ์ฑ์ ๋ํด ๋ช
์์ ์ด๋ ๋ฌต์์ ์ด๋ ์ด๋ ํ ์ข
๋ฅ์ ์ง์ ์ด๋ ๋ณด์ฆ์ ํ์ง ์์ต๋๋ค. ์ฌ์ดํธ์ ์ฌ์ฉ๊ณผ ์ฌ์ดํธ์ ์ ๋ณด๋ฅผ ์ ๋ขฐํ๋ ๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ฑ
์์
๋๋ค.
|
| 255 |
""",
|
| 256 |
"""
|
| 257 |
+
## ๐ธ Dollar-Cost Averaging Calculator
|
| 258 |
+
|
| 259 |
+
Try out the Dollar-Cost Averaging (DCA) calculator! This tool helps you calculate the average purchase price of stocks when making additional investments. ๐
|
| 260 |
+
|
| 261 |
+
### Input Values ๐
|
| 262 |
+
|
| 263 |
+
Please enter the following details:
|
| 264 |
+
|
| 265 |
+
- **Old Average Price (old_avg_price)**: The average price of the existing stocks ๐ต
|
| 266 |
+
- **Old Quantity (old_quantity)**: The quantity of the existing stocks ๐ฆ
|
| 267 |
+
- **New Price (new_price)**: The price of the newly purchased stocks ๐ฒ
|
| 268 |
+
- **New Quantity (new_quantity)**: The quantity of the newly purchased stocks ๐
|
| 269 |
+
|
| 270 |
+
### Calculation Process ๐ข
|
| 271 |
+
|
| 272 |
+
1. **Current Investment**: Calculate the current investment amount by multiplying the old average price by the old quantity.
|
| 273 |
+
2. **Additional Investment**: Calculate the additional investment amount by multiplying the new price by the new quantity.
|
| 274 |
+
3. **Total Investment and Total Quantity**: Add the current investment amount and additional investment amount to get the total investment. Sum the old quantity and new quantity to get the total quantity of stocks.
|
| 275 |
+
4. **New Average Price**: Calculate the new average price by dividing the total investment by the total quantity of stocks.
|
| 276 |
+
5. **Return Calculation**: Calculate the old and new returns to analyze the investment results.
|
| 277 |
+
|
| 278 |
+
### Results ๐ฏ
|
| 279 |
+
|
| 280 |
+
**1) Old Return**
|
| 281 |
+
|
| 282 |
+
Displays the return based on the old average price.
|
| 283 |
+
|
| 284 |
+
**2) New Return**
|
| 285 |
+
|
| 286 |
+
Displays the return based on the new average price.
|
| 287 |
+
|
| 288 |
+
**3) Additional Investment**
|
| 289 |
+
|
| 290 |
+
Shows the amount of additional investment made.
|
| 291 |
+
|
| 292 |
+
**4) Average Price**
|
| 293 |
+
|
| 294 |
+
Displays the newly calculated average price.
|
| 295 |
+
|
| 296 |
+
**5) Total Quantity**
|
| 297 |
+
|
| 298 |
+
Shows the total quantity of stocks.
|
| 299 |
+
|
| 300 |
+
**6) Total Investment**
|
| 301 |
+
|
| 302 |
+
Displays the total investment amount.
|
| 303 |
+
|
| 304 |
+
### Example ๐
|
| 305 |
+
|
| 306 |
+
- **Old Average Price**: 100
|
| 307 |
+
- **Old Quantity**: 10
|
| 308 |
+
- **New Price**: 120
|
| 309 |
+
- **New Quantity**: 5
|
| 310 |
+
|
| 311 |
+
Results:
|
| 312 |
+
- New Average Price: 105
|
| 313 |
+
- Total Quantity: 15
|
| 314 |
+
- Total Investment: 1,575
|
| 315 |
+
- Old Return: 20.00%
|
| 316 |
+
- New Return: 14.29%
|
| 317 |
+
- Additional Investment: 600
|
| 318 |
+
|
| 319 |
+
Use the Dollar-Cost Averaging Calculator to easily compute the average purchase price of your investments! ๐๐ฐ
|
| 320 |
+
|
| 321 |
+
### Disclaimer
|
| 322 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 323 |
"""
|
| 324 |
)
|
| 325 |
|
| 326 |
# Retirement Planning Tool Section
|
| 327 |
create_tool_section(
|
| 328 |
+
"RETIREMENT PLANNING CALCULATOR",
|
| 329 |
"""
|
| 330 |
+
## ๐๏ธ ์ํด ๊ณํ ๊ณ์ฐ๊ธฐ ์ฌ์ฉ๋ฒ
|
| 331 |
+
|
| 332 |
+
์๋
ํ์ธ์! ๐ ์ํด ๊ณํ ๊ณ์ฐ๊ธฐ์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค. ์ด ๋๊ตฌ๋ ์ฌ๋ฌ๋ถ์ ์ํด ํ ์ฌ์ ์ํฉ์ ์์ธกํ๊ณ ์ค๋นํ๋ ๋ฐ ๋์์ ์ค ๊ฑฐ์์. ์๋๋ ์ด ๊ณ์ฐ๊ธฐ์ ์๋ ๋ฐฉ์๊ณผ ํ์ํ ์ ๋ณด์
๋๋ค.
|
| 333 |
+
|
| 334 |
+
### 1. ์
๋ ฅ ๊ฐ ๐
|
| 335 |
+
|
| 336 |
+
๊ณ์ฐ๊ธฐ์์ ๋ค์ ์ ๋ณด๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์:
|
| 337 |
+
|
| 338 |
+
- **ํ์ฌ ๋์ด (current_age)**: ํ์ฌ ๋์ด ๐
|
| 339 |
+
- **์ํด ๋์ด (retirement_age)**: ์ํดํ๊ณ ์ถ์ ๋์ด ๐
|
| 340 |
+
- **ํ์ ๊ธฐ๋ ์๋ช
(life_expectancy)**: ์์๋๋ ํ์ ์๋ช
๐
|
| 341 |
+
- **์๋ณ ํ์ํ ์ํ๋น (monthly_income_required)**: ๋งค์ ํ์ํ ์ํ๋น ๐ต
|
| 342 |
+
- **์ธํ๋ ์ด์
์จ (inflation_rate)**: ๋ฌผ๊ฐ ์์น๋ฅ ๐
|
| 343 |
+
- **ํ์ฌ ํฌ์์ก (current_investment)**: ํ์ฌ ๋ณด์ ํ ํฌ์์ก ๐ฐ
|
| 344 |
+
- **์๋ณ ์ถ๊ฐ ํฌ์์ก (monthly_investment)**: ๋งค์ ์ถ๊ฐ๋ก ํฌ์ํ ๊ธ์ก ๐ธ
|
| 345 |
+
- **์ ํฌ์์ก์ ์ฐ๊ฐ ์ฆ๊ฐ์ก (annual_increase_in_monthly_investment)**: ๋งค์ ์ฆ๊ฐํ ํฌ์์ก(๋งค๋
์ ์ ๋ฆฝ์ ํฌ์๊ธ์์ ๋๋ ค๋๊ฐ ๊ธ์ก. (ex: ๋งค๋
10๋ง์์ฉ ๋๋ฆด ์์ ์ด๋ผ๋ฉด, 100,000 ์
๋ ฅ)) ๐
|
| 346 |
+
- **๋ฐฐ๋น๊ธ ์ฌํฌ์ ์ฌ๋ถ (reinvest_dividends)**: ๋ฐฐ๋น๊ธ์ ์ฌํฌ์ํ ์ง ์ฌ๋ถ ๐
|
| 347 |
+
- **์ํด ์ ์์ต๋ฅ (pre_retirement_roi)**: ์ํด ์ ์์ ์ฐ ์์ต๋ฅ ๐
|
| 348 |
+
- **์ํด ์ ๋ฐฐ๋น ์์ต๋ฅ (pre_retirement_dividend_yield)**: ์ํด ์ ๋ฐฐ๋น๊ธ ์์ต๋ฅ ๐
|
| 349 |
+
- **์ํด ํ ์์ต๋ฅ (post_retirement_roi)**: ์ํด ํ ์์ ์ฐ ์์ต๋ฅ ๐ฟ
|
| 350 |
+
- **์ํด ํ ๋ฐฐ๋น ์์ต๋ฅ (post_retirement_dividend_yield)**: ์ํด ํ ๋ฐฐ๋น๊ธ ์์ต๋ฅ ๐ต
|
| 351 |
+
|
| 352 |
+
### 2. ๊ณ์ฐ ๊ณผ์ ๐ข
|
| 353 |
+
|
| 354 |
+
**1) ์ํด ์ ์์ฐ ๊ณ์ฐ**
|
| 355 |
+
|
| 356 |
+
ํ์ฌ ์์ฐ๊ณผ ๋งค์ ์ถ๊ฐ ํฌ์์ก์ ๊ณ ๋ คํด ์ํด ์ ์์ฐ์ ๊ณ์ฐํด์. ์๋ณ ์์ต๋ฅ ์ ์ ์ฉํ์ฌ ์์ฐ์ด ์ด๋ป๊ฒ ์ฑ์ฅํ ์ง ์์ธกํฉ๋๋ค.
|
| 357 |
+
|
| 358 |
+
**๊ณต์:**
|
| 359 |
+
\[ \text{์๊ฐ ์์ต๋ฅ } = \left(1 + \frac{\text{์ฐ ์์ต๋ฅ }}{100}\right)^{\frac{1}{12}} - 1 \]
|
| 360 |
+
|
| 361 |
+
**2) ์ํด ํ ์์ฐ ๋ฐ ๋ฐฐ๋น ์์ต ๊ณ์ฐ**
|
| 362 |
+
|
| 363 |
+
์ํด ์์ ์ ์์ฐ๊ณผ ๋ฐฐ๋น ์์ต์ ๊ธฐ๋กํ๊ณ , ์ํด ํ ๋งค๋
์์ฐ๊ณผ ๋ฐฐ๋น ์์ต์ ์
๋ฐ์ดํธํฉ๋๋ค. ๋งค๋
ํ์ํ ์ํ๋น๋ ๋ฌผ๊ฐ ์์น๋ฅ ์ ๋ง์ถฐ ์กฐ์ ํฉ๋๋ค.
|
| 364 |
+
|
| 365 |
+
**3) ์ถ๊ฐ ํ๊ธ ํ์๋ ๊ณ์ฐ**
|
| 366 |
+
|
| 367 |
+
๋ฐฐ๋น ์์ต๊ณผ ํ์ํ ์ํ๋น๋ฅผ ๋น๊ตํ์ฌ ์ถ๊ฐ๋ก ํ์ํ ํ๊ธ์ ๊ณ์ฐํด์.
|
| 368 |
+
|
| 369 |
+
**๊ณต์:**
|
| 370 |
+
\[ \text{์ฐ๊ฐ ํ์ํ ์ํ๋น} = \text{์๋ณ ํ์ํ ์ํ๋น} \times 12 \]
|
| 371 |
+
\[ \text{์ถ๊ฐ ํ๊ธ ํ์๋} = \text{์ฐ๊ฐ ํ์ํ ์ํ๋น} - \text{์ฐ๊ฐ ๋ฐฐ๋น ์์ต} \]
|
| 372 |
+
|
| 373 |
+
### 3. ๊ฒฐ๊ณผ ์ถ๋ ฅ ๐
|
| 374 |
+
|
| 375 |
+
๊ณ์ฐ๋ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ ํ์์ผ๋ก ์ ๊ณต๋ฉ๋๋ค:
|
| 376 |
+
|
| 377 |
+
- **์ํด ์งํ ํ์ํ ์ํ๋น**์ **๋ฐฐ๋น ์์ต**์ ํ๋์ ๋ณผ ์ ์์ด์.
|
| 378 |
+
- ์ํด ํ ๋งค๋
์์ฐ, ํ์ํ ์ํ๋น, ๋ฐฐ๋น ์์ต ๋ฑ์ ํ๋ก ํ์ธํ ์ ์์ต๋๋ค.
|
| 379 |
+
- ์ถ๊ฐ๋ก ํ์ํ ํ๊ธ์ ๊ณ์ฐํ์ฌ ์ด ํ์ ํ๊ธ์ ๋ณด์ฌ์ค๋๋ค.
|
| 380 |
+
|
| 381 |
+
### ์์ ๐
|
| 382 |
+
|
| 383 |
+
**์
๋ ฅ ๊ฐ ์์:**
|
| 384 |
+
|
| 385 |
+
- ํ์ฌ ๋์ด: 40์ธ
|
| 386 |
+
- ์ํด ๋์ด: 65์ธ
|
| 387 |
+
- ํ์ ๊ธฐ๋ ์๋ช
: 85์ธ
|
| 388 |
+
- ์๋ณ ํ์ํ ์ํ๋น: 2,000,000 ์
|
| 389 |
+
- ์ธํ๋ ์ด์
์จ: 2%
|
| 390 |
+
- ํ์ฌ ํฌ์์ก: 50,000,000 ์
|
| 391 |
+
- ์๋ณ ์ถ๊ฐ ํฌ์์ก: 500,000 ์
|
| 392 |
+
- ์๋ณ ํฌ์ ์ฆ๊ฐ์ก: 50,000 ์
|
| 393 |
+
- ๋ฐฐ๋น๊ธ ์ฌํฌ์ ์ฌ๋ถ: ์
|
| 394 |
+
- ์ํด ์ ์์ต๋ฅ : 5%
|
| 395 |
+
- ์ํด ์ ๋ฐฐ๋น ์์ต๋ฅ : 2%
|
| 396 |
+
- ์ํด ํ ์์ต๋ฅ : 4%
|
| 397 |
+
- ์ํด ํ ๋ฐฐ๋น ์์ต๋ฅ : 2%
|
| 398 |
+
|
| 399 |
+
**์ถ๋ ฅ ๊ฒฐ๊ณผ:**
|
| 400 |
+
|
| 401 |
+
- ์ํด ์งํ ์๋ณ ํ์ํ ์ํ๋น: 2,000,000 ์
|
| 402 |
+
- ์ํด ์งํ ์๋ณ ๋ฐฐ๋น ์์ต: 500,000 ์
|
| 403 |
+
- ์ด ์ถ๊ฐ ํ๊ธ ํ์๋: ๋งค๋
์ถ๊ฐ๋ก ํ์ํ ๊ธ์ก
|
| 404 |
+
|
| 405 |
+
**๊ทธ๋ํ์ CSV ํ์ผ ๐:**
|
| 406 |
+
|
| 407 |
+
- ์ํด ๊ณํ์ ์๊ฐ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ๊ทธ๋ํ์ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ํฌํจํ CSV ํ์ผ ๋ค์ด๋ก๋ ๋งํฌ๋ ์ ๊ณต๋ฉ๋๋ค.
|
| 408 |
+
|
| 409 |
+
์ํด ๊ณํ ๊ณ์ฐ๊ธฐ๋ฅผ ํตํด ์ฌ๋ฌ๋ถ์ ์ฌ์ ๊ณํ์ ๋ฏธ๋ฆฌ ์ค๋นํ๊ณ , ์์ ์ ์ธ ๋ฏธ๋๋ฅผ ๊ณํํด๋ณด์ธ์! ๐๐ผ
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
### ๋ฉด์ฑ
์กฐํญ
|
| 413 |
+
์ด ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ ๋ณด๋ ์ผ๋ฐ์ ์ธ ์ ๋ณด ์ ๊ณต ๋ชฉ์ ๋ง์ ์ํด ์ ๊ณต๋ฉ๋๋ค. ์ฌ์ดํธ์ ๋ชจ๋ ์ ๋ณด๋ ์ ์๋ก ์ ๊ณต๋์ง๋ง, ์ฌ์ดํธ์ ์ ๋ณด์ ์ ํ์ฑ, ์ ์ ์ฑ, ์ ํจ์ฑ, ์ ๋ขฐ์ฑ, ๊ฐ์ฉ์ฑ ๋๋ ์์ ์ฑ์ ๋ํด ๋ช
์์ ์ด๋ ๋ฌต์์ ์ด๋ ์ด๋ ํ ์ข
๋ฅ์ ์ง์ ์ด๋ ๋ณด์ฆ์ ํ์ง ์์ต๋๋ค. ์ฌ์ดํธ์ ์ฌ์ฉ๊ณผ ์ฌ์ดํธ์ ์ ๋ณด๋ฅผ ์ ๋ขฐํ๋ ๊ฒ์ ์ ์ ์ผ๋ก ์ฌ์ฉ์์ ์ฑ
์์
๋๋ค.
|
| 414 |
""",
|
| 415 |
"""
|
| 416 |
+
## ๐๏ธ Retirement Planning Calculator Guide
|
| 417 |
+
|
| 418 |
+
Welcome to the Retirement Planning Calculator! ๐ This tool helps you forecast and prepare for your financial situation after retirement. Hereโs how it works and what information you need to provide:
|
| 419 |
+
|
| 420 |
+
### 1. Input Values ๐
|
| 421 |
+
|
| 422 |
+
Please provide the following details:
|
| 423 |
+
|
| 424 |
+
- **Current Age (current_age)**: Your current age ๐
|
| 425 |
+
- **Retirement Age (retirement_age)**: Age at which you plan to retire ๐
|
| 426 |
+
- **Life Expectancy (life_expectancy)**: Expected lifespan ๐
|
| 427 |
+
- **Monthly Income Required (monthly_income_required)**: Monthly expenses needed ๐ต
|
| 428 |
+
- **Inflation Rate (inflation_rate)**: Rate of inflation ๐
|
| 429 |
+
- **Current Investment (current_investment)**: Amount currently invested ๐ฐ
|
| 430 |
+
- **Monthly Investment (monthly_investment)**: Additional monthly investment ๐ธ
|
| 431 |
+
- **Annual Increase in Monthly Investment (annual_increase_in_monthly_investment)**: Increase in monthly investment (For example, if you plan to increase your monthly investment by $100,000 each year, enter 100,000.) ๐
|
| 432 |
+
- **Reinvest Dividends (reinvest_dividends)**: Whether to reinvest dividends ๐
|
| 433 |
+
- **Pre-Retirement ROI (pre_retirement_roi)**: Expected annual return before retirement ๐
|
| 434 |
+
- **Pre-Retirement Dividend Yield (pre_retirement_dividend_yield)**: Dividend yield before retirement ๐
|
| 435 |
+
- **Post-Retirement ROI (post_retirement_roi)**: Expected annual return after retirement ๐ฟ
|
| 436 |
+
- **Post-Retirement Dividend Yield (post_retirement_dividend_yield)**: Dividend yield after retirement ๐ต
|
| 437 |
+
|
| 438 |
+
### 2. Calculation Process ๐ข
|
| 439 |
+
|
| 440 |
+
**1) Pre-Retirement Asset Calculation**
|
| 441 |
+
|
| 442 |
+
The calculator uses your current assets and monthly contributions to forecast your asset growth before retirement. Monthly returns are applied to estimate how your assets will grow.
|
| 443 |
+
|
| 444 |
+
**Formula:**
|
| 445 |
+
\[ \text{Monthly Return} = \left(1 + \frac{\text{Annual ROI}}{100}\right)^{\frac{1}{12}} - 1 \]
|
| 446 |
+
|
| 447 |
+
**2) Post-Retirement Asset and Dividend Income Calculation**
|
| 448 |
+
|
| 449 |
+
The calculator tracks your assets and dividend income at retirement, then updates these figures annually. It also adjusts monthly income needs for inflation.
|
| 450 |
+
|
| 451 |
+
**3) Additional Cash Needed Calculation**
|
| 452 |
+
|
| 453 |
+
It compares dividend income with required income to determine if additional cash is needed.
|
| 454 |
+
|
| 455 |
+
**Formula:**
|
| 456 |
+
\[ \text{Annual Income Required} = \text{Monthly Income Required} \times 12 \]
|
| 457 |
+
\[ \text{Additional Cash Required} = \text{Annual Income Required} - \text{Annual Dividend Income} \]
|
| 458 |
+
|
| 459 |
+
### 3. Results ๐
|
| 460 |
+
|
| 461 |
+
The results are displayed as follows:
|
| 462 |
+
|
| 463 |
+
- **Immediate Post-Retirement Income Needs** and **Dividend Income** for easy comparison.
|
| 464 |
+
- Annual asset, required income, and dividend income details in a table.
|
| 465 |
+
- Total additional cash required to cover any shortfall.
|
| 466 |
+
|
| 467 |
+
### Example ๐
|
| 468 |
+
|
| 469 |
+
**Example Input Values:**
|
| 470 |
+
|
| 471 |
+
- Current Age: 40 years
|
| 472 |
+
- Retirement Age: 65 years
|
| 473 |
+
- Life Expectancy: 85 years
|
| 474 |
+
- Monthly Income Required: $2,000
|
| 475 |
+
- Inflation Rate: 2%
|
| 476 |
+
- Current Investment: $50,000
|
| 477 |
+
- Monthly Investment: $500
|
| 478 |
+
- Monthly Increase in Investment: $50
|
| 479 |
+
- Reinvest Dividends: Yes
|
| 480 |
+
- Pre-Retirement ROI: 5%
|
| 481 |
+
- Pre-Retirement Dividend Yield: 2%
|
| 482 |
+
- Post-Retirement ROI: 4%
|
| 483 |
+
- Post-Retirement Dividend Yield: 2%
|
| 484 |
+
|
| 485 |
+
**Output Results:**
|
| 486 |
+
|
| 487 |
+
- Monthly Income Required Immediately After Retirement: $2,000
|
| 488 |
+
- Monthly Dividend Income Immediately After Retirement: $500
|
| 489 |
+
- Total Additional Cash Required: Amount needed annually
|
| 490 |
+
|
| 491 |
+
**Graph and CSV File ๐:**
|
| 492 |
+
|
| 493 |
+
- Visual graphs of the retirement plan and a CSV file with detailed calculations are available for download.
|
| 494 |
+
|
| 495 |
+
Use the Retirement Planning Calculator to prepare for your future and ensure a secure retirement! ๐๐ผ
|
| 496 |
+
|
| 497 |
+
### Disclaimer
|
| 498 |
The information provided by this tool is for general informational purposes only. All information on the site is provided in good faith, but we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information on the site. Your use of the site and reliance on any information on the site is solely at your own risk.
|
| 499 |
"""
|
| 500 |
)
|
| 501 |
+
with gr.Accordion("๐ค Support Us", open=False):
|
| 502 |
+
# Support Us Section
|
| 503 |
+
gr.Markdown("""
|
| 504 |
+
If you find this tool useful and would like to support the development of such projects, please consider making a donation. Your support is greatly appreciated.
|
| 505 |
|
| 506 |
+
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M8SBRC396DPBW)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
|
| 508 |
+
Or, if you prefer, you can also support us through Toss at:
|
| 509 |
|
| 510 |
+
<a href="https://toss.me/eichijei" target="_blank">
|
| 511 |
+
<img src="https://static.toss.im/logos/png/1x/logo-toss.png" alt="Donate with Toss" style="width: 150px;">
|
| 512 |
+
</a>
|
| 513 |
+
""")
|
| 514 |
|
interface/dollar_cost_averaging_interface.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from modules.dollar_cost_averaging import gradio_dollar_cost_averaging, load_css
|
| 3 |
+
|
| 4 |
+
def dollar_cost_averaging_interface_fn(old_avg_price, old_quantity, new_price, new_quantity):
|
| 5 |
+
result = gradio_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity)
|
| 6 |
+
css = load_css()
|
| 7 |
+
return css + result
|
| 8 |
+
|
| 9 |
+
# Define the inputs
|
| 10 |
+
markdown1 = gr.Markdown("### First Purchase")
|
| 11 |
+
old_price = gr.Textbox(label="Old Price", value="")
|
| 12 |
+
old_quantity = gr.Textbox(label="Quantity", value="")
|
| 13 |
+
markdown2 = gr.Markdown("### Second Purchase")
|
| 14 |
+
new_price = gr.Textbox(label="New Price", value="")
|
| 15 |
+
new_quantity = gr.Textbox(label="Quantity", value="")
|
| 16 |
+
|
| 17 |
+
dollar_cost_averaging_inputs = [old_price, old_quantity, new_price, new_quantity]
|
| 18 |
+
output = gr.HTML()
|
| 19 |
+
|
| 20 |
+
# Define the update function
|
| 21 |
+
def update_output(old_avg_price, old_quantity, new_price, new_quantity):
|
| 22 |
+
return dollar_cost_averaging_interface_fn(old_avg_price, old_quantity, new_price, new_quantity)
|
interface/rebalancing_interface.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from modules.rebalancing import rebalancing_tool, load_css
|
| 3 |
+
from modules.utils import get_currency_codes
|
| 4 |
+
|
| 5 |
+
def rebalancing_interface_fn(main_currency, holdings, cash_amount, cash_ratio):
|
| 6 |
+
result = rebalancing_tool(main_currency, holdings, cash_amount, cash_ratio)
|
| 7 |
+
css = load_css()
|
| 8 |
+
return css + result
|
| 9 |
+
|
| 10 |
+
currency_codes = get_currency_codes()
|
| 11 |
+
examples = [
|
| 12 |
+
["KRW", "458730 KRW 580 8,\n368590 KRW 80 2", 507977, 0],
|
| 13 |
+
["KRW", "SCHD USD 500 8,\nQQQ USD 200 2", 0, 15]
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
rebalancing_inputs = [
|
| 17 |
+
gr.Dropdown(label="Main Currency", choices=currency_codes, value="KRW", info="Assets converted to main currency."),
|
| 18 |
+
gr.Textbox(label="Holdings", lines=2, info="Format: [ ticker currency quantity weight, ... ]", placeholder="e.g., SCHD USD 500 8, QQQ USD 200 2"),
|
| 19 |
+
gr.Number(label="Cash", value="", info="MAIN CURRENCY CASH"),
|
| 20 |
+
gr.Slider(label="Cash Ratio (%)", minimum=0, maximum=100, step=1)
|
| 21 |
+
]
|
| 22 |
+
|
| 23 |
+
output = gr.HTML()
|
| 24 |
+
|
| 25 |
+
# Define the update function
|
| 26 |
+
def update_output(main_currency, holdings, cash_amount, cash_ratio):
|
| 27 |
+
return rebalancing_interface_fn(main_currency, holdings, cash_amount, cash_ratio)
|
| 28 |
+
|
| 29 |
+
# portfolio_rebalancing_interface = gr.Interface(
|
| 30 |
+
# fn=portfolio_rebalancing_interface_fn,
|
| 31 |
+
# inputs=portfolio_inputs,
|
| 32 |
+
# outputs=gr.HTML(),
|
| 33 |
+
# examples=examples,
|
| 34 |
+
# cache_examples=False,
|
| 35 |
+
# live=False
|
| 36 |
+
# )
|
| 37 |
+
|
interface/retirement_planning_interface.py
CHANGED
|
@@ -2,26 +2,36 @@ import gradio as gr
|
|
| 2 |
from modules.utils import load_css
|
| 3 |
from modules.retirement_planning import retirement_planning
|
| 4 |
|
| 5 |
-
def retirement_planning_interface_fn(
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
css = load_css()
|
| 8 |
return css + result
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
| 22 |
|
| 23 |
output = gr.HTML()
|
| 24 |
|
| 25 |
# Define the update function
|
| 26 |
-
def update_output(
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from modules.utils import load_css
|
| 3 |
from modules.retirement_planning import retirement_planning
|
| 4 |
|
| 5 |
+
def retirement_planning_interface_fn(
|
| 6 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 7 |
+
):
|
| 8 |
+
result = retirement_planning(
|
| 9 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 10 |
+
)
|
| 11 |
css = load_css()
|
| 12 |
return css + result
|
| 13 |
|
| 14 |
+
# Define the input components
|
| 15 |
+
current_age = gr.Slider(label="Current Age (15-60 Years)", value=15, minimum=15, maximum=60, step=1)
|
| 16 |
+
retirement_age = gr.Slider(label="Retirement Age (Upto 70 Years)", value=55, minimum=15, maximum=70, step=1)
|
| 17 |
+
life_expectancy = gr.Slider(label="Life Expectancy (Upto 100 Years)", value=80, minimum=30, maximum=100, step=1)
|
| 18 |
+
monthly_income_required = gr.Number(label="Monthly Income Required In Retirement Years (CPP)", value=2000000)
|
| 19 |
+
inflation_rate = gr.Number(label="Expected Inflation Rate (%)", value=3)
|
| 20 |
+
current_investment = gr.Number(label="Current Investment", value=10000000)
|
| 21 |
+
monthly_investment = gr.Number(label="Monthly Investment", value=500000)
|
| 22 |
+
annual_increase_in_monthly_investment = gr.Number(label="Annual Increase in Monthly Investment", value=0) # ์ถ๊ฐ๋ ์
๋ ฅ
|
| 23 |
+
reinvest_dividends = gr.Checkbox(label="Reinvest Dividends", value=True)
|
| 24 |
+
pre_retirement_roi = gr.Number(label="Expected Return On Investment (Pre-retirement) (%)", value=8)
|
| 25 |
+
pre_retirement_dividend_yield = gr.Number(label="Expected Dividend Yield (Pre-retirement) (%)", value=3.3)
|
| 26 |
+
post_retirement_roi = gr.Number(label="Expected Return On Investment (Post-retirement) (%)", value=8)
|
| 27 |
+
post_retirement_dividend_yield = gr.Number(label="Expected Dividend Yield (Post-retirement) (%)", value=3.3)
|
| 28 |
|
| 29 |
output = gr.HTML()
|
| 30 |
|
| 31 |
# Define the update function
|
| 32 |
+
def update_output(
|
| 33 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 34 |
+
):
|
| 35 |
+
return retirement_planning_interface_fn(
|
| 36 |
+
current_age, retirement_age, life_expectancy, monthly_income_required, inflation_rate, current_investment, monthly_investment, annual_increase_in_monthly_investment, reinvest_dividends, pre_retirement_roi, pre_retirement_dividend_yield, post_retirement_roi, post_retirement_dividend_yield
|
| 37 |
+
)
|
interface/share_price_trend_interface.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from modules.utils import load_css
|
| 3 |
+
from modules.share_price_trend import share_price_trend
|
| 4 |
+
|
| 5 |
+
# Define the interface for the Compare tab
|
| 6 |
+
def share_price_trend_interface_fn(stock_codes, period):
|
| 7 |
+
result = share_price_trend(stock_codes, period)
|
| 8 |
+
css = load_css()
|
| 9 |
+
return css + result
|
| 10 |
+
|
| 11 |
+
share_price_trend_inputs = [
|
| 12 |
+
gr.Textbox(label="Stock Codes", info="Enter stock codes separated by comma.", placeholder="e.g., AAPL,GOOGL,MSFT", value="SCHD,QQQ"),
|
| 13 |
+
gr.Number(label="input # of days below", value="90")
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
output=gr.HTML()
|
| 17 |
+
|
| 18 |
+
# Define the update function
|
| 19 |
+
def update_output(stock_codes, period):
|
| 20 |
+
return share_price_trend_interface_fn(stock_codes, period)
|
| 21 |
+
|
| 22 |
+
# compare_stock_prices_interface = gr.Interface(
|
| 23 |
+
# fn=compare_stock_prices_interface_fn,
|
| 24 |
+
# inputs=compare_inputs,
|
| 25 |
+
# outputs=gr.HTML(),
|
| 26 |
+
# live=False
|
| 27 |
+
# )
|
modules/.DS_Store
ADDED
|
Binary file (6.15 kB). View file
|
|
|
modules/dollar_cost_averaging.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from modules.utils import load_css
|
| 2 |
+
|
| 3 |
+
def dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity):
|
| 4 |
+
# ์
๋ ฅ๊ฐ์ ์ซ์๋ก ๋ณํ (๊ฐ ์
๋ ฅ๊ฐ์ด None์ผ ๊ฒฝ์ฐ 0.0์ผ๋ก ์ค์ )
|
| 5 |
+
old_avg_price = float(old_avg_price) if old_avg_price else 0.0
|
| 6 |
+
old_quantity = float(old_quantity) if old_quantity else 0.0
|
| 7 |
+
new_price = float(new_price) if new_price else 0.0
|
| 8 |
+
new_quantity = float(new_quantity) if new_quantity else 0.0
|
| 9 |
+
|
| 10 |
+
# ํ์ฌ ํฌ์ ๊ธ์ก ๊ณ์ฐ (์ด์ ํ๊ท ๊ฐ๊ฒฉ * ์ด์ ์๋)
|
| 11 |
+
current_investment = old_avg_price * old_quantity
|
| 12 |
+
# ์ถ๊ฐ ํฌ์ ๊ธ์ก ๊ณ์ฐ (์ ๊ฐ๊ฒฉ * ์ ์๋)
|
| 13 |
+
additional_investment = new_price * new_quantity
|
| 14 |
+
# ์ด ํฌ์ ๊ธ์ก ๊ณ์ฐ (ํ์ฌ ํฌ์ ๊ธ์ก + ์ถ๊ฐ ํฌ์ ๊ธ์ก)
|
| 15 |
+
total_investment = current_investment + additional_investment
|
| 16 |
+
# ์ด ์ฃผ์ ์ ๊ณ์ฐ (์ด์ ์๋ + ์ ์๋)
|
| 17 |
+
total_quantity = old_quantity + new_quantity
|
| 18 |
+
# ์ ํ๊ท ๊ฐ๊ฒฉ ๊ณ์ฐ (์ด ํฌ์ ๊ธ์ก / ์ด ์ฃผ์ ์)
|
| 19 |
+
new_avg_price = total_investment / total_quantity if total_quantity != 0 else 0.0
|
| 20 |
+
# ์ด์ ์์ต๋ฅ ๊ณ์ฐ (์ด์ ํ๊ท ๊ฐ๊ฒฉ์ ๊ธฐ์ค์ผ๋ก)
|
| 21 |
+
old_return = (new_price / old_avg_price - 1) * 100 if old_avg_price != 0 else 0.0
|
| 22 |
+
# ์๋ก์ด ์์ต๋ฅ ๊ณ์ฐ (์ ๊ฐ๊ฒฉ / ์ ํ๊ท ๊ฐ๊ฒฉ - 1) * 100
|
| 23 |
+
new_return = (new_price / new_avg_price - 1) * 100 if new_avg_price != 0 else 0.0
|
| 24 |
+
|
| 25 |
+
# ์ ํ๊ท ๊ฐ๊ฒฉ, ์ด ์๋, ์ด ํฌ์ ๊ธ์ก, ์๋ก์ด ์์ต๋ฅ , ์ถ๊ฐ ํฌ์ ๊ธ์ก, ์ด์ ์์ต๋ฅ ๋ฐํ
|
| 26 |
+
return new_avg_price, total_quantity, total_investment, new_return, additional_investment, old_return
|
| 27 |
+
|
| 28 |
+
def gradio_dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity):
|
| 29 |
+
css = load_css()
|
| 30 |
+
|
| 31 |
+
# ์
๋ ฅ๊ฐ์ ์ซ์๋ก ๋ณํ
|
| 32 |
+
old_avg_price = float(old_avg_price) if old_avg_price else 0.0
|
| 33 |
+
old_quantity = float(old_quantity) if old_quantity else 0.0
|
| 34 |
+
new_price = float(new_price) if new_price else 0.0
|
| 35 |
+
new_quantity = float(new_quantity) if new_quantity else 0.0
|
| 36 |
+
|
| 37 |
+
# ํ๊ท ๊ฐ๊ฒฉ, ์ด ์๋, ์ด ํฌ์ ๊ธ์ก, ์์ต๋ฅ ๋ฐ ์ถ๊ฐ ํฌ์ ๊ธ์ก ๊ณ์ฐ
|
| 38 |
+
new_avg_price, total_quantity, total_investment, new_return, additional_investment, old_return = dollar_cost_averaging(old_avg_price, old_quantity, new_price, new_quantity)
|
| 39 |
+
|
| 40 |
+
# ์์ต๋ฅ ์ ๋ฐ๋ฅธ ํด๋์ค ์ค์
|
| 41 |
+
new_return_class = ""
|
| 42 |
+
old_return_class = ""
|
| 43 |
+
if new_return > 0:
|
| 44 |
+
new_return_class = f"<span style='color: #4caf50; font-weight: bold;'>{new_return:+,.2f}%</span>"
|
| 45 |
+
elif new_return < 0:
|
| 46 |
+
new_return_class = f"<span style='color: #f44336; font-weight: bold;'>{new_return:,.2f}%</span>"
|
| 47 |
+
else:
|
| 48 |
+
new_return_class = f"<span><strong>0</strong></span>"
|
| 49 |
+
|
| 50 |
+
if old_return > 0:
|
| 51 |
+
old_return_class = f"<span style='color: #4caf50; font-weight: bold;'>{old_return:+,.2f}%</span>"
|
| 52 |
+
elif old_return < 0:
|
| 53 |
+
old_return_class = f"<span style='color: #f44336; font-weight: bold;'>{old_return:,.2f}%</span>"
|
| 54 |
+
else:
|
| 55 |
+
old_return_class = f"<span><strong>0</strong></span>"
|
| 56 |
+
|
| 57 |
+
# HTML ๊ฒฐ๊ณผ ์์ฑ
|
| 58 |
+
result_html = css + f"""
|
| 59 |
+
<div class="wrap-text" style="box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); border-radius: 0.5rem; padding: 3rem; position: relative; width: 100%; padding: 1.5rem;">
|
| 60 |
+
<div>
|
| 61 |
+
<div style="margin-bottom: 1.5rem;">
|
| 62 |
+
<!-- ์ด์ ์์ต๋ฅ ํ์ -->
|
| 63 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Old Return</div>
|
| 64 |
+
<div style="font-size: 1.5rem;">
|
| 65 |
+
{old_return_class}
|
| 66 |
+
</div>
|
| 67 |
+
<hr style="margin: 1.5rem 0;">
|
| 68 |
+
</div>
|
| 69 |
+
</div>
|
| 70 |
+
<div>
|
| 71 |
+
<div style="margin-bottom: 1.5rem;">
|
| 72 |
+
<!-- ์๋ก์ด ์์ต๋ฅ ํ์ -->
|
| 73 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">New Return</div>
|
| 74 |
+
<div style="font-size: 1.5rem;">
|
| 75 |
+
{new_return_class}
|
| 76 |
+
</div>
|
| 77 |
+
<hr style="margin: 1.5rem 0;">
|
| 78 |
+
</div>
|
| 79 |
+
</div>
|
| 80 |
+
<div>
|
| 81 |
+
<div style="margin-bottom: 1.5rem;">
|
| 82 |
+
<!-- ์ถ๊ฐ ํฌ์ ๊ธ์ก ํ์ -->
|
| 83 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Additional Investment</div>
|
| 84 |
+
<div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
|
| 85 |
+
<span style='color: #1678fb'>{additional_investment:,.0f}</span>
|
| 86 |
+
</div>
|
| 87 |
+
<hr style="margin: 1.5rem 0;">
|
| 88 |
+
</div>
|
| 89 |
+
</div>
|
| 90 |
+
<div>
|
| 91 |
+
<div style="margin-bottom: 1.5rem;">
|
| 92 |
+
<!-- ํ๊ท ๊ฐ๊ฒฉ ํ์ -->
|
| 93 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Average Price</div>
|
| 94 |
+
<div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
|
| 95 |
+
<span style='color: #1678fb'>{new_avg_price:,.0f}</span>
|
| 96 |
+
</div>
|
| 97 |
+
<hr style="margin: 1.5rem 0;">
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
<div>
|
| 101 |
+
<div style="margin-bottom: 1.5rem;">
|
| 102 |
+
<!-- ์ด ์๋ ํ์ -->
|
| 103 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Total Quantity</div>
|
| 104 |
+
<div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
|
| 105 |
+
<span style='color: #1678fb'>{total_quantity:,.0f}</span>
|
| 106 |
+
</div>
|
| 107 |
+
<hr style="margin: 1.5rem 0;">
|
| 108 |
+
</div>
|
| 109 |
+
</div>
|
| 110 |
+
<div>
|
| 111 |
+
<div style="margin-bottom: 1.5rem;">
|
| 112 |
+
<!-- ์ด ํฌ์ ๊ธ์ก ํ์ -->
|
| 113 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Total Investment</div>
|
| 114 |
+
<div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
|
| 115 |
+
<span style='color: #1678fb'>{total_investment:,.0f}</span>
|
| 116 |
+
</div>
|
| 117 |
+
<hr style="margin: 1.5rem 0;">
|
| 118 |
+
</div>
|
| 119 |
+
</div>
|
| 120 |
+
</div>
|
| 121 |
+
"""
|
| 122 |
+
return result_html
|
modules/rebalancing.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import pytz
|
| 3 |
+
import math
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import FinanceDataReader as fdr
|
| 6 |
+
import yfinance as yf
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
from concurrent.futures import ThreadPoolExecutor
|
| 9 |
+
from modules.utils import load_css, get_currency_symbol, format_quantity
|
| 10 |
+
|
| 11 |
+
# ์ฃผ์ด์ง ์
๋ ฅ์ ๊ตฌ๋ฌธ ๋ถ์ํ์ฌ ์ฃผ์ ๋ณด์ ๋, ํ๊ธ ๊ธ์ก ๋ฐ ํ๊ธ ๋น์จ์ ๊ณ์ฐํ๋ ํจ์
|
| 12 |
+
def parse_input(holdings, cash_amount, cash_ratio):
|
| 13 |
+
lines = holdings.strip().split(',')
|
| 14 |
+
stock_inputs = []
|
| 15 |
+
total_target_weight = 0
|
| 16 |
+
|
| 17 |
+
for line in lines:
|
| 18 |
+
parts = line.split()
|
| 19 |
+
if len(parts) == 4:
|
| 20 |
+
stock_code, currency_code, quantity_expr, target_weight_expr = parts
|
| 21 |
+
quantity = math.floor(eval(quantity_expr.replace(' ', '')))
|
| 22 |
+
target_weight = eval(target_weight_expr.replace(' ', ''))
|
| 23 |
+
target_ratio = (1 - cash_ratio / 100) * target_weight
|
| 24 |
+
stock_inputs.append((currency_code, stock_code, quantity, target_weight, target_ratio))
|
| 25 |
+
total_target_weight += target_weight
|
| 26 |
+
|
| 27 |
+
cash_amount = math.floor(cash_amount) if cash_amount else 0
|
| 28 |
+
main_currency_cash_inputs = {'amount': cash_amount, 'target_weight': cash_ratio / 100.0}
|
| 29 |
+
|
| 30 |
+
stock_total_weight = total_target_weight
|
| 31 |
+
|
| 32 |
+
for i in range(len(stock_inputs)):
|
| 33 |
+
stock_inputs[i] = (stock_inputs[i][0], stock_inputs[i][1], stock_inputs[i][2], stock_inputs[i][3], (1 - main_currency_cash_inputs['target_weight']) * stock_inputs[i][3] / stock_total_weight)
|
| 34 |
+
|
| 35 |
+
return stock_inputs, main_currency_cash_inputs
|
| 36 |
+
|
| 37 |
+
# ์ฃผ์ด์ง ํตํ ์ฝ๋์ ๋ฉ์ธ ํตํ ๊ฐ์ ํ์จ์ ๊ฐ์ ธ์ค๋ ํจ์
|
| 38 |
+
def get_portfolio_exchange_rate(currency_code, main_currency):
|
| 39 |
+
if currency_code.lower() == main_currency.lower():
|
| 40 |
+
return 1.0
|
| 41 |
+
|
| 42 |
+
ticker = f"{currency_code.upper()}{main_currency.upper()}=X"
|
| 43 |
+
data = yf.download(ticker, period='1d', progress=False) # progress=False ์ถ๊ฐ
|
| 44 |
+
if not data.empty:
|
| 45 |
+
return data['Close'].iloc[0]
|
| 46 |
+
else:
|
| 47 |
+
raise ValueError("Failed to retrieve exchange rate data.")
|
| 48 |
+
|
| 49 |
+
# ์ฃผ์ด์ง ์ฃผ์ ์ฝ๋์ ํตํ ์ฝ๋์ ๋ํด ํ์จ์ ๋ฐ์ํ ์ฃผ์ ๊ฐ๊ฒฉ์ ๊ฐ์ ธ์ค๋ ํจ์
|
| 50 |
+
def get_portfolio_exchange_reflected_stock_price(stock_code, currency_code, main_currency):
|
| 51 |
+
new_price = get_portfolio_current_stock_price(stock_code)
|
| 52 |
+
exchange_rate = get_portfolio_exchange_rate(currency_code, main_currency)
|
| 53 |
+
return math.floor(new_price * exchange_rate)
|
| 54 |
+
|
| 55 |
+
# ์ฃผ์ด์ง ์ฃผ์ ์ฝ๋์ ํ์ฌ ์ฃผ์ ๊ฐ๊ฒฉ์ ๊ฐ์ ธ์ค๋ ํจ์
|
| 56 |
+
def get_portfolio_current_stock_price(stock_code):
|
| 57 |
+
df = fdr.DataReader(stock_code)
|
| 58 |
+
return df['Close'].iloc[-1]
|
| 59 |
+
|
| 60 |
+
# ์ฃผ์ด์ง ์ฃผ์ ์
๋ ฅ ๋ฐ ํ๊ธ ์
๋ ฅ์ ๋ฐํ์ผ๋ก ํฌํธํด๋ฆฌ์ค๋ฅผ ๊ตฌ์ถํ๋ ํจ์
|
| 61 |
+
def build_portfolio(stock_inputs, main_currency_cash_inputs, main_currency):
|
| 62 |
+
portfolio = {}
|
| 63 |
+
target_weights = {}
|
| 64 |
+
|
| 65 |
+
with ThreadPoolExecutor() as executor:
|
| 66 |
+
results = executor.map(lambda x: (x[1], get_portfolio_exchange_reflected_stock_price(x[1], x[0], main_currency), x[2], x[3], x[4], x[0]), stock_inputs)
|
| 67 |
+
|
| 68 |
+
for stock_code, new_price, quantity, target_weight, target_ratio, currency_code in results:
|
| 69 |
+
portfolio[stock_code] = {'quantity': quantity, 'price': new_price, 'target_weight': target_weight, 'currency': currency_code}
|
| 70 |
+
target_weights[stock_code] = target_ratio
|
| 71 |
+
|
| 72 |
+
return portfolio, target_weights, main_currency_cash_inputs
|
| 73 |
+
|
| 74 |
+
# ํฌํธํด๋ฆฌ์ค ์ฌ์กฐ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์
|
| 75 |
+
def get_portfolio_rebalancing_info(portfolio, target_weights, main_currency_cash_inputs, main_currency):
|
| 76 |
+
css = load_css()
|
| 77 |
+
|
| 78 |
+
current_time = datetime.now().strftime("%b-%d-%Y")
|
| 79 |
+
|
| 80 |
+
total_value = sum(stock['price'] * stock['quantity'] for stock in portfolio.values()) + main_currency_cash_inputs['amount']
|
| 81 |
+
total_new_stock_value = 0
|
| 82 |
+
total_trade_value = 0
|
| 83 |
+
adjustments = []
|
| 84 |
+
|
| 85 |
+
currency_symbol = get_currency_symbol(main_currency)
|
| 86 |
+
|
| 87 |
+
# ํฌํธํด๋ฆฌ์ค ์ ๋ณด ์์ฑ
|
| 88 |
+
portfolio_info = css + f"""
|
| 89 |
+
<div class="wrap-text" style="box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); border-radius: 0.5rem; padding: 3rem; position: relative; width: 100%; padding: 1.5rem;">
|
| 90 |
+
<div>
|
| 91 |
+
<div style="margin-bottom: 1.5rem;">
|
| 92 |
+
<!-- ์ ์ฒด ํ๊ฐ๊ธ์ก ํ์ -->
|
| 93 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Market Value</div>
|
| 94 |
+
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{currency_symbol}{total_value:,.0f}</span>
|
| 95 |
+
As of {current_time}
|
| 96 |
+
<hr style="margin: 1.5rem 0;">
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
# ํ์ฌ ๋น์จ ๋ฐ ๊ฐ์น๋ฅผ ๊ณ์ฐ
|
| 103 |
+
current_weights = {stock_code: (stock['price'] * stock['quantity'] / total_value) * 100 for stock_code, stock in portfolio.items()}
|
| 104 |
+
current_values = {stock_code: stock['price'] * stock['quantity'] for stock_code, stock in portfolio.items()}
|
| 105 |
+
|
| 106 |
+
# ํ๊ธ์ ํ์ฌ ๋น์จ ๋ฐ ๊ฐ์น์ ํฌํจ
|
| 107 |
+
current_weights['CASH'] = (main_currency_cash_inputs['amount'] / total_value) * 100
|
| 108 |
+
current_values['CASH'] = main_currency_cash_inputs['amount']
|
| 109 |
+
|
| 110 |
+
# ํ์ฌ ๋น์จ์ ๊ธฐ์ค์ผ๋ก ์ฃผ์์ ๋ด๋ฆผ์ฐจ์์ผ๋ก ์ ๋ ฌ
|
| 111 |
+
# sorted_stocks = sorted(current_weights.items(), key=lambda x: x[1], reverse=True)
|
| 112 |
+
sorted_stocks = current_weights.items()
|
| 113 |
+
|
| 114 |
+
# ํ์ฌ ๋ณด์ ๋ ๋ฐ ๊ฐ์น ์น์
ํ์
|
| 115 |
+
current_info_html = "<h3>Your Portfolio Holdings</h3><div class='table-container'><table>"
|
| 116 |
+
current_info_html += "<thead><tr><th>Stock Code</th><th>Current Weight (%)</th><th>Current Value</th></tr></thead><tbody>"
|
| 117 |
+
for stock_code, weight in sorted_stocks:
|
| 118 |
+
current_info_html += (
|
| 119 |
+
f"<tr>"
|
| 120 |
+
f"<td>{stock_code.upper()}</td>"
|
| 121 |
+
f"<td>{weight:.1f}%</td>"
|
| 122 |
+
f"<td>{currency_symbol}{current_values[stock_code]:,.0f}</td>"
|
| 123 |
+
f"</tr>"
|
| 124 |
+
)
|
| 125 |
+
current_info_html += "</tbody></table></div><br>"
|
| 126 |
+
|
| 127 |
+
# ์กฐ์ ํญ๋ชฉ ์์ฑ
|
| 128 |
+
for stock_code, stock_data in portfolio.items():
|
| 129 |
+
current_value = stock_data['price'] * stock_data['quantity']
|
| 130 |
+
target_value = total_value * target_weights.get(stock_code, 0)
|
| 131 |
+
difference = target_value - current_value
|
| 132 |
+
trade_quantity = math.floor(difference / stock_data['price']) if difference > 0 else -math.ceil(-difference / stock_data['price'])
|
| 133 |
+
new_quantity = trade_quantity + stock_data['quantity']
|
| 134 |
+
new_value = new_quantity * stock_data['price']
|
| 135 |
+
trade_value = trade_quantity * stock_data['price']
|
| 136 |
+
total_trade_value += abs(trade_value)
|
| 137 |
+
total_new_stock_value += new_value
|
| 138 |
+
current_value_pct = (current_value / total_value) * 100
|
| 139 |
+
new_value_pct = (new_value / total_value) * 100
|
| 140 |
+
|
| 141 |
+
adjustments.append((difference, current_value, target_value, current_value_pct, trade_quantity, stock_code, stock_data['price'], new_value, trade_value, stock_data['quantity'], new_quantity, target_weights[stock_code], new_value_pct, stock_data['target_weight'], stock_data['currency']))
|
| 142 |
+
|
| 143 |
+
# ํ๊ธ์ ๋ํ ์กฐ์ ํญ๋ชฉ ์์ฑ
|
| 144 |
+
main_currency_new_amount = total_value - total_new_stock_value
|
| 145 |
+
main_currency_target_value = total_value * main_currency_cash_inputs['target_weight']
|
| 146 |
+
main_currency_difference = main_currency_new_amount - main_currency_cash_inputs['amount']
|
| 147 |
+
trade_quantity = main_currency_difference
|
| 148 |
+
new_quantity = main_currency_cash_inputs['amount'] + trade_quantity
|
| 149 |
+
new_value = new_quantity
|
| 150 |
+
trade_value = trade_quantity
|
| 151 |
+
current_value = main_currency_cash_inputs['amount']
|
| 152 |
+
current_value_pct = (current_value / total_value) * 100
|
| 153 |
+
new_value_pct = (new_value / total_value) * 100
|
| 154 |
+
|
| 155 |
+
adjustments.append((main_currency_difference, current_value, main_currency_target_value, current_value_pct, trade_quantity, 'CASH', 1, new_value, trade_value, main_currency_cash_inputs['amount'], new_quantity, main_currency_cash_inputs['target_weight'], new_value_pct, '', 'main_currency'))
|
| 156 |
+
|
| 157 |
+
# ํตํ๋ณ ํฌํธํด๋ฆฌ์ค ์์ฝ ์์ฑ
|
| 158 |
+
currency_totals = {stock_data['currency']: {'amount': 0, 'weight': 0} for stock_data in portfolio.values()}
|
| 159 |
+
|
| 160 |
+
for stock_code, stock_data in portfolio.items():
|
| 161 |
+
currency = stock_data['currency']
|
| 162 |
+
current_value = stock_data['price'] * stock_data['quantity']
|
| 163 |
+
currency_totals[currency]['amount'] += current_value
|
| 164 |
+
currency_totals[currency]['weight'] += current_value / total_value
|
| 165 |
+
|
| 166 |
+
currency_totals['CASH'] = {'amount': main_currency_cash_inputs['amount'], 'weight': main_currency_cash_inputs['amount'] / total_value}
|
| 167 |
+
# sorted_currencies = sorted(currency_totals.items(), key=lambda x: x[1]['weight'], reverse=True)
|
| 168 |
+
sorted_currencies = currency_totals.items()
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# ํตํ๋ณ ์์ฝ ํ
์ด๋ธ ์์ฑ
|
| 172 |
+
currency_table = "<h3>Your Portfolio by Currency</h3><div class='table-container wrap-text'><table>"
|
| 173 |
+
currency_table += "<thead><tr><th>Currency</th><th>Total Weight (%)</th><th>Total Value</th></tr></thead><tbody>"
|
| 174 |
+
|
| 175 |
+
for currency, data in sorted_currencies:
|
| 176 |
+
currency_table += (
|
| 177 |
+
f"<tr>"
|
| 178 |
+
f"<td>{currency.upper()}</td>"
|
| 179 |
+
f"<td>{data['weight'] * 100:.1f}%</td>"
|
| 180 |
+
f"<td>{currency_symbol}{data['amount']:,}</td>"
|
| 181 |
+
f"</tr>"
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
currency_table += "</tbody></table></div><br>"
|
| 185 |
+
|
| 186 |
+
# ์ฌ์กฐ์ ๋ถ์ ํ
์ด๋ธ ์์ฑ
|
| 187 |
+
result_message = portfolio_info + current_info_html + currency_table + "<h3>Re-Balancing Analysis</h3><div class='table-container wrap-text'><table>"
|
| 188 |
+
result_message += "<thead><tr><th>Stock Code</th><th>Current Weight (%)</th><th>Target Weight</th><th>Target Ratio (%)</th><th>Buy or Sell?</th><th>Trade Amount</th><th>Current Price per Share</th><th>Estimated # of<br> Shares to Buy or Sell</th><th>Quantity of Units</th><th>Market Value</th><th>% Asset Allocation</th></tr></thead><tbody>"
|
| 189 |
+
|
| 190 |
+
for adj in adjustments:
|
| 191 |
+
difference, current_value, target_value, current_value_pct, trade_quantity, stock_code, price, new_value, trade_value, old_quantity, new_quantity, target_ratio, new_value_pct, target_weight, currency = adj
|
| 192 |
+
Buy_or_Sell = ""
|
| 193 |
+
if trade_quantity > 0:
|
| 194 |
+
Buy_or_Sell = f"<span class='buy-sell buy'>Buy</span>"
|
| 195 |
+
elif trade_quantity < 0:
|
| 196 |
+
Buy_or_Sell = f"<span class='buy-sell sell'>Sell</span>"
|
| 197 |
+
else:
|
| 198 |
+
Buy_or_Sell = f"<span></span>"
|
| 199 |
+
|
| 200 |
+
current_value_pct_str = f"{current_value_pct:.1f}%"
|
| 201 |
+
target_weight_str = f"<span class='highlight-edit'>{target_weight}</span>" if stock_code != 'CASH' else ''
|
| 202 |
+
target_ratio_str = f"<span class='highlight-edit'>{target_ratio * 100:.1f}%</span>" if stock_code == 'CASH' else f"{target_ratio * 100:.1f}%"
|
| 203 |
+
trade_value_str = f"<span class='highlight-sky'>{format_quantity(trade_value)}</span>" if trade_value != 0 else ''
|
| 204 |
+
price_str = f"{currency_symbol}{price:,.0f}" if stock_code != 'CASH' else ''
|
| 205 |
+
trade_quantity_str = (
|
| 206 |
+
f"<span class='highlight-sky'>{format_quantity(trade_quantity)}</span>"
|
| 207 |
+
if stock_code != 'CASH' and trade_value != 0 else ''
|
| 208 |
+
)
|
| 209 |
+
old_quantity_str = f"{old_quantity:,.0f} โ {new_quantity:,.0f}" if stock_code != 'CASH' else ''
|
| 210 |
+
new_value_str = f"{currency_symbol}{new_value:,.0f}"
|
| 211 |
+
new_value_pct_str = f"{new_value_pct:.1f}%"
|
| 212 |
+
|
| 213 |
+
result_message += (
|
| 214 |
+
f"<tr>"
|
| 215 |
+
f"<td>{stock_code.upper()}</td>"
|
| 216 |
+
f"<td>{current_value_pct_str}</td>"
|
| 217 |
+
f"<td>{target_weight_str}</td>"
|
| 218 |
+
f"<td>{target_ratio_str}</td>"
|
| 219 |
+
f"<td>{Buy_or_Sell}</td>"
|
| 220 |
+
f"<td>{trade_value_str}</td>"
|
| 221 |
+
f"<td>{price_str}</td>"
|
| 222 |
+
f"<td>{trade_quantity_str}</td>"
|
| 223 |
+
f"<td>{old_quantity_str}</td>"
|
| 224 |
+
f"<td>{new_value_str}</td>"
|
| 225 |
+
f"<td>{new_value_pct_str}</td>"
|
| 226 |
+
f"</tr>"
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
result_message += "</tbody></table></div>"
|
| 230 |
+
|
| 231 |
+
return result_message
|
| 232 |
+
|
| 233 |
+
# ํฌํธํด๋ฆฌ์ค ์ฌ์กฐ์ ๋๊ตฌ์ ์ฃผ์ ํจ์
|
| 234 |
+
def rebalancing_tool(main_currency, holdings, cash_amount, cash_ratio):
|
| 235 |
+
try:
|
| 236 |
+
stock_inputs, main_currency_cash_inputs = parse_input(holdings, cash_amount, cash_ratio)
|
| 237 |
+
portfolio, target_weights, main_currency_cash_inputs = build_portfolio(stock_inputs, main_currency_cash_inputs, main_currency)
|
| 238 |
+
result = get_portfolio_rebalancing_info(portfolio, target_weights, main_currency_cash_inputs, main_currency)
|
| 239 |
+
return result
|
| 240 |
+
except Exception as e:
|
| 241 |
+
return str(e)
|
modules/retirement_planning.py
CHANGED
|
@@ -1,18 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from modules.utils import load_css
|
| 2 |
|
| 3 |
-
def retirement_planning(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
# NoneType์ผ ๋ 0์ผ๋ก ์ฒ๋ฆฌ
|
| 5 |
current_age = current_age if current_age is not None else 0
|
| 6 |
retirement_age = retirement_age if retirement_age is not None else 0
|
| 7 |
current_investment = current_investment if current_investment is not None else 0
|
| 8 |
monthly_investment = monthly_investment if monthly_investment is not None else 0
|
|
|
|
| 9 |
pre_retirement_roi = pre_retirement_roi if pre_retirement_roi is not None else 0
|
| 10 |
post_retirement_roi = post_retirement_roi if post_retirement_roi is not None else 0
|
| 11 |
pre_retirement_dividend_yield = pre_retirement_dividend_yield if pre_retirement_dividend_yield is not None else 0
|
| 12 |
post_retirement_dividend_yield = post_retirement_dividend_yield if post_retirement_dividend_yield is not None else 0
|
| 13 |
life_expectancy = life_expectancy if life_expectancy is not None else 0
|
|
|
|
|
|
|
| 14 |
|
| 15 |
# ์ํด ์ ํ์ ๋
์ ๊ณ์ฐ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
years_to_retirement = retirement_age - current_age
|
| 17 |
post_retirement_years = life_expectancy - retirement_age
|
| 18 |
|
|
@@ -30,6 +59,8 @@ def retirement_planning(current_age=None, retirement_age=None, current_investmen
|
|
| 30 |
# ๋ฐฐ๋น๊ธ์ ์ฌํฌ์ํ ๊ฒฝ์ฐ ๋ฐฐ๋น๊ธ ์ถ๊ฐ
|
| 31 |
if reinvest_dividends:
|
| 32 |
total_investment += total_investment * (pre_retirement_dividend_yield / 100 / 12)
|
|
|
|
|
|
|
| 33 |
|
| 34 |
# ์ํด ์์ ์์ ์ ์ด ํฌ์์ก๊ณผ ์ฐ๊ฐ ๋ฐฐ๋น ์์ต ์ ์ฅ
|
| 35 |
investment_at_retirement = total_investment
|
|
@@ -39,8 +70,19 @@ def retirement_planning(current_age=None, retirement_age=None, current_investmen
|
|
| 39 |
# ์ํด ํ ์๊ฐ ์ด์์จ ๊ณ์ฐ
|
| 40 |
monthly_return_post = (1 + post_retirement_roi / 100) ** (1 / 12) - 1
|
| 41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
# ์ํด ํ ํฌ์ ๋ชฉ๋ก ์ด๊ธฐํ
|
| 43 |
-
post_retirement_investments = [(retirement_age, investment_at_retirement, annual_dividend_at_retirement, monthly_dividend_at_retirement)]
|
| 44 |
|
| 45 |
# ์ํด ํ ๊ฐ ๋
๋์ ํฌ์ ๋ฐ ๋ฐฐ๋น ์์ต ๊ณ์ฐ
|
| 46 |
for year in range(1, post_retirement_years + 1):
|
|
@@ -50,60 +92,117 @@ def retirement_planning(current_age=None, retirement_age=None, current_investmen
|
|
| 50 |
annual_dividend_income = total_investment * (post_retirement_dividend_yield / 100)
|
| 51 |
# ์๊ฐ ๋ฐฐ๋น ์์ต ๊ณ์ฐ
|
| 52 |
monthly_dividend_income = annual_dividend_income / 12
|
| 53 |
-
#
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
# style.css์์ CSS ์ฝ๊ธฐ
|
| 57 |
css = load_css()
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
# ์ํด ๊ณํ์ ๋ํ HTML ๊ฒฐ๊ณผ ์์ฑ
|
| 60 |
result_html = css + f"""
|
| 61 |
<div class="wrap-text" style="box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); border-radius: 0.5rem; padding: 3rem; position: relative; width: 100%; padding: 1.5rem;">
|
| 62 |
<div>
|
| 63 |
<div style="margin-bottom: 1.5rem;">
|
| 64 |
-
|
| 65 |
-
<
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
<hr style="margin: 1.5rem 0;">
|
| 70 |
</div>
|
| 71 |
-
</div>
|
| 72 |
-
<div>
|
| 73 |
<div style="margin-bottom: 1.5rem;">
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{annual_dividend_at_retirement:,.0f}</span>
|
| 77 |
Annual
|
| 78 |
-
<
|
| 79 |
-
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{monthly_dividend_at_retirement:,.0f}</span>
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
<hr style="margin: 1.5rem 0;">
|
| 82 |
</div>
|
|
|
|
| 83 |
</div>
|
| 84 |
</div>
|
| 85 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
<div class='table-container'>
|
| 87 |
<table>
|
| 88 |
<thead>
|
| 89 |
<tr>
|
| 90 |
<th>Age</th>
|
| 91 |
<th>SAVINGS</th>
|
| 92 |
-
<th>Annual</th>
|
| 93 |
-
<th>
|
|
|
|
|
|
|
|
|
|
| 94 |
</tr>
|
| 95 |
</thead>
|
| 96 |
<tbody>
|
| 97 |
"""
|
| 98 |
-
|
| 99 |
-
# ๊ฐ ์ฐ๋๋ณ ํฌ์์ ๋ฐฐ๋น
|
| 100 |
-
for age, investment, annual_dividend_income, monthly_dividend_income in post_retirement_investments:
|
|
|
|
| 101 |
result_html += f"""
|
| 102 |
<tr>
|
| 103 |
<td>{age}</td>
|
| 104 |
<td>{investment:,.0f}</td>
|
|
|
|
| 105 |
<td>{annual_dividend_income:,.0f}</td>
|
|
|
|
| 106 |
<td>{monthly_dividend_income:,.0f}</td>
|
|
|
|
| 107 |
</tr>
|
| 108 |
"""
|
| 109 |
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import csv
|
| 3 |
+
from io import StringIO
|
| 4 |
+
import matplotlib.pyplot as plt
|
| 5 |
+
import io
|
| 6 |
+
|
| 7 |
from modules.utils import load_css
|
| 8 |
|
| 9 |
+
def retirement_planning(
|
| 10 |
+
current_age=None,
|
| 11 |
+
retirement_age=None,
|
| 12 |
+
life_expectancy=None,
|
| 13 |
+
monthly_income_required=None,
|
| 14 |
+
inflation_rate=None,
|
| 15 |
+
current_investment=None,
|
| 16 |
+
monthly_investment=None,
|
| 17 |
+
annual_increase_in_monthly_investment=None, # ์ถ๊ฐ๋ ์
๋ ฅ
|
| 18 |
+
reinvest_dividends=None,
|
| 19 |
+
pre_retirement_roi=None,
|
| 20 |
+
pre_retirement_dividend_yield=None,
|
| 21 |
+
post_retirement_roi=None,
|
| 22 |
+
post_retirement_dividend_yield=None
|
| 23 |
+
):
|
| 24 |
# NoneType์ผ ๋ 0์ผ๋ก ์ฒ๋ฆฌ
|
| 25 |
current_age = current_age if current_age is not None else 0
|
| 26 |
retirement_age = retirement_age if retirement_age is not None else 0
|
| 27 |
current_investment = current_investment if current_investment is not None else 0
|
| 28 |
monthly_investment = monthly_investment if monthly_investment is not None else 0
|
| 29 |
+
annual_increase_in_monthly_investment = annual_increase_in_monthly_investment if annual_increase_in_monthly_investment is not None else 0
|
| 30 |
pre_retirement_roi = pre_retirement_roi if pre_retirement_roi is not None else 0
|
| 31 |
post_retirement_roi = post_retirement_roi if post_retirement_roi is not None else 0
|
| 32 |
pre_retirement_dividend_yield = pre_retirement_dividend_yield if pre_retirement_dividend_yield is not None else 0
|
| 33 |
post_retirement_dividend_yield = post_retirement_dividend_yield if post_retirement_dividend_yield is not None else 0
|
| 34 |
life_expectancy = life_expectancy if life_expectancy is not None else 0
|
| 35 |
+
monthly_income_required = monthly_income_required if monthly_income_required is not None else 0
|
| 36 |
+
inflation_rate = inflation_rate if inflation_rate is not None else 0
|
| 37 |
|
| 38 |
# ์ํด ์ ํ์ ๋
์ ๊ณ์ฐ
|
| 39 |
+
if retirement_age > life_expectancy:
|
| 40 |
+
return "<p style='color: red;'>Error: Retirement age cannot be greater than life expectancy.</p>"
|
| 41 |
+
|
| 42 |
+
if retirement_age < current_age:
|
| 43 |
+
return "<p style='color: red;'>Error: Retirement age cannot be less than current age.</p>"
|
| 44 |
+
|
| 45 |
years_to_retirement = retirement_age - current_age
|
| 46 |
post_retirement_years = life_expectancy - retirement_age
|
| 47 |
|
|
|
|
| 59 |
# ๋ฐฐ๋น๊ธ์ ์ฌํฌ์ํ ๊ฒฝ์ฐ ๋ฐฐ๋น๊ธ ์ถ๊ฐ
|
| 60 |
if reinvest_dividends:
|
| 61 |
total_investment += total_investment * (pre_retirement_dividend_yield / 100 / 12)
|
| 62 |
+
# ์ฐ๊ฐ ์ฆ๊ฐ์ก์ ์ ํฌ์์ก์ ์ถ๊ฐ
|
| 63 |
+
monthly_investment += annual_increase_in_monthly_investment
|
| 64 |
|
| 65 |
# ์ํด ์์ ์์ ์ ์ด ํฌ์์ก๊ณผ ์ฐ๊ฐ ๋ฐฐ๋น ์์ต ์ ์ฅ
|
| 66 |
investment_at_retirement = total_investment
|
|
|
|
| 70 |
# ์ํด ํ ์๊ฐ ์ด์์จ ๊ณ์ฐ
|
| 71 |
monthly_return_post = (1 + post_retirement_roi / 100) ** (1 / 12) - 1
|
| 72 |
|
| 73 |
+
# ์ฐ๊ฐ ๋ฌผ๊ฐ์์น๋ฅ ์ ๋ฐ์ํ ์ ์ํ๋น ๊ณ์ฐ
|
| 74 |
+
monthly_income_required_inflated = monthly_income_required
|
| 75 |
+
monthly_income_required_over_time = []
|
| 76 |
+
for age in range(current_age, life_expectancy + 1):
|
| 77 |
+
if age >= retirement_age:
|
| 78 |
+
monthly_income_required_over_time.append((age, monthly_income_required_inflated))
|
| 79 |
+
monthly_income_required_inflated *= (1 + inflation_rate / 100 / 12) ** 12
|
| 80 |
+
|
| 81 |
+
annual_income_required_at_retirement = monthly_income_required_over_time[0][1] * 12
|
| 82 |
+
monthly_income_required_at_retirement = monthly_income_required_over_time[0][1]
|
| 83 |
+
|
| 84 |
# ์ํด ํ ํฌ์ ๋ชฉ๋ก ์ด๊ธฐํ
|
| 85 |
+
post_retirement_investments = [(retirement_age, investment_at_retirement, annual_income_required_at_retirement, annual_dividend_at_retirement, monthly_dividend_at_retirement, monthly_income_required_at_retirement, annual_dividend_at_retirement - annual_income_required_at_retirement)]
|
| 86 |
|
| 87 |
# ์ํด ํ ๊ฐ ๋
๋์ ํฌ์ ๋ฐ ๋ฐฐ๋น ์์ต ๊ณ์ฐ
|
| 88 |
for year in range(1, post_retirement_years + 1):
|
|
|
|
| 92 |
annual_dividend_income = total_investment * (post_retirement_dividend_yield / 100)
|
| 93 |
# ์๊ฐ ๋ฐฐ๋น ์์ต ๊ณ์ฐ
|
| 94 |
monthly_dividend_income = annual_dividend_income / 12
|
| 95 |
+
# ์ฐ๋๋ณ ๋ฌผ๊ฐ์์น๋ฅ ๋ฐ์ํ ์ ์ํ๋น ๊ฐฑ์
|
| 96 |
+
inflated_income_required = monthly_income_required_over_time[year][1] if year < len(monthly_income_required_over_time) else monthly_income_required_over_time[-1][1]
|
| 97 |
+
# ๊ฐ ์ฐ๋๋ณ ํฌ์์ ๋ฐฐ๋น ์์ต ๋ฐ ์ ์ํ๋น๋ฅผ ๋ฆฌ์คํธ์ ์ถ๊ฐ
|
| 98 |
+
difference = annual_dividend_income - inflated_income_required * 12
|
| 99 |
+
post_retirement_investments.append((retirement_age + year, total_investment, inflated_income_required * 12, annual_dividend_income, monthly_dividend_income, inflated_income_required, difference))
|
| 100 |
+
|
| 101 |
+
# ๋ง์ด๋์ค ๊ฐ์ difference ํฉ๊ณ ๊ณ์ฐ
|
| 102 |
+
negative_differences_sum = sum(diff for _, _, _, _, _, _, diff in post_retirement_investments if diff < 0)
|
| 103 |
+
|
| 104 |
+
# CSV ํ์ผ ์์ฑ
|
| 105 |
+
csv_output = StringIO()
|
| 106 |
+
csv_writer = csv.writer(csv_output)
|
| 107 |
+
csv_writer.writerow(['Age', 'SAVINGS', 'Annual Income Required', 'Annual Dividend Income', 'Monthly Income Required', 'Monthly Dividend Income', 'Additional Cash Required'])
|
| 108 |
+
for age, investment, annual_income_required, annual_dividend_income, monthly_dividend_income, income_required, difference in post_retirement_investments:
|
| 109 |
+
additional_cash_required = f'{abs(difference):,.0f}' if difference < 0 else ''
|
| 110 |
+
csv_writer.writerow([age, f'{investment:,.0f}', f'{annual_income_required:,.0f}', f'{annual_dividend_income:,.0f}', f'{income_required:,.0f}', f'{monthly_dividend_income:,.0f}', additional_cash_required])
|
| 111 |
+
|
| 112 |
+
csv_string = csv_output.getvalue().encode('utf-8')
|
| 113 |
+
csv_base64 = base64.b64encode(csv_string).decode('utf-8')
|
| 114 |
|
| 115 |
# style.css์์ CSS ์ฝ๊ธฐ
|
| 116 |
css = load_css()
|
| 117 |
|
| 118 |
+
# SVG ๊ทธ๋ํ ์์ฑ
|
| 119 |
+
fig, ax = plt.subplots()
|
| 120 |
+
ages = [investment[0] for investment in post_retirement_investments]
|
| 121 |
+
income_required = [investment[2] for investment in post_retirement_investments]
|
| 122 |
+
dividend_income = [investment[3] for investment in post_retirement_investments]
|
| 123 |
+
|
| 124 |
+
ax.plot(ages, income_required, label='Income Required', color='red')
|
| 125 |
+
ax.plot(ages, dividend_income, label='Dividend Income', color='green')
|
| 126 |
+
ax.set_xlabel('Age')
|
| 127 |
+
ax.set_ylabel('Amount')
|
| 128 |
+
# ax.set_title('Retirement Plan Overview')
|
| 129 |
+
ax.legend()
|
| 130 |
+
ax.grid(True)
|
| 131 |
+
|
| 132 |
+
# ๊ทธ๋ํ๋ฅผ SVG ํ์์ผ๋ก ์ ์ฅ
|
| 133 |
+
svg_output = io.StringIO()
|
| 134 |
+
plt.savefig(svg_output, format='svg')
|
| 135 |
+
plt.close(fig)
|
| 136 |
+
svg_data = svg_output.getvalue()
|
| 137 |
+
svg_base64 = base64.b64encode(svg_data.encode('utf-8')).decode('utf-8')
|
| 138 |
+
|
| 139 |
# ์ํด ๊ณํ์ ๋ํ HTML ๊ฒฐ๊ณผ ์์ฑ
|
| 140 |
result_html = css + f"""
|
| 141 |
<div class="wrap-text" style="box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1); border-radius: 0.5rem; padding: 3rem; position: relative; width: 100%; padding: 1.5rem;">
|
| 142 |
<div>
|
| 143 |
<div style="margin-bottom: 1.5rem;">
|
| 144 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;"></div>
|
| 145 |
+
<img src="data:image/svg+xml;base64,{svg_base64}" alt="Retirement Plan Graph" style="width: 100%; height: auto;"/>
|
| 146 |
+
</div>
|
| 147 |
+
<div style="margin-bottom: 1.5rem;">
|
| 148 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Income Required Immediately After Retirement</div>
|
| 149 |
+
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{annual_income_required_at_retirement:,.0f}</span>
|
| 150 |
+
Annual
|
| 151 |
+
<div></div>
|
| 152 |
+
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{monthly_income_required_at_retirement:,.0f}</span>
|
| 153 |
+
MONTHLY
|
| 154 |
<hr style="margin: 1.5rem 0;">
|
| 155 |
</div>
|
|
|
|
|
|
|
| 156 |
<div style="margin-bottom: 1.5rem;">
|
| 157 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Dividend Income Immediately After Retirement</div>
|
| 158 |
+
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{annual_dividend_at_retirement:,.0f}</span>
|
|
|
|
| 159 |
Annual
|
| 160 |
+
<div></div>
|
| 161 |
+
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{monthly_dividend_at_retirement:,.0f}</span>
|
| 162 |
+
MONTHLY
|
| 163 |
+
<hr style="margin: 1.5rem 0;">
|
| 164 |
+
</div>
|
| 165 |
+
<div style="margin-bottom: 1.5rem;">
|
| 166 |
+
<div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">Total Additional Cash Required</div>
|
| 167 |
+
<span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{abs(negative_differences_sum):,.0f}</span>
|
| 168 |
<hr style="margin: 1.5rem 0;">
|
| 169 |
</div>
|
| 170 |
+
|
| 171 |
</div>
|
| 172 |
</div>
|
| 173 |
+
<div style="margin-bottom: 2rem;"></div>
|
| 174 |
+
<div style="display: flex; align-items: center; justify-content: space-between;">
|
| 175 |
+
<h3>Retirement Plan Overview</h3>
|
| 176 |
+
<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>
|
| 177 |
+
</div>
|
| 178 |
<div class='table-container'>
|
| 179 |
<table>
|
| 180 |
<thead>
|
| 181 |
<tr>
|
| 182 |
<th>Age</th>
|
| 183 |
<th>SAVINGS</th>
|
| 184 |
+
<th>Annual Income Required</th>
|
| 185 |
+
<th>Annual Dividend Income</th>
|
| 186 |
+
<th>Monthly Income Required</th>
|
| 187 |
+
<th>Monthly Dividend Income</th>
|
| 188 |
+
<th>Additional Cash Required</th>
|
| 189 |
</tr>
|
| 190 |
</thead>
|
| 191 |
<tbody>
|
| 192 |
"""
|
| 193 |
+
|
| 194 |
+
# ๊ฐ ์ฐ๋๋ณ ํฌ์์ ๋ฐฐ๋น ์์ต ๋ฐ ์ ์ํ๋น๋ฅผ ํ
์ด๋ธ์ ์ถ๊ฐ
|
| 195 |
+
for age, investment, annual_income_required, annual_dividend_income, monthly_dividend_income, income_required, difference in post_retirement_investments:
|
| 196 |
+
additional_cash_required = f'{abs(difference):,.0f}' if difference < 0 else ''
|
| 197 |
result_html += f"""
|
| 198 |
<tr>
|
| 199 |
<td>{age}</td>
|
| 200 |
<td>{investment:,.0f}</td>
|
| 201 |
+
<td>{annual_income_required:,.0f}</td>
|
| 202 |
<td>{annual_dividend_income:,.0f}</td>
|
| 203 |
+
<td>{income_required:,.0f}</td>
|
| 204 |
<td>{monthly_dividend_income:,.0f}</td>
|
| 205 |
+
<td>{additional_cash_required}</td>
|
| 206 |
</tr>
|
| 207 |
"""
|
| 208 |
|
modules/share_price_trend.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
import FinanceDataReader as fdr
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 6 |
+
|
| 7 |
+
def get_stock_prices(stock_code, days):
|
| 8 |
+
try:
|
| 9 |
+
df = fdr.DataReader(stock_code)
|
| 10 |
+
end_date = pd.to_datetime('today')
|
| 11 |
+
start_date = pd.date_range(end=end_date, periods=days, freq='B')[0]
|
| 12 |
+
df = df[(df.index >= start_date) & (df.index <= end_date)]
|
| 13 |
+
return df['Close']
|
| 14 |
+
except Exception as e:
|
| 15 |
+
print(f"Failed to fetch data for {stock_code}: {e}")
|
| 16 |
+
return None
|
| 17 |
+
|
| 18 |
+
def share_price_trend(stock_codes, days):
|
| 19 |
+
stock_prices = {}
|
| 20 |
+
with ThreadPoolExecutor(max_workers=10) as executor:
|
| 21 |
+
futures = {executor.submit(get_stock_prices, stock_code.strip(), int(days)): stock_code.strip() for stock_code in stock_codes.split(',')}
|
| 22 |
+
for future in as_completed(futures):
|
| 23 |
+
stock_code = futures[future]
|
| 24 |
+
try:
|
| 25 |
+
prices = future.result()
|
| 26 |
+
if prices is not None:
|
| 27 |
+
stock_prices[stock_code] = prices
|
| 28 |
+
except Exception as e:
|
| 29 |
+
print(f"Failed to fetch data for {stock_code}: {e}")
|
| 30 |
+
|
| 31 |
+
plt.switch_backend('agg')
|
| 32 |
+
plt.style.use('tableau-colorblind10') # ํ
๋ง ๋ณ๊ฒฝ
|
| 33 |
+
|
| 34 |
+
fig, ax = plt.subplots(figsize=(8, 4.5))
|
| 35 |
+
for stock_code, prices in stock_prices.items():
|
| 36 |
+
relative_prices = prices / prices.iloc[0]
|
| 37 |
+
ax.plot(prices.index, relative_prices, label=stock_code.upper())
|
| 38 |
+
|
| 39 |
+
ax.spines['top'].set_visible(False)
|
| 40 |
+
ax.spines['right'].set_visible(False)
|
| 41 |
+
|
| 42 |
+
ax.set_xlabel('Date')
|
| 43 |
+
ax.set_ylabel('Relative Price (Normalized to 1)')
|
| 44 |
+
ax.legend()
|
| 45 |
+
plt.tight_layout()
|
| 46 |
+
|
| 47 |
+
svg_graph = io.StringIO()
|
| 48 |
+
plt.savefig(svg_graph, format='svg')
|
| 49 |
+
svg_graph.seek(0)
|
| 50 |
+
svg_data = svg_graph.getvalue()
|
| 51 |
+
plt.close()
|
| 52 |
+
|
| 53 |
+
svg_data = svg_data.replace('<svg ', '<svg width="100%" height="100%" ')
|
| 54 |
+
svg_data = svg_data.replace('</svg>', '''
|
| 55 |
+
<defs>
|
| 56 |
+
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
|
| 57 |
+
<stop offset="0%" style="stop-color:rgb(173,216,230);stop-opacity:1" />
|
| 58 |
+
<stop offset="100%" style="stop-color:rgb(0,191,255);stop-opacity:1" />
|
| 59 |
+
</linearGradient>
|
| 60 |
+
<filter id="dropshadow" height="130%">
|
| 61 |
+
<feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
|
| 62 |
+
<feOffset dx="2" dy="2" result="offsetblur"/>
|
| 63 |
+
<feMerge>
|
| 64 |
+
<feMergeNode/>
|
| 65 |
+
<feMergeNode in="SourceGraphic"/>
|
| 66 |
+
</feMerge>
|
| 67 |
+
</filter>
|
| 68 |
+
</defs>
|
| 69 |
+
<style>
|
| 70 |
+
@keyframes lineAnimation {
|
| 71 |
+
from {
|
| 72 |
+
stroke-dasharray: 0, 1000;
|
| 73 |
+
}
|
| 74 |
+
to {
|
| 75 |
+
stroke-dasharray: 1000, 0;
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
path {
|
| 79 |
+
animation: lineAnimation 1s linear forwards;
|
| 80 |
+
}
|
| 81 |
+
</style>
|
| 82 |
+
</svg>''')
|
| 83 |
+
|
| 84 |
+
# Replace line color with gradient, add shadow filter, and apply animation
|
| 85 |
+
svg_data = svg_data.replace('stroke="#1f77b4"', 'stroke="url(#grad1)" filter="url(#dropshadow)"')
|
| 86 |
+
|
| 87 |
+
html_table = "<h3>Stock Prices Data</h3><div class='table-container'><table>"
|
| 88 |
+
html_table += "<thead><tr><th>Date</th>"
|
| 89 |
+
for stock_code in stock_prices.keys():
|
| 90 |
+
html_table += f"<th>{stock_code.upper()}</th>"
|
| 91 |
+
html_table += "</tr></thead><tbody>"
|
| 92 |
+
|
| 93 |
+
dates = stock_prices[list(stock_prices.keys())[0]].index[::-1]
|
| 94 |
+
for date in dates:
|
| 95 |
+
html_table += f"<tr><td>{date.strftime('%Y-%m-%d')}</td>"
|
| 96 |
+
for stock_code in stock_prices.keys():
|
| 97 |
+
html_table += f"<td>{stock_prices[stock_code][date]:,.2f}</td>"
|
| 98 |
+
html_table += "</tr>"
|
| 99 |
+
|
| 100 |
+
html_table += "</tbody></table></div>"
|
| 101 |
+
|
| 102 |
+
graph_html = f'<h3>Relative Stock Prices Over the Last {days} Days</h3>{svg_data}'
|
| 103 |
+
return graph_html + html_table
|
| 104 |
+
|
| 105 |
+
# ์์ ์ฌ์ฉ ๋ฐฉ๋ฒ
|
| 106 |
+
# stock_codes = "AAPL,MSFT,GOOGL"
|
| 107 |
+
# days = 30
|
| 108 |
+
# result = compare_stock_prices(stock_codes, days)
|
| 109 |
+
# print(result)
|
style.css
CHANGED
|
@@ -13,8 +13,8 @@
|
|
| 13 |
#col-container {
|
| 14 |
margin: 0 auto;
|
| 15 |
max-width: 100%;
|
| 16 |
-
font-family: '
|
| 17 |
-
text-transform: uppercase;
|
| 18 |
|
| 19 |
}
|
| 20 |
|
|
@@ -142,6 +142,7 @@
|
|
| 142 |
}
|
| 143 |
}
|
| 144 |
|
|
|
|
| 145 |
.table-container {
|
| 146 |
width: 100%;
|
| 147 |
overflow: auto;
|
|
@@ -163,12 +164,9 @@
|
|
| 163 |
background-color: var(--background-color-light);
|
| 164 |
}
|
| 165 |
|
| 166 |
-
.table-container tr:hover td {
|
| 167 |
-
background: #e7f9ef;
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
.table-container th {
|
| 171 |
background-color: var(--highlight-color-light);
|
|
|
|
| 172 |
position: sticky;
|
| 173 |
top: 0;
|
| 174 |
z-index: 2;
|
|
@@ -185,6 +183,16 @@
|
|
| 185 |
z-index: 3;
|
| 186 |
}
|
| 187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
@media (prefers-color-scheme: dark) {
|
| 189 |
.table-container {
|
| 190 |
border: 1px hidden #444;
|
|
@@ -209,5 +217,15 @@
|
|
| 209 |
.table-container tr:hover td {
|
| 210 |
background: #e7f9ef;
|
| 211 |
color: #000;
|
|
|
|
|
|
|
|
|
|
| 212 |
}
|
| 213 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
#col-container {
|
| 14 |
margin: 0 auto;
|
| 15 |
max-width: 100%;
|
| 16 |
+
font-family: 'Montserrat', 'ui-sans-serif', 'system-ui', 'sans-serif';
|
| 17 |
+
/* text-transform: uppercase; ๋ชจ๋ ํ
์คํธ๋ฅผ ๋๋ฌธ์๋ก ๋ณํํฉ๋๋ค */
|
| 18 |
|
| 19 |
}
|
| 20 |
|
|
|
|
| 142 |
}
|
| 143 |
}
|
| 144 |
|
| 145 |
+
/* ๊ธฐ๋ณธ ํ
์ด๋ธ ์คํ์ผ */
|
| 146 |
.table-container {
|
| 147 |
width: 100%;
|
| 148 |
overflow: auto;
|
|
|
|
| 164 |
background-color: var(--background-color-light);
|
| 165 |
}
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
.table-container th {
|
| 168 |
background-color: var(--highlight-color-light);
|
| 169 |
+
color: var(--header-color-light); /* ๋ผ์ดํธ ๋ชจ๋์์ ํ์ดํ ์์์ ์ค์ */
|
| 170 |
position: sticky;
|
| 171 |
top: 0;
|
| 172 |
z-index: 2;
|
|
|
|
| 183 |
z-index: 3;
|
| 184 |
}
|
| 185 |
|
| 186 |
+
/* ๋ง์ฐ์ค์ค๋ฒ ํจ๊ณผ */
|
| 187 |
+
.table-container tr:hover td {
|
| 188 |
+
background: #e7f9ef;
|
| 189 |
+
color: #000;
|
| 190 |
+
/* border: 1px solid #4caf50;
|
| 191 |
+
transform: scale(1.02);
|
| 192 |
+
transition: all 0.3s ease; */
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
/* ์ด๋์ด ํ
๋ง ์ ์ฉ */
|
| 196 |
@media (prefers-color-scheme: dark) {
|
| 197 |
.table-container {
|
| 198 |
border: 1px hidden #444;
|
|
|
|
| 217 |
.table-container tr:hover td {
|
| 218 |
background: #e7f9ef;
|
| 219 |
color: #000;
|
| 220 |
+
/* border: 1px solid #4caf50;
|
| 221 |
+
transform: scale(1.02);
|
| 222 |
+
transition: all 0.3s ease; */
|
| 223 |
}
|
| 224 |
}
|
| 225 |
+
|
| 226 |
+
/* ์ถ๊ฐ์ ์ธ ๋ง์ฐ์ค์ค๋ฒ ํจ๊ณผ */
|
| 227 |
+
/* .table-container tr:hover td {
|
| 228 |
+
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
|
| 229 |
+
cursor: pointer;
|
| 230 |
+
} */
|
| 231 |
+
|