DEV Community

Cover image for Stock Market Trends Analysis Using .NET MAUI Toolkit Candle Chart
Phinter Atieno for Syncfusion, Inc.

Posted on • Originally published at syncfusion.com on

Stock Market Trends Analysis Using .NET MAUI Toolkit Candle Chart

TL;DR: Let’s see how to analyze stock trends using the .NET MAUI Toolkit Candle Chart. We’ll fetch historical stock data, structure it, and visualize open, high, low, and close prices. The chart supports interactive features like zooming, panning, and real-time updates. We’ll enhance it with a secondary Y-axis, DateTime Range Selector, and Segmented Control for dynamic data exploration.

Welcome to our Chart of the Week blog series!

Understanding market trends is crucial in finance. Stock charts help us to visualize price movements and trends elegantly. Syncfusion® .NET MAUI Toolkit Cartesian Chart allows users to create clear, interactive stock charts. With the support for Line, Candle, and OHLC chart types, it helps us to identify patterns and trends over time.

Today, we’ll analyze Amazon’s stock prices using the .NET MAUI Toolkit Candle Chart to gain insights into market trends and price movements. The Candle Chart helps visualize open, high, low, and close (OHLC) values, making it easier to track price fluctuations and trading volume.

Refer to the following image.

Analyzing stock prices and market trends with .NET MAUI Toolkit Candle Chart

Step 1: Gather data

Before creating the chart, we need relevant data. For this blog, we’ll retrieve historical data for Amazon’s (AMZN) stock prices from Yahoo Finance.

Step 2: Preparing the data for the chart

Next, we need to structure the stock data properly. We’ll define a StockModel class containing essential properties such as Date, Open, High, Low, Close, and Volume.

Refer to the following code example.

public class StockModel
{
    public DateTime Date {get;}
    public double Open {get;}
    public double High {get;}
    public double Low {get;}
    public double Close {get;}
    public double Volume {get;}

    public StockModel(DateTime date, double open, double high, double low, double close, double volume)
    {
        Date = date;
        Open = open;
        High = high;
        Low = low;
        Close = close;
        Volume = volume;
    }
}

Enter fullscreen mode Exit fullscreen mode

To generate the data collection, we’ll use the StockViewModel class and StockPrices property. We’ll also create the ReadCSV method to convert the CSV data into a collection of stock data and store it in the StockPrices property.

The RangeStart and RangeEnd properties define the date range for the stock data.

Refer to the following code example.

public class StockViewModel : INotifyPropertyChanged
{
    private ObservableCollection<StockModel>? _stockPrices;
    private DateTime _rangeStart = new DateTime(2024, 1, 1);
    private DateTime _rangeEnd = new DateTime(2024, 6, 30);

    public DateTime RangeStart
    {
        get => _rangeStart;
        set
        {
            if (_rangeStart != value)
            {
                _rangeStart = value;
                OnPropertyChanged(nameof(RangeStart));
            }
        }
    }

    public DateTime RangeEnd
    {
        get => _rangeEnd;
        set
        {
            if (_rangeEnd != value)
            {
                _rangeEnd = value;
                OnPropertyChanged(nameof(RangeEnd));
            }
        }
    }

    public ObservableCollection<StockModel>? StockPrices
    {
        get => _stockPrices;
        set
        {
            if (_stockPrices != value)
            {
                _stockPrices = value;
                OnPropertyChanged(nameof(StockPrices));
            }
        }
    }

    public StockViewModel()
    {
        StockPrices = new ObservableCollection<StockModel>(
            ReadCSV("StockChart.Resources.Raw.amazon.csv").Reverse()
        );
    }

