#ifndef __StandardCandles_h_ #define __StandardCandles_h_ #include #include "../misc_framework/DataNodes.h" #include "../data_framework/GenericTosData.h" class BarCounter; class StandardCandles : public DataNode { public: struct SingleCandle { double open, high, low, close; Integer volume; bool operator ==(SingleCandle const &other) const; bool operator !=(SingleCandle const &other) const; void clear() { open = high = low = close = 0.0; volume = 0; } }; typedef std::vector< SingleCandle > CandleArray; enum LastTransition { ltNew, // We have not yet displayed any data. ltReset, // Major changes. ltNewCandle, // Added one new candle to the end. ltUpdateCurrentCandle /* The candle being built has changed. Reserved. Not currently reported. */ }; typedef unsigned int Epoch; private: BarCounter *_barCounter; GenericTosDataNode *_tosData; CandleArray _history; SingleCandle _currentCandle; bool _currentCandleValid; bool _doNotifySoon; bool _forceActive; // For debug purposes only. bool _strict; // If a candle is missing, delete all history. LastTransition _lastTransition; Epoch _epoch; // A candle is encoded as open:high:low:close:volume. All five should be // present. (If volume is unknown, set it to 0.) If any of the values // are bad, this function will return false. If this returns an error, // candle will be in an undefined state. This will check for improperly // formatted data. It might make additional checks, like high >= low, but // do not rely on this. static bool decode(std::string const &encoded, SingleCandle &candle); // A candle array is a list of candles encoded as described above, and // sperated by a semicolon. In case of error, a message will be sent to // the log, and array will be empty. The initial contents of array are // always abandoned. If debug is not empty, it will be added to any error // messages. static void decode(std::string const &encoded, CandleArray &array, std::string const &debug = ""); void reset(); void addCandle(SingleCandle const &candle); void addCurrentCandle(); void notifyIfRequired(); enum { wTosData, wBarData }; void onWakeup(int msgId); void onTosData(); void onBarData(); void onBroadcast(BroadcastMessage &message, int msgId); static bool restoreHistory(); StandardCandles(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(DataNodeListener *listener, int msgId, StandardCandles *&node, std::string const &symbol, int minutesPerBar, bool strict = false); // The second find has the same requirements as the first. Symbol followed // by minutes. The first one is more explicit, and does more checks at // compile time. The second one is good because often the caller will // take exactly the same inputs, and it makes no sense to extract this // data just to build an identical argument list. There will be run-time // checks which are unavoidable, regardless of any comiple-time checks. // In both find() routines strict is optional. If you leave it out, it is // the same as setting it to false. static DataNodeLink *find(DataNodeListener *listener, int msgId, StandardCandles *&node, DataNodeArgument const &args); CandleArray const &getHistory() const { return _history; } LastTransition getLastTransition() const { return _lastTransition; } int historicalCandleCount() const { return _history.size(); } // Only meaningful if there is at least one candle. Note: This is often the // end of the last candle reported by getHistory(). This is the start of the // candle in progress. This is is MARKET_HOURS_OPEN during the first candle // of the day. This is not defined if isActive() is false. In the callback // from this data node, this refers to the brand new and completely empty // current candle that we just started to watch. time_t getCurrentCandleStartTime() const; // This changes when the history changes. Ideally we wouldn't need // this because of the notification event. But it's possible that // one data node is listening to two others, each of which are listening // to this data node. The top level consumer will hear about a change // when only one of the two intermediate nodes has had a chance to // update. The solution is for the middle layer to update it's value // when someone asks for the value. But that can be a lot of work, // so we use the epoch to know if we can use the cached value. Epoch getEpoch() const { return _epoch; } // This returns true between the open and the close, false at other times. // We use a simple model where we ignore all data when this is false. When // This is true, you can look at the TOS data to see the most recent price. // When this is false, you should look at the close for the last candle to // see the last "official" price. That will allow you to see a last price // which is consistent with the candles. bool isActive() const; // This is true when the candle framework is in the process of reporting // a new timer event. It doesn't matter if this specific data node is // in the process of notifying it's listeners. If there is a timer event in // progress, and that time is appropriate to this data node, then this will // return true. For example, 5 minutes after the open all of the 5 minute // candles and all of the 1 minute candles will return true for this, but all // of the 10 minute candles and the 2 minute candles will return false. // // This allows you to decide whether you should only look at historical // candles, or you should also looking at the candle in progress. If you // look at everything including the candle in progress a millisecond before // the timer event, then you look at only the historical candles during the // callback, then you will see the same thing. This gives you one last // chance to see the state before a new candle is added. This allows you // to be consistant between things that try to watch the data as it updates, // and things that look on the timer events. bool isUpdating() const; bool shouldUseLive() const { return isActive() && !isUpdating(); } // Note: We don't notify the listener each time this changes. SingleCandle &getCurrentCandle() { return _currentCandle; } bool isCurrentCandleValid() { return _currentCandleValid; } // Looks up a value in the CSV file, and parses it. This is // automatically done in the constructor. But it can be useful at other // times, in particularly when debugging. static void getHistoricalCandles(std::string const &symbol, std::string const &minutesPerBar, CandleArray &array); static void getHistoricalCandles(std::string const &symbol, int minutesPerBar, CandleArray &array) { getHistoricalCandles(symbol, ntoa(minutesPerBar), array); } int getMinutesPerBar() const; static void registerDebugger(DataNodeManager *manager); std::string const &getSymbol() const { return _tosData->symbol(); } }; #endif