#ifndef __LinearRegression_h_ #define __LinearRegression_h_ #include "StandardCandles.h" #include "GenericTosData.h" /* This computes the linear regression channel for a stock based on the closing * price of historical candles. Like a typical desktop application we use the * last print as the last data point, without waiting for the candle to end. * As typically used in stock analytics, we compute two values at the same * time. One is the point where the stock should be at this time, if it was * right on the linear regression line. The other is the standard deviation of * the recent points. The channel is typically defined as being n standard * deviations above the regression line, and n standard deviations below the * line. */ class LinearRegressionBase : public DataNode { public: class Accumulator { private: double _sx, _sxx, _sy, _syy, _sxy; // Sums. int _count; public: Accumulator(); void addPoint(double x, double y); void removePoint(double x, double y); double getStdDev() const; void getMAndB(double &m, double &b) const; double getEstimated(double x) const; int getCount() const { return _count; } bool empty() const { return _count == 0; } void reset(); }; private: StandardCandles *_candleData; GenericTosDataNode *_tosData; int _bars; StandardCandles::Epoch _lastEpoch; // This includes the most recent N-1 complete bars. To see the current // value, add the point in progress from the TOS. To see the value for the // last complete bar, add the oldest point that we should be looking at. Accumulator _accumulator; enum { wTos, wCandle }; void onWakeup(int msgId); void onTos(); void onCandle(); void makeCurrent(); static void get(Accumulator const &accumulator, bool &valid, double &mlr, double &stdDev, int lastX); static void get(Accumulator const &accumulator, bool &valid, double &mlr, int lastX); Accumulator fillAccumulator(bool live); LinearRegressionBase(DataNodeArgument const &args); friend class DataNode; public: // We should be valid any time we have enough data points. We check for // other problems, just to be Safe. // // mlr is Moving Linear regression, or where the regression line suggests we // should be right now. // // We pack the stdDev with the mlr because we know how this data is normally // used. Also to avoid duplicated effort in the valid processing. // // If live is true, we look at the candle in progress. Otherwise we stop // with the last complete candle. In either case we still use the same // number of candles to compute the formula. void get(bool live, bool &valid, double &mlr, double &stdDev); void get(bool live, bool &valid, double &mlr); static DataNodeLink *find(DataNodeListener *listener, int msgId, LinearRegressionBase *&node, std::string const &symbol, int minutesPerBar, int bars, bool strict = false); static DataNodeLink *find(LinearRegressionBase *&node, std::string const &symbol, int minutesPerBar, int bars, bool strict = false); // This gives you a generic data node factory which you can use to access // just the standard deviation. That data node will, of course, find a // LinearRegressionBase to do the bulk of the work. But it will conform to // the generic data node interface and provide the answer via getDouble(). // Live is false by default. We expect to use this with the SMA to implement // a bollinger band filter. SMAs filters are currently not live; they only // update at the end of each candle. So these will be consistent. static DataNodeArgument stdDevFactory(int minutesPerBar, int bars, bool live = false, bool strict = false); }; #endif