Unit ConfirmedCrossing; { These alerts report when the price crosses over some threshold. The threashold comes from another data node, and is a parameter. The price is based on our volume blocks. The low of the volume block must be above the threshold to call it above, and the high of the volume block must be below the threshold to call it below. } Interface Implementation Uses DataNodes, GenericDataNodes, AlertBase, GenericL1DataNode, GenericTosDataNode, NormalVolumeBreakBars, VolumeWeightedData, Prices, StandardPlaceHolders, CsvFileData, MarketData, VWAP, DateUtils, Math; //////////////////////////////////////////////////////////////////////// // TConfirmedCrossing //////////////////////////////////////////////////////////////////////// Type TConfirmedCrossing = Class(TAlert) Private PriceLevelDataNode : TGenericDataNode; FReportAbove : Boolean; FPriceLevelName, FDirectionString : String; CurrentState : TAboveOrBelow; LowestRecentPrice, HighestRecentPrice : Double; LastPrice : Double; // Cache this so we don't have to check validity again. RecentPricesValid : Boolean; BarData : TNormalVolumeBreakBars; Procedure NewPriceLevel; Procedure UpdateRecentPriceLevels; Procedure NewBarData; Protected Constructor Create(Params : TParamList); Override; End; Constructor TConfirmedCrossing.Create(Params : TParamList); Var Symbol : String; FactoryUnknown : IUnknown; Factory : IGenericDataNodeFactory; Link : TDataNodeLink; Begin Assert(Length(Params)=4, 'Expected params: (Symbol, PriceLevelFactory, PriceLevelName, ReportAbove)'); Inherited Create; Symbol := Params[0]; FactoryUnknown := Params[1]; Factory := FactoryUnknown As IGenericDataNodeFactory; FPriceLevelName := Params[2]; FReportAbove := Params[3]; If FReportAbove Then FDirectionString := 'above' Else FDirectionString := 'below'; Factory := Factory.Duplicate; Factory.SetValue(SymbolNameKey, Symbol); Factory.Find(NewPriceLevel, PriceLevelDataNode, Link); AddAutoLink(Link); TNormalVolumeBreakBars.Find(Symbol, NewBarData, BarData, Link); AddAutoLink(Link); DoInCorrectThread(NewPriceLevel) End; Procedure TConfirmedCrossing.NewPriceLevel; Begin If PriceLevelDataNode.IsValid Then Begin LastPrice := PriceLevelDataNode.GetDouble; If RecentPricesValid Then Begin LowestRecentPrice := Min(LowestRecentPrice, LastPrice); HighestRecentPrice := Max(HighestRecentPrice, LastPrice) End Else Begin LowestRecentPrice := LastPrice; HighestRecentPrice := LastPrice; RecentPricesValid := True End End Else Begin RecentPricesValid := False; CurrentState := ccEqual End End; Procedure TConfirmedCrossing.UpdateRecentPriceLevels; Begin If PriceLevelDataNode.IsValid Then Begin LastPrice := PriceLevelDataNode.GetDouble; LowestRecentPrice := LastPrice; HighestRecentPrice := LastPrice; RecentPricesValid := True End Else RecentPricesValid := False End; Procedure TConfirmedCrossing.NewBarData; Var Bars : TVolumeBlocks; LastBar : TVolumeBlock; NewState : TAboveOrBelow; Begin SetLength(Bars, 0); // Damn compiler warnings! If Not RecentPricesValid Then CurrentState := ccEqual Else Begin NewState := ccEqual; Bars := BarData.GetBlocks; If Length(Bars) > 0 Then Begin LastBar := Bars[Pred(Length(Bars))]; If LastBar.Low > HighestRecentPrice Then NewState := ccAbove Else If LastBar.High < LowestRecentPrice Then NewState := ccBelow End; If (FReportAbove And (NewState = ccAbove) And (CurrentState = ccBelow)) Or ((Not FReportAbove) And (NewState = ccBelow) And (CurrentState = ccAbove)) Then Report('Price crossed ' + FDirectionString + ' ' + FPriceLevelName + ' (' + FormatPrice(LastPrice) + '). Confirmed by volume.'); If NewState <> ccEqual Then CurrentState := NewState End; UpdateRecentPriceLevels End; //////////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////////// Initialization TGenericDataNodeFactory.StoreFactory('CrossedAboveVWAP', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, TGenericDataNodeFactory.CreateWithSymbolPlaceholder(TVWAP) As IGenericDataNodeFactory, 'VWAP', True)); TGenericDataNodeFactory.StoreFactory('CrossedBelowVWAP', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, TGenericDataNodeFactory.CreateWithSymbolPlaceholder(TVWAP) As IGenericDataNodeFactory, 'VWAP', False)); TGenericDataNodeFactory.StoreFactory('CrossedAbove200Day', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, MakeDoubleCsvFactory('TC_OvernightData.csv', '200 Day SMA'), '200 day SMA', True)); TGenericDataNodeFactory.StoreFactory('CrossedBelow200Day', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, MakeDoubleCsvFactory('TC_OvernightData.csv', '200 Day SMA'), '200 day SMA', False)); TGenericDataNodeFactory.StoreFactory('CrossedAbove50Day', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, MakeDoubleCsvFactory('TC_OvernightData.csv', '50 Day SMA'), '50 day SMA', True)); TGenericDataNodeFactory.StoreFactory('CrossedBelow50Day', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, MakeDoubleCsvFactory('TC_OvernightData.csv', '50 Day SMA'), '50 day SMA', False)); TGenericDataNodeFactory.StoreFactory('CrossedAbove20Day', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, MakeDoubleCsvFactory('TC_OvernightData.csv', '20 Day SMA'), '20 day SMA', True)); TGenericDataNodeFactory.StoreFactory('CrossedBelow20Day', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, MakeDoubleCsvFactory('TC_OvernightData.csv', '20 Day SMA'), '20 day SMA', False)); TGenericDataNodeFactory.StoreFactory('CrossedAboveCloseConfirmed', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, TGenericDataNodeFactory.FindFactory('MostRecentClose'), 'close', True)); TGenericDataNodeFactory.StoreFactory('CrossedBelowCloseConfirmed', TGenericDataNodeFactory.CreateWithArgs(TConfirmedCrossing, StandardSymbolPlaceHolder, TGenericDataNodeFactory.FindFactory('MostRecentClose'), 'close', False)); End.