//=========================================================================== // // Based on SpryWare snapshot tutorial 3 and ReadFromActiv.C // //=========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include "../for_spryware/SpryWareSimple.h" #include "SymbolList.h" #include "../../shared/MiscSupport.h" #include "../../shared/TwoDLookup.h" #include "StockInfo.h" #include "ReadFromSpryWare.h" class SpryWareSymbolsClient { private: static const int RECORDS_PER_REQUEST = 2500; MisDirectory *pDirectory; // Ignore stocks not listed on one of these exchanges. std::set< std::string > _exchanges; // This is what we export to the main program. It might get merged with data from the database. StockInfoList _stockInfo; //helper function called from getSymbols() void getSymbolsFromTable(std::string); void finishStockInfo(); bool isValidSymbol(std::string); public: SpryWareSymbolsClient(){} void getSymbols(); void setExchanges(std::vector< std::string > const &exchanges); StockInfoList getStockInfoList() const { return _stockInfo; } }; void SpryWareSymbolsClient::setExchanges(std::vector< std::string > const &exchanges) { _exchanges.clear(); for (std::vector< std::string >::const_iterator it = exchanges.begin(); it != exchanges.end(); it++) { std::string const &exchange = trim__((const std::string) *it); if (!exchange.empty()) _exchanges.insert(exchange); } if (_exchanges.empty()) { // Fill in defaults. _exchanges.insert("Q"); // NASDAQ _exchanges.insert("N"); // New York _exchanges.insert("U"); // OTC Bulletin Board _exchanges.insert("A"); // AMEX _exchanges.insert("T"); // Canada _exchanges.insert("V"); // Canada } } void SpryWareSymbolsClient::getSymbols() { std::string str; // Create the directory object pDirectory = CreateMisDirectory(); if (0==pDirectory) { fprintf(stderr,"Error: Unable to Create MisDirectory\n"); return; } // Open and parse the config file if (0 == pDirectory->GetConfigFile(str) && true==File::IsExist(str.c_str())) { fprintf(stdout,"Loaded config file %s\n", str.c_str()); } else if (0 == pDirectory->GetConfigFile(str) && false==File::IsExist(str.c_str())) fprintf(stderr,"Config file %s does not exist\n", str.c_str()); getSymbolsFromTable(TABLE_EQUITY_PRICE); //finishStockInfo(); // we are done, so clean up delete pDirectory; } static uint32_t getFromUInt32(Database::Record const &record, unsigned int id) { uint32_t result; unsigned rc = record.GetField(id, result); if (rc == RETURN_CODE_SUCCESS) return result; else return 0; } static double getPricePrice(Database::Record &record, unsigned int id) { Database::IField *field; unsigned rc = record.GetField(id, field); if (rc != RETURN_CODE_SUCCESS) return 0.0; Database::Price *priceField = dynamic_cast< Database::Price * >(field); assert(priceField); double result; if (priceField->GetPrice(result) != RETURN_CODE_SUCCESS) return 0.0; return result; } static time_t getPriceTime(Database::Record &record, unsigned int id) { Database::IField *field; unsigned rc = record.GetField(id, field); if (rc != RETURN_CODE_SUCCESS) return 0; Database::Price *priceField = dynamic_cast< Database::Price * >(field); assert(priceField); Database::DateTime dateTime; if (!priceField->GetDateTime(dateTime)) return 0; if (!dateTime.IsDate()) return 0; unsigned year; unsigned month; unsigned day; if (!dateTime.GetDate(year, month, day)) return 0; unsigned hour; unsigned minute; unsigned second; unsigned milliseconds; if (!dateTime.GetTime(hour, minute, second, milliseconds)) { hour = 0; minute = 0; second = 0; } // Oddly enough I get a date and no time for the last, the close, and the // previous close. tableviewer shows me only a time for the last, and only // a date for the other two. struct tm brokenDown; brokenDown.tm_sec = second; brokenDown.tm_min = minute; brokenDown.tm_hour = hour; brokenDown.tm_mday = day; brokenDown.tm_mon = month - 1; brokenDown.tm_year = year - 1900; brokenDown.tm_isdst = -1; return mktime(&brokenDown); } template < class X > X max(X a, X b, X c) { return std::max(a, std::max(b, c)); } void SpryWareSymbolsClient::getSymbolsFromTable(std::string tableName) { Database::ITable *pTable; SpryWareOpenArgs options; // Create an instance of the table, and attempt to open it SpryWareOpenArgs().connect(pDirectory, tableName, pTable); if(!pTable) { printf("Unable to Connect to table \'%s\'\n", tableName.c_str()); return; } else { std::string hostName; pTable->GetHostName(hostName); fprintf(stdout, "Connected to table \'%s\' on %s\n", tableName.c_str(), hostName.c_str()); } // Create new key Database::Key *key = new Database::Key; // Use EquityPrice table with key zero key->Initialize(pTable, 0); // Create and initialize record to hold data returned from query Database::Record *record = new Database::Record; record->Initialize(pTable); Database::IndexPosition position = {0}; //initial seed value unsigned rc = pTable->GetFirst(*key, *record, false, &position); if (rc) { fprintf(stderr,"First GetFirst: %d %s\n", rc, MIS::ERR::ToString(rc)); return; } //initialize buffer so we don't have to go to the database for every single record unsigned bufferLength = Database::KeyRecordListHelper::GetBufferLength(RECORDS_PER_REQUEST,(unsigned)key->GetLength(),(unsigned)record->GetLength()); Database::KeyRecordListHelper *helper = new Database::KeyRecordListHelper(NULL, bufferLength, 0 ,(unsigned)key->GetLength(), (unsigned)record->GetLength()); bool isFirst = true; while(true) { if(isFirst) isFirst = false; else { helper->Reset(); rc = helper->Add(*key, *record); if (rc) fprintf(stderr,"Add failed: %d %s\n", rc, MIS::ERR::ToString(rc)); rc= pTable->GetNext(*helper); if (rc) fprintf(stderr,"Populating helper failed: %d %s\n", rc, MIS::ERR::ToString(rc)); rc = helper->GetFirst(*key, *record); } if (rc) { if (rc != 29) fprintf(stderr,"GetFirst failed: %d %s\n", rc, MIS::ERR::ToString(rc)); break; } else { while (true) { Database::IField *pIField; // Put the symbol name from this field in the IField object if(RETURN_CODE_SUCCESS == record->GetField(MIS::FID::SKU, pIField)) { // Export the symbol key field's data to a string for printing, and print it out std::string sprySymbol = trim__(pIField->ToString(true)); if (isValidSymbol(sprySymbol)) { StockInfo info; info.symbol = symbolFromSpryWare(sprySymbol); info.dollarVolume = // Typically LAST is from today, CLOSE is from yesterday, // and PREVIOUS_CLOSE is from the day before yesterday. max(getPricePrice(*record, MIS::FID::LAST), getPricePrice(*record, MIS::FID::CLOSE), getPricePrice(*record, MIS::FID::PREVIOUS_CLOSE)) * std::max(getFromUInt32(*record, MIS::FID::VOLUME), getFromUInt32(*record, MIS::FID::PREVIOUS_VOLUME)); info.closeDate = max(getPriceTime(*record, MIS::FID::LAST), getPriceTime(*record, MIS::FID::CLOSE), getPriceTime(*record, MIS::FID::PREVIOUS_CLOSE)); _stockInfo.push_back(info); assert(sprySymbol == symbolToSpryWare(info.symbol)); std::cout<<"Adding symbol: "<GetNext(*key, *record); if (rc) { if (rc != 29) fprintf(stderr,"GetNext failed: %d %s\n", rc, MIS::ERR::ToString(rc)); break; } } } } pDirectory->Disconnect(pTable); delete key; delete record; delete helper; } bool SpryWareSymbolsClient::isValidSymbol(std::string symbol) { if(symbol.empty()) return false; else if((symbol[0] != '$') && !(isalpha(symbol[0]) > 0)) return false; else if (symbol.find("..") != std::string::npos) return false; int count = 0; for (unsigned int i = 0; i < symbol.length(); i++) { if (symbol[i] == '.') count++; } if (count == 1) return false; return true; } void readFromSpryWare(std::vector< std::string > const &exchanges, StockInfoList &results) { results.clear(); SpryWareSymbolsClient client; client.setExchanges(exchanges); client.getSymbols(); results = client.getStockInfoList(); }