Chart of the Week: Forecasting Stock Trends with AI-Powered Smart .NET MAUI Candle Chart

Jollen Moyani - Aug 28 - - Dev Community

TL;DR: Discover how to create an AI-powered .NET MAUI Candle Chart that predicts stock trends for the future. This guide walks you through visualizing historical data, integrating Azure OpenAI for predictive analytics, and displaying forecasted stock values on a chart. Empower your trading decisions with insights into future market movements.

Welcome back to the Chart of the Week blog series!

In the fast-paced world of stock trading, staying ahead of market trends is crucial.

Imagine tracking a stock’s performance over time. You have historical data, but what about the future trends? This is where AI becomes a game-changer. By analyzing historical stock data, AI can predict future movements, giving you an edge in making informed trading decisions.

These predictions can be visualized directly on a candle chart, providing a seamless visualization of past and future trends. Remember, the quality of your AI prompts directly influences the accuracy of the predictions.

In this blog, we’ll explore how to create an AI-powered smart .NET MAUI Candle Chart that forecasts stock data for the next 45 days, empowering traders to make data-driven decisions confidently.

Benefits of the AI-Powered Candle Chart

  • Informed trading decisions: Predict future stock prices to make more informed buying or selling decisions.
  • Visual insight: A combined view of historical and forecasted data offers a comprehensive understanding of market trends.
  • Strategic advantage: With insights into potential price movements, you can better plan trades and minimize risks.

Implementing the solution

To bring this concept to life, we’ve created a .NET MAUI app that integrates predictive analytics into a candle chart. Here’s how we did it:

Step 1: Visualizing historical stock data using the .NET MAUI Candle Chart

We start by displaying the historical stock data using a candle series chart. This type of chart is beneficial for financial data, showing the open, high, low, and close prices for each period.

Gathering data for the chart

Let’s first gather the stock details. Then, refer to the following code example to design a business model to hold High, Low, Open, and Close values of the stocks with the respective dates.

public class DataModel
{
    public double High { get; set; }
    public double Low { get; set; }
    public DateTime Date { get; set; }
    public double Open { get; set; }
    public double Close { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Populating the chart data

Next, we define a ViewModel class to encapsulate the stock data in a collection for easy binding to the chart.

public class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<DataModel> stockData;
    public ObservableCollection<DataModel> StockData {
       get { return stockData; }
       set
       {
           stockData = value;
           OnPropertyChanged(nameof(StockData));
       }
    }


    public const string msftStockSource = "6/28/2024\t453.07\t455.38\t446.41\t446.95\t28,362,270\r\n6/27/2024\t452.18\t456.17\t451.77\t452.85\t14,806,320\r\n6/26/20..."; // Complete data from CSV file downloaded.

    public event PropertyChangedEventHandler? PropertyChanged;

    public ViewModel()
    {
        stockData = GenerateColection(msftStockSource);
    }

    private ObservableCollection<DataModel> GenerateColection(string dataSource)
    {
        string[] alphapetRows = dataSource.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
        ObservableCollection<DataModel> alphabetCollection = new ObservableCollection<DataModel>();

        foreach (string row in alphapetRows)
        {
            string[] columns = row.Split('\t');
            DataModel item = new DataModel(GetDateTime(columns[0]), GetDouble(columns[2]), GetDouble(columns[3]), GetDouble(columns[1]), GetDouble(columns[4]));
            alphabetCollection.Add(item);
        }

        return alphabetCollection;
    }
}
Enter fullscreen mode Exit fullscreen mode

Configuring .NET MAUI Candle Chart

Let’s design and configure the Syncfusion .NET MAUI Candle Chart. Then, we’ll customize the axis range, color palettes, and other elements for enhanced readability and visualization. Remember to set the chart’s binding context.

Refer to the following code example.

<ContentPage.BindingContext>
 <local:ViewModel x:Name="viewModel"/>
</ContentPage.BindingContext>
. . . 
<chart:SfCartesianChart x:Name="chart" >

 <chart:SfCartesianChart.XAxes>

  <chart:DateTimeAxis x:Name="dateTime" LabelExtent="30" IntervalType="Months" ShowMajorGridLines="False" EdgeLabelsDrawingMode="Center" Interval="1" EdgeLabelsVisibilityMode="Default" IsVisible="{OnPlatform WinUI=True,Android=False}">

   <chart:DateTimeAxis.LabelStyle>
    <chart:ChartAxisLabelStyle FontSize="12.49" LabelFormat="MMM dd" />
   </chart:DateTimeAxis.LabelStyle>

   <chart:DateTimeAxis.MajorTickStyle>
    <chart:ChartAxisTickStyle StrokeWidth="0"/>
   </chart:DateTimeAxis.MajorTickStyle>

