3

I'm trying to build an financial chart using live charts but i want to have two charts that either share x-axis or have the x-axis synced. Im currently trying to have two charts with synced x-axis but running to trouble because of axis label width of the main chart, see the picture below.

I tried to subtract the axis label width from the secondary chart but that property is always 0 :(

private void Grid_LayoutUpdated(object sender, EventArgs e)
{
   var axisWidth = MainChart.AxisY[1].ActualWidth; //always 0 :(
   IndicatorChart.Width -= axisWidth;
}

Anyone has any idea how to solve this with livecharts? Thanks in advance! problem: the secondary is wider then the main chart

here's the XAML:

<UserControl x:Class="TradeChart.GearedExample"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:TradeChart"
         xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
        xmlns:geared="clr-namespace:LiveCharts.Geared;assembly=LiveCharts.Geared"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
    <Grid LayoutUpdated="Grid_LayoutUpdated" Loaded="Grid_Loaded">
        <Grid.RowDefinitions>
            <RowDefinition Height="4*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <lvc:CartesianChart Zoom="X" Pan="X" DisableAnimations="True" AnimationsSpeed="0:0:0.15" Series="{Binding SeriesCollection}" MouseMove="UIElement_OnMouseMove"  LayoutUpdated="MainChart_LayoutUpdated" x:Name="MainChart" DataTooltip="{x:Null}">
            <lvc:CartesianChart.AxisX>
                <lvc:Axis Labels="{Binding Labels}" IsMerged="True">
                    <lvc:Axis.Sections>
                        <lvc:AxisSection Value="{Binding XPointer}"
                                     SectionWidth="1"
                                     SectionOffset="-0.5"
                                     Fill="#59FF5722"
                                     Stroke="#ff5722"
                                     StrokeThickness=".5"
                                     DataLabelForeground="White"
                                     DataLabel="True"/>
                    </lvc:Axis.Sections>
                </lvc:Axis>
            </lvc:CartesianChart.AxisX>
            <lvc:CartesianChart.AxisY>
                <lvc:Axis LabelFormatter="{Binding Formatter}">
                    <lvc:Axis.Sections>
                        <lvc:AxisSection Value="{Binding YPointer}" 
                                     DataLabel="True"
                                     StrokeThickness="1"
                                     Stroke="#ff5722"
                                     DisableAnimations="True"
                                     DataLabelForeground="White"
                                     Panel.ZIndex="1"/>
                    </lvc:Axis.Sections>
                </lvc:Axis>
                <lvc:Axis Position="RightTop" x:Name="VolumeAxis" LayoutUpdated="Axis_LayoutUpdated" Width="400">
                </lvc:Axis>
            </lvc:CartesianChart.AxisY>
        </lvc:CartesianChart>
        <lvc:CartesianChart Grid.Row="1" Zoom="X" Pan="X" DisableAnimations="True" AnimationsSpeed="0:0:0.15" Series="{Binding IndicatorSeriesCollection}" MouseMove="UIElement_OnMouseMove" x:Name="IndicatorChart" DataTooltip="{x:Null}">
            <lvc:CartesianChart.AxisX>
                <lvc:Axis Labels="{Binding Labels}" IsMerged="True">
                    <lvc:Axis.Sections>
                        <lvc:AxisSection Value="{Binding XPointer}"
                                     SectionWidth="1"
                                     SectionOffset="-0.5"
                                     Fill="#59FF5722"
                                     Stroke="#ff5722"
                                     StrokeThickness=".5"
                                     DataLabelForeground="White"
                                     DataLabel="True"/>
                    </lvc:Axis.Sections>
                </lvc:Axis>
            </lvc:CartesianChart.AxisX>
            <lvc:CartesianChart.AxisY>
                <lvc:Axis LabelFormatter="{Binding Formatter}" MinValue="0" MaxValue="100">
                    <lvc:Axis.Sections>
                        <lvc:AxisSection Value="{Binding YPointer}" 
                                     DataLabel="True"
                                     StrokeThickness="1"
                                     Stroke="#ff5722"
                                     DisableAnimations="True"
                                     DataLabelForeground="White"
                                     Panel.ZIndex="1"/>
                    </lvc:Axis.Sections>
                </lvc:Axis>
            </lvc:CartesianChart.AxisY>
        </lvc:CartesianChart>
    </Grid>
</UserControl>

SeriesCollection:

    private void CreateCandleSeries(IReadOnlyList<Candle> candles)
    {
        var values = new List<OhlcPoint>();
        foreach (var candle in candles)
        {
            values.Add(new OhlcPoint
            {
                Open = (double)candle.Open,
                Close = (double)candle.Close,
                High = (double)candle.High,
                Low = (double)candle.Low
            });
        }
        var gearValues = new GearedValues<OhlcPoint>();
        gearValues.AddRange(values);
        gearValues.WithQuality(Quality.Low);
        var candleSeries = new GCandleSeries()
        {
            Values = gearValues,
            ScalesYAt = 0,

        };

        var increaseBrush = candleSeries.IncreaseBrush;
        var decreaseBrush = candleSeries.DecreaseBrush;
        candleSeries.IncreaseBrush = decreaseBrush;
        candleSeries.DecreaseBrush = increaseBrush;

        SeriesCollection.Add(candleSeries);
    }

IndicatorSeriesCollection:

    private void CreateSingleLineOscilatorSeries(IReadOnlyList<Candle> candles, int period, Func<int, int?, int?, IReadOnlyList<AnalyzableTick<decimal?>>> indicator, SeriesCollection collectionToAddTo, int? startIndex = null, int? endIndex = null)
    {
        var indicatorCandleValues = indicator.Invoke(period, startIndex, endIndex);
        var indicatorValues = new List<double>();
        foreach (var value in indicatorCandleValues)
        {
            if (value.Tick.HasValue)
            {
                indicatorValues.Add((double)value.Tick.Value);
            }
            else
            {
                indicatorValues.Add(-1);
            }
        }
        var indicatorMapper = new CartesianMapper<double>().X((value, index) => index).Y((value, index) => value);
        var gearValues = new GearedValues<double>();
        gearValues.AddRange(indicatorValues);
        gearValues.WithQuality(Quality.High);
        var smaSeries = new GLineSeries(indicatorMapper)
        {
            Fill = Brushes.Transparent,
            Values = gearValues,
            LineSmoothness = 0,

            ScalesYAt = 0
        };
        collectionToAddTo.Add(smaSeries);
    }
RBarryYoung
  • 55,398
  • 14
  • 96
  • 137
mrplatina
  • 239
  • 4
  • 7
  • Can you post the code for the SeriesCollection and IndicatorSeriesCollection. – Niza Siwale Oct 22 '17 at 18:35
  • sure, is posted now – mrplatina Oct 22 '17 at 20:28
  • Your GCandleSeries is a dual series with high/low and open/close values hence you have two Y-axis in the top chart, while GLineSeries is a single series in the bottom chart – Niza Siwale Oct 23 '17 at 06:57
  • Yes i know, and thats what I want, my question is if it's possible to align the top chart main windows with the bottom chart main window. For example by calculating the bottom chart width by subtracting top chart right Y-axis width from the bottom chart width. Sorry if my question is unclear, i'm not that experienced with wcf. – mrplatina Oct 23 '17 at 14:30

3 Answers3

2

Okay I gave up trying to align the two charts, however i managed to solve the problem by putting the bottom chart into the main chart and using MinValue and MaxValue in order to make it stick to the bottom.

Here's how I set the MinValue and MaxValue:

candlechart MinValue=candles.Min() - 0.2*candles.Min()
candlechart MaxValue=candles.Max()

volume MinValue=volumes.Min()
volume MaxValues=volumes.Max() * 2

the oscilator is pending between 0 and 100 so I set the max and min values as this:

oscilator MaxValue=600
oscilator MinValue=20

result: enter image description here

Klaus Byskov Pedersen
  • 117,245
  • 29
  • 183
  • 222
mrplatina
  • 239
  • 4
  • 7
0

I had similar issue and was only able to solve it using IsMerged property of the Axis:

...
<lvc:Axis IsMerged="True"/>
Dima G
  • 1,945
  • 18
  • 22
0

The Width property was not hooked up to the Axis and by default would use the biggest item within the Axis to decide the Width value resulting in different sized graphs.

As the project does not look to be actively developed I created a fork of the project for the .net core 3.1 and committed the changes there.

https://github.com/matsydoodles/Live-Charts/commit/12349fd4e329a4a6a42ff2fa4d3362d546161aa4

metoyou
  • 639
  • 1
  • 7
  • 22