TL;DR: Explore how to visualize S&P 500 returns after interest rate cuts using a Risk Heat Map with Flutter 100% Stacked Bar Chart. This blog demonstrates how to effectively deploy color coding, custom renderers, and axis configurations to craft a dynamic and interactive heat map, making historical market performance intelligible at a glance.
Welcome to our Chart of the Week blog series!
Charts offer a powerful means to represent complex datasets visually, aiding in the understanding and interpretation of information. In industries like business, education, and research, charts play a crucial role in transforming raw data into visually compelling graphics, uncovering trends that may otherwise go unnoticed.
In this blog, we’ll walk you through creating a risk heat map using the Syncfusion Flutter 100% Stacked Bar Chart to showcase S&P 500 returns after interest rate cuts over time.
The StackedBar100Series displays data as horizontal bars, where each bar represents the percentage distribution of categories within a whole.
Here, we use this chart to create a risk heat map that highlights the variations in S&P 500 returns after interest rate cuts over different time intervals—3 months, 6 months, and 1 year. By applying color coding and percentages, the chart effectively reveals the stock market’s relative performance over decades.
Color plays an essential role in creating a risk heatmap effect. By applying conditional color coding, we can visually differentiate various risk levels, such as low, medium, and high risk, making it easier to identify and assess patterns or critical points at a glance.
In this visualization, we use the pointColorMapper property to assign specific colors to data points based on their values or predefined conditions. This color mapping technique adds a layer of visual meaning, allowing for quick interpretation of risk levels. Using color this way makes the information more intuitive, enhancing clarity and making it easier to understand.
Axes play an essential role in enhancing a chart’s readability. By configuring the axes and their labels, we ensure that the data is presented in an organized and comprehensible manner.
In this visualization, we configure the y-axis with multi-level labels to represent the time frames: 3 months, 6 months, and 1 year after the rate cut. These multi-level labels help organize the data into meaningful intervals, making it easier for viewers to interpret the data in terms of time. By adjusting the y-axis this way, the chart clearly shows how S&P 500 returns evolved over different periods following the rate cut.
The onCreateRenderer callback allows you to assign a custom series renderer to render unique shapes for each segment by creating a custom renderer class, such as _HeatmapSeriesRenderer, which extends StackedBar100SeriesRenderer.
In this custom renderer, the key methods include populateDataSource, which efficiently converts and updates raw data into a structured format for dynamic and interactive chart rendering. The custom series allows you to create unique visual effects for your chart by extending the default chart series behavior.
Here, we’ll create risk heat maps to visualize the S&P 500 returns after a rate cut. By using a custom series renderer, we can ensure equal height for each stacked bar to create the heat map effect. This involves adjusting the fixed position and dimensions.
Refer to the following image.
Before proceeding, refer to our previous blog on how to create an interactive heat map using Flutter Charts.
Let’s get started!
Collect data on the S&P 500 performance following interest rate cuts over the last five decades from S&P 500-rate-cuts. Organize this information into a list that includes the year of the first-rate cut and the returns for 3 months, 6 months, and 1 year after the rate cut.
Now, create a _HeatMapData model that contains details of the 3 months after the first rate cut, 6 months after the first rate cut, one year after the first rate cut, and the year when the data was recorded.
Refer to the following code example.
class _HeatMapData { _HeatMapData(this.year, this.threeMonths, this.sixMonths, this.oneYear); final DateTime year; final num threeMonths; final num sixMonths; final num oneYear; }
Next, create a list to hold the S&P 500 returns after interest rate cuts data and the year the data was recorded, which will be the source of the Flutter Chart.
Refer to the following code example.
List <_HeatMapData>? _heatMapData; @override void initState() { _heatMapData = <_HeatMapData>[ _HeatMapData(DateTime(1973), -10.2, -6.2, -36.0), _HeatMapData(DateTime(1974), -14.7, -15.3, 7.5), _HeatMapData(DateTime(1980), 15.0, 28.9, 30.3), _HeatMapData(DateTime(1981), -11.0, -7.9, -17.8), _HeatMapData(DateTime(1982), -4.8, 17.4, 36.5), _HeatMapData(DateTime(1987), 0.1, 1.7, 7.5), _HeatMapData(DateTime(1989), 7.4, 7.5, 11.9), _HeatMapData(DateTime(1995), 10, 5.1, 13.4), _HeatMapData(DateTime(1998), 17.2, 26.5, 27.3), _HeatMapData(DateTime(2001), -16.3, -12.4, -14.9), _HeatMapData(DateTime(2007), -4.4, -11.8, -27.2), _HeatMapData(DateTime(2019), 3.8, 13.3, 14.5), ]; super.initState(); }
To create a risk heat map, we’ll use three StackedBar100Series to visualize S&P 500 returns following a rate cut. The DateTimeCategoryAxis on the X-axis displays non-linear date intervals, while the NumericAxis on the Y-axis shows descriptive time intervals (3 months, 6 months, and 1 year) after rate cuts. This axis configuration will be explained later.
Refer to the following code example.
SfCartesianChart _buildHeatmapChart() { return SfCartesianChart( primaryXAxis: DateTimeCategoryAxis(), primaryYAxis: NumericAxis(), series: _buildHeatmapSeries(), ); } List<CartesianSeries<_HeatMapData, DateTime>> _buildHeatmapSeries() { return <CartesianSeries<_HeatMapData, DateTime>>[ for (int i = 0; i < 3; i++) StackedBar100Series<_HeatMapData, DateTime>( dataSource: _heatMapData!, xValueMapper: (_HeatMapData data, int index) => data.year, yValueMapper: (_HeatMapData data, int index) => _yValue(data, i), name: _seriesName(i), ), ]; } num _yValue(_HeatMapData data, int seriesIndex) { switch (seriesIndex) { case 0: return data.threeMonths; case 1: return data.sixMonths; case 2: return data.oneYear; default: return 0; } } String _seriesName(int seriesIndex) { switch (seriesIndex) { case 0: return '3 Months After First Rate Cut'; case 1: return '6 Months After First Rate Cut'; case 2: return '1 Year After First Rate Cut'; default: return ''; } }
Refer to the following image.
The onCreateRenderer callback enables the creation of a custom heat map series renderer, allowing you to render unique shapes for the segments. To achieve this, we’ll define a custom renderer class, such as _HeatmapSeriesRenderer, that extends the StackedBar100SeriesRenderer class.
Refer to the following code example.
List<CartesianSeries<_HeatMapData, DateTime>> _buildHeatmapSeries() { return <CartesianSeries<_HeatMapData, DateTime>>[ for (int i = 0; i < 3; i++) StackedBar100Series<_HeatMapData, DateTime>( dataSource: _heatMapData!, xValueMapper: (_HeatMapData data, int index) => data.year, yValueMapper: (_HeatMapData data, int index) => _yValue(data, i), animationDuration: 0, name: _seriesName(i), onCreateRenderer: (ChartSeries<_HeatMapData, DateTime> series) { return _HeatmapSeriesRenderer(); }, ), ]; }
The heat map visualization needs to maintain a consistent value range to ensure proper color representation.
class _HeatmapSeriesRenderer extends StackedBar100SeriesRenderer<_HeatMapData, DateTime> { _HeatmapSeriesRenderer(); @override void populateDataSource( [List<ChartValueMapper<_HeatMapData, num>>? yPaths, List<List<num>>? chaoticYLists, List<List<num>>? yLists, List<ChartValueMapper<_HeatMapData, Object>>? fPaths, List<List<Object?>>? chaoticFLists, List<List<Object?>>? fLists]) { super.populateDataSource( yPaths,
chaoticYLists,
yLists,
fPaths,
chaoticFLists,
fLists
); // Always keep positive 0 to 100 range even set negative value. yMin = 0; yMax = 100; // Calculate heatmap segment top and bottom values. _computeHeatMapValues(); }
We calculate the top and bottom values for each segment using the _computeHeatMapValues() method, ensuring they are evenly distributed, which makes the chart visually balanced.
The height of each segment is determined using the formula yValue = 100 / seriesLength, where seriesLength is the number of series dependent on the y-axis. For each series, we skip those that are not visible or have no data, then assign the bottom value based on its position and set the top value as stackValue + yValue.
Finally, we loop through the data points and apply these values, ensuring all segments maintain a consistent height across the heatmap.
Refer to the following code example.
void _computeHeatMapValues() { if (xAxis == null || yAxis == null) { return; } if (yAxis!.dependents.isEmpty) { return; } // Get the number of series dependent on the yAxis. final int seriesLength = yAxis!.dependents.length; // Calculate the proportional height for each series // (as a percentage of the total height). final num yValue = 100 / seriesLength; // Loop through each dependent series to calculate top and bottom values // for the heatmap. for (int i = 0; i < seriesLength; i++) { // Check if the current series is a '_HeatmapSeriesRenderer'. if (yAxis!.dependents[i] is _HeatmapSeriesRenderer) { final _HeatmapSeriesRenderer current = yAxis!.dependents[i] as _HeatmapSeriesRenderer; // Skip processing if the series is not visible or has no data. if (!current.controller.isVisible || current.dataCount == 0) { continue; } // Calculate the bottom (stack) value for the current series. num stackValue = 0; stackValue = yValue * i; current.topValues.clear(); current.bottomValues.clear(); // Loop through the data points in the current series. final int length = current.dataCount; for (int j = 0; j < length; j++) { // Add the bottom value (stackValue) for the current data point. current.bottomValues.add(stackValue.toDouble()); // Add the top value (stackValue + yValue) for the current data point. current.topValues.add((stackValue + yValue).toDouble()); } } } } }
Refer to the following image.
Color coding is the essence of a heatmap, allowing users to quickly identify risk levels by glancing at varying color intensities. In this step, we’ll customize the heatmap’s appearance and implement dynamic color mapping to visually represent risk values effectively.
The _buildColor function serves as the backbone of the heatmap, mapping risk values to specific color intensities. Here’s how it works:
Refer to the following code example.
Color _buildColor(num value) { switch (value) { case var val when val >= 20.0: return Colors.green.shade900; case var val when val >= 15.0: return Colors.green.shade700; case var val when val >= 10.0: return Colors.green.shade500; case var val when val >= 5.0: return Colors.green.shade300; case var val when val >= 2.5: return Colors.orange.shade200; case var val when val > 0.0: return Colors.orange.shade400; case var val when val >= -2.5: return Colors.orange.shade600; case var val when val >= -5.0: return Colors.orange.shade800; case var val when val >= -10.0: return Colors.red.shade200; case var val when val >= -15.0: return Colors.red.shade400; case var val when val >= -20.0: return Colors.red.shade600; default: return Colors.red.shade800; } }
Adjusting colors, borders, and labels makes the heat map clear and easy to understand. The pointColorMapper property returns the _buildColor function in the StackedBar100Series, which automatically assigns colors to each segment based on its data value. This helps display different intensity levels in the chart.
The width property controls the size of each segment, keeping the layout organized. The borderColor makes the segment edges clear.
Refer to the following code example.
StackedBar100Series<_HeatMapData, DateTime>( . . . . . . pointColorMapper: (_HeatMapData data, int index) =>_buildColor(_yValue(data, i)), width: 1, name: _seriesName(i), animationDuration: 0, borderColor: Colors.white, borderRadius: const BorderRadius.all(Radius.circular(10)), . . . . . ),
Refer to the following image.
To further enhance the appearance and interactivity of the Flutter Heat Map Chart, you can configure the following customizations, as previously explained in the blog Create an Interactive Heat Map Using Flutter Charts.
Refer to the following image.
For more details, refer to the visualizing the S&P 500 returns after rate cuts using heat maps in Flutter Charts demo on GitHub.
Thank you for reading! In this blog, we’ve explored how to visualize the S&P 500 returns after interest rate cuts by creating a Heat Map using the Syncfusion Flutter 100% Stacked Bar Chart.
The existing customers can download the new version of Essential Studio on the License and Downloads page. If you are not a Syncfusion customer, try our 30-day free trial to check out our incredible features.
You can also contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!