   <chart:DateTimeAxis.AxisLineStyle>
    <chart:ChartLineStyle StrokeWidth="0"/>
   </chart:DateTimeAxis.AxisLineStyle>
 </chart:SfCartesianChart.XAxes>

 <chart:SfCartesianChart.YAxes>

   <chart:NumericalAxis EdgeLabelsVisibilityMode="AlwaysVisible" PlotOffsetEnd="20" EdgeLabelsDrawingMode="Center">

    <chart:NumericalAxis.LabelStyle>
     <chart:ChartAxisLabelStyle FontSize="12.49" />
    </chart:NumericalAxis.LabelStyle>

    <chart:NumericalAxis.MajorGridLineStyle>
     <chart:ChartLineStyle StrokeWidth="0.2"/>
    </chart:NumericalAxis.MajorGridLineStyle>

   </chart:NumericalAxis>
 </chart:SfCartesianChart.YAxes>

 <chart:CandleSeries x:Name="candleSeries" ItemsSource="{Binding StockData}"                                      
                      XBindingPath="Date"
                      Open="Open"
                      High="High"    
                      Low="Low"
                      Close="Close"      
                      EnableTooltip="True"
                      BearishFill="#FF4E4E"
                      BullishFill="#25E739"
                      EnableAnimation="False"/>

 <chart:CandleSeries x:Name="forecastCandle" 
                      ItemsSource="{Binding ForeCastData}"                                      
                      XBindingPath="Date"
                      Open="Open"
                      High="High"    
                      EnableTooltip="True"          
                      Low="Low"
                      Close="Close"   
                      Opacity="0.75"                
                      BearishFill="#FF4E4E"
                      BullishFill="#25E739"
                      EnableAnimation="False"/>

 <chart:SfCartesianChart.ZoomPanBehavior>
   <chart:ChartZoomPanBehavior EnablePanning="True" EnableDirectionalZooming="False" EnableDoubleTap="False" EnablePinchZooming="False"/>
 </chart:SfCartesianChart.ZoomPanBehavior>

</chart:SfCartesianChart>
Enter fullscreen mode Exit fullscreen mode

Adding buttons using .NET MAUI Segmented Control

We’ve added three buttons (3 Month, 6 Month, and 1 Year) to filter data. To design these buttons, we’ve used the .NET MAUI Segmented Control. Refer to the following code example.

xmlns:buttons="clr-namespace:Syncfusion.Maui.Buttons;assembly=Syncfusion.Maui.Buttons" 
. . . 
<buttons:SfSegmentedControl x:Name="DaysegmentedControl" SegmentCornerRadius="0" CornerRadius="2" SelectedIndex="0" SegmentWidth="80" SelectionChanged="DaysegmentedControl_SelectionChanged">
  <buttons:SfSegmentedControl.ItemsSource>
    <x:Array Type="{x:Type x:String}">
      <x:String>3 Month</x:String>
      <x:String>6 Month</x:String>
      <x:String>1 Year</x:String>
    </x:Array>
  </buttons:SfSegmentedControl.ItemsSource>

  <buttons:SfSegmentedControl.SegmentTemplate>
   <DataTemplate>
    <Label Text="{Binding Text}" Grid.Row="0" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" FontSize="14" />
   </DataTemplate>
  </buttons:SfSegmentedControl.SegmentTemplate>

  <buttons:SfSegmentedControl.SelectionIndicatorSettings>
   <buttons:SelectionIndicatorSettings SelectionIndicatorPlacement="Fill" Background="#F7F2FB" TextColor="#374151"/>
  </buttons:SfSegmentedControl.SelectionIndicatorSettings>
</buttons:SfSegmentedControl>

<buttons:SfButton StrokeThickness="0" x:Name="aiButton" CornerRadius="5" Text="AI" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"
HorizontalOptions="End" Clicked="AIButtonClicked" FontSize="20" WidthRequest="30" HeightRequest="30" Loaded="image_Loaded" ToolTipProperties.Text="Forecaste future stocks" BackgroundColor="#F7F2FB" />
Enter fullscreen mode Exit fullscreen mode

Now, the chart will look like the following image, displaying the last three months of stock details.

Visualizing historical stock data using the .NET MAUI Candle Chart

Visualizing historical stock data using the .NET MAUI Candle Chart

Step 2: AI-driven stock prediction

Here, we’ll use Azure OpenAI to analyze historical data and predict the stock’s performance for the next 45 days. The AI model identifies patterns in the data and uses them to forecast future prices, offering insights into potential market movements.

Let’s look at how we can achieve these results.

Integrating Azure OpenAI with your .NET MAUI app

First, ensure you have access to Azure OpenAI and have created a deployment in the Azure portal. You can find the Azure.AI.OpenAI NuGet package from the NuGet Gallery.

Once you get your key and endpoint, follow these steps.

Setting up Azure OpenAI

We’ll assume using the GPT-35 model deployed under the name GPT35Turbo. Let’s start creating OpenAIService.

internal class AzureOpenAIService
{
    const string endpoint = "https://{YOUR_END_POINT}.openai.azure.com";
    const string deploymentName = "GPT35Turbo";

