#include "../misc_framework/GenericDataNodes.h" #include "GenericL1Data.h" #include "GenericTosData.h" #include "../misc_framework/CsvFileDataNodes.h" #include "../../shared/MiscSupport.h" #include "../../shared/SimpleLogFile.h" #include "AverageHistoricalVolume.h" #include "../../shared/MarketHours.h" #include "SimpleMarketData.h" //////////////////////////////////////////////////////////////////// // SimpleL1User //////////////////////////////////////////////////////////////////// class SimpleL1User : public GenericDataNode { public: enum Type { BestBidPrice, BestAskPrice, BestBidSize, BestAskSize, BidExchange, AskExchange }; private: Type _type; GenericL1DataNode *l1Data; SimpleL1User(DataNodeArgument args); friend class GenericDataNodeFactory; static void storeFactory(std::string const &name, Type type); public: void getDouble(bool &valid, double &value) const; void getInteger(bool &valid, Integer &value) const; void getString(bool &valid, std::string &value) const; static void storeStandardFactories(); }; SimpleL1User::SimpleL1User(DataNodeArgument args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); std::string const &symbol = argList[0].getStringValue(); _type = (Type)argList[1].getIntValue(); addAutoLink(GenericL1DataNode::find(this, 0, l1Data, symbol)); } void SimpleL1User::getDouble(bool &valid, double &value) const { valid = l1Data->getValid(); if (!valid) return; switch (_type) { case BestBidPrice: value = l1Data->getCurrent().bidPrice; break; case BestAskPrice: value = l1Data->getCurrent().askPrice; break; default: value = 0.0; } // This will not work right for certain special entities. Certain indexes // allow negative numbers. valid = value > 0.0; } void SimpleL1User::getInteger(bool &valid, Integer &value) const { valid = l1Data->getValid(); if (!valid) return; switch (_type) { case BestBidSize: value = l1Data->getCurrent().bidSize; break; case BestAskSize: value = l1Data->getCurrent().askSize; break; default: value = 0; } valid = value > 0; } void SimpleL1User::getString(bool &valid, std::string &value) const { valid = l1Data->getValid(); if (!valid) return; switch (_type) { case BidExchange: value = l1Data->getCurrent().bidExchange; break; case AskExchange: value = l1Data->getCurrent().askExchange; break; default: value.clear(); } valid = !value.empty(); } void SimpleL1User::storeFactory(std::string const &name, Type type) { DataNodeArgumentVector args; args.push_back(symbolPlaceholderObject); args.push_back(type); DataNodeArgument factory = GenericDataNodeFactory::create< SimpleL1User >(args); GenericDataNodeFactory::storeFactory(name, factory); } void SimpleL1User::storeStandardFactories() { storeFactory("BidPrice", BestBidPrice); storeFactory("AskPrice", BestAskPrice); storeFactory("BidSize", BestBidSize); storeFactory("AskSize", BestAskSize); storeFactory("BidExchange", BidExchange); storeFactory("AskExchange", AskExchange); } //////////////////////////////////////////////////////////////////// // SimpleTosUser //////////////////////////////////////////////////////////////////// class SimpleTosUser : public GenericDataNode { public: enum Type { TodaysClosePrice, LastPrintPrice, LastPrintTime, LastPrintSize, Volume, LastPrintExchange, LastPrintFormT, LastPrintUpdatesLast, OpenPrice, HighPrice, LowPrice, TodaysRange }; private: Type _type; GenericTosDataNode *tosData; SimpleTosUser(DataNodeArgument args); friend class GenericDataNodeFactory; static void storeFactory(std::string const &name, Type type); public: void getDouble(bool &valid, double &value) const; void getInteger(bool &valid, Integer &value) const; void getString(bool &valid, std::string &value) const; void getBoolean(bool &valid, bool &value) const; static void storeStandardFactories(); }; SimpleTosUser::SimpleTosUser(DataNodeArgument args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); std::string const &symbol = argList[0].getStringValue(); _type = (Type)argList[1].getIntValue(); addAutoLink(GenericTosDataNode::find(this, 0, tosData, symbol)); } void SimpleTosUser::getDouble(bool &valid, double &value) const { valid = tosData->getValid(); if (!valid) return; switch (_type) { case TodaysClosePrice: value = tosData->getLast().todaysClose; break; case LastPrintPrice: value = tosData->getLast().price; break; case OpenPrice: value = tosData->getLast().open; break; case HighPrice: value = tosData->getLast().high; break; case LowPrice: value = tosData->getLast().low; break; case TodaysRange: value = tosData->getLast().high - tosData->getLast().low; break; default: value = 0.0; } // This will not work right for certain special entities. Certain indexes // allow negative numbers. valid = value > 0.0; } void SimpleTosUser::getInteger(bool &valid, Integer &value) const { valid = tosData->getValid(); if (!valid) return; switch (_type) { case LastPrintTime: value = tosData->getLast().time; break; case LastPrintSize: value = tosData->getLast().size; break; case Volume: value = tosData->getLast().volume; break; default: value = 0; } valid = value > 0; } void SimpleTosUser::getString(bool &valid, std::string &value) const { valid = tosData->getValid(); if (!valid) return; switch (_type) { case LastPrintExchange: value = tosData->getLast().exchange; break; default: value.clear(); } valid = !value.empty(); } void SimpleTosUser::getBoolean(bool &valid, bool &value) const { valid = tosData->getValid(); if (!valid) return; switch (_type) { case LastPrintFormT: value = tosData->getLast().formT; break; case LastPrintUpdatesLast: value = tosData->getLast().updatesLast; break; default: valid = false; } } void SimpleTosUser::storeFactory(std::string const &name, Type type) { DataNodeArgumentVector args; args.push_back(symbolPlaceholderObject); args.push_back(type); DataNodeArgument factory = GenericDataNodeFactory::create< SimpleTosUser >(args); GenericDataNodeFactory::storeFactory(name, factory); } void SimpleTosUser::storeStandardFactories() { storeFactory("TodaysClosePrice", TodaysClosePrice); storeFactory("LastPrintPrice", LastPrintPrice); storeFactory("LastPrintTime", LastPrintTime); storeFactory("LastPrintSize", LastPrintSize); storeFactory("Volume", Volume); storeFactory("LastPrintExchange", LastPrintExchange); storeFactory("LastPrintFormT", LastPrintFormT); storeFactory("LastPrintUpdatesLast", LastPrintUpdatesLast); storeFactory("OpenPrice", OpenPrice); storeFactory("HighPrice", HighPrice); storeFactory("LowPrice", LowPrice); storeFactory("TodaysRange", TodaysRange); } //////////////////////////////////////////////////////////////////// // DayRange // // This appears to be obsolete. We compute this in this server, and // then store the result in the database. The database now has // enough raw information that we can compute this value in the // distribution servers. //////////////////////////////////////////////////////////////////// /* class DayRange : public GenericDataNode { private: GenericTosDataNode *tosData; GenericL1DataNode *l1Data; DayRange(DataNodeArgument args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; DayRange::DayRange(DataNodeArgument args) { std::string const &symbol = args.getStringValue(); addAutoLink(GenericTosDataNode::find(this, 0, tosData, symbol)); addAutoLink(GenericL1DataNode::find(this, 0, l1Data, symbol)); } void DayRange::getDouble(bool &valid, double &value) const { L1Data const ¤t = l1Data->getCurrent(); const double lastPrice = tosData->getLast().price; valid = l1Data->getValid() && tosData->getValid() && (current.high > current.low) && (current.low > 0) && (lastPrice > 0); if (valid) value = (lastPrice - current.low) / (current.high - current.low); } */ //////////////////////////////////////////////////////////////////// // DistanceFromNbbo // // This is a way to say how likely it is that a print is invalid. If the // print in between the bid and ask it is very reasonable. The further // away it is, the more likely that it is not real. // // There are a lot of cases where this would have helped -- late prints // and stuff -- but we've fixed those in other places. But this still should // be useful. Especially in the oddsmaker, where we want to see prices that // we can actually get. // // There are some cases where the bid and ask are messed up. Sometimes a // bid or ask gets stuck on an ECN, and someone manages to get a print // at the odd value. However, this is rare. Usually the bid and ask do a // good job. // // If the spread is negative, we still look to see if the value is BETWEEN // the bid and ask. These still look like reasonable prints. // // Anyting between the bid and ask (inclusive) gives a result of 0. // Otherwise we compare the price to whichever one is closer to the price. // We return the % difference in the normal way. The difference between the // price and the bid or ask divided by the bid or ask * 100. //////////////////////////////////////////////////////////////////// class DistanceFromNbbo : public GenericDataNode { private: GenericTosDataNode *tosData; GenericL1DataNode *l1Data; DistanceFromNbbo(DataNodeArgument args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; DistanceFromNbbo::DistanceFromNbbo(DataNodeArgument args) { std::string const &symbol = args.getStringValue(); addAutoLink(GenericTosDataNode::find(this, 0, tosData, symbol)); addAutoLink(GenericL1DataNode::find(this, 0, l1Data, symbol)); } void DistanceFromNbbo::getDouble(bool &valid, double &value) const { const double bid = l1Data->getCurrent().bidPrice; const double ask = l1Data->getCurrent().askPrice; const double lastPrice = tosData->getLast().price; valid = l1Data->getValid() && tosData->getValid() && (bid > 0) && (ask > 0) && (lastPrice > 0); if (valid) { const double rangeTop = std::max(bid, ask); const double rangeBottom = std::min(bid, ask); if (lastPrice > rangeTop) value = (lastPrice - rangeTop) / rangeTop * 100.0; else if (lastPrice < rangeBottom) value = (rangeBottom - lastPrice) / rangeBottom * 100.0; else value = 0.0; } } //////////////////////////////////////////////////////////////////// // MostRecentClose //////////////////////////////////////////////////////////////////// class MostRecentClose : public GenericDataNode { private: GenericTosDataNode *tosData; double _previousClose; MostRecentClose(DataNodeArgument const &args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; MostRecentClose::MostRecentClose(DataNodeArgument const &args) { std::string const &symbol = args.getStringValue(); _previousClose = getPreviousClose(symbol); addAutoLink(GenericTosDataNode::find(this, 0, tosData, symbol)); } void MostRecentClose::getDouble(bool &valid, double &value) const { // We switch over from yesterday's close to today's close as soon as the // closing bell rings. This does not mean that the closing price is frozen // yet. There is no way to know that. // Note that this data node does not notify the user when there is a shift. // Typically this value will change at the close. Perhaps we should send out // a notification. The exact value of that is questionable since there is // only one expected use for this data node. That is for the background info // for an alert. The background info only reads values, it never signals // anything. if (secondOfTheDay(getSubmitTime()) >= MARKET_HOURS_CLOSE) value = tosData->getLast().todaysClose; else value = _previousClose; valid = value; } //////////////////////////////////////////////////////////////////// // UpForTheDay // // The formuala is simple: // ((Current Price) - (Yesterday's Close)) / (Yesterday's Close) * 100. // // This is really aimed at the various market indicies. For the // individual stocks, we send the current price and the close price // to the database seperately. The indicies can only be used in this // way. //////////////////////////////////////////////////////////////////// class UpForTheDay : public GenericDataNode { private: GenericTosDataNode *_tosData; double _previousClose; UpForTheDay(DataNodeArgument const &args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; UpForTheDay::UpForTheDay(DataNodeArgument const &args) { std::string const &symbol = args.getStringValue(); _previousClose = getPreviousClose(symbol); addAutoLink(GenericTosDataNode::find(this, 0, _tosData, symbol)); } void UpForTheDay::getDouble(bool &valid, double &value) const { valid = false; if ((_previousClose <= 0) || (!_tosData->getValid())) return; const double price = _tosData->getLast().price; if (price <= 0) return; value = (price - _previousClose) / _previousClose * 100.0; valid = true; return; } //////////////////////////////////////////////////////////////////// // RelativeVolume // // This data node never notifies its listeners. If it tried to, it would do // so on every TOS or L1 event for the symbol. But it really updates // continuously, because it takes the current time into consideration. The // only expected use is info for an existing alert, so this is not a problem. //////////////////////////////////////////////////////////////////// class RelativeVolume : public GenericDataNode { private: GenericDataNode *_currentVolume; AverageHistoricalData *_expectedVolume; //Integer _volumePoints[AHV_PERIODS + 1]; Integer *_volumePoints; void onWakeup(int msgId); void updateExpectedVolume(); RelativeVolume(DataNodeArgument args); friend class GenericDataNodeFactory; ~RelativeVolume(); public: void getDouble(bool &valid, double &value) const; }; void RelativeVolume::updateExpectedVolume() { if (_expectedVolume->isValid()) { _volumePoints[0] = _expectedVolume->getPreMarketVolume(); for (int i = 1; i <= AHV_PERIODS; i++) _volumePoints[i] = _volumePoints[i-1] + _expectedVolume->getVolume(i); } } void RelativeVolume::onWakeup(int msgId) { // We don't expect this to happen much. Currently the CSV data nodes never // notify their listeners. But they could. updateExpectedVolume(); // The delphi version deferred this call. Instead of immediately updating // the volume, it would clear the valid flag. In theory that could reduce // the workload, if a lot of stocks had volume data but this data node was // never used. I think that's unlikely. And doing it this way made it // easier for getDouble() to be const. } RelativeVolume::RelativeVolume(DataNodeArgument args) : _volumePoints(new Integer[AHV_PERIODS + 1]) { addAutoLink(AverageHistoricalData::find(this, 0, _expectedVolume, args.getStringValue())); DataNodeArgument factory = GenericDataNodeFactory::findFactory("Volume"); factory = factory.replace(symbolPlaceholder, args); addAutoLink(factory.getValue< GenericDataNodeFactory >() .find(_currentVolume)); updateExpectedVolume(); } RelativeVolume::~RelativeVolume() { delete[] _volumePoints; } void RelativeVolume::getDouble(bool &valid, double &value) const { if (!_expectedVolume->isValid()) { valid = false; return; } Integer actualValue; _currentVolume->getInteger(valid, actualValue); if (!valid) return; double period = secondOfTheDay(getSubmitTime()) - MARKET_HOURS_OPEN; period /= AHV_PERIOD; if (period < 0) { valid = false; return; } double expectedValue; if (period >= AHV_PERIODS) expectedValue = _volumePoints[AHV_PERIODS]; else { // (int) rounds toward 0. If the number is too big, max int is returned, // and there is no exception. int lowerPeriod = (int)period; double fraction = period - lowerPeriod; expectedValue = _volumePoints[lowerPeriod] * (1 - fraction) + _volumePoints[lowerPeriod+1] * fraction; } if (expectedValue == 0) if (actualValue == 0) // 0/0 -> 1 because 1 means exactly what was expected! value = 1; else // This is a good enough approximation of +infinity based on the way it // will be used. This will typically be compared to a reasonable value, // and 999999 is above all reasonable values. That's not 100% true. // We eventually added a relative volume column. But I've never seen // this value in that column in real situations. value = 999999; else value = actualValue / expectedValue; valid = true; } //////////////////////////////////////////////////////////////////// // VWAP // // We assume that we are running all day. Otherwise we will miss a lot of // prints and we have no way to recover. // // This will work if you leave it on overnight. It will reset automatically // at midnight. //////////////////////////////////////////////////////////////////// class VWAP : public GenericDataNode { private: GenericTosDataNode *_tosData; time_t _resetDate; Integer _totalShares; double _totalPrice; void checkDate(); void onWakeup(int msgId); void getDouble(bool &valid, double &value) const; VWAP(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void VWAP::checkDate() { if (getSubmitTime() > _resetDate) { // At midnight, automatically reset. _resetDate = midnight(getSubmitTime()) + MARKET_HOURS_DAY; _totalShares = 0; _totalPrice = 0.0; notifyListeners(); } } void VWAP::onWakeup(int msgId) { checkDate(); if (_tosData->getValid()) if (_tosData->getLast().newPrint) { _totalShares += _tosData->getLast().size; _totalPrice += _tosData->getLast().size * _tosData->getLast().price; notifyListeners(); } } void VWAP::getDouble(bool &valid, double &value) const { valid = _totalShares > 0; if (valid) value = _totalPrice / _totalShares; } VWAP::VWAP(DataNodeArgument const &args) : _resetDate(0), _totalShares(0), _totalPrice(0.0) { addAutoLink(GenericTosDataNode::find(this, 0, _tosData, args.getStringValue())); // The Delphi code also looked at the L1 data. That was used just to check // for the reset. That seems like overkill since we don't expect to be left // running overnight anyway. If that becomes a requirement, then we could // check the time when getDouble(), or we could get a timer event to wake us // at midnight. } //////////////////////////////////////////////////////////////////// // Expected Open // // Before the open we use the last print. That's a best guess at /// what the open will be. After the open we use the official open // from the market. // // The previous definition is listed below. It caused all sorts of // problems, and I'm not sure what the original value was. // // Between the (first print after the) open and the (first print after the) // close, this is the official open from the market. Otherwise this is the // last print, which is our best estimate of the next open price. // Warning: This can oscillate shortly after the close. This is especially // bad for certain AMEX issues which the ECNs report as closed at 1pm, and // the exchange reports as open until much later. //////////////////////////////////////////////////////////////////// class ExpectedOpen : public GenericDataNode { private: GenericTosDataNode *_tosData; void getDouble(bool &valid, double &value) const; ExpectedOpen(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void ExpectedOpen::getDouble(bool &valid, double &value) const { value = 0.0; if (_tosData->getValid()) { // Start with the "real" open price. value = _tosData->getLast().open; if (value == 0.0) // Before the open, use the most recent price. value = _tosData->getLast().price; } valid = value != 0.0; } ExpectedOpen::ExpectedOpen(DataNodeArgument const &args) { std::string const &symbol = args.getStringValue(); addAutoLink(GenericTosDataNode::find(this, 0, _tosData, symbol)); } //////////////////////////////////////////////////////////////////// // PostMarketVolume // // This uses a very simple formula. We record last volume number // before 1pm. And we compare that to the current volume. This does // not look at form T. // // We assume that we are running all day. We can't ask for the volume // at the end of normal trading. We have to watch and record that // ourselves. If we don't see anything, we assume it we really 0, not // an error! // // This will work if you leave it on overnight. It will reset automatically // at midnight. //////////////////////////////////////////////////////////////////// class PostMarketVolume : public GenericDataNode { GenericTosDataNode *_tosData; time_t _resetDate; Integer _preAndNormalShares; bool _postMarketStarted; void checkDate(); void onWakeup(int msgId); void getInteger(bool &valid, Integer &value) const; PostMarketVolume(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void PostMarketVolume::checkDate() { const time_t time = _tosData->getValid()?_tosData->getLast().time:getSubmitTime(); if (time > _resetDate) { // At midnight, automatically reset. /* TclList msg; msg<symbol() <<"old _resetDate"<<_resetDate <<"old _preAndNormalShares"<<_preAndNormalShares <<"old _postMarketStarted"<<(int)_postMarketStarted; sendToLogFile(msg); */ _resetDate = midnight(time) + MARKET_HOURS_DAY; _preAndNormalShares = 0; _postMarketStarted = false; notifyListeners(); } } void PostMarketVolume::onWakeup(int msgId) { checkDate(); if (_tosData->getValid()) { if (!_postMarketStarted) { const int time = secondOfTheDay(_tosData->getLast().time); _postMarketStarted = time >= MARKET_HOURS_CLOSE; /* if (_postMarketStarted) { TclList msg; msg<symbol() <<"Post Market starts now!" <<"_preAndNormalShares"<<_preAndNormalShares; sendToLogFile(msg); } */ } if (_postMarketStarted) notifyListeners(); else _preAndNormalShares = _tosData->getLast().volume; } } void PostMarketVolume::getInteger(bool &valid, Integer &value) const { valid = _tosData->getValid(); if (valid) { if (_postMarketStarted) value = std::max((Integer)0, _tosData->getLast().volume - _preAndNormalShares); else value = 0; } } PostMarketVolume::PostMarketVolume(DataNodeArgument const &args) : _resetDate(0), _preAndNormalShares(0), _postMarketStarted(false) { addAutoLink(GenericTosDataNode::find(this, 0, _tosData, args.getStringValue())); } //////////////////////////////////////////////////////////////////// // Global //////////////////////////////////////////////////////////////////// DataNode::Integer getAverageDailyVolume(std::string const &symbol) { return strtollDefault(FileOwnerDataNode::getStringValue ("OvernightData.csv", "Avg Daily Volume", symbol), 0); } double getTickVolatility(std::string const &symbol) { double result = strtodDefault(FileOwnerDataNode::getStringValue ("OvernightData.csv", "Tick Volatility", symbol), 0.0); if (result > 0.0) return result; return 0.0; } std::string getListedExchange(std::string const &symbol) { return FileOwnerDataNode::getStringValue("OvernightData.csv", "Listed Exchange", symbol); } double getPreviousClose(std::string const &symbol) { // Seems like this should be in TC_OvernightData, not here! return strtodDefault(FileOwnerDataNode::getStringValue ("OvernightData.csv", "Last Price", symbol), 0.0); } bool symbolIsIndex(std::string const &symbol) { if (symbol.empty()) return false; return symbol[0] == '='; } const std::string s_AMEX = "AMEX"; const std::string s_NASD = "NASD"; const std::string s_NYSE = "NYSE"; const std::string s_NDX = "$NDX"; const std::string s_ARCA = "ARCA"; static PropertyList longExchangeNames; std::string longExchangeName(std::string const &dataFeedExchangeName) { if (dataFeedExchangeName.empty()) return dataFeedExchangeName; std::string possible = getPropertyDefault(longExchangeNames, dataFeedExchangeName); if (!possible.empty()) return possible; return '[' + dataFeedExchangeName + ']'; } static std::string nyseOnDataFeedValue; static std::string amexOnDataFeedValue; static std::string nasdaqOnDataFeedValue; static std::string arcaOnDataFeedValue; std::string const &nyseOnDataFeed() { return nyseOnDataFeedValue; } std::string const &amexOnDataFeed() { return amexOnDataFeedValue; } std::string const &nasdaqOnDataFeed() { return nasdaqOnDataFeedValue; } std::string const &arcaOnDataFeed() { return arcaOnDataFeedValue; } static void initDataFeedNames() { TwoDArray data; data.loadFromCSV(FileOwnerDataNode::findDataFile("exchanges.csv")); for (TwoDArray::StringList::const_iterator it = data.getRowHeaders().begin(); it != data.getRowHeaders().end(); it++) { if (it->empty()) continue; longExchangeNames[*it] = data.get("human", *it); const std::string internal = data.get("internal", *it); if (internal == s_AMEX) amexOnDataFeedValue = *it; else if (internal == s_NYSE) nyseOnDataFeedValue = *it; else if (internal == s_NASD) nasdaqOnDataFeedValue = *it; else if (internal == s_ARCA) arcaOnDataFeedValue = *it; } } // This implementation is specific to SpryWare. std::string compositeToRegional(std::string const &symbol, std::string const &exchangeCode) { if (symbol.find('.') != symbol.npos) // This is presumably a Canadian symbol. return ""; return symbol + '.' + exchangeCode; } void initializeSimpleMarketData() { initDataFeedNames(); SimpleL1User::storeStandardFactories(); SimpleTosUser::storeStandardFactories(); GenericDataNodeFactory::storeStandardFactory< DistanceFromNbbo > ("DistanceFromNbbo"); GenericDataNodeFactory::storeStandardFactory< MostRecentClose > ("MostRecentClose"); GenericDataNodeFactory::storeStandardFactory< RelativeVolume >("RelVol"); GenericDataNodeFactory::storeStandardFactory< VWAP >("VWAP"); GenericDataNodeFactory::storeStandardFactory< ExpectedOpen >("ExpectedOpen"); GenericDataNodeFactory::storeFactory("ClosePrice", // Previous close. makeCsvFactory("OvernightData.csv", "Last Price")); GenericDataNodeFactory::sf< UpForTheDay >("QQQQD", "QQQ"); GenericDataNodeFactory::sf< UpForTheDay >("SPYD", "SPY"); GenericDataNodeFactory::sf< UpForTheDay >("DIAD", "DIA"); GenericDataNodeFactory::storeStandardFactory< PostMarketVolume > ("PostMarketVolume"); } /* This will convert from our internal names that we use in our database. * We keep the codes from the data feed in some cases because there are a * lot more of them. { static const std::string s_American = "American"; static const std::string s_NASDAQ = "NASDAQ"; static const std::string s_NewYork = "New York"; if (shortExchangeName == s_NASD) return s_NASDAQ; else if (shortExchangeName == s_NYSE) return s_NewYork; else if (shortExchangeName == s_AMEX) return s_American; // Else If ShortExchangeName = 'CBOE' Then // Result := 'Chicago Board Options' else return shortExchangeName; } */ // Obsolete: The database now stores the high and low, so this can be // compued later. /* This is simply the high of the day less the low of the day. We choose to use the L1 data directly so that the high and low will be undefined before the open. TTodaysRange = */ /* TPositionInRange = Class(TGenericDataNode) Private Data : Array [0..2] Of TGenericDataNode; Public Constructor Create(Params : TParamList); Override; Function IsValid : Boolean; Override; Published Function GetDouble : Double; Override; End; */