implementing DataStream
This commit is contained in:
@@ -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]),
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user