Page 1 of 1
Get the visible points
Posted: Wed Dec 16, 2015 2:28 am
by 16577003
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)
Re: Get the visible points
Posted: Wed Dec 16, 2015 4:47 pm
by yeray
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;
Re: Get the visible points
Posted: Thu Dec 17, 2015 9:46 am
by 16577003
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?
Re: Get the visible points
Posted: Thu Dec 17, 2015 11:33 am
by yeray
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;
Re: Get the visible points
Posted: Fri Dec 18, 2015 1:38 am
by 16577003
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.
Re: Get the visible points
Posted: Fri Dec 18, 2015 9:31 am
by yeray
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;
Re: Get the visible points
Posted: Mon Dec 21, 2015 6:56 am
by 16577003
Hi,
I just realized after using:
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?
Re: Get the visible points
Posted: Mon Dec 21, 2015 1:16 pm
by yeray
Hello,
n2n wrote:I just realized after using:
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;
Re: Get the visible points
Posted: Tue Dec 22, 2015 1:46 am
by 16577003
Hi,
This will certainly do the trick for dateformat, however i still need
To only show xvalues that i have added. would it be possible to calculate this manually aswell?
Re: Get the visible points
Posted: Tue Dec 22, 2015 8:25 am
by yeray
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.