re-balancing-app-demo / modules /share_price_trend.py
cryman38's picture
Upload 16 files
48b95e7 verified
import io
import matplotlib.pyplot as plt
import FinanceDataReader as fdr
import pandas as pd
from concurrent.futures import ThreadPoolExecutor, as_completed
def get_stock_prices(stock_code, days):
try:
df = fdr.DataReader(stock_code)
end_date = pd.to_datetime('today')
start_date = pd.date_range(end=end_date, periods=days, freq='B')[0]
df = df[(df.index >= start_date) & (df.index <= end_date)]
if df.empty:
print(f"<p style='color: red;'>No data available for {stock_code}</p>")
return None
return df['Close']
except Exception as e:
print(f"<p style='color: red;'>Failed to fetch data for {stock_code}: {e}</p>")
return None
def share_price_trend(stock_codes, days):
stock_prices = {}
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(get_stock_prices, stock_code.strip(), int(days)): stock_code.strip() for stock_code in stock_codes.split(',')}
for future in as_completed(futures):
stock_code = futures[future]
try:
prices = future.result()
if prices is not None:
stock_prices[stock_code] = prices
except Exception as e:
print(f"<p style='color: red;'>Failed to fetch data for {stock_code}: {e}</p>")
if not stock_prices:
return "<p style='color: red;'>No data available for the provided stock codes.</p>"
plt.switch_backend('agg')
plt.style.use('tableau-colorblind10')
fig, ax = plt.subplots(figsize=(8, 4.5))
for stock_code, prices in stock_prices.items():
relative_prices = prices / prices.iloc[0]
ax.plot(prices.index, relative_prices, label=stock_code.upper())
# Remove the axes and ticks
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
# Add grid for better readability
ax.grid(True, which='both', linestyle='--', linewidth=0.5)
ax.legend()
plt.tight_layout()
svg_graph = io.StringIO()
plt.savefig(svg_graph, format='svg')
svg_graph.seek(0)
svg_data = svg_graph.getvalue()
plt.close()
svg_data = svg_data.replace('<svg ', '<svg width="100%" height="100%" ')
svg_data = svg_data.replace('</svg>', '''
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(173,216,230);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(0,191,255);stop-opacity:1" />
</linearGradient>
<filter id="dropshadow" height="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
<feOffset dx="2" dy="2" result="offsetblur"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<style>
@keyframes lineAnimation {
from {
stroke-dasharray: 0, 1000;
}
to {
stroke-dasharray: 1000, 0;
}
}
path {
animation: lineAnimation 1s linear forwards;
}
</style>
</svg>''')
svg_data = svg_data.replace('stroke="#1f77b4"', 'stroke="url(#grad1)" filter="url(#dropshadow)"')
html_table = "<h3>Stock Prices Data</h3><div class='table-container'><table>"
html_table += "<thead><tr><th>Date</th>"
for stock_code in stock_prices.keys():
html_table += f"<th>{stock_code.upper()}</th>"
html_table += "</tr></thead><tbody>"
# Create a date range with only business days
all_dates = pd.date_range(start=min(df.index.min() for df in stock_prices.values()), end=pd.to_datetime('today'), freq='B')
all_dates = all_dates[::-1] # Reverse the order of dates
for date in all_dates:
html_table += f"<tr><td>{date.strftime('%Y-%m-%d')}</td>"
for stock_code in stock_prices.keys():
price = stock_prices[stock_code].get(date, None)
if price is not None:
html_table += f"<td>{price:,.2f}</td>"
else:
html_table += "<td>N/A</td>"
html_table += "</tr>"
html_table += "</tbody></table></div>"
graph_html = f'<h3>Relative Stock Prices Over the Last {days} Days</h3>{svg_data}'
return graph_html + html_table
# Example usage
# stock_codes = "AAPL,MSFT,GOOGL"
# days = 30
# result = share_price_trend(stock_codes, days)
# print(result)