~/cartesianChart/financialseries.md

Candle Sticks Series Props

This article do not include all the properties of the Candle Sticks Series Props class, it only highlights some features, to explore the full object checkout the API explorer

Name property

The name property is a string identifier that is normally used in tooltips and legends to display the data name, if this property is not set, then the library will generate a name for the series that by default is called "Series 1" when it is the first series in the series collection, "Series 2" when it is the second series in the series collection, "Series 3" when it is the third series in the series collection, and so on a series n will be named "Series n".

SeriesCollection = new ISeries[]
{
    new CandleSticksSeriesProps<int>
    {
        Values = new []{ 2, 5, 4, 2, 6 },
        Name = "Income", // mark
        Stroke = null
    },
    new CandleSticksSeriesProps<int>
    {
        Values = new []{ 3, 7, 2, 9, 4 },
        Name = "Outcome", // mark
        Stroke = null
    }
};

Values property

The Values property is of type IEnumerable<T>, this means that you can use any object that implements the IEnumerable<T> interface, such as Array, List<T> or ObservableCollection<T>, this property contains the data to plot, you can use any type as the generic argument (<T>) as soon as you let the library how to handle it, the library already knows how to handle multiple types, but you can register any type and teach the library how to handle any object in a chart, for more information please see the mappers article.

var series1 = new CandleSticksSeriesProps<int>
{
    Values = new List<int> { 2, 1, 3 }
};

// == Update the chart when a value is added, removed or replaced  == // mark
// using ObservableCollections allows the chart to update
// every time you add a new element to the values collection
// (not needed in Blazor, it just... updates)
var series2 = new CandleSticksSeriesProps<double>
{
    Values = new ObservableCollection<double> { 2, 1, 3 }
}
series2.add(4); // and the chart will animate the change!

// == Update the chart when a property in our collection changes  == // mark
// if the object implements INotifyPropertyChanged, then the chart will
// update automatically when a property changes, the library already provides
// many 'ready to go' objects such as the ObservableValue class.
var observableValue =  new ObservableValue(5);
var series3 = new CandleSticksSeriesProps<ObservableValue>
{
    Values = new ObservableCollection<ObservableValue> { observableValue },
}
observableValue.Value = 9; // the chart will animate the change from 5 to 9!

// == Passing X and Y coordinates // mark 
// you can indicate both, X and Y using the Observable point class.
// or you could define your own object using mappers.
var series4 = new CandleSticksSeriesProps<ObservablePoint>
{
    Values = new ObservableCollection<ObservablePoint> { new ObservablePoint(2, 6)}
}
// == Custom types and mappers == // mark
// finally you can also use your own object, take a look at the City class.
public class City 
{
    public string Name { get; set; }
    public double Population { get; set; }
}
// we must let the series know how to handle the city class.
// use the Mapping property to build a point from the city class
// you could also register the map globally.
// for more about global mappers info see:
// https://lvcharts.com/docs/eto/2.0.0-beta.330/Overview.Mappers
var citiesSeries = new CandleSticksSeriesProps<City>
{
    Values = new City[]
    { 
        new City { Name = "Tokio", Population = 9 },
        new City { Name = "New York", Population = 11 },
        new City { Name = "Mexico City", Population = 10 },
    },
    Mapping = (city, point) =>
    {
        // this function will be called for every city in our data collection
        // in this case Tokio, New York and Mexico city
        // it takes the city and the point in the chart liveCharts built for the given city
        // you must map the coordinates to the point

        // use the Population property as the primary value (normally Y)
        point.PrimaryValue = (float)city.Population;

        // use the index of the city in our data collection as the secondary value
        // (normally X)
        point.SecondaryValue = point.Context.Index;
    }
};

Automatic updates do not have a significant performance impact in most of the cases!

Data labels

Data labels are labels for every point in a series, there are multiple properties to customize them, take a look at the following sample:

new CandleSticksSeriesProps<double>
{
    DataLabelsSize = 20,
    DataLabelsPaint = new SolidColorPaint(SKColors.Blue),
    // all the available positions at:
    // https://lvcharts.com/api/2.0.0-beta.330/LiveChartsCore.Measure.DataLabelsPosition
    DataLabelsPosition = LiveChartsCore.Measure.DataLabelsPosition.Top,
    // The DataLabelsFormatter is a function 
    // that takes the current point as parameter
    // and returns a string.
    // in this case we returned the PrimaryValue property as currency
    DataLabelsFormatter = (point) => point.PrimaryValue.ToString("C2"),
    Values = new ObservableCollection<double> { 2, 1, 3, 5, 3, 4, 6 },
    Fill = null
}

