#ifndef __SynchronizedTimers_h_ #define __SynchronizedTimers_h_ #include "../misc_framework/DataNodes.h" class SynchronizedTimer; /* This unit is reposnible for telling us when to update all of the bar / * candle data structures. It is primarily used by StandardCandles.C. */ /* This is only active during market hours. It resets itself to say no * data after the close, and doesn't change back until the open. So the * bar number before the first market print and after the first post-market * print is UNKNOWN_TIME. * * The first bar is always 0 and starts when the market opens. The last bar * always ends at the market close. * * Currently 2,5, 10, 15, and 30 minute bars are the same all day long. That * is to say that every bar is eacly the same lenghth. That's not possible * with 60 minute bars because there are 6 1/2 hours in the day. Typically * either the first bar or the last bar is 30 minutes, and the other 6 are * all 60 minutes. Brad says that making the first bar shorter is the most * common approach, and the one we should take. Until recently I avoided * 60 minute candles to avoid making that choice. * * The issue with 60 minute bars is controlled by the _offset property. * Currently BarCounter::BarCounter() sets this property based on the number * of minutes per bar. This is 30 (to say end the first candle 30 minutes * earlier than normal) for the minutes per bar is 60. _offset is 0 (to say * end the first candle 0 minutes earlier than normal) for everything else. * _offset could be an input to this object. You could avoid the question * of how to handle the 60 minute bars by making both avaiable. * * New rule (11/27/2009): After further research Brad decided that all candles * should start at the open. If one candle is shorter than the rest, it will * be the last candle of the day. We keep the _offset property to allow for * future changes. But for now _offset is always 0. */ class BarCounter : public DataNode { public: // Initially we store this for the time, before we know the real time. static const int UNKNOWN_TIME; enum BarCounterTransition { // This is the intial state. bctNone, // We have started a valid bar, but previously we were not on a valid // bar. This is the first bar we've seen today. bctFirst, // We have moved directly from one valid bar to the next. bctNext, // We gracefully transitioned from a valid bar to the end of the day. bctEnd, // Somehow the clock skipped forward in an unexpected manner. bctSkip // Note the abscence of a backward item. Some old prints are // unavoidable. The consumer should look for these and ignore them. // Note the abscence of a skip/end hybrid. We can't really detect an // ungraceful shift to the end very well, so we often give it the // benefit of the doubt. }; /* For the candle/bar data, we pay particular attention to making sure that * multiple data nodes that fire at the same time all see the same data. * For example, If I'm looking at the number of up-candles on a 5, 10, and * 15 minute chart for the same alert, they will all report consistantly. * This is always a problem with the way that our data node work, but here * it's most obvious, because so many things are all keyed to go off * together. * * The data nodes in this unit include a property called TimePhase. Each * outgoing notification will go out twice. First we update our own * internals. Then we set TimePhase to tpUpdate and notify our listeners, * then we set TimePhase to tpNotify, and we notify our listeners again. * * This is only part of the soltuion. The assumption is that the next level * of listners (each presumably holding candles for one specific stock for * one specific time frame) will update their data on the tpUpdate phase, * then notify their listeners on the tpNotify phase. Of course, this trick * only works once. The next level up doesn't know about the two phases. * However, we expect these data nodes to have their own tricks. In * paricular, some type of epoch counter, so they know if someone is reading * from them when an update to them is pending. * * These two seperate methods come from the fact that we designed this * unit to be completely seperate from the next level up, thinking we * could make it more generically useful. The original idea worked well, * when people only looked at one chart at a time. But when we had to * make different charts work togather, we did a quick patchwork, and added * TimePhase. */ enum TimePhase { tpNotActive, tpUpdate, tpNotify }; private: int _minutesPerBar; int _offset; SynchronizedTimer *_basicTimer; int _currentBar; int _previousBar; BarCounterTransition _lastTransition; time_t _ignoreIfBefore; TimePhase _timePhase; bool _notifyRequired; void onWakeup(int msgId); BarCounter(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(DataNodeListener *listener, int msgId, BarCounter *&node, int minutesPerBar); int getCurrentBar() const { return _currentBar; } int getPreviousBar() const { return _previousBar; } BarCounterTransition getLastTransition() const { return _lastTransition; } time_t getIgnoreIfBefore() const { return _ignoreIfBefore; } int getMinutesPerBar() const { return _minutesPerBar; } int getBarsPerDay() const; TimePhase getTimePhase() const { return _timePhase; } // This was added specifically to drive StandardCandles::isUpdating(). See // StandardCandles.h for more info. bool isUpdating() const; }; /* This class is primarily used by the BarCounter class. We export this * class outside of this unit so that SynchronizedTimer::SafeSetTime() will be * available. The low level code responsible for retrieving TOS data from * the data provider should call that function before sending TOS data * through normal channels. */ class SynchronizedTimer : public DataNode { private: // When this candle started. This is the ideal start time, which might be // before the actual signal came in. time_t _startTime; // This is the ideal start time of the next candle. time_t _nextToReport; // Minutes from midnight of the start of the candle. int _minutes; BarCounter::TimePhase _timePhase; void setTimeImpl(time_t remoteTime); static __thread SynchronizedTimer *instance; SynchronizedTimer(DataNodeArgument const &args); friend class DataNode; public: // Listeners use this like a standard data node. Find an instance. When // the number of minutes on the clock changes, the user will be notified, // and the Minutes property will contain the right number. static DataNodeLink *find(DataNodeListener *listener, int msgId, SynchronizedTimer *&node); // This is the number of minutes between the open and the beginning of the // current one minute candle. This might will negative before the open, and // it will be at least the number of minutes in the trading day after the // close. int getMinutes() const { return _minutes; } time_t getStartTime() const { return _startTime; } BarCounter::TimePhase getTimePhase() const { return _timePhase; } // Producers call SetTime with the time. RemoteTime implies that this // probably comes from a datafeed, not from our local clock. This method // and the next can only be called from the data node thread. static void setTime(time_t remoteTime); // This compares RemoteTime to the real time according to our local // clock. This will give us a warning if something is fishy, and // sometimes ignore the request. The debugger/simulator should always // call SetTime(), as that time will probably be off from the real time. // This is a tough one to implement. The assumption is that RemoteTime // should never be that far ahead of our clock. If we receive a print // with tomorrows date, that would kill us! I think this actually // happened, before we added this test, but I can't be sure. static void safeSetTime(time_t remoteTime); }; #endif