#include "DumpCandles.h" #include ///////////////////////////////////////////////////////////////////////// // GLOBAL ///////////////////////////////////////////////////////////////////////// BarList restoreOriginalPieces(BarList const &bars, CorporateActions const &corporateActions) { BarList result = bars; if(corporateActions.splits.size() > 0) { for (BarList::iterator barsIt = result.begin(); barsIt != result.end(); barsIt++) { for (std::vector< Split >::const_iterator splitsIt = corporateActions.splits.begin(); splitsIt != corporateActions.splits.end(); splitsIt++) { if (barsIt->startTime < splitsIt->date) { barsIt->open *= splitsIt->splitFactor; barsIt->high *= splitsIt->splitFactor; barsIt->low *= splitsIt->splitFactor; barsIt->close *= splitsIt->splitFactor; // Volume is adjusted for splits as well // (from SpryWare at least) barsIt->volume = (unsigned int)(barsIt->volume / splitsIt->splitFactor); } } } } return result; } inline time_t endTime(const time_t startTime, const int minutes) { int seconds = minutes * MARKET_HOURS_MINUTE; return (midnight(startTime) + (1 + secondOfTheDay(startTime) / seconds) * seconds); } std::string mysqlDateTime(time_t time) { struct tm *brokenDown; char str[20]; brokenDown = localtime(&time); strftime(str, 20, "%Y-%m-%d %X", brokenDown); std::string result = str; return result; } std::string mysqlDate(time_t time) { struct tm *brokenDown; char str[11]; brokenDown = localtime(&time); strftime(str, 11, "%Y-%m-%d", brokenDown); std::string result = str; return result; } ///////////////////////////////////////////////////////////////////////// // DumpCandles ///////////////////////////////////////////////////////////////////////// void DumpCandles::sendData(AccumulateInsert &data) { if (!data.empty()) _database.tryQueryUntilSuccess(data.get()); } void DumpCandles::newData(AccumulateInsert &accumulatedData, std::string newData) { accumulatedData.add(newData); if (accumulatedData.full()) _database.tryQueryUntilSuccess(accumulatedData.get()); } void DumpCandles::dumpIntraday(const std::string &symbol, const double high, const double low, const double close, const time_t endTime, const int minutes, AccumulateInsert &sendTo) { std::string newEntry; if (endTime != 0) { assert((high != DBL_MAX) && (low != DBL_MAX) && (close != DBL_MAX)); std::string newEntry ="(\"" + mysqlEscapeString(symbol) + "\", \"" + mysqlDateTime(endTime) + "\", " + dtoa(close) + ", " + dtoa(high) + ", " + dtoa(low) + ')'; newData(sendTo, newEntry); } } void DumpCandles::addData(const std::string &symbol, const BarList &data1MinAdjusted, const BarList &data1DayAdjusted, const CorporateActions &corporateActions) { // By default the data has been adjusted for splits. But our // historical alerts have the original prices. So update the candle data // from to show us the prices before the splits. BarList data1Day = restoreOriginalPieces(data1DayAdjusted, corporateActions); BarList data1Min = restoreOriginalPieces(data1MinAdjusted, corporateActions); // End of day data is straightforward. for (BarList::const_iterator it = data1Day.begin(); it != data1Day.end(); it++) { if (it->startTime >= _cutoff) { newData(_dailyData, "(\"" + mysqlEscapeString(symbol) + "\", \"" + mysqlDate(it->startTime) + "\", " + dtoa(it->open) + ", " + dtoa(it->close) + ')'); } } // Check to see if the intra-day data has any volume. Normally we would // not record a candle if there was no volume in that candle. But there // are a few odd symbols that do not show volume ever. I think most // indices are like that, although some don't give us ANY intra-day data. // I'm curious about the print count. That might be available for some of // these items with no volume. I think that some of the indices update // approximately once per minute, and each update counts as a print, but // I'm not sure. bool noVolume = true; for (BarList::const_iterator it = data1Min.begin(); it != data1Min.end(); it++) { if (it->volume > 0) { noVolume = false; break; } } // Accumulate the intra-day data. We read 1 minute candles from the data // source. We currently store 5 minute candles in the database. We've set // this up so we could easily change it to work with other time frames. time_t lastEndTime = 0; time_t currentEndTime; double lastClose = DBL_MAX; double currentLow = DBL_MAX; double currentHigh = DBL_MAX; int currentSeconds; for (BarList::const_iterator it = data1Min.begin(); it != data1Min.end(); it++) { if (it->startTime >= _cutoff && (noVolume || it->volume > 0)) { currentSeconds = secondOfTheDay(it->startTime); if (currentSeconds >= MARKET_HOURS_OPEN && currentSeconds < MARKET_HOURS_CLOSE) { currentEndTime = endTime(it->startTime, 5); if (currentEndTime != lastEndTime) { dumpIntraday(symbol, currentHigh, currentLow, lastClose, lastEndTime, 5, _intradayData); lastEndTime = currentEndTime; currentHigh = it->high; currentLow = it->low; } else { currentHigh = std::max(currentHigh, it->high); currentLow = std::min(currentLow, it->low); } } lastClose = it->close; } } dumpIntraday(symbol, currentHigh, currentLow, lastClose, lastEndTime, 5, _intradayData); }