The previous series will result in the following chart:

image

UpStroke property

Defines the stroke to use when the Open is greater than the Close.

UpFill property

Defines the fill to use when the Open is greater than the Close.

DownStroke property

Defines the stroke to use when the Close is greater than the Open.

DownFill property

Defines the stroke to use when the Close is greater than the Open.

Paints example

The following sample illustrates the use of the previous properties.

image

XAxes = new[]
{
    new Axis
    {
        LabelsRotation = 15,
        Labeler = value => new DateTime((long)value).ToString("yyyy MMM dd"),
        // set the unit width of the axis to "days"
        // since our X axis is of type date time and 
        // the interval between our points is in days
        UnitWidth = TimeSpan.FromDays(1).Ticks
    }
};

Series = new ISeries[]
{
    new CandlesticksSeries<FinancialPoint>
    {
        UpFill = new SolidColorPaint(SKColors.Blue), // mark
        UpStroke = new SolidColorPaint(SKColors.CornflowerBlue) { StrokeThickness = 5 }, // mark
        DownFill = new SolidColorPaint(SKColors.Red), // mark
        DownStroke = new SolidColorPaint(SKColors.Orange) { StrokeThickness = 5 }, // mark
        Values = new ObservableCollection<FinancialPoint>
        {
            //                             date,        high, open, close, low
            new FinancialPoint(new DateTime(2021, 1, 1), 523, 500, 450, 400),
            new FinancialPoint(new DateTime(2021, 1, 2), 500, 450, 425, 400),
            new FinancialPoint(new DateTime(2021, 1, 3), 490, 425, 400, 380),
            new FinancialPoint(new DateTime(2021, 1, 4), 420, 400, 420, 380),
            new FinancialPoint(new DateTime(2021, 1, 5), 520, 420, 490, 400),
            new FinancialPoint(new DateTime(2021, 1, 6), 580, 490, 560, 440),
            new FinancialPoint(new DateTime(2021, 1, 7), 570, 560, 350, 340),
            new FinancialPoint(new DateTime(2021, 1, 8), 380, 350, 380, 330),
            new FinancialPoint(new DateTime(2021, 1, 9), 440, 380, 420, 350),
            new FinancialPoint(new DateTime(2021, 1, 10), 490, 420, 460, 400),
            new FinancialPoint(new DateTime(2021, 1, 11), 520, 460, 510, 460),
            new FinancialPoint(new DateTime(2021, 1, 12), 580, 510, 560, 500),
            new FinancialPoint(new DateTime(2021, 1, 13), 600, 560, 540, 510),
            new FinancialPoint(new DateTime(2021, 1, 14), 580, 540, 520, 500),
            new FinancialPoint(new DateTime(2021, 1, 15), 580, 520, 560, 520),
            new FinancialPoint(new DateTime(2021, 1, 16), 590, 560, 580, 520),
            new FinancialPoint(new DateTime(2021, 1, 17), 650, 580, 630, 550),
            new FinancialPoint(new DateTime(2021, 1, 18), 680, 630, 650, 600),
            new FinancialPoint(new DateTime(2021, 1, 19), 670, 650, 600, 570),
            new FinancialPoint(new DateTime(2021, 1, 20), 640, 600, 610, 560),
            new FinancialPoint(new DateTime(2021, 1, 21), 630, 610, 630, 590),
        }
    }
};

Paints can create gradients, dashed lines and more, if you need help using the Paint instances take a look at the Paints article.

MaxBarWidth property

Specifies the maximum width a column can take, take a look at the following sample, where the max width is 10.

image

XAxes = new[]
{
    new Axis
    {
        LabelsRotation = 15,
        Labeler = value => new DateTime((long)value).ToString("yyyy MMM dd"),
        // set the unit width of the axis to "days"
        // since our X axis is of type date time and 
        // the interval between our points is in days
        UnitWidth = TimeSpan.FromDays(1).Ticks // mark
    }
};

