Get the visible points

TeeChart FireMonkey (Windows,OSX,iOS & Android) for Embarcadero RAD Studio, Delphi and C++ Builder (XE5+)
Post Reply
n2n
Newbie
Newbie
Posts: 19
Joined: Mon Nov 23, 2015 12:00 am

Get the visible points

Post by n2n » Wed Dec 16, 2015 2:28 am

I insert 1000 values to my candle series, X-axis is DateTime


1. i want to get the number of currently displaying points when i zoom in so i can scale candles widths, problem is when i use VisibleCount of series it will return 1000 which is all my data, but after i zoomed in i have like only 5 points visible so that value is incorrect.

2. when i zoom with mouse wheel it will keep zooming, how may i limit the zoom to for example not zoomin more than 2 days gap in x-axis and not zoomout when all points are displaying and with left and right of chart.

3. how can i force x-axis to only display the dates i have inserted and not any data in between? for example i add data with 5 days in gap but when i zoomin it will expand data range to all dates in between (see img)
Attachments
cap1.PNG
cap1.PNG (4.1 KiB) Viewed 20220 times

Yeray
Site Admin
Site Admin
Posts: 9514
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Get the visible points

Post by Yeray » Wed Dec 16, 2015 4:47 pm

Hello,
n2n wrote:I insert 1000 values to my candle series, X-axis is DateTime
This is the basic application I've started with to test this (note I've tried this with VCL but it shouldn't be different in FMX):

Code: Select all

uses CandleCh;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.View3D:=false;
  Chart1.Legend.Visible:=false;

  with Chart1.AddSeries(TCandleSeries) as TCandleSeries do
  begin
    XValues.DateTime:=true;
    FillSampleValues(1000);
  end;
end;
n2n wrote:1. i want to get the number of currently displaying points when i zoom in so i can scale candles widths, problem is when i use VisibleCount of series it will return 1000 which is all my data, but after i zoomed in i have like only 5 points visible so that value is incorrect.
Try forcing a chart repaint at OnZoom event before retrieving the VisibleCount. Ie:

Code: Select all

procedure TForm1.Chart1Zoom(Sender: TObject);
begin
  Chart1.Draw;
  Caption:=IntToStr(Chart1[0].VisibleCount);
end;
n2n wrote:2. when i zoom with mouse wheel it will keep zooming, how may i limit the zoom to for example not zoomin more than 2 days gap in x-axis and not zoomout when all points are displaying and with left and right of chart.
You can also force the axis scale (minimum, maximum or both) at OnZoom event. Ie:

Code: Select all

procedure TForm1.Chart1Zoom(Sender: TObject);
begin
  if (Chart1.Axes.Bottom.Maximum-Chart1.Axes.Bottom.Minimum
      < DateTimeStep[dtOneDay]*2) then
  begin
    Chart1.Axes.Bottom.Maximum:=Chart1.Axes.Bottom.Minimum+DateTimeStep[dtOneDay]*2;
  end;

  Chart1.Draw;
  Caption:=IntToStr(Chart1[0].VisibleCount);
end;
n2n wrote:3. how can i force x-axis to only display the dates i have inserted and not any data in between? for example i add data with 5 days in gap but when i zoomin it will expand data range to all dates in between (see img)
Adding this at OnCreate seems to do what you describe:

Code: Select all

  Chart1.Axes.Bottom.LabelStyle:=talPointValue;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

n2n
Newbie
Newbie
Posts: 19
Joined: Mon Nov 23, 2015 12:00 am

Re: Get the visible points

Post by n2n » Thu Dec 17, 2015 9:46 am

Thanks for the reply,

Last problem solved, however how may i delete the space between missing dates? for example i have value for 1st dec and 5th dec and 3 days in between with no data, currently it will create empty space for them, how can i remove them?

Yeray
Site Admin
Site Admin
Posts: 9514
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Get the visible points

Post by Yeray » Thu Dec 17, 2015 11:33 am

Hello,
n2n wrote:how may i delete the space between missing dates? for example i have value for 1st dec and 5th dec and 3 days in between with no data, currently it will create empty space for them, how can i remove them?
This usually indicates the points are being added with an XValue - in your case a TDateTime.
I'd suggest you to convert your datetimes to strings (to use as labels) and add the values in consecutive XValues but with label. Ie:

Code: Select all

uses CandleCh, DateUtils;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
    tmpDate: TDateTime;
begin
  Chart1.View3D:=false;
  Chart1.Legend.Visible:=false;

  with Chart1.AddSeries(TCandleSeries) as TCandleSeries do
  begin
    XValues.DateTime:=true;
    FillSampleValues(10);

    tmpDate:=XValue[0];
    for i:=1 to Count-1 do
    begin
      tmpDate:=IncDay(tmpDate,1+Round(random*4));
      XValue[i]:=tmpDate;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var tmpDate: TDateTime;
    tmpS: string;
    i: Integer;
