{********************************************}
{ TeeChart Pro Charting Library              }
{ EquiVolume Series                          }
{ Copyright (c) 2012-2020 by Steema Software }
{ All Rights Reserved                        }
{********************************************}
unit TeeEquiVolume;
{$I TeeDefs.inc}

interface

uses {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows,
     {$ENDIF}
     {$ENDIF}
     Classes, SysUtils,
     {$IFDEF D6}
     Types,
     {$ENDIF}

     {$IFDEF D16}
     System.UITypes,
     {$ENDIF}

     {$IFDEF FMX}
     {$ELSE}
     Graphics,
     {$ENDIF}
     TeeProcs, Series, CandleCh, TeEngine, Chart;

type
  TBoxFillStyle=(bfsFillToClose, bfsOutline);

  TEquiVolumeSeries=class(TCandleSeries)
  private
    { Private declarations }
    FBoxFillStyle : TBoxFillStyle;
    FVolumeValues : TChartValueList;
    FVolumePointer : TSeriesPointer;

    FCumulativeGap : Integer;
    FTotalGap : Integer;
    FPointGap : Integer;
    FCalcPassed : Boolean;
    FTotalVolume : TChartValue;
    FPublicValuePerPixel : TChartValue;

    OldIncrement : TAxisValue;
    OldLabelsAngle : Integer;
    OldLabelsSize : Integer;
    OldCalcX : TAxisCalcPos;
    OldCalcPosPoint : TAxisCalcPosPoint;

    procedure AxisCalcMaxLabel(Sender: TChartAxis;
                   LabelIndex: Integer; var LabelValue: TAxisValue; var Stop: Boolean);
    Function AxisCalcPosLabels(aAxis : TChartAxis; Value : Integer):Integer;
    procedure ChartGetAxisLabel(Sender: TChartAxis;
                   Series: TChartSeries; ValueIndex: Integer; var LabelText: String);
    Function CalcXAccumulated(Const Value:TChartValue):Integer;
    procedure CustomizeAxis(const AChart:TCustomAxisPanel; Enable:Boolean);
    procedure GetMarkText(Sender:TChartSeries; ValueIndex:Integer; var MarkText:String);
    function GetVolumePointer: TSeriesPointer;
    procedure SetBoxFillStyle(const Value: TBoxFillStyle);
    procedure SetPointGap(const Value: Integer);
    procedure SetVolumeValues(const Value: TChartValueList);
    procedure SetVolumePointer(const Value: TSeriesPointer);
    function XLabelText(const Axis:TChartAxis; ValueIndex:Integer):String;
  protected
    Function IsPointInChartRect(ValueIndex : Integer) : Boolean; override;
    Function GetCandleWidth(ValueIndex : Integer) : Double;
    Function AdjustCalcPosPoint(const PixelVal : Integer) : TAxisValue;
    Function CalcXPixelGap(PixelVal : Integer) : Integer;
    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    Procedure DrawAxisLabels;
    procedure DrawValue(ValueIndex:Integer); override;
    procedure DoDrawPointer(isUp : Boolean; x, y : Integer);
    Procedure DrawAllValues; override;
    class Function GetEditorClass:String; override;
    Procedure GalleryChanged3D(Is3D:Boolean); override;
    procedure SetActive(Value:Boolean); override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
  public
    { Public declarations }
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;

    Function Add(const aDate : TDateTime; const Volume, Open, High, Low, Close : TChartValue) : Integer; overload;
    Function Add(const Volume, Open, High, Low, Close : TChartValue) : Integer; overload;
    Function Add(Index : Integer; const Volume, Open, High, Low, Close : TChartValue) : Integer; overload;

    Procedure CalcUnitSize(const Min,Max : TChartValue);
    Function CalcYPos(ValueIndex:Integer):Integer; override;
  published
    property BoxFillStyle:TBoxFillStyle read FBoxFillStyle write SetBoxFillStyle
                                      default bfsFillToClose;
    property VolumeValues:TChartValueList read FVolumeValues write SetVolumeValues;
    property VolumePointer:TSeriesPointer read GetVolumePointer write SetVolumePointer;
    property PointGap:Integer read FPointGap write SetPointGap;
  end;

implementation

uses
  TeeProco, TeeConst, TeCanvas;

type
  TShapeAccess=class(TTeeCustomShapeBrushPen);

{ TEquiVolumeSeries }

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

  UpCloseColor := RGB(192,192,252); // RGB(68,102,163);
  DownCloseColor := clRed; //RGB(255,192,128);
  FVolumeValues := TChartValueList.Create(Self, TeeMsg_EquiVolumeVolValues);

  Marks.Visible:=True;
  TShapeAccess(Marks).DefaultVisible:=True;

  Marks.ArrowLength:=-17;
  Marks.Arrow.Hide;
  Marks.Pen.Hide;
  Marks.Transparent:=True;
  Marks.Font.Size:=7;
end;

Destructor TEquiVolumeSeries.Destroy;
begin
  FVolumePointer.Free;
  inherited;
end;

function TEquiVolumeSeries.Add(const aDate: TDateTime; const Volume, Open, High,
                               Low, Close: TChartValue): Integer;
begin
  FVolumeValues.TempValue := Volume;
  result := AddCandle(aDate, Open, High, Low, Close);
end;

function TEquiVolumeSeries.Add(const Volume, Open, High, Low, Close: TChartValue): Integer;
begin
  result := Add(Count, Volume, Open, High, Low, Close);
end;

function TEquiVolumeSeries.Add(Index: Integer; const Volume, Open, High, Low,
                               Close: TChartValue): Integer;
begin
  FVolumeValues.TempValue := Volume;
  result := AddCandle(Count, Open, High, Low, Close);
end;

procedure TEquiVolumeSeries.AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False);
var data : Array of Single;
    days : Array[0..6,0..5] of Single;
    date : TDateTime;
    a1, a2, t, idx : Integer;