    string key = "";

    public AzureOpenAIService(string key)
    {
        this. Key = key;
    }
}
Enter fullscreen mode Exit fullscreen mode
Connecting to Azure OpenAI

In our .NET MAUI app, we should set up a connection to the Azure OpenAI service, as shown in the following code example.

//At the time of required.
var client = new OpenAIClient(new Uri(endpoint), new AzureKeyCredential(key)); 
Enter fullscreen mode Exit fullscreen mode
Build a prompt and trigger the AI service

Let’s build a prompt and trigger the AI service with a button click.

private async void AIButtonClicked(object sender, EventArgs e)
{
   AzureOpenAIService service = new AzureOpenAIService();
   var data = viewModel.StockData;
   var last10Items = data.Skip(Math.Max(0, data.Count - 10)).Take(10).ToList();

   //Build a prompt for AI. 
   var prompt = service.GeneratePrompt(last10Items);
   var value = await service.GetAnswerFromGPT(prompt, viewModel, selected_Index);

   //Update forecast value to chart.
   await viewModel.AddRangeWithDelayAsync(value, 300);  
}

. . . 
//AI service.

internal string GeneratePrompt(List<DataModel> historicalData)
{
    var prompt = "Predicted OHLC values for the next 45 time step(s) for the following data:\n";
    foreach (var data in historicalData)
    {
        prompt += $"{data.Date:yyyy-MM-dd}: {data.High}, {data.Low}, {data.Open}, {data.Close}\n";
    }
    prompt += "and the prediction output data should be in the yyyy-MM-dd:High:Low:Open:Close, no other explanation required\n";
    return prompt;
}

. . .
public Task<ObservableCollection<DataModel>> GetAnswerFromGPT(string userPrompt, ViewModel viewModel, int index)
{
    var client = new OpenAIClient(new Uri(endpoint), new AzureKeyCredential(key));

    var chatCompletionsOptions = new ChatCompletionsOptions
    {
        DeploymentName = deploymentName,
        Temperature = (float)0.5,
        MaxTokens = 800,
        NucleusSamplingFactor = (float)0.95,
        FrequencyPenalty = 0,
        PresencePenalty = 0,
    };

    // Add the system message and user message to the options.
    chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(userPrompt));
    try
    {
        var response = client.GetChatCompletionsAsync(chatCompletionsOptions);
        var content = response.Value.Choices[0].Message.Content;
    }
    catch (Exception)
    {
        return Task.FromResult(viewModel.GenerateDataSource(index));
    }
}
Enter fullscreen mode Exit fullscreen mode

Displaying forecasted data

Then, display the predicted stock values for the next 45 days on the .NET MAUI Candle Chart. This will allow users to see historical and forecasted data in a single view.

The forecasted data is visually distinct and uses a plot band of the date-time axis, making it easy to differentiate from past performance. Refer to the following code example.

<chart:DateTimeAxis x:Name="dateTime">
 . . .
 <chart:DateTimeAxis.PlotBands>
  <chart:DateTimePlotBandCollection>
   <chart:DateTimePlotBand Start="2024-06-29" Fill="{AppThemeBinding Light='#66E7E0EC', Dark='#6636323B'}"/>
  </chart:DateTimePlotBandCollection>
 </chart:DateTimeAxis.PlotBands>
</chart:DateTimeAxis>
Enter fullscreen mode Exit fullscreen mode

Refer to the following output image.

Forecasting stock trends with the AI-powered smart .NET MAUI Candle Chart

Forecasting stock trends with the AI-powered smart .NET MAUI Candle Chart

GitHub reference

For more details, refer to the forecasting stock trends with AI-powered smart .NET MAUI Candle Chart GitHub demo.

Conclusion

Thanks for reading! In this blog, we’ve seen how to forecast stock trends using the AI-powered smart .NET MAUI Candle Chart. We have also learned to integrate Azure OpenAI for robust predictive analytics to drive intelligent trading decisions. Try out the steps in this blog and leave your feedback in the comments section below!

If you need assistance, please do not hesitate to contact us via our support forum, support portal, or feedback portal. We are always eager to help you!

Related blogs

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .