Pre loader

Candlestick body width for random x values

Welcome to the SciChart Forums!

  • Please read our Question Asking Guidelines for how to format a good question
  • Some reputation is required to post answers. Get up-voted to avoid the spam filter!
  • We welcome community answers and upvotes. Every Q&A improves SciChart for everyone

WPF Forums | JavaScript Forums | Android Forums | iOS Forums

0
0

Hi,

I’ve encountered an issue with the candlestick graph I’ve drawn that consists of a set of random X date time values – this causes the candlesticks have varying distances from one another.

Rendering the data from an initial set seems to be fine but as I run a NSTimer to add on more points, the body of the new candlesticks seem to be 0. (Note, the date time values are still random but have been made to be set after the last placed point)

In fact, the initial data points that I added see to be rendered ‘body-less’ if it’s within the same visible range as the newly added points. And if I pan along the newly added points, the bodies of some data points will appear at random visible ranges.

Is this a normal behaviour or should I take anything else into consideration?

Here are a couple of my configurations which I think may have affected anything:
1. My XAxis has set its visible range limit mode to ClipMode_Min
2. Candlestick series style data body width is set to be default.
3. I have a the following modifiers set up.

self.xDragModifier = [SCIXAxisDragModifier new];
self.xDragModifier.axisId = @"xAxis";
self.xDragModifier.dragMode = SCIAxisDragMode_Pan;
self.xDragModifier.clipModeX = SCIZoomPanClipMode_ClipAtMin;
[self.xDragModifier setModifierName:@"XAxis DragModifier"];

self.pinchZoomModifier = [[SCIPinchZoomModifier alloc] init];
[self.pinchZoomModifier setModifierName:@"PinchZoom Modifier"];
self.pinchZoomModifier.xyDirection = SCIXYDirection_XDirection;

self.zoomPanModifier = [SCIZoomPanModifier new];
self.zoomPanModifier.clipModeX = SCIZoomPanClipMode_ClipAtMin;
self.zoomPanModifier.xyDirection = SCIXYDirection_XDirection;
[self.zoomPanModifier setModifierName:@"ZoomPan Modifier"];

SCIModifierGroup * gm = [[SCIModifierGroup alloc] initWithChildModifiers:@[self.xDragModifier, self.pinchZoomModifier, self.zoomPanModifier]];
self.chartSurface.chartModifier = gm;

The points are drawing just fine, but the body of the candlestick is not appearing as expected.

Thanks in advance.

Version
1.2
  • You must to post comments
0
0

Hi Andrii,

Thank you for the response and taking your time to look into this issue.

I’ll give the workaround a try and hopefully can get back to you with the results ASAP.

Once again, thank you.

Regards,

  • You must to post comments
0
0

Good day

Sorry for slow response, we were not able to reproduce the issue. Please clarify how exactly do you add data points to data series.

One of possible reasons for this can be the following:
Data change do not trigger redraw on surface on its own. When you finish editing data you should call invalidate method on surface.

[surface invalidateElement];  // where surface is SCIChartSurface

If that was the reason, please let us know, otherwise the following information could be useful for us:
1) How do you create data series instance?
2) How do you append data to data series? (initially and on timer)
3) How do you trigger charts redraw on timer event? (change of visible range, invalidating surface)
4) Which version of framework do you use?

Best regards
SciChart iOS team

  • You must to post comments
0
0

Hi,

thanks for the reply.
I’ve got the invalidate to be called everytime I add a data to the series.

1) How do you create data series instance?

 self.candlestickSeries = [[SCIOhlcDataSeries alloc]initWithXType:SCIDataType_DateTime YType:SCIDataType_Float];
    self.candlestickSeries.dataDistributionCalculator = [SCIUserDefinedDistributionCalculator new];
    
    //Candlestick
    SCIFastCandlestickRenderableSeries *renderableCandlestickSeries = [SCIFastCandlestickRenderableSeries new];
    renderableCandlestickSeries.xAxisId = @"xAxis";
    renderableCandlestickSeries.yAxisId = @"yAxis";
    renderableCandlestickSeries.style = [self generateCandlestickSeriesStyle];
    [renderableCandlestickSeries setDataSeries:self.candlestickSeries];
    
    [self.chartSurface attachRenderableSeries:renderableCandlestickSeries];
    [self.chartSurface invalidateElement];
    
    NSLog(@"Done initialisation for candlestick");

Function to generate the candlestick series style (in case you need to have a look at it too):

-(SCICandlestickSeriesStyle *)generateCandlestickSeriesStyle
{
    SCIBrushSolid *downBodyBrush = [[SCIBrushSolid alloc]initWithColor:[UIColor redColor]];
    SCIPenSolid *downWickPen = [[SCIPenSolid alloc]initWithColor:[UIColor redColor] Width:0.5];
    SCIBrushSolid *upBodyBrush = [[SCIBrushSolid alloc]initWithColor:[UIColor greenColor]];
    SCIPenSolid *upWickPen = [[SCIPenSolid alloc]initWithColor:[UIColor greenColor] Width:0.5];
    
    SCICandlestickSeriesStyle *style = [SCICandlestickSeriesStyle new];
    
    style.downWickPen = downWickPen;
    style.downBodyBrush = downBodyBrush;
    style.upWickPen = upWickPen;
    style.upBodyBrush = upBodyBrush;
    
    return style;
}

