Unit AverageHistoricalVolume; Interface Uses Classes, DataNodes, CsvFileData; Const OneHour = 1.0 / 24; AHVPeriod = OneHour / 4; AHVPeriodMs = 15 * 60 * 1000; AHVStartTime = 6.5 * OneHour; AHVPeriods = 26; // Number of periods during market hours. AHVPreMarket = 0; AHVAfterMarket = AHVPeriods+1; AHVNone = -1; // This period is ever used. This valid is useful in // caching scenarios, where you initialize your cache to // this, so you know it's empty. Type // 0 is midnight to 6:30am. (All premarket.) // 1 is 6:30am - 6:45am. (First period of the market.) // Periods is 12:45pm - 1:00pm. (Last period of the market.) // Periods + 1 is 1:00p, - midnight TAHVPeriods = 0..AHVPeriods+1; TAHVPeriodSet = Set Of TAHVPeriods; TAverageHistoricalData = Class(TDataNodeWithStringKey) Private VolumeData : TFileOwnerDataNode; FSymbol : String; Constructor Create(Symbol : String); Protected Class Function CreateNew(Data : String) : TDataNodeWithStringKey; Override; Public Function IsValid : Boolean; Function GetVolume(P : TAHVPeriods) : Integer; Function GetPreMarketVolume : Integer; Function GetPostMarketVolume : Integer; Class Procedure Find(Symbol : String; OnChange : TThreadMethod; Out Node : TAverageHistoricalData; Out Link : TDataNodeLink); // Returns the period that containst the time. If the time // is on the boundary of two periods, it chooses the higher one. Class Function HigherTimeFrame(DateTime : TDateTime) : TAHVPeriods; // This returns latest time in the current time frame, and the first // time in the next time frame. This is just a time, with no date. Class Function TimeFrameEnd(Period : TAHVPeriods) : TDateTime; End; Implementation Uses SysUtils, DateUtils, Types, Math; Constructor TAverageHistoricalData.Create(Symbol : String); Var Link : TDataNodeLink; Begin Inherited Create; FSymbol := Symbol; TFileOwnerDataNode.Find('V_OvernightData.csv', NotifyListeners, VolumeData, Link); AddAutoLink(Link) End; Class Function TAverageHistoricalData.CreateNew(Data : String) : TDataNodeWithStringKey; Begin Result := Create(Data) End; Function TAverageHistoricalData.IsValid : Boolean; Begin Result := VolumeData.ContainsRow(FSymbol) End; Function TAverageHistoricalData.GetVolume(P : TAHVPeriods) : Integer; Var ValueStr : String; Begin ValueStr := VolumeData.Get(Format('%d', [P]), FSymbol); Result := StrToIntDef(ValueStr, 0) End; Function TAverageHistoricalData.GetPreMarketVolume : Integer; Var ValueStr : String; Begin ValueStr := VolumeData.Get('Pre', FSymbol); Result := StrToIntDef(ValueStr, 0) End; Function TAverageHistoricalData.GetPostMarketVolume : Integer; Var ValueStr : String; Begin ValueStr := VolumeData.Get('Post', FSymbol); Result := StrToIntDef(ValueStr, 0) End; Class Procedure TAverageHistoricalData.Find(Symbol : String; OnChange : TThreadMethod; Out Node : TAverageHistoricalData; Out Link : TDataNodeLink); Var TempNode : TDataNodeWithStringKey; Begin FindCommon(TAverageHistoricalData, Symbol, OnChange, TempNode, Link); Node := TempNode As TAverageHistoricalData End; Class Function TAverageHistoricalData.HigherTimeFrame(DateTime : TDateTime) : TAHVPeriods; Var Time : TDateTime; Begin Time := TimeOf(DateTime); If CompareDateTime(Time, AHVStartTime) = LessThanValue Then Result := 0 Else Result := Min(High(TAHVPeriods), Succ(MillisecondsBetween(Time, AHVStartTime) Div AHVPeriodMs)) //Result := Min(High(TAHVPeriods), Succ(Floor((Time-AHVStartTime)/AHVPeriod))) End; Class Function TAverageHistoricalData.TimeFrameEnd(Period : TAHVPeriods) : TDateTime; Begin If Period = AHVAfterMarket Then Result := 1.0 Else Result := IncMinute(AHVStartTime, Period * 15) End; End.