begin
  SetLength(data, 31);

  data[0]:=215;  data[1]:=15.10;  data[2]:=15.50;  data[3]:=12.30;  data[4]:=12.90;
  data[5]:=171;  data[6]:=12.90;  data[7]:=13.10;  data[8]:=11.50;  data[9]:=11.70;
  data[10]:=121;  data[11]:=11.70;  data[12]:=12.60;  data[13]:=10.90;  data[14]:=11.90;
  data[15]:=415;  data[16]:=12.20;  data[17]:=14.10;  data[18]:=11.95;  data[19]:=12.95;
  data[20]:=212;  data[21]:=12.95;  data[22]:=15.99;  data[23]:=12.80;  data[24]:=13.80;
  data[25]:=73;  data[26]:=14.10;  data[27]:=15.10;  data[28]:=12.00;  data[29]:=12.00;
  data[30]:=10;

  date := Now;

  for a1 :=0 to 5 do
    for a2 :=0 to 5 do
        days[a1,a2] := data[(a1*5)+a2];

  for t := 0 to numValues -1 do
    if (t < 6) then
      Add(date-(numValues-t), days[t, 0], days[t, 1], days[t, 2], days[t, 3], days[t, 4])
    else
    Begin
      idx := 5 - RandomValue(5);
      Add(date-(numValues-t), days[idx, 0], days[idx, 1], days[idx, 2], days[idx, 3], days[idx, 4])
    end;

  data:=nil;
end;

function TEquiVolumeSeries.AdjustCalcPosPoint(const PixelVal: Integer): TAxisValue;
begin
  result := OldCalcPosPoint(CalcXPixelGap(PixelVal));
end;

procedure TEquiVolumeSeries.Assign(Source: TPersistent);
begin
  if Source is TEquiVolumeSeries then
  With TEquiVolumeSeries(Source) do
  begin
    Self.FBoxFillStyle :=FBoxFillStyle;
    Self.FPointGap     :=FPointGap;
    Self.VolumePointer :=FVolumePointer;
  end;

  inherited;
end;

procedure TEquiVolumeSeries.AxisCalcMaxLabel(Sender: TChartAxis;
  LabelIndex: Integer; var LabelValue: TAxisValue; var Stop: Boolean);
begin
  if Visible and (GetHorizAxis <> nil) then
     CalcUnitSize(GetHorizAxis.Minimum, GetHorizAxis.Maximum);

  LabelValue:=0;
end;

function TEquiVolumeSeries.XLabelText(const Axis:TChartAxis; ValueIndex:Integer):String;
begin
  if XValues.DateTime then
     result:=FormatDateTime(DateTimeDefaultFormat(Axis.Maximum-Axis.Minimum),XValues[ValueIndex])
  else
     result:=FloatToStr(XValues[ValueIndex]);
