#ifndef __OneMinuteCandles_h_ #define __OneMinuteCandles_h_ #include #include "../shared/SWTLocker.h" #include "../generate_alerts/data_framework/GenericTosData.h" #include "CandleDataNode.h" /* This replaces a big part of standard candles. This will collect * candle data during the day. It will also consult the database * to see candles from before the program started. * * We only store one minute candles. Others are created as needed. * This gives us the maximum flexibility. It takes a little more * effort to find the candles, but it saves us from storing many * different types of candles. * * This class does not distinguish between premarket, postmarket, * and normal trading. For the most part it doesn't even care about * days or timezones. The user specifies the exact times he cares about, * and this class looks up the data. * * (It might make sense to think about days. If you keep today in * memory and yesterday in the database, then you should never have a * candle that is half in one place and half in the other. That's not * 100% true. :( You can start monitoring a stock at any time. What * if you have half the data in memory and half in the database or on * another server? It's certainly possible to give the user the right * answer.) * * We flush the cache every night. The cache doesn't know anything * about splits and will miss some corrections. Splits only happen * between days. Our overnight process deals with all of the * corrections at the same time. So we have to clear our cache once a * day or we'll have stale data. Ideally that would be coordinated * with our overnight process. * * This class can accumulate prints in almost any order. If we get * a late print, we update the appropriate candle. We do not attempt * to tell the listener when a new candle starts. The user will * probably use a BarCounter or SynchronizedTimer to decide when * to do something interesting, like grab the next bar. * * It is up to the caller to decide which candles to build. I.e. * 30 minute vs 60 minute. If there's a partial candle, is it the * first one of the day or the last one? Include premarket data? * etc. Presumably another class will handle all of those decisions, * and will map between the row number on a grid and the start and * end times of the candle. */ class OneMinuteCandles : public CandleDataNode { private: GenericTosDataNode *_tosData; const std::string _symbol; static time_t initialDatabaseCutoff(); // This is the stuff we HAVE TO keep in memory. It's recent, so it's // probably not in the database yet. struct Data { time_t databaseCutoff; SingleCandle::ByStartTime byStartTime; EpochCounter epochCounter; Data() : databaseCutoff(initialDatabaseCutoff()) { } TclList debugDump() const; }; SWTLocker< Data > _data; Integer _lastVolume; time_t _lastStartTime; void onWakeup(int msgId); static const std::string RESET_CHANNEL; void classInit(); void resetNow(); void onBroadcast(BroadcastMessage &message, int msgId); // The caller is responsible for the lock. SingleCandle getFromMemory(Data const &data, time_t start, time_t end); static OneMinuteCandles::CachedCandles getFromDatabase(std::string const &symbol, time_t start, time_t end); static CachedCandles getCacheFromDatabase(std::string const &symbol); static std::string const &getCacheSize(); static bool cacheDisabled() { return getCacheSize().empty(); } CachedCandlesHolder _databaseCache; static void verifyCachedCandles(CachedCandles const &cachedCandles); OneMinuteCandles(std::string const &key, std::string const &symbol); public: // Generate a candle for each given time period. Start by finding // the one minute candles in the period. Join them in the natural // way. Return an empty candle if there is no data in that time // period. // // start and end should both be on exact 1 minute boundaries. If // they are not, some type of rounding will occur. We do not keep // the data to make any candles that do not start and end on these // boundaries. // // A print will be included in this candle if (time >= start) && // (time < end). That is to say, prints that happen right on the // boundary between two candles will be included in the later // candle. virtual void threadSafeGet(AllRowTimes const &requests, SingleCandle::ByStartTime &destination); // Roughly, a combination of threadSafeRestartAt() and threadSafeGet(). // // You don't specify which candles you want. The epoch says when to start, // and you get get every one minute candle after that, too. // // Note that we are now returning a std::map, where threadSafeGet() passed // that same data by reference. It seems that C++11 has fixed some // performance issues so we don't have to pass things by reference as much. SingleCandle::ByStartTime threadSafeGetAllSince(EpochCounter::Epoch epoch); // Return any and all one minute candles in the given range. // // The oldest candle will be specified by the epoch counter and the given // startAt value, whichever names an older candle. // // The newest candle will come before endBefore. I.e. we're using the // standard STL semantics for specifying the end of the range. // // startAt could be 0 if you wanted everything. In particular, it only looks // at what's in memory. This won't go back to the database. This was mostly // created for a routine which copies candles from memory to the database, so // there's no reason to ever read from the database. // // endBefore could be a very large number if you wanted everything. If you // really wanted that you should consider the simpler version of // threadSafeGetAllSince() which only takes one input. // // Typically endBefore will be the beginning of the current minute, right // now. The idea is to avoid any candles still in progress. That's an // optimization so we don't send a lot of candles to the database that we // expect to change. // // Typically startAt in one call will be exactly the same as endBefore for // the previous call. That ensures that we don't miss anything. Imagine // we ask for the data at 10:45:06. Imagine there is already data for the // candle that starts at 10:45. We choose to skip that candle because it's // likely to change soon, so we set endBefore to 10:45:00. No other prints // are added to that candle after we make this first request. The next // request should say startAt=10:45:00. That guarantees that the second // request will get the candle that starts at 10:45, even though the epoch // counter will tell us that this candle hasn't changed since the last time // we asked. // // epoch has its normal meaning. Read the current epoch before making this // call and save the value. Send that epoch as an input to the following // call. Use 0 for the epoch for the first call. This will give you // additional candles, but only if something has changed since we saved the // epoch. SingleCandle::ByStartTime threadSafeGetAllSince(EpochCounter::Epoch epoch, time_t startAt, time_t endBefore); // Don't bother making a request before this time. This might be an // estimate. There is no guarantee that there is a candle at exactly this // time. //static time_t oldestCandle(std::string const &symbol); virtual time_t threadSafeRestartAt(EpochCounter::Epoch epoch); // This is used to bootstrap another server that just started. Old history // still comes from the database, possibly via a proxy. Today's history // should come from the local machine. This is thread safe. virtual std::string marshalTodaysCandles(); virtual time_t oldestFastTime(); virtual StartTimeList startTimeList(time_t startBefore); virtual StartTimeList startTimeList(StartTimeList const &previous); virtual TclList debugDump() const; static OneMinuteCandles *find(std::string const &symbol); }; #endif