#include #include #include "../../shared/SimpleLogFile.h" #include "../misc_framework/Timers.h" #include "../misc_framework/CsvFileDataNodes.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/SimpleMarketData.h" #include "../data_framework/TradeDirection.h" #include "ReportAlertsThread.h" #include "TiqMisc.h" #include "KoyoteAlerts.h" /* 6 windows; Symbol list=all stocks>$2 and ADV>300k shares on AMEX, NYSE and NASDAQ - (no preferred); pre, regular and post market in same window Window 1 Name: Break High Description: New Intraday Highs Columns= Symbol, Last, $Change Window Type: Standard scroll up; most recent on top Window 2 Name: Break Low Description: New Intraday Lows Columns= Symbol, Last, $Change Window Type: Standard scroll up; most recent on top Window 3 Name: 52 Week High Description: New Intraday 52 Week Highs Columns= Symbol Window Type: Standard scroll up; most recent on top Window 4 Name: 52 Week Low Description: New Intraday 52 Week Lows Columns= Symbol Window Type: Standard scroll up; most recent on top */ ///////////////////////////////////////////////////////////////////// // KoyoteTopList // // Window 5 // Name: Change Points Up // Description: Top List of $Change for the Day - Gainers // Columns: Symbol, Last, $Change // Dynamic Sort Order: $Change (biggest $ gainers on top) // Window Type: Top List; Dynamic Updates // // Window 6 // Name: Change Points Down // Description: Top List of $Change for the Day - Losers // Columns: Symbol, Last, $Change // Dynamic Sort Order: $Change (biggest $ losers on top) // Window Type: Top List; Dynamic Updates // ///////////////////////////////////////////////////////////////////// /* fromBrad Williams toPhilip Smolen dateWed, Apr 6, 2011 at 2:49 PM subject1 simple TIQ window (and flip) hide details Apr 6 (5 days ago) Hi Phil, Then updated on skype 4/12/2011 I was hoping I wouldn’t need to ask but I do. Can I get 2 very simple TIQ windows for the Koyote TIQ environment? They are defined as follows: 1. UP VOLUME a. Stocks under $10 i. Min Up From Close = $0.50 ii. Min Day Range = $0.25 b. Stocks over $10 i. Min Up From Close = $1.00 ii. Min Day Range = $0.75 Aside from the a/b choice, it looks like this: http://www.trade-ideas.com/StockInfo/_TopListResult.html?form=1&show0=Price&show1=FCD&show2=PV&MinFCD=1&MinPV=150&MinPrice=10&MinTV=150000&SL=X1o5&sort=MaxPV&X_NYSE=on&X_ARCA=on&X_AMEX=on&XN=on&WN=Up+Volume+%28stocks%3e%2410%2c+down+from+close%3e%241%2cmin+day+range%3e%240.75%2ccur+vol%3e150k%2ccur+vol%3e150%25adv%29 2. DOWN VOLUME flipped, the standard way. http://www.trade-ideas.com/StockInfo/_TopListResult.html?form=1&show0=Price&show1=FCD&show2=PV&MinPV=150&MinPrice=10&MinTV=150000&MaxFCD=-1&SL=X1o5&sort=MaxPV&X_NYSE=on&X_ARCA=on&X_AMEX=on&XN=on&WN=Down+Volume+%28stocks%3e%2410%2c+down+from+close%3e%241%2cmin+day+range%3e%240.75%2ccur+vol%3e150k%2ccur+vol%3e150%25adv%29 */ class KoyoteTopList : DataNode { private: struct ByIndexItem { double previousClose; GenericTosDataNode *tos; }; typedef std::vector< ByIndexItem > ByIndex; ByIndex _byIndex; struct ByMoveItem { double upForTheDay; int index; bool operator <(ByMoveItem const &other) const; bool operator ==(ByMoveItem const &other) const; }; TimerThread::Helper _timer; KoyoteTopList(DataNodeArgument const &args); friend class DataNode; protected: void onBroadcast(BroadcastMessage &message, int msgId); public: static DataNodeLink *find(std::string const &fileName) { KoyoteTopList *node; return findHelper(NULL, 0, node, fileName); } }; bool KoyoteTopList::ByMoveItem::operator <(ByMoveItem const &other) const { if (upForTheDay < other.upForTheDay) return true; return index < other.index; } bool KoyoteTopList::ByMoveItem::operator ==(ByMoveItem const &other) const { return (upForTheDay == other.upForTheDay) && (index == other.index); } KoyoteTopList::KoyoteTopList(DataNodeArgument const &args) : _timer(getOwnerChannel(), &getManager()->getTimerThread()) { std::string const &fileName = args.getStringValue(); TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__< 0.0) { GenericTosDataNode::find(item.tos, symbol); _byIndex.push_back(item); } } } registerForBroadcast(_timer, 0); getManager()->getTimerThread().requestPeriodicBroadcast(_timer, 5000); } void KoyoteTopList::onBroadcast(BroadcastMessage &message, int msgId) { // Timer! typedef std::set< ByMoveItem > ByMove; ByMove highest; ByMove lowest; int index = 0; for (ByIndex::const_iterator it = _byIndex.begin(); it != _byIndex.end(); it++, index++) { ByIndexItem const &source = *it; if (source.tos->getValid()) { const double price = source.tos->getLast().price; if (price > 0) { ByMoveItem destination; destination.upForTheDay = price - source.previousClose; destination.index = index; highest.insert(destination); lowest.insert(destination); if (highest.size() > 40) { highest.erase(highest.begin()); lowest.erase(--lowest.end()); } } } } static const std::string upCategory = "KOYOTE-5"; index = 0; std::string out; out = "w_id=105"; for (ByMove::const_reverse_iterator it = highest.rbegin(); it != highest.rend(); it++, index++) { const std::string indexStr = ntoa(index); ByMoveItem const &byMoveItem = *it; ByIndexItem const &byIndexItem = _byIndex[byMoveItem.index]; out +="&symbol"; out += indexStr; out += '='; out += byIndexItem.tos->symbol(); out += "&last"; out += indexStr; out += '='; out += formatPrice(byIndexItem.tos->getLast().price); out += "&change"; out += indexStr; out += '='; out += formatPrice(byMoveItem.upForTheDay); } ReportAlertsThread::getInstance()->reportAlert(upCategory, out); static const std::string downCategory = "KOYOTE-6"; index = 0; out = "w_id=106"; for (ByMove::const_iterator it = lowest.begin(); it != lowest.end(); it++, index++) { const std::string indexStr = ntoa(index); ByMoveItem const &byMoveItem = *it; ByIndexItem const &byIndexItem = _byIndex[byMoveItem.index]; out +="&symbol"; out += indexStr; out += '='; out += byIndexItem.tos->symbol(); out += "&last"; out += indexStr; out += '='; out += formatPrice(byIndexItem.tos->getLast().price); out += "&change"; out += indexStr; out += '='; out += formatPrice(byMoveItem.upForTheDay); } ReportAlertsThread::getInstance()->reportAlert(downCategory, out); } class KoyoteVolumeTopList : DataNode { private: struct ByIndexItem { double previousClose; double averageDailyVolume; GenericTosDataNode *tos; }; typedef std::vector< ByIndexItem > ByIndex; ByIndex _byIndex; struct Sortable { double percentVol; int index; bool operator <(Sortable const &other) const; bool operator ==(Sortable const &other) const; }; typedef std::set< Sortable > Sorted; void dumpList(std::string const &category, Sorted const &list); TimerThread::Helper _timer; KoyoteVolumeTopList(DataNodeArgument const &args); friend class DataNode; protected: void onBroadcast(BroadcastMessage &message, int msgId); public: static DataNodeLink *find(std::string const &fileName) { KoyoteVolumeTopList *node; return findHelper(NULL, 0, node, fileName); } }; bool KoyoteVolumeTopList::Sortable::operator <(Sortable const &other) const { if (percentVol < other.percentVol) return true; return index < other.index; } bool KoyoteVolumeTopList::Sortable::operator ==(Sortable const &other) const { return index == other.index; } KoyoteVolumeTopList::KoyoteVolumeTopList(DataNodeArgument const &args) : _timer(getOwnerChannel(), &getManager()->getTimerThread()) { std::string const &fileName = args.getStringValue(); TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__< 0.0) && (item.averageDailyVolume > 0.0)) { addAutoLink(GenericTosDataNode::find(item.tos, symbol)); _byIndex.push_back(item); } } } registerForBroadcast(_timer, 0); getManager()->getTimerThread().requestPeriodicBroadcast(_timer, 5000); } void KoyoteVolumeTopList::onBroadcast(BroadcastMessage &message, int msgId) { // Timer! Sorted upList; Sorted downList; int index = 0; for (ByIndex::const_iterator it = _byIndex.begin(); it != _byIndex.end(); it++, index++) { ByIndexItem const &source = *it; if (source.tos->getValid()) { if (!source.tos->getValid()) continue; TosData const &last = source.tos->getLast(); if ((last.price == 0) || (last.high == 0) || (last.low == 0)) continue; const Integer volumeToday = last.volume; if (volumeToday < 150000) continue; const double rangeForTheDay = last.high - last.low; double minUpFromClose; if (last.price < 10.0) { if (rangeForTheDay < 0.25) continue; minUpFromClose = 0.5; } else { if (rangeForTheDay < 0.75) continue; minUpFromClose= 1.0; } Sortable destination; destination.percentVol = volumeToday / source.averageDailyVolume * 100.0; if (destination.percentVol < 150) continue; destination.index = index; const double upFromClose = last.price - source.previousClose; if (upFromClose > minUpFromClose) upList.insert(destination); else if (upFromClose < - minUpFromClose) downList.insert(destination); } } static const std::string upCategory = "KOYOTE-7"; dumpList(upCategory, upList); static const std::string downCategory = "KOYOTE-8"; dumpList(downCategory, downList); } void KoyoteVolumeTopList::dumpList(std::string const &category, Sorted const &list) { int index = 0; std::string out; for (Sorted::const_reverse_iterator it = list.rbegin(); (it != list.rend()) && (index < 40); it++, index++) { const std::string indexStr = ntoa(index); Sortable const &sorted = *it; ByIndexItem const &byIndexItem = _byIndex[sorted.index]; out +="&symbol"; out += indexStr; out += '='; out += byIndexItem.tos->symbol(); out += "&last"; out += indexStr; out += '='; out += formatPrice(byIndexItem.tos->getLast().price); out += "&change"; out += indexStr; out += '='; out += formatPrice(byIndexItem.tos->getLast().price - byIndexItem.previousClose); out += "&percent_vol"; out += indexStr; out += '='; out += formatPrice(sorted.percentVol); } ReportAlertsThread::getInstance()->reportAlert(category, out); } ///////////////////////////////////////////////////////////////////// // KoyoteHighLow // // This reports new highs and lows to one pair of windows, and new // 52 week highs and lows to an additional pair of windows. ///////////////////////////////////////////////////////////////////// class KoyoteHighLow : public DataNode { private: TradeDirection _direction; double _52week, _previousClose; GenericTosDataNode *_tosData; bool _marketHasOpened; bool marketHasOpened() const { return _tosData->getLast().open; } double _lastReported; void resetLastReported(); bool primed() { return _lastReported != _direction.leastReportable(); } void onWakeup(int msgId); KoyoteHighLow(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol, bool up) { KoyoteHighLow *node; return findHelper(NULL, 0, node, argList(symbol, up)); } }; void KoyoteHighLow::resetLastReported() { _lastReported = 0.0; if (_tosData->getValid() && marketHasOpened()) { // This part is only necessary if we restart the software during the // trading day. if (_direction.isLong()) _lastReported = _tosData->getLast().high; else _lastReported = _tosData->getLast().low; } if (!_lastReported) _lastReported = _direction.leastReportable(); } KoyoteHighLow::KoyoteHighLow(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, Long std::string const &symbol = argList[0].getStringValue(); _direction = argList[1].getBooleanValue(); addAutoLink(GenericTosDataNode::find(this, 0, _tosData, symbol)); _marketHasOpened = false; resetLastReported(); const std::string fieldName52 = _direction.isLong()?"52 Week High":"52 Week Low"; _52week = strtodDefault(FileOwnerDataNode::getStringValue ("F_OvernightData.csv", fieldName52, symbol), // If we don't know the 52 week high/low, then don't report // this alert at all. _direction.mostReportable()); _previousClose = getPreviousClose(symbol); TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__ <getValid()) return; if (_marketHasOpened != marketHasOpened()) { resetLastReported(); _marketHasOpened = marketHasOpened(); } if (!_tosData->getLast().newPrint) return; const double price = _tosData->getLast().price; if (_direction.moreReportable(price, _lastReported)) { const bool wasPrimed = primed(); _lastReported = price; if (wasPrimed) { std::string msg = "symbol="; msg += urlEncode(_tosData->symbol()); msg += "&last="; msg += formatPrice(price); msg += "&change="; msg += formatPrice(price - _previousClose); msg += "&time="; msg += formatTime(time(NULL)); std::string category; if (_direction.isLong()) // Break High category = "KOYOTE-1"; else // Break Low category = "KOYOTE-2"; ReportAlertsThread::getInstance()->reportAlert(category, msg); if (_direction.moreReportable(price, _52week)) { if (_direction.isLong()) // 52 Week High category = "KOYOTE-3"; else // 52 Week Low category = "KOYOTE-4"; ReportAlertsThread::getInstance()->reportAlert(category, msg); } } } } // F_OvernightData.csv:,"Company Name","Listed Exchange","Lifetime High","Lifetime Low","52 Week High","52 Week Low","Previous High","Previous Low","Previous Close","Previous Open","Correlation R2","F Correlation R2","Consolidation Days","Consolidation Top","Consolidation Bottom","Average True Range","Bunny 130","Previous Volume","Prev Put Volume","Prev Call Volume",Dividend,Earnings,"P/E Ratio",Beta,Yield,"Market Cap","Shares Outstanding","EPS Net Income",Income,Revenues,Assets,Debt,"14 Day RSI" ///////////////////////////////////////////////////////////////////// // Global ///////////////////////////////////////////////////////////////////// static std::vector< DataNodeLink * > cleanup; static void alertsForSymbol(std::string const &symbol) { TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__<<"koyote"<