cryman38 commited on
Commit
ecf632b
ยท
verified ยท
1 Parent(s): 07635e3

Upload 16 files

Browse files
app.py CHANGED
@@ -1,127 +1,157 @@
1
-
2
  import gradio as gr
3
- from interface.portfolio_rebalancing_interface import examples ,portfolio_inputs, output as portpolio_output, update_output as portpolio_update_output
4
- from interface.compare_stock_prices_interface import compare_inputs, output as compare_output, update_output as compare_update_output
5
- from interface.cost_averaging_interface import cost_averaging_inputs, output, update_output, markdown1, old_price, old_quantity, markdown2, new_price, new_quantity
6
- from interface.retirement_planning_interface import retirement_planning_inputs, output as retirement_output, update_output as retirement_update_output
 
 
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("๐Ÿ—บ๏ธ๐Ÿ”Portfolio"):
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 portfolio_inputs:
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(portfolio_inputs),
21
  inputs=[],
22
- outputs=portfolio_inputs
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=portpolio_update_output,
29
- inputs=portfolio_inputs,
30
- outputs=portpolio_output
31
  )
32
  with gr.Column(): # Column for output
33
- portpolio_output.render()
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=portfolio_inputs
47
  )
48
- with gr.TabItem("Compare"):
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 compare_inputs:
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(compare_inputs),
57
  inputs=[],
58
- outputs=compare_inputs
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=compare_update_output,
65
- inputs=compare_inputs,
66
- outputs=compare_output
67
  )
68
  with gr.Column(): # Column for output
69
- compare_output.render()
70
 
71
- # Add change event handlers for Compare inputs
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
- markdown1.render()
82
  old_price.render()
83
  old_quantity.render()
84
- markdown2.render()
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(cost_averaging_inputs),
91
  inputs=[],
92
- outputs=cost_averaging_inputs
93
  )
94
  with gr.Column(): # Column for output
95
- output.render()
96
 
97
  # Add change event handlers for cost averaging inputs
98
- cost_averaging_inputs[0].change(fn=update_output, inputs=cost_averaging_inputs, outputs=output)
99
- cost_averaging_inputs[1].change(fn=update_output, inputs=cost_averaging_inputs, outputs=output)
100
- cost_averaging_inputs[2].change(fn=update_output, inputs=cost_averaging_inputs, outputs=output)
101
- cost_averaging_inputs[3].change(fn=update_output, inputs=cost_averaging_inputs, outputs=output)
 
 
102
 
103
- with gr.TabItem("Retirement Planning"):
104
  with gr.Row(): # Use Row to place inputs and output side by side
105
  with gr.Column(): # Column for inputs
106
- for input_component in retirement_planning_inputs:
107
- input_component.render()
108
- # Add the ClearButton below the inputs
109
- clear_button = gr.ClearButton(value="Clear")
110
- clear_button.click(
111
- fn=lambda: [None] * len(retirement_planning_inputs),
112
- inputs=[],
113
- outputs=retirement_planning_inputs
114
- )
115
- with gr.Column(): # Column for output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  retirement_output.render()
117
-
118
  # Add change event handlers for retirement planning inputs
