Spaces:
Running
Running
Upload 15 files
Browse files- app.py +0 -15
- interface/.DS_Store +0 -0
- interface/rebalancing_interface.py +1 -1
- modules/rebalancing.py +1 -40
- modules/utils.py +0 -71
app.py
CHANGED
@@ -27,7 +27,6 @@ from interface.retirement_planning_interface import (
|
|
27 |
examples as retirement_planning_examples,
|
28 |
component_rows as retirement_planning_component_rows,
|
29 |
)
|
30 |
-
from interface.about_interface import render_about_tab # "Support Us" ํญ ์ถ๊ฐ
|
31 |
|
32 |
tabs_configuration = [
|
33 |
("๐ Re-Balancing Calculator", rebalancing_inputs, rebalancing_output, rebalancing_update_output, rebalancing_examples, rebalancing_component_rows),
|
@@ -62,21 +61,7 @@ Or, if you prefer, you can also support us through KakaoPay at:
|
|
62 |
|
63 |
If you have any questions or feedback, please contact us at: <a href="mailto:[email protected]">[email protected]</a>
|
64 |
""")
|
65 |
-
MARKDOWN11 = """
|
66 |
-
# TuneIt ๐ฅ Investment Toolkit
|
67 |
|
68 |
-
Welcome to the **TuneIt Investment Toolkit**! This all-in-one platform is designed to help you optimize your financial strategies, whether you're balancing your portfolio, analyzing market trends, planning for retirement, or maximizing dividends.
|
69 |
-
|
70 |
-
Explore the tools below:
|
71 |
-
|
72 |
-
- `๐ Re-Balancing Calculator`: Adjust your portfolio to maintain your desired asset allocation.
|
73 |
-
- `๐ Share Price Trend`: Analyze historical share price movements.
|
74 |
-
- `๐ Dollar Cost Averaging Calculator`: Assess the benefits of spreading out your investments.
|
75 |
-
- `๐ธ Dividend-Based Retirement Planning`: Calculate the investment required to achieve a dividend-based income stream during retirement.
|
76 |
-
|
77 |
-
Dive into each tool to enhance your investment decisions and secure your financial future.
|
78 |
-
|
79 |
-
"""
|
80 |
# Helper function to add buttons
|
81 |
def clear_buttons(inputs):
|
82 |
clear_button = gr.ClearButton(value="Clear")
|
|
|
27 |
examples as retirement_planning_examples,
|
28 |
component_rows as retirement_planning_component_rows,
|
29 |
)
|
|
|
30 |
|
31 |
tabs_configuration = [
|
32 |
("๐ Re-Balancing Calculator", rebalancing_inputs, rebalancing_output, rebalancing_update_output, rebalancing_examples, rebalancing_component_rows),
|
|
|
61 |
|
62 |
If you have any questions or feedback, please contact us at: <a href="mailto:[email protected]">[email protected]</a>
|
63 |
""")
|
|
|
|
|
64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
# Helper function to add buttons
|
66 |
def clear_buttons(inputs):
|
67 |
clear_button = gr.ClearButton(value="Clear")
|
interface/.DS_Store
CHANGED
Binary files a/interface/.DS_Store and b/interface/.DS_Store differ
|
|
interface/rebalancing_interface.py
CHANGED
@@ -4,7 +4,7 @@ from modules.utils import currency_codes, current_time
|
|
4 |
|
5 |
examples = [
|
6 |
["KRW", "458730 KRW 843 [8],\n368590 KRW 131 [2],\n458730 KRW 32 [8],\n368590 KRW 5 [2],", 0],
|
7 |
-
["KRW", "SCHD USD
|
8 |
["USD", "AAPL USD 500 [20], \nMSFT USD 300 [15], \nGOOGL USD 400 [10], \nAMZN USD 50 [5], \nTSLA USD 200 [10], \nNVDA USD 250 [10], \nNFLX USD 150 [10], \nQQQ USD 450 [5], \nADBE USD 100 [10], \nPYPL USD 50 [5]", 10000]
|
9 |
]
|
10 |
|
|
|
4 |
|
5 |
examples = [
|
6 |
["KRW", "458730 KRW 843 [8],\n368590 KRW 131 [2],\n458730 KRW 32 [8],\n368590 KRW 5 [2],", 0],
|
7 |
+
["KRW", "SCHD USD 41.776322 [8],\nQQQ USD 0.632312 [2],",0],
|
8 |
["USD", "AAPL USD 500 [20], \nMSFT USD 300 [15], \nGOOGL USD 400 [10], \nAMZN USD 50 [5], \nTSLA USD 200 [10], \nNVDA USD 250 [10], \nNFLX USD 150 [10], \nQQQ USD 450 [5], \nADBE USD 100 [10], \nPYPL USD 50 [5]", 10000]
|
9 |
]
|
10 |
|
modules/rebalancing.py
CHANGED
@@ -2,7 +2,7 @@ import math
|
|
2 |
import FinanceDataReader as fdr
|
3 |
import yfinance as yf
|
4 |
from concurrent.futures import ThreadPoolExecutor
|
5 |
-
from modules.utils import load_css, get_currency_symbol, format_quantity,
|
6 |
from collections import defaultdict
|
7 |
|
8 |
def parse_input(holdings, cash_amount):
|
@@ -105,44 +105,6 @@ def build_portfolio(stock_inputs, main_currency):
|
|
105 |
|
106 |
return portfolio, target_ratios, total_value
|
107 |
|
108 |
-
def generate_portfolio_info(portfolio, total_value, main_currency):
|
109 |
-
css = load_css()
|
110 |
-
currency_symbol = get_currency_symbol(main_currency)
|
111 |
-
|
112 |
-
# ๋ณด์ ์ข
๋ชฉ๋ณ ์ด์ก์ ๊ณ์ฐํฉ๋๋ค.
|
113 |
-
holdings_totals = {
|
114 |
-
stock_code: {
|
115 |
-
'value': stock['price'] * stock['quantity'],
|
116 |
-
'weight': (stock['price'] * stock['quantity'] / total_value)
|
117 |
-
}
|
118 |
-
for stock_code, stock in portfolio.items()
|
119 |
-
}
|
120 |
-
|
121 |
-
# ํตํ๋ณ ์ด์ก์ ๊ณ์ฐํฉ๋๋ค.
|
122 |
-
currency_totals = defaultdict(lambda: {'value': 0, 'weight': 0})
|
123 |
-
for stock in portfolio.values():
|
124 |
-
currency = stock['currency']
|
125 |
-
value = stock['price'] * stock['quantity']
|
126 |
-
currency_totals[currency]['value'] += value
|
127 |
-
currency_totals[currency]['weight'] += value / total_value
|
128 |
-
|
129 |
-
# ํ์ฌ ๋น์ค์ ์ฌ์ฉํ์ฌ ํฌํธํด๋ฆฌ์ค ํธ๋ฆฌ๋งต ์ฐจํธ๋ฅผ ์์ฑํฉ๋๋ค.
|
130 |
-
currunt_weights = {stock_code: details['weight'] for stock_code, details in holdings_totals.items()}
|
131 |
-
currunt_weights_chart = plot_donut_chart(currunt_weights)
|
132 |
-
|
133 |
-
currency_weights = {currency: details['weight'] for currency, details in currency_totals.items()}
|
134 |
-
currency_weights_chart = plot_donut_chart(currency_weights)
|
135 |
-
|
136 |
-
# HTML ์์ฑ
|
137 |
-
portfolio_info = css + f"""
|
138 |
-
<div class="wrap-text">
|
139 |
-
<h3>Your Portfolio Holdings</h3>
|
140 |
-
{currunt_weights_chart}
|
141 |
-
</div>
|
142 |
-
"""
|
143 |
-
|
144 |
-
return portfolio_info
|
145 |
-
|
146 |
def generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_currency, cash_amount, allow_selling):
|
147 |
css = load_css()
|
148 |
currency_symbol = get_currency_symbol(main_currency)
|
@@ -290,7 +252,6 @@ def rebalancing_tool(main_currency, holdings, cash_amount, allow_selling):
|
|
290 |
try:
|
291 |
stock_inputs, cash_amount = parse_input(holdings, cash_amount)
|
292 |
portfolio, target_ratios, total_value = build_portfolio(stock_inputs, main_currency)
|
293 |
-
portfolio_info = generate_portfolio_info(portfolio, total_value, main_currency)
|
294 |
rebalancing_analysis = generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_currency, cash_amount, allow_selling)
|
295 |
|
296 |
return rebalancing_analysis
|
|
|
2 |
import FinanceDataReader as fdr
|
3 |
import yfinance as yf
|
4 |
from concurrent.futures import ThreadPoolExecutor
|
5 |
+
from modules.utils import load_css, get_currency_symbol, format_quantity, format_value, current_time
|
6 |
from collections import defaultdict
|
7 |
|
8 |
def parse_input(holdings, cash_amount):
|
|
|
105 |
|
106 |
return portfolio, target_ratios, total_value
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
def generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_currency, cash_amount, allow_selling):
|
109 |
css = load_css()
|
110 |
currency_symbol = get_currency_symbol(main_currency)
|
|
|
252 |
try:
|
253 |
stock_inputs, cash_amount = parse_input(holdings, cash_amount)
|
254 |
portfolio, target_ratios, total_value = build_portfolio(stock_inputs, main_currency)
|
|
|
255 |
rebalancing_analysis = generate_rebalancing_analysis(portfolio, target_ratios, total_value, main_currency, cash_amount, allow_selling)
|
256 |
|
257 |
return rebalancing_analysis
|
modules/utils.py
CHANGED
@@ -59,74 +59,3 @@ def get_currency_codes():
|
|
59 |
|
60 |
currency_codes = get_currency_codes()
|
61 |
|
62 |
-
|
63 |
-
import matplotlib.pyplot as plt
|
64 |
-
import numpy as np
|
65 |
-
from io import BytesIO
|
66 |
-
import base64
|
67 |
-
from matplotlib import font_manager
|
68 |
-
|
69 |
-
# Global dictionary to store color mapping
|
70 |
-
color_map_storage = {}
|
71 |
-
|
72 |
-
def get_color_for_label(index, color_map, num_labels):
|
73 |
-
"""Retrieve or generate color for the given index."""
|
74 |
-
if index not in color_map_storage:
|
75 |
-
cmap = plt.get_cmap(color_map)
|
76 |
-
# Generate a color based on index (inverse of the index for color intensity)
|
77 |
-
color_map_storage[index] = cmap(1 - index / (num_labels - 1))
|
78 |
-
return color_map_storage[index]
|
79 |
-
|
80 |
-
def plot_donut_chart(data, color_map='Blues', font_path='./font/Quicksand-Regular.ttf', legend_fontsize=30):
|
81 |
-
# ๋ฐ์ดํฐ ํํฐ๋ง: ๋น์ค์ด 0์ด ์๋ ํญ๋ชฉ๋ง ์ถ์ถ
|
82 |
-
filtered_data = {k: v for k, v in data.items() if v > 0}
|
83 |
-
|
84 |
-
if not filtered_data:
|
85 |
-
return '<p>No data to display.</p>' # ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฒฝ์ฐ ์ฒ๋ฆฌ
|
86 |
-
|
87 |
-
# ๋น์ค์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฌ
|
88 |
-
sorted_data = sorted(filtered_data.items(), key=lambda item: item[1], reverse=True)
|
89 |
-
labels, sizes = zip(*sorted_data)
|
90 |
-
|
91 |
-
# ์์ ๋งต์ ์ค์
|
92 |
-
num_labels = len(labels)
|
93 |
-
|
94 |
-
# ์ํ ์ฐจํธ์ ์์ ๋ฆฌ์คํธ ์์ฑ
|
95 |
-
colors = [get_color_for_label(i, color_map, num_labels) for i in range(num_labels)]
|
96 |
-
|
97 |
-
# ๋๋ ์ฐจํธ ์๊ฐํ
|
98 |
-
fig, ax = plt.subplots(figsize=(12, 8), dpi=300) # figsize์ dpi๋ฅผ ์ค์ ํ์ฌ ํด์๋ ๋์ด๊ธฐ
|
99 |
-
wedges, _ = ax.pie(
|
100 |
-
sizes,
|
101 |
-
colors=colors,
|
102 |
-
labels=[None]*num_labels, # ๋ผ๋ฒจ์ ์์ ๊ธฐ
|
103 |
-
autopct=None, # ๊ฐ ํ์๋ฅผ ์์ ๊ธฐ
|
104 |
-
startangle=-90, # 12์ ๋ฐฉํฅ๋ถํฐ ์์
|
105 |
-
pctdistance=0.85,
|
106 |
-
wedgeprops=dict(width=0.4, edgecolor='w') # ๋๋ ์ฐจํธ
|
107 |
-
)
|
108 |
-
|
109 |
-
# y์ถ ๋ค์ง๊ธฐ
|
110 |
-
ax.invert_yaxis()
|
111 |
-
|
112 |
-
# ๋ฒ๋ก ์์ฑ
|
113 |
-
handles = [plt.Line2D([0], [0], marker='o', color='w', label=f'{label} {size * 100:.1f}%',
|
114 |
-
markersize=15, markerfacecolor=get_color_for_label(i, color_map, num_labels))
|
115 |
-
for i, (label, size) in enumerate(zip(labels, sizes))]
|
116 |
-
|
117 |
-
# ๋ฒ๋ก ์ถ๊ฐ, ์ ๋ชฉ ์ ๊ฑฐ, ๊ธ์ ํฌ๊ธฐ๋ฅผ ํค์ฐ๊ณ ๋ฒ๋ก ๋ฐ์ค๋ฅผ ์กฐ์
|
118 |
-
ax.legend(handles=handles, loc='upper left', bbox_to_anchor=(1, 1),
|
119 |
-
prop=font_manager.FontProperties(fname=font_path, size=legend_fontsize), frameon=False)
|
120 |
-
|
121 |
-
# ์ถ์ ์จ๊น๋๋ค.
|
122 |
-
ax.axis('off')
|
123 |
-
|
124 |
-
# SVG๋ก ์ ์ฅ
|
125 |
-
buf = BytesIO()
|
126 |
-
plt.savefig(buf, format='svg', bbox_inches='tight') # bbox_inches='tight'๋ฅผ ์ถ๊ฐํ์ฌ ๋ฒ๋ก๊ฐ ์๋ฆฌ๋ ๋ฌธ์ ๋ฅผ ๋ฐฉ์ง
|
127 |
-
plt.close(fig)
|
128 |
-
buf.seek(0)
|
129 |
-
|
130 |
-
# SVG ๋ฐ์ดํฐ๋ฅผ base64๋ก ์ธ์ฝ๋ฉ
|
131 |
-
svg_str = buf.getvalue().decode('utf-8')
|
132 |
-
return f'<img src="data:image/svg+xml;base64,{base64.b64encode(svg_str.encode("utf-8")).decode("utf-8")}" />'
|
|
|
59 |
|
60 |
currency_codes = get_currency_codes()
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|