end;

function TEquiVolumeSeries.AxisCalcPosLabels(aAxis: TChartAxis; Value: Integer): Integer;
var tmp : Integer;
begin
  if Visible and Assigned(ParentChart) and Assigned(aAxis) and (aAxis = GetHorizAxis)
     and ((aAxis.Maximum-aAxis.Minimum)>0) and XValues.DateTime then
        result:=ParentChart.MultiLineTextWidth(XLabelText(aAxis,0),tmp)
  else
    result:=Value;
end;

procedure TEquiVolumeSeries.CalcUnitSize(const Min, Max: TChartValue);
var i,
    gapCtr : Integer;
    tmp : TChartValue;
begin
  if not FCalcPassed then
  Begin
    FTotalVolume := 0;
    gapCtr:=0;

    if (Count > 0) and (min <> -1) then
    Begin
      for i := 0 to Count-1 do
      Begin
        if RemoveGaps then
           tmp:=i
        else
           tmp:=XValues.Value[i];

        if (tmp >= min) and (tmp <= max) then
        Begin
          FTotalVolume := FTotalVolume + FVolumeValues[i];
          Inc(gapCtr);
        end
        else
        if tmp > max then
          break;
      end;

      FTotalGap := gapCtr * PointGap;

      FCalcPassed := True;
    end;
  end;
end;

function TEquiVolumeSeries.CalcXAccumulated(Const Value:TChartValue):Integer;
Var r : TRect;
    idx, i, t : Integer;
    valPerPixel, accumulatedWidth : TChartValue;
begin
  if FTotalVolume > 0 then
  Begin
    r := ParentChart.ChartRect;
    valPerPixel  := ((r.Right - r.Left) - FTotalGap) / FTotalVolume;
    FPublicValuePerPixel := valPerPixel;

    accumulatedWidth := 0;

    idx := 0; // -1;
    i := FirstDisplayedIndex;
    if (i = -1) then i := 0;

    for t := 0 to XValues.Count - 1 do
    Begin
      if (XValues[t] >= GetHorizAxis.Minimum) then
      Begin
        i := t;
        idx := i - 1;
        break;
      end;
    end;

    if Value < XValues[i] then
       result := 0
    else
    begin

      while ((i < XValues.Count) and (XValues[i] < Value)) do
      Begin
        accumulatedWidth := accumulatedWidth + (FVolumeValues[i] * valPerPixel);
        Inc(i);
      end;

      FCumulativeGap := FPointGap * (i - (idx+1));

      if i < XValues.Count then
         accumulatedWidth := accumulatedWidth + ((FVolumeValues[i] * 0.5) * valPerPixel) + FCumulativeGap + FPointGap;

      result := Round(accumulatedWidth) + r.Left;
    end;
  end
  else
    result := OldCalcX(Value);
end;

function TEquiVolumeSeries.CalcXPixelGap(PixelVal: Integer): Integer;
begin
  result := PixelVal;
end;

function TEquiVolumeSeries.CalcYPos(ValueIndex: Integer): Integer;
begin
  result:=CalcYPosValue(LowValues[ValueIndex]);
end;

procedure TEquiVolumeSeries.ChartGetAxisLabel(Sender: TChartAxis;
  Series: TChartSeries; ValueIndex: Integer; var LabelText: String);
begin
  if Visible and Sender.Horizontal then
     LabelText := ' ';
end;

procedure TEquiVolumeSeries.DrawAllValues;
begin
  FCalcPassed := False;
  inherited;
  DrawAxisLabels;
end;

Procedure TEquiVolumeSeries.DrawAxisLabels;
var bottomAxisPos,
    strHeight,
    strWidth,
    lastAxisLabelPos,
    i : Integer;
    axis : TChartAxis;
    tmpCR, tmpR : TRect;
    tmpLabel : String;
    tmpX : Integer;
