Unit TalSpecialMarketData; { This unit includes some computed fields that are different for different data feeds. As much as possible, these differernce are hidden in the implementation of the TOS and L1 data nodes. But some items must be handled at a higher level. } Interface Implementation Uses GenericL1DataNode, GenericTosDataNode, GenericDataNodes, DataFeedSelector, DataNodes, SimpleMarketData, DateUtils; //////////////////////////////////////////////////////////////////////// // TMostRecentCloseTal //////////////////////////////////////////////////////////////////////// // Tal demotes most values from today to yesterday at approximately 4:50am // Pacific time. At that time, HIST_CLOSE gets the value of SETTLE, and // SETTLE gets the empty value. At that time HIST_CLOSE is the close from // the previous trading session. HIST_CLOSE does not change until tomorrow. // SETTLE will be set to the close price some time after the market closes, // approx 4:30 for most equities, and 5:30 for QQQ and the like. // // We don't use SETTLE anywhere in the realtime code because it is not // defined at any interesting time for us. It might make some sense to use // the SETTLE price, if available, as todays close, otherwise to use the // code below. But our approximation is good enough. // // Between midnight and the open we can use TodaysClose, if it is present. If // not then we must use PrevClose. Around 4:50am when the datafeed updates // the day, TodaysClose becomes unrelyable. We keep the last value of it, // which is still correct, but if we reconnect the DataFeed will not give // it back to us. At that time we no longer need it because we can use // PrevClose. // Starting with the first non-form-t print of a stock, until the close, // TodaysClose is meaningless, so we must use PrevClose. We can start // with the first print of the day, if that is easier. We actually // start with the first print that has a date associated with it, and // I'm not certain if that's the first non-formT print or the first // print. // After the close, the TodaysClose field is today's close. PrevClose // is still the previous days close. // FormT and not-form-t prints can be intermixed near the open and the close, // and that causes a lot of problems. For that reason we just use the // time of day to say closed. // QQQ and some others are wrong. They do not close at 1pm, but I can't // find a definative time when they do close. To add to the confusion, // the AMEX reports non-formT trades after 1pm, but starting at 1 all the // ECNs call the trades formT. (i.e. they don't know either!) // Futures are completely different because they trade 24 hours. We just // use the value from eSignal as the close. Type TMostRecentCloseTal = Class(TGenericDataNode) Private TosData : TGenericTosDataNode; L1Data : TGenericL1DataNode; AlwaysUsePreviousClose : Boolean; Protected Constructor Create(Params : TParamList); Override; Public Function IsValid : Boolean; Override; Published Function GetDouble : Double; Override; End; Constructor TMostRecentCloseTal.Create(Params : TParamList); Var Symbol : String; Link : TDataNodeLink; Begin Assert(Length(Params)=1, 'Expected params: (Symbol)'); Symbol := Params[0]; Inherited Create; AlwaysUsePreviousClose := SymbolIsFuture(Symbol); TGenericL1DataNode.Find(Symbol, NotifyListeners, L1Data, Link); AddAutoLink(Link); TGenericTosDataNode.Find(Symbol, NotifyListeners, TosData, Link); AddAutoLink(Link) End; Function TMostRecentCloseTal.IsValid : Boolean; Begin Result := L1Data.IsValid And TosData.IsValid End; Function TMostRecentCloseTal.GetDouble : Double; Const CloseTime = 13.0 / 24; // 1pm Var Last : PTosData; Begin Result := 0; If IsValid Then Begin Last := TosData.GetLast; If (Not AlwaysUsePreviousClose) And // For products which trade 24 hours, just trust the exchange. ((TimeOf(GetSubmitTime) >= CloseTime) Or // After the close use todays close. (DateOf(Last^.Time) < Today)) Then // Early in the morning TodaysClose always has last nights close or nothing, and PrevClose may or may not have updated from the day before yet. This case also covers Saturdays, Sundays, and market holidays. Result := Last^.TodaysClose; If Result = 0 Then Result := L1Data.GetCurrent^.PrevClose End End; Initialization If DataFeedChoice = dfcTal Then TGenericDataNodeFactory.StoreStandardFactory('MostRecentClose', TMostRecentCloseTal); End.