begin
  with Chart1[0] as TCandleSeries do
  begin
    tmpDate:=XValue[0];
    for i:=0 to Count-1 do
    begin
      tmpS:=FormatDateTime(Chart1.Axes.Bottom.DateTimeFormat, XValue[i]);
      Labels[i]:=tmpS;

      XValue[i]:=tmpDate;
      tmpDate:=IncDay(tmpDate);
    end;
  end;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

n2n
Newbie
Newbie
Posts: 19
Joined: Mon Nov 23, 2015 12:00 am

Re: Get the visible points

Post by n2n » Fri Dec 18, 2015 1:38 am

Hi,
Changing my Xvalues to string will cause me alot of problems, i do actually use the xvalue double data for some calculations, would there be any other workaround to overcome this with maintaining Xvalue with double data and not using Labels? also im currently using OnGetAxisLabel, to convert TDateTime to string due to some conditions when i zoom in i require to use different FormatDateTime.

Yeray
Site Admin
Site Admin
Posts: 9514
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Get the visible points

Post by Yeray » Fri Dec 18, 2015 9:31 am

Hello,

The XValue is the array of doubles TeeChart takes to calculate the x pixel position to draw each value in the series. That's why the easiest way to change the later is by changing the first.

An alternative to keep your DateTimes next to each value would be overriding TCandleSeries adding a new array of to it. Then, your values could be drawn using XValue as always (0, 1, 2,...) and you can make the axis to show the values in the array of DateTimes in the series.
Here it is a simple example doing it:

Code: Select all

uses CandleCh, DateUtils, TeeProCo, TeeConst;

type
  TMyCandleSeries = class(TCandleSeries)
  private
    FDate : TChartValueList;

    procedure SetDateValues(const Value:TChartValueList);

  protected
    //Changes the axis from showing the values in XValues to show the values in FDate
    function ValueListOfAxis(const Axis:TChartAxis):TChartValueList; override;

    //Use custom AddDateCandle function instead of AddOHLC fucntion
    procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;

  public
    property DateValues:TChartValueList read FDate write SetDateValues;

    constructor Create(AOwner: TComponent); override;

    //Adds a candle without XValue, so consecutive
    function AddDateCandle(const ADate:TDateTime;
                           const AOpen,AHigh,ALow,AClose:Double;
                           const ALabel:String='';
                           AColor:TColor=clTeeColor ):Integer;
  end;

  TValueListAccess=class(TChartValueList);

Constructor TMyCandleSeries.Create(AOwner: TComponent);
begin
  inherited;

  TValueListAccess(XValues).InitDateTime(False);
  XValues.Name:=TeeMsg_ValuesX;

  FDate:=TChartValueList.Create(Self,TeeMsg_ValuesDate);
  TValueListAccess(DateValues).InitDateTime(True);

  NotMandatoryValueList:=FDate;
end;

Procedure TMyCandleSeries.SetDateValues(const Value:TChartValueList);
Begin
  SetChartValueList(FDate,Value);
end;

function TMyCandleSeries.ValueListOfAxis(const Axis:TChartAxis):TChartValueList;
begin
  if AssociatedToAxis(Axis) then
     if Axis.Horizontal then result:=FDate
                        else result:=YValues
  else
     result:=nil;
end;

Function TMyCandleSeries.AddDateCandle(const ADate:TDateTime;
                                      const AOpen,AHigh,ALow,AClose:Double;
                                      const ALabel:String='';
                                      AColor:TColor=clTeeColor ):Integer;
begin
  HighValues.TempValue:=AHigh;
  LowValues.TempValue:=ALow;
  OpenValues.TempValue:=AOpen;
  DateValues.TempValue:=ADate;
  result:=AddY(AClose,ALabel,AColor);
end;

Procedure TMyCandleSeries.AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False);
Var AOpen  : Double;
    AHigh  : Double;
    ALow   : Double;
    AClose : Double;
    t      : Integer;
    s      : TSeriesRandomBounds;
Begin
  s:=RandomBounds(NumValues);

  with s do
  begin
    tmpX:=Date;
    AOpen:=MinY+RandomValue(Round(DifY)); { starting open price }

    t:=1;

    while t<=NumValues do
    Begin
      // Generate random figures
      GetRandomOHLC(AOpen,AClose,AHigh,ALow,DifY);

      AddDateCandle(tmpX,AOpen,AHigh,ALow,AClose);
      Inc(t);

      tmpX:=tmpX+StepX;  { <-- next point X value }

      // Tomorrow, the market will open at today's close plus/minus something }
      AOpen:=AClose+RandomValue(10)-5;
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
    tmpDate: TDateTime;