Begin
  if Assigned(ParentChart) then
  begin
    axis := GetHorizAxis;
    bottomAxisPos := axis.PosAxis;
    ParentChart.Canvas.UnClipRectangle;

    ParentChart.Canvas.Brush.Style:=bsClear;
    ParentChart.Canvas.BackMode:=cbmTransparent;
    
    ParentChart.Canvas.AssignFont3D(axis.LabelsFont);

    strHeight:= ParentChart.Canvas.FontHeight;
    strWidth := ParentChart.Canvas.TextWidth(XLabelText(Axis,0));

    if not XValues.DateTime then
       Inc(strWidth,2);

    if axis.LabelsAngle <> 0 then
       SwapInteger(strWidth,strHeight);

    UnClipCanvas(ParentChart.Canvas.ReferenceCanvas);

    tmpCR := ParentChart.ChartRect;
    tmpR := TeeRect(GetVertAxis.PosAxis - 5, bottomAxisPos,
                    tmpCR.Right +5,
                    ParentChart.ChartBounds.Bottom);

    ParentChart.Canvas.ClipRectangle(tmpR);

    lastAxisLabelPos := 0;

    for i := 0 to XValues.Count -1 do
    Begin
      tmpX:=axis.CalcPosValue(XValues[i]);

      if ((tmpX - (strWidth *0.5)) > lastAxisLabelPos) and (tmpX < ParentChart.Width) then
      Begin
        tmpLabel := XLabelText(Axis,i);

        Axis.DrawAxisLabel(tmpX, Axis.PosLabels, axis.LabelsAngle, tmpLabel);

        (*
        if axis.LabelsAngle = 0 then
          ParentChart.Canvas.TextOut(tmpX - (strWidth div 2),
                                     bottomAxisPos + axis.TickLength + 1, tmpLabel)
        else
          ParentChart.Canvas.RotateLabel(tmpX - (strWidth div 2),
                                        bottomAxisPos + strHeight + axis.TickLength + 1,
                                        tmpLabel, axis.LabelsAngle);
        *)

        lastAxisLabelPos := tmpX - 1 + (strWidth div 2);
      End;

    End;

    ParentChart.Canvas.UnClipRectangle;
  End;
End;

procedure TEquiVolumeSeries.DoDrawPointer(isUp: Boolean; x, y: Integer);
var tmp : Integer;
begin
  if Assigned(ParentChart) and Assigned(FVolumePointer) and FVolumePointer.Visible then
  begin
    if isUp then
    begin
      FVolumePointer.Style := psTriangle;
      tmp:=(FVolumePointer.VertSize div 2) + 4;
    end
    else
    begin
      FVolumePointer.Style := psDownTriangle;
      tmp:= -(FVolumePointer.VertSize+2);
    end;

    FVolumePointer.Draw(x, y + tmp, FVolumePointer.Color, FVolumePointer.Style);
  end;
end;

procedure TEquiVolumeSeries.DrawValue(ValueIndex: Integer);
Var
  //tmpColor,
  //tmpBorderPenColor,
  //tmpHighLowPenColor : TColor;

  //yOpen,
  yHigh,
  yLow,
  yClose,
  pointWidth,
  tmpLeftWidth,
  tmpRightWidth,
  x : Integer;
  tmpStyle  : TSeriesPointerStyle;
begin
  if (not IsNull(ValueIndex)) and (XValues[ValueIndex] >= GetHorizAxis.Minimum) then
  Begin
    //tmpBorderPenColor := Pen.Color;
    //tmpHighLowPenColor := HighLowPen.Color;

    if Assigned(OnGetPointerStyle) then
    Begin
      tmpStyle:=OnGetPointerStyle(Self, ValueIndex); // tmpColor
      Pointer.Style := tmpStyle;
    end