2) How do you append data to data series? (initially and on timer)

Initial:


for (FeedModel *m in self.dataArray)
{
      if ([m.timestamp compare:self.previousDate] != NSOrderedSame || !self.previousDate)
      {
           self.previousDate = m.timestamp;
           
           [self.candlestickSeries appendX:SCIGeneric(m.timestamp) Open:SCIGeneric(m.mid-0.0003) High:SCIGeneric(m.mid+0.0005) Low:SCIGeneric(m.mid-0.0004) Close:SCIGeneric(m.mid)];
          
      }
}
    
    [self.chartSurface invalidateElement];

Timer:

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
    {
        [self.candlestickSeries appendX:SCIGeneric(feed.timestamp) Open:SCIGeneric(feed.mid-0.0003) High:SCIGeneric(feed.mid+0.0005) Low:SCIGeneric(feed.mid-0.0004) Close:SCIGeneric(feed.mid)];
        
        dispatch_async(dispatch_get_main_queue(), ^
        {
            [self.chartSurface invalidateElement];
            [self.chartSurface.viewportManager animateZoomExtentsY:1.0f];
            [self.chartSurface.viewportManager calculateNewXAxisRange:self.xAxis];
        });
    });

3) How do you trigger charts redraw on timer event? (change of visible range, invalidating surface)

[self.chartSurface invalidateElement];
[self.chartSurface.viewportManager animateZoomExtentsY:1.0f];
[self.chartSurface.viewportManager calculateNewXAxisRange:self.xAxis];  //Not sure if this is really needed. Removing it doesn't change anything though.

4) Which version of framework do you use?
iOS v1.2.0.824_SDK

Regards.

  • You must to post comments
0
0

Good day

It seems that issues with drawing are connected with unsorted OHLC data.
SciChart is tuned to work with sorted data, and at the moment only Line, Scatter and Bubble series works correctly with unsorted data at cost of performance loss.

So general response in such cases: Candlestick series is not supposed to be used with unsorted data. It is better to sort data before adding to SciChart, and you can use “insertAt:X:Open:High:Low:Close:” method on data series. High speed drawing can’t be achieved with unsorted data and most time consuming routines is not implemented for unsorted data for that reason.

In case it is not possible, and you need some workaround:

1) There was an issue with OHLC data series, it was not able to calculate data range correctly with unsorted data. You can get 1.2.2 release with fixes at the link: http://www.scichart.com/Downloads/iOS/v1.2/SciChart_iOS_v1.2.2.915_SDK.zip

2) Using 1.2.2 version replace in your code:

self.candlestickSeries = [[SCIOhlcDataSeries alloc]initWithXType:SCIDataType_DateTime YType:SCIDataType_Float];
self.candlestickSeries.dataDistributionCalculator = [SCIUserDefinedDistributionCalculator new];

With:

self.candlestickSeries = [[SCIOhlcDataSeries alloc]initWithXType:SCIDataType_DateTime YType:SCIDataType_Float];
[self.candlestickSeries setAcceptUnsortedData:YES];

3) You will get strange candles width behaviour. So you need to override “getDatapointWidthFrom:Amount:Calculator:WidthFraction:”
You have to return from that method any suitable width in points, or implement something like that:

@interface CustomCandlesWidth : SCIFastCandlestickRenderableSeries
@end

@implementation CustomCandlesWidth

-(float) getDatapointWidthFrom:(id<SCIPointSeries>)pointSeries Amount:(int)barsAmount Calculator:(id<SCICoordinateCalculator>)xCalc WidthFraction:(double)widthFraction {
    SCIArrayController * x = [pointSeries xValues];
    double dataPointWidth = 0;
    int count = (int)[x count];
    double dist = DBL_MAX;
    for (int i = 0; i < count; i++) {
        double current = [xCalc getCoordinateFrom:SCIGenericDouble([x valueAt:i])];
        for (int j = 0; j < count; j++) {
            if (j == i) continue;
            double compare = [xCalc getCoordinateFrom:SCIGenericDouble([x valueAt:j])];
            dist = MIN( dist, fabs(current - compare) );
        }
    }
    dataPointWidth = dist;
    return (dataPointWidth * widthFraction);
}

@end

(Warning: such implementation can hit hard your performance with large amounts of data)
And use custom renderable series instead of default SCIFastCandlestickRenderableSeries

4) And even after all fixes you won’t be able without lots of codding to get to work Rollover, Tooltip, Cusror modifier, because hit test is not implemented for unsorted OHLC series.

Sorry for unconvenience
Best regards
Andrii
SciChart iOS developer

  • You must to post comments
Showing 4 results
Your Answer

Please first to submit.

Try SciChart Today

Start a trial and discover why we are the choice
of demanding developers worldwide

Start TrialCase Studies