begin
  Chart1.View3D:=false;
  Chart1.Legend.Visible:=false;

  with Chart1.AddSeries(TMyCandleSeries) as TMyCandleSeries do
  begin
    FillSampleValues(10);

    //Modifying the DateValues array that only affects the labels, not the x positions of the candles
    tmpDate:=DateValues[0];
    for i:=1 to Count-1 do
    begin
      tmpDate:=IncDay(tmpDate,1+Round(random*4));
      DateValues[i]:=tmpDate;
    end;
  end;

  Chart1.Axes.Bottom.LabelStyle:=talPointValue;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

n2n
Newbie
Newbie
Posts: 19
Joined: Mon Nov 23, 2015 12:00 am

Re: Get the visible points

Post by n2n » Mon Dec 21, 2015 6:56 am

Hi,

I just realized after using:

Code: Select all

LabelStyle := talPointValue;
To limit labels with only the ones with value, it will significantly reduce the performance when we have 1000 over points on our BottomAxis, and even though only few of the labels are showing actually but it will trigger the OnGetAxisLabel for every single point and this will even will trigger on every occasion even on mouse move. is it possible to improve performance on this issue?

Yeray
Site Admin
Site Admin
Posts: 9514
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Get the visible points

Post by Yeray » Mon Dec 21, 2015 1:16 pm

Hello,
n2n wrote:I just realized after using:

Code: Select all

LabelStyle := talPointValue;
To limit labels with only the ones with value, it will significantly reduce the performance when we have 1000 over points on our BottomAxis, and even though only few of the labels are showing actually but it will trigger the OnGetAxisLabel for every single point and this will even will trigger on every occasion even on mouse move. is it possible to improve performance on this issue?
If you are using OnGetAxisLabel only to format the bottom axis labels depending on the axis scale after zooming:
n2n wrote:also im currently using OnGetAxisLabel, to convert TDateTime to string due to some conditions when i zoom in i require to use different FormatDateTime.
You could try changing the bottom axis FormatDateTime at OnZoom/OnUndoZoom:

Code: Select all

procedure TForm1.Chart1Zoom(Sender: TObject);
var i: Integer;
    tmpFirstIndex, tmpLastIndex: Integer;
    visibleRange: double;
begin
  Chart1.Draw;
  tmpFirstIndex:=-1;
  tmpLastIndex:=-1;

  if Chart1[0] is TMyCandleSeries then
    with TMyCandleSeries(Chart1[0]) do
    begin
      for i:=0 to Count-1 do
      begin
        if (XValue[i]>=Chart1.Axes.Bottom.Minimum) and
           (XValue[i]<=Chart1.Axes.Bottom.Maximum) then
        begin
          tmpFirstIndex:=i;
          break;
        end;
      end;

      for i:=Count-1 downto 0 do
      begin
        if (XValue[i]>=Chart1.Axes.Bottom.Minimum) and
           (XValue[i]<=Chart1.Axes.Bottom.Maximum) then
        begin
          tmpLastIndex:=i;
          break;
        end;
      end;

      if (tmpFirstIndex>-1) and (tmpLastIndex>-1) then
      begin
        visibleRange:=DateValues[tmpLastIndex]-DateValues[tmpFirstIndex];

        if visibleRange < DateTimeStep[dtOneMonth] then
        begin
          Chart1.Title.Text.Text:=FormatDateTime('MMM yyyy', DateValues[LastDisplayedIndex]);
          Chart1.Axes.Bottom.DateTimeFormat:='dd';
        end
        else
        if visibleRange < DateTimeStep[dtOneYear] then
        begin
          Chart1.Title.Text.Text:=FormatDateTime('yyyy', DateValues[LastDisplayedIndex]);
          Chart1.Axes.Bottom.DateTimeFormat:='dd/MM';
        end;
      end;
    end;
end;

procedure TForm1.Chart1UndoZoom(Sender: TObject);
begin
  Chart1.Title.Text.Text:='TeeChart';
  Chart1.Axes.Bottom.DateTimeFormat:='dd/MM/yyyy';
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

n2n
Newbie
Newbie
Posts: 19
Joined: Mon Nov 23, 2015 12:00 am

Re: Get the visible points

Post by n2n » Tue Dec 22, 2015 1:46 am

Hi,

This will certainly do the trick for dateformat, however i still need

Code: Select all

LabelStyle := talPointValue;
To only show xvalues that i have added. would it be possible to calculate this manually aswell?

Yeray
Site Admin
Site Admin
Posts: 9514
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Get the visible points

Post by Yeray » Tue Dec 22, 2015 8:25 am

Hello,
n2n wrote:would it be possible to calculate this manually aswell?
I'm not sure to understand what do you mean here, or where do you need to get the labels.
However, if you know the value to get the label for, you can just call the axis LabelValue:

Code: Select all

  mystring:=Chart1.Axes.Bottom.LabelValue(Chart1[0].XValue[10]);
If you are using the customized TMyCandleSeries I suggested above, you would use this instead:

Code: Select all

  mystring:=Chart1.Axes.Bottom.LabelValue(Chart1[0].DateValues[10]);
Just note the chart needs to be drawn at least once to use those functions.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Post Reply