//    else
//       tmpStyle:=Pointer.Style
    ;

    x := CalcXPosValue(DateValues[ValueIndex]); // The horizontal position

    // Vertical positions of Open, High, Low & Close values for this point
    //yOpen := CalcYPosValue(OpenValues[ValueIndex]);
    yHigh := CalcYPosValue(HighValues[ValueIndex]);
    yLow := CalcYPosValue(LowValues[ValueIndex]);
    yClose := CalcYPosValue(CloseValues[ValueIndex]);

    pointWidth := Round(getCandleWidth(ValueIndex));
    tmpLeftWidth := pointWidth div 2;
    tmpRightWidth := pointWidth - tmpLeftWidth;

    with ParentChart.Canvas do
    Begin
      AssignBrush(Self.Brush);
      AssignVisiblePen(Self.Pen);

      if pointWidth > 0 then
      Begin
        if BoxFillStyle = bfsOutline then
        Begin
          Brush.Style:=bsClear;

          if (ValueIndex > 0) and ((CloseValues[ValueIndex] < CloseValues[ValueIndex - 1])) then
            Pen.Color := DownCloseColor
          else
            Pen.Color := clBlack; //MM todo - requires new outline pen

          RectangleWithZ(TeeRect(x - tmpLeftWidth, yHigh, x + tmpRightWidth + 1, yLow),MiddleZ);
        end
        else
        begin
          Brush.Color := clWhite;

          RectangleWithZ(TeeRect(x - tmpLeftWidth, yHigh, x + tmpRightWidth + 1, yLow),MiddleZ);

          if ValueIndex = 0 then
             Brush.Color := UpCloseColor
          else
          if CloseValues[ValueIndex] < CloseValues[ValueIndex - 1] then
          Begin
            Brush.Color := DownCloseColor;

            RectangleWithZ(TeeRect(x - tmpLeftWidth, yHigh, x + tmpRightWidth + 1, yClose),MiddleZ);

            if VolumePointer.Visible and (VolumeValues[ValueIndex] > VolumeValues[ValueIndex - 1]) then
               DoDrawPointer(False, x, yLow);
          end
          else
          begin
            Brush.Color := UpCloseColor;

            RectangleWithZ(TeeRect(x - tmpLeftWidth, yClose, x + tmpRightWidth + 1, yLow),MiddleZ);

            if VolumePointer.Visible and (VolumeValues[ValueIndex] > VolumeValues[ValueIndex - 1]) then
               DoDrawPointer(True, x, yHigh);
          End;
        End;
      End;
    End;
  End;
end;

procedure TEquiVolumeSeries.GalleryChanged3D(Is3D: Boolean);
begin
  inherited;

  if Assigned(ParentChart) then
  begin
    DownCloseColor:=ParentChart.GetDefaultColor(0);
    UpCloseColor:=RGB(192,192,252); //ParentChart.GetDefaultColor(3);
  end;
end;

function TEquiVolumeSeries.GetCandleWidth(ValueIndex: Integer): Double;
Var r : TRect;
    valPerPixel : TChartValue;
begin
  r := ParentChart.ChartRect;
  valPerPixel := (r.Right - r.Left - FTotalGap) / FTotalVolume;
  result := VolumeValues[valueIndex] * valPerPixel;
end;

class function TEquiVolumeSeries.GetEditorClass: String;
begin
  result:='TEquiVolumeEditor'; // Do not localize
end;

procedure TEquiVolumeSeries.GetMarkText(Sender: TChartSeries;
  ValueIndex: Integer; var MarkText: String);
var avgVol : TChartValue;
begin
  avgVol := VolumeValues.TotalABS / VolumeValues.Count;

  if avgVol=0 then
     MarkText:=''
  else
     MarkText := FormatFloat('0.0',(VolumeValues[ValueIndex] / avgVol));
end;

function TEquiVolumeSeries.IsPointInChartRect(ValueIndex: Integer): Boolean;
Var
  x,
  //yOpen,
  yHigh, yLow,
  //yClose,
  tmpLeftWidth, tmpRightWidth,
  pointWidth : Integer;
  point, r : TRect;
begin
  r := ParentChart.ChartRect;
  x := CalcXPosValue(DateValues[valueIndex]);

  //yOpen := CalcYPosValue(OpenValues[valueIndex]);
  yHigh := CalcYPosValue(HighValues[valueIndex]);
  yLow := CalcYPosValue(LowValues[valueIndex]);
  //yClose := CalcYPosValue(CloseValues[valueIndex]);

  pointWidth := Round(GetCandleWidth(valueIndex));
  tmpLeftWidth := pointWidth div 2;
  tmpRightWidth := pointWidth - tmpLeftWidth;

  point := TeeRect(x - tmpLeftWidth, yHigh, x + tmpRightWidth + 1, yLow);  //??FromLRTB

  result := TeeRectIntersects(r, point);
end;

type
  TAxesAccess=class(TChartAxes);
  TAxisAccess=class(TChartAxis);

procedure TEquiVolumeSeries.SetBoxFillStyle(const Value: TBoxFillStyle);
begin
  if FBoxFillStyle<>Value then
  begin
    FBoxFillStyle:=Value;
    Repaint;
  end;
end;

