#include "../shared/ThreadMonitor.h" #include "GridReaderBase.h" #include "../shared/TclUtil.h" #include "TclNamedReference.h" #include "ExecutionContext.h" #include "CandleDataNode.h" #include "../shared/SimpleLogFile.h" #include "CandleDataProvider.h" #include "TclGridObjectWrapper.h" class HasDebugDump { public: virtual std::string debugDump() const =0; virtual ~HasDebugDump() { } }; // From Dave Mabe on 9/21/2016 via Skype: "for the AI, I use 16 cells per row // and I request 510 rows per grid. The most trades we've had in one day was // 108." These numbers seem so small compared to the memory problems we're // hunting. I added in a factor of 1,000; by default you have to request // 1,000 times what's expected before you trigger anything. // // Note that each cell is a double, 8 bytes long. We store them in vectors, // so it's mostly efficient, but it's possible that we've requested 2x as // much as required. static int maxCellCount = 16 * 510 * 1000; class PrototypeWrapper : public HasNamedReference, public HasDebugDump { public: const GridPrototypeRef ref; PrototypeWrapper(GridPrototypeRef const &prototype) : ref(prototype) { assert(ref); } virtual std::string getTypeName() const { return "prototype"; } virtual std::string debugDump() const { return ref->debugDump(); } }; class GridWrapper : public HasNamedReference, public HasDebugDump { public: const GridInstanceRef ref; GridWrapper(GridInstanceRef const &grid) : ref(grid) { assert(ref); } virtual std::string getTypeName() const { return "grid"; } virtual std::string debugDump() const { return ref->debugDump(); } }; static GridPrototypeRef createPrototype(CandleTimer::Ref timer, std::vector< std::vector< std::string > > const &body, bool packed) { // Call verifyPrototypeBody() first to make sure these assertions don't fail. const int prototypeRows = body.size(); assert(prototypeRows >= 2); const int prototypeColumns = body[0].size(); assert(prototypeColumns >= 1); GridPrototype *prototype = new GridPrototype(prototypeRows - 1, prototypeColumns, timer, packed); for (int col = 0; col < prototypeColumns; col++) prototype->setColumnName(body[0][col], col); for (int row = 1; row < prototypeRows; row++) { std::vector< std::string > current = body[row]; // Ignore any extra columns. Any missing columns are treated as the // empty string. current.resize(prototypeColumns); for (int col = 0; col < prototypeColumns; col++) prototype->setFormula(row - 1, col, current[col]); } return prototype; } static int verifyPrototypeBody(Tcl_Interp *interp, std::vector< std::vector< std::string > > const &body) { const int prototypeRows = body.size(); if (prototypeRows < 2) { Tcl_SetObjResult(interp, makeTclString("body must have at least 2 rows")); return TCL_ERROR; } const int prototypeColumns = body[0].size(); if (prototypeColumns < 1) { Tcl_SetObjResult(interp, makeTclString("body must have at least 1 column")); return TCL_ERROR; } std::set< std::string > names; for (auto it = body[0].cbegin(), end = body[0].cend(); it != end; it++) { const bool newItem = names.insert(*it).second; if (!newItem) { Tcl_SetObjResult(interp, makeTclString("duplicate column name: \"" + *it + "\"")); return TCL_ERROR; } } return TCL_OK; } static int createPrototypeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "type grid options"); return TCL_ERROR; } static char const *types[] = {"intraday", "daily", NULL}; int isDaily; // 0 == false == intraday, 1 == true == dialy if (Tcl_GetIndexFromObj(interp, objv[1], types, "type", 0, &isDaily) != TCL_OK) return TCL_ERROR; std::vector< std::vector< std::string > > body; if (convertFromTcl(interp, body, objv[2]) != TCL_OK) return TCL_ERROR; if (verifyPrototypeBody(interp, body) != TCL_OK) return TCL_ERROR; int rowCount = 0; time_t atTime = 0; bool pack = false; Tcl_Obj *options = objv[3]; if (dictGetIfExists(interp, options, "row_count", rowCount) != TCL_OK) return TCL_ERROR; if (dictGetIfExists(interp, options, "at_time", atTime) != TCL_OK) return TCL_ERROR; if (dictGetIfExists(interp, options, "pack", pack) != TCL_OK) return TCL_ERROR; CandleTimer::Ref timer; if (isDaily) { std::string candleSize = "day"; if (dictGetIfExists(interp, options, "candle_size", candleSize) != TCL_OK) return TCL_ERROR; if (candleSize == "day") timer = new DailyCandleTimer(rowCount, atTime); else if (candleSize == "week") timer = new WeeklyCandleTimer(rowCount, atTime); else if (candleSize == "month") timer = new MonthlyCandleTimer(rowCount, atTime); else { Tcl_SetObjResult(interp, makeTclString("invalid candle_size: \"" + candleSize + "\", expecting day, week, or year")); return TCL_ERROR; } } else { // Intraday int minutesPerCandle = 0; int startOffset = 0; int endOffset = 0; if (dictGetIfExists(interp, options, "minutes_per_candle", minutesPerCandle) != TCL_OK) return TCL_ERROR; if (dictGetIfExists(interp, options, "start_offset", startOffset) != TCL_OK) return TCL_ERROR; if (dictGetIfExists(interp, options, "end_offset", endOffset) != TCL_OK) return TCL_ERROR; int firstCandleSize = minutesPerCandle; if (dictGetIfExists(interp, options, "first_candle_size", firstCandleSize) != TCL_OK) return TCL_ERROR; timer = new IntradayCandleTimer(rowCount, atTime, minutesPerCandle, startOffset, endOffset, firstCandleSize); } GridPrototypeRef prototype = createPrototype(timer, body, pack); const int64_t initialCellCount = prototype->getWidth() * (int64_t)timer->getPreviewRowCount(time(NULL)); if (initialCellCount > maxCellCount) { const std::string message = "Invalid prototype. " "The initial cell count would be " + ntoa(initialCellCount) + ", but the maximum allowed is " + ntoa(maxCellCount) + "."; Tcl_SetObjResult(interp, convertToTcl(message)); return TCL_ERROR; } NamedReference wrapper = new PrototypeWrapper(prototype); Tcl_SetObjResult(interp, createTclObject(wrapper)); return TCL_OK; } static int debugDumpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "object"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; HasDebugDump *dumpable = namedReference.as< HasDebugDump >(); if (!dumpable) { Tcl_SetObjResult(interp, makeTclString("Object of type " + namedReference.as< HasNamedReference >()->getTypeName())); return TCL_OK; } Tcl_SetObjResult(interp, makeTclString(dumpable->debugDump())); return TCL_OK; } static int getGridRowCount(bool preview, GridInstanceRef const &grid, time_t time, Tcl_Interp *interp) { GridPrototypeRef prototype = grid->getPrototype(); const int possible = preview ?prototype->getCandleTimer()->getPreviewRowCount(time) :prototype->getCandleTimer()->getTotalRowCount(time); const int result = grid->getPossibleToPacked(possible); Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); return TCL_OK; } static int getTotalRowCountCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if ((objc < 2) || (objc > 3)) { Tcl_WrongNumArgs(interp, 1, objv, "prototype|grid ?time?"); return TCL_ERROR; } time_t time = 0; if (objc >= 3) if (Tcl_GetLongFromObj(interp, objv[2], &time) != TCL_OK) return TCL_ERROR; if (time <= 0) time = ::time(NULL); NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; if (PrototypeWrapper *wrapper = namedReference.as< PrototypeWrapper >()) { const int result = wrapper->ref->getCandleTimer()->getTotalRowCount(time); Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); return TCL_OK; } else if (GridWrapper *wrapper = namedReference.as< GridWrapper >()) return getGridRowCount(false, wrapper->ref, time, interp); else { reportNotApplicableError(interp, namedReference, "prototype or grid"); return TCL_ERROR; } } static int getPreviewRowCountCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if ((objc < 2) ||(objc > 3)) { Tcl_WrongNumArgs(interp, 1, objv, "prototype|grid ?time?"); return TCL_ERROR; } time_t time = 0; if (objc >= 3) if (Tcl_GetLongFromObj(interp, objv[2], &time) != TCL_OK) return TCL_ERROR; if (time <= 0) time = ::time(NULL); NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; if (PrototypeWrapper *wrapper = namedReference.as< PrototypeWrapper >()) { const int result = wrapper->ref->getCandleTimer()->getPreviewRowCount(time); Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); return TCL_OK; } else if (GridWrapper *wrapper = namedReference.as< GridWrapper >()) return getGridRowCount(true, wrapper->ref,time, interp); else { reportNotApplicableError(interp, namedReference, "prototype or grid"); return TCL_ERROR; } } static int getTimesCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { enum { Default, Packed, Possible, Error } which = Error; if (objc == 3) which = Default; else if (objc == 4) { std::string whichString = getString(objv[1]); if (whichString == "-packed") which = Packed; else if (whichString == "-possible") which = Possible; } if (which == Error) { Tcl_WrongNumArgs(interp, 1, objv, "?-packed? ?-possible? prototype|grid row"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[objc-2], interp); if (!namedReference) return TCL_ERROR; int row; if (Tcl_GetIntFromObj(interp, objv[objc-1], &row) != TCL_OK) return TCL_ERROR; time_t start, end; if (PrototypeWrapper *wrapper = namedReference.as< PrototypeWrapper >()) { if (which == Default) which = Possible; if (which != Possible) { Tcl_SetObjResult(interp, makeTclString("The -packed option is not " "meaningful for prototypes.")); return TCL_ERROR; } wrapper->ref->getCandleTimer()->getTimes(row, start, end); } else if (GridWrapper *wrapper = namedReference.as< GridWrapper >()) { if (which == Default) which = Packed; if (which == Packed) row = wrapper->ref->getPackedToPossible(row); wrapper->ref->getPrototype()->getCandleTimer()->getTimes(row, start, end); } else { reportNotApplicableError(interp, namedReference, "prototype or grid"); return TCL_ERROR; } Tcl_Obj *result = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, result, Tcl_NewLongObj(start)); Tcl_ListObjAppendElement(interp, result, Tcl_NewLongObj(end)); Tcl_SetObjResult(interp, result); return TCL_OK; } static GridDataProviderRef findDataProvider(std::string dataType, std::string symbol) { if (dataType == CandleTimer::DAILY) return new CandleDataProvider(symbol, false); if (dataType == CandleTimer::INTRADAY) return new CandleDataProvider(symbol, true); return NULL; } static int createGridCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "prototype symbol"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; PrototypeWrapper *prototypeWrapper = namedReference.as< PrototypeWrapper >(); if (!prototypeWrapper) { reportNotApplicableError(interp, namedReference, "prototype"); return TCL_ERROR; } std::string symbol = getString(objv[2]); const std::string dataType = prototypeWrapper->ref->getCandleTimer()->dataType(); GridDataProviderRef dataProvider = findDataProvider(dataType, symbol); if (!dataProvider) { Tcl_SetObjResult(interp, makeTclString("Unable to find a data provider for " + dataType + " / " + symbol)); return TCL_ERROR; } GridInstanceRef grid = new GridInstance(prototypeWrapper->ref, dataProvider); NamedReference gridWrapper = new GridWrapper(grid); Tcl_SetObjResult(interp, createTclObject(gridWrapper)); return TCL_OK; } static int getColumnsCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "prototype|grid"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; GridPrototypeRef prototype; if (PrototypeWrapper *wrapper = namedReference.as< PrototypeWrapper >()) prototype = wrapper->ref; else if (GridWrapper *wrapper = namedReference.as< GridWrapper >()) prototype = wrapper->ref->getPrototype(); if (!prototype) { reportNotApplicableError(interp, namedReference, "prototype or grid"); return TCL_ERROR; } std::vector< std::string > columns; prototype->getColumns(columns); Tcl_Obj *result = Tcl_NewObj(); for (std::vector< std::string >::const_iterator it = columns.begin(); it != columns.end(); it++) Tcl_ListObjAppendElement(interp, result, makeTclString(*it)); Tcl_SetObjResult(interp, result); return TCL_OK; } static int fillCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { GridFiller &gridFiller = *(GridFiller *)clientData; Tcl_Obj *errorVarName; int endOfOptions; if (((objc == 5) || (objc == 6)) && (getString(objv[1]) == "-collect-errors")) { errorVarName = objv[2]; endOfOptions = 3; } else if ((objc == 3) || (objc == 4)) { errorVarName = NULL; endOfOptions = 1; } else { Tcl_WrongNumArgs(interp, 1, objv, "?-collect-errors varName? grid complete|preview ?time?"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[endOfOptions], interp); if (!namedReference) return TCL_ERROR; GridWrapper *wrapper = namedReference.as< GridWrapper >(); if (!wrapper) { reportNotApplicableError(interp, namedReference, "grid"); return TCL_ERROR; } time_t time = 0; if (objc == endOfOptions + 3) { if (Tcl_GetLongFromObj(interp, objv[endOfOptions + 2], &time) != TCL_OK) return TCL_ERROR; } if (time <= 0) time = ::time(NULL); int expectedRowCount; CandleTimer::Ref candleTimer = wrapper->ref->getPrototype()->getCandleTimer(); const std::string fillToString = getString(objv[endOfOptions + 1]); if (fillToString == "preview") expectedRowCount = candleTimer->getPreviewRowCount(time); else if (fillToString == "complete") expectedRowCount = candleTimer->getTotalRowCount(time); else { Tcl_SetObjResult(interp, makeTclString("fill_to should be \"preview\" or \"complete\"")); return TCL_ERROR; } // TODO set an upper bound on expectedRowCount. Like candleTimer->getPreviewRowCount(::time(now)) int recentWork = 0; GridInstance *const gridWriter = wrapper->ref->writeLock(); class ErrorCollectorImpl : public ErrorCollector { private: Tcl_Obj *_errorVarName; virtual void add(GridInstance *grid, Tcl_Interp *interp) { std::string key = ntoa(grid->getCalculationRowPacked()); key += ','; key += ntoa(grid->getCalculationCol()); TclObjHolder keyObj = makeTclString(key); Tcl_ObjSetVar2(interp, _errorVarName, keyObj.getObj(), Tcl_GetObjResult(interp), TCL_GLOBAL_ONLY); } public: ErrorCollectorImpl() : _errorVarName(NULL) { } bool init(Tcl_Obj *errorVarName, Tcl_Interp *interp) { if (!errorVarName) // NULL. No variable specified. return false; char *name = Tcl_GetStringFromObj(errorVarName, NULL); if (*name == '\0') // The empty string. We treat that the same as nothing specified. return false; _errorVarName = errorVarName; // Completely remove any variable with this name. Tcl_UnsetVar(interp, name, TCL_GLOBAL_ONLY); // Create an empty array variable with this name. Tcl_SetVar2(interp, name, "", "", TCL_GLOBAL_ONLY); Tcl_UnsetVar2(interp, name, "", TCL_GLOBAL_ONLY); return true; // Success. } } errorCollector; if (errorCollector.init(errorVarName, interp)) gridFiller.setErrorCollector(&errorCollector); gridWriter->removeOldData(); //sendToLogFile(TclList()<getDataProvider()->preloadToEnd(gridWriter); while (expectedRowCount > wrapper->ref->completedRowCountPossible()) { ThreadMonitor::find().increment("fillRow"); gridFiller.fillRow(gridWriter); //sendToLogFile(TclList()<completedRowCountPossible()); recentWork++; } gridFiller.setErrorCollector(NULL); //sendToLogFile(TclList()<getDataProvider()->releasePreload(); gridWriter->releaseWriteLock(); // Return the number of lines we updated. Tcl_SetObjResult(interp, Tcl_NewIntObj(recentWork)); return TCL_OK; } static int completedRowCountCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "grid"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; GridWrapper *wrapper = namedReference.as< GridWrapper >(); if (!wrapper) { reportNotApplicableError(interp, namedReference, "grid"); return TCL_ERROR; } const bool packed = (bool)clientData; int result; if (packed) result = wrapper->ref->completedRowCountPacked(); else result = wrapper->ref->completedRowCountPossible(); Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); return TCL_OK; } static int referenceCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { // Value should always be overridden or it should not be used. double value = -42; bool ok = false; if (objc >= 4) { NamedReference namedReference = extractNamedReference(objv[objc-3], interp); if (!namedReference) return TCL_ERROR; GridWrapper *wrapper = namedReference.as< GridWrapper >(); if (!wrapper) { reportNotApplicableError(interp, namedReference, "grid"); return TCL_ERROR; } int row; if (Tcl_GetIntFromObj(interp, objv[objc-2], &row) != TCL_OK) return TCL_ERROR; const std::string column = getString(objv[objc-1]); if (objc == 4) { value = wrapper->ref->referencePacked(row, column); ok = true; } else if ((objc == 5) && (getString(objv[1]) == "-possible")) { value = wrapper->ref->referencePossible(row, column, GridInstance::Round::Up); ok = true; } else if ((objc == 6) && (getString(objv[1]) == "-possible")) { GridInstance::Round round; std::string roundAsString = getString(objv[2]); if (roundAsString == "-round-down") { round = GridInstance::Round::Down; ok = true; } if (roundAsString == "-round-up") { round = GridInstance::Round::Up; ok = true; } if (roundAsString == "-fail") { round = GridInstance::Round::Fail; ok = true; } if (ok) value = wrapper->ref->referencePossible(row, column, round); } } if (!ok) { Tcl_WrongNumArgs(interp, 1, objv, "?-possible? ?-round-up? ?-round-down? " "?-fail? grid row column"); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewDoubleObj(value)); return TCL_OK; } static int getPrototypeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "grid"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; GridWrapper *wrapper = namedReference.as< GridWrapper >(); if (!wrapper) { reportNotApplicableError(interp, namedReference, "grid"); return TCL_ERROR; } const GridPrototypeRef prototype = wrapper->ref->getPrototype(); NamedReference resultWrapper = new PrototypeWrapper(prototype); Tcl_SetObjResult(interp, createTclObject(resultWrapper)); return TCL_OK; } static int maxCellCountCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc == 1) { // Requesting the current value Tcl_SetObjResult(interp, Tcl_NewIntObj(maxCellCount)); return TCL_OK; } else if (objc == 2) { // Changing the value int newValue; if (Tcl_GetIntFromObj(interp, objv[1], &newValue) != TCL_OK) return TCL_ERROR; if (newValue <= 0) { Tcl_SetObjResult(interp, makeTclString("new_value must be > 0")); return 0; } maxCellCount = newValue; return TCL_OK; } else { Tcl_WrongNumArgs(interp, 1, objv, "?new_value?"); return TCL_ERROR; } } static int oldestFastTimeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "intraday|daily symbol"); return TCL_ERROR; } static char const *timeFrameList[] = { "daily", "intraday", NULL }; int timeFrameIndex; int result = Tcl_GetIndexFromObj(interp, objv[1], timeFrameList, "daily or intraday", 0, &timeFrameIndex); if (result != TCL_OK) return TCL_ERROR; const std::string symbol = getString(objv[2]); CandleDataNode *data = CandleDataNode::find(symbol, timeFrameIndex); Tcl_SetObjResult(interp, Tcl_NewLongObj(data->oldestFastTime())); data->decrementReferenceCount(); return TCL_OK; } static int startRequiredForNCandlesCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if ((objc < 4) || (objc > 5)) { Tcl_WrongNumArgs(interp, 1, objv, "prototype symbol count ?start_before_time?"); return TCL_ERROR; } NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; CandleTimer::Ref timer; if (PrototypeWrapper *wrapper = namedReference.as< PrototypeWrapper >()) { timer = wrapper->ref->getCandleTimer(); } else if (GridWrapper *wrapper = namedReference.as< GridWrapper >()) { timer = wrapper->ref->getPrototype()->getCandleTimer(); } else { reportNotApplicableError(interp, namedReference, "prototype or grid"); return TCL_ERROR; } const std::string symbol = getString(objv[2]); int count; if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) return TCL_ERROR; if (count <= 0) { Tcl_SetObjResult(interp, makeTclString("count must be > 0")); return TCL_ERROR; } time_t startBeforeTime; if (objc == 4) startBeforeTime = time(NULL); // Default is now. else { if (Tcl_GetLongFromObj(interp, objv[4], &startBeforeTime) != TCL_OK) return TCL_ERROR; } bool intraday; { std::string dataType = timer->dataType(); if (dataType == CandleTimer::DAILY) intraday = false; else if (dataType == CandleTimer::INTRADAY) intraday = true; else { Tcl_SetObjResult(interp, makeTclString("Unknown candle type: " + dataType)); return TCL_ERROR; } } std::set< time_t > bigCandles; CandleDataNode *const data = CandleDataNode::find(symbol, intraday); /* sendToLogFile(TclList()<debugDump()); */ // The outer loop grabs one block of data at a time from the data source. // The inner loop iterates over each time stored in that block. We exit // early if we find the result. for (CandleDataNode::StartTimeList list = data->startTimeList(startBeforeTime); list.goodToGo(); list = data->startTimeList(list)) for (time_t startOfSmallCandle : list) { //TclList msg; //msg<getTotalRowCount(startOfSmallCandle); //msg<<"possibleRow"<getTimes(possibleRow, bigCandleStart, bigCandleEnd); //msg<<"bigCandleStart"<= bigCandleStart) && (startOfSmallCandle < bigCandleEnd)) { bigCandles.insert(possibleRow); //msg<<"bigCandles.size()"<= count) { // Done. Break out of both loops. //msg<<"DONE!"; //sendToLogFile(msg); list.clear(); break; } } } //sendToLogFile(msg); } //sendToLogFile(TclList()<debugDump()); data->decrementReferenceCount(); // We either ran out of data, or found what we needed. if (bigCandles.empty()) { // Nothing found. Return the initial start time because that seems // safe. I'd hate to return 0 or a very negative number and find out // that our estimate was wrong! Tcl_SetObjResult(interp, Tcl_NewLongObj(startBeforeTime)); } else { // Return the oldest (smallest) time. time_t candleStart, candleEnd; timer->getTimes(*bigCandles.begin(), candleStart, candleEnd); Tcl_SetObjResult(interp, Tcl_NewLongObj(candleStart)); } return TCL_OK; } static int reanchorCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if ((objc < 3) || (objc > 4)) { Tcl_WrongNumArgs(interp, 1, objv, "prototype count ?start_before_time?"); return TCL_ERROR; } // TODO This gird or prototype code has been cloned a few times. // It should be a function. NamedReference namedReference = extractNamedReference(objv[1], interp); if (!namedReference) return TCL_ERROR; GridPrototypeRef originalPrototype; if (PrototypeWrapper *wrapper = namedReference.as< PrototypeWrapper >()) { originalPrototype = wrapper->ref; } else if (GridWrapper *wrapper = namedReference.as< GridWrapper >()) { originalPrototype = wrapper->ref->getPrototype(); } else { reportNotApplicableError(interp, namedReference, "prototype or grid"); return TCL_ERROR; } int count; if (Tcl_GetIntFromObj(interp, objv[2], &count) != TCL_OK) return TCL_ERROR; time_t startBeforeTime; if (objc == 3) startBeforeTime = time(NULL); // Default is now. else { if (Tcl_GetLongFromObj(interp, objv[3], &startBeforeTime) != TCL_OK) return TCL_ERROR; } const CandleTimer::Ref newTimer = originalPrototype->getCandleTimer()->reanchor(count, startBeforeTime); const GridPrototypeRef newPrototype = new GridPrototype(*originalPrototype, newTimer); NamedReference wrapper = new PrototypeWrapper(newPrototype); Tcl_SetObjResult(interp, createTclObject(wrapper)); return TCL_OK; } void installGridObjectWrapper(Tcl_Interp *interp, GridFiller *gridFiller) { Tcl_CreateObjCommand(interp, "ti::create_prototype", createPrototypeCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::debug_dump", debugDumpCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::get_total_row_count", getTotalRowCountCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::get_preview_row_count", getPreviewRowCountCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::get_times", getTimesCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::create_grid", createGridCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::get_columns", getColumnsCmd, NULL, NULL); if (gridFiller) Tcl_CreateObjCommand(interp, "ti::fill", fillCmd, gridFiller, NULL); Tcl_CreateObjCommand(interp, "ti::completed_row_count_possible", completedRowCountCmd, (ClientData)false, NULL); Tcl_CreateObjCommand(interp, "ti::reference", referenceCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::get_prototype", getPrototypeCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::completed_row_count_packed", completedRowCountCmd, (ClientData)true, NULL); Tcl_CreateObjCommand(interp, "ti::max_cell_count", maxCellCountCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::oldest_fast_time", oldestFastTimeCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::start_required_for_n_candles", startRequiredForNCandlesCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::reanchor", reanchorCmd, NULL, NULL); }