implementing DataStream

This commit is contained in:
Michael Schimmel
2025-06-07 23:54:10 +02:00
parent d53742fd12
commit c4152d25cd
2 changed files with 174 additions and 69 deletions
+11 -13
View File
@@ -68,7 +68,7 @@ type
procedure PaintBoxMouseLeave(Sender: TObject); procedure PaintBoxMouseLeave(Sender: TObject);
procedure SymbolComboBoxChange(Sender: TObject); // Event handler for symbol selection change procedure SymbolComboBoxChange(Sender: TObject); // Event handler for symbol selection change
private private
FDataServer: IDataServer<TAskBidItem>; FDataServer: IAuraDataServer<TAskBidItem>;
FActiveOHLCCache: TOHLC; FActiveOHLCCache: TOHLC;
FCandleWidthInPixels: Integer; FCandleWidthInPixels: Integer;
FCandleSpacing: Integer; FCandleSpacing: Integer;
@@ -87,7 +87,7 @@ type
FSymbolFiles: TArray<string>; // Stores full paths from FindOldestFilesPerSymbol FSymbolFiles: TArray<string>; // Stores full paths from FindOldestFilesPerSymbol
procedure PopulateTimeframeComboBox; procedure PopulateTimeframeComboBox;
procedure PopulateSymbolComboBox; procedure SetupDataServer;
procedure EnsureCacheForCurrentTimeframe(ARebuildIfPresent: Boolean = False); procedure EnsureCacheForCurrentTimeframe(ARebuildIfPresent: Boolean = False);
procedure ClearAllCachesInForm; procedure ClearAllCachesInForm;
procedure DrawChart(ACanvas: TCanvas; ARect: TRect); procedure DrawChart(ACanvas: TCanvas; ARect: TRect);
@@ -260,8 +260,6 @@ var
BuilderNCaptionKey, BuilderVCaptionKey: string; // These will be the unique keys BuilderNCaptionKey, BuilderVCaptionKey: string; // These will be the unique keys
BuilderNDescription, BuilderVDescription: string; BuilderNDescription, BuilderVDescription: string;
begin begin
FDataServer := TAskBidServer.Create;
IsMemoryLow := IsMemoryLow :=
function: Boolean function: Boolean
var var
@@ -280,6 +278,9 @@ begin
end; end;
end; end;
// Assuming we have a valid default value in the Path-Edit:
SetupDataServer;
FCandleWidthInPixels := InitialCandleWidth; FCandleWidthInPixels := InitialCandleWidth;
FCandleSpacing := InitialCandleSpacing; FCandleSpacing := InitialCandleSpacing;
FDisplayStartIndex := 0; FDisplayStartIndex := 0;
@@ -323,10 +324,6 @@ begin
FMouseCurrentlyInChartArea := False; FMouseCurrentlyInChartArea := False;
PaintBox.OnMouseLeave := PaintBoxMouseLeave; PaintBox.OnMouseLeave := PaintBoxMouseLeave;
FBufferBitmap := TBitmap.Create; FBufferBitmap := TBitmap.Create;
SetLength(FSymbolFiles, 0); // Initialize FSymbolFiles array
// Assuming we have a valid default value in the Path-Edit:
PopulateSymbolComboBox;
end; end;
procedure TChartForm.FormDestroy(Sender: TObject); procedure TChartForm.FormDestroy(Sender: TObject);
@@ -597,7 +594,7 @@ begin
begin begin
DataPathEdit.Text := selectedDirectory; // Update the edit box with the selected directory DataPathEdit.Text := selectedDirectory; // Update the edit box with the selected directory
Memo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + ' - Data directory selected: ' + selectedDirectory); Memo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + ' - Data directory selected: ' + selectedDirectory);
PopulateSymbolComboBox; SetupDataServer;
end end
else else
begin begin
@@ -1475,16 +1472,18 @@ begin
Memo.Lines.Add('GoToCurrent: Already showing the latest candles.'); Memo.Lines.Add('GoToCurrent: Already showing the latest candles.');
end; end;
procedure TChartForm.PopulateSymbolComboBox; procedure TChartForm.SetupDataServer;
var var
fileName: string; fileName: string;
pathValue, symbolValue: string; pathValue, symbolValue: string;
yearValue, monthValue: Integer; yearValue, monthValue: Integer;
i: Integer; i: Integer;
begin begin
FDataServer := TAuraTABFileServer.Create( DataPathEdit.Text );
var currTxt := SymbolComboBox.Text; var currTxt := SymbolComboBox.Text;
FSymbolFiles := FindOldestFilesPerSymbol(DataPathEdit.Text); // Find all relevant symbol files [cite: 238] FSymbolFiles := FDataServer.EnumerateAssetFiles;
SymbolComboBox.Items.Clear; SymbolComboBox.Items.Clear;
SymbolComboBox.ClearSelection; // Ensure no previous selection remains SymbolComboBox.ClearSelection; // Ensure no previous selection remains
SymbolComboBox.Text := ''; SymbolComboBox.Text := '';
@@ -1502,8 +1501,7 @@ begin
for i := Low(FSymbolFiles) to High(FSymbolFiles) do for i := Low(FSymbolFiles) to High(FSymbolFiles) do
begin begin
fileName := FSymbolFiles[i]; fileName := FSymbolFiles[i];
// TryParseFileName extracts symbol from the filename. [cite: 237] if TAuraTABFileServer.TryParseFileName(fileName, pathValue, symbolValue, yearValue, monthValue) then
if TryParseFileName(fileName, pathValue, symbolValue, yearValue, monthValue) then
begin begin
SymbolComboBox.Items.AddObject( SymbolComboBox.Items.AddObject(
Format('%s %.2d-%.4d', [symbolValue, monthValue, yearValue]), Format('%s %.2d-%.4d', [symbolValue, monthValue, yearValue]),
+163 -56
View File
@@ -3,15 +3,20 @@ unit Myc.ChartDataController;
interface interface
uses uses
System.SysUtils, System.Classes, System.Generics.Collections, System.StrUtils, System.SysUtils,
System.Classes,
System.Generics.Collections,
System.StrUtils,
Myc.Futures, Myc.Futures,
Myc.Trade.DataPoint, Myc.Trade.DataStream, Myc.OHLCCache; Myc.Trade.DataPoint,
Myc.Trade.DataStream,
Myc.OHLCCache;
type type
TTimeframeSetting = record TTimeframeSetting = record
Caption: string; // Unique key identifier (e.g., "M1", "Auto", "Every 20 Ticks") Caption: string; // Unique key identifier (e.g., "M1", "Auto", "Every 20 Ticks")
Description: string; // User-friendly description for UI Description: string; // User-friendly description for UI
Seconds: Int64; // TF_AUTO_AGGREGATION, TF_TICK_BY_TICK, TF_GENERIC_CALLBACK, or >0 Seconds: Int64; // TF_AUTO_AGGREGATION, TF_TICK_BY_TICK, TF_GENERIC_CALLBACK, or >0
end; end;
TChartDataController = class TChartDataController = class
@@ -21,22 +26,24 @@ type
FTimeframeSettingsArray: TArray<TTimeframeSetting>; // Now includes registered generic builders FTimeframeSettingsArray: TArray<TTimeframeSetting>; // Now includes registered generic builders
FMemoLog: TStrings; FMemoLog: TStrings;
FGenericBuilders: TDictionary<string, IGenericCandleBuilder>; // Keyed by TTimeframeSetting.Caption FGenericBuilders: TDictionary<string, IGenericCandleBuilder>; // Keyed by TTimeframeSetting.Caption
FDataServer: IDataServer<TAskBidItem>; FDataServer: IAuraDataServer<TAskBidItem>;
procedure InitializeBaseTimeframeSettings; // Renamed for clarity procedure InitializeBaseTimeframeSettings; // Renamed for clarity
function GetGenericBuilder(const ABuilderCaption: string): IGenericCandleBuilder; function GetGenericBuilder(const ABuilderCaption: string): IGenericCandleBuilder;
public public
constructor Create(const ADataServer: IDataServer<TAskBidItem>; AMemoTarget: TStrings); constructor Create(const ADataServer: IAuraDataServer<TAskBidItem>; AMemoTarget: TStrings);
destructor Destroy; override; destructor Destroy; override;
procedure LoadTickDataSeries(const AFileName: string); procedure LoadTickDataSeries(const AFileName: string);
// GetOrBuildCache now uses a single ASelectedTimeframeCaption for all types of timeframes. // 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(
AAutoAggCandleWidth: Integer; const ASelectedTimeframeCaption: string; // e.g., "M1", "Auto", "Every 20 Ticks"
AAutoAggCandleSpacing: Integer; AAutoAggCandleWidth: Integer;
AAutoAggPaintBoxClientWidth: Integer; AAutoAggCandleSpacing: Integer;
AForceRebuild: Boolean): TOHLC; AAutoAggPaintBoxClientWidth: Integer;
AForceRebuild: Boolean
): TOHLC;
procedure ClearAllDataAndCaches; procedure ClearAllDataAndCaches;
function GetTickDataCount: Integer; function GetTickDataCount: Integer;
@@ -45,9 +52,7 @@ type
function HasData: Boolean; function HasData: Boolean;
// ABuilderCaption is the unique key used for FTimeframeSettingsArray.Caption and FGenericBuilders key. // ABuilderCaption is the unique key used for FTimeframeSettingsArray.Caption and FGenericBuilders key.
procedure RegisterGenericBuilder(const ABuilderCaption: string; procedure RegisterGenericBuilder(const ABuilderCaption: string; const ABuilderDescription: string; ABuilder: IGenericCandleBuilder);
const ABuilderDescription: string;
ABuilder: IGenericCandleBuilder);
// GetGenericBuilderCaptions is removed. Use GetAvailableTimeframes. // GetGenericBuilderCaptions is removed. Use GetAvailableTimeframes.
function GetAvailableTimeframes: TArray<TTimeframeSetting>; function GetAvailableTimeframes: TArray<TTimeframeSetting>;
end; end;
@@ -62,12 +67,12 @@ const
{ TChartDataController } { TChartDataController }
constructor TChartDataController.Create(const ADataServer: IDataServer<TAskBidItem>; AMemoTarget: TStrings); constructor TChartDataController.Create(const ADataServer: IAuraDataServer<TAskBidItem>; AMemoTarget: TStrings);
var var
I: Integer; // Standard loop variable I: Integer; // Standard loop variable
begin begin
inherited Create; inherited Create;
FDataServer:=ADataServer; FDataServer := ADataServer;
FMemoLog := AMemoTarget; FMemoLog := AMemoTarget;
FGenericBuilders := TDictionary<string, IGenericCandleBuilder>.Create; FGenericBuilders := TDictionary<string, IGenericCandleBuilder>.Create;
@@ -105,20 +110,59 @@ begin
InitialSettings := TList<TTimeframeSetting>.Create; InitialSettings := TList<TTimeframeSetting>.Create;
try try
// Define standard, non-generic timeframes // Define standard, non-generic timeframes
ts.Caption := 'Auto'; ts.Description := 'Auto (Screen Fit)'; ts.Seconds := TF_AUTO_AGGREGATION; InitialSettings.Add(ts); ts.Caption := 'Auto';
ts.Caption := 'Tick'; ts.Description := 'Tick-by-Tick'; ts.Seconds := TF_TICK_BY_TICK; InitialSettings.Add(ts); ts.Description := 'Auto (Screen Fit)';
ts.Seconds := TF_AUTO_AGGREGATION;
InitialSettings.Add(ts);
ts.Caption := 'Tick';
ts.Description := 'Tick-by-Tick';
ts.Seconds := TF_TICK_BY_TICK;
InitialSettings.Add(ts);
// The old "Generic Rule" placeholder is removed. Generic rules are added via RegisterGenericBuilder. // The old "Generic Rule" placeholder is removed. Generic rules are added via RegisterGenericBuilder.
ts.Caption := 'M1'; ts.Description := '1 Minute (M1)'; ts.Seconds := 60; InitialSettings.Add(ts); ts.Caption := 'M1';
ts.Caption := 'M5'; ts.Description := '5 Minutes (M5)'; ts.Seconds := 5 * 60; InitialSettings.Add(ts); ts.Description := '1 Minute (M1)';
ts.Caption := 'M10'; ts.Description := '10 Minutes (M10)'; ts.Seconds := 10 * 60; InitialSettings.Add(ts); ts.Seconds := 60;
ts.Caption := 'M15'; ts.Description := '15 Minutes (M15)'; ts.Seconds := 15 * 60; InitialSettings.Add(ts); InitialSettings.Add(ts);
ts.Caption := 'M30'; ts.Description := '30 Minutes (M30)'; ts.Seconds := 30 * 60; InitialSettings.Add(ts); ts.Caption := 'M5';
ts.Caption := 'H1'; ts.Description := '1 Hour (H1)'; ts.Seconds := 60 * 60; InitialSettings.Add(ts); ts.Description := '5 Minutes (M5)';
ts.Caption := 'H4'; ts.Description := '4 Hours (H4)'; ts.Seconds := 4 * 60 * 60; InitialSettings.Add(ts); ts.Seconds := 5 * 60;
ts.Caption := 'D1'; ts.Description := '1 Day (D1)'; ts.Seconds := dcSecsPerDayConst; InitialSettings.Add(ts); InitialSettings.Add(ts);
ts.Caption := 'W1'; ts.Description := '1 Week (W1)'; ts.Seconds := 7 * dcSecsPerDayConst; InitialSettings.Add(ts); ts.Caption := 'M10';
ts.Caption := 'MN1'; ts.Description := '1 Month (MN1)'; ts.Seconds := 30 * dcSecsPerDayConst; InitialSettings.Add(ts); ts.Description := '10 Minutes (M10)';
ts.Caption := 'Y1'; ts.Description := '1 Year (Y1)'; ts.Seconds := 365 * dcSecsPerDayConst; InitialSettings.Add(ts); ts.Seconds := 10 * 60;
InitialSettings.Add(ts);
ts.Caption := 'M15';
ts.Description := '15 Minutes (M15)';
ts.Seconds := 15 * 60;
InitialSettings.Add(ts);
ts.Caption := 'M30';
ts.Description := '30 Minutes (M30)';
ts.Seconds := 30 * 60;
InitialSettings.Add(ts);
ts.Caption := 'H1';
ts.Description := '1 Hour (H1)';
ts.Seconds := 60 * 60;
InitialSettings.Add(ts);
ts.Caption := 'H4';
ts.Description := '4 Hours (H4)';
ts.Seconds := 4 * 60 * 60;
InitialSettings.Add(ts);
ts.Caption := 'D1';
ts.Description := '1 Day (D1)';
ts.Seconds := dcSecsPerDayConst;
InitialSettings.Add(ts);
ts.Caption := 'W1';
ts.Description := '1 Week (W1)';
ts.Seconds := 7 * dcSecsPerDayConst;
InitialSettings.Add(ts);
ts.Caption := 'MN1';
ts.Description := '1 Month (MN1)';
ts.Seconds := 30 * dcSecsPerDayConst;
InitialSettings.Add(ts);
ts.Caption := 'Y1';
ts.Description := '1 Year (Y1)';
ts.Seconds := 365 * dcSecsPerDayConst;
InitialSettings.Add(ts);
FTimeframeSettingsArray := InitialSettings.ToArray; FTimeframeSettingsArray := InitialSettings.ToArray;
finally finally
InitialSettings.Free; InitialSettings.Free;
@@ -130,9 +174,11 @@ begin
Result := Copy(FTimeframeSettingsArray); // Returns a copy of the current (potentially dynamic) list Result := Copy(FTimeframeSettingsArray); // Returns a copy of the current (potentially dynamic) list
end; end;
procedure TChartDataController.RegisterGenericBuilder(const ABuilderCaption: string; procedure TChartDataController.RegisterGenericBuilder(
const ABuilderDescription: string; const ABuilderCaption: string;
ABuilder: IGenericCandleBuilder); const ABuilderDescription: string;
ABuilder: IGenericCandleBuilder
);
var var
NewTimeframeSetting: TTimeframeSetting; NewTimeframeSetting: TTimeframeSetting;
I: Integer; I: Integer;
@@ -150,7 +196,12 @@ begin
if CompareText(FTimeframeSettingsArray[I].Caption, ABuilderCaption) = 0 then if CompareText(FTimeframeSettingsArray[I].Caption, ABuilderCaption) = 0 then
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Cannot register generic builder. A timeframe (base or generic) with caption "%s" already exists.', [ABuilderCaption])); FMemoLog.Add(
Format(
'DataController: Cannot register generic builder. A timeframe (base or generic) with caption "%s" already exists.',
[ABuilderCaption]
)
);
Exit; Exit;
end; end;
end; end;
@@ -173,7 +224,12 @@ begin
// for an existing generic timeframe caption that was somehow registered differently. // for an existing generic timeframe caption that was somehow registered differently.
// For safety, let's log and replace if this happens. // For safety, let's log and replace if this happens.
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Warning - Generic builder key "%s" already in dictionary, but not found as a TimeframeSetting caption. Replacing builder interface.', [ABuilderCaption])); FMemoLog.Add(
Format(
'DataController: Warning - Generic builder key "%s" already in dictionary, but not found as a TimeframeSetting caption. Replacing builder interface.',
[ABuilderCaption]
)
);
FGenericBuilders.Remove(ABuilderCaption); FGenericBuilders.Remove(ABuilderCaption);
FGenericBuilders.Add(ABuilderCaption, ABuilder); FGenericBuilders.Add(ABuilderCaption, ABuilder);
end end
@@ -194,7 +250,12 @@ begin
FOHLCCacheList.Add(nil); // Use TList.Add method FOHLCCacheList.Add(nil); // Use TList.Add method
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Registered new generic builder timeframe: Caption="%s", Description="%s".', [ABuilderCaption, ABuilderDescription])); FMemoLog.Add(
Format(
'DataController: Registered new generic builder timeframe: Caption="%s", Description="%s".',
[ABuilderCaption, ABuilderDescription]
)
);
end; end;
end; end;
@@ -214,8 +275,7 @@ begin
try try
FTickDataArray := FDataServer.LoadDataSeries(AFileName).WaitFor(); FTickDataArray := FDataServer.LoadDataSeries(AFileName).WaitFor();
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Loading tick records from series: %s', FMemoLog.Add(Format('DataController: Loading tick records from series: %s', [AFileName]));
[AFileName]));
except except
on E: Exception do on E: Exception do
begin begin
@@ -226,9 +286,11 @@ begin
end; end;
// ASelectedTimeframeCaption is the UNIQUE key (e.g. "M1", "Auto", or "Every 20 Ticks") // ASelectedTimeframeCaption is the UNIQUE key (e.g. "M1", "Auto", or "Every 20 Ticks")
function TChartDataController.GetOrBuildCache(const ASelectedTimeframeCaption: string; function TChartDataController.GetOrBuildCache(
const ASelectedTimeframeCaption: string;
AAutoAggCandleWidth, AAutoAggCandleSpacing, AAutoAggPaintBoxClientWidth: Integer; AAutoAggCandleWidth, AAutoAggCandleSpacing, AAutoAggPaintBoxClientWidth: Integer;
AForceRebuild: Boolean): TOHLC; AForceRebuild: Boolean
): TOHLC;
var var
Index: Integer; Index: Integer;
CacheNeedsRebuild: Boolean; CacheNeedsRebuild: Boolean;
@@ -269,28 +331,48 @@ begin
FOHLCCacheList[Index] := CacheAtIndex; FOHLCCacheList[Index] := CacheAtIndex;
CacheNeedsRebuild := True; CacheNeedsRebuild := True;
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Cache for [%s] (Desc: "%s") is nil. Creating & Building...', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description])); Format(
'DataController: Cache for [%s] (Desc: "%s") is nil. Creating & Building...',
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description]
)
);
end end
else if not CacheAtIndex.IsValid then else if not CacheAtIndex.IsValid then
begin begin
CacheNeedsRebuild := True; CacheNeedsRebuild := True;
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Cache for [%s] (Desc: "%s") exists but is invalid. Rebuilding...', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description])); Format(
'DataController: Cache for [%s] (Desc: "%s") exists but is invalid. Rebuilding...',
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description]
)
);
end end
else if CacheAtIndex.AggregationTimeframeSeconds <> CurrentTimeframeSetting.Seconds then else if CacheAtIndex.AggregationTimeframeSeconds <> CurrentTimeframeSetting.Seconds then
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Aggregation timeframe (seconds) mismatch for [%s] (Desc: "%s"). Cache has %ds, requested %ds. Forcing rebuild.', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description, CacheAtIndex.AggregationTimeframeSeconds, CurrentTimeframeSetting.Seconds])); Format(
'DataController: Aggregation timeframe (seconds) mismatch for [%s] (Desc: "%s"). Cache has %ds, requested %ds. Forcing rebuild.',
[
CurrentTimeframeSetting.Caption,
CurrentTimeframeSetting.Description,
CacheAtIndex.AggregationTimeframeSeconds,
CurrentTimeframeSetting.Seconds
]
)
);
CacheNeedsRebuild := True; CacheNeedsRebuild := True;
end; end;
// If it's a generic callback timeframe, an additional check for builder matching might be needed if not forced. // If it's a generic callback timeframe, an additional check for builder matching might be needed if not forced.
// The TOHLC's ApproxTimePerCandleText stores "(BuilderCaption)" for generic builds. // The TOHLC's ApproxTimePerCandleText stores "(BuilderCaption)" for generic builds.
// ASelectedTimeframeCaption *is* the builder caption for generic rules. // ASelectedTimeframeCaption *is* the builder caption for generic rules.
if (CurrentTimeframeSetting.Seconds = TF_GENERIC_CALLBACK) and (not CacheNeedsRebuild) and Assigned(CacheAtIndex) and CacheAtIndex.IsValid then if (CurrentTimeframeSetting.Seconds = TF_GENERIC_CALLBACK)
and (not CacheNeedsRebuild)
and Assigned(CacheAtIndex)
and CacheAtIndex.IsValid then
begin begin
// For generic, CurrentTimeframeSetting.Caption is the Builder's caption. // For generic, CurrentTimeframeSetting.Caption is the Builder's caption.
// We check if the existing cache was built with this same builder. // We check if the existing cache was built with this same builder.
@@ -301,8 +383,17 @@ begin
if CacheAtIndex.ApproxTimePerCandleText <> ExpectedCacheBuilderInfo then if CacheAtIndex.ApproxTimePerCandleText <> ExpectedCacheBuilderInfo then
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Generic builder changed for timeframe key [%s] (Desc: "%s"). Cache has info "%s", current builder key implies info "%s". Forcing rebuild.', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description, CacheAtIndex.ApproxTimePerCandleText, ExpectedCacheBuilderInfo])); Format(
'DataController: Generic builder changed for timeframe key [%s] (Desc: "%s"). Cache has info "%s", current builder key implies info "%s". Forcing rebuild.',
[
CurrentTimeframeSetting.Caption,
CurrentTimeframeSetting.Description,
CacheAtIndex.ApproxTimePerCandleText,
ExpectedCacheBuilderInfo
]
)
);
CacheNeedsRebuild := True; CacheNeedsRebuild := True;
end; end;
end; end;
@@ -312,8 +403,12 @@ begin
if HasData then if HasData then
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Building cache for [%s] (Desc: "%s")...', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description])); Format(
'DataController: Building cache for [%s] (Desc: "%s")...',
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description]
)
);
if CurrentTimeframeSetting.Seconds = TF_GENERIC_CALLBACK then if CurrentTimeframeSetting.Seconds = TF_GENERIC_CALLBACK then
begin begin
@@ -322,8 +417,12 @@ begin
if not Assigned(LGenericBuilder) then if not Assigned(LGenericBuilder) then
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Cannot build cache for generic timeframe [%s] (Desc: "%s"). Builder with this caption not found in dictionary.', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description])); Format(
'DataController: Cannot build cache for generic timeframe [%s] (Desc: "%s"). Builder with this caption not found in dictionary.',
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description]
)
);
CacheAtIndex.ClearCache(); CacheAtIndex.ClearCache();
Result := CacheAtIndex; Result := CacheAtIndex;
Exit; Exit;
@@ -350,8 +449,12 @@ begin
else else
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Using existing valid cache for [%s] (Desc: "%s")', FMemoLog.Add(
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description])); Format(
'DataController: Using existing valid cache for [%s] (Desc: "%s")',
[CurrentTimeframeSetting.Caption, CurrentTimeframeSetting.Description]
)
);
end; end;
Result := CacheAtIndex; Result := CacheAtIndex;
end; end;
@@ -394,7 +497,9 @@ begin
// The current behavior is that builder registrations and the full list of timeframe definitions persist. // The current behavior is that builder registrations and the full list of timeframe definitions persist.
if FMemoLog <> nil then // This is the line (approx. 376) the user reported if FMemoLog <> nil then // This is the line (approx. 376) the user reported
FMemoLog.Add('DataController: Instance data and OHLC caches cleared. Registered builders and timeframe definitions (including custom ones) remain.'); FMemoLog.Add(
'DataController: Instance data and OHLC caches cleared. Registered builders and timeframe definitions (including custom ones) remain.'
);
end; end;
function TChartDataController.GetTickDataCount: Integer; function TChartDataController.GetTickDataCount: Integer;
@@ -414,7 +519,9 @@ begin
else else
begin begin
if FMemoLog <> nil then if FMemoLog <> nil then
FMemoLog.Add(Format('DataController: Attempted to access invalid tick data index %d. Count is %d.', [AIndex, Length(FTickDataArray)])); FMemoLog.Add(
Format('DataController: Attempted to access invalid tick data index %d. Count is %d.', [AIndex, Length(FTickDataArray)])
);
Result := Default(TDataRecord<TAskBidItem>); Result := Default(TDataRecord<TAskBidItem>);
end; end;
end; end;