How to Build Real-Time WinForms Financial Charts and Graphs
Quick Start Guide | |
---|---|
What You Will Need |
Visual Studio ComponentOne Studio Enterprise |
Controls Referenced | |
Tutorial Concept | This tutorial demonstrates basic stock chart types with FlexChart, such as simple line and candle. Then, we cover advanced chart types and overlays with the FinancialChart component. |
In today's data-centric era, elevating your financial data with stunning visuals can make your analysis both informative and easier. Financial charts act as a guide in the stock market. They help traders make informed decisions and optimize their strategies according to market shifts.
ComponentOne includes a charting control named FinancialChart for WinForms applications. It enables .NET developers to create stock trend charts using predefined financial indicators and special chart types. In this blog, we’ll show how to create a real-time financial chart for stock data in WinForms by following the steps below:
- Setup a .NET WinForms Application with the FinancialChart Control
- Fetch Real-Time Stock Data
- Bind the Data to the Chart
- Implement a Timer to Update the Chart in Real Time
- Customize Chart Settings to Enhance the Visuals
Ready to get started? Download ComponentOne Today!
Setup a WinForms Application with the FinancialChart Control
Let's begin by following the steps below to set up a new .NET 8 WinForms application that includes the ComponentOne FinancialChart dependency.
- Open Visual Studio and select File | New | Project to create a new Windows Forms App.
- Right-click on the project in Solution Explorer and click on Manage NuGet Packages… from the context menu.
- Search for the C1.Win.FinancialChart package in the NuGet Package Manager and click on Install. This adds the FinancialChart control to the toolbox.
- Add the required controls to the form to support multiple financial chart types and allow users to switch between light and dark themes. Next, drop the FinancialChart control from the toolbox onto the form designer. The design of the form will appear as below:
With the design phase complete, we are ready to build the functionality of our application!
Fetch Real-Time Stock Data
To keep our chart updated with live stock prices, we must connect it to a source of real-time data. Although APIs can provide stock data in various formats, we will use JSON data for this example.
There are many free and paid API services that provide frequent JSON data updates for real-time charting, but free options often come with usage limits. Therefore, for this sample demonstration, we will use locally saved JSON data instead of connecting to an API. The sample implementation is designed to work regardless of the market hours. If the current time falls outside the market hours window, we set a dummy start time for the live updates. Below is the method to set the chart’s start time:
// method to set the start time for the chart live updates
public void SetStartTime()
{
TimeSpan currentTime = DateTime.Now.TimeOfDay;
marketOpeningTime = new TimeSpan(11, 40, 0); //set the market opening time
marketClosingTime = new TimeSpan(19, 55, 0); //set the market closing time
if (currentTime >= marketOpeningTime && currentTime < marketClosingTime)
{
// Set start time to current time if within market hours
liveStartTime = currentTime;
}
else
{
// Set a dummy start time if the current time is outside market hours
liveStartTime = new TimeSpan(14, DateTime.Now.TimeOfDay.Minutes, 00);
}
}
Note: If the chart is only intended to be used during market hours, we can remove the if-else block and simply set ‘liveStartTime’ to ‘currentTime’ without any conditions.
Now, it’s time to load past stock data into the chart from the locally saved JSON file named ‘msft.json’. This file contains five-minute interval data for the ‘Microsoft Corp’ stock and is used to load the data from the market opening time up to the current time.
// Fetches stock data from an embedded JSON resource file.
public ObservableCollection<Quote> GetSymbolsData()
{
var assembly = Assembly.GetExecutingAssembly();
// Define the resource name for the embedded JSON file.
string resourceName = "RealTimeFinancialChartDemo.Resources.msft.json";
// Read the embedded resource as a stream and deserialize it into a collection of Quote objects.
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
string jsonContent = reader.ReadToEnd();
stockData = JsonConvert.DeserializeObject<ObservableCollection<Quote>>(jsonContent); }
return stockData;
}
After loading the data, we will filter it based on the start time defined by the SetStartTime() method. This step can be omitted if the API we’re using already provides quotes up to the current time.
Below is the code snippet for this step:
// Filters quotes that have a timestamp before the specified current time.
public ObservableCollection<Quote> FilterQuotesBeforeCurrentTime(ObservableCollection<Quote> quotes, TimeSpan currentTime)
{
// Filter quotes based on their timestamp and order them in descending order.
var filteredQuotes = new ObservableCollection<Quote>(
quotes.Where(q => q.timeStamp.TimeOfDay < currentTime)
.OrderByDescending(q => q.timeStamp.TimeOfDay)
);
return filteredQuotes;
}
Bind the Data to the Financial Chart
The filtered quotes obtained in the previous step contain information, such as the stock’s opening, high, low, and closing prices, along with the trading volume and timestamps. In this step, we will bind these properties of filtered quotes to the corresponding axes of the chart control. Below is the code snippet to bind these properties:
// setting the bindings for the chart
financialChartMain.DataSource = filteredQuotes = dataService.FilterQuotesBeforeCurrentTime(dataService.GetSymbolsData(), liveStartTime);
quotesDate = filteredQuotes[0].timeStamp;
financialChartMain.BindingX = "timeStamp";
financialChartMain.Binding = "high,low,open,close,vol";
Now that the bindings are set, we will add the financial series to the chart as follows:
// adding the financial series to the chart
financialChartMain.Series.Clear();
financialChartMain.Series.Add(new FinancialSeries());
Implement a Timer to Update the Chart in Real Time
After we complete the data binding, it’s time to simulate real-time updates in the chart. To accomplish this, we will generate new JSON data every 10 seconds with randomly changing stock prices. The chart is set up to display a new candlestick every five minutes, so any data generated within a five-minute interval will modify the latest candle. We are using the System.Windows.Forms.Timer class to create a timer that triggers these updates at regular intervals. The steps below outline the implementation:
Initialize the Timer
We will create a ‘Timer’ instance by handling its Tick event for every 10-second interval.
dataUpdateTimer = new System.Windows.Forms.Timer();
dataUpdateTimer.Tick += DataUpdateTimer_Tick;
dataUpdateTimer.Interval = 10000; // setting interval in milliseconds
dataUpdateTimer.Start();
Update Data on Each Tick
Within the DataUpdateTimer_Tick event handler, we will fetch new stock data from an API or by generating random values every 10 seconds. Since our chart displays candlesticks at five-minute intervals, this event determines whether to create a new candle or update the latest one. The code to achieve this is as follows:
// event handler for the updation/addition of new quotes
private void DataUpdateTimer_Tick(object? sender, EventArgs e)
{
var latestQuoteTime = filteredQuotes[0].timeStamp.TimeOfDay;
if (DateTime.Now.TimeOfDay.Minutes % 5 == 0 && DateTime.Now.TimeOfDay.Seconds < 10 && filteredQuotes[0].timeStamp.TimeOfDay < marketClosingTime)
{
// adding new quote after every 5 minutes
GenerateNextQuote(latestQuoteTime.Add(new TimeSpan(0, 5, 0)), true);
}
else
{
// updating the latest quote
GenerateNextQuote(latestQuoteTime, false);
}
}
Define a Method to Generate Random Stock Prices in JSON Format
Next, we will implement a method to generate random stock prices when invoked. This method serializes the data into a JSON string that contains the latest quote information:
// Generates a new random quote JSON string based on the current data and time.
public static string GenerateRandomQuoteJson(ObservableCollection<Quote> quotesData, TimeSpan currentTime, bool isNewInterval)
{
var random = new Random();
Quote latestQuote = quotesData[0];
Quote newQuote = new Quote();
// Generate a new random quote if it's a new time interval.
if (isNewInterval)
{
DateTime quotesDate = latestQuote.timeStamp;
var low = Math.Round(random.NextDouble() * (346.0 - 344.0) + 344.0, 3); // Low between 344 and 346
var high = Math.Round(random.NextDouble() * (347.0 - low) + low, 3); // High is between low and 347
var open = Math.Round(random.NextDouble() * (high - low) + low, 3); // Open is between low and high
var close = Math.Round(random.NextDouble() * (high - low) + low, 3); // Close is between low and high
newQuote.id = random.Next(100, 999);
newQuote.open = open;
newQuote.high = high;
newQuote.low = low;
newQuote.close = close;
newQuote.vol = random.Next(150000, 500000);
newQuote.time = new DateTime(quotesDate.Year, quotesDate.Month, quotesDate.Day,
currentTime.Hours, currentTime.Minutes, currentTime.Seconds).ToString("dd-MM-yyyy HH:mm");
}
else
{
// Adjust the current price within the existing price range if it's not a new interval.
double currentPrice = random.NextDouble() * ((quotesData[1].high + 0.5) - (quotesData[1].low - 0.5)) + (quotesData[1].low - 0.5);
newQuote.close = currentPrice;
newQuote.high = (currentPrice > latestQuote.high) ? currentPrice : latestQuote.high;
newQuote.low = (currentPrice < latestQuote.low) ? currentPrice : latestQuote.low;
newQuote.open = latestQuote.open;
newQuote.vol = latestQuote.vol;
newQuote.time = latestQuote.time;
}
// Convert the Quote object to a JSON string
string jsonString = JsonConvert.SerializeObject(newQuote, Formatting.Indented);
return jsonString;
}
Note: The process of updating the chart depends on how we receive the data from the API. If the API provides complete stock quote details for each desired time interval, the data can simply be added to the chart’s data source without modifying existing candles.
Customize Chart Settings to Enhance the Visuals
In this step, we will modify the settings of the chart to make it more informative and appealing. This includes setting the chart type and tooltip and customizing the axes. Refer to this documentation for more information about styling the chart elements.
Use the code below to customize chart settings:
// setting the chart type and tool tip
financialChartMain.ChartType = C1.Chart.Finance.FinancialChartType.Candlestick;
financialChartMain.ToolTip.Content = "Time: {time}hrs\nOpen: ${open}\nHigh: ${high}\nLow: ${low}\nClose: ${close}";
// setting the required X-axis properties
financialChartMain.AxisX.MajorGrid = true;
financialChartMain.AxisX.MajorGridStyle.StrokeColor = Color.FromArgb(60, Color.Gray);
financialChartMain.AxisX.MajorUnit = 1 / 96.0;
financialChartMain.AxisX.LabelAngle = 90;
financialChartMain.AxisX.Min = new DateTime(2024, 7, 21).Add(marketOpeningTime).AddMinutes(-5).ToOADate();
financialChartMain.AxisX.Max = new DateTime(2024, 7, 21).Add(marketClosingTime).AddMinutes(5).ToOADate();
// setting the required Y-axis properties
financialChartMain.AxisY.Position = C1.Chart.Position.Right;
financialChartMain.AxisY.Min = 340;
financialChartMain.AxisY.MajorGridStyle.StrokeColor = Color.FromArgb(60, Color.Gray);
We also assign the colors to the candlesticks based on their open-close values by handling the SymbolRendering event of the Financial chart. This color coding makes it easy to identify positive or negative price changes at a glance. Use the code below to assign colors:
// event handler to set the candle's color for the "CandleStick" chart type
private void ChartForm_SymbolRendering(object? sender, C1.Win.Chart.RenderSymbolEventArgs e)
{
if (financialChartMain.ChartType == C1.Chart.Finance.FinancialChartType.Candlestick && e.Item is Quote quoteItem)
{
if (quoteItem.open > quoteItem.close)
{
e.Engine.SetStroke(Brushes.Red);
e.Engine.SetFill(Brushes.Red);
}
else
{
e.Engine.SetStroke(Brushes.Green);
e.Engine.SetFill(Brushes.Green);
}
}
}
Now that we’ve successfully completed all the steps, the live financial chart is ready to view. It will appear as below:
Download the sample to follow along and try it yourself.
Conclusion
In this blog, we created a highly functional and visually engaging stock chart for a WinForms application using the ComponentOne FinancialChart control API. This API also supports various technical analysis tools, like indicators, overlays, and Fibonacci tools, enabling in-depth market evaluation and informed trading decisions.
Ready to try it out? Download ComponentOne Today!
You can experiment more with the FinancialChart control by referring to its official documentation.
If you have any inquiries, please leave them in the comments section below. Happy Coding!