Unit FastRunning; { These are the running up/down alerts without volume confirmation. These watch the movent of the best bid and ask. For symbols with no bid and ask, they watch the last print. } Interface Implementation Uses DataNodes, GenericDataNodes, AlertBase, StandardPlaceHolders, Prices, GenericL1DataNode, GenericTosDataNode, SimpleMarketData, DateUtils, Math, Classes; //////////////////////////////////////////////////////////////////////// // TRunningCommon //////////////////////////////////////////////////////////////////////// Type TRunningCommon = Class(TDataNodeWithStringKey) Private FState : TAboveOrBelow; FPriceIncrease, FQuality : Double; TickVolatilityData : TGenericDataNode; Init : Boolean; ppHigh, pHigh, High, ppLow, pLow, Low : Double; Bid, Ask : Double; UpdateTimeTag : TDateTime; Protected Constructor Create(Symbol : String); Virtual; Class Function CreateNew(Data : String) : TDataNodeWithStringKey; Override; Procedure NewData; Procedure GetCurrentValues(Out Valid : Boolean; Out LowerValue, HigherValue : Double); Virtual; Abstract; Public Property State : TAboveOrBelow Read FState; Property PriceIncrease : Double Read FPriceIncrease; Property Quality : Double Read FQuality; End; Class Function TRunningCommon.CreateNew(Data : String) : TDataNodeWithStringKey; Begin Result := Create(Data) End; Constructor TRunningCommon.Create(Symbol : String); Var Link : TDataNodeLink; Factory : IGenericDataNodeFactory; Begin Inherited Create; Factory := TGenericDataNodeFactory.FindFactory('TickVolatility').Duplicate; Factory.SetValue(SymbolNameKey, Symbol); Factory.Find(Nil, TickVolatilityData, Link); AddAutoLink(Link) End; Procedure TRunningCommon.NewData; Const OneMinute = 1.0 / 24.0 / 60.0; ThirtySeconds = OneMinute / 2.0; Var CurrentTimeTag : TDateTime; CutOff : Double; Procedure RefreshTimeTag; Var Time : TDateTime; Begin Time := TimeOf(CurrentTimeTag); Time := Ceil(Time / ThirtySeconds) * ThirtySeconds; UpdateTimeTag := DateOf(CurrentTimeTag) + Time End; Var LastAsk, LastBid, NewAsk, NewBid : Double; NewDataValid : Boolean; Begin FState := ccEqual; GetCurrentValues(NewDataValid, NewBid, NewAsk); If Not (TickVolatilityData.IsValid And NewDataValid) Then Begin Init := False; Exit End; If TickVolatilityData.GetDouble < MinVolatility Then Begin Init := False; Exit End; LastBid := Bid; LastAsk := Ask; Bid := NewBid; Ask := NewAsk; CurrentTimeTag := GetSubmitTime; If (Not Init) Or (CurrentTimeTag + OneMinute < UpdateTimeTag) // Should be no more than 30 seconds before UpdateTimeTag // This must be the start of a playback. Then Begin ppHigh := Ask; pHigh := Ask; High := Ask; ppLow := Bid; pLow := Bid; Low := Bid; RefreshTimeTag; Init := True; Exit End; If CurrentTimeTag > UpdateTimeTag + OneMinute Then Begin ppHigh := LastAsk; pHigh := LastAsk; High := LastAsk; ppLow := LastBid; pLow := LastBid; Low := LastBid; RefreshTimeTag End Else If CurrentTimeTag > UpdateTimeTag + ThirtySeconds Then Begin ppHigh := High; pHigh := High; High := LastAsk; ppLow := Low; pLow := Low; Low := LastBid; RefreshTimeTag End Else If CurrentTimeTag > UpdateTimeTag Then Begin ppHigh := pHigh; pHigh := High; High := LastAsk; ppLow := pLow; pLow := Low; Low := LastBid; RefreshTimeTag End; CutOff := TickVolatilityData.GetDouble / Sqrt(5); If ppLow - Ask > CutOff Then Begin FPriceIncrease := Bid - ppLow; FQuality := Sqr(OneMinute / (OneMinute - (UpdateTimeTag - CurrentTimeTag))) * ((ppLow - Ask) / CutOff); FState := ccBelow; NotifyListeners; ppLow := Bid; pLow := Bid; Low := Bid End Else If Bid - ppHigh > CutOff Then Begin FPriceIncrease := Ask - ppHigh; FQuality := Sqr(OneMinute / (OneMinute - (UpdateTimeTag - CurrentTimeTag))) * ((Bid - ppHigh) / CutOff); FState := ccAbove; NotifyListeners; ppHigh := Ask; pHigh := Ask; High := Ask End; Low := Min(Low, Bid); High := Max(High, Ask) End; //////////////////////////////////////////////////////////////////////// // TRunningCommonBBO //////////////////////////////////////////////////////////////////////// Type TRunningCommonBBO = Class(TRunningCommon) Private L1Data : TGenericL1DataNode; Constructor Create(Symbol : String); Override; Protected Procedure GetCurrentValues(Out Valid : Boolean; Out LowerValue, HigherValue : Double); Override; Public Class Procedure Find(Symbol : String; OnChange : TThreadMethod; Out Node : TRunningCommon; Out Link : TDataNodeLink); End; Procedure TRunningCommonBBO.GetCurrentValues( Out Valid : Boolean; Out LowerValue, HigherValue : Double); Var Bid, Ask : Double; Begin Bid := L1Data.GetCurrent.BidPrice; Ask := L1Data.GetCurrent.AskPrice; Valid := False; If L1Data.IsValid Then Begin LowerValue := Min(Bid, Ask); HigherValue := Max(Bid, Ask); If (LowerValue > 0) Then Valid := True End End; Class Procedure TRunningCommonBBO.Find(Symbol : String; OnChange : TThreadMethod; Out Node : TRunningCommon; Out Link : TDataNodeLink); Var TempNode : TDataNodeWithStringKey; Begin FindCommon(TRunningCommonBBO, Symbol, OnChange, TempNode, Link); Node := TempNode As TRunningCommon End; Constructor TRunningCommonBBO.Create(Symbol : String); Var Link : TDataNodeLink; Begin Inherited; TGenericL1DataNode.Find(Symbol, NewData, L1Data, Link); AddAutoLink(Link) End; //////////////////////////////////////////////////////////////////////// // TRunningCommonTosOnly //////////////////////////////////////////////////////////////////////// Type TRunningCommonTosOnly = Class(TRunningCommon) Private TosData : TGenericTosDataNode; Constructor Create(Symbol : String); Override; Protected Procedure GetCurrentValues(Out Valid : Boolean; Out LowerValue, HigherValue : Double); Override; Public Class Procedure Find(Symbol : String; OnChange : TThreadMethod; Out Node : TRunningCommon; Out Link : TDataNodeLink); End; Procedure TRunningCommonTosOnly.GetCurrentValues( Out Valid : Boolean; Out LowerValue, HigherValue : Double); Begin Valid := False; If TosData.IsValid Then Begin LowerValue := TosData.GetLast.Price; HigherValue := LowerValue; If LowerValue > 0 Then Valid := True End End; Class Procedure TRunningCommonTosOnly.Find(Symbol : String; OnChange : TThreadMethod; Out Node : TRunningCommon; Out Link : TDataNodeLink); Var TempNode : TDataNodeWithStringKey; Begin FindCommon(TRunningCommonTosOnly, Symbol, OnChange, TempNode, Link); Node := TempNode As TRunningCommon End; Constructor TRunningCommonTosOnly.Create(Symbol : String); Var Link : TDataNodeLink; Begin Inherited; TGenericTosDataNode.Find(Symbol, NewData, TosData, Link); AddAutoLink(Link) End; //////////////////////////////////////////////////////////////////////// // TRunning //////////////////////////////////////////////////////////////////////// Type TRunning = Class(TAlert) Private Data : TRunningCommon; Up : Boolean; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TRunning.Create(Params : TParamList); Var Symbol : String; Link : TDataNodeLink; Begin Assert(Length(Params) = 2, 'Expected params: (Symbol, Up)'); Inherited Create; Symbol := Params[0]; Up := Params[1]; If SymbolIsIndex(Symbol[1]) Then // Indicies have no bid or ask. Pretend that the bid and ask are both the last. TRunningCommonTosOnly.Find(Symbol, NewData, Data, Link) Else // Normal symbol TRunningCommonBBO.Find(Symbol, NewData, Data, Link); AddAutoLink(Link) End; Procedure TRunning.NewData; Begin If Up And (Data.State = ccAbove) Then Report('Running up: ' + FormatPrice(Data.PriceIncrease, True) + ' in less than one minute.', Data.Quality) Else If (Not Up) And (Data.State = ccBelow) Then Report('Running down: ' + FormatPrice(Data.PriceIncrease, True) + ' in less than one minute.', Data.Quality) End; //////////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////////// Initialization TGenericDataNodeFactory.StoreFactory('RunningUp', TGenericDataNodeFactory.CreateWithArgs(TRunning, StandardSymbolPlaceHolder, True)); TGenericDataNodeFactory.StoreFactory('RunningDown', TGenericDataNodeFactory.CreateWithArgs(TRunning, StandardSymbolPlaceHolder, False)); End.