/** * Copyright Mikael Högdahl - triyana@users.sourceforge.net * * This source is distributed under the terms of the Q Public License version 1.0, * created by Trolltech (www.trolltech.com). */ #include #include "MHPrice.h" #include "MHDate.h" const double MHPrice::MIN_VALUE = -999999999999.0; const double MHPrice::MAX_VALUE = 999999999999.0; const double MHPrice::ZERO_VALUE = 0.000000000001; /** * Copy constructor * @param const MHPrice& - Point to copy from */ MHPrice::MHPrice (const MHPrice& x) : MH() { aHigh = x.aHigh; aLow = x.aLow; aClose = x.aClose; aOpen = x.aOpen; aVol = x.aVol; strncpy (aDate, x.aDate, 8); aDate[8] = '\0'; } /** * Create new data point * @param const char* - Date value in the form of YYYYMMDD * @param double - Data point */ MHPrice::MHPrice (const char* d, double h) : MH() { aHigh = h; aLow = h; aClose = h; aOpen = h; aVol = h; strncpy (aDate, d, 8); aDate[8] = '\0'; } /** * Create new numerical data point * @param double - X point * @param double - Y point */ MHPrice::MHPrice (double h, double c) : MH() { aHigh = h; aLow = 0.0; aClose = c; aOpen = 0.0; aVol = 0.0; aDate[0] = '\0'; } /** * Create new data point * @param const char* - Date value in the form of YYYYMMDD * @param double - Highest value in this data point * @param double - Lowest value in this data point * @param double - Last value in this data point * @param double - Open value in this data point * @param double - Volume value in this data point */ MHPrice::MHPrice (const char* d, double h, double l, double c, double o, double v) : MH() { aHigh = h; aLow = l; aClose = c; aOpen = o; aVol = v; strncpy (aDate, d, 8); aDate[8] = '\0'; } /** * Operator = * @param const MHPrice& - Right side of expression */ MHPrice& MHPrice::operator=(const MHPrice& x) { strncpy (aDate, x.aDate, 8); aDate[8] = '\0'; aHigh = x.aHigh; aLow = x.aLow; aClose = x.aClose; aOpen = x.aOpen; aVol = x.aVol; return *this; } /** * Make a vector with the ATR model (Average True Range) * @param MHVector* - Vector with MHPrices * @param MHVector* - The vector with converted data * @param int - Number of points in range */ void MHPrice::ATR (MHVector* in, MHVector* out, int nMvg) { int nSize = in->Size(); double nGetr = 0; double nTot = 0; double nPrevC = 0; double nPrevGetr = 0; MHPrice* p = 0; if (nMvg < 2 || nMvg > in->Size()) return; nMvg--; // Iterate through all data points and calc ATR data for (int f = 0; f < nSize; f++) { p = (MHPrice*)(*in)[f]; if (f == 0) { nGetr = p->aHigh - p->aLow; nTot += nGetr; } else { double t1 = p->aHigh - p->aLow; double t2 = fabs(p->aHigh - nPrevC); double t3 = fabs(p->aLow - nPrevC); if (t1 > t2 && t1 > t3) nGetr = t1; else if (t2 > t1 && t2 > t3) nGetr = t2; else nGetr = t3; nTot += nGetr; if (f == nMvg) { nPrevGetr = nTot / (nMvg + 1); out->Push (new MHPrice (p->aDate, nPrevGetr)); } else if (f > nMvg) { nPrevGetr = ((nPrevGetr * nMvg) + nGetr) / (nMvg + 1); out->Push (new MHPrice (p->aDate, nPrevGetr)); } } nPrevC = p->aClose; } } /** * Adjust price and volume * If volume is less than 0 then the volume is divided with the price factor */ void MHPrice::Adjust (double price, double volume) { if (price > 0.0000001) { aHigh *= price; aLow *= price; aClose *= price; aOpen *= price; } if (volume > 0.0000001) aVol *= volume; else aVol /= price; } /** * Make a vector with the Bollinger model * @param MHVector* - Vector with MHPrices * @param MHVector* - The vector with converted data * @param MHVector* - The vector with converted data + 2 * std. dev. * @param MHVector* - The vector with converted data - 2 * std. dev. * @param int - Number of points to use in moving average */ void MHPrice::Bollinger (MHVector* in, MHVector* mvg, MHVector* upper, MHVector* lower, int nMvg) { MHVector stddev; MHPrice* ps = 0; MHPrice* pm = 0; MHPrice::MovingAverage (in, mvg, nMvg); MHPrice::StdDev (in, &stddev, nMvg); // Create two new dataseries with mvg +/- standard deviations * 2 for (int i = 0; i < stddev.Size(); i++) { ps = (MHPrice*)stddev[i]; pm = (MHPrice*)mvg->Get (i); if (pm) { upper->Push (new MHPrice (ps->aDate, pm->aClose + (ps->aClose) * 2)); lower->Push (new MHPrice (ps->aDate, pm->aClose - (ps->aClose) * 2)); } } stddev.Erase(); } /** * Compare two price objects or one price with a string object * @param MH* - THe other object * @param int - Sort type, not used * @return int - 1, 0, -1 */ int MHPrice::Compare (const MH* o, int type) { if (*(((MH*)o)->Class() + 2) == 'S') return compare((MHString*)o); else return compare((MHPrice*)o); } /** * Copy points to new vector, create new points * @param MHVector* - In serie * @param MHVector* - Out serie * @param const char* - Start date, if 0 then all data is copied * @param const char* - Stop date, if 0 then all data is copied */ void MHPrice::Copy (MHVector* in, MHVector* out, const char* pStartDate, const char* pStopDate) { MHPrice* p; for (int f = 0; f < in->Size(); f++) { p = (MHPrice*)(*in)[f]; if (pStartDate == 0 || pStopDate == 0) out->Push (new MHPrice (*p)); else if (*p >= pStartDate && *p <= pStopDate) out->Push (new MHPrice (*p)); } } /** * Copy pointers to points to new vector * @param MHVector* - In serie * @param MHVector* - Out serie */ void MHPrice::CopyPointers (MHVector* in, MHVector* out) { for (int f = 0; f < in->Size(); f++) out->Push ((MHPrice*)(*in)[f]); } /** * Fix slave list so it's range is the same as master * @param MHVector* - In serie * @param MHVector* - Out serie */ void MHPrice::Cut (MHVector* in, MHVector* out) { MHPrice* p1 = (MHPrice*)in->First(); MHPrice* p2 = (MHPrice*)in->Last(); MHPrice* p; MHVector v; if (!p1 || !p2) return; for (int f = 0; f < out->Size(); f++) { p = (MHPrice*) (*out)[f]; if (*p >= *p1 && *p <= *p2) { // First date must be the same if (v.Size() == 0 && *p != *p1) v.Push (new MHPrice(*p1)); v.Push (new MHPrice(*p)); } } // Last date must be the same p = (MHPrice*) v.Last(); if (p && *p != *p2) v.Push (new MHPrice(*p2)); out->Erase(); MHPrice::CopyPointers (&v, out); v.Empty(); } /** * Convert day price data to weekly data * @param MHVector* - In serie * @param MHVector* - Out serie * @param int - One of MHDate::MONDAY - MHDate::SUNDAY */ void MHPrice::Day2Week (MHVector* in, MHVector* out, int nWeekDay) { bool Init = false; MHString StopStr, StartStr; MHPrice* p; MHPrice p2; MHDate StopDate; for (int f = 0; f < in->Size(); f++) { p = (MHPrice*) (*in)[f]; if (Init == false) { // Initiate first date. // Move stopdate to right day in week MHDate tmp; tmp.Set (p->aDate); tmp.GotoWeekday (nWeekDay); tmp.Get (MHDate::LONG_DATE, StopStr); StopDate = tmp; // Then move start date back one week tmp.AddDays (-7); tmp.Get (MHDate::LONG_DATE, StartStr); p2 = *p; Init = true; } if (*p > StopStr()) { // Current point is out of range so save old point to out vector p2.SetDate (StopStr()); out->Push (new MHPrice(p2)); // Set current point as last point p2 = *p; } else if (*p > StartStr() && *p <= StopStr()) { // Save last point for every time serie range if (p2 == "") p2 = *p; else { if (p->aHigh > p2.aHigh) p2.aHigh = p->aHigh; if (p->aLow < p2.aLow) p2.aLow = p->aLow; p2.aVol += p->aVol; p2.aClose = p->aClose; } } // Set new range if point is outside current range while (*p > StopStr()) { StartStr = StopStr; StopDate.AddDays (7); StopDate.Get (MHDate::LONG_DATE, StopStr); } } if (StopStr != MHString("")) { p2.SetDate (StopStr()); out->Push (new MHPrice(p2)); } } /** * Create vector with the difference of two vectors * @param MHVector* - Vector 1 * @param MHVector* - Vector 2 * @param MHVector* - Out vector * @param MHVector* - Out vector with only 0.0 values in it, default 0 */ void MHPrice::Diff (MHVector* in1, MHVector* in2, MHVector* out, MHVector* out2) { MHPrice* p1 = 0; MHPrice* p2 = 0; for (int f = 0; f < in1->Size(); f++) { p1 = (MHPrice*) in1->Get(f); p2 = (MHPrice*) in2->Find (p1, MHVector::FIND_EXACT); if (p1 && p2) { out->Push (new MHPrice (p1->aDate, p1->aClose - p2->aClose, 0.0, p1->aClose - p2->aClose, 0.0, 0.0)); out2->Push (new MHPrice (p1->aDate, 0.0, 0.0, 0.0, 0.0, 0.0)); } } } /** * Make a vector with the exponential moving average of price data * @param MHVector* - In serie * @param MHVector* - Out serie * @param int - Number of points in moving average */ void MHPrice::ExponentialMovingAverage (MHVector* in, MHVector* out, int nMvg) { double sma = 0.0; double prev = 0.0; double multi = 2.0 / double(nMvg + 1.0); MHPrice* p; if (nMvg > 1 && nMvg < in->Size()) { // Iterate through all data points for (int f = 0; f < in->Size(); f++) { p = (MHPrice*) (*in)[f]; if (f < (nMvg - 1)) { sma += p->aClose; } else if (f == (nMvg - 1)) { sma += p->aClose; prev = sma / nMvg; out->Push (new MHPrice (p->aDate, prev)); } else { prev = ((p->aClose - prev) * multi) + prev; out->Push (new MHPrice (p->aDate, prev)); } } } } /** * Make a vector with the exponential moving average of price data * @param MHVector* - In serie * @param MHVector* - MACD out serie * @param MHVector* - EMA MACD out serie * @param int - Number of points in short ema * @param int - Number of points in long ema * @param int - Number of points in ema of macd */ void MHPrice::MACD (MHVector* in, MHVector* macd1, MHVector* macd2, int emaShort, int emaLong, int emaMacd) { MHPrice* p1 = 0; MHPrice* p2 = 0; MHVector macdShort; MHVector macdLong; int emaDiff = emaLong - emaShort; if (emaDiff <= 0) return; ExponentialMovingAverage (in, &macdShort, emaShort); ExponentialMovingAverage (in, &macdLong, emaLong); for (int f = 0; f < macdLong.Size(); f++) { p1 = (MHPrice*) macdLong.Get(f); p2 = (MHPrice*) macdShort.Get(f + emaDiff); if (p1 && p2) { macd1->Push (new MHPrice (p1->aDate, p2->aClose - p1->aClose)); } } ExponentialMovingAverage (macd1, macd2, emaMacd); macdShort.Erase (); macdLong.Erase (); } /** * Make a vector with the Momentum model * @param MHVector* - Vector with MHPrices * @param MHVector* - The vector with converted data * @param MHVector* - The vector with zero line * @param int - Number of days back in time */ void MHPrice::Momentum (MHVector* in, MHVector* out, MHVector* zero, int nMvg) { int nSize = in->Size(); int nStart = nMvg - 1; MHPrice* p; MHPrice* p2; if (nMvg < 2 && nMvg > nSize) return; // Iterate through all data points and calc RSI data for (int f = 1; f < nSize; f++) { if (f >= nStart) { p = (MHPrice*) (*in)[f]; p2 = (MHPrice*) (*in)[f - nStart]; out->Push (new MHPrice (p->aDate, p->aClose - p2->aClose)); zero->Push (new MHPrice (p->aDate, 0)); } } } /** * Make a vector with moving average of price data * @param MHVector* - In serie * @param MHVector* - Out serie * @param int - Number of points in moving average */ void MHPrice::MovingAverage (MHVector* in, MHVector* out, int nMvg) { int nCount = 0; double nSum = 0; double* v = new double [in->Size()]; MHPrice* p; if (nMvg > 1 && nMvg < in->Size()) { // Iterate through all data points for (int f = 0; f < in->Size(); f++) { p = (MHPrice*) (*in)[f]; nCount++; if (nCount < nMvg) { // Add data until the first moving average price can be calculated v[nCount - 1] = p->aClose; nSum += p->aClose; } else if (nCount == nMvg) { // This is the first point v[nCount - 1] = p->aClose; nSum += p->aClose; out->Push (new MHPrice (p->aDate, nSum / nMvg)); } else { // And here comes the rest v[nCount - 1] = p->aClose; // Remove oldest data in range and add current to sum nSum -= v[nCount - (nMvg + 1)]; nSum += p->aClose; // save new point, (point 2 and forward) out->Push (new MHPrice (p->aDate, nSum / nMvg)); } } } delete []v; } /** * Make a vector with all buy/sell signals * @param MHVector* - Short moving average * @param MHVector* - Long moving average * @param MHVector* - Out serie */ void MHPrice::MovingAverageSignals (MHVector* in_short, MHVector* in_long, MHVector* out) { MHPrice* priceShort; MHPrice* priceLong; int signal = 0; for (int f = 0; f < in_short->Size(); f++) { priceShort = (MHPrice*) (*in_short)[f]; priceLong = (MHPrice*) in_long->Find (priceShort, MHVector::FIND_EXACT, 0, 0); if (priceShort && priceLong) { if (signal == 0) { // Initiate first signal, next time signal change start to store them in out vector if (priceShort->aClose < priceLong->aClose) signal = SELL; else if (priceShort->aClose > priceLong->aClose) signal = BUY; } else { if (signal == BUY && priceShort->aClose < priceLong->aClose) { signal = SELL; out->Push (new MHPrice (priceShort->aDate, priceShort->aClose)); } else if (signal == SELL && priceShort->aClose > priceLong->aClose) { signal = BUY; out->Push (new MHPrice (priceShort->aDate, priceShort->aClose)); } } } } } /** * Create a list with MHPrices that are normalised with * the first data point as the reference point * @param MHVector* - Vector with MHPrices * @param MHVector* - The vector with converted data * @param const double& - Start number */ void MHPrice::Normalise (MHVector* in, MHVector* out, double nOrigo) { bool bFirst = true; double nFactor = 0; MHPrice* p; for (int f = 0; f < in->Size(); f++) { p = (MHPrice*) (*in)[f]; if (p->aClose < 0) continue; if (bFirst == true) { nFactor = p->aClose / nOrigo; bFirst = false; } out->Push(new MHPrice ( p->aDate, p->aHigh / nFactor, p->aLow / nFactor, p->aClose / nFactor, p->aOpen / nFactor, p->aVol / nFactor)); } } /** * Print to outstream all data points for a vector * @param MHVector* - In serie */ void MHPrice::Print (MHVector* in) { for (int f = 0; f < in->Size(); f++) ;//cout << *((MHPrice*)(*in)[f]) << endl; } /** * Make a vector with the Relative Strength Index * @param MHVector* - Vector with MHPrices * @param MHVector* - The vector with converted data * @param int - Number of points to use */ void MHPrice::RSI (MHVector* in, MHVector* out, int nMvg) { double nAvgGain = 0; double nAvgLoss = 0; double nDiff; MHPrice* p; MHPrice* pp; if (nMvg < 2 || nMvg > in->Size()) return; // Iterate through all data points and calc RSI data for (int f = 1; f < in->Size(); f++) { p = (MHPrice*) (*in)[f]; pp = (MHPrice*) (*in)[f - 1]; nDiff = p->aClose - pp->aClose; if (f <= nMvg) { if (nDiff > 0) nAvgGain += nDiff; else nAvgLoss += fabs(nDiff); } if (f == nMvg) { nAvgGain = nAvgGain / nMvg; nAvgLoss = nAvgLoss / nMvg; out->Push (new MHPrice (p->aDate, 100 - (100 / (1 + (nAvgGain / nAvgLoss))))); } else if (f > nMvg) { nAvgGain = ((nAvgGain * (nMvg - 1)) + ((nDiff > 0) ? fabs(nDiff) : 0)) / nMvg; nAvgLoss = ((nAvgLoss * (nMvg - 1)) + ((nDiff < 0) ? fabs(nDiff) : 0)) / nMvg; out->Push (new MHPrice (p->aDate, 100 - (100 / (1 + (nAvgGain / nAvgLoss))))); } } } /** * Make a vector with all buy/sell signals for rsi moel * @param MHVector* - In serie * @param MHVector* - Out serie * @param double - Low value * @param double - High value */ void MHPrice::RSISignals (MHVector* in, MHVector* out, double low, double high) { MHPrice* price; double last = 0.0; int signal = NEUTRAL; int signal2 = NEUTRAL; for (int f = 0; f < in->Size(); f++) { price = (MHPrice*) (*in)[f]; if (signal == NEUTRAL && price->aClose >= high) { signal = SELL; } else if (signal == SELL && signal2 == NEUTRAL && price->aClose < last) { out->Push (new MHPrice (price->aDate, -1, -1, price->aClose, SELL, -1)); signal2 = SELL; } else if (signal == SELL && signal2 == SELL && price->aClose < high) { signal = signal2 = NEUTRAL; } else if (signal == NEUTRAL && price->aClose <= low) { signal = BUY; } else if (signal == BUY && signal2 == NEUTRAL && price->aClose > last) { out->Push (new MHPrice (price->aDate, -1, -1, price->aClose, BUY, -1)); signal2 = BUY; } else if (signal == BUY && signal2 == BUY && price->aClose > low) { signal = signal2 = NEUTRAL; } last = price->aClose; } } /** * Save points to file * @param const char* - The filename * @param MHVector* - In serie * @return bool - true if ok */ bool MHPrice::Save (const char* pFilename, MHVector* in) { FILE* file = fopen (pFilename, "wb"); char buffer[201]; MHPrice* p; if (file) { for (int f = 0; f < in->Size(); f++) { p = (MHPrice*)(*in)[f]; snprintf (buffer, 200, "%s\t%f\t%f\t%f\t%f\t%f\n", p->aDate, p->aHigh, p->aLow, p->aClose, p->aOpen, p->aVol); fwrite (buffer, sizeof(char), strlen(buffer), file); } fclose (file); return true; } return false; } /** * Make a vector with data converted to standar deviation * @param MHVector* - In serie * @param MHVector* - Out serie * @param int - Number of points for average */ void MHPrice::StdDev (MHVector* in, MHVector* out, int nDays) { int nCount = 0; double nMean = 0; double nDev = 0; double nDev2 = 0; double nSum = 0; MHPrice* p; if (nDays < 2 || nDays > in->Size()) return; for (int f = 0; f < in->Size(); f++) { p = (MHPrice*)(*in)[f]; nCount++; nSum += p->aClose; if (nCount >= nDays) { nMean = nSum / nDays; // Create nDays points nDev2 = 0; for (int j = nCount - nDays; j < nCount; j++) { p = (MHPrice*) (*in)[j]; nDev = p->aClose - nMean; nDev *= nDev; nDev2 += nDev; } nDev2 /= nDays; nDev2 = sqrt (nDev2); p = (MHPrice*) (*in)[nCount - 1]; out->Push (new MHPrice (p->aDate, nDev2)); p = (MHPrice*) (*in)[nCount - nDays]; nSum -= p->aClose; } } } /** * Make a vector with the stochastics formula * @param MHVector* - In serie * @param MHVector* - Out serie * @param int - %k no. of days * @param int - smoothing of %k * @param int - %d no. of days */ void MHPrice::Stochastics (MHVector* in, MHVector* kout, MHVector* dout, MHVector* out20, MHVector* out80, int k, int sma, int d) { double high, low, kval, diff1, diff2; MHPrice* p; MHPrice* p2; MHVector ve; kout->Erase (); dout->Erase (); for (int f = 0; f < in->Size(); f++) { p = (MHPrice*)(*in)[f]; if ((f + 1) >= k) { high = p->aHigh; low = p->aLow; // Get max/min prices for current range for (int j = ((f + 1) - k); j < ((f + 1) - 1); j++) { p2 = (MHPrice*) (*in)[j]; if (p2->aHigh > high) high = p2->aHigh; if (p2->aLow < low) low = p2->aLow; } diff1 = p->aClose - low; diff2 = high - low; if (diff2 > 0.00001) { kval = 100 * (diff1 / diff2); ve.Push (new MHPrice (p->aDate, kval)); if (out20) out20->Push (new MHPrice (p->aDate, 20.0)); if (out80) out80->Push (new MHPrice (p->aDate, 80.0)); } } } if (sma > 1) MHPrice::MovingAverage (&ve, kout, sma); else MHPrice::Copy (&ve, kout); MHPrice::MovingAverage (kout, dout, d); ve.Erase (); } /** * Validate point and try to fix missing data */ void MHPrice::Validate () { MHDate now; if (strlen(aDate) != 8) SetDate (now.Get (MHDate::LONG_DATE)); if (aClose > aHigh) aHigh = aClose; if (aClose < aLow) aLow = aClose; if (aOpen < 0.0000001 && aOpen > -0.0000001) aOpen = -1; if (aVol < 0.0000001 && aVol > -0.0000001) aVol = -1; if (aClose < 0.0000001 && aClose > -0.0000001) { aHigh = aLow =aClose = -1; } } /** * Make a vector with only volume data * @param MHVector* - Vector with MHPrices * @param MHVector* - The vector with converted data */ void MHPrice::Volume (MHVector* in, MHVector* out) { MHPrice* p; for (int f = 0; f < in->Size(); f++) { p = (MHPrice*) (*in)[f]; if (p->aVol >= 0.0000001) out->Push (new MHPrice (p->aDate, p->aVol)); } }