1+ #!/usr/bin/env python3
2+ """
3+ Stock visualization example using the Borsdata API client.
4+
5+ This example demonstrates how to create beautiful visualizations of stock data
6+ including price trends, volume, and basic technical indicators.
7+ """
8+
9+ import os
10+ from datetime import datetime , timedelta
11+ import pandas as pd
12+ import matplotlib .pyplot as plt
13+ import seaborn as sns
14+ from dotenv import load_dotenv
15+ from borsdata_client import BorsdataClient
16+
17+ # Set style for prettier plots
18+ plt .style .use ('seaborn-v0_8' )
19+ sns .set_palette ("husl" )
20+
21+ # Load environment variables from .env file
22+ load_dotenv ()
23+
24+ # Get API key from environment
25+ api_key = os .getenv ("BORSDATA_API_KEY" )
26+ if not api_key :
27+ raise ValueError ("BORSDATA_API_KEY environment variable not set" )
28+
29+ def get_stock_data (client : BorsdataClient , ticker : str , days : int = 365 ) -> pd .DataFrame :
30+ """
31+ Fetch stock data and convert it to a pandas DataFrame.
32+
33+ Args:
34+ client: BorsdataClient instance
35+ ticker: Stock ticker symbol
36+ days: Number of days of historical data to fetch
37+
38+ Returns:
39+ DataFrame with stock price data
40+ """
41+ # Find the instrument
42+ instruments = client .get_instruments ()
43+ instrument = next ((i for i in instruments if i .ticker == ticker ), None )
44+ if not instrument :
45+ raise ValueError (f"Could not find instrument with ticker { ticker } " )
46+
47+ # Get historical prices
48+ today = datetime .now ()
49+ start_date = today - timedelta (days = days )
50+ prices = client .get_stock_prices (
51+ instrument_id = instrument .ins_id ,
52+ from_date = start_date ,
53+ to_date = today
54+ )
55+
56+ # Convert to DataFrame
57+ df = pd .DataFrame ([{
58+ 'Date' : datetime .strptime (p .d , '%Y-%m-%d' ),
59+ 'Open' : p .o ,
60+ 'High' : p .h ,
61+ 'Low' : p .l ,
62+ 'Close' : p .c ,
63+ 'Volume' : p .v
64+ } for p in prices ])
65+
66+ df .set_index ('Date' , inplace = True )
67+
68+ # Add some technical indicators
69+ df ['SMA_20' ] = df ['Close' ].rolling (window = 20 ).mean ()
70+ df ['SMA_50' ] = df ['Close' ].rolling (window = 50 ).mean ()
71+ df ['Daily_Return' ] = df ['Close' ].pct_change ()
72+
73+ return df
74+
75+ def plot_stock_analysis (df : pd .DataFrame , ticker : str ):
76+ """
77+ Create a comprehensive visualization of stock data.
78+
79+ Args:
80+ df: DataFrame with stock price data
81+ ticker: Stock ticker symbol for the title
82+ """
83+ # Create a figure with subplots
84+ fig = plt .figure (figsize = (16 , 10 ), constrained_layout = True )
85+
86+ # Define grid for subplots
87+ gs = fig .add_gridspec (3 , 1 , height_ratios = [2 , 1 , 1 ])
88+
89+ # Price and Moving Averages
90+ ax1 = fig .add_subplot (gs [0 ])
91+ ax1 .plot (df .index , df ['Close' ], label = 'Close Price' , linewidth = 1.5 )
92+ ax1 .plot (df .index , df ['SMA_20' ], label = '20-day SMA' , linestyle = '--' , alpha = 0.8 )
93+ ax1 .plot (df .index , df ['SMA_50' ], label = '50-day SMA' , linestyle = '--' , alpha = 0.8 )
94+ ax1 .set_title (f'{ ticker } Stock Price Analysis' , fontsize = 14 , pad = 20 )
95+ ax1 .set_ylabel ('Price' )
96+ ax1 .legend (loc = 'center left' , bbox_to_anchor = (1.02 , 0.5 ),
97+ fancybox = True , shadow = True , framealpha = 1 )
98+ ax1 .grid (True , alpha = 0.3 )
99+
100+ # Volume
101+ ax2 = fig .add_subplot (gs [1 ], sharex = ax1 )
102+ ax2 .bar (df .index , df ['Volume' ], alpha = 0.5 , color = 'darkblue' )
103+ ax2 .set_ylabel ('Volume' )
104+ ax2 .grid (True , alpha = 0.3 )
105+
106+ # Daily Returns
107+ ax3 = fig .add_subplot (gs [2 ], sharex = ax1 )
108+ ax3 .plot (df .index , df ['Daily_Return' ], color = 'green' , alpha = 0.7 )
109+ ax3 .axhline (y = 0 , color = 'black' , linestyle = '-' , alpha = 0.3 )
110+ ax3 .fill_between (df .index , df ['Daily_Return' ], 0 ,
111+ where = (df ['Daily_Return' ] >= 0 ), color = 'green' , alpha = 0.3 )
112+ ax3 .fill_between (df .index , df ['Daily_Return' ], 0 ,
113+ where = (df ['Daily_Return' ] < 0 ), color = 'red' , alpha = 0.3 )
114+ ax3 .set_ylabel ('Daily Returns' )
115+ ax3 .grid (True , alpha = 0.3 )
116+
117+ # Adjust layout and display
118+ plt .xlabel ('Date' )
119+
120+ # Add some statistics as text
121+ stats_text = (
122+ f"Statistics:\n "
123+ f"Current Price: { df ['Close' ].iloc [- 1 ]:.2f} \n "
124+ f"52-week High: { df ['High' ].max ():.2f} \n "
125+ f"52-week Low: { df ['Low' ].min ():.2f} \n "
126+ f"Daily Vol.: { df ['Daily_Return' ].std ()* 100 :.2f} %"
127+ )
128+
129+ # Add text in a better position that won't conflict with tight_layout
130+ ax1 .text (0.02 , 0.98 , stats_text ,
131+ transform = ax1 .transAxes ,
132+ bbox = dict (facecolor = 'white' , alpha = 0.8 , edgecolor = 'none' ),
133+ fontsize = 10 ,
134+ verticalalignment = 'top' )
135+
136+ return fig
137+
138+ def main ():
139+ """Run the stock visualization example."""
140+ # Example tickers (can be changed to any stock available in Borsdata)
141+ tickers = ['ERIC B' , 'VOLV B' ]
142+
143+ with BorsdataClient (api_key ) as client :
144+ for ticker in tickers :
145+ try :
146+ print (f"\n Fetching data for { ticker } ..." )
147+ df = get_stock_data (client , ticker )
148+
149+ print ("Creating visualization..." )
150+ fig = plot_stock_analysis (df , ticker )
151+
152+ # Save the plot
153+ filename = f"{ ticker .replace (' ' , '_' )} _analysis.png"
154+ fig .savefig (filename )
155+ print (f"Saved visualization to { filename } " )
156+ plt .close (fig )
157+
158+ except Exception as e :
159+ print (f"Error processing { ticker } : { e } " )
160+
161+ if __name__ == "__main__" :
162+ main ()
0 commit comments