Series = new ISeries[]
{
    new CandlesticksSeries<FinancialPoint>
    {
        Values = new ObservableCollection<FinancialPoint>
        {
            new FinancialPoint(new DateTime(2021, 1, 1), 523, 500, 450, 400),
            new FinancialPoint(new DateTime(2021, 1, 2), 500, 450, 425, 400),
            new FinancialPoint(new DateTime(2021, 1, 3), 490, 425, 400, 380),
            new FinancialPoint(new DateTime(2021, 1, 4), 420, 400, 420, 380),
            new FinancialPoint(new DateTime(2021, 1, 5), 520, 420, 490, 400),
            new FinancialPoint(new DateTime(2021, 1, 6), 580, 490, 560, 440),
            new FinancialPoint(new DateTime(2021, 1, 7), 570, 560, 350, 340),
            new FinancialPoint(new DateTime(2021, 1, 8), 380, 350, 380, 330),
            new FinancialPoint(new DateTime(2021, 1, 9), 440, 380, 420, 350),
            new FinancialPoint(new DateTime(2021, 1, 10), 490, 420, 460, 400)
        }
    }
};

Plotting custom types

You can teach LiveCharts to plot anything, imagine the case where we have an array of the Stock class defined bellow:

public class Stock
{
    public DateTime Date { get; set; }
    public double Open { get; set; }
    public double Close { get; set; }
    public double High { get; set; }
    public double Low { get; set; }
}

You can register this type globally, this means that every time LiveCharts finds a Stock instance in a chart it will use the mapper we registered, global mappers are unique for a type, if you need to plot multiple properties then you should use local mappers.

// Ideally you should call this when your application starts
// If you need help to decide where to add this code
// please see the installation guide in this docs.

// in this case we have an array of the Stock class

LiveCharts.Configure(config =>
    config
        .HasMap<Stock>((stock, point) =>
        {
            // in this lambda function we take an instance of the City class (see city parameter)
            // and the point in the chart for that instance (see point parameter)
            // LiveCharts will call this method for every instance of our City class array,
            // now we need to populate the point coordinates from our City instance to our point

            point.SecondaryValue = stock.Date.Ticks; // use the date for the X axis (secondary)

            // now LiveCharts uses Primary (high), Tertiary (open)
            // Quaternary (close) and Quinary (low) planes to represent
            // a financial point:

            point.PrimaryValue = (float)stock.High;
            point.TertiaryValue = (float)stock.Open;
            point.QuaternaryValue = (float)stock.Close;
            point.QuinaryValue = (float)stock.Low;
        })
        .HasMap<Foo>(...) // you can register more types here using our fluent syntax
        .HasMap<Bar>(...)
    );

Now we are ready to plot stock all over our application:

var stockData = new[]
{
    new Stock
    {
        Date = new DateTime(2021, 1, 1),
        Open = 200f,
        Close = 280f,
        High = 290f,
        Low = 180f
    },
    new Stock
    {
        Date = new DateTime(2021, 1, 2),
        Open = 280f,
        Close = 220f,
        High = 320f,
        Low = 210f
    }
};

XAxes = new[]
{
    // set the UnitWidth to "days" to support date time scaled points.
    new Axis
    {
        UnitWidth = TimeSpan.FromDays(1).Ticks,
        LabelsRotation = 20,
        Labeler = p => new DateTime((long)p).ToShortDateString(),
        MinStep = TimeSpan.FromDays(1).Ticks
    }
};

Series = new[]
{
    new CandlesticksSeries<Stock>
    {
        TooltipLabelFormatter =
            (p) => $"H: {p.PrimaryValue:N2}, O: {p.TertiaryValue:N2}, C: {p.QuaternaryValue:N2}, L: {p.QuinaryValue:N2}",
        Values = stockData
    }
};

image

Alternatively you could create a local mapper that will only work for a specific series, global mappers will be ignored when the series Mapping property is not null.

Series = new[]
{
    new CandlesticksSeries<Stock>
    {
        Mapping = (stock, point) =>
        {
            point.SecondaryValue = stock.Date.Ticks;
            point.PrimaryValue = (float)stock.High;
            point.TertiaryValue = (float)stock.Open;
            point.QuaternaryValue = (float)stock.Close;
            point.QuinaryValue = (float)stock.Low;
        },
        Values = stockData
    }
};