diff --git a/ChartDisplay/ChartDisplayMain.pas b/ChartDisplay/ChartDisplayMain.pas index 8b5b378..5e41630 100644 --- a/ChartDisplay/ChartDisplayMain.pas +++ b/ChartDisplay/ChartDisplayMain.pas @@ -32,8 +32,8 @@ uses System.UITypes, System.Types, Vcl.FileCtrl, // Added for SelectDirectory - Myc.Trade.DataSeries, // For TTickData, FindOldestFilesPerSymbol, TryParseFileName [cite: 231, 237, 238] Myc.OHLCCache, // For TOHLC, IGenericCandleBuilder, TF_ constants + Myc.Trade.DataPoint, Myc.Trade.DataStream, Myc.ChartDataController, // Uses the updated DataController Vcl.Buttons; @@ -68,6 +68,7 @@ type procedure PaintBoxMouseLeave(Sender: TObject); procedure SymbolComboBoxChange(Sender: TObject); // Event handler for symbol selection change private + FDataServer: IDataServer; FActiveOHLCCache: TOHLC; FCandleWidthInPixels: Integer; FCandleSpacing: Integer; @@ -116,8 +117,8 @@ type FDescriptionText: string; // User-friendly description public constructor Create(ATickCount: Integer); - function Init(const ADataSet: TArray): Boolean; - function IsNewBar(AIndex: Integer; const ATick: TAskBid.TTick): Boolean; + function Init(const ADataSet: TArray>): Boolean; + function IsNewBar(AIndex: Integer; const ATick: TDataRecord): Boolean; function GetCaption: string; // Returns the Description for UI property Caption: string read GetCaption; // Interface property uses GetCaption end; @@ -129,8 +130,8 @@ type FDescriptionText: string; // User-friendly description public constructor Create(ATargetTickVolume: Integer); - function Init(const ADataSet: TArray): Boolean; - function IsNewBar(AIndex: Integer; const ATick: TAskBid.TTick): Boolean; + function Init(const ADataSet: TArray>): Boolean; + function IsNewBar(AIndex: Integer; const ATick: TDataRecord): Boolean; function GetCaption: string; // Returns the Description for UI property Caption: string read GetCaption; // Interface property uses GetCaption end; @@ -146,12 +147,12 @@ begin FDescriptionText := Format('Every %d Ticks', [FTickCountForNewBar]); end; -function TEveryNTicksBuilder.Init(const ADataSet: TArray): Boolean; +function TEveryNTicksBuilder.Init(const ADataSet: TArray>): Boolean; begin Result := True; end; -function TEveryNTicksBuilder.IsNewBar(AIndex: Integer; const ATick: TAskBid.TTick): Boolean; +function TEveryNTicksBuilder.IsNewBar(AIndex: Integer; const ATick: TDataRecord): Boolean; begin Result := (AIndex mod FTickCountForNewBar = 0); end; @@ -172,13 +173,13 @@ begin FCurrentTickInBarCount := 0; end; -function TVolumeAccumulationBuilder.Init(const ADataSet: TArray): Boolean; +function TVolumeAccumulationBuilder.Init(const ADataSet: TArray>): Boolean; begin FCurrentTickInBarCount := 0; Result := True; end; -function TVolumeAccumulationBuilder.IsNewBar(AIndex: Integer; const ATick: TAskBid.TTick): Boolean; +function TVolumeAccumulationBuilder.IsNewBar(AIndex: Integer; const ATick: TDataRecord): Boolean; begin Inc(FCurrentTickInBarCount); if FCurrentTickInBarCount >= FTargetTickVolumePerBar then @@ -259,6 +260,8 @@ var BuilderNCaptionKey, BuilderVCaptionKey: string; // These will be the unique keys BuilderNDescription, BuilderVDescription: string; begin + FDataServer := TAskBidServer.Create; + IsMemoryLow := function: Boolean var @@ -284,7 +287,7 @@ begin FDragStartX := 0; FDragStartDisplayIndex := 0; - FDataController := TChartDataController.Create(Memo.Lines); + FDataController := TChartDataController.Create(FDataServer, Memo.Lines); // Create and register generic builders BuilderN := TEveryNTicksBuilder.Create(DefaultEveryNTicksCount); @@ -331,6 +334,9 @@ begin FreeAndNil(FDataController); FreeAndNil(FBufferBitmap); FActiveOHLCCache := nil; + + FDataServer.ClearCache; + FDataServer := nil; end; procedure TChartForm.ClearAllCachesInForm; @@ -752,8 +758,8 @@ begin and (HoveredCandle.OriginalDataIndexEnd >= 0) and (HoveredCandle.OriginalDataIndexEnd < FDataController.GetTickDataLength) then // Use GetTickDataLength begin - UTCTimeStart := FDataController.GetTickDataItem(HoveredCandle.OriginalDataIndexStart).OADateTime; - UTCTimeEnd := FDataController.GetTickDataItem(HoveredCandle.OriginalDataIndexEnd).OADateTime; + UTCTimeStart := FDataController.GetTickDataItem(HoveredCandle.OriginalDataIndexStart).TimeStamp; + UTCTimeEnd := FDataController.GetTickDataItem(HoveredCandle.OriginalDataIndexEnd).TimeStamp; LocalTimeStart := TTimeZone.Local.ToLocalTime(UTCTimeStart); LocalTimeEnd := TTimeZone.Local.ToLocalTime(UTCTimeEnd); TimeZoneStr := TTimeZone.Local.GetAbbreviation(LocalTimeStart); // Get abbreviation for the local time zone @@ -1296,7 +1302,7 @@ begin if (CrosshairCandle.OriginalDataIndexStart >= 0) and (CrosshairCandle.OriginalDataIndexStart < FDataController.GetTickDataLength) then // Use GetTickDataLength begin - UTCTimeStart := FDataController.GetTickDataItem(CrosshairCandle.OriginalDataIndexStart).OADateTime; + UTCTimeStart := FDataController.GetTickDataItem(CrosshairCandle.OriginalDataIndexStart).TimeStamp; LocalTimeStart := TTimeZone.Local.ToLocalTime(UTCTimeStart); TimeStr := FormatDateTime('hh:nn:ss', LocalTimeStart); // Display time part diff --git a/common/Myc.ChartDataController.pas b/common/Myc.ChartDataController.pas index 6530710..eeb878d 100644 --- a/common/Myc.ChartDataController.pas +++ b/common/Myc.ChartDataController.pas @@ -5,7 +5,7 @@ interface uses System.SysUtils, System.Classes, System.Generics.Collections, System.StrUtils, Myc.Futures, - Myc.Trade.DataSeries, Myc.OHLCCache; + Myc.Trade.DataPoint, Myc.Trade.DataStream, Myc.OHLCCache; type TTimeframeSetting = record @@ -16,22 +16,23 @@ type TChartDataController = class private - FTickDataArray: TArray; + FTickDataArray: TArray>; FOHLCCacheList: TList; FTimeframeSettingsArray: TArray; // Now includes registered generic builders FMemoLog: TStrings; FGenericBuilders: TDictionary; // Keyed by TTimeframeSetting.Caption + FDataServer: IDataServer; procedure InitializeBaseTimeframeSettings; // Renamed for clarity function GetGenericBuilder(const ABuilderCaption: string): IGenericCandleBuilder; public - constructor Create(AMemoTarget: TStrings); + constructor Create(const ADataServer: IDataServer; AMemoTarget: TStrings); destructor Destroy; override; procedure LoadTickDataSeries(const AFileName: string); // GetOrBuildCache now uses a single ASelectedTimeframeCaption for all types of timeframes. - function GetOrBuildCache(const ASelectedTimeframeCaption: string; // e.g., "M1", "Auto", "Every 20 Ticks" + function GetOrBuildCache( const ASelectedTimeframeCaption: string; // e.g., "M1", "Auto", "Every 20 Ticks" AAutoAggCandleWidth: Integer; AAutoAggCandleSpacing: Integer; AAutoAggPaintBoxClientWidth: Integer; @@ -40,7 +41,7 @@ type procedure ClearAllDataAndCaches; function GetTickDataCount: Integer; function GetTickDataLength: Integer; - function GetTickDataItem(AIndex: Integer): TAskBid.TTick; + function GetTickDataItem(AIndex: Integer): TDataRecord; function HasData: Boolean; // ABuilderCaption is the unique key used for FTimeframeSettingsArray.Caption and FGenericBuilders key. @@ -61,11 +62,12 @@ const { TChartDataController } -constructor TChartDataController.Create(AMemoTarget: TStrings); +constructor TChartDataController.Create(const ADataServer: IDataServer; AMemoTarget: TStrings); var I: Integer; // Standard loop variable begin inherited Create; + FDataServer:=ADataServer; FMemoLog := AMemoTarget; FGenericBuilders := TDictionary.Create; @@ -210,7 +212,7 @@ procedure TChartDataController.LoadTickDataSeries(const AFileName: string); begin ClearAllDataAndCaches(); try - FTickDataArray := TAskBid.LoadDataSeries(AFileName).WaitFor(); + FTickDataArray := FDataServer.LoadDataSeries(AFileName).WaitFor(); if FMemoLog <> nil then FMemoLog.Add(Format('DataController: Loading tick records from series: %s', [AFileName])); @@ -405,7 +407,7 @@ begin Result := Length(FTickDataArray); end; -function TChartDataController.GetTickDataItem(AIndex: Integer): TAskBid.TTick; +function TChartDataController.GetTickDataItem(AIndex: Integer): TDataRecord; begin if (AIndex >= 0) and (AIndex < Length(FTickDataArray)) then Result := FTickDataArray[AIndex] @@ -413,7 +415,7 @@ begin begin if FMemoLog <> nil then FMemoLog.Add(Format('DataController: Attempted to access invalid tick data index %d. Count is %d.', [AIndex, Length(FTickDataArray)])); - Result := Default(TAskBid.TTick); + Result := Default(TDataRecord); end; end; diff --git a/common/Myc.OHLCCache.pas b/common/Myc.OHLCCache.pas index 66e33ed..ab480f3 100644 --- a/common/Myc.OHLCCache.pas +++ b/common/Myc.OHLCCache.pas @@ -35,7 +35,7 @@ interface uses System.Classes, System.SysUtils, System.Math, System.Generics.Collections, System.DateUtils, - Myc.Trade.DataSeries; + Myc.Trade.DataStream, Myc.Trade.DataPoint; const TF_AUTO_AGGREGATION = 0; @@ -56,8 +56,8 @@ type // Interface for generic candle building logic IGenericCandleBuilder = interface - function Init(const ADataSet: TArray): Boolean; // Changed DataSet to ADataSet for clarity - function IsNewBar(AIndex: Integer; const ATick: TAskBid.TTick): Boolean; // Changed Idx to AIndex, Tick to ATick + function Init(const ADataSet: TArray>): Boolean; // Changed DataSet to ADataSet for clarity + function IsNewBar(AIndex: Integer; const ATick: TDataRecord): Boolean; // Changed Idx to AIndex, Tick to ATick function GetCaption: string; // Method for the Caption property property Caption: string read GetCaption; end; @@ -80,7 +80,7 @@ type public constructor Create; procedure ClearCache; - procedure Build(const ATickData: TArray; + procedure Build(const ATickData: TArray>; ASelectedTimeframeSeconds: Int64; AAutoAggCandleWidth: Integer; AAutoAggCandleSpacing: Integer; @@ -188,7 +188,7 @@ begin Result := Length(FCachedCandles); end; -procedure TOHLC.Build(const ATickData: TArray; +procedure TOHLC.Build(const ATickData: TArray>; ASelectedTimeframeSeconds: Int64; AAutoAggCandleWidth: Integer; AAutoAggCandleSpacing: Integer; AAutoAggPaintBoxClientWidth: Integer; AMemoForLogging: TStrings; AGenericCandleBuilder: IGenericCandleBuilder = nil); // Parameter changed @@ -200,7 +200,7 @@ var CandleList: TList; TempCandle: TCachedCandle; LogTimePerCandleStr: string; - CurrentTick: TAskBid.TTick; + CurrentTick: TDataRecord; begin if AMemoForLogging <> nil then AMemoForLogging.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + Format(' - TOHLC.Build: TF Secs: %d...', @@ -353,8 +353,8 @@ begin SetLength(FCachedCandles, NumCandlesToCacheAuto); if (NumDataPoints > 1) and (NumCandlesToCacheAuto > 0) then begin - var FirstTickTimeVal_A := ATickData[0].OADateTime; - var LastTickTimeVal_A := ATickData[High(ATickData)].OADateTime; + var FirstTickTimeVal_A := ATickData[0].TimeStamp; + var LastTickTimeVal_A := ATickData[High(ATickData)].TimeStamp; var TotalDataDurationDaysVal_A := LastTickTimeVal_A - FirstTickTimeVal_A; if NumCandlesToCacheAuto > 0 then // Avoid division by zero if NumCandlesToCacheAuto is somehow 0 here begin @@ -432,7 +432,7 @@ begin CurrentTickIndex := 0; while CurrentTickIndex <= High(ATickData) do begin - BucketStartTimeOADate := Floor(ATickData[CurrentTickIndex].OADateTime / TimeframeIntervalOADays) * TimeframeIntervalOADays; + BucketStartTimeOADate := Floor(ATickData[CurrentTickIndex].TimeStamp / TimeframeIntervalOADays) * TimeframeIntervalOADays; BucketEndTimeOADate := BucketStartTimeOADate + TimeframeIntervalOADays; DataIndexStart := CurrentTickIndex; @@ -444,8 +444,8 @@ begin // Iterate through ticks that fall into the current bucket while (CurrentTickIndex <= High(ATickData)) and - (ATickData[CurrentTickIndex].OADateTime >= BucketStartTimeOADate) and - (ATickData[CurrentTickIndex].OADateTime < BucketEndTimeOADate) do + (ATickData[CurrentTickIndex].TimeStamp >= BucketStartTimeOADate) and + (ATickData[CurrentTickIndex].TimeStamp < BucketEndTimeOADate) do begin if ATickData[CurrentTickIndex].Data.Ask > HighPrice then HighPrice := ATickData[CurrentTickIndex].Data.Ask; // Changed if ATickData[CurrentTickIndex].Data.Ask < LowPrice then LowPrice := ATickData[CurrentTickIndex].Data.Ask; // Changed @@ -492,8 +492,8 @@ begin if Length(ATickData) > 0 then begin - var FirstTickTimeVal := ATickData[0].OADateTime; - var LastTickTimeVal := ATickData[High(ATickData)].OADateTime; + var FirstTickTimeVal := ATickData[0].TimeStamp; + var LastTickTimeVal := ATickData[High(ATickData)].TimeStamp; FTotalDataTimeSpanText := FormatTimeSpanFromSeconds((LastTickTimeVal - FirstTickTimeVal) * SecsPerDay_Double); if AMemoForLogging <> nil then AMemoForLogging.Add(Format('TOHLC.Build: Total data time span: %s.', [FTotalDataTimeSpanText]));