#include #include "../../shared/SimpleLogFile.h" #include "../misc_framework/GenericDataNodes.h" #include "../data_framework/TradeDirection.h" #include "../data_framework/SimpleMarketData.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/StandardCandles.h" #include "../data_framework/AverageHistoricalVolume.h" #include "TiqMisc.h" #include "ReportAlertsThread.h" #include "WestonDatabaseMonitor.h" ///////////////////////////////////////////////////////////////////// // sbird@iglide.net // Shane Bird // // His statement of requirements: // 1- multiples of average volume on a minute to minute basis. Example, GS has averaged 50k shares for the last three months on the one minute bar at 10:00. Today, GS traded 100k shares on the one minute bar at 10:00, at that moment GS's current real time volume multiple is 2 X it's average. I would like to start out with a minimum of 1.5 x real time volume average. // 2- Opening price must be above yesterdays low. // 3- Price must trade and close below yesterdays low on a 5 minute candlestick bar. // 4- If price trades back up to or through yesterdays low at any point during the day a signal is generated. // // We have some flexibility in #1. In particlar, I'm concerned that in the first // 10 seconds of the one minute candle we will never be 1.5x the normal average for // the end of the candle. The initial thought it to make it 1-2 minutes rather // than 0-1 minutes. // // 4 is always the trigger. // // These instructions are a little out of date. See comments sprinkled // throughout the code. // // [Alert Window 24] // subscription=ShaneBird-BackUp // title=Shane Bird // display=list // extra=Rel Vol // ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // ShaneBirdVolume // // For now we're doing the simple thing. We're looking at the 15 // minute volume and dividing that by 15. Ideally the overnight // application would give me something similar to the 15 minute // volume, but looking at one minute periods. // // This is like relative volume because we are looking at what's // normal for this time of day. And this is like the N-minute // volume because we are not looking at the entire day. If we // focused on longer time frames, this could be useful to other // people, possibly replacing our N-minute volume. // // This sends out a notification each time there is a print. That's // based heavily on the implemention. The expected use is to ignore // any notifications. ///////////////////////////////////////////////////////////////////// class ShaneBirdVolume : public GenericDataNode { private: GenericDataNode *_recentVolume; AverageHistoricalData *_expectedVolume; ShaneBirdVolume(DataNodeArgument args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; ShaneBirdVolume::ShaneBirdVolume(DataNodeArgument args) { std::string const &symbol = args.getStringValue(); addAutoLink(AverageHistoricalData::find(this, 0, _expectedVolume, symbol)); DataNodeArgument factory = GenericDataNodeFactory::findFactory("VolumeChange2"); factory = factory.replace(symbolPlaceholder, args); addAutoLink(factory.getValue< GenericDataNodeFactory >() .find(this, 0, _recentVolume)); } void ShaneBirdVolume::getDouble(bool &valid, double &value) const { Integer recentVolume; _recentVolume->getInteger(valid, recentVolume); if (!valid) return; const Integer normal15MinVolume = _expectedVolume->getVolume(AverageHistoricalData::higherTimeFrame(getSubmitTime())); if (normal15MinVolume <= 0) { // Avoid a /0 error. valid = false; return; } // For simplicity we'll assume that we are looking at 1.5 minutes worth of data // in recentVolume. This should be right on average. RelativeVolume goes one // step further than that and looks at the exact time. value = recentVolume * 10.0 / normal15MinVolume; valid = true; } ///////////////////////////////////////////////////////////////////// // ShaneBirdAlert ///////////////////////////////////////////////////////////////////// class ShaneBirdAlert : public DataNode { private: static double minVolumeRatio; static const time_t BLACK_OUT_PERIOD = (30 * MARKET_HOURS_MINUTE); static const Integer MIN_VOLUME_TODAY = 200000; static const double MIN_NET_DOWN_TODAY; GenericTosDataNode *_tosData; ShaneBirdVolume *_recentRelativeVolume; StandardCandles *_candleData; double _pointToWatch; double _previousClose; std::string _symbol; TradeDirection _direction; bool _primed; // rule 3 time_t _lastFired; // New rule 8/10/2010 enum {wCandle, wTos}; void onWakeup(int msgId); void onCandle(); void onTos(); void onBroadcast(BroadcastMessage &message, int msgId); ShaneBirdAlert(DataNodeArgument args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol, bool up); static DataNodeLink *find(); }; const double ShaneBirdAlert::MIN_NET_DOWN_TODAY = 0.10; double ShaneBirdAlert::minVolumeRatio = 1.5; void ShaneBirdAlert::onBroadcast(BroadcastMessage &message, int msgId) { DatabaseNotes const &m = dynamic_cast< DatabaseNotes & >(message); static const std::string MIN_RV = "Min RV"; const double newVolumeRatio = getProperty(m.values, MIN_RV, minVolumeRatio); if (newVolumeRatio != minVolumeRatio) { TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__ <getHistory(); if (candles.empty()) return; if (_direction.moreReportable(_pointToWatch, candles.rbegin()->close)) // if ((candles.rbegin())->close < _pointToWatch) -- This is the bullish case _primed = true; } void ShaneBirdAlert::onTos() { if (!_primed) return; if (!_tosData->getValid()) return; TosData const &last = _tosData->getLast(); if (last.open <= 0) return; if (!_direction.moreReportable(last.open, _pointToWatch)) // if (last.open <= _pointToWatch) -- This is the bullish case. return; if (_direction.moreReportable(_pointToWatch, last.price)) // if (last.price < _pointToWatch) -- This is the bullish case. return; bool valid; double relativeVolume; _recentRelativeVolume->getDouble(valid, relativeVolume); if (!(valid && (relativeVolume >= minVolumeRatio) && (_lastFired < getSubmitTime() - BLACK_OUT_PERIOD) && (last.volume >= MIN_VOLUME_TODAY) && _direction.downByAtLeast(last.price - _previousClose, MIN_NET_DOWN_TODAY))) { // Why didn't I see this alert? We don't expect this to happen very // often. Because we are clearing the primed flag, it can't happen that // often. TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__ <<"relativeVolume"; if (valid) msg<getValid() && _tosData->getLast().price) { info += "&last="; info += urlEncode(formatPrice(_tosData->getLast().price)); } if (_previousClose) { info += "&prev_close="; info += urlEncode(formatPrice(_previousClose)); } ReportAlertsThread::getInstance()->reportAlert(category, info); _lastFired = getSubmitTime(); } // Clear this if we should have reported, even if the signal was stopped by the blackout. // We don't want to report a second alert the instant the blackout ends. It has to be // a legitimate alert that would have happened even without our blackout logic. // Similar rules for the volume today filter. The requirement from the // customer was vague. But I'm saying that we have to meet that requirement // when we first cross the line. If not, we have to be primed again before // we signal any alerts. // Similar for the down on the day filter. It's as if this filter was a // normal TI Pro filter which has nothing to do with the alter except that // the end user put them together. // Similar for the volume ratio filter. If we crossed the line first, and // the volume ratio was not high enough, there will not be an alert until // the next time we get primed. The alert can only be triggered by the // price crossing. All the other filters have to be true first, we can't // wait for the volume ratio or another filter to become true to trigger. _primed = false; } ///////////////////////////////////////////////////////////////////// // Global ///////////////////////////////////////////////////////////////////// static std::vector< DataNodeLink * > cleanup; static void alertsForSymbol(std::string const &symbol) { TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__<<"ShaneBird"<