Unit LocksAndCrosses; { These are very fast, very noisy alerts. Most of the logic is just for filtering. These made more sense, maybe, a few years back where the NASDAQ worked differently and they happened less frequently. But people ask for them, so... } Interface Implementation Uses DataNodes, GenericDataNodes, GenericL1DataNode, AlertBase, GenericFundamentalDataNode, StandardPlaceHolders, DateUtils, Math; //////////////////////////////////////////////////////////////////////// // TLockedOrCrossed //////////////////////////////////////////////////////////////////////// Type TLockedOrCrossedState = (locsBad, locsWaiting, locsActive, locsOvershot, locsOvershotPrimed); Type TLockedOrCrossed = Class(TAlert) Private L1Data : TGenericL1DataNode; State : TLockedOrCrossedState; LastEndTime : TDateTime; Procedure NewData; Protected Constructor Create(Params : TParamList); Override; Function GetState(Data : PL1Data) : TLockedOrCrossedState; Virtual; Abstract; Procedure ReportNow; Virtual; Abstract; Function HigherQuality : Boolean; Virtual; End; Function TLockedOrCrossed.HigherQuality : Boolean; Begin Result := False End; Constructor TLockedOrCrossed.Create(Params : TParamList); Var Symbol : String; Link : TDataNodeLink; Begin Assert(Length(Params)=1, 'Expected params: (Symbol)'); Symbol := Params[0]; Inherited Create; TGenericL1DataNode.Find(Symbol, NewData, L1Data, Link); AddAutoLink(Link) End; Procedure TLockedOrCrossed.NewData; Const PrimedStates = [locsWaiting, locsOvershotPrimed]; Var Current : PL1Data; PreviousState : TLockedOrCrossedState; IdleTimeInSeconds : Double; Begin PreviousState := State; State := locsBad; If L1Data.IsValid Then Begin Current := L1Data.GetCurrent; If (Current.BidPrice > 0) And (Current.AskPrice > 0) Then State := GetState(Current) End; If (State = locsOvershot) And (PreviousState In PrimedStates) Then State := locsOvershotPrimed Else If (State = locsActive) And HigherQuality Then ReportNow Else If (State = locsActive) And (PreviousState <> locsActive) Then Begin IdleTimeInSeconds := (GetSubmitTime - LastEndTime) * 60.0 * 60.0 * 24.0; If (IdleTimeInSeconds > 300.0) Then ReportNow End; If Not ((State In PrimedStates) And (PreviousState In PrimedStates)) Then LastEndTime := GetSubmitTime End; //////////////////////////////////////////////////////////////////////// // TMarketLocked //////////////////////////////////////////////////////////////////////// Type TMarketLocked = Class(TLockedOrCrossed) Protected Function GetState(Data : PL1Data) : TLockedOrCrossedState; Override; Procedure ReportNow; Override; End; Function TMarketLocked.GetState(Data : PL1Data) : TLockedOrCrossedState; Begin If Data.BidPrice = Data.AskPrice Then Result := locsActive Else If Data.BidPrice > Data.AskPrice Then Result := locsOvershot Else Result := locsWaiting End; Procedure TMarketLocked.ReportNow; Begin Report('Market Locked') End; //////////////////////////////////////////////////////////////////////// // TMarketCrossedBase //////////////////////////////////////////////////////////////////////// Type TMarketCrossedType = (mctDown, mctUp, mctOther); TMarketCrossedBase = Class(TLockedOrCrossed) Private FundamentalData : TGenericFundamentalDataNode; HighestReportedCross, CurrentCross : Double; FCrossType : TMarketCrossedType; Protected Constructor Create(Params : TParamList); Override; Function GetState(Data : PL1Data) : TLockedOrCrossedState; Override; Procedure ReportNow; Override; Function HigherQuality : Boolean; Override; Public Property CrossType : TMarketCrossedType Read FCrossType; End; Constructor TMarketCrossedBase.Create(Params : TParamList); Var Symbol : String; Link : TDataNodeLink; Begin HighestReportedCross := MaxDouble; Inherited; Symbol := Params[0]; TGenericFundamentalDataNode.Find(Symbol, Nil, FundamentalData, Link); AddAutoLink(Link) End; Function TMarketCrossedBase.HigherQuality : Boolean; Begin Result := CurrentCross > HighestReportedCross End; Function TMarketCrossedBase.GetState(Data : PL1Data) : TLockedOrCrossedState; Begin If Data.BidPrice > Data.AskPrice Then Begin Result := locsActive; CurrentCross := Data.BidPrice - Data.AskPrice End Else Result := locsWaiting End; Procedure TMarketCrossedBase.ReportNow; Const Msg : Array [TMarketCrossedType] Of String = ('Market Crossed Down', 'Market Crossed Up', 'Market Crossed'); Var PrimaryMarket : String; Current : PL1Data; BidPrimary, AskPrimary : Boolean; Begin FCrossType := mctOther; If L1Data.IsValid And FundamentalData.IsValid Then Begin PrimaryMarket := FundamentalData.GetCurrent.ListedExchange; If PrimaryMarket <> '' Then Begin Current := L1Data.GetCurrent; BidPrimary := Current.BidExchange = PrimaryMarket; AskPrimary := Current.AskExchange = PrimaryMarket; If BidPrimary Then Begin If Not AskPrimary Then FCrossType := mctDown End Else Begin If AskPrimary Then FCrossType := mctUp End End End; Report(Msg[FCrossType], CurrentCross); HighestReportedCross := CurrentCross End; //////////////////////////////////////////////////////////////////////// // TMarketCrossedSelector //////////////////////////////////////////////////////////////////////// Type TMarketCrossedSelector = Class(TAlert) Private CrossType : TMarketCrossedType; Base : TMarketCrossedBase; Protected Constructor Create(Params : TParamList); Override; Procedure PossibleAlert; End; Constructor TMarketCrossedSelector.Create(Params : TParamList); Var Symbol : String; Link : TDataNodeLink; Factory : IGenericDataNodeFactory; TempNode : TGenericDataNode; Begin Assert(Length(Params)=2, 'Expected params: (Symbol, Type)'); Symbol := Params[0]; CrossType := Params[1]; Inherited Create; Factory := TGenericDataNodeFactory.CreateWithArgs(TMarketCrossedBase, Symbol); Factory.Find(PossibleAlert, TempNode, Link); AddAutoLink(Link); Base := TMarketCrossedBase(TempNode) End; Procedure TMarketCrossedSelector.PossibleAlert; Var Quality : Double; QualityValid : Boolean; Begin If Base.CrossType = CrossType Then Begin Base.GetQuality(Quality, QualityValid); If QualityValid Then Report(Base.GetString, Quality) Else Report(Base.GetString) End End; //////////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////////// Initialization TGenericDataNodeFactory.StoreStandardFactory('MarketLocked', TMarketLocked); TGenericDataNodeFactory.StoreFactory('MarketCrossedDown', TGenericDataNodeFactory.CreateWithArgs(TMarketCrossedSelector, StandardSymbolPlaceHolder, mctDown)); TGenericDataNodeFactory.StoreFactory('MarketCrossedUp', TGenericDataNodeFactory.CreateWithArgs(TMarketCrossedSelector, StandardSymbolPlaceHolder, mctUp)); TGenericDataNodeFactory.StoreFactory('MarketCrossed', TGenericDataNodeFactory.CreateWithArgs(TMarketCrossedSelector, StandardSymbolPlaceHolder, mctOther)); End.