{**********************************************}
{   TCandleSeries (derived from OHLCSeries)    }
{   TVolumeSeries (derived from TCustomSeries) }
{   TRSIFunction  (Resistance Strength Index)  }
{   TADXFunction                               }
{   TATRFunction  (Average True Range)         }
{   TCompressFunction (OHLC Compression)       }
{                                              }
{   Copyright (c) 1995-2020 by Steema Software }
{                                              }
{   TDarvasSeries                              }
{   Copyright (c) 2006-2020 by Narcís Calvet   }
{**********************************************}
unit CandleCh;
{$I TeeDefs.inc}

interface

{
  Financial TCandleSeries derives from TOHLCSeries (Open, High, Low & Close).
  See OHLChart.pas unit for TOHLCSeries source code.

  TCandleSeries overrides the TChartSeries.DrawValue method to paint its
  points in several financial styles (CandleStick, Bars, OpenClose, etc).

  TVolumeSeries overrides the TChartSeries.DrawValue method to paint
  points as thin vertical bars.

  TADXFunction is a commonly used financial indicator. It requires an
  OHLC (financial) series as the datasource.

  TATRFunction (Average True Range) is a commonly used financial indicator.
  It requires an OHLC (financial) series as the datasource.

  TRSIFunction (Resistence Strength Index) is a commonly used financial
  indicator. It requires an OHLC (financial) series as the datasource.
}
Uses {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows,
     {$ENDIF}
     {$ENDIF}
     Classes,

     {$IFDEF D18}
     System.Generics.Collections,
     {$ENDIF}

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

     {$IFDEF FMX}
     FMX.Types, System.Types,
     {$IFDEF D17}
     System.UIConsts,
     {$ENDIF}
     {$IFDEF D19}
     FMX.Graphics,
     {$ENDIF}

     {$ELSE}
     Graphics,
     {$IFDEF D9}
     Types,
     {$ENDIF}
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Canvas, FMXTee.Engine, FMXTee.Series, FMXTee.Series.OHLC
     {$ELSE}
     Series, OHLChart, TeEngine, TeCanvas
     {$ENDIF};

Const
  DefCandleWidth = 4;  { 2 + 1 + 2  Default width for Candle points }

type
  TCandleStyle=(csCandleStick,csCandleBar,csOpenClose,csLine);

  TCandleColorStyle=(cssRelativeToOpen, cssRelativeToClose);

  {$IFNDEF D11}
  TCandleItem=record
    yOpen  : Integer;
    yClose : Integer;
    yHigh  : Integer;
    yLow   : Integer;
    tmpX   : Integer;
    Left   : Integer;
    Right  : Integer;
  end;
  {$ENDIF}

  TCandleBorderColor=(cbUseColor, cbSameAsCandle);

  TCandleSeries=class(TOHLCSeries)
  private
    {$IFDEF D11}
    type
      TCandleItem=record
        yOpen  : Integer;
        yClose : Integer;
        yHigh  : Integer;
        yLow   : Integer;
        tmpX   : Integer;
        Left   : Integer;
        Right  : Integer;
      end;
  private
    {$ENDIF}

    FAutoSize       : Boolean;
    FBorderColor    : TCandleBorderColor;
    FCandleStyle    : TCandleStyle;
    FCandleWidth    : Integer;
    FColorStyle     : TCandleColorStyle;
    FDownClose      : TTeeGradient;
    FHighLowColor   : TCandleBorderColor;
    FHighLowPen     : TChartPen;
    FRemoveGaps     : Boolean;
    FShowCloseTick  : Boolean;
    FShowOpenTick   : Boolean;
    FUpClose        : TTeeGradient;
    FOpenTickColor  : TColor;
    FCloseTickColor : TColor;

    ISize           : Integer;
    OldP            : TPoint;

    procedure CalcItem(ValueIndex:Integer; out AItem:TCandleItem);
    function GetCandleDepth:Integer;
    Function GetDark3D:Boolean;
    function GetDownColor:TColor;
    Function GetDraw3D:Boolean;
    Function GetPen:TChartPen;
    function GetUpColor:TColor;

    procedure SetAutoSize(const Value:Boolean);
    procedure SetBorderColor(const Value: TCandleBorderColor);
    Procedure SetCandleDepth(const Value:Integer);
    procedure SetCandlePen(Value:TChartPen);
    Procedure SetCandleStyle(Value:TCandleStyle);
    Procedure SetCandleWidth(Value:Integer);
    procedure SetColorStyle(const Value: TCandleColorStyle);
    procedure SetDark3D(Value:Boolean);
    Procedure SetDownColor(const Value:TColor);
    procedure SetDownGradient(const Value: TTeeGradient);
    procedure SetDraw3D(Value:Boolean);
    procedure SetHighLowColor(const Value: TCandleBorderColor);
    procedure SetHighLowPen(const Value: TChartPen);
    procedure SetRemoveGaps(const Value:Boolean);
    Procedure SetShowCloseTick(Value:Boolean);
    Procedure SetShowOpenTick(Value:Boolean);
    Procedure SetUpColor(const Value:TColor);
    procedure SetUpGradient(const Value: TTeeGradient);
    Procedure SetOpenTickColor(const Value:TColor);
    Procedure SetCloseTickColor(const Value:TColor);
  protected
    Procedure AssignFormatting(Source:TChartSeries); override;
    Function CalculateColor(ValueIndex:Integer):TColor;
    function CalculateGradient(ValueIndex:Integer):TCustomTeeGradient;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    procedure DoBeforeDrawChart; override;
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    procedure DrawValue(ValueIndex:Integer); override;
    Procedure GalleryChanged3D(Is3D:Boolean); override;
    class Function GetEditorClass:String; override;
    function HasPointLabels:Boolean; override;
    Function IsPointInChartRect(ValueIndex:Integer):Boolean; override;
    Procedure PrepareForGallery; override;
    procedure RecalcAxisMargins(const Axis:TChartAxis; var R:TRect); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Function Add( const ADate:TDateTime;
                  const AOpen,AHigh,ALow,AClose:Double):Integer; overload;

    Function AddCandle( const ADate:TDateTime;
                        const AOpen,AHigh,ALow,AClose:Double;
                        const ALabel:String='';
                        AColor:TColor=clTeeColor ):Integer;

    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function Clicked(x,y:Integer):Integer; override;
    function ClickedCandle(ValueIndex:Integer; const P:TPoint):Boolean;
    Function LegendItemColor(LegendIndex:Integer):TColor; override;

    Function MaxXValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;

    property CandleDepth:Integer read GetCandleDepth write SetCandleDepth;
  published
    property Active;
    property ColorEachPoint;
    property ColorSource;
    property Cursor;
    property Depth;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    property Title;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property AutoSize:Boolean read FAutoSize write SetAutoSize default True;
    property BorderColor:TCandleBorderColor read FBorderColor write SetBorderColor default cbUseColor;
    property CandleStyle:TCandleStyle read FCandleStyle write SetCandleStyle
                                      default csCandleStick;
    property CandleWidth:Integer read FCandleWidth write SetCandleWidth
                                 default DefCandleWidth;
    property ColorStyle:TCandleColorStyle read FColorStyle write SetColorStyle
                                          default cssRelativeToOpen;

    property Draw3D:Boolean read GetDraw3D write SetDraw3D default True;
    property Dark3D:Boolean read GetDark3D write SetDark3D default True;
    property DownCloseColor:TColor read GetDownColor write SetDownColor
                                   default {$IFDEF FMX}claRed{$ELSE}clRed{$ENDIF};
    property DownCloseGradient:TTeeGradient read FDownClose write SetDownGradient;

    property HighLowColor:TCandleBorderColor read FHighLowColor write SetHighLowColor default cbUseColor;
    property HighLowPen:TChartPen read FHighLowPen write SetHighLowPen;

    property OpenTickColor:TColor read FOpenTickColor write SetOpenTickColor
                                  default clNone;
    property CloseTickColor:TColor read FCloseTickColor write SetCloseTickColor
                                   default clNone;
    property ShowCloseTick:Boolean read FShowCloseTick write SetShowCloseTick
                                   default True;
    property ShowOpenTick:Boolean read FShowOpenTick write SetShowOpenTick
                                  default True;
    property UpCloseColor:TColor read GetUpColor write SetUpColor
                                 default {$IFDEF FMX}claWhite{$ELSE}clWhite{$ENDIF};
    property UpCloseGradient:TTeeGradient read FUpClose write SetUpGradient;

    property Pen:TChartPen read GetPen write SetCandlePen;

    property RemoveGaps:Boolean read FRemoveGaps write SetRemoveGaps default False;

    property Transparency;
  end;

  { Used in financial charts for Volume quantities (or OpenInterest) }
  { Overrides FillSampleValues to create random POSITIVE values }
  { Overrides DrawValue to paint a thin vertical bar }
  { Declares VolumeValues (same like YValues) }
  TVolumeSeries=class(TCustomSeries)
  private
    FUseYOrigin: Boolean;

    FOrigin: Double;
    IColor : TColor;
    Function GetVolumeValues:TChartValueList;
    Procedure PrepareCanvas(Forced:Boolean; AColor:TColor);
    procedure SetOrigin(const Value: Double);
    procedure SetUseOrigin(const Value: Boolean);
    Procedure SetVolumeValues(Value:TChartValueList);
  protected
    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    Procedure AssignFormatting(Source:TChartSeries); override;
    Procedure CalcHorizMargins(var LeftMargin,RightMargin:Integer); override;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    procedure DrawAllValues; override;
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    procedure DrawValue(ValueIndex:Integer); override;
    class Function GetEditorClass:String; override;
    function OriginPosition:Integer;
    Procedure PrepareForGallery; override;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; Var BackColor:TColor;
                                   Var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    Procedure SetSeriesColor(AColor:TColor); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Procedure Assign(Source:TPersistent); override;
    Function Clicked(x,y:Integer):Integer; override;
    Function NumSampleValues:Integer; override;
  published
    property Active;
    property ColorEachPoint;
    property ColorSource;
    property Cursor;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    property Title;
    property Transparency;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property LinePen;
    property UseYOrigin:Boolean read FUseYOrigin write SetUseOrigin default False;
    property VolumeValues:TChartValueList read GetVolumeValues write SetVolumeValues;
    property XValues;
    property YOrigin:Double read FOrigin write SetOrigin;
  end;

  { Financial A.D.X function }

  TADXFunction=class(TFinancialFunction)
  private
    IDMDown : TFastLineSeries;
    IDMUp   : TFastLineSeries;

    function GetDownPen: TChartPen;
    function GetUpPen: TChartPen;
    procedure SetDownPen(const Value: TChartPen);
    procedure SetUpPen(const Value: TChartPen);
  protected
    class Function GetEditorClass:String; override;
    procedure PrepareForGallery(const Chart:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure AddPoints(Source:TChartSeries); override;

    property DMDown:TFastLineSeries read IDMDown;
    property DMUp:TFastLineSeries read IDMUp;
  published
    property DownLinePen:TChartPen read GetDownPen write SetDownPen;
    property UpLinePen:TChartPen read GetUpPen write SetUpPen;
  end;

  { ATR, Average True Range }
  TATRFunction=class(TFinancialFunction)
  protected
    procedure PrepareForGallery(const Chart:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner:TComponent); override;
    procedure AddPoints(Source:TChartSeries); override;
  end;

  { RSI, Relative Strentgh Index }
  TRSIStyle=(rsiOpenClose,rsiClose);

  TRSIFunction = class(TTeeMovingFunction)
  private
    FStyle  : TRSIStyle;

    ISeries : TChartSeries;
    Opens   : TChartValueList;
    Closes  : TChartValueList;
    procedure SetStyle(Const Value:TRSIStyle);
  protected
    class function GallerySource:TChartSeriesClass; override;
    Function IsValidSource(const Value:TChartSeries):Boolean; override;
    procedure PrepareForGallery(const Chart:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner:TComponent); override;

    Function Calculate( const Series:TChartSeries;
                        FirstIndex,LastIndex:Integer):Double; override;
  published
    property Style:TRSIStyle read FStyle write SetStyle default rsiOpenClose;
  end;

  TDarvasSeries=class(TCandleSeries)
  private
    FBoxBrush : TTeeBrush;
    FBoxPen   : TChartPen;
    FBoxTransp: TTeeTransparency;
    FNumBoxes : Integer;

    procedure SetBoxBrush(const Value: TChartBrush);
    procedure SetBoxPen(const Value: TChartPen);
    procedure SetBoxTransp(const Value: TTeeTransparency);
  protected
    Procedure AssignFormatting(Source:TChartSeries); override;
    procedure CalculateBoxes;
    procedure DrawAllValues; override;
  public
    Boxes     : Array of TRect;
    BoxesRect : Array of TRect;

    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    Function ClickedBox(x,y:Integer):Integer;
    property NumBoxes:Integer read FNumBoxes;
  published
    property Active;
    property BoxBrush:TChartBrush read FBoxBrush write SetBoxBrush;
    property BoxPen:TChartPen read FBoxPen write SetBoxPen;
    property BoxTransparency:TTeeTransparency read FBoxTransp write SetBoxTransp default 70;
    property Brush;
    property ColorEachPoint;
    property ColorSource;
    property Cursor;
    property Depth;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    property Title;
    property Transparency;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;
  end;

  {
  TCompressFunction:

  The purpose of this function is to "compress" an OHLC series
  (for example a financial Candle series).

  ( OHLC stands for "Open High Low Close" in financial terms )

  To "compress" means to find out the OHLC of a date-time period from
  the original data.

  This function also works with non-OHLC source series.
  }

  TCompressionPeriod=( ocDay, ocWeek, ocMonth, ocBiMonth, ocQuarter, ocYear,
                       ocSecond, ocTenSecond, ocMinute, ocTwoMinutes,
                       ocFiveMinutes, ocTenMinutes, ocFifteenMinutes,
                       ocTwentyMinutes, ocHalfHour, ocHour, ocTwoHours,
                       ocThreeHour, ocSixHour, ocTwelveHour);

  TCompressGetDate=procedure(Sender:TTeeFunction; Source:TChartSeries;
                             ValueIndex:Integer; Var Date:TDateTime) of object;

  TCompressFunction=class(TTeeFunction)
  private
    FCompress: TCompressionPeriod;
    FOnGetDate: TCompressGetDate;
    procedure SetCompress(const Value: TCompressionPeriod);
  protected
    class function GetEditorClass: String; override;
    procedure PrepareForGallery(const Chart:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner: TComponent); override;
    procedure AddPoints(Source: TChartSeries); override;

    Procedure CompressSeries(Source:TChartSeries;
                             DestOHLC:TOHLCSeries;
                             Volume:TChartSeries=nil;
                             DestVolume:TChartSeries=nil);
  published
    property Compress:TCompressionPeriod read FCompress write SetCompress default ocWeek;
    property OnGetDate:TCompressGetDate read FOnGetDate write FOnGetDate;
  end;

implementation

uses
  Math, SysUtils,
  {$IFDEF D6}
  DateUtils,
  {$ENDIF}
  {$IFDEF FMX}
  FMXTee.Procs, FMXTee.Chart, FMXTee.ProConstants, FMXTee.Constants
  {$ELSE}
  TeeProcs, Chart, TeeProCo, TeeConst
  {$ENDIF}
  ;

{ TCandleSeries }
Constructor TCandleSeries.Create(AOwner: TComponent);
Begin
  inherited;

  FUpClose:=TChartGradient.Create(CanvasChanged);
  SetUpColor({$IFDEF FMX}claWhite{$ELSE}clWhite{$ENDIF});

  FDownClose:=TChartGradient.Create(CanvasChanged);
  SetDownColor({$IFDEF FMX}claRed{$ELSE}clRed{$ENDIF});

  FAutoSize      :=True;
  FCandleWidth   :=DefCandleWidth;
  FCandleStyle   :=csCandleStick;

  FShowOpenTick  :=True;
  FShowCloseTick :=True;

  Pointer.Draw3D :=True;

  FHighLowPen:=TChartPen.Create(CanvasChanged);
  FHighLowPen.Color:=clTeeColor;
  FHighLowPen.EndStyle:=esSquare;

  FOpenTickColor:=clNone;
  FCloseTickColor:=clNone;
end;

Destructor TCandleSeries.Destroy;
begin
  FHighLowPen.Free;
  FUpClose.Free;
  FDownClose.Free;
  inherited;
end;

Procedure TCandleSeries.SetShowOpenTick(Value:Boolean);
Begin
  SetBooleanProperty(FShowOpenTick,Value);
End;

Procedure TCandleSeries.SetShowCloseTick(Value:Boolean);
Begin
  SetBooleanProperty(FShowCloseTick,Value);
End;

Function TCandleSeries.CalculateColor(ValueIndex:Integer):TColor;
begin
  if InternalColor(ValueIndex)=clTeeColor then
     result:=CalculateGradient(ValueIndex).StartColor
  else
     result:=ValueColor[ValueIndex];
end;

function TCandleSeries.CalculateGradient(ValueIndex:Integer):TCustomTeeGradient;

  // color algorithm when open is equal to current close
  // (or when previous close is equal to current close)
  function CalculateSameValues:TCustomTeeGradient;
  var Current  : TChartValue;
      Previous : TChartValue;
  begin
    if ValueIndex=0 then
       result:=FUpClose // <-- first point
    else
    begin
      Current:=CloseValues.Value[ValueIndex];
      Previous:=CloseValues.Value[ValueIndex-1];

      if Current>Previous then
         result:=FUpClose
      else
      if Current<Previous then
         result:=FDownClose
      else
         result:=CalculateGradient(ValueIndex-1);
    end;
  end;

  function CalculateRelativeToOpen:TCustomTeeGradient;
  begin
    if OpenValues.Value[ValueIndex]>CloseValues.Value[ValueIndex] then
       result:=FDownClose
    else
    if OpenValues.Value[ValueIndex]<CloseValues.Value[ValueIndex] then
       result:=FUpClose
    else
       result:=CalculateSameValues;
  end;

begin
 if ColorStyle=cssRelativeToOpen then
    result:=CalculateRelativeToOpen
 else
 if ValueIndex=0 then result:=CalculateRelativeToOpen
                 else result:=CalculateSameValues;
end;

Function TCandleSeries.MaxXValue:TChartValue;
begin
  if RemoveGaps then
     result:=Count-1
  else
     result:=inherited MaxXValue;
end;

Function TCandleSeries.MinXValue:TChartValue;
begin
  if RemoveGaps then
     result:=0
  else
     result:=inherited MinXValue;
end;

Function TCandleSeries.CalcXPos(ValueIndex:Integer):Integer;
begin
  if RemoveGaps then
     result:=CalcXPosValue(ValueIndex)
  else
     result:=CalcXPosValue(DateValues.Value[ValueIndex]);
end;

procedure TCandleSeries.CalcItem(ValueIndex:Integer; out AItem:TCandleItem);
begin
  with AItem do
  begin
    tmpX:=CalcXPos(ValueIndex);

    { Vertical positions of Open, High, Low & Close values for this point }
    YOpen :=CalcYPosValue(OpenValues.Value[ValueIndex]);
    YHigh :=CalcYPosValue(HighValues.Value[ValueIndex]);
    YLow  :=CalcYPosValue(LowValues.Value[ValueIndex]);
    YClose:=CalcYPosValue(CloseValues.Value[ValueIndex]);

    Left:=CandleWidth div 2; { calc half Candle Width }
    Right:=CandleWidth-Left;

    // For GDI+, decrement 1 pixel to "center" better the candle around the X position
    if ParentChart.Canvas.SupportsAlpha then
       Dec(Right);
  end;
end;

// Called only when axis is Automatic (min and/or max)
procedure TCandleSeries.RecalcAxisMargins(const Axis:TChartAxis; var R:TRect);
var t,
    tmp1,
    tmp2    : Integer;
    tmpDiff : TeeNativeInt;
begin
  inherited;

  if Axis=GetHorizAxis then
  begin
    if AutoSize or (FCandleWidth=0) then
    begin
      if Count>0 then
      begin
        tmpDiff:=MaxLongint;

        tmp1:=CalcXPos(0);

        for t:=1 to Min(10,Count-1) do
        begin
          tmp2:=CalcXPos(t);

          tmpDiff:=Min(tmpDiff,tmp2-tmp1);
          tmp1:=tmp2;
        end;

        ISize:=Max(1,tmpDiff div 2);
      end;
    end;

    if ISize=0 then
       ISize:=DefCandleWidth;

    if Pointer.InflateMargins then
    begin
      if Axis.AutomaticMaximum then
         if R.Right<ISize then
            R.Right:=ISize;

      if Axis.AutomaticMinimum then
         if R.Left<ISize then
            R.Left:=ISize;
    end;
  end;
end;

Procedure TCandleSeries.DrawLegendShape(ValueIndex:Integer; Const Rect:TRect);
var tmpH : Integer;
    tmpV : Integer;
begin
  with ParentChart.Canvas do
  begin
    tmpH:=({$IFDEF FMX}Round{$ENDIF}(Rect.Right-Rect.Left) div 5)-1;
    tmpV:={$IFDEF FMX}Round{$ENDIF}(Rect.Bottom-Rect.Top) div 5;

    with Rect do
         Rectangle(Left+tmpH,Top+tmpV,Right-tmpH,Bottom-tmpV);

    tmpH:=({$IFDEF FMX}Round{$ENDIF}(Rect.Left+Rect.Right) div 2);

    DoVertLine(tmpH,Rect.Top,Rect.Top+tmpV-1);
    DoVertLine(tmpH,Rect.Bottom,Rect.Bottom-tmpV+1);
  end;
end;

type
  TPointerAccess=class(TSeriesPointer);
  TVisualAccess=class(TVisualBlock);
  TCanvas3DAccess=class(TCanvas3D);

procedure TCandleSeries.DrawValue(ValueIndex:Integer);
var
  tmpHover  : Boolean;

  Procedure CheckHighLowPen(const AColor:TColor);
  begin
    if not tmpHover then
    with ParentChart.Canvas do
    begin
      if HighLowColor=cbUseColor then
         if HighLowPen.Color=clTeeColor then
            AssignVisiblePenColor(HighLowPen,Self.Pen.Color)
         else
            AssignVisiblePen(HighLowPen)
      else
         AssignVisiblePenColor(HighLowPen,AColor);

      BackMode:=cbmTransparent;
    end;
  end;

  procedure TryDrawGradient(tmpR:TRect);
  var tmpGrad : TCustomTeeGradient;
  begin
    // if Up or Close gradient visible...
    if FUpClose.Visible or FDownClose.Visible then
    begin
      tmpGrad:=CalculateGradient(ValueIndex);

      if tmpGrad.Visible then
      begin
        if tmpR.Top>tmpR.Bottom then
           {$IFDEF FMX}SwapSingle{$ELSE}SwapInteger{$ENDIF}(tmpR.Top, tmpR.Bottom);

        if ParentChart.View3D then
           if Pointer.Draw3D then
              tmpR:=ParentChart.Canvas.CalcRect3D(tmpR,TPointerAccess(Pointer).GetStartZ)
           else
              tmpR:=ParentChart.Canvas.CalcRect3D(tmpR,TPointerAccess(Pointer).GetMiddleZ);

        tmpGrad.Draw(ParentChart.Canvas,tmpR);
        ParentChart.Canvas.Brush.Style:=bsClear;
      end;
    end;
  end;

var tmpItem   : TCandleItem;
    P         : TPoint;
    tmpFirst  : Integer;
    tmpColor  : TColor;
    tmpR      : TRect;
    tmpVisual : TVisualBlock;
    tmpBlend  : TTeeBlend;
begin
  if not IsNull(ValueIndex) then //TF02014524
  begin
    if Assigned(OnGetPointerStyle) then
       OnGetPointerStyle(Self,ValueIndex);

    { Prepare Pointer Pen and Brush styles }
    Pointer.PrepareCanvas(ParentChart.Canvas,clTeeColor);

    CalcItem(ValueIndex,tmpItem);

    with tmpItem,ParentChart,Canvas do
    begin
      if Transparency>0 then
         tmpBlend:=BeginBlending(TeeZeroRect,Transparency)
      else
         tmpBlend:=nil;

      tmpColor:=CalculateColor(ValueIndex);

      tmpHover:=PrepareHoverPen(ValueIndex);
      
      if (FCandleStyle=csCandleStick) or (FCandleStyle=csOpenClose) then
      begin
        { draw Candle Stick }

        CheckHighLowPen(tmpColor);

        if View3D and Pointer.Draw3D then
        begin
          tmpR.Top:=yClose;
          tmpR.Bottom:=yOpen;
          if tmpR.Top>tmpR.Bottom then
             {$IFDEF FMX}SwapSingle{$ELSE}SwapInteger{$ENDIF}(tmpR.Top,tmpR.Bottom);

          { Draw Candle Vertical Line from bottom to Low }
          if FCandleStyle=csCandleStick then
             VertLine3D(tmpX,tmpR.Bottom,yLow,MiddleZ);

          { Draw 3D Candle }
          Brush.Color:=tmpColor;

          if not tmpHover then
            if Self.Pen.Visible then
               if yOpen=yClose then AssignVisiblePenColor(Self.Pen,tmpColor)
                               else AssignVisiblePen(Self.Pen)
               else
                  Pen.Style:=psClear;

          tmpR.Left:=tmpX-tmpItem.Left;
          tmpR.Right:=tmpX+tmpItem.Right;

          tmpVisual:=Visuals[ValueIndex];

          if Assigned(tmpVisual) then
          begin
            TCanvas3DAccess(ParentChart.Canvas).BeginEntity('');

            TVisualAccess(tmpVisual).SetBounds(tmpR,TPointerAccess(Pointer).GetStartZ,
                          TPointerAccess(Pointer).GetEndZ,ParentChart.Height);

            TVisualAccess(tmpVisual).SetFormatting(Self.Pen,Self.Brush,False,tmpColor,Self.Transparency);
          end
          else
             Cube(tmpR,TPointerAccess(Pointer).GetStartZ,TPointerAccess(Pointer).GetEndZ,Pointer.Dark3D);

          TryDrawGradient(tmpR);

          CheckHighLowPen(tmpColor);

          { Draw Candle Vertical Line from Top to High }
          if FCandleStyle=csCandleStick then
             VertLine3D(tmpX,tmpR.Top,yHigh,MiddleZ);
        end
        else
        begin
          { Draw Candle Vertical Line from High to Low }
          if FCandleStyle=csCandleStick then
             if View3D then
                VertLine3D(tmpX,yHigh,yLow,MiddleZ)
             else
                DoVertLine(tmpX,yHigh,yLow);

          { remember that Y coordinates are inverted }

          { prevent zero height rectangles 5.02 }
          { in previous releases, an horizontal line was displayed instead
            of the small candle rectangle }
          if yOpen=yClose then Dec(yClose);

          { draw the candle }
          Brush.Color:=tmpColor;

          if not tmpHover then
            if Self.Pen.Visible then
               if (yOpen=yClose) or (FBorderColor=cbSameAsCandle) then
                  AssignVisiblePenColor(Self.Pen,tmpColor)
               else
                  AssignVisiblePen(Self.Pen)
            else
               Pen.Style:=psClear;

          if View3D then
          begin
            tmpR:=TeeRect(tmpX-tmpItem.Left,yOpen,tmpX+tmpItem.Right,yClose);
            TryDrawGradient(tmpR);
            RectangleWithZ(tmpR,MiddleZ);
          end
          else
          begin
            if not Self.Pen.Visible then
               if yOpen<yClose then Dec(yOpen) else Dec(yClose);

            tmpR:=TeeRect(tmpX-tmpItem.Left,yOpen,tmpX+tmpItem.Right+1,yClose);
            TryDrawGradient(tmpR);
            Rectangle(tmpR);
          end;
        end;
      end
      else
      if FCandleStyle=csLine then  // Line
      begin
        P:=TeePoint(tmpX,YClose);

        tmpFirst:=FirstDisplayedIndex;

        if (ValueIndex<>tmpFirst) and (not IsNull(ValueIndex)) then
        begin
          if not IsNull(ValueIndex-1) then
          begin
            if not tmpHover then
               AssignVisiblePenColor(Self.Pen,tmpColor);

            BackMode:=cbmTransparent;

            if View3D then LineWithZ(OldP,P,MiddleZ)
                      else Line(OldP,P);
          end;

          OldP:=P;
        end
        else if ValueIndex=tmpFirst then OldP:=P;
      end
      else
      begin // Draw Candle bar
        BackMode:=cbmTransparent;

        // Draw Candle Vertical Line from High to Low
        if View3D then
        begin
          if not tmpHover then
             AssignVisiblePenColor(Self.Pen,tmpColor);

          VertLine3D(tmpX,yLow,yHigh,MiddleZ);

          if ShowOpenTick then
          begin
            if not tmpHover then
               if not (FOpenTickColor=clNone) then
                  AssignVisiblePenColor(Self.Pen,FOpenTickColor);

            HorizLine3D(tmpX,tmpX-tmpItem.Left-1,yOpen,MiddleZ);
          end;
          if ShowCloseTick then
          begin
            if not tmpHover then
               if not (FCloseTickColor=clNone) then
                  AssignVisiblePenColor(Self.Pen,FCloseTickColor);

            HorizLine3D(tmpX,tmpX+tmpItem.Right+1,yClose,MiddleZ);
          end;
        end
        else
        begin
          if not tmpHover then
             AssignVisiblePenColor(Self.Pen,tmpColor);

          DoVertLine(tmpX,yLow,yHigh);

          if ShowOpenTick then
          begin
            if not tmpHover then
               if not (FOpenTickColor=clNone) then
                  AssignVisiblePenColor(Self.Pen,FOpenTickColor);

            DoHorizLine(tmpX,tmpX-tmpItem.Left-1,yOpen);
          end;
          if ShowCloseTick then
          begin
            if not tmpHover then
               if not (FCloseTickColor=clNone) then
                  AssignVisiblePenColor(Self.Pen,FCloseTickColor);

            DoHorizLine(tmpX,tmpX+tmpItem.Right+1,yClose);
          end;
        end;
      end;

      if Transparency>0 then
         EndBlending(tmpBlend);
    end;
  end;
end;

Procedure TCandleSeries.SetUpColor(const Value:TColor);
Begin
  FUpClose.StartColor:=Value;
end;

Procedure TCandleSeries.SetDownColor(const Value:TColor);
Begin
  FDownClose.StartColor:=Value;
end;

Procedure TCandleSeries.SetCandleWidth(Value:Integer);
Begin
  SetIntegerProperty(FCandleWidth,Value);
end;

procedure TCandleSeries.SetCloseTickColor(const Value: TColor);
begin
  SetColorProperty(FCloseTickColor, Value);
end;

Procedure TCandleSeries.SetCandleStyle(Value:TCandleStyle);
Begin
  if FCandleStyle<>Value then
  begin
    FCandleStyle:=Value;
    Pointer.Visible:=CandleStyle<>csLine;
    Repaint;
  end;
end;

class Function TCandleSeries.GetEditorClass:String;
Begin
  result:='TCandleEditor'; // Do not localize
End;

function TCandleSeries.HasPointLabels:Boolean;
begin
  result:=RemoveGaps;
end;

Procedure TCandleSeries.GalleryChanged3D(Is3D:Boolean);
begin
  inherited;

  if Assigned(ParentChart) then
  begin
    DownCloseColor:=ParentChart.GetDefaultColor(2);
    UpCloseColor:=ParentChart.GetDefaultColor(5);
  end;
end;

Procedure TCandleSeries.PrepareForGallery;
Begin
  inherited;

  FillSampleValues(4);
  ColorEachPoint:=True;

  Pointer.Pen.Width:=2;
  CandleWidth:=12;
  AutoSize:=True;
  RemoveGaps:=True;
end;

Procedure TCandleSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TCandleSeries then
  With TCandleSeries(Source) do
  begin
    Self.FAutoSize      :=FAutoSize;
    Self.FBorderColor   :=FBorderColor;
    Self.FCandleWidth   :=FCandleWidth;
    Self.FCandleStyle   :=FCandleStyle;
    Self.FColorStyle    :=FColorStyle;
    Self.UpCloseGradient:=FUpClose;
    Self.DownCloseGradient:=FDownClose;
    Self.HighLowPen     :=FHighLowPen;
    Self.FHighLowColor  :=FHighLowColor;
    Self.FShowOpenTick  :=FShowOpenTick;
    Self.FShowCloseTick :=FShowCloseTick;
  end;

  inherited;
end;

procedure TCandleSeries.DoBeforeDrawChart;
begin
  inherited;

  ISize:=FCandleWidth;

  if ISize=0 then
     ISize:=DefCandleWidth;
end;

Function TCandleSeries.GetDraw3D:Boolean;
begin
  result:=Pointer.Draw3D;
end;

procedure TCandleSeries.SetDraw3D(Value:Boolean);
begin
  Pointer.Draw3D:=Value;
end;

Function TCandleSeries.GetDark3D:Boolean;
begin
  result:=Pointer.Dark3D;
end;

procedure TCandleSeries.SetDark3D(Value:Boolean);
begin
  Pointer.Dark3D:=Value;
end;

Function TCandleSeries.GetPen:TChartPen;
begin
  result:=Pointer.Pen;
end;

procedure TCandleSeries.SetCandlePen(Value:TChartPen);
begin
  Pointer.Pen.Assign(Value);
end;

Function TCandleSeries.AddCandle( const ADate:TDateTime;
                                  const AOpen,AHigh,ALow,AClose:Double;
                                  const ALabel:String='';
                                  AColor:TColor=clTeeColor ):Integer;
begin
  result:=AddOHLC(ADate,AOpen,AHigh,ALow,AClose,ALabel,AColor);
end;

// Add Candle OHLC, using Count (index) as "X" (for example to remove weekend gaps)
Function TCandleSeries.Add( const ADate:TDateTime;
                            const AOpen,AHigh,ALow,AClose:Double):Integer;
var ALabel : String;
begin
  DateTimeToString(ALabel,DateTimeDefaultFormat(ADate),ADate);
  result:=AddOHLC(Count,AOpen,AHigh,ALow,AClose,ALabel);
end;

class procedure TCandleSeries.CreateSubGallery(
  AddSubChart: TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_CandleBar);
  AddSubChart(TeeMsg_CandleNoOpen);
  AddSubChart(TeeMsg_CandleNoClose);
  AddSubChart(TeeMsg_NoBorder);
  AddSubChart(TeeMsg_Line);
end;

class procedure TCandleSeries.SetSubGallery(ASeries: TChartSeries;
  Index: Integer);
begin
  with TCandleSeries(ASeries) do
  begin
    GalleryChanged3D(ParentChart.View3D);

    // Reset properties that might have changed:
    Pen.Show;
    ShowOpenTick:=True;
    ShowCloseTick:=True;

    Case Index of
      0: CandleStyle:=csCandleStick;
      1: CandleStyle:=csCandleBar;
      2: begin CandleStyle:=csCandleBar; ShowOpenTick:=False; end;
      3: begin CandleStyle:=csCandleBar; ShowCloseTick:=False; end;
      4: begin CandleStyle:=csCandleStick; Pen.Hide; end;
      5: begin CandleStyle:=csLine; end;
    else
      inherited;
    end;
  end;
end;

function TCandleSeries.ClickedCandle(ValueIndex:Integer; const P:TPoint):Boolean;
var tmpItem   : TCandleItem;
    tmpTop    : Integer;
    tmpBottom : Integer;
    tmpFirst  : Integer;
    tmpTo     : TPoint;
begin
  result:=False;

  CalcItem(ValueIndex,tmpItem);

  with tmpItem do
  Begin
    if (FCandleStyle=csCandleStick) or (FCandleStyle=csOpenClose) then
    begin
      tmpTop:=yClose;
      tmpBottom:=yOpen;

      if tmpTop>tmpBottom then
         SwapInteger(tmpTop,tmpBottom);

      if ParentChart.View3D and Pointer.Draw3D then
      begin
        if (FCandleStyle=csCandleStick) and
           (
             PointInLine(P,tmpX,tmpBottom,tmpX,yLow) or
             PointInLine(P,tmpX,tmpTop,tmpX,yHigh)
           )  then
        begin
          result:=True;
          exit;
        end;

        if PointInRect(TeeRect(tmpX-tmpItem.Left,tmpTop,tmpX+tmpItem.Right,tmpBottom),P) then
           result:=True;
      end
      else
      begin
        if (FCandleStyle=csCandleStick) and
           PointInLine(P,tmpX,yLow,tmpX,yHigh) then
        begin
          result:=True;
          exit;
        end;

        if tmpTop=tmpBottom then
           Inc(tmpBottom);

        if ParentChart.View3D then
        begin
          if PointInRect(TeeRect(tmpX-tmpItem.Left,tmpTop,tmpX+tmpItem.Right,tmpBottom),P) then
             result:=True;
        end
        else
        begin
          if not Self.Pen.Visible then
             if yOpen<yClose then Dec(tmpBottom) else Dec(tmpTop);

          if PointInRect(TeeRect(tmpX-tmpItem.Left,tmpTop,tmpX+tmpItem.Right+1,tmpBottom),P) then
             result:=True;
        end;
      end;
    end
    else
    if CandleStyle=csLine then
    begin
      tmpFirst:=FirstDisplayedIndex;
      tmpTo:=TeePoint(tmpX,YClose);

      if ValueIndex<>tmpFirst then
         result:=PointInLine(P,OldP,tmpTo);

      OldP:=tmpTo;
    end
    else
      result:=PointInLine(P,tmpX,yLow,tmpX,yHigh) or
              (ShowOpenTick and PointInLine(P,tmpX,yOpen,tmpX-tmpItem.Left-1,yOpen)) or
              (ShowCloseTick and PointInLine(P,tmpX,yClose,tmpX+tmpItem.Right+1,yClose));
  end;
end;

function TCandleSeries.Clicked(x, y: Integer): Integer;
var t : Integer;
    P : TPoint;
begin
  result:=TeeNoPointClicked;

  if (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    if Assigned(ParentChart) then
       ParentChart.Canvas.Calculate2DPosition(X,Y,TPointerAccess(Pointer).GetStartZ);

    P:=TeePoint(x,y);

    for t:=FirstValueIndex to LastValueIndex do
        if ClickedCandle(t,P) then
        begin
          result:=t;
          break;
        end;
  end;
end;

function TCandleSeries.LegendItemColor(LegendIndex: Integer): TColor;
begin
  result:=CalculateColor(LegendIndex);
end;

function TCandleSeries.MaxYValue: TChartValue;
begin
  if CandleStyle=csLine then result:=CloseValues.MaxValue
                        else result:=inherited MaxYValue;
end;

function TCandleSeries.MinYValue: TChartValue;
begin
  if CandleStyle=csLine then result:=CloseValues.MinValue
                        else result:=inherited MinYValue;
end;

procedure TCandleSeries.SetRemoveGaps(const Value:Boolean);
begin
  SetBooleanProperty(FRemoveGaps,Value);
  CalcVisiblePoints:=not RemoveGaps;
end;

procedure TCandleSeries.SetBorderColor(const Value: TCandleBorderColor);
begin
  if FBorderColor<>Value then
  begin
    FBorderColor:=Value;
    Repaint;
  end;
end;

procedure TCandleSeries.SetHighLowColor(const Value: TCandleBorderColor);
begin
  if FHighLowColor<>Value then
  begin
    FHighLowColor:=Value;
    Repaint;
  end;
end;

procedure TCandleSeries.SetHighLowPen(const Value: TChartPen);
begin
  FHighLowPen.Assign(Value);
end;

procedure TCandleSeries.SetOpenTickColor(const Value: TColor);
begin
  SetColorProperty(FOpenTickColor, Value);
end;

procedure TCandleSeries.SetColorStyle(const Value: TCandleColorStyle);
begin
  if FColorStyle<>Value then
  begin
    FColorStyle:=Value;
    Repaint;
  end;
end;

function TCandleSeries.GetDownColor: TColor;
begin
  result:=FDownClose.StartColor;
end;

function TCandleSeries.GetUpColor:TColor;
begin
  result:=FUpClose.StartColor;
end;

function TCandleSeries.IsPointInChartRect(ValueIndex: Integer): Boolean;
var r : TRect;
    tmp : TCandleItem;
    tmpTop,
    tmpBottom : Integer;
begin
  r:=ParentChart.ChartRect;

  CalcItem(ValueIndex,tmp);

  with tmp do
  begin
    if yClose > yOpen then
    begin
      tmpTop:=yOpen;
      tmpBottom:=yClose;
    end
    else
    begin
      tmpTop:=yClose;
      tmpBottom:=yOpen;
    end;

    Case CandleStyle of
    csCandleStick,
      csCandleBar,
      csOpenClose : result:=TeeRectIntersects(r,TeeRect(tmpX - Left, tmpTop, tmpX + Right, tmpBottom));
    else
      result:=False;
    end;
  end;
end;

procedure TCandleSeries.SetDownGradient(const Value: TTeeGradient);
begin
  FDownClose.Assign(Value);
end;

procedure TCandleSeries.SetUpGradient(const Value: TTeeGradient);
begin
  FUpClose.Assign(Value);
end;

function TCandleSeries.GetCandleDepth: Integer;
begin
  result:=Pointer.Depth;
end;

procedure TCandleSeries.SetAutoSize(const Value:Boolean);
begin
  SetBooleanProperty(FAutoSize,Value);
end;

procedure TCandleSeries.SetCandleDepth(const Value: Integer);
begin
  Pointer.Depth:=Value;
end;

{ TVolumeSeries }
Constructor TVolumeSeries.Create(AOwner: TComponent);
begin
  inherited;

  DrawArea:=False;
  IMandatoryPen:=True;
  ClickableLine:=False;
  Pointer.Hide;
  FUseYOrigin:=False;
end;

Function TVolumeSeries.GetVolumeValues:TChartValueList;
Begin
  result:=YValues;
end;

Procedure TVolumeSeries.SetSeriesColor(AColor:TColor);
begin
  inherited;
  LinePen.Color:=AColor;
end;

Procedure TVolumeSeries.SetVolumeValues(Value:TChartValueList);
Begin
  SetYValues(Value);
end;

Procedure TVolumeSeries.PrepareCanvas(Forced:Boolean; AColor:TColor);
begin
  if Forced or (AColor<>IColor) then
  begin
    ParentChart.Canvas.AssignVisiblePenColor(LinePen,AColor);
    ParentChart.Canvas.BackMode:=cbmTransparent;
    IColor:=AColor;
  end;
end;

// Enable transparency here, if existing, only once and not at each DrawValue
// for each point
procedure TVolumeSeries.DrawAllValues;
var tmpBlend : TTeeBlend;
    tmpTransp : TTeeTransparency;
begin
  tmpTransp:=Max({$IFDEF FMX}100-{$ENDIF}Round(ParentChart.Canvas.ColorAlpha(Pen.Color)/2.55),Transparency);

  if tmpTransp>0 then
     tmpBlend:=ParentChart.Canvas.BeginBlending(ParentChart.ChartRect,tmpTransp)
  else
     tmpBlend:=nil;

  inherited;

  if tmpTransp>0 then
     ParentChart.Canvas.EndBlending(tmpBlend);
end;

procedure TVolumeSeries.DrawValue(ValueIndex:Integer);
var tmpForce : Boolean;
begin
  tmpForce:=HoverEnabled and (Selected.HoverIndex<>-1);

  PrepareCanvas(tmpForce or (ValueIndex=FirstDisplayedIndex), ValueColor[ValueIndex]);

  PrepareHoverPen(ValueIndex);

  { moves to x,y coordinates and draws a vertical bar to top or bottom,
    depending on the vertical Axis.Inverted property }

  with ParentChart do
  if View3D then
     Canvas.VertLine3D(CalcXPos(ValueIndex),CalcYPos(ValueIndex),OriginPosition,MiddleZ)
  else
     Canvas.DoVertLine(CalcXPos(ValueIndex),OriginPosition,CalcYPos(ValueIndex));
end;

function TVolumeSeries.OriginPosition:Integer;
begin
  if UseYOrigin then
     result:=CalcYPosValue(YOrigin)
  else
  With GetVertAxis do
       if Inverted then result:=IStartPos
                   else result:=IEndPos;
end;

Function TVolumeSeries.NumSampleValues:Integer;
Begin
  result:=40;
end;

Procedure TVolumeSeries.AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False);
Var t : Integer;
    s : TSeriesRandomBounds;
begin
  s:=RandomBounds(NumValues);

  with s do
  for t:=1 to NumValues do
  Begin
    AddXY(tmpX,RandomValue(Round(DifY) div 15));
    tmpX:=tmpX+StepX;
  end;
end;

Procedure TVolumeSeries.PrepareForGallery;
begin
  inherited;
  FillSampleValues(26);
  Pointer.InflateMargins:=True;
end;

class Function TVolumeSeries.GetEditorClass:String;
Begin
  result:='TVolumeSeriesEditor'; // Do not localize
End;

class procedure TVolumeSeries.CreateSubGallery(
  AddSubChart: TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Dotted);
  AddSubChart(TeeMsg_Colors);
  AddSubChart(TeeMsg_Origin);
end;

class procedure TVolumeSeries.SetSubGallery(ASeries: TChartSeries;
  Index: Integer);
begin
  With TVolumeSeries(ASeries) do
  Case Index of
    1: Pen.SmallDots:=True;
    2: ColorEachPoint:=True;
    3: UseYOrigin:=True;
  else inherited
  end;
end;

procedure TVolumeSeries.DrawLegendShape(ValueIndex: Integer;
  const Rect: TRect);
begin
  with Rect{$IFDEF FMX}.Round{$ENDIF} do
       ParentChart.Canvas.DoHorizLine(Left,Right,(Top+Bottom) div 2);
end;

procedure TVolumeSeries.Assign(Source: TPersistent);
begin
  if Source is TVolumeSeries then
  with TVolumeSeries(Source) do
  begin
    Self.FUseYOrigin:= UseYOrigin;
    Self.FOrigin    := YOrigin;
  end;

  inherited;
end;

Procedure TVolumeSeries.AssignFormatting(Source:TChartSeries);
begin
  inherited;
  Pen.Visible:=True; // Pen should be always visible
end;

procedure TVolumeSeries.SetOrigin(const Value: Double);
begin
  SetDoubleProperty(FOrigin,Value);
end;

procedure TVolumeSeries.SetUseOrigin(const Value: Boolean);
begin
  SetBooleanProperty(FUseYOrigin,Value);
end;

procedure TVolumeSeries.PrepareLegendCanvas(ValueIndex: Integer;
  var BackColor: TColor; var BrushStyle: {$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
begin
  PrepareCanvas(True,SeriesColor);
end;

function TVolumeSeries.Clicked(x, y: Integer): Integer;
var t    : Integer;
    P    : TPoint;
    tmpX : Integer;
    tmpOrigin : Integer;
begin
  if (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    P.X:=x;
    P.Y:=y;

    tmpOrigin:=OriginPosition;

    for t:=FirstValueIndex to LastValueIndex do
    begin
      tmpX:=CalcXPos(t);

      if PointInLine(P,TeePoint(tmpX,tmpOrigin),TeePoint(tmpX,CalcYPos(t)),
                     {$IFDEF FMX}Round{$ENDIF}(Pen.Width-1)) then
      begin
        result:=t;
        exit;
      end;
    end;
  end;

  result:=TeeNoPointClicked;
end;

procedure TVolumeSeries.CalcHorizMargins(var LeftMargin,
  RightMargin: Integer);
var tmp : Integer;
begin
  inherited;

  // Assume LinePen is always visible

  tmp:=1+Round(LinePen.Width*0.5);
  Inc(LeftMargin,tmp);
  Inc(RightMargin,tmp);
end;

{ TADXFunction }

Constructor TADXFunction.Create(AOwner: TComponent);
begin
  inherited;
  InternalSetPeriod(14);
  SingleSource:=True;
  HideSourceList:=True;

  IDMDown:=TFastLineSeries.Create(Self);
  HideSeries(IDMDown);
  IDMDown.SeriesColor:={$IFDEF FMX}claRed{$ELSE}clRed{$ENDIF};

  IDMUp:=TFastLineSeries.Create(Self);
  HideSeries(IDMUp);
  IDMUp.SeriesColor:={$IFDEF FMX}claGreen{$ELSE}clGreen{$ENDIF};
end;

procedure TADXFunction.AddPoints(Source: TChartSeries);

  Procedure PrepareSeries(const ASeries:TChartSeries);
  begin
    With ASeries do
    begin
      ParentChart:=ParentSeries.ParentChart;
      CustomVertAxis:=ParentSeries.CustomVertAxis;
      VertAxis:=ParentSeries.VertAxis;
      XValues.DateTime:=ParentSeries.XValues.DateTime;
      AfterDrawValues:=ParentSeries.AfterDrawValues;
      BeforeDrawValues:=ParentSeries.BeforeDrawValues;
    end;
  end;

  Function CalcADX(Index:Integer):Double;
  var tmpUp   : TChartValue;
      tmpDown : TChartValue;
      tmp     : TChartValue;
  begin
    Index:=Index-Round(Period);

    tmpUp:=IDMUp.YValues.Value[Index];
    tmpDown:=IDMDown.YValues.Value[Index];

    tmp:=tmpUp+tmpDown;

    if tmp=0 then
       result:=0
    else
       result:=(100*Abs(tmpUp-tmpDown)/tmp);
  end;

var tmpTR,
    tmpDMUp,
    tmpDMDown : TDoubleArray;
    t         : Integer;
    tt        : Integer;
    tmpClose  : Double;
    Closes,
    Highs,
    Lows      : TChartValueList;
    tmpTR2,
    tmpUp2,
    tmpDown2  : Double;
    tmpX      : Double;
    tmp       : Integer;
    tmpADX    : Double;
begin
  if (Period<2) or not IsValidSource(Source) then Exit;

  ParentSeries.Clear;
  IDMUp.Clear;
  IDMDown.Clear;
  PrepareSeries(IDMUp);
  PrepareSeries(IDMDown);

  if Source.Count>=(2*Period) then
  begin
    With TOHLCSeries(Source) do
    begin
      Closes:=CloseValues;
      Highs:=HighValues;
      Lows:=LowValues;
    end;

    SetLength(tmpTR, Source.Count);
    SetLength(tmpDMUp, Source.Count);
    SetLength(tmpDMDown, Source.Count);

    for t:=1 to Source.Count-1 do
    begin
      tmpClose:=Closes.Value[t-1];
      tmpTR[t]:=Highs.Value[t]-Lows.Value[t];
      tmpTR[t]:=Math.Max(tmpTR[t],Abs(Highs.Value[t]-tmpClose));
      tmpTR[t]:=Math.Max(tmpTR[t],Abs(Lows.Value[t]-tmpClose));

      if (Highs.Value[t]-Highs.Value[t-1])>(Lows.Value[t-1]-Lows.Value[t]) then
      begin
          tmpDMUp[t]:=Highs.Value[t]-Highs.Value[t-1];
          tmpDMUp[t]:=Math.Max(0,tmpDMUp[t]);
      end
      else
          tmpDMUp[t]:=0;

      if (Lows.Value[t-1]-Lows.Value[t])>(Highs.Value[t]-Highs.Value[t-1]) then
      begin
          tmpDMDown[t]:=Lows.Value[t-1]-Lows.Value[t];
          tmpDMDown[t]:=Math.Max(0,tmpDMDown[t]);
      end
      else
          tmpDMDown[t]:=0;
    end;

    tmpTR2:=0;
    tmpUp2:=0;
    tmpDown2:=0;

    tmp:=Round(Period);

    for t:=tmp to Source.Count-1 do
    begin
      if t=tmp then
      begin
        for tt:=1 to Round(Period) do
        begin
          tmpTR2  :=tmpTR2+tmpTR[tt];
          tmpUp2  :=tmpUp2+tmpDMUp[tt];
          tmpDown2:=tmpDown2+tmpDMDown[tt];
        end;
      end
      else
      begin
        tmpTR2:=tmpTR2-(tmpTR2/Period)+tmpTR[t];
        tmpUp2:=tmpUp2-(tmpUp2/Period)+tmpDMUp[t];
        tmpDown2:=tmpDown2-(tmpDown2/Period)+tmpDMDown[t];
      end;

      tmpX:=Source.XValues[t];
      IDMUp.AddXY( tmpX, 100*(tmpUp2/tmpTR2));
      IDMDown.AddXY( tmpX, 100*(tmpDown2/tmpTR2));
    end;

    tmpTR:=nil;
    tmpDMUp:=nil;
    tmpDMDown:=nil;
    tmpADX:=0;

    tmp:=Round((2*Period)-2);

    for t:=tmp to Source.Count-1 do
    begin
      if t=tmp then
      begin
        tmpADX:=0;

        for tt:=Round(Period) to tmp do
            tmpADX:=tmpADX+CalcADX(tt);

        tmpADX:=tmpADX/(Period-1);
      end
      else
      begin
        tmpADX:=((tmpADX*(Period-1))+(CalcADX(t)))/Period;
      end;

      ParentSeries.AddXY(Source.XValues[t],tmpADX);
    end;
  end;
end;

Destructor TADXFunction.Destroy;
begin
  {$IFDEF D18}
  IDMDown.DisposeOf;
  IDMUp.DisposeOf;
  {$ELSE}
  IDMDown.Free;
  IDMUp.Free;
  {$ENDIF}

  inherited;
end;

procedure TADXFunction.PrepareForGallery(const Chart:TCustomAxisPanel);
begin
  inherited;
  SplitAxes;

  DMDown.VertAxis:=aRightAxis;
  DMUp.VertAxis:=aRightAxis;
end;

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

function TADXFunction.GetDownPen: TChartPen;
begin
  result:=DMDown.Pen;
end;

function TADXFunction.GetUpPen: TChartPen;
begin
  result:=DMUp.Pen;
end;

procedure TADXFunction.SetDownPen(const Value: TChartPen);
begin
  DMDown.Pen:=Value;
end;

procedure TADXFunction.SetUpPen(const Value: TChartPen);
begin
  DMUp.Pen:=Value;
end;

{ TATRFunction }
Constructor TATRFunction.Create(AOwner: TComponent);
begin
  inherited;
  InternalSetPeriod(14);
  SingleSource:=True;
  HideSourceList:=True;
end;

procedure TATRFunction.AddPoints(Source: TChartSeries);
var tmpTR    : TDoubleArray;
    t        : Integer;
    tmpClose : Double;
    Closes,
    Highs,
    Lows      : TChartValueList;
    tmp      : Integer;
    tmpATR   : Double;
begin
  if (Period<1) or not IsValidSource(Source) then Exit;

  ParentSeries.Clear;

  if Source.Count>=Period then
  begin
    With TOHLCSeries(Source) do
    begin
      Closes:=CloseValues;
      Highs:=HighValues;
      Lows:=LowValues;
    end;

    SetLength(tmpTR, Source.Count);

    tmpTR[0]:=Highs.Value[0]-Lows.Value[0];

    for t:=1 to Source.Count-1 do
    begin
      tmpClose:=Closes.Value[t-1];
      tmpTR[t]:=Highs.Value[t]-Lows.Value[t];
      tmpTR[t]:=Math.Max(tmpTR[t],Abs(Highs.Value[t]-tmpClose));
      tmpTR[t]:=Math.Max(tmpTR[t],Abs(Lows.Value[t]-tmpClose));
    end;

    tmp:=Round(Period)-1;
    tmpATR:=0;

    for t:=0 to Source.Count-1 do
    begin

      if t<tmp then tmpATR:=tmpATR + tmpTR[t]
      else
      begin
        if t>tmp then tmpATR:=tmpATR*(Period-1);
        tmpATR:=(tmpATR + tmpTR[t])/Period;

        ParentSeries.AddXY(Source.XValues[t],tmpATR);
      end;
    end;

    tmpTR:=nil;
  end;
end;

procedure TATRFunction.PrepareForGallery(const Chart:TCustomAxisPanel);
begin
  inherited;
  SplitAxes;
end;

{ R.S.I. }
Constructor TRSIFunction.Create(AOwner: TComponent);
begin
  inherited;
  FStyle:=rsiOpenClose;
  SingleSource:=True;
  HideSourceList:=True;
end;

Function TRSIFunction.Calculate( const Series:TChartSeries;
                                 FirstIndex,LastIndex:Integer):Double;
var NumPoints : Integer;
    t         : Integer;
    tmpClose  : Double;
    Ups       : Double;
    Downs     : Double;
Begin
  if ISeries<>Series then
  begin // Cache lists during calculation
    Closes:=Series.GetYValueList(TeeMsg_ValuesClose);
    Opens :=Series.GetYValueList(TeeMsg_ValuesOpen);
    ISeries:=Series;
  end;

  Ups:=0;
  Downs:=0;

  With Series do
  Begin
    if Self.Style=rsiOpenClose then
    begin // use today Close and Open prices (do not use "yesterday" close)
      for t:=FirstIndex to LastIndex do
      Begin
        tmpClose:=Closes.Value[t];
        if Opens.Value[t]>tmpClose then Downs:=Downs+tmpClose
                                   else Ups  :=Ups  +tmpClose;
      end;
    end
    else
    begin // Use Close prices (today - yesterday)
      for t:=FirstIndex+1 to LastIndex do
      Begin
        tmpClose:=Closes.Value[t]-Closes.Value[t-1];
        if tmpClose<0 then Downs:=Downs-tmpClose
                      else Ups  :=Ups  +tmpClose;
      end;
    end;
  end;

  { Calculate RSI }
  NumPoints:=(LastIndex-FirstIndex)+1;
  Downs:=Downs/NumPoints;
  Ups  :=Ups  /NumPoints;
  if Downs<>0 then
  Begin
    result:=100.0 - ( 100.0 / ( 1.0+Abs(Ups/Downs) ) );
    if result<0   then result:=0 else
    if result>100 then result:=100;
  end
  else result:=100; // Special case, to avoid divide by zero
end;

// R.S.I. function needs an OHLC series values to calculate.
function TRSIFunction.IsValidSource(const Value: TChartSeries): Boolean;
begin
  result:=Value is TOHLCSeries;
end;

procedure TRSIFunction.PrepareForGallery(const Chart:TCustomAxisPanel);
begin
  inherited;
  Period:=10;
  SplitAxes;
  ParentSeries.GetVertAxis.SetMinMax(0,100);
  ParentSeries.GetVertAxis.AxisValuesFormat:='0%';
end;

// R.S.I. function needs a Candle series to show at Functions Gallery.
class function TRSIFunction.GallerySource: TChartSeriesClass;
begin
  result:=TCandleSeries;
end;

procedure TRSIFunction.SetStyle(const Value: TRSIStyle);
begin
  if Style<>Value then
  begin
    FStyle:=Value;
    ReCalculate;
  end;
end;

{ TDarvasSeries }
constructor TDarvasSeries.Create(AOwner: TComponent);
begin
  inherited;
  FBoxBrush:=TTeeBrush.Create(CanvasChanged);
  FBoxBrush.BackColor:=clTeeColor;

  FBoxPen:=CreateChartPen;

  FBoxTransp:=70;
end;

Destructor TDarvasSeries.Destroy;
begin
  FBoxBrush.Free;
  FBoxPen.Free;
  Boxes:=nil;
  BoxesRect:=nil;
  inherited;
end;

// Algorithm based on:
// http://www.gerryco.com/tech/darvas.html state diagram.
procedure TDarvasSeries.CalculateBoxes;
var
  BoxTop,
  BoxBottom : TChartValue;
  StartIndex,
  EndIndex,
  TopIndex,
  BottomIndex,
  Today,
  State     : Integer;
begin
  FNumBoxes:=0;
  Boxes:=nil;
  BoxesRect:=nil;

  State:=0;
  Today:=0;
  TopIndex:=-1;
  BottomIndex:=-1;
  BoxTop:=HighValues.MinValue;
  BoxBottom:=LowValues.MaxValue;
  StartIndex:=0;

  repeat
    case State of
      0: begin
           StartIndex:=Today;
           State:=1;
         end;

      1: if Today<Count then
         begin
           BoxTop:=HighValues.Items[Today];
           TopIndex:=Today;
           BottomIndex:=Today;
           Inc(Today);

           if Today<Count then
              if BoxTop>HighValues.Items[Today] then State:=2
                                                else State:=1;
         end
         else State:=5;

      2: if Today<Count then
         begin
           Inc(Today);

           if Today<Count then
              if BoxTop>HighValues.Items[Today] then State:=3
                                                else State:=1;
         end
         else State:=5;

      3: if Today<Count then
         begin
           BoxBottom:=LowValues.Items[Today];
           BottomIndex:=Today;
           Inc(Today);

           if Today<Count then
              if BoxTop>HighValues.Items[Today] then
                 if BoxBottom<LowValues.Items[Today] then State:=4
                                                     else State:=3
              else State:=1;
         end
         else State:=5;

      4: if Today<Count then
         begin
           Inc(Today);

           if Today<Count then
              if BoxTop>HighValues.Items[Today] then
                 if BoxBottom<LowValues.Items[Today] then State:=5
                                                     else State:=3
              else State:=1;
         end
         else State:=5;

      5: if Today<Count then
         begin
           Inc(Today);

           if Today<Count then
              if ((BoxTop<HighValues.Items[Today]) or
                  (BoxBottom>LowValues.Items[Today])) then
                 State:=6
              else
                 State:=5;
         end
         else State:=6;

      6: begin
           if Today<Count then EndIndex:=Today
                          else EndIndex:=Today-1;

           SetLength(Boxes,FNumBoxes+1);
           with Boxes[NumBoxes] do
           begin
             Left:=StartIndex;
             Top:=TopIndex;
             Right:=EndIndex;
             Bottom:=BottomIndex;
           end;

           SetLength(BoxesRect,FNumBoxes+1);
           with BoxesRect[NumBoxes] do
           begin
             Left:=CalcXPos(StartIndex);
             Top:=GetVertAxis.CalcYPosValue(HighValues.Value[TopIndex]);
             Right:=CalcXPos(EndIndex);
             Bottom:=GetVertAxis.CalcYPosValue(LowValues.Value[BottomIndex]);
           end;

           Inc(FNumBoxes);

           if Today<Count then State:=0
                          else State:=7;
        end;
    end;

  until State=7;
end;

procedure TDarvasSeries.DrawAllValues;
var tmpBlend : TTeeBlend;
    t        : Integer;
    tmpColor : TColor;
begin
  inherited;

  CalculateBoxes;

  with ParentChart.Canvas do
  begin
    if FBoxTransp>0 then
       tmpBlend:=BeginBlending(TeeZeroRect, Max(FBoxTransp,Transparency))
    else
       tmpBlend:=nil;

    try
      AssignVisiblePen(Self.FBoxPen);

      for t:=0 to NumBoxes-1 do
      begin
        if Assigned(tmpBlend) then
           if ParentChart.View3D then
              tmpBlend.SetRectangle(RectFromRectZ(BoxesRect[t],StartZ))
           else
              tmpBlend.SetRectangle(BoxesRect[t]);

        tmpColor:=Self.FBoxBrush.BackColor;
        if tmpColor=clTeeColor then
           tmpColor:=ParentChart.GetDefaultColor(1);

        AssignBrush(Self.FBoxBrush,tmpColor);

        if ParentChart.View3D then
           RectangleWithZ(BoxesRect[t],StartZ)
        else
           Rectangle(BoxesRect[t]);

        if Assigned(tmpBlend) then
           if t<(NumBoxes-1) then
              tmpBlend.DoBlend(Transparency);
      end;

    finally
      if FBoxTransp>0 then
         EndBlending(tmpBlend);
    end;
  end;
end;

function TDarvasSeries.ClickedBox(x, y: Integer): Integer;
var t : Integer;
begin
  result:=TeeNoPointClicked;

  if Assigned(ParentChart) then
     ParentChart.Canvas.Calculate2DPosition(X,Y,StartZ);

  for t:=0 to NumBoxes-1 do
      if PointInRect(BoxesRect[t],x,y) then
      begin
        result:=t;
        break;
      end;
end;

procedure TDarvasSeries.AssignFormatting(Source: TChartSeries);
begin
  if Source is TDarvasSeries then
  With TDarvasSeries(Source) do
  begin
    Self.BoxBrush:=FBoxBrush;
    Self.BoxPen:=FBoxPen;
    Self.FBoxTransp:=FBoxTransp;
  end;

  inherited;
end;

procedure TDarvasSeries.SetBoxBrush(const Value: TChartBrush);
begin
  FBoxBrush.Assign(Value);
end;

procedure TDarvasSeries.SetBoxPen(const Value: TChartPen);
begin
  FBoxPen.Assign(Value);
end;

procedure TDarvasSeries.SetBoxTransp(const Value: TTeeTransparency);
begin
  if FBoxTransp<>Value then
  begin
    FBoxTransp:=Value;
    Repaint;
  end;
end;

{ TCompressFunction }

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

  CanUsePeriod:=False;
  SingleSource:=True;
  HideSourceList:=True;
  FCompress:=ocWeek;
end;

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

procedure TCompressFunction.AddPoints(Source: TChartSeries);
begin
  if ParentSeries is TOHLCSeries then
     CompressSeries(Source,TOHLCSeries(ParentSeries),nil,nil);
end;

Procedure TCompressFunction.CompressSeries(Source:TChartSeries;
                                           DestOHLC:TOHLCSeries;
                                           Volume:TChartSeries=nil;
                                           DestVolume:TChartSeries=nil);
var t        : Integer;
    tmpGroup : Integer;
    OldGroup : Integer;
    tmp      : Integer;
    Year     : Word;
    Month    : Word;
    Day      : Word;
    Hour     : Word;
    Minute   : Word;
    Second   : Word;
    MilliSecond : Word;
    DoIt     : Boolean;
    tmpDate  : TDateTime;
    tmpValue : TChartValue;
    tmpList  : TChartValueList;
    tmpPeriod : Integer;
begin
  DestOHLC.Clear;
  if Assigned(DestVolume) then DestVolume.Clear;

  OldGroup:=0;

  tmpList:=ValueList(Source);
  tmpPeriod:=Round(Period);

  for t:=0 to Source.Count-1 do
  begin
    tmpDate:=Source.NotMandatoryValueList.Value[t];

    if tmpPeriod=0 then
    begin

      // user event
      if Assigned(FOnGetDate) then FOnGetDate(Self,Source,t,tmpDate);

      {$IFDEF D6}
      DecodeDateTime(tmpDate,Year,Month,Day,Hour,Minute,Second,MilliSecond);
      {$ELSE}
      DecodeDate(tmpDate,Year,Month,Day);
      DecodeTime(tmpDate,Hour,Minute,Second,MilliSecond);
      {$ENDIF}

      case Compress of
       // Date
         ocDay: tmpGroup:=Trunc(tmpDate);
        ocWeek: begin
                  tmpGroup:=DayOfWeek(tmpDate)-1;
                  if tmpGroup=0 then tmpGroup:=7;
                end;
       ocMonth: tmpGroup:=Month;
     ocBiMonth: tmpGroup:=(Month-1) div 2;
     ocQuarter: tmpGroup:=(Month-1) div 3;
        ocYear: tmpGroup:=Year;

     // Time

      ocSecond: tmpGroup:=Second;
   ocTenSecond: tmpGroup:=Second div 10;
      ocMinute: tmpGroup:=Minute;
  ocTwoMinutes: tmpGroup:=Minute div 2;
 ocFiveMinutes: tmpGroup:=Minute div 5;
  ocTenMinutes: tmpGroup:=Minute div 10;
ocFifteenMinutes: tmpGroup:=Minute div 15;
ocTwentyMinutes: tmpGroup:=Minute div 20;
    ocHalfHour: tmpGroup:=Minute div 30;
        ocHour: tmpGroup:=Hour;
    ocTwoHours: tmpGroup:=Hour div 2;
   ocThreeHour: tmpGroup:=Hour div 3;
     ocSixHour: tmpGroup:=Hour div 6;
     else
       // ocTwelveHour:
       tmpGroup:=Hour div 12;
      end;

      if Compress=ocWeek then DoIt:=tmpGroup<OldGroup
                         else DoIt:=tmpGroup<>OldGroup;

      OldGroup:=tmpGroup;
    end
    else DoIt:=(t mod tmpPeriod)=0;

    if (t=0) or DoIt then
    begin

      case PeriodAlign of
        paFirst: ;
        paCenter: ;
      else
        // paLast:
        
      end;

      if Source is TOHLCSeries then
      With TOHLCSeries(Source) do
        tmp:=DestOHLC.AddOHLC(
                  tmpDate,
                  OpenValues.Value[t],
                  HighValues.Value[t],
                  LowValues.Value[t],
                  CloseValues.Value[t])
      else
      With Source do
      begin
        tmpValue:=tmpList.Value[t];
        tmp:=DestOHLC.AddOHLC(
                  tmpDate,
                  tmpValue,
                  tmpValue,
                  tmpValue,
                  tmpValue);
      end;

      DestOHLC.Labels[tmp]:=Source.Labels[t];

      if Assigned(DestVolume) then
         DestVolume.AddXY(Source.NotMandatoryValueList.Value[t],
                          Volume.YValues.Value[t]);
    end
    else
    begin
      tmp:=DestOHLC.Count-1;

      if Source is TOHLCSeries then
      with TOHLCSeries(Source) do
      begin
        DestOHLC.CloseValues.Value[tmp]:=CloseValues.Value[t];
        DestOHLC.DateValues.Value[tmp] :=DateValues.Value[t];

        if HighValues[t]>DestOHLC.HighValues.Value[tmp] then
           DestOHLC.HighValues.Value[tmp]:=HighValues.Value[t];

        if LowValues[t]<DestOHLC.LowValues.Value[tmp] then
           DestOHLC.LowValues.Value[tmp]:=LowValues.Value[t];
      end
      else
      with Source do
      begin
        tmpValue:=tmpList.Value[t];
        DestOHLC.CloseValues.Value[tmp]:=tmpValue;
        DestOHLC.DateValues.Value[tmp] :=NotMandatoryValueList.Value[t];

        if tmpValue>DestOHLC.HighValues.Value[tmp] then
           DestOHLC.HighValues.Value[tmp]:=tmpValue;

        if tmpValue<DestOHLC.LowValues.Value[tmp] then
           DestOHLC.LowValues.Value[tmp]:=tmpValue;
      end;

      DestOHLC.Labels[tmp]:=Source.Labels[t];

      if Assigned(DestVolume) then
      begin
        With DestVolume.YValues do
             Value[tmp]:=Value[tmp]+Volume.YValues.Value[t];

        DestVolume.XValues.Value[tmp]:=Volume.XValues.Value[t];
      end;
    end;
  end;

  if Assigned(DestVolume) then
  begin
    DestVolume.XValues.Modified:=True;
    DestVolume.YValues.Modified:=True;
  end;
end;

procedure TCompressFunction.SetCompress(const Value: TCompressionPeriod);
begin
  if FCompress<>Value then
  begin
    FCompress:=Value;
    ReCalculate;
  end;
end;

Procedure TCompressFunction.PrepareForGallery(const Chart:TCustomAxisPanel);
begin
  if Chart.SeriesCount>1 then
     Chart[1].{$IFDEF AUTOREFCOUNT}DisposeOf{$ELSE}Free{$ENDIF};

  CreateNewSeries(Chart.Owner,Chart,TCandleSeries,TCompressFunction).VertAxis:=aRightAxis;

  ParentSeries:=Chart[1];
  ParentSeries.DataSource:=Chart[0];

  Chart.Axes.Left.EndPosition:=50;
  Chart.Axes.Right.StartPosition:=50;
end;

initialization
  RegisterTeeSeries( TCandleSeries, @TeeMsg_GalleryCandle,
                                    @TeeMsg_GalleryFinancial, 1);
  RegisterTeeSeries( TVolumeSeries, @TeeMsg_GalleryVolume,
                                    @TeeMsg_GalleryFinancial, 1);
  RegisterTeeSeries( TDarvasSeries, @TeeMsg_GalleryDarvas,
                                    @TeeMsg_GalleryFinancial, 1);

  RegisterTeeFunction( TADXFunction, @TeeMsg_FunctionADX,
                                     @TeeMsg_GalleryFinancial );
  RegisterTeeFunction( TATRFunction, @TeeMsg_FunctionATR,
                                     @TeeMsg_GalleryFinancial );
  RegisterTeeFunction( TRSIFunction, @TeeMsg_FunctionRSI,
                                     @TeeMsg_GalleryFinancial );
  RegisterTeeFunction( TCompressFunction, @TeeMsg_FunctionCompress,
                                          @TeeMsg_GalleryFinancial );

  TeeVolumeSeriesClass:=TVolumeSeries;
  TeeCandleSeriesClass:=TCandleSeries;
finalization
  UnRegisterTeeSeries([TCandleSeries,TVolumeSeries,TDarvasSeries]);
  UnRegisterTeeFunctions([ TADXFunction,TATRFunction,TRSIFunction, TCompressFunction ]);
end.