119
- for input_component in retirement_planning_inputs:
 
 
120
  input_component.change(
121
  fn=retirement_update_output,
122
- inputs=retirement_planning_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("๐Ÿ’ก How To Use"):
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
- "Portfolio",
17
  """
18
- # ํฌํŠธํด๋ฆฌ์˜ค ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ ๋„๊ตฌ
19
 
20
- ์ด ๋„๊ตฌ๋Š” ํˆฌ์ž์ž๋“ค์ด ํ˜„์žฌ ๋ณด์œ  ์ž์‚ฐ์„ ๋ถ„์„ํ•˜๊ณ  ๋ชฉํ‘œ ๋ฐฐ๋ถ„์„ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋งค์ˆ˜/๋งค๋„ ์กฐ์น˜๋ฅผ ์ œ์•ˆํ•˜์—ฌ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ์žฌ์กฐ์ •ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์ฃผ์‹ ์ฝ”๋“œ, ํ†ตํ™” ๋ฐ ์›ํ•˜๋Š” ๋ชฉํ‘œ ๋น„์œจ์„ ๊ณ ๋ คํ•˜์—ฌ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ์กฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
21
 
22
- ## ์ฃผ์š” ๊ธฐ๋Šฅ
23
- - **๋‹ค์ค‘ ํ†ตํ™” ์ง€์›**: ๋ชจ๋“  ๊ธˆ์•ก์€ ๋ฉ”์ธ ํ†ตํ™”๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.
24
- - **์ƒ์„ธ ๋ถ„์„**: ํ˜„์žฌ ํฌํŠธํด๋ฆฌ์˜ค, ํ†ตํ™” ๋ฐฐ๋ถ„ ๋ฐ ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ๋ฐธ๋Ÿฐ์‹ฑ ๊ณ„์‚ฐ์‹œ 1์ฃผ๋‹จ์œ„๋กœ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค.
25
 
26
- ## ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
27
- 1. **ํฌํŠธํด๋ฆฌ์˜ค ์„ธ๋ถ€ ์ •๋ณด ์ž…๋ ฅ**: ์ง€์ •๋œ ํ˜•์‹์œผ๋กœ `์ฃผ์‹์ฝ”๋“œ ํ†ตํ™” ๋ณด์œ ์ˆ˜๋Ÿ‰ ๋ชฉํ‘œ๋น„์ค‘`์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์ข…๋ชฉ์•ˆ์˜ ํ•ญ๋ชฉ๋“ค์€ ๋„์–ด์“ฐ๊ธฐ๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ฃผ์‹์ข…๋ชฉ์„ (,)๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
28
- 2. **ํ˜„๊ธˆ ๊ธˆ์•ก ๋ฐ ๋น„์œจ ์„ค์ •**: ์œ ์ง€ํ•˜๋ ค๋Š” ํ˜„๊ธˆ ๊ธˆ์•ก ๋ฐ ๋น„์œจ(%)์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
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
- # Portfolio Rebalancing Tool
 
 
50
 
51
- This tool helps investors rebalance their portfolios by analyzing their current holdings and suggesting buy/sell actions to achieve target allocations. It provides detailed information on how to adjust the portfolio, considering various stock codes, currencies, and desired target ratios.
52
 
53
- ## Key Features
54
- - **Multi-currency Support**: All amounts are converted to the main currency.
55
- - **Detailed Analysis**: Displays current portfolio, currency allocation, and rebalancing recommendations. Rebalancing is calculated on a per-share basis.
56
 
57
- ## How to Use
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
- ## Example Input
62
- ```
63
- AAPL USD 100 2,
64
- TSLA USD 50 1,
65
- 005930 KRW 200 3
66
- ```
67
- - `AAPL USD 100 2`: 100 shares of Apple (AAPL) in USD, target ratio 2.
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
- ## Important Notes
 
 
 
 
 
 
 
 
 
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
- ## Disclaimer
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
- "Compare",
84
  """
85
- # ์ฃผ์‹ ๊ทธ๋ž˜ํ”„ ๋น„๊ต ๋„๊ตฌ
86
-
87
- ์ด ๋„๊ตฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ ๊ธฐ๊ฐ„ ๋™์•ˆ ์—ฌ๋Ÿฌ ํšŒ์‚ฌ์˜ ์ƒ๋Œ€ ์ฃผ๊ฐ€๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์—ญ์‚ฌ์ ์ธ ์ฃผ๊ฐ€๋ฅผ ๊ฐ€์ ธ์™€ ์ƒ๋Œ€ ์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ฃผ๋„๋ก ์ •์ƒํ™”ํ•˜๊ณ  ๋น„๊ต ๊ทธ๋ž˜ํ”„๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
88
-
89
- ## ์ฃผ์š” ๊ธฐ๋Šฅ
90
- - **์ฃผ์‹ ๋น„๊ต**: ์‚ฌ์šฉ๏ฟฝ๏ฟฝ๊ฐ€ ์ •์˜ํ•œ ๊ธฐ๊ฐ„ ๋™์•ˆ ์—ฌ๋Ÿฌ ์ฃผ์‹์˜ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.
91
- - **์ƒ๋Œ€์  ๊ฐ€๊ฒฉ๋น„๊ต**: ๊ฐ€๊ฒฉ์„ ๋™์ผํ•œ ๊ธฐ์ค€์„ ์—์„œ ์‹œ์ž‘ํ•˜๋Š” ์ƒ๋Œ€์ ์ธ ๋ณ€ํ™”๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
92
-
93
- ## ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
94
- 1. **์ฃผ์‹ ์ฝ”๋“œ ์ž…๋ ฅ**: ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ์ฃผ์‹ ์ฝ”๋“œ ๋ชฉ๋ก์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค (์˜ˆ: `AAPL, GOOGL, TSLA`).
95
- 2. **๋‚ ์งœ ์ˆ˜ ์ง€์ •**: ์ฃผ๊ฐ€๋ฅผ ๋น„๊ตํ•  ๊ธฐ๊ฐ„์˜ ๋‚ ์งœ ์ˆ˜๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
96
- 3. **์ถœ๋ ฅ**: ์ฃผ๊ฐ€๋ฅผ ๊ฐ€์ ธ์™€ ๋น„๊ต ๊ทธ๋ž˜ํ”„๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
97
-
98
- ## ์˜ˆ์ œ ์ž…๋ ฅ
99
- - `์ฃผ์‹ ์ฝ”๋“œ`: AAPL, GOOGL, TSLA
100
- - `๋‚ ์งœ ์ˆ˜`: 30
101
-
102
- ## ์˜ˆ์ œ ์ถœ๋ ฅ
103
- - Apple (AAPL), Google (GOOGL), Tesla (TSLA)์˜ ์ตœ๊ทผ 30์ผ๊ฐ„์˜ ์ƒ๋Œ€ ์ฃผ๊ฐ€๋ฅผ ๋น„๊ตํ•˜๋Š” ๊ทธ๋ž˜ํ”„.
104
-
105
- ## ์ค‘์š” ์‚ฌํ•ญ
106
- - ๋ชจ๋“  ์ฃผ์‹ ์ฝ”๋“œ๊ฐ€ ์œ ํšจํ•˜๊ณ  ๋ฐ์ดํ„ฐ ์†Œ์Šค์—์„œ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.
107
- - ๋„๊ตฌ๋Š” ์˜์—…์ผ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ฏ€๋กœ ์ฃผ๋ง ๋ฐ ๊ณตํœด์ผ์€ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.
108
- - ์ด ๋„๊ตฌ๋Š” ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
109
- - FinanceDataReader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
110
- - ๊ทธ๋ž˜ํ”ฝ ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•ด matplotlib์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
111
-
112
- ## ๋ฉด์ฑ… ์กฐํ•ญ
 
 
 
 
 
 
 
 
 
 
 
 
113
  ์ด ๋„๊ตฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๋Š” ์ผ๋ฐ˜์ ์ธ ์ •๋ณด ์ œ๊ณต ๋ชฉ์ ๋งŒ์„ ์œ„ํ•ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ์˜ ๋ชจ๋“  ์ •๋ณด๋Š” ์„ ์˜๋กœ ์ œ๊ณต๋˜์ง€๋งŒ, ์‚ฌ์ดํŠธ์˜ ์ •๋ณด์˜ ์ •ํ™•์„ฑ, ์ ์ ˆ์„ฑ, ์œ ํšจ์„ฑ, ์‹ ๋ขฐ์„ฑ, ๊ฐ€์šฉ์„ฑ ๋˜๋Š” ์™„์ „์„ฑ์— ๋Œ€ํ•ด ๋ช…์‹œ์ ์ด๋“  ๋ฌต์‹œ์ ์ด๋“  ์–ด๋– ํ•œ ์ข…๋ฅ˜์˜ ์ง„์ˆ ์ด๋‚˜ ๋ณด์ฆ์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ์˜ ์‚ฌ์šฉ๊ณผ ์‚ฌ์ดํŠธ์˜ ์ •๋ณด๋ฅผ ์‹ ๋ขฐํ•˜๋Š” ๊ฒƒ์€ ์ „์ ์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค.
114
  """,
115
  """
116
- # Stock Comparison Tool
 
 
 
 
 
 
 
117
 
118
- This tool allows users to compare the relative prices of multiple companies over a specified period. It fetches historical stock prices, normalizes them to show relative performance, and generates a comparison graph.
119
 
120
- ## Key Features
121
- - **Stock Comparison**: Compare the performance of multiple stocks over a user-defined period.
122
- - **Relative Price Comparison**: Shows relative changes starting from the same baseline.
 
 
 
123
 
124
- ## How to Use
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
- ## Example Input
130
- - `Stock Codes`: AAPL, GOOGL, TSLA
131
- - `Number of Days`: 30
132
 
133
- ## Example Output
134
- - A graph comparing the relative stock prices of Apple (AAPL), Google (GOOGL), and Tesla (TSLA) over the last 30 days.
135
 
136
- ## Important Notes
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
- ## Disclaimer
 
 
 
 
 
 
 
 
 
 
 
 
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
- "Cost Averaging",
151
  """
152
- # ๋ฌผํƒ€๊ธฐ ๊ณ„์‚ฐ ๋„๊ตฌ
153
-
154
- ์ด ๋„๊ตฌ๋Š” ํˆฌ์ž์ž๊ฐ€ ๋‹ค๋ฅธ ๊ฐ€๊ฒฉ์œผ๋กœ ์ถ”๊ฐ€ ์ฃผ์‹์„ ๋งค์ˆ˜ํ•œ ํ›„ ์ƒˆ๋กœ์šด ํ‰๊ท  ๋งค์ˆ˜ ๋‹จ๊ฐ€์™€ ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ํˆฌ์ž๊ฐ€ ์ „์ฒด ๋น„์šฉ ๊ธฐ๋ฐ˜๊ณผ ์ž ์žฌ์  ์ˆ˜์ต์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
155
-
156
- ## ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
157
- 1. **ํ˜„์žฌ ํˆฌ์ž ์„ธ๋ถ€ ์ •๋ณด ์ž…๋ ฅ**: ๋ณด์œ ํ•œ ์ฃผ์‹์˜ ํ‰๊ท  ๊ฐ€๊ฒฉ๊ณผ ์ˆ˜๋Ÿ‰์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
158
- 2. **์ƒˆ๋กœ์šด ๋งค์ˆ˜ ์„ธ๋ถ€ ์ •๋ณด ์ž…๋ ฅ**: ๋งค์ˆ˜ํ•˜๋ ค๋Š” ์ฃผ์‹์˜ ๊ฐ€๊ฒฉ๊ณผ ์ˆ˜๋Ÿ‰์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
159
- 3. **์ถœ๋ ฅ**: ๋„๊ตฌ๋Š” ์ƒˆ๋กœ์šด ํ‰๊ท  ๊ฐ€๊ฒฉ, ์ด ์ˆ˜๋Ÿ‰, ์ถ”๊ฐ€ ํˆฌ์ž์•ก, ์ด ํˆฌ์ž์•ก ๋ฐ ์ƒˆ๋กœ์šด ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•˜๊ณ  ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
160
-
161
- ## ์˜ˆ์ œ ์ž…๋ ฅ
162
- - `์ด์ „ ํ‰๊ท  ๊ฐ€๊ฒฉ`: 100
163
- - `์ด์ „ ์ˆ˜๋Ÿ‰`: 50
164
- - `์ƒˆ๋กœ์šด ๊ฐ€๊ฒฉ`: 120
165
- - `์ƒˆ๋กœ์šด ์ˆ˜๋Ÿ‰`: 30
166
-
167
- ## ์˜ˆ์ œ ์ถœ๋ ฅ
168
- - `์ˆ˜์ต๋ฅ `: 11.1%
169
- - `์ƒˆ๋กœ์šด ํ‰๊ท  ๊ฐ€๊ฒฉ`: 108
170
- - `์ด ์ˆ˜๋Ÿ‰`: 80
171
- - `์ถ”๊ฐ€ ํˆฌ์ž๊ธˆ`: 3600
172
- - `์ด ํˆฌ์ž๊ธˆ`: 8640
173
-
174
- ## ์ค‘์š” ์‚ฌํ•ญ
175
- - ๋ชจ๋“  ์ž…๋ ฅ์ด ์œ ํšจํ•œ ์ˆซ์ž์ธ์ง€ ํ™•์ธํ•˜์„ธ์š”.
176
- - ๋„๊ตฌ๋Š” ์ด์ „ ๋ฐ ์ƒˆ๋กœ์šด ํˆฌ์ž์˜ ๊ฐ€์ค‘ ํ‰๊ท ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ํ‰๊ท  ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
177
- - ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ์„ ์œ„ํ•ด Gradio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
178
- - ์‹ค์‹œ๊ฐ„ ๊ณ„์‚ฐ ๋ฐ ์ฆ‰๊ฐ๏ฟฝ๏ฟฝ๏ฟฝ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
179
-
180
- ## ๋ฉด์ฑ… ์กฐํ•ญ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  ์ด ๋„๊ตฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๋Š” ์ผ๋ฐ˜์ ์ธ ์ •๋ณด ์ œ๊ณต ๋ชฉ์ ๋งŒ์„ ์œ„ํ•ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ์˜ ๋ชจ๋“  ์ •๋ณด๋Š” ์„ ์˜๋กœ ์ œ๊ณต๋˜์ง€๋งŒ, ์‚ฌ์ดํŠธ์˜ ์ •๋ณด์˜ ์ •ํ™•์„ฑ, ์ ์ ˆ์„ฑ, ์œ ํšจ์„ฑ, ์‹ ๋ขฐ์„ฑ, ๊ฐ€์šฉ์„ฑ ๋˜๋Š” ์™„์ „์„ฑ์— ๋Œ€ํ•ด ๋ช…์‹œ์ ์ด๋“  ๋ฌต์‹œ์ ์ด๋“  ์–ด๋– ํ•œ ์ข…๋ฅ˜์˜ ์ง„์ˆ ์ด๋‚˜ ๋ณด์ฆ์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ์˜ ์‚ฌ์šฉ๊ณผ ์‚ฌ์ดํŠธ์˜ ์ •๋ณด๋ฅผ ์‹ ๋ขฐํ•˜๋Š” ๊ฒƒ์€ ์ „์ ์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค.
182
  """,
183
  """
184
- # Cost Averaging Calculator
185
-
186
- This tool helps investors calculate the new average purchase price and yield after purchasing additional shares at different prices. It is useful for understanding the impact of new investments on the overall cost basis and potential returns.
187
-
188
- ## How to Use
189
- 1. **Enter Current Investment Details**: Provide the average price and quantity of the shares you hold.
190
- 2. **Enter New Purchase Details**: Provide the price and quantity of the shares you want to purchase.
191
- 3. **Output**: The tool calculates and displays the new average price, total quantity, additional investment amount, total investment amount, and new yield.
192
-
193
- ## Example Input
194
- - `Previous Average Price`: 100
195
- - `Previous Quantity`: 50
196
- - `New Price`: 120
197
- - `New Quantity`: 30
198
-
199
- ## Example Output
200
- - `Return`: 11.1%
201
- - `New Average Price`: 108
202
- - `Total Quantity`: 80
203
- - `Additional Investment Amount`: 3600
204
- - `Total Investment Amount`: 8640
205
-
206
- ## Important Notes
207
- - Ensure all inputs are valid numbers.
208
- - The tool calculates the new average price based on the weighted average of previous and new investments.
209
- - Developed using Gradio for an easy-to-use user interface.
210
- - Supports real-time calculations and instant feedback.
211
-
212
- ## Disclaimer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- "Retirement Planning",
220
  """
221
- # ์€ํ‡ด ๊ณ„ํš ๋„๊ตฌ
222
-
223
- ์ด ๋„๊ตฌ๋Š” ๊ฐœ์ธ์ด ์€ํ‡ด ์‹œ์ ๊นŒ์ง€ ํˆฌ์ž ์„ฑ์žฅ๊ณผ ์€ํ‡ด ํ›„ ๋ฐฐ๋‹น ์†Œ๋“์„ ์˜ˆ์ธกํ•˜์—ฌ ์€ํ‡ด ๊ณ„ํš์„ ์„ธ์šฐ๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ํˆฌ์ž๊ธˆ, ์›”๊ฐ„ ํˆฌ์ž๊ธˆ, ์€ํ‡ด ์ „ํ›„์˜ ํˆฌ์ž ์ˆ˜์ต๋ฅ  (ROI) ๋ฐ ๋ฐฐ๋‹น ์ˆ˜์ต๋ฅ ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์š”์ธ์„ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค.
224
-
225
- ## ์ฃผ์š” ๊ธฐ๋Šฅ
226
- - **์œ ์—ฐํ•œ ์ž…๋ ฅ**: ์‚ฌ์šฉ์ž๋Š” ํ˜„์žฌ ๋‚˜์ด, ์€ํ‡ด ๋‚˜์ด, ํ˜„์žฌ ํˆฌ์ž์•ก, ์›”๊ฐ„ ํˆฌ์ž์•ก, ROI, ๋ฐฐ๋‹น ์ˆ˜์ต๋ฅ  ๋ฐ ๊ธฐ๋Œ€ ์ˆ˜๋ช…์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
227
- - **์ƒ์„ธ ์˜ˆ์ธก**: ๋„๊ตฌ๋Š” ์€ํ‡ด ์‹œ ์ด ํˆฌ์ž ๊ฐ€์น˜, ์˜ˆ์ƒ ์—ฐ๊ฐ„ ๋ฐ ์›”๊ฐ„ ๋ฐฐ๋‹น ์†Œ๋“, ์€ํ‡ด ํ›„ ํˆฌ์ž ์„ฑ์žฅ ๋ฐ ๋ฐฐ๋‹น ์†Œ๋“์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
228
- - **์žฌํˆฌ์ž ์˜ต์…˜**: ์‚ฌ์šฉ์ž๋Š” ์€ํ‡ด ์ „ ๋ฐฐ๋‹น๊ธˆ์„ ์žฌํˆฌ์žํ•˜์—ฌ ๋ณต๋ฆฌ ์„ฑ์žฅ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
229
-
230
- ## ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
231
- 1. **ํ˜„์žฌ ์„ธ๋ถ€ ์‚ฌํ•ญ ์ž…๋ ฅ**: ํ˜„์žฌ ๋‚˜์ด, ์€ํ‡ด ๋‚˜์ด, ํ˜„์žฌ ํˆฌ์ž์•ก ๋ฐ ์›”๊ฐ„ ํˆฌ์ž์•ก์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
232
- 2. **ํˆฌ์ž ์ˆ˜์ต๋ฅ  ์ง€์ •**: ์€ํ‡ด ์ „ํ›„์˜ ์˜ˆ์ƒ ROI ๋ฐ ๋ฐฐ๋‹น ์ˆ˜์ต๋ฅ ์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
233
- 3. **์žฌํˆฌ์ž ์˜ต์…˜ ์„ ํƒ**: ์€ํ‡ด ์ „ ๋ฐฐ๋‹น๊ธˆ์„ ์žฌํˆฌ์žํ• ์ง€ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
234
- 4. **๊ธฐ๋Œ€ ์ˆ˜๋ช… ์„ค์ •**: ๊ธฐ๋Œ€ ์ˆ˜๋ช…์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
235
- 5. **์ถœ๋ ฅ**: ๋„๊ตฌ๋Š” ์€ํ‡ด ์‹œ ์ด ํˆฌ์ž ๊ฐ€์น˜, ์—ฐ๊ฐ„ ๋ฐ ์›”๊ฐ„ ๋ฐฐ๋‹น ์†Œ๋“, ์€ํ‡ด ํ›„ ํˆฌ์ž ์„ฑ์žฅ ๋ฐ ๋ฐฐ๋‹น ์†Œ๋“์„ ๊ณ„์‚ฐํ•˜๊ณ  ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
236
-
237
- ## ์˜ˆ์ œ ์ž…๋ ฅ
238
- - `ํ˜„์žฌ ๋‚˜์ด`: 30
239
- - `์€ํ‡ด ๋‚˜์ด`: 65
240
- - `์ดˆ๊ธฐ ํˆฌ์ž๊ธˆ`: 50000
241
- - `์›”๊ฐ„ ํˆฌ์ž๊ธˆ`: 500
242
- - `์€ํ‡ด ์ „ ROI`: 7%
243
- - `์€ํ‡ด ํ›„ ROI`: 5%
244
- - `์€ํ‡ด ์ „ ๋ฐฐ๋‹น ์ˆ˜์ต๋ฅ `: 2%
245
- - `์€ํ‡ด ํ›„ ๋ฐฐ๋‹น ์ˆ˜์ต๋ฅ `: 3%
246
- - `๋ฐฐ๋‹น๊ธˆ ์žฌํˆฌ์ž`: ์˜ˆ
247
- - `๊ธฐ๋Œ€ ์ˆ˜๋ช…`: 85
248
-
249
- ## ์˜ˆ์ œ ์ถœ๋ ฅ
250
- - `์€ํ‡ด ์‹œ ์ด ๊ธˆ์•ก`: 500,000
251
- - `์€ํ‡ด ์‹œ ์—ฐ๊ฐ„ ๋ฐฐ๋‹น ์†Œ๋“`: 10,000
252
- - `์€ํ‡ด ์‹œ ์›”๊ฐ„ ๋ฐฐ๋‹น ์†Œ๋“`: 833
253
- - `์€ํ‡ด ํ›„ ํˆฌ์ž ์„ฑ์žฅ ๋ฐ ๋ฐฐ๋‹น ์†Œ๋“`: ์€ํ‡ด ํ›„ ๋งค๋…„ ์ƒ์„ธ ํ‘œ ํ˜•์‹์œผ๋กœ ์ œ๊ณต.
254
-
255
- ## ์ค‘์š” ์‚ฌํ•ญ
256
- - ๋ชจ๋“  ์ž…๋ ฅ์ด ์œ ํšจํ•œ ์ˆซ์ž์ธ์ง€ ํ™•์ธํ•˜์„ธ์š”.
257
- - ๋„๊ตฌ๋Š” ์€ํ‡ด ํ›„ ์ถ”๊ฐ€ ํˆฌ์ž๋‚˜ ๋ฐฐ๋‹น๊ธˆ ์žฌํˆฌ์ž๋ฅผ ๊ฐ€์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
258
- - ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ์„ ์œ„ํ•ด Gradio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
259
- - ์ƒ์„ธํ•œ ์€ํ‡ด ๊ณ„ํš ์˜ˆ์ธก๊ณผ ์ฆ‰๊ฐ์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
260
-
261
- ## ๋ฉด์ฑ… ์กฐํ•ญ
262
- ์ด ๋„๊ตฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๋Š” ์ผ๋ฐ˜์ ์ธ ์ •๋ณด ์ œ๊ณต ๋ชฉ์ ๋งŒ์„ ์œ„ํ•ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ์˜ ๋ชจ๋“  ์ •๋ณด๋Š” ์„ ์˜๋กœ ์ œ๊ณต๋˜์ง€๋งŒ, ์‚ฌ์ดํŠธ์˜ ์ •๋ณด์˜ ์ •ํ™•์„ฑ, ์ ์ ˆ์„ฑ, ์œ ํšจ์„ฑ, ์‹ ๋ขฐ์„ฑ, ๊ฐ€์šฉ์„ฑ ๋˜๋Š” ์™„์ „์„ฑ์— ๋Œ€ํ•ด ๋ช…์‹œ์ ์ด๋“  ๋ฌต์‹œ์ ์ด๋“  ์–ด๋– ํ•œ ์ข…๋ฅ˜์˜ ์ง„์ˆ ์ด๋‚˜ ๋ณด์ฆ์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ์˜ ์‚ฌ์šฉ๊ณผ ์‚ฌ์ดํŠธ์˜ ์ •๋ณด๋ฅผ ์‹ ๋ขฐํ•˜๋Š” ๊ฒƒ์€ ์ „์ ์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  """,
264
  """
265
- # Retirement Planning Tool
266
-
267
- This tool helps individuals plan for retirement by predicting investment growth and dividend income up to the retirement date and beyond. It considers various factors such as initial investment, monthly contributions, ROI before and after retirement, and dividend yield.
268
-
269
- ## Key Features
270
- - **Flexible Input**: Users can enter their current age, retirement age, current investment amount, monthly contribution, ROI, dividend yield, and life expectancy.
271
- - **Detailed Projections**: The tool calculates and displays total investment value at retirement, expected annual and monthly dividend income, investment growth, and dividend income after retirement.
272
- - **Reinvestment Option**: Users can choose to reinvest dividends before retirement for compounded growth.
273
-
274
- ## How to Use
275
- 1. **Enter Current Details**: Provide current age, retirement age, current investment amount, and monthly contribution.
276
- 2. **Specify ROI**: Enter the expected ROI and dividend yield before and after retirement.
277
- 3. **Select Reinvestment Option**: Choose whether to reinvest dividends before retirement.
278
- 4. **Set Life Expectancy**: Enter your expected life expectancy.
279
- 5. **Output**: The tool calculates and displays total investment value at retirement, annual and monthly dividend income, and investment growth and dividend income after retirement.
280
-
281
- ## Example Input
282
- - `Current Age`: 30
283
- - `Retirement Age`: 65
284
- - `Initial Investment`: 50000
285
- - `Monthly Contribution`: 500
286
- - `Pre-Retirement ROI`: 7%
287
- - `Post-Retirement ROI`: 5%
288
- - `Pre-Retirement Dividend Yield`: 2%
289
- - `Post-Retirement Dividend Yield`: 3%
290
- - `Dividend Reinvestment`: Yes
291
- - `Life Expectancy`: 85
292
-
293
- ## Example Output
294
- - `Total Amount at Retirement`: 500,000
295
- - `Annual Dividend Income at Retirement`: 10,000
296
- - `Monthly Dividend Income at Retirement`: 833
297
- - `Post-Retirement Investment Growth and Dividend Income`: Detailed annually in tabular format.
298
-
299
- ## Important Notes
300
- - Ensure all inputs are valid numbers.
301
- - The tool assumes no additional investments or dividend reinvestments after retirement.
302
- - Developed using Gradio for an easy-to-use user interface.
303
- - Supports detailed retirement planning projections and instant feedback.
304
-
305
- ## Disclaimer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # Support Us Section
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
- [![Donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M8SBRC396DPBW)
316
 
317
- Or, if you prefer, you can also support us through Toss at:
318
 
319
- <a href="https://toss.me/eichijei" target="_blank">
320
- <img src="https://static.toss.im/logos/png/1x/logo-toss.png" alt="Donate with Toss" style="width: 150px;">
321
- </a>
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
+ [![Donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](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(current_age, retirement_age, current_investment, monthly_investment, pre_retirement_roi, post_retirement_roi, pre_retirement_dividend_yield, post_retirement_dividend_yield, reinvest_dividends, life_expectancy):
6
- result = retirement_planning(current_age, retirement_age, current_investment, monthly_investment, pre_retirement_roi, post_retirement_roi, pre_retirement_dividend_yield, post_retirement_dividend_yield, reinvest_dividends, life_expectancy)
 
 
 
 
7
  css = load_css()
8
  return css + result
9
 
10
- retirement_planning_inputs = [
11
- gr.Slider(label="Current Age (15-60 Years)", value=15, minimum=15, maximum=60, step=1),
12
- gr.Slider(label="Retirement Age (Upto 70 Years)", value=55, minimum=15, maximum=70, step=1),
13
- gr.Number(label="Current Investment", value=10000000),
14
- gr.Number(label="Monthly Investment", value=500000),
15
- gr.Number(label="Expected Return On Investment (Pre-retirement) (%)", value=8),
16
- gr.Number(label="Expected Return On Investment (Post-retirement) (%)", value=8),
17
- gr.Number(label="Expected Dividend Yield (Pre-retirement) (%)", value=3.3),
18
- gr.Number(label="Expected Dividend Yield (Post-retirement) (%)", value=3.3),
19
- gr.Checkbox(label="Reinvest Dividends", value=True),
20
- gr.Slider(label="Life Expectancy (Upto 100 Years)", value=80, minimum=30, maximum=100, step=1)
21
- ]
 
 
22
 
23
  output = gr.HTML()
24
 
25
  # Define the update function
26
- def update_output(current_age, retirement_age, current_investment, monthly_investment, pre_retirement_roi, post_retirement_roi, pre_retirement_dividend_yield, post_retirement_dividend_yield, reinvest_dividends, life_expectancy):
27
- return retirement_planning_interface_fn(current_age, retirement_age, current_investment, monthly_investment, pre_retirement_roi, post_retirement_roi, pre_retirement_dividend_yield, post_retirement_dividend_yield, reinvest_dividends, life_expectancy)
 
 
 
 
 
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(current_age=None, retirement_age=None, current_investment=None, monthly_investment=None, pre_retirement_roi=None, post_retirement_roi=None, pre_retirement_dividend_yield=None, post_retirement_dividend_yield=None, reinvest_dividends=False, life_expectancy=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- post_retirement_investments.append((retirement_age + year, total_investment, annual_dividend_income, monthly_dividend_income))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- <div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">SAVINGS at retirement</div>
66
- <div style="font-size: 1.5rem; font-weight: bold; color: #1c75bc;">
67
- <span style='color: #1678fb'>{investment_at_retirement:,.0f}</span>
68
- </div>
 
 
 
 
 
69
  <hr style="margin: 1.5rem 0;">
70
  </div>
71
- </div>
72
- <div>
73
  <div style="margin-bottom: 1.5rem;">
74
- <!-- ์€ํ‡ด ์‹œ์ ์˜ ์—ฐ๊ฐ„ ๋ฐ ์›”๊ฐ„ ๋ฐฐ๋‹น ์ˆ˜์ต ํ‘œ์‹œ -->
75
- <div style="font-size: 1.5rem; margin-top: 1.5rem; margin-bottom: 1.5rem;">DIVIDEND INCOME at retirement</div>
76
- <span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{annual_dividend_at_retirement:,.0f}</span>
77
  Annual
78
- <p></p>
79
- <span style='font-size: 1.5rem; font-weight: bold; color: #1678fb'>{monthly_dividend_at_retirement:,.0f}</span>
80
- Monthly
 
 
 
 
 
81
  <hr style="margin: 1.5rem 0;">
82
  </div>
 
83
  </div>
84
  </div>
85
- <h3>Dividend Income After Retirement</h3>
 
 
 
 
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>Monthly</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: 'Patrick Hand', 'ui-sans-serif', 'system-ui', 'sans-serif';
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
+