procedure TEquiVolumeSeries.SetParentChart(const Value: TCustomAxisPanel);
begin
  if ParentChart<>Value then
  begin
    CustomizeAxis(ParentChart,False);

    inherited;

    if Assigned(FVolumePointer) then
       FVolumePointer.ParentChart:=ParentChart;

    CustomizeAxis(ParentChart,Visible);
  end;
end;

procedure TEquiVolumeSeries.CustomizeAxis(const AChart:TCustomAxisPanel; Enable:Boolean);

  procedure SetAxis(const Value: TChartAxis);
  begin
    if not Assigned(Value) then
    Begin
      if GetHorizAxis <> nil then
      Begin
        With GetHorizAxis do
        Begin
          CalcXPosValue:=nil;
          CalcPosValue:=nil;
          CalcXPosValue:=OldCalcX;
          CalcPosValue:=CalcXPosValue;
          OldCalcX:=nil;
          CalcPosPoint:=oldCalcPosPoint;
          OnGetMarkText:=nil;

          if ParentChart <> nil then
             TAxesAccess(ParentChart.Axes).CalcPosLabels:= nil;

          TAxisAccess(GetHorizAxis).OnCalcMaxLabel := nil;
        end;
      end;
    end
    else
    Begin
      With GetHorizAxis do
      Begin
        OldCalcX:=nil;
        OldCalcPosPoint:=nil;

        OldCalcX:=CalcXPosValue;
        CalcXPosValue:=CalcXAccumulated;
        TAxesAccess(ParentChart.Axes).CalcPosLabels:=AxisCalcPosLabels;

        // Value.CheckSeriesSkipLabel := true; //MM future use

        TAxisAccess(GetHorizAxis).OnCalcMaxLabel := AxisCalcMaxLabel;

        OldCalcPosPoint := CalcPosPoint;
        CalcPosPoint := AdjustCalcPosPoint;
        OnGetMarkText := GetMarkText;

        CalcPosValue := CalcXPosValue;
      end
    end
  end;

begin
  if Enable then
  begin
    if Assigned(AChart) then
    begin
      RecalcGetAxis;

      with GetHorizAxis do
      begin
        LabelsGridIgnoresLabelSize:=True;

        OldLabelsAngle:=Texts.Angle;
        LabelsAngle:=90;

        OldLabelsSize:=Texts.Font.Size;
        LabelsFont.Size:=6;

        OldIncrement:=Increment;
        Increment:=DateTimeStep[TDateTimeStep(dtOneDay)];
      end;

      SetAxis(GetHorizAxis);
      AChart.OnGetAxisLabel:=ChartGetAxisLabel;
    end;
  end
  else
  begin
    if Assigned(AChart) then
    begin
      AChart.OnGetAxisLabel:=nil;

      SetAxis(nil);

      with GetHorizAxis do
      begin
        LabelsGridIgnoresLabelSize:=False;
        Increment:=OldIncrement;
        Texts.Angle:=OldLabelsAngle;
        Texts.Font.Size:=OldLabelsSize;
      end;
    end;
  end;
end;

procedure TEquiVolumeSeries.SetPointGap(const Value: Integer);
begin
  SetIntegerProperty(FPointGap,Value);
end;

procedure TEquiVolumeSeries.SetVolumePointer(const Value: TSeriesPointer);
begin
  if Assigned(Value) then
     VolumePointer.Assign(Value)
  else
     FreeAndNil(FVolumePointer);
end;

procedure TEquiVolumeSeries.SetVolumeValues(const Value: TChartValueList);
begin
  FVolumeValues.Assign(Value);
end;

function TEquiVolumeSeries.GetVolumePointer: TSeriesPointer;
begin
  if not Assigned(FVolumePointer) then
  begin
    FVolumePointer:=TSeriesPointer.Create(Self);
    FVolumePointer.Color :=  clYellow;
    FVolumePointer.HorizSize := 6;
    FVolumePointer.VertSize := 6;
  end;

  result:=FVolumePointer;
end;

procedure TEquiVolumeSeries.SetActive(Value: Boolean);
begin
  if Visible<>Value then
  begin
    inherited;
    CustomizeAxis(ParentChart,Visible);
  end;
end;

initialization
  RegisterTeeSeries(TEquiVolumeSeries, @TeeMsg_EquiVolumeSeries,
                                   @TeeMsg_GalleryFinancial,1);
finalization
  UnRegisterTeeSeries([TEquiVolumeSeries]);
end.
