File size: 4,669 Bytes
48b95e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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)