lib/LOWPASS.cpp

Go to the documentation of this file.
00001 /*
00002  *  Qtstalker stock charter
00003  *
00004  *  Copyright (C) 2001-2007 Stefan S. Stratigakos
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
00019  *  USA.
00020  */
00021 
00022 #include "LOWPASS.h"
00023 #include "PrefDialog.h"
00024 #include <qdict.h>
00025 #include <qobject.h>
00026 #include <math.h>
00027 
00028 #define MAXNUM 2147483647
00029 
00030 #if ! defined ( PI )
00031 #define PI 3.141592653589793
00032 #endif
00033 
00034 LOWPASS::LOWPASS ()
00035 {
00036   pluginName = "LOWPASS";
00037   helpFile = "lowpass.html";
00038 
00039   colorLabel = "color";
00040   lineTypeLabel = "lineType";
00041   labelLabel = "label";
00042   inputLabel = "input";
00043   pluginLabel = "plugin";
00044   freqLabel = "freq";
00045   widthLabel = "width";
00046 
00047   formatList.append(FormatInputArray);
00048   formatList.append(FormatDouble);
00049   formatList.append(FormatDouble);
00050     
00051   setDefaults();
00052 }
00053 
00054 LOWPASS::~LOWPASS ()
00055 {
00056 }
00057 
00058 void LOWPASS::setDefaults ()
00059 {
00060   color.setNamedColor("red");
00061   lineType = PlotLine::Line;
00062   label = pluginName;
00063   freq = 0.05;          // values between 0.0 and 0.5
00064   width = 0.2;          //values between 0.0001 and 0.2
00065   input = BarData::Close;
00066 }
00067 
00068 Indicator * LOWPASS::calculate ()
00069 {
00070   Indicator *output = new Indicator;
00071   output->setDateFlag(dateFlag);
00072   output->setLogScale(logScale);
00073 
00074   PlotLine *in = data->getInput(input);
00075   if (! in)
00076   {
00077     qDebug("LOWPASS::calculate: no input");
00078     return output;
00079   }
00080 
00081   PlotLine *line = getLowpass(in, freq, width);
00082   line->setColor(color);
00083   line->setType(lineType);
00084   line->setLabel(label);
00085   output->addLine(line);
00086   delete in;
00087   return output;
00088 }
00089 
00090 PlotLine * LOWPASS::getLowpass (PlotLine *in, double fre, double wid)
00091 {
00092   PlotLine *out = new PlotLine;
00093   
00094   if (in->getSize() == 0)
00095     return out;
00096     
00097 // ----------------------------------------------------------------------
00098   double slope = 0;       // will be modified on call to detrend
00099   double intercept = 0;
00100   int length = 0;      // original caller size
00101   int n = 0;          // size raised to next power of 2 for fft
00102   int i = 0;
00103 
00104   length = in->getSize();
00105 
00106   // Detrend input series
00107   PlotLine *series = detrend(in, slope, intercept, true);
00108 
00109   // Raise length to next power of 2, pad with zero
00110   PlotLine *series2 = raise2Power(series, 0);
00111 
00112   n = series2->getSize();
00113 
00114   //qtsFFT fft(n);        // construct fft object
00115   fft = new qtsFFT(n);
00116  
00117   // do fft
00118   PlotLine * fftFreq = fft->do_FFTqts(series2);
00119   //PlotLine * fftFreq = fft.do_FFTqts(series2);
00120 
00121   // apply low pass filter
00122   double f = 0; 
00123   double dist = 0; 
00124   double wt = 0;
00125   int halfn = n/2;
00126 
00127   double freqSave = fftFreq->getData(halfn);
00128 
00129   for (i = 0 ; i < halfn ; i++)
00130   {
00131     f = (double) i / (double) n ;  // Frequency
00132     if (f <= fre)                 // Flat response
00133       wt = 1.0 ;
00134     else
00135     {
00136       dist = (f - fre) / wid;
00137       wt = exp ( -dist * dist ) ;
00138     }
00139 
00140     fftFreq->setData(i, fftFreq->getData(i) * wt) ;
00141     fftFreq->setData(halfn + i, fftFreq->getData(halfn + i) * wt) ;
00142   }
00143 
00144   dist = (0.5 - fre) / wid;     // Do Nyquist in fftFreq[0]
00145   fftFreq->setData(halfn, freqSave * exp ( -dist * dist )) ;
00146 
00147   // Do inverse FFT to recover real domain
00148   PlotLine *fftReal = fft->do_iFFTqts(fftFreq);
00149   //PlotLine *fftReal = fft.do_iFFTqts(fftFreq);
00150 
00151   // Retrend input series, n.b. original length
00152   PlotLine *series3 = detrend(fftReal, slope, intercept, false);
00153 
00154   for (i = 0; i < length; i++)
00155     out->append(series3->getData(i));
00156 
00157   delete series;
00158   delete series2;
00159   delete series3;
00160   delete fftReal;
00161   delete fftFreq;
00162   delete fft;
00163   
00164   return out;
00165 }
00166 
00167 PlotLine * LOWPASS::detrend(PlotLine *x, double &slope, double &intercept, bool detrend)
00168 {
00169 // detrend = true (default) = detrebd x  , return slope/intercept
00170 // detrend = false = undo detrend using slope/intercept provided
00171 
00172   int length = x->getSize();
00173   int i = 0;
00174 
00175   PlotLine *result = new PlotLine;
00176 
00177   if (detrend)
00178   {
00179     intercept = x->getData(0) ;
00180     slope = (x->getData(length-1) - intercept) / (length-1) ;
00181 
00182     for(i = 0; i < length; i++)
00183       result->append(x->getData(i)  - intercept - slope * i) ;
00184   }
00185   else         //retrend
00186   {
00187     for(i = 0; i < length; i++)
00188       result->append(x->getData(i)   + intercept + slope * i ) ;
00189   }
00190 
00191   return result;
00192 }
00193 
00194 PlotLine * LOWPASS::raise2Power(PlotLine *x, double pad)
00195 {
00196 //   Raise the caller's n up to the next power of two
00197 //   pad remainder with pad, default = 0;
00198 
00199   PlotLine *result = new PlotLine;
00200 
00201   int length = x->getSize();
00202   int n = 0;
00203   int i = 0;
00204 
00205   for (n = 2 ; n < MAXNUM / 2 ; n *= 2)
00206   {
00207     if (n >= length)
00208       break ;
00209   }
00210 
00211   for (i = 0; i < n; i++)
00212   {
00213     if (i < length)
00214       result->append(x->getData(i));
00215     else
00216       result->append(pad);      // pad with zero
00217   }
00218 
00219   return result;
00220 }
00221 
00222 int LOWPASS::indicatorPrefDialog (QWidget *w)
00223 {
00224   QString pl = QObject::tr("Parms");
00225   QString cl = QObject::tr("Color");
00226   QString ll = QObject::tr("Label");
00227   QString ltl = QObject::tr("Line Type");
00228   QString fl = QObject::tr("Freq");
00229   QString wl = QObject::tr("Width");
00230   QString il = QObject::tr("Input");
00231   
00232   PrefDialog *dialog = new PrefDialog(w);
00233   dialog->setCaption(QObject::tr("LOWPASS Indicator"));
00234   dialog->createPage (pl);
00235   dialog->setHelpFile(helpFile);
00236 
00237   dialog->addColorItem(cl, pl, color);
00238   dialog->addTextItem(ll, pl, label);
00239   dialog->addComboItem(ltl, pl, lineTypes, lineType);
00240   dialog->addComboItem(il, pl, inputTypeList, input);
00241   dialog->addDoubleItem(fl, pl, freq, 0, 0.5);
00242   dialog->addDoubleItem(wl, pl, width, 0.0001, 0.2);
00243   
00244   int rc = dialog->exec();
00245   
00246   if (rc == QDialog::Accepted)
00247   {
00248     dialog->getColor(cl, color);
00249     lineType = (PlotLine::LineType) dialog->getComboIndex(ltl);
00250     dialog->getText(ll, label);
00251     input = (BarData::InputType) dialog->getComboIndex(il);
00252 
00253     freq = dialog->getDouble(fl);
00254     if (freq < 0.0)
00255       freq = 0.0;
00256     if (freq > 0.5)
00257       freq = 0.5;
00258 
00259     width = dialog->getDouble(wl);
00260     if (width < 0.0001)
00261       width = 0.0001;
00262     if (width > 0.2)
00263       width = 0.2;
00264 
00265     rc = TRUE;
00266   }
00267   else
00268     rc = FALSE;
00269   
00270   delete dialog;
00271   return rc;
00272 }
00273 
00274 PlotLine * LOWPASS::calculateCustom (QString &p, QPtrList<PlotLine> &d)
00275 {
00276   // format: DATA_ARRAY, FREQ, WIDTH
00277 
00278   if (checkFormat(p, d, 3, 3))
00279     return 0;
00280 
00281   double t = formatStringList[1].toDouble();
00282   if (t < 0.0)
00283     t = 0.0;
00284   if (t > 0.5)
00285     t = 0.5;
00286   freq = t;
00287 
00288   t = formatStringList[2].toDouble();
00289   if (t < 0.0001)
00290     t = 0.0001;
00291   if (t > 0.2)
00292     t = 0.2;
00293   width = t;
00294 
00295   PlotLine *pl = getLowpass(d.at(0), freq, width);
00296   return pl;
00297 }
00298 
00299 void LOWPASS::getIndicatorSettings (Setting &dict)
00300 {
00301   QString ts = color.name();
00302   dict.setData(colorLabel, ts);
00303   ts = QString::number(lineType);
00304   dict.setData(lineTypeLabel, ts);
00305   dict.setData(labelLabel, label);
00306   ts = QString::number(input);
00307   dict.setData(inputLabel, ts);
00308   dict.setData(pluginLabel, pluginName);
00309   ts = QString::number(freq);
00310   dict.setData(freqLabel, ts);
00311   ts = QString::number(width);
00312   dict.setData(widthLabel, ts);
00313 }
00314 
00315 void LOWPASS::setIndicatorSettings (Setting &dict)
00316 {
00317   setDefaults();
00318   
00319   if (! dict.count())
00320     return;
00321   
00322   QString s;
00323   dict.getData(colorLabel, s);
00324   if (s.length())
00325     color.setNamedColor(s);
00326     
00327   dict.getData(lineTypeLabel, s);
00328   if (s.length())
00329     lineType = (PlotLine::LineType) s.toInt();
00330 
00331   dict.getData(labelLabel, s);
00332   if (s.length())
00333     label = s;
00334       
00335   dict.getData(inputLabel, s);
00336   if (s.length())
00337     input = (BarData::InputType) s.toInt();
00338 
00339   dict.getData(freqLabel, s);
00340   if (s.length())
00341     freq = s.toFloat();
00342     
00343   dict.getData(widthLabel, s);
00344   if (s.length())
00345     width = s.toFloat();
00346 }
00347 
00348 void LOWPASS::formatDialog (QStringList &vl, QString &rv, QString &rs)
00349 {
00350   rs.truncate(0);
00351   rv.truncate(0);
00352   QString pl = QObject::tr("Parms");
00353   QString vnl = QObject::tr("Variable Name");
00354   QString fl = QObject::tr("Freq");
00355   QString wl = QObject::tr("Width");
00356   QString il = QObject::tr("Input");
00357   PrefDialog *dialog = new PrefDialog(0);
00358   dialog->setCaption(QObject::tr("LOWPASS Format"));
00359   dialog->createPage (pl);
00360   dialog->setHelpFile(helpFile);
00361 
00362   QString s;
00363   dialog->addTextItem(vnl, pl, s);
00364   dialog->addComboItem(il, pl, vl, input);
00365   dialog->addDoubleItem(fl, pl, freq, 0, 0.5);
00366   dialog->addDoubleItem(wl, pl, width, 0.0001, 0.2);
00367 
00368   int rc = dialog->exec();
00369   
00370   if (rc == QDialog::Accepted)
00371   {
00372     dialog->getText(vnl, rv);
00373 
00374     dialog->getCombo(il, s);
00375     rs.append(s + ",");
00376 
00377     double t = dialog->getDouble(fl);
00378     rs.append(QString::number(t) + ",");
00379 
00380     t = dialog->getDouble(wl);
00381     rs.append(QString::number(t));
00382   }
00383 
00384   delete dialog;
00385 }
00386