    public ObservableCollection<StockModel> ReadCSV(string resourceStream)
    {
        var executingAssembly = typeof(App).GetTypeInfo().Assembly;
        using var inputStream = executingAssembly.GetManifestResourceStream(resourceStream)
            ?? throw new FileNotFoundException($"Resource {resourceStream} not found.");

        var lines = new List<string>();
        using (var reader = new StreamReader(inputStream))
        {
            string? line;
            while ((line = reader.ReadLine())!= null)
            {
                lines.Add(line);
            }
        }

        var formats = new[] {"MM/dd/yyyy", "M/d/yyyy", "M/dd/yyyy", "MM/d/yyyy"};
        return new ObservableCollection<StockModel>(
            lines.Select(line =>
            {
                var data = line.Split(',');

                if (data.Length < 6) return null;

                if (DateTime.TryParseExact(data[0], formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
                {
                    return new StockModel(
                        date,
                        Convert.ToDouble(data[1]),
                        Convert.ToDouble(data[2]),
                        Convert.ToDouble(data[3]),
                        Convert.ToDouble(data[4]),
                        Convert.ToDouble(data[5]) / 1000000
                    );
                }

                return null;
            })
            .Where(stock => stock!= null)
            .ToList()!
        );
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler? PropertyChanged;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure the layout

In this demo, we’ll use the following three essential controls:

Refer to the following code example to design the layout to position these controls and enhance the UI.

XAML

<!-- Border for all controls -->
<Border Margin="15" StrokeThickness="2" Stroke="#79747E">
    <Border.StrokeShape>
        <RoundRectangle CornerRadius="10"/>
    </Border.StrokeShape>

    <Grid RowDefinitions="1.5*,6.5*,2*" ColumnDefinitions="*,*" Margin="10,-10,20,15">        
        --------
        --------
    </Grid>
</Border>

Enter fullscreen mode Exit fullscreen mode

Step 4: Configure the .NET MAUI Toolkit Cartesian Chart

Let’s configure the .NET MAUI Toolkit Cartesian Chart control by following the official documentation.

<charts:SfCartesianChart>
    <charts:SfCartesianChart.XAxes>
        <charts:DateTimeAxis />
    </charts:SfCartesianChart.XAxes>
    <charts:SfCartesianChart.YAxes>
        <charts:NumericalAxis />
    </charts:SfCartesianChart.YAxes>
</charts:SfCartesianChart>

Enter fullscreen mode Exit fullscreen mode

Step 5: Add a secondary Y-Axis to visualize the stock volume

Now, add a secondary Y-axis to the Cartesian Chart to highlight the volume of stock prices using a Column series.

Refer to the following code example.

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

Enter fullscreen mode Exit fullscreen mode

Step 6: Binding data to .NET MAUI Candle and Column Series

Next, we’ll bind the stock data to the Candle and Column Charts series. Before doing this, we need to set the ViewModel as the BindingContext for the ContentPage. This allows us to access the ViewModel across the entire page.

If we bind the ViewModel only to SfCartesianChart, we won’t be able to access it globally within the page, which is necessary for other controls.

Refer to the following code example.

<ContentPage.BindingContext>
    <local:StockViewModel x:Name="viewModel"></local:StockViewModel>
</ContentPage.BindingContext><chart:SfCartesianChart.Series>
    <chart:CandleSeries x:Name="stockseries"
                         ItemsSource="{Binding StockPrices}"
                         XBindingPath="Date"
                         Open="Open"
                         High="High"
                         Low="Low"
                         Close="Close"/>

    <chart:ColumnSeries x:Name="volumeSeries"
                         ItemsSource="{Binding StockPrices}"
                         XBindingPath="Date"
                         YBindingPath="Volume"/>
</chart:SfCartesianChart.Series>

Enter fullscreen mode Exit fullscreen mode

Step 7: Configure the secondary Y-axis

To properly configure the secondary Y-axis for the volume data, we must:

  • Specify a name to the axis using the Name property.
  • Set the CrossAt value.
  • Mention the axis name in the YAxisName property of the ColumnSeries.

Refer to the following code example.

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

    <chart:NumericalAxis Name="volumeAxis" 
                         CrossesAt="2023-11-08"
                         IsVisible="False">
    </chart:NumericalAxis>
</chart:SfCartesianChart.YAxes>

<chart:ColumnSeries x:Name="volumeSeries"
                     ItemsSource="{Binding StockPrices}"
                     XBindingPath="Date"
                     YBindingPath="Volume"
                     YAxisName="volumeAxis"/>

Enter fullscreen mode Exit fullscreen mode

Step 8: Add interactive features to the chart

To enhance the user experience, we can add interactive features like the trackball, which allows users to see precise data points when hovering over the chart.

Refer to the following code example.

<chart:SfCartesianChart.TrackballBehavior>
  <chart:ChartTrackballBehavior ShowMarkers="False"></chart:ChartTrackballBehavior>
</chart:SfCartesianChart.TrackballBehavior>

Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Integrating trackball into the chart to display additional details


Integrating trackball into the chart to display additional details

Step 9: Add the .NET MAUI DateTime Range Selector

Then, add the .NET MAUI DateTime Range Selector control to zoom in and select the specific date ranges in the chart.

Configure the .NET MAUI DateTime Range Selector

First, we need to configure the DateTimeRangeSelector in our ContentPage because this is a separate control from the Syncfusion .NET MAUI suite.

<sliders:SfDateTimeRangeSelector x:Name="Selector" 
                                  Grid.Row="2" 
                                  Grid.ColumnSpan="2"
                                  RangeStart="2024-01-01" 
                                  RangeEnd="2024-06-30"                                                                                    
                                  Minimum="2023-11-08" 
                                  Maximum="2025-02-07"
                                  IntervalType="Months"
                                  Interval="2"/>

Enter fullscreen mode Exit fullscreen mode

In the SfDateTimeRangeSelector, we specify the data range by setting the following properties:

Add the Cartesian Chart inside the DateTime Range Selector

Now, add the SfCartesianChart inside the DateTimeRangeSelector. The chart’s height and width depend on the SfDateTimeRangeSelector’s width and height.

<sliders:SfDateTimeRangeSelector x:Name="Selector">
    <chart:SfCartesianChart>

        <chart:SfCartesianChart.XAxes>
            <chart:DateTimeAxis IsVisible="False"
                                ShowMajorGridLines="False"/>
        </chart:SfCartesianChart.XAxes>

        <chart:SfCartesianChart.YAxes>
            <chart:NumericalAxis IsVisible="False"
                                 ShowMajorGridLines="False"/>
        </chart:SfCartesianChart.YAxes>

        <chart:SfCartesianChart.Series>
            <chart:SplineAreaSeries ItemsSource="{Binding StockPrices}"
                                     XBindingPath="Date"
                                     YBindingPath="Volume">
            </chart:SplineAreaSeries>
        </chart:SfCartesianChart.Series>

    </chart:SfCartesianChart>
</sliders:SfDateTimeRangeSelector>

Enter fullscreen mode Exit fullscreen mode

Step 10: Connect the DateTime Range Selector to the Candle Chart

In our case, we use the SfDateTimeRangeSelector to easily select a specific date range. To achieve this, we bind the RangeStart and RangeEnd properties of the SfDateTimeRangeSelector to the Minimum and Maximum values of the Candle Chart’s X-axis.

Refer to the following code example.

<Grid RowDefinitions="1.5*,6.5*,2*" ColumnDefinitions="*,*" Margin="10,-10,20,15">

    <chart:SfCartesianChart Grid.Row="1" Grid.ColumnSpan="2">
        <chart:SfCartesianChart.XAxes>
            <chart:DateTimeAxis x:Name="xAxis"           
                                 Minimum="{Binding RangeStart, Mode=TwoWay}"
                                 Maximum="{Binding RangeEnd, Mode=TwoWay}">
            </chart:DateTimeAxis>
        </chart:SfCartesianChart.XAxes>

        --------------
        --------------

    </chart:SfCartesianChart>

    <sliders:SfDateTimeRangeSelector x:Name="Selector"  
                                      Grid.Row="2"  
                                      Grid.ColumnSpan="2"
                                      RangeStart="{Binding RangeStart, Mode=TwoWay}" 
                                      RangeEnd="{Binding RangeEnd, Mode=TwoWay}"                                                                                    
                                      Minimum="2023-11-08" 
                                      Maximum="2025-02-07"
                                      IntervalType="Months"
                                      Interval="2"/>
</Grid>

Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Adding DateTime Range Selector to the .NET MAUI Candle Chart


Adding DateTime Range Selector to the .NET MAUI Candle Chart

Step 12: Add Segmented Control to customize ranges

We use the Segmented Control to switch between different time ranges, such as 3 months, 6 months, 9 months, 1 year, and all data for Amazon’s stock from November 2023 to January 2025. This allows users to visualize the stock price data based on the selected range.

Refer to the following code example.

<HorizontalStackLayout Grid.Row="0" 
                        Grid.Column="1" 
                        HorizontalOptions="End" 
                        VerticalOptions="End" 
                        Spacing="5">

    <segmentedControl:SfSegmentedControl x:Name="segmentedControl">

        <segmentedControl:SfSegmentedControl.ItemsSource>
            <x:Array Type="{x:Type x:String}">
                <x:String>3m</x:String>
                <x:String>6m</x:String>
                <x:String>9m</x:String>
                <x:String>1y</x:String>
                <x:String>All</x:String>
            </x:Array>
        </segmentedControl:SfSegmentedControl.ItemsSource>

    </segmentedControl:SfSegmentedControl>

</HorizontalStackLayout>

Enter fullscreen mode Exit fullscreen mode

Refer to the following image.

Adding Segmented Control to design time ranges


Adding Segmented Control to design time ranges

Step 13: Customize the appearance of the Charts and other elements

Let’s customize the appearance of the .NET MAUI Column and Candle Charts and the DateTime Range Selector to enhance their readability .

Customizing the chart title

To customize the chart title, refer to the following code example.

<Grid>    
    <HorizontalStackLayout Grid.Row="0" 
                            Grid.Column="0" 
                            Margin="{ OnPlatform iOS='0,0,2,40', Android='0,0,2,40', Default='-20,0,0,0'}">

        <Image Source="stockchart.png" 
               WidthRequest="{OnPlatform Android=60, Default=90, iOS=60}" 
               HeightRequest="{OnPlatform Android=60, Default=90, iOS=60}"/>

        <VerticalStackLayout VerticalOptions="Center">
            <Label Text="{OnPlatform Default='Analyzing Stock Price Trends and Market Movements',
                                  Android='Analysis Stock Trends and Market Movements',
                                  iOS='Analysis Stock Trends and Market Movements'}"
                   FontSize="{OnPlatform Default=22, Android=14, iOS=14}"
                   VerticalOptions="Center"
                   FontAttributes="Bold"
                   TextColor="Gray"/>

            <Label Text="Tracking Growth, Risks, and Investment Opportunities"
                   LineBreakMode="WordWrap"
                   HorizontalOptions="Start"
                   FontSize="{OnPlatform Default=16, Android=11, iOS=11}"
                   VerticalOptions="Center"
                   TextColor="Gray"/>
        </VerticalStackLayout>

    </HorizontalStackLayout>

    ………….
    ………….

</Grid>

Enter fullscreen mode Exit fullscreen mode

Customizing the Candle Series

To customize the appearance of the CandleStick series, we’ll set the following properties:

Refer to the following code example.

<chart:CandleSeries x:Name="stockseries"
                     ItemsSource="{Binding StockPrices}"
                     XBindingPath="Date"
                     Open="Open"
                     High="High"
                     Low="Low"
                     Close="Close"
                     EnableSolidCandle="True"
                     BearishFill="Red"
                     BullishFill="Green"
                     ShowTrackballLabel="True"
                     TrackballLabelTemplate="{StaticResource trackballLabelTemplate}"/>

Enter fullscreen mode Exit fullscreen mode

Let’s customize the chart trackball using the TrackballTemplate property. This allows us to change the layout, colors, fonts and displayed data within the trackball.

Refer to the following code example.

<chart:SfCartesianChart Grid.Row="1" Grid.ColumnSpan="2">

    <chart:SfCartesianChart.Resources>   
        <DataTemplate x:Key="trackballLabelTemplate">
            <VerticalStackLayout Spacing="6" Background="White" Padding="4" Margin="-2.5,-2,-2,-2">

                <HorizontalStackLayout Spacing="8">
                    <Label Text="Open:" FontSize="12" FontAttributes="Bold" TextColor="DarkGray"/>
                    <Label Text="{Binding DataItem.Open, StringFormat=' ${0}'}" FontSize="12" TextColor="Black"/>
                </HorizontalStackLayout>

                <HorizontalStackLayout Spacing="8">
                    <Label Text="Close:" FontSize="12" FontAttributes="Bold" TextColor="DarkGray"/>
                    <Label Text="{Binding DataItem.Close, StringFormat=' ${0}'}" FontSize="12" TextColor="Black"/>
                </HorizontalStackLayout>

                <HorizontalStackLayout Spacing="8">
                    <Label Text="High:" FontSize="12" FontAttributes="Bold" TextColor="DarkGray"/>
                    <Label Text="{Binding DataItem.High, StringFormat=' ${0}'}" FontSize="12" TextColor="Green"/>
                </HorizontalStackLayout>

                <HorizontalStackLayout Spacing="8">
                    <Label Text="Low:" FontSize="12" FontAttributes="Bold" TextColor="DarkGray"/>
                    <Label Text="{Binding DataItem.Low, StringFormat=' ${0}'}" FontSize="12" TextColor="Red"/>
                </HorizontalStackLayout>

                <HorizontalStackLayout Spacing="8">
                    <Label Text="Volume:" FontSize="12" FontAttributes="Bold" TextColor="DarkGray"/>
                    <Label Text="{Binding DataItem.Volume, StringFormat='{0:F2}M'}" FontSize="12" TextColor="Blue"/>
                </HorizontalStackLayout>

            </VerticalStackLayout>
        </DataTemplate>
    </chart:SfCartesianChart.Resources>

    ……………
    ……………
    ……………

</chart:SfCartesianChart>

Enter fullscreen mode Exit fullscreen mode

Customizing the Column series

To customize the appearance of the ColumnSeries, let’s configure the following properties:

Refer to the following code example.

<chart:ColumnSeries x:Name="volumeSeries"
                     ItemsSource="{Binding StockPrices}"
                     XBindingPath="Date"
                     YBindingPath="Volume"
                     ShowTrackballLabel="False"
                     Fill="#512BD4"
                     YAxisName="volumeAxis"/>

Enter fullscreen mode Exit fullscreen mode

Customizing the chart Axis

Then, customize the X- and Y-axes by setting the following properties:

Refer to the following code example.

<chart:SfCartesianChart Grid.Row="1" Grid.ColumnSpan="2" EnableSideBySideSeriesPlacement="False">

    <chart:SfCartesianChart.XAxes>
        <chart:DateTimeAxis x:Name="xAxis"           
                            Minimum="{Binding RangeStart, Mode=TwoWay}"
                            Maximum="{Binding RangeEnd, Mode=TwoWay}"
                            ShowTrackballLabel="True"
                            ShowMajorGridLines="False"
                            LabelCreated="Primary_LabelCreated">
        </chart:DateTimeAxis>
    </chart:SfCartesianChart.XAxes>

    <chart:SfCartesianChart.YAxes>
        <chart:NumericalAxis x:Name="yAxis"
                             Minimum="80"
                             Interval="20"
                             ShowMajorGridLines="False" 
                             ShowMinorGridLines="False"
                             EdgeLabelsDrawingMode="Shift">
            <chart:NumericalAxis.LabelStyle>
                <chart:ChartAxisLabelStyle LabelFormat="$0"/>
            </chart:NumericalAxis.LabelStyle>
            <chart:NumericalAxis.Title>
                <chart:ChartAxisTitle Text="Stock Prices">
                </chart:ChartAxisTitle>
            </chart:NumericalAxis.Title>
        </chart:NumericalAxis>

        <chart:NumericalAxis Name="volumeAxis" 
                             Minimum="0"
                             Maximum="500" 
                             Interval="50" 
                             IsVisible="False"
                             EdgeLabelsDrawingMode="Shift"
                             ShowMajorGridLines="False" 
                             ShowMinorGridLines="False"
                             CrossesAt="2023-11-08">
        </chart:NumericalAxis>
    </chart:SfCartesianChart.YAxes>

    ------------
    ------------

</chart:SfCartesianChart>

Enter fullscreen mode Exit fullscreen mode

Customizing the DateTime Range Selector

To enhance the appearance of the DateTime Range Selector, configure its properties as follows:

<sliders:SfDateTimeRangeSelector x:Name="Selector" 
                                  Grid.Row="2" 
                                  Grid.ColumnSpan="2"
                                  RangeStart="{Binding RangeStart, Mode=TwoWay}" 
                                  RangeEnd="{Binding RangeEnd, Mode=TwoWay}"                                                                                    
                                  Margin="50,0,-25,0" 
                                  Minimum="2023-11-08" 
                                  Maximum="2025-02-07"
                                  DateFormat="MMM yyyy"
                                  IntervalType="Months"
                                  Interval="2"
                                  ShowLabels="True"
                                  ShowTicks="True"
                                  LabelsPlacement="OnTicks"
                                  EdgeLabelsPlacement="Inside"
                                  DragBehavior="Both" 
                                  EnableDeferredUpdate="False">

    <sliders:SfDateTimeRangeSelector.TrackStyle>
        <sliders:SliderTrackStyle ActiveFill="#512BD4"
                                  InactiveFill="#DFD8F7" />
    </sliders:SfDateTimeRangeSelector.TrackStyle>

    <sliders:SfDateTimeRangeSelector.Tooltip>
        <sliders:SliderTooltip DateFormat="MMMM dd, yyyy" 
                                Fill="#DFD8F7"
                                Stroke="#512BD4"
                                StrokeThickness="2"
                                TextColor="#512BD4"
                                FontSize="{OnPlatform Default=14,Android=10,iOS=10}"
                                FontAttributes="Bold"
                                Padding="{OnPlatform Default='12,12',Android='5,5',iOS='5,5'}"/>
    </sliders:SfDateTimeRangeSelector.Tooltip>

    <sliders:SfDateTimeRangeSelector.ThumbStyle>
        <sliders:SliderThumbStyle StrokeThickness="2" 
                                   Stroke="#512BD4" 
                                   Fill="#DFD8F7" />
    </sliders:SfDateTimeRangeSelector.ThumbStyle>

</sliders:SfDateTimeRangeSelector>

Enter fullscreen mode Exit fullscreen mode

Customizing the Segmented Control

To customize the appearance of the Segmented Control, set the following properties:

Refer to the following code example.

<segmentedControl:SfSegmentedControl x:Name="segmentedControl" 
                                      SegmentWidth="{OnPlatform Android=30, iOS=30, Default=100}" 
                                      WidthRequest="{OnPlatform WinUI=500, MacCatalyst=500}" 
                                      Stroke="#FF116DF9"    
                                      StrokeThickness="2"  
                                      CornerRadius="8" 
                                      SelectionChanged="SfSegmentedControl_SelectionChanged">

    <segmentedControl:SfSegmentedControl.SelectionIndicatorSettings>
        <segmentedControl:SelectionIndicatorSettings SelectionIndicatorPlacement="Fill"/>
    </segmentedControl:SfSegmentedControl.SelectionIndicatorSettings>

    -------------------
    -------------------

</segmentedControl:SfSegmentedControl>

Enter fullscreen mode Exit fullscreen mode

These customizations enhance the readability and visual appeal of your charts and controls, making the data easier to interpret and analyze.

After executing these code examples, we’ll get the output that resembles the following image.

Analyzing stock prices and market trends using .NET MAUI Toolkit Candle Chart


Analyzing stock prices and market trends using .NET MAUI Toolkit Candle Chart

GitHub repository

For more details, refer to stock price analysis using the .NET MAUI Toolkit Candle Chart GitHub demo.

Conclusion

Thank you for reading! In this blog, we’ve explored how to analyze prices and market trends using the Syncfusion® .NET MAUI Toolkit Candle Chart. We encourage you to follow the steps outlined in this blog. Stay tuned for next week’s Chart of the Week!

If you require assistance, please don’t hesitate to contact us via our support forum, support portal, or feedback portal. We are always eager to help you!

Related Blogs

Retry later

Top comments (0)

Retry later
Retry later