cryman38 commited on
Commit
753f710
·
verified ·
1 Parent(s): fe3649a

Upload 14 files

Browse files
modules/compare_stock_prices.py CHANGED
@@ -1,5 +1,4 @@
1
  import io
2
- import base64
3
  import matplotlib.pyplot as plt
4
  import FinanceDataReader as fdr
5
  import pandas as pd
@@ -7,24 +6,18 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
7
 
8
  def get_stock_prices(stock_code, days):
9
  try:
10
- # 주식 데이터를 가져옴
11
  df = fdr.DataReader(stock_code)
12
  end_date = pd.to_datetime('today')
13
- # 영업일 기준으로 시작 날짜를 계산
14
  start_date = pd.date_range(end=end_date, periods=days, freq='B')[0]
15
- # 지정된 기간 동안의 주식 종가 데이터를 필터링
16
  df = df[(df.index >= start_date) & (df.index <= end_date)]
17
  return df['Close']
18
  except Exception as e:
19
- # 데이터 가져오기 실패 시 에러 메시지 출력
20
  print(f"Failed to fetch data for {stock_code}: {e}")
21
  return None
22
 
23
  def compare_stock_prices(stock_codes, days):
24
- # 주식 그래프 생성을 위한 병렬 처리
25
  stock_prices = {}
26
  with ThreadPoolExecutor(max_workers=10) as executor:
27
- # 각 주식 코드에 대해 get_stock_prices 함수를 비동기로 실행
28
  futures = {executor.submit(get_stock_prices, stock_code.strip(), int(days)): stock_code.strip() for stock_code in stock_codes.split(',')}
29
  for future in as_completed(futures):
30
  stock_code = futures[future]
@@ -33,39 +26,64 @@ def compare_stock_prices(stock_codes, days):
33
  if prices is not None:
34
  stock_prices[stock_code] = prices
35
  except Exception as e:
36
- # 데이터 가져오기 실패 시 에러 메시지 출력
37
  print(f"Failed to fetch data for {stock_code}: {e}")
38
 
39
- # Matplotlib의 기본 설정을 사용하여 백엔드를 설정
40
  plt.switch_backend('agg')
 
41
 
42
- # 주식에 대한 그래프를 그림
43
- fig, ax = plt.subplots(figsize=(10, 6))
44
  for stock_code, prices in stock_prices.items():
45
- # 첫 번째 데이터 포인트를 기준으로 상대적 가격 계산
46
  relative_prices = prices / prices.iloc[0]
47
- # 주식 가격을 그래프에 그림
48
- ax.plot(prices.index, relative_prices, label=stock_code.upper()) # 주식 코드를 대문자로 표시
49
 
50
- # 위와 오른쪽 테두리 없애기
51
  ax.spines['top'].set_visible(False)
52
  ax.spines['right'].set_visible(False)
53
 
54
  ax.set_xlabel('Date')
55
  ax.set_ylabel('Relative Price (Normalized to 1)')
56
- # ax.set_title(f'Relative Stock Prices Over the Last {days} Days')
57
  ax.legend()
58
  plt.tight_layout()
59
 
60
- # 그래프를 HTML로 변환하여 반환
61
- html_graph = io.BytesIO()
62
- plt.savefig(html_graph, format='png', dpi=300)
63
- html_graph.seek(0)
64
- graph_encoded = base64.b64encode(html_graph.getvalue()).decode()
65
- graph_html = f'<h3>Relative Stock Prices Over the Last {days} Days</h3><img src="data:image/png;base64,{graph_encoded}"/>'
66
- plt.close() # 리소스 정리
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- # 주식 가격 데이터를 HTML 테이블로 변환
69
  html_table = "<h3>Stock Prices Data</h3><div class='table-container'><table>"
70
  html_table += "<thead><tr><th>Date</th>"
71
  for stock_code in stock_prices.keys():
@@ -81,6 +99,7 @@ def compare_stock_prices(stock_codes, days):
81
 
82
  html_table += "</tbody></table></div>"
83
 
 
84
  return graph_html + html_table
85
 
86
  # 예시 사용 방법
 
1
  import io
 
2
  import matplotlib.pyplot as plt
3
  import FinanceDataReader as fdr
4
  import pandas as pd
 
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 compare_stock_prices(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]
 
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():
 
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
  # 예시 사용 방법
modules/retirement_planning.py CHANGED
@@ -62,7 +62,7 @@ def retirement_planning(current_age=None, retirement_age=None, current_investmen
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;">Total value 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>
@@ -72,7 +72,7 @@ def retirement_planning(current_age=None, retirement_age=None, current_investmen
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>
@@ -88,7 +88,7 @@ def retirement_planning(current_age=None, retirement_age=None, current_investmen
88
  <thead>
89
  <tr>
90
  <th>Age</th>
91
- <th>Total</th>
92
  <th>Annual</th>
93
  <th>Monthly</th>
94
  </tr>
 
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>
 
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>
 
88
  <thead>
89
  <tr>
90
  <th>Age</th>
91
+ <th>SAVINGS</th>
92
  <th>Annual</th>
93
  <th>Monthly</th>
94
  </tr>
style.css CHANGED
@@ -159,63 +159,70 @@
159
  }
160
 
161
  }
162
-
163
  /* 기본 테이블 스타일 */
164
  .table-container {
165
  max-width: 100%;
166
  overflow-x: auto;
167
  border: 1px solid #ddd;
168
- }
169
 
170
- .table-container table {
171
  width: 100%;
172
  border-collapse: collapse;
173
  table-layout: auto;
174
- }
175
 
176
- .table-container th,
177
- .table-container td {
178
  padding: 8px 16px;
179
  border: 1px solid #ddd;
180
  background: #fff;
181
  /* white-space: nowrap; */
182
  text-align: left;
183
  text-transform: uppercase;
184
- }
185
 
186
- .table-container thead {
187
  background: #f9f9f9;
188
- }
189
-
190
 
191
- .table-container th:first-child,
192
- .table-container td:first-child {
193
  position: sticky;
194
  left: 0;
195
  background: #f9f9f9;
196
  z-index: 2;
197
- }
198
 
199
- /* 다크 모드 */
200
- @media (prefers-color-scheme: dark) {
 
 
 
 
 
201
  .table-container {
202
- border: 1px solid #444;
203
  }
204
 
205
  .table-container th,
206
  .table-container td {
207
- border: 1px solid #444;
208
- background: #333;
209
- color: #ccc;
210
  }
211
 
212
  .table-container thead {
213
- background: #444;
214
  }
215
 
216
  .table-container th:first-child,
217
  .table-container td:first-child {
218
- background: #444;
219
  }
220
- }
221
-
 
 
 
 
 
159
  }
160
 
161
  }
 
162
  /* 기본 테이블 스타일 */
163
  .table-container {
164
  max-width: 100%;
165
  overflow-x: auto;
166
  border: 1px solid #ddd;
167
+ }
168
 
169
+ .table-container table {
170
  width: 100%;
171
  border-collapse: collapse;
172
  table-layout: auto;
173
+ }
174
 
175
+ .table-container th,
176
+ .table-container td {
177
  padding: 8px 16px;
178
  border: 1px solid #ddd;
179
  background: #fff;
180
  /* white-space: nowrap; */
181
  text-align: left;
182
  text-transform: uppercase;
183
+ }
184
 
185
+ .table-container thead {
186
  background: #f9f9f9;
187
+ }
 
188
 
189
+ .table-container th:first-child,
190
+ .table-container td:first-child {
191
  position: sticky;
192
  left: 0;
193
  background: #f9f9f9;
194
  z-index: 2;
195
+ }
196
 
197
+ /* 마우스 오버 효과 */
198
+ .table-container tr:hover td {
199
+ background: #f1f1f1;
200
+ }
201
+
202
+ /* 다크 모드 */
203
+ @media (prefers-color-scheme: dark) {
204
  .table-container {
205
+ border: 1px solid #444;
206
  }
207
 
208
  .table-container th,
209
  .table-container td {
210
+ border: 1px solid #444;
211
+ background: #333;
212
+ color: #ccc;
213
  }
214
 
215
  .table-container thead {
216
+ background: #444;
217
  }
218
 
219
  .table-container th:first-child,
220
  .table-container td:first-child {
221
+ background: #444;
222
  }
223
+
224
+ /* 다크 모드 마우스 오버 효과 */
225
+ .table-container tr:hover td {
226
+ background: #555;
227
+ }
228
+ }