Files

322 lines
25 KiB
Plaintext
Raw Permalink Normal View History

2018-04-06 20:55:57 +02:00
//+------------------------------------------------------------------+
//| TimeSeries.mqh |
//| Copyright 2017, Vasiliy Sokolov. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Vasiliy Sokolov."
#property link "http://www.mql5.com"
#include <Arrays\ArrayObj.mqh>
#include "TimeValue.mqh"
//+------------------------------------------------------------------+
//| Universal timeseries: stores the values of TimeValue and |
//| provides quick access to them |
//+------------------------------------------------------------------+
class CTimeSeries
{
private:
CArrayObj m_series;
bool m_reverse;
datetime FromFinamData(string data);
public:
bool Add(datetime time, double value);
bool Add(datetime time, double& values[]);
void AddOrChange(datetime time, double value, int buff_index=0);
void Clear();
int Total(void){return m_series.Total();}
double operator[](int index);
double operator[](datetime time);
bool GetReverse(void);
void SetReverse(bool reverse);
CTimeValue* GetTimeValue(datetime time);
CTimeValue* GetTimeValue(int index);
void OrderBy(ENUM_COMP_TYPE comp_type);
int SearchLessOrEqual(datetime time);
bool SaveToCsv(datetime begin_time, datetime end_time, string path, int common=FILE_COMMON, int time_format=TIME_DATE|TIME_MINUTES, int digits = 4, uchar del = ';');
CTimeSeries(void);
bool LoadFromCsv(string path_csv, uchar del=',', int line_begin=0, int common = FILE_COMMON);
int LoadFromArray(uchar& array[], uchar del=',', int line_begin=0);
bool ExitsDataByTime(datetime time);
void ToDoubleArray(double& array[]);
void ToDoubleArray(int column, double& array[]);
};
//+------------------------------------------------------------------+
//| Default initialization |
//+------------------------------------------------------------------+
CTimeSeries::CTimeSeries() : m_reverse(false)
{
OrderBy(COMP_BY_TIME);
}
//+------------------------------------------------------------------+
//| Sorting series |
//+------------------------------------------------------------------+
void CTimeSeries::OrderBy(ENUM_COMP_TYPE comp_type)
{
m_series.Sort(comp_type);
}
//+------------------------------------------------------------------+
//| Adds a new value to the collection. If the data for the specified|
//| date already exist, returns false, and the data are not added |
//+------------------------------------------------------------------+
bool CTimeSeries::Add(datetime time,double value)
{
if(ExitsDataByTime(time))
return false;
CTimeValue* tv = new CTimeValue(time, value);
return m_series.InsertSort(tv);
}
//+------------------------------------------------------------------+
//| Returns the first column as an array of type double              |
//+------------------------------------------------------------------+
void CTimeSeries::ToDoubleArray(double &array[])
{
ToDoubleArray(0, array);
}
//+------------------------------------------------------------------+
//| Returns column with number 'column' as an array of type double   |
//+------------------------------------------------------------------+
void CTimeSeries::ToDoubleArray(int column,double &array[])
{
int total = m_series.Total();
ArrayResize(array, total);
for(int i = 0; i < total; i++)
{
CTimeValue* tv = m_series.At(i);
array[i] = tv.Value(column);
}
}
//+------------------------------------------------------------------+
//| Returns true if the data for the specified date exist |
//| returns false otherwise |
//+------------------------------------------------------------------+
bool CTimeSeries::ExitsDataByTime(datetime time)
{
if(m_series.Total() == 0)
return false;
CTimeValue* t = m_series.At(m_series.Total()-1);
if(t.Time() < time)
return false;
if(t.Time() == time)
return true;
CTimeValue* tv_s = new CTimeValue(time, 0.0);
int index = m_series.Search(tv_s);
delete tv_s;
return (index != -1);
}
//+------------------------------------------------------------------+
//| Adds a new value to the collection, if a value with such time |
//| is not present. If an element with this time at the specified |
//| index exists, modifies its values |
//+------------------------------------------------------------------+
void CTimeSeries::AddOrChange(datetime time,double value, int buff_index=0)
{
if(m_series.SortMode() != COMP_BY_TIME)
m_series.Sort(COMP_BY_TIME);
int index = -1;
if(m_series.Total() > 0)
{
CTimeValue* t = m_series.At(m_series.Total()-1);
if(t.Time() > time)
{
CTimeValue* tv_s = new CTimeValue(time, value);
index = m_series.Search(tv_s);
delete tv_s;
}
}
CTimeValue* tv = NULL;
if(index == -1)
{
tv = new CTimeValue(time, value);
m_series.InsertSort(tv);
}
else
{
tv = m_series.At(index);
tv.Value(buff_index, value);
}
}
//+------------------------------------------------------------------+
//| Saves content to a CSV file |
//+------------------------------------------------------------------+
bool CTimeSeries::SaveToCsv(datetime begin_time, datetime end_time, string path, int common=FILE_COMMON, int time_format=TIME_DATE|TIME_MINUTES, int digits = 4, uchar del = ';')
{
string lines[];
int total = ArrayResize(lines, m_series.Total());
string s = "";
int count = 0;
for(int i = 0; i < total; i++)
{
CTimeValue* tv = m_series.At(i);
if(tv.Time() < begin_time)
continue;
if(tv.Time() > end_time)
break;
lines[count] = tv.ToString(time_format, digits, del)+"\r\n";
count++;
s = lines[i];
}
total = ArrayResize(lines, count);
int h = FileOpen(path, FILE_WRITE|FILE_TXT|common, del, CP_ACP);
if(h == INVALID_HANDLE)
return false;
for(int i = 0; i < total; i++)
FileWriteString(h, lines[i]);
FileClose(h);
return true;
}
//+------------------------------------------------------------------+
//| Removes all items from the collection |
//+------------------------------------------------------------------+
void CTimeSeries::Clear(void)
{
m_series.Clear();
}
//+------------------------------------------------------------------+
//| Adds a list of new values to the collection |
//+------------------------------------------------------------------+
bool CTimeSeries::Add(datetime time,double& values[])
{
if(time == D'2017.06.12')
int dbg = 4;
if(ExitsDataByTime(time))
return false;
CTimeValue* tv = new CTimeValue(time, values);
return m_series.InsertSort(tv);
}
//+------------------------------------------------------------------+
//| Sets direct or reverse indexation in a collection |
//+------------------------------------------------------------------+
void CTimeSeries::SetReverse(bool reverse)
{
m_reverse = reverse;
}
//+------------------------------------------------------------------+
//| Returns true if reverse indexation is used, returns false |
//| if otherwise. |
//+------------------------------------------------------------------+
bool CTimeSeries::GetReverse(void)
{
return m_reverse;
}
//+------------------------------------------------------------------+
//| Get the index value |
//+------------------------------------------------------------------+
double CTimeSeries::operator[](int index)
{
CTimeValue* tv = NULL;
if(!m_reverse)
tv = m_series.At(index);
else
{
int ri = m_series.Total() - index - 1;
tv = m_series.At(ri);
}
return tv.Value();
}
//+------------------------------------------------------------------+
//| Returns the closest value with a smaller or the same time |
//+------------------------------------------------------------------+
double CTimeSeries::operator[](datetime time)
{
int index = SearchLessOrEqual(time);
if(index == -1)
return EMPTY_VALUE;
CTimeValue* tv = m_series.At(index);
return tv.Value();
}
//+------------------------------------------------------------------+
//| Returns the closest value with a smaller or the same time |
//| TimeValue |
//+------------------------------------------------------------------+
CTimeValue* CTimeSeries::GetTimeValue(datetime time)
{
CTimeValue* tv = NULL;
int index = SearchLessOrEqual(time);
if(index == -1)
return tv;
tv = m_series.At(index);
return tv;
}
//+------------------------------------------------------------------+
//| Returns the TimeValue corresponding to the index |
//+------------------------------------------------------------------+
CTimeValue* CTimeSeries::GetTimeValue(int index)
{
return m_series.At(index);
}
//+------------------------------------------------------------------+
//| Returns index of element with time equal to specified. |
//| If no element with this time exist, returns an element with time |
//| less than specified but greater than other elements with smaller |
//| time |
//+------------------------------------------------------------------+
int CTimeSeries::SearchLessOrEqual(datetime time)
{
CTimeValue* tv_s = new CTimeValue(time, EMPTY_VALUE);
if(m_series.SortMode() != COMP_BY_TIME)
m_series.Sort(COMP_BY_TIME);
int index = m_series.SearchLessOrEqual(tv_s);
delete tv_s;
return index;
}
//+------------------------------------------------------------------+
//| Loads a CSV file from the drive to a timeseries |
//+------------------------------------------------------------------+
bool CTimeSeries::LoadFromCsv(string path_csv, uchar del=',', int line_begin=0, int common = FILE_COMMON)
{
int h = FileOpen(path_csv, FILE_READ|FILE_BIN|common);
if(h == INVALID_HANDLE)
{
printf("Failed open file " + path_csv + " Last error: " + (string)GetLastError());
return false;
}
uchar array[];
FileReadArray(h, array, 0, WHOLE_ARRAY);
FileClose(h);
return (LoadFromArray(array, del, line_begin)>0);
}
//+------------------------------------------------------------------+
//| Loads a CSV file passed as a byte array to a timeseries. |
//| |
//| Returns the number of lines added |
//+------------------------------------------------------------------+
int CTimeSeries::LoadFromArray(uchar &array[], uchar del=',',int line_begin=0)
{
string csv = CharArrayToString(array, 0, WHOLE_ARRAY, CP_ACP);
string lines[];
StringSplit(csv, '\n', lines);
int total = ArraySize(lines);
int count = 0;
for(int i = line_begin; i < total; i++)
{
string col[];
double values[];
string l = lines[i];
if(StringSplit(lines[i], del, col)<=1)
continue;
int col_size = ArraySize(col)-1;
ArrayResize(values, col_size);
datetime time = FromFinamData(col[0]);
for(int c = 1; c <= col_size; c++)
values[c-1] = StringToDouble(col[c]);
if(Add(time, values))
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Parses the broker date |
//+------------------------------------------------------------------+
datetime CTimeSeries::FromFinamData(string data)
{
int l = StringLen(data);
uchar ch = (uchar)StringGetCharacter(data, 0);
int year = (int)StringSubstr(data, 0, 4);
int month = (int)StringSubstr(data, 4, 2);
int day = (int)StringSubstr(data, 6, 2);
MqlDateTime time_str={0};
time_str.day = day;
time_str.year = year;
time_str.mon = month;
return StructToTime(time_str);
}