DEV Community

Cover image for How to Build Real-Time WinForms Financial Charts and Graphs
Chelsea Devereaux for MESCIUS inc.

Posted on • Originally published at developer.mescius.com

How to Build Real-Time WinForms Financial Charts and Graphs

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 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.

1.Open Visual Studio and select File | New | Project to create a new Windows Forms App.

Configure

2.Right-click on the project in Solution Explorer and click on Manage NuGet Packages… from the context menu.

Manage

3.Search for the C1.Win.FinancialChart package in the NuGet Package Manager and click on Install. This adds the FinancialChart control to the toolbox.

Package

4.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:

FinancialChart

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);
        }
    }
Enter fullscreen mode Exit fullscreen mode

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;
    }
Enter fullscreen mode Exit fullscreen mode

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;
    }
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
Enter fullscreen mode Exit fullscreen mode

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;
    }
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

Now that we’ve successfully completed all the steps, the live financial chart is ready to view. It will appear as below:

Live Chart

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.

